diff options
Diffstat (limited to 'ml/dlib/dlib/matrix')
50 files changed, 30343 insertions, 0 deletions
diff --git a/ml/dlib/dlib/matrix/cblas_constants.h b/ml/dlib/dlib/matrix/cblas_constants.h new file mode 100644 index 000000000..6ff89f141 --- /dev/null +++ b/ml/dlib/dlib/matrix/cblas_constants.h @@ -0,0 +1,22 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_CBLAS_CONSTAnTS_Hh_ +#define DLIB_CBLAS_CONSTAnTS_Hh_ + +#ifndef CBLAS_H +namespace dlib +{ + namespace blas_bindings + { + enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102}; + enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113}; + enum CBLAS_UPLO {CblasUpper=121, CblasLower=122}; + enum CBLAS_DIAG {CblasNonUnit=131, CblasUnit=132}; + enum CBLAS_SIDE {CblasLeft=141, CblasRight=142}; + + } +} +#endif // if not CBLAS_H + +#endif // DLIB_CBLAS_CONSTAnTS_Hh_ + diff --git a/ml/dlib/dlib/matrix/lapack/fortran_id.h b/ml/dlib/dlib/matrix/lapack/fortran_id.h new file mode 100644 index 000000000..8027ea34f --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/fortran_id.h @@ -0,0 +1,62 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_BOOST_NUMERIC_BINDINGS_TRAITS_FORTRAN_H +#define DLIB_BOOST_NUMERIC_BINDINGS_TRAITS_FORTRAN_H + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// FORTRAN BINDING STUFF FROM BOOST +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +// Permission to copy, use, modify, sell and +// distribute this software is granted provided this copyright notice appears +// in all copies. This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// Copyright (C) 2002, 2003 Si-Lab b.v.b.a., Toon Knapen and Kresimir Fresl + + +// First we need to know what the conventions for linking +// C with Fortran is on this platform/toolset +#if defined(LAPACK_FORCE_UNDERSCORE) +#define DLIB_BIND_FORTRAN_LOWERCASE_UNDERSCORE +#elif defined(LAPACK_FORCE_NOUNDERSCORE) +#define DLIB_BIND_FORTRAN_LOWERCASE +#elif defined(__GNUC__) || defined(__ICC) || defined(__sgi) || defined(__COMO__) || defined(__KCC) +#define DLIB_BIND_FORTRAN_LOWERCASE_UNDERSCORE +#elif defined(__IBMCPP__) || defined(_MSC_VER) || defined(__BORLANDC__) +#define DLIB_BIND_FORTRAN_LOWERCASE +#else +#error do not know how to link with fortran for the given platform +#endif + +// Next we define macros to convert our symbols to +// the current convention +#if defined(DLIB_BIND_FORTRAN_LOWERCASE_UNDERSCORE) +#define DLIB_FORTRAN_ID( id ) id##_ +#elif defined(DLIB_BIND_FORTRAN_LOWERCASE) +#define DLIB_FORTRAN_ID( id ) id +#else +#error do not know how to bind to fortran calling convention +#endif + + + +namespace dlib +{ + namespace lapack + { + // stuff from f2c used to define what exactly is an integer in fortran +#if (defined(__alpha__) || defined(__sparc64__) || defined(__x86_64__) || defined(__ia64__)) && !defined(MATLAB_MEX_FILE) + typedef int integer; + typedef unsigned int uinteger; +#else + typedef long int integer; + typedef unsigned long int uinteger; +#endif + + } +} + +#endif // DLIB_BOOST_NUMERIC_BINDINGS_TRAITS_FORTRAN_H + diff --git a/ml/dlib/dlib/matrix/lapack/gees.h b/ml/dlib/dlib/matrix/lapack/gees.h new file mode 100644 index 000000000..a8ee63ff1 --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/gees.h @@ -0,0 +1,264 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_ES_Hh_ +#define DLIB_LAPACk_ES_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { +#if defined(__alpha__) || defined(__sparc64__) || defined(__x86_64__) || defined(__ia64__) + typedef int logical; +#else + typedef long int logical; +#endif + typedef logical (*L_fp)(...); + + extern "C" + { + void DLIB_FORTRAN_ID(dgees) (char *jobvs, char *sort, L_fp select, integer *n, + double *a, integer *lda, integer *sdim, double *wr, + double *wi, double *vs, integer *ldvs, double *work, + integer *lwork, logical *bwork, integer *info); + + void DLIB_FORTRAN_ID(sgees) (char *jobvs, char *sort, L_fp select, integer *n, + float *a, integer *lda, integer *sdim, float *wr, + float *wi, float *vs, integer *ldvs, float *work, + integer *lwork, logical *bwork, integer *info); + + } + + inline int gees (char jobvs, integer n, + double *a, integer lda, double *wr, + double *wi, double *vs, integer ldvs, double *work, + integer lwork) + { + // No sorting allowed + integer info = 0; + char sort = 'N'; + L_fp fnil = 0; + logical bwork = 0; + integer sdim = 0; + DLIB_FORTRAN_ID(dgees)(&jobvs, &sort, fnil, &n, + a, &lda, &sdim, wr, + wi, vs, &ldvs, work, + &lwork, &bwork, &info); + return info; + } + + + inline int gees (char jobvs, integer n, + float *a, integer lda, float *wr, + float *wi, float *vs, integer ldvs, float *work, + integer lwork) + { + // No sorting allowed + integer info = 0; + char sort = 'N'; + L_fp fnil = 0; + logical bwork = 0; + integer sdim = 0; + DLIB_FORTRAN_ID(sgees)(&jobvs, &sort, fnil, &n, + a, &lda, &sdim, wr, + wi, vs, &ldvs, work, + &lwork, &bwork, &info); + return info; + } + + + } + + // ------------------------------------------------------------------------------------ + +/* -- LAPACK driver routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ +/* .. Function Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DGEES computes for an N-by-N real nonsymmetric matrix A, the */ +/* eigenvalues, the real Schur form T, and, optionally, the matrix of */ +/* Schur vectors Z. This gives the Schur factorization A = Z*T*(Z**T). */ + +/* Optionally, it also orders the eigenvalues on the diagonal of the */ +/* real Schur form so that selected eigenvalues are at the top left. */ +/* The leading columns of Z then form an orthonormal basis for the */ +/* invariant subspace corresponding to the selected eigenvalues. */ + +/* A matrix is in real Schur form if it is upper quasi-triangular with */ +/* 1-by-1 and 2-by-2 blocks. 2-by-2 blocks will be standardized in the */ +/* form */ +/* [ a b ] */ +/* [ c a ] */ + +/* where b*c < 0. The eigenvalues of such a block are a +- sqrt(bc). */ + +/* Arguments */ +/* ========= */ + +/* JOBVS (input) CHARACTER*1 */ +/* = 'N': Schur vectors are not computed; */ +/* = 'V': Schur vectors are computed. */ + +/* SORT (input) CHARACTER*1 */ +/* Specifies whether or not to order the eigenvalues on the */ +/* diagonal of the Schur form. */ +/* = 'N': Eigenvalues are not ordered; */ +/* = 'S': Eigenvalues are ordered (see SELECT). */ + +/* SELECT (external procedure) LOGICAL FUNCTION of two DOUBLE PRECISION arguments */ +/* SELECT must be declared EXTERNAL in the calling subroutine. */ +/* If SORT = 'S', SELECT is used to select eigenvalues to sort */ +/* to the top left of the Schur form. */ +/* If SORT = 'N', SELECT is not referenced. */ +/* An eigenvalue WR(j)+sqrt(-1)*WI(j) is selected if */ +/* SELECT(WR(j),WI(j)) is true; i.e., if either one of a complex */ +/* conjugate pair of eigenvalues is selected, then both complex */ +/* eigenvalues are selected. */ +/* Note that a selected complex eigenvalue may no longer */ +/* satisfy SELECT(WR(j),WI(j)) = .TRUE. after ordering, since */ +/* ordering may change the value of complex eigenvalues */ +/* (especially if the eigenvalue is ill-conditioned); in this */ +/* case INFO is set to N+2 (see INFO below). */ + +/* N (input) INTEGER */ +/* The order of the matrix A. N >= 0. */ + +/* A (input/output) DOUBLE PRECISION array, dimension (LDA,N) */ +/* On entry, the N-by-N matrix A. */ +/* On exit, A has been overwritten by its real Schur form T. */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. LDA >= max(1,N). */ + +/* SDIM (output) INTEGER */ +/* If SORT = 'N', SDIM = 0. */ +/* If SORT = 'S', SDIM = number of eigenvalues (after sorting) */ +/* for which SELECT is true. (Complex conjugate */ +/* pairs for which SELECT is true for either */ +/* eigenvalue count as 2.) */ + +/* WR (output) DOUBLE PRECISION array, dimension (N) */ +/* WI (output) DOUBLE PRECISION array, dimension (N) */ +/* WR and WI contain the real and imaginary parts, */ +/* respectively, of the computed eigenvalues in the same order */ +/* that they appear on the diagonal of the output Schur form T. */ +/* Complex conjugate pairs of eigenvalues will appear */ +/* consecutively with the eigenvalue having the positive */ +/* imaginary part first. */ + +/* VS (output) DOUBLE PRECISION array, dimension (LDVS,N) */ +/* If JOBVS = 'V', VS contains the orthogonal matrix Z of Schur */ +/* vectors. */ +/* If JOBVS = 'N', VS is not referenced. */ + +/* LDVS (input) INTEGER */ +/* The leading dimension of the array VS. LDVS >= 1; if */ +/* JOBVS = 'V', LDVS >= N. */ + +/* WORK (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) */ +/* On exit, if INFO = 0, WORK(1) contains the optimal LWORK. */ + +/* LWORK (input) INTEGER */ +/* The dimension of the array WORK. LWORK >= max(1,3*N). */ +/* For good performance, LWORK must generally be larger. */ + +/* If LWORK = -1, then a workspace query is assumed; the routine */ +/* only calculates the optimal size of the WORK array, returns */ +/* this value as the first entry of the WORK array, and no error */ +/* message related to LWORK is issued by XERBLA. */ + +/* BWORK (workspace) LOGICAL array, dimension (N) */ +/* Not referenced if SORT = 'N'. */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit */ +/* < 0: if INFO = -i, the i-th argument had an illegal value. */ +/* > 0: if INFO = i, and i is */ +/* <= N: the QR algorithm failed to compute all the */ +/* eigenvalues; elements 1:ILO-1 and i+1:N of WR and WI */ +/* contain those eigenvalues which have converged; if */ +/* JOBVS = 'V', VS contains the matrix which reduces A */ +/* to its partially converged Schur form. */ +/* = N+1: the eigenvalues could not be reordered because some */ +/* eigenvalues were too close to separate (the problem */ +/* is very ill-conditioned); */ +/* = N+2: after reordering, roundoff changed values of some */ +/* complex eigenvalues so that leading eigenvalues in */ +/* the Schur form no longer satisfy SELECT=.TRUE. This */ +/* could also be caused by underflow due to scaling. */ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, long NR4, + long NC1, long NC2, long NC3, long NC4, + typename MM, + typename layout + > + int gees ( + const char jobz, + matrix<T,NR1,NC1,MM,column_major_layout>& a, + matrix<T,NR2,NC2,MM,layout>& wr, + matrix<T,NR3,NC3,MM,layout>& wi, + matrix<T,NR4,NC4,MM,column_major_layout>& vs + ) + { + matrix<T,0,1,MM,column_major_layout> work; + + const long n = a.nr(); + + wr.set_size(n,1); + wi.set_size(n,1); + + if (jobz == 'V') + vs.set_size(n,n); + else + vs.set_size(NR4?NR4:1, NC4?NC4:1); + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::gees(jobz, n, + &a(0,0), a.nr(), &wr(0,0), + &wi(0,0), &vs(0,0), vs.nr(), &work_size, + -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual decomposition + info = binding::gees(jobz, n, + &a(0,0), a.nr(), &wr(0,0), + &wi(0,0), &vs(0,0), vs.nr(), &work(0,0), + work.size()); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_ES_Hh_ + diff --git a/ml/dlib/dlib/matrix/lapack/geev.h b/ml/dlib/dlib/matrix/lapack/geev.h new file mode 100644 index 000000000..d8fdc4af5 --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/geev.h @@ -0,0 +1,234 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_GEEV_Hh_ +#define DLIB_LAPACk_GEEV_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dgeev) (char *jobvl, char *jobvr, integer *n, double * a, + integer *lda, double *wr, double *wi, double *vl, + integer *ldvl, double *vr, integer *ldvr, double *work, + integer *lwork, integer *info); + + void DLIB_FORTRAN_ID(sgeev) (char *jobvl, char *jobvr, integer *n, float * a, + integer *lda, float *wr, float *wi, float *vl, + integer *ldvl, float *vr, integer *ldvr, float *work, + integer *lwork, integer *info); + + } + + inline int geev (char jobvl, char jobvr, integer n, double *a, + integer lda, double *wr, double *wi, double *vl, + integer ldvl, double *vr, integer ldvr, double *work, + integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(dgeev)(&jobvl, &jobvr, &n, a, + &lda, wr, wi, vl, + &ldvl, vr, &ldvr, work, + &lwork, &info); + return info; + } + + inline int geev (char jobvl, char jobvr, integer n, float *a, + integer lda, float *wr, float *wi, float *vl, + integer ldvl, float *vr, integer ldvr, float *work, + integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(sgeev)(&jobvl, &jobvr, &n, a, + &lda, wr, wi, vl, + &ldvl, vr, &ldvr, work, + &lwork, &info); + return info; + } + + + } + + // ------------------------------------------------------------------------------------ + +/* -- LAPACK driver routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DGEEV computes for an N-by-N real nonsymmetric matrix A, the */ +/* eigenvalues and, optionally, the left and/or right eigenvectors. */ + +/* The right eigenvector v(j) of A satisfies */ +/* A * v(j) = lambda(j) * v(j) */ +/* where lambda(j) is its eigenvalue. */ +/* The left eigenvector u(j) of A satisfies */ +/* u(j)**H * A = lambda(j) * u(j)**H */ +/* where u(j)**H denotes the conjugate transpose of u(j). */ + +/* The computed eigenvectors are normalized to have Euclidean norm */ +/* equal to 1 and largest component real. */ + +/* Arguments */ +/* ========= */ + +/* JOBVL (input) CHARACTER*1 */ +/* = 'N': left eigenvectors of A are not computed; */ +/* = 'V': left eigenvectors of A are computed. */ + +/* JOBVR (input) CHARACTER*1 */ +/* = 'N': right eigenvectors of A are not computed; */ +/* = 'V': right eigenvectors of A are computed. */ + +/* N (input) INTEGER */ +/* The order of the matrix A. N >= 0. */ + +/* A (input/output) DOUBLE PRECISION array, dimension (LDA,N) */ +/* On entry, the N-by-N matrix A. */ +/* On exit, A has been overwritten. */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. LDA >= max(1,N). */ + +/* WR (output) DOUBLE PRECISION array, dimension (N) */ +/* WI (output) DOUBLE PRECISION array, dimension (N) */ +/* WR and WI contain the real and imaginary parts, */ +/* respectively, of the computed eigenvalues. Complex */ +/* conjugate pairs of eigenvalues appear consecutively */ +/* with the eigenvalue having the positive imaginary part */ +/* first. */ + +/* VL (output) DOUBLE PRECISION array, dimension (LDVL,N) */ +/* If JOBVL = 'V', the left eigenvectors u(j) are stored one */ +/* after another in the columns of VL, in the same order */ +/* as their eigenvalues. */ +/* If JOBVL = 'N', VL is not referenced. */ +/* If the j-th eigenvalue is real, then u(j) = VL(:,j), */ +/* the j-th column of VL. */ +/* If the j-th and (j+1)-st eigenvalues form a complex */ +/* conjugate pair, then u(j) = VL(:,j) + i*VL(:,j+1) and */ +/* u(j+1) = VL(:,j) - i*VL(:,j+1). */ + +/* LDVL (input) INTEGER */ +/* The leading dimension of the array VL. LDVL >= 1; if */ +/* JOBVL = 'V', LDVL >= N. */ + +/* VR (output) DOUBLE PRECISION array, dimension (LDVR,N) */ +/* If JOBVR = 'V', the right eigenvectors v(j) are stored one */ +/* after another in the columns of VR, in the same order */ +/* as their eigenvalues. */ +/* If JOBVR = 'N', VR is not referenced. */ +/* If the j-th eigenvalue is real, then v(j) = VR(:,j), */ +/* the j-th column of VR. */ +/* If the j-th and (j+1)-st eigenvalues form a complex */ +/* conjugate pair, then v(j) = VR(:,j) + i*VR(:,j+1) and */ +/* v(j+1) = VR(:,j) - i*VR(:,j+1). */ + +/* LDVR (input) INTEGER */ +/* The leading dimension of the array VR. LDVR >= 1; if */ +/* JOBVR = 'V', LDVR >= N. */ + +/* WORK (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) */ +/* On exit, if INFO = 0, WORK(1) returns the optimal LWORK. */ + +/* LWORK (input) INTEGER */ +/* The dimension of the array WORK. LWORK >= max(1,3*N), and */ +/* if JOBVL = 'V' or JOBVR = 'V', LWORK >= 4*N. For good */ +/* performance, LWORK must generally be larger. */ + +/* If LWORK = -1, then a workspace query is assumed; the routine */ +/* only calculates the optimal size of the WORK array, returns */ +/* this value as the first entry of the WORK array, and no error */ +/* message related to LWORK is issued by XERBLA. */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit */ +/* < 0: if INFO = -i, the i-th argument had an illegal value. */ +/* > 0: if INFO = i, the QR algorithm failed to compute all the */ +/* eigenvalues, and no eigenvectors have been computed; */ +/* elements i+1:N of WR and WI contain eigenvalues which */ +/* have converged. */ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, long NR4, long NR5, + long NC1, long NC2, long NC3, long NC4, long NC5, + typename MM, + typename layout + > + int geev ( + const char jobvl, + const char jobvr, + matrix<T,NR1,NC1,MM,column_major_layout>& a, + matrix<T,NR2,NC2,MM,layout>& wr, + matrix<T,NR3,NC3,MM,layout>& wi, + matrix<T,NR4,NC4,MM,column_major_layout>& vl, + matrix<T,NR5,NC5,MM,column_major_layout>& vr + ) + { + matrix<T,0,1,MM,column_major_layout> work; + + const long n = a.nr(); + + wr.set_size(n,1); + wi.set_size(n,1); + + if (jobvl == 'V') + vl.set_size(n,n); + else + vl.set_size(NR4?NR4:1, NC4?NC4:1); + + if (jobvr == 'V') + vr.set_size(n,n); + else + vr.set_size(NR5?NR5:1, NC5?NC5:1); + + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::geev(jobvl, jobvr, n, &a(0,0), + a.nr(), &wr(0,0), &wi(0,0), &vl(0,0), + vl.nr(), &vr(0,0), vr.nr(), &work_size, + -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual decomposition + info = binding::geev(jobvl, jobvr, n, &a(0,0), + a.nr(), &wr(0,0), &wi(0,0), &vl(0,0), + vl.nr(), &vr(0,0), vr.nr(), &work(0,0), + work.size()); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_GEEV_Hh_ + + diff --git a/ml/dlib/dlib/matrix/lapack/geqrf.h b/ml/dlib/dlib/matrix/lapack/geqrf.h new file mode 100644 index 000000000..c1f8fc050 --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/geqrf.h @@ -0,0 +1,168 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_GEQRF_Hh_ +#define DLIB_LAPACk_GEQRF_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dgeqrf) (integer *m, integer *n, double *a, integer * + lda, double *tau, double *work, integer *lwork, + integer *info); + + void DLIB_FORTRAN_ID(sgeqrf) (integer *m, integer *n, float *a, integer * + lda, float *tau, float *work, integer *lwork, + integer *info); + } + + inline int geqrf (integer m, integer n, double *a, integer lda, + double *tau, double *work, integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(dgeqrf)(&m, &n, a, &lda, + tau, work, &lwork, &info); + return info; + } + + inline int geqrf (integer m, integer n, float *a, integer lda, + float *tau, float *work, integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(sgeqrf)(&m, &n, a, &lda, + tau, work, &lwork, &info); + return info; + } + + + } + + // ------------------------------------------------------------------------------------ + +/* -- LAPACK routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DGEQRF computes a QR factorization of a real M-by-N matrix A: */ +/* A = Q * R. */ + +/* Arguments */ +/* ========= */ + +/* M (input) INTEGER */ +/* The number of rows of the matrix A. M >= 0. */ + +/* N (input) INTEGER */ +/* The number of columns of the matrix A. N >= 0. */ + +/* A (input/output) DOUBLE PRECISION array, dimension (LDA,N) */ +/* On entry, the M-by-N matrix A. */ +/* On exit, the elements on and above the diagonal of the array */ +/* contain the min(M,N)-by-N upper trapezoidal matrix R (R is */ +/* upper triangular if m >= n); the elements below the diagonal, */ +/* with the array TAU, represent the orthogonal matrix Q as a */ +/* product of min(m,n) elementary reflectors (see Further */ +/* Details). */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. LDA >= max(1,M). */ + +/* TAU (output) DOUBLE PRECISION array, dimension (min(M,N)) */ +/* The scalar factors of the elementary reflectors (see Further */ +/* Details). */ + +/* WORK (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) */ +/* On exit, if INFO = 0, WORK(1) returns the optimal LWORK. */ + +/* LWORK (input) INTEGER */ +/* The dimension of the array WORK. LWORK >= max(1,N). */ +/* For optimum performance LWORK >= N*NB, where NB is */ +/* the optimal blocksize. */ + +/* If LWORK = -1, then a workspace query is assumed; the routine */ +/* only calculates the optimal size of the WORK array, returns */ +/* this value as the first entry of the WORK array, and no error */ +/* message related to LWORK is issued by XERBLA. */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit */ +/* < 0: if INFO = -i, the i-th argument had an illegal value */ + +/* Further Details */ +/* =============== */ + +/* The matrix Q is represented as a product of elementary reflectors */ + +/* Q = H(1) H(2) . . . H(k), where k = min(m,n). */ + +/* Each H(i) has the form */ + +/* H(i) = I - tau * v * v' */ + +/* where tau is a real scalar, and v is a real vector with */ +/* v(1:i-1) = 0 and v(i) = 1; v(i+1:m) is stored on exit in A(i+1:m,i), */ +/* and tau in TAU(i). */ + + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, + long NC1, long NC2, + typename MM + > + int geqrf ( + matrix<T,NR1,NC1,MM,column_major_layout>& a, + matrix<T,NR2,NC2,MM,column_major_layout>& tau + ) + { + matrix<T,0,1,MM,column_major_layout> work; + + tau.set_size(std::min(a.nr(), a.nc()), 1); + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::geqrf(a.nr(), a.nc(), &a(0,0), a.nr(), + &tau(0,0), &work_size, -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual decomposition + info = binding::geqrf(a.nr(), a.nc(), &a(0,0), a.nr(), + &tau(0,0), &work(0,0), work.size()); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_GEQRF_Hh_ + + + diff --git a/ml/dlib/dlib/matrix/lapack/gesdd.h b/ml/dlib/dlib/matrix/lapack/gesdd.h new file mode 100644 index 000000000..e6b4d26e1 --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/gesdd.h @@ -0,0 +1,364 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_SDD_Hh_ +#define DLIB_LAPACk_SDD_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dgesdd) (char const* jobz, + const integer* m, const integer* n, double* a, const integer* lda, + double* s, double* u, const integer* ldu, + double* vt, const integer* ldvt, + double* work, const integer* lwork, integer* iwork, integer* info); + + void DLIB_FORTRAN_ID(sgesdd) (char const* jobz, + const integer* m, const integer* n, float* a, const integer* lda, + float* s, float* u, const integer* ldu, + float* vt, const integer* ldvt, + float* work, const integer* lwork, integer* iwork, integer* info); + + } + + inline integer gesdd (const char jobz, + const integer m, const integer n, double* a, const integer lda, + double* s, double* u, const integer ldu, + double* vt, const integer ldvt, + double* work, const integer lwork, integer* iwork) + { + integer info = 0; + DLIB_FORTRAN_ID(dgesdd)(&jobz, &m, &n, a, &lda, s, u, &ldu, vt, &ldvt, work, &lwork, iwork, &info); + return info; + } + + inline integer gesdd (const char jobz, + const integer m, const integer n, float* a, const integer lda, + float* s, float* u, const integer ldu, + float* vt, const integer ldvt, + float* work, const integer lwork, integer* iwork) + { + integer info = 0; + DLIB_FORTRAN_ID(sgesdd)(&jobz, &m, &n, a, &lda, s, u, &ldu, vt, &ldvt, work, &lwork, iwork, &info); + return info; + } + } + + // ------------------------------------------------------------------------------------ + +/* -- LAPACK driver routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DGESDD computes the singular value decomposition (SVD) of a real */ +/* M-by-N matrix A, optionally computing the left and right singular */ +/* vectors. If singular vectors are desired, it uses a */ +/* divide-and-conquer algorithm. */ + +/* The SVD is written */ + +/* A = U * SIGMA * transpose(V) */ + +/* where SIGMA is an M-by-N matrix which is zero except for its */ +/* min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and */ +/* V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA */ +/* are the singular values of A; they are real and non-negative, and */ +/* are returned in descending order. The first min(m,n) columns of */ +/* U and V are the left and right singular vectors of A. */ + +/* Note that the routine returns VT = V**T, not V. */ + +/* The divide and conquer algorithm makes very mild assumptions about */ +/* floating point arithmetic. It will work on machines with a guard */ +/* digit in add/subtract, or on those binary machines without guard */ +/* digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or */ +/* Cray-2. It could conceivably fail on hexadecimal or decimal machines */ +/* without guard digits, but we know of none. */ + +/* Arguments */ +/* ========= */ + +/* JOBZ (input) CHARACTER*1 */ +/* Specifies options for computing all or part of the matrix U: */ +/* = 'A': all M columns of U and all N rows of V**T are */ +/* returned in the arrays U and VT; */ +/* = 'S': the first min(M,N) columns of U and the first */ +/* min(M,N) rows of V**T are returned in the arrays U */ +/* and VT; */ +/* = 'O': If M >= N, the first N columns of U are overwritten */ +/* on the array A and all rows of V**T are returned in */ +/* the array VT; */ +/* otherwise, all columns of U are returned in the */ +/* array U and the first M rows of V**T are overwritten */ +/* in the array A; */ +/* = 'N': no columns of U or rows of V**T are computed. */ + +/* M (input) INTEGER */ +/* The number of rows of the input matrix A. M >= 0. */ + +/* N (input) INTEGER */ +/* The number of columns of the input matrix A. N >= 0. */ + +/* A (input/output) DOUBLE PRECISION array, dimension (LDA,N) */ +/* On entry, the M-by-N matrix A. */ +/* On exit, */ +/* if JOBZ = 'O', A is overwritten with the first N columns */ +/* of U (the left singular vectors, stored */ +/* columnwise) if M >= N; */ +/* A is overwritten with the first M rows */ +/* of V**T (the right singular vectors, stored */ +/* rowwise) otherwise. */ +/* if JOBZ .ne. 'O', the contents of A are destroyed. */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. LDA >= max(1,M). */ + +/* S (output) DOUBLE PRECISION array, dimension (min(M,N)) */ +/* The singular values of A, sorted so that S(i) >= S(i+1). */ + +/* U (output) DOUBLE PRECISION array, dimension (LDU,UCOL) */ +/* UCOL = M if JOBZ = 'A' or JOBZ = 'O' and M < N; */ +/* UCOL = min(M,N) if JOBZ = 'S'. */ +/* If JOBZ = 'A' or JOBZ = 'O' and M < N, U contains the M-by-M */ +/* orthogonal matrix U; */ +/* if JOBZ = 'S', U contains the first min(M,N) columns of U */ +/* (the left singular vectors, stored columnwise); */ +/* if JOBZ = 'O' and M >= N, or JOBZ = 'N', U is not referenced. */ + +/* LDU (input) INTEGER */ +/* The leading dimension of the array U. LDU >= 1; if */ +/* JOBZ = 'S' or 'A' or JOBZ = 'O' and M < N, LDU >= M. */ + +/* VT (output) DOUBLE PRECISION array, dimension (LDVT,N) */ +/* If JOBZ = 'A' or JOBZ = 'O' and M >= N, VT contains the */ +/* N-by-N orthogonal matrix V**T; */ +/* if JOBZ = 'S', VT contains the first min(M,N) rows of */ +/* V**T (the right singular vectors, stored rowwise); */ +/* if JOBZ = 'O' and M < N, or JOBZ = 'N', VT is not referenced. */ + +/* LDVT (input) INTEGER */ +/* The leading dimension of the array VT. LDVT >= 1; if */ +/* JOBZ = 'A' or JOBZ = 'O' and M >= N, LDVT >= N; */ +/* if JOBZ = 'S', LDVT >= min(M,N). */ + +/* WORK (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) */ +/* On exit, if INFO = 0, WORK(1) returns the optimal LWORK; */ + +/* LWORK (input) INTEGER */ +/* The dimension of the array WORK. LWORK >= 1. */ +/* If JOBZ = 'N', */ +/* LWORK >= 3*min(M,N) + max(max(M,N),7*min(M,N)). */ +/* If JOBZ = 'O', */ +/* LWORK >= 3*min(M,N)*min(M,N) + */ +/* max(max(M,N),5*min(M,N)*min(M,N)+4*min(M,N)). */ +/* If JOBZ = 'S' or 'A' */ +/* LWORK >= 3*min(M,N)*min(M,N) + */ +/* max(max(M,N),4*min(M,N)*min(M,N)+4*min(M,N)). */ +/* For good performance, LWORK should generally be larger. */ +/* If LWORK = -1 but other input arguments are legal, WORK(1) */ +/* returns the optimal LWORK. */ + +/* IWORK (workspace) INTEGER array, dimension (8*min(M,N)) */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit. */ +/* < 0: if INFO = -i, the i-th argument had an illegal value. */ +/* > 0: DBDSDC did not converge, updating process failed. */ + +/* Further Details */ +/* =============== */ + +/* Based on contributions by */ +/* Ming Gu and Huan Ren, Computer Science Division, University of */ +/* California at Berkeley, USA */ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, long NR4, + long NC1, long NC2, long NC3, long NC4, + typename MM + > + int gesdd ( + const char jobz, + matrix<T,NR1,NC1,MM,column_major_layout>& a, + matrix<T,NR2,NC2,MM,column_major_layout>& s, + matrix<T,NR3,NC3,MM,column_major_layout>& u, + matrix<T,NR4,NC4,MM,column_major_layout>& vt + ) + { + matrix<T,0,1,MM,column_major_layout> work; + matrix<integer,0,1,MM,column_major_layout> iwork; + + const long m = a.nr(); + const long n = a.nc(); + s.set_size(std::min(m,n), 1); + + // make sure the iwork memory block is big enough + if (iwork.size() < 8*std::min(m,n)) + iwork.set_size(8*std::min(m,n), 1); + + if (jobz == 'A') + { + u.set_size(m,m); + vt.set_size(n,n); + } + else if (jobz == 'S') + { + u.set_size(m, std::min(m,n)); + vt.set_size(std::min(m,n), n); + } + else if (jobz == 'O') + { + DLIB_CASSERT(false, "jobz == 'O' not supported"); + } + else + { + u.set_size(NR3?NR3:1, NC3?NC3:1); + vt.set_size(NR4?NR4:1, NC4?NC4:1); + } + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::gesdd(jobz, a.nr(), a.nc(), &a(0,0), a.nr(), + &s(0,0), &u(0,0), u.nr(), &vt(0,0), vt.nr(), + &work_size, -1, &iwork(0,0)); + + if (info != 0) + return info; + + // There is a bug in an older version of LAPACK in Debian etch + // that causes the gesdd to return the wrong value for work_size + // when jobz == 'N'. So verify the value of work_size. + if (jobz == 'N') + { + using std::min; + using std::max; + const T min_work_size = 3*min(m,n) + max(max(m,n),7*min(m,n)); + if (work_size < min_work_size) + work_size = min_work_size; + } + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual SVD + info = binding::gesdd(jobz, a.nr(), a.nc(), &a(0,0), a.nr(), + &s(0,0), &u(0,0), u.nr(), &vt(0,0), vt.nr(), + &work(0,0), work.size(), &iwork(0,0)); + + return info; + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, long NR4, + long NC1, long NC2, long NC3, long NC4, + typename MM + > + int gesdd ( + const char jobz, + matrix<T,NR1,NC1,MM,row_major_layout>& a, + matrix<T,NR2,NC2,MM,row_major_layout>& s, + matrix<T,NR3,NC3,MM,row_major_layout>& u_, + matrix<T,NR4,NC4,MM,row_major_layout>& vt_ + ) + { + matrix<T,0,1,MM,row_major_layout> work; + matrix<integer,0,1,MM,row_major_layout> iwork; + + // Row major order matrices are transposed from LAPACK's point of view. + matrix<T,NR4,NC4,MM,row_major_layout>& u = vt_; + matrix<T,NR3,NC3,MM,row_major_layout>& vt = u_; + + + const long m = a.nc(); + const long n = a.nr(); + s.set_size(std::min(m,n), 1); + + // make sure the iwork memory block is big enough + if (iwork.size() < 8*std::min(m,n)) + iwork.set_size(8*std::min(m,n), 1); + + if (jobz == 'A') + { + u.set_size(m,m); + vt.set_size(n,n); + } + else if (jobz == 'S') + { + u.set_size(std::min(m,n), m); + vt.set_size(n, std::min(m,n)); + } + else if (jobz == 'O') + { + DLIB_CASSERT(false, "jobz == 'O' not supported"); + } + else + { + u.set_size(NR4?NR4:1, NC4?NC4:1); + vt.set_size(NR3?NR3:1, NC3?NC3:1); + } + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::gesdd(jobz, m, n, &a(0,0), a.nc(), + &s(0,0), &u(0,0), u.nc(), &vt(0,0), vt.nc(), + &work_size, -1, &iwork(0,0)); + + if (info != 0) + return info; + + // There is a bug in an older version of LAPACK in Debian etch + // that causes the gesdd to return the wrong value for work_size + // when jobz == 'N'. So verify the value of work_size. + if (jobz == 'N') + { + using std::min; + using std::max; + const T min_work_size = 3*min(m,n) + max(max(m,n),7*min(m,n)); + if (work_size < min_work_size) + work_size = min_work_size; + } + + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual SVD + info = binding::gesdd(jobz, m, n, &a(0,0), a.nc(), + &s(0,0), &u(0,0), u.nc(), &vt(0,0), vt.nc(), + &work(0,0), work.size(), &iwork(0,0)); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_SDD_Hh_ + + diff --git a/ml/dlib/dlib/matrix/lapack/gesvd.h b/ml/dlib/dlib/matrix/lapack/gesvd.h new file mode 100644 index 000000000..e00654db6 --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/gesvd.h @@ -0,0 +1,323 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_SVD_Hh_ +#define DLIB_LAPACk_SVD_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dgesvd) (const char* jobu, const char* jobvt, + const integer* m, const integer* n, double* a, const integer* lda, + double* s, double* u, const integer* ldu, + double* vt, const integer* ldvt, + double* work, const integer* lwork, integer* info); + + void DLIB_FORTRAN_ID(sgesvd) (const char* jobu, const char* jobvt, + const integer* m, const integer* n, float* a, const integer* lda, + float* s, float* u, const integer* ldu, + float* vt, const integer* ldvt, + float* work, const integer* lwork, integer* info); + + } + + inline integer gesvd (const char jobu, const char jobvt, + const integer m, const integer n, double* a, const integer lda, + double* s, double* u, const integer ldu, + double* vt, const integer ldvt, + double* work, const integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(dgesvd)(&jobu, &jobvt, &m, &n, a, &lda, s, u, &ldu, vt, &ldvt, work, &lwork, &info); + return info; + } + + inline integer gesvd (const char jobu, const char jobvt, + const integer m, const integer n, float* a, const integer lda, + float* s, float* u, const integer ldu, + float* vt, const integer ldvt, + float* work, const integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(sgesvd)(&jobu, &jobvt, &m, &n, a, &lda, s, u, &ldu, vt, &ldvt, work, &lwork, &info); + return info; + } + + } + + // ------------------------------------------------------------------------------------ + +/* -- LAPACK driver routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DGESVD computes the singular value decomposition (SVD) of a real */ +/* M-by-N matrix A, optionally computing the left and/or right singular */ +/* vectors. The SVD is written */ + +/* A = U * SIGMA * transpose(V) */ + +/* where SIGMA is an M-by-N matrix which is zero except for its */ +/* min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and */ +/* V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA */ +/* are the singular values of A; they are real and non-negative, and */ +/* are returned in descending order. The first min(m,n) columns of */ +/* U and V are the left and right singular vectors of A. */ + +/* Note that the routine returns V**T, not V. */ + +/* Arguments */ +/* ========= */ + +/* JOBU (input) CHARACTER*1 */ +/* Specifies options for computing all or part of the matrix U: */ +/* = 'A': all M columns of U are returned in array U: */ +/* = 'S': the first min(m,n) columns of U (the left singular */ +/* vectors) are returned in the array U; */ +/* = 'O': the first min(m,n) columns of U (the left singular */ +/* vectors) are overwritten on the array A; */ +/* = 'N': no columns of U (no left singular vectors) are */ +/* computed. */ + +/* JOBVT (input) CHARACTER*1 */ +/* Specifies options for computing all or part of the matrix */ +/* V**T: */ +/* = 'A': all N rows of V**T are returned in the array VT; */ +/* = 'S': the first min(m,n) rows of V**T (the right singular */ +/* vectors) are returned in the array VT; */ +/* = 'O': the first min(m,n) rows of V**T (the right singular */ +/* vectors) are overwritten on the array A; */ +/* = 'N': no rows of V**T (no right singular vectors) are */ +/* computed. */ + +/* JOBVT and JOBU cannot both be 'O'. */ + +/* M (input) INTEGER */ +/* The number of rows of the input matrix A. M >= 0. */ + +/* N (input) INTEGER */ +/* The number of columns of the input matrix A. N >= 0. */ + +/* A (input/output) DOUBLE PRECISION array, dimension (LDA,N) */ +/* On entry, the M-by-N matrix A. */ +/* On exit, */ +/* if JOBU = 'O', A is overwritten with the first min(m,n) */ +/* columns of U (the left singular vectors, */ +/* stored columnwise); */ +/* if JOBVT = 'O', A is overwritten with the first min(m,n) */ +/* rows of V**T (the right singular vectors, */ +/* stored rowwise); */ +/* if JOBU .ne. 'O' and JOBVT .ne. 'O', the contents of A */ +/* are destroyed. */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. LDA >= max(1,M). */ + +/* S (output) DOUBLE PRECISION array, dimension (min(M,N)) */ +/* The singular values of A, sorted so that S(i) >= S(i+1). */ + +/* U (output) DOUBLE PRECISION array, dimension (LDU,UCOL) */ +/* (LDU,M) if JOBU = 'A' or (LDU,min(M,N)) if JOBU = 'S'. */ +/* If JOBU = 'A', U contains the M-by-M orthogonal matrix U; */ +/* if JOBU = 'S', U contains the first min(m,n) columns of U */ +/* (the left singular vectors, stored columnwise); */ +/* if JOBU = 'N' or 'O', U is not referenced. */ + +/* LDU (input) INTEGER */ +/* The leading dimension of the array U. LDU >= 1; if */ +/* JOBU = 'S' or 'A', LDU >= M. */ + +/* VT (output) DOUBLE PRECISION array, dimension (LDVT,N) */ +/* If JOBVT = 'A', VT contains the N-by-N orthogonal matrix */ +/* V**T; */ +/* if JOBVT = 'S', VT contains the first min(m,n) rows of */ +/* V**T (the right singular vectors, stored rowwise); */ +/* if JOBVT = 'N' or 'O', VT is not referenced. */ + +/* LDVT (input) INTEGER */ +/* The leading dimension of the array VT. LDVT >= 1; if */ +/* JOBVT = 'A', LDVT >= N; if JOBVT = 'S', LDVT >= min(M,N). */ + +/* WORK (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) */ +/* On exit, if INFO = 0, WORK(1) returns the optimal LWORK; */ +/* if INFO > 0, WORK(2:MIN(M,N)) contains the unconverged */ +/* superdiagonal elements of an upper bidiagonal matrix B */ +/* whose diagonal is in S (not necessarily sorted). B */ +/* satisfies A = U * B * VT, so it has the same singular values */ +/* as A, and singular vectors related by U and VT. */ + +/* LWORK (input) INTEGER */ +/* The dimension of the array WORK. */ +/* LWORK >= MAX(1,3*MIN(M,N)+MAX(M,N),5*MIN(M,N)). */ +/* For good performance, LWORK should generally be larger. */ + +/* If LWORK = -1, then a workspace query is assumed; the routine */ +/* only calculates the optimal size of the WORK array, returns */ +/* this value as the first entry of the WORK array, and no error */ +/* message related to LWORK is issued by XERBLA. */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit. */ +/* < 0: if INFO = -i, the i-th argument had an illegal value. */ +/* > 0: if DBDSQR did not converge, INFO specifies how many */ +/* superdiagonals of an intermediate bidiagonal form B */ +/* did not converge to zero. See the description of WORK */ +/* above for details. */ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, long NR4, + long NC1, long NC2, long NC3, long NC4, + typename MM + > + int gesvd ( + const char jobu, + const char jobvt, + matrix<T,NR1,NC1,MM,column_major_layout>& a, + matrix<T,NR2,NC2,MM,column_major_layout>& s, + matrix<T,NR3,NC3,MM,column_major_layout>& u, + matrix<T,NR4,NC4,MM,column_major_layout>& vt + ) + { + matrix<T,0,1,MM,column_major_layout> work; + + const long m = a.nr(); + const long n = a.nc(); + s.set_size(std::min(m,n), 1); + + if (jobu == 'A') + u.set_size(m,m); + else if (jobu == 'S') + u.set_size(m, std::min(m,n)); + else + u.set_size(NR3?NR3:1, NC3?NC3:1); + + if (jobvt == 'A') + vt.set_size(n,n); + else if (jobvt == 'S') + vt.set_size(std::min(m,n), n); + else + vt.set_size(NR4?NR4:1, NC4?NC4:1); + + + if (jobu == 'O' || jobvt == 'O') + { + DLIB_CASSERT(false, "job == 'O' not supported"); + } + + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::gesvd(jobu, jobvt, a.nr(), a.nc(), &a(0,0), a.nr(), + &s(0,0), &u(0,0), u.nr(), &vt(0,0), vt.nr(), + &work_size, -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual SVD + info = binding::gesvd(jobu, jobvt, a.nr(), a.nc(), &a(0,0), a.nr(), + &s(0,0), &u(0,0), u.nr(), &vt(0,0), vt.nr(), + &work(0,0), work.size()); + + return info; + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, long NR4, + long NC1, long NC2, long NC3, long NC4, + typename MM + > + int gesvd ( + char jobu, + char jobvt, + matrix<T,NR1,NC1,MM,row_major_layout>& a, + matrix<T,NR2,NC2,MM,row_major_layout>& s, + matrix<T,NR3,NC3,MM,row_major_layout>& u_, + matrix<T,NR4,NC4,MM,row_major_layout>& vt_ + ) + { + matrix<T,0,1,MM,row_major_layout> work; + + // Row major order matrices are transposed from LAPACK's point of view. + matrix<T,NR4,NC4,MM,row_major_layout>& u = vt_; + matrix<T,NR3,NC3,MM,row_major_layout>& vt = u_; + std::swap(jobu, jobvt); + + const long m = a.nc(); + const long n = a.nr(); + s.set_size(std::min(m,n), 1); + + if (jobu == 'A') + u.set_size(m,m); + else if (jobu == 'S') + u.set_size(std::min(m,n), m); + else + u.set_size(NR4?NR4:1, NC4?NC4:1); + + if (jobvt == 'A') + vt.set_size(n,n); + else if (jobvt == 'S') + vt.set_size(n, std::min(m,n)); + else + vt.set_size(NR3?NR3:1, NC3?NC3:1); + + if (jobu == 'O' || jobvt == 'O') + { + DLIB_CASSERT(false, "job == 'O' not supported"); + } + + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::gesvd(jobu, jobvt, m, n, &a(0,0), a.nc(), + &s(0,0), &u(0,0), u.nc(), &vt(0,0), vt.nc(), + &work_size, -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual SVD + info = binding::gesvd(jobu, jobvt, m, n, &a(0,0), a.nc(), + &s(0,0), &u(0,0), u.nc(), &vt(0,0), vt.nc(), + &work(0,0), work.size()); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_SVD_Hh_ + diff --git a/ml/dlib/dlib/matrix/lapack/getrf.h b/ml/dlib/dlib/matrix/lapack/getrf.h new file mode 100644 index 000000000..a1f0b139d --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/getrf.h @@ -0,0 +1,132 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_GETRF_Hh_ +#define DLIB_LAPACk_GETRF_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dgetrf) (integer* m, integer *n, double *a, + integer* lda, integer *ipiv, integer *info); + + void DLIB_FORTRAN_ID(sgetrf) (integer* m, integer *n, float *a, + integer* lda, integer *ipiv, integer *info); + + } + + inline int getrf (integer m, integer n, double *a, + integer lda, integer *ipiv) + { + integer info = 0; + DLIB_FORTRAN_ID(dgetrf)(&m, &n, a, &lda, ipiv, &info); + return info; + } + + inline int getrf (integer m, integer n, float *a, + integer lda, integer *ipiv) + { + integer info = 0; + DLIB_FORTRAN_ID(sgetrf)(&m, &n, a, &lda, ipiv, &info); + return info; + } + + + } + + // ------------------------------------------------------------------------------------ + + +/* -- LAPACK routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DGETRF computes an LU factorization of a general M-by-N matrix A */ +/* using partial pivoting with row interchanges. */ + +/* The factorization has the form */ +/* A = P * L * U */ +/* where P is a permutation matrix, L is lower triangular with unit */ +/* diagonal elements (lower trapezoidal if m > n), and U is upper */ +/* triangular (upper trapezoidal if m < n). */ + +/* This is the right-looking Level 3 BLAS version of the algorithm. */ + +/* Arguments */ +/* ========= */ + +/* M (input) INTEGER */ +/* The number of rows of the matrix A. M >= 0. */ + +/* N (input) INTEGER */ +/* The number of columns of the matrix A. N >= 0. */ + +/* A (input/output) DOUBLE PRECISION array, dimension (LDA,N) */ +/* On entry, the M-by-N matrix to be factored. */ +/* On exit, the factors L and U from the factorization */ +/* A = P*L*U; the unit diagonal elements of L are not stored. */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. LDA >= max(1,M). */ + +/* IPIV (output) INTEGER array, dimension (min(M,N)) */ +/* The pivot indices; for 1 <= i <= min(M,N), row i of the */ +/* matrix was interchanged with row IPIV(i). */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit */ +/* < 0: if INFO = -i, the i-th argument had an illegal value */ +/* > 0: if INFO = i, U(i,i) is exactly zero. The factorization */ +/* has been completed, but the factor U is exactly */ +/* singular, and division by zero will occur if it is used */ +/* to solve a system of equations. */ + + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, + long NC1, long NC2, + typename MM, + typename layout + > + int getrf ( + matrix<T,NR1,NC1,MM,column_major_layout>& a, + matrix<integer,NR2,NC2,MM,layout>& ipiv + ) + { + const long m = a.nr(); + const long n = a.nc(); + + ipiv.set_size(std::min(m,n), 1); + + // compute the actual decomposition + return binding::getrf(m, n, &a(0,0), a.nr(), &ipiv(0,0)); + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_GETRF_Hh_ + diff --git a/ml/dlib/dlib/matrix/lapack/ormqr.h b/ml/dlib/dlib/matrix/lapack/ormqr.h new file mode 100644 index 000000000..ab66ff4d2 --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/ormqr.h @@ -0,0 +1,224 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_ORMQR_Hh_ +#define DLIB_LAPACk_ORMQR_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dormqr) (char *side, char *trans, integer *m, integer *n, + integer *k, const double *a, integer *lda, const double *tau, + double * c_, integer *ldc, double *work, integer *lwork, + integer *info); + + void DLIB_FORTRAN_ID(sormqr) (char *side, char *trans, integer *m, integer *n, + integer *k, const float *a, integer *lda, const float *tau, + float * c_, integer *ldc, float *work, integer *lwork, + integer *info); + + } + + inline int ormqr (char side, char trans, integer m, integer n, + integer k, const double *a, integer lda, const double *tau, + double *c_, integer ldc, double *work, integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(dormqr)(&side, &trans, &m, &n, + &k, a, &lda, tau, + c_, &ldc, work, &lwork, &info); + return info; + } + + inline int ormqr (char side, char trans, integer m, integer n, + integer k, const float *a, integer lda, const float *tau, + float *c_, integer ldc, float *work, integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(sormqr)(&side, &trans, &m, &n, + &k, a, &lda, tau, + c_, &ldc, work, &lwork, &info); + return info; + } + + + + } + + // ------------------------------------------------------------------------------------ + +/* -- LAPACK routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DORMQR overwrites the general real M-by-N matrix C with */ + +/* SIDE = 'L' SIDE = 'R' */ +/* TRANS = 'N': Q * C C * Q */ +/* TRANS = 'T': Q**T * C C * Q**T */ + +/* where Q is a real orthogonal matrix defined as the product of k */ +/* elementary reflectors */ + +/* Q = H(1) H(2) . . . H(k) */ + +/* as returned by DGEQRF. Q is of order M if SIDE = 'L' and of order N */ +/* if SIDE = 'R'. */ + +/* Arguments */ +/* ========= */ + +/* SIDE (input) CHARACTER*1 */ +/* = 'L': apply Q or Q**T from the Left; */ +/* = 'R': apply Q or Q**T from the Right. */ + +/* TRANS (input) CHARACTER*1 */ +/* = 'N': No transpose, apply Q; */ +/* = 'T': Transpose, apply Q**T. */ + +/* M (input) INTEGER */ +/* The number of rows of the matrix C. M >= 0. */ + +/* N (input) INTEGER */ +/* The number of columns of the matrix C. N >= 0. */ + +/* K (input) INTEGER */ +/* The number of elementary reflectors whose product defines */ +/* the matrix Q. */ +/* If SIDE = 'L', M >= K >= 0; */ +/* if SIDE = 'R', N >= K >= 0. */ + +/* A (input) DOUBLE PRECISION array, dimension (LDA,K) */ +/* The i-th column must contain the vector which defines the */ +/* elementary reflector H(i), for i = 1,2,...,k, as returned by */ +/* DGEQRF in the first k columns of its array argument A. */ +/* A is modified by the routine but restored on exit. */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. */ +/* If SIDE = 'L', LDA >= max(1,M); */ +/* if SIDE = 'R', LDA >= max(1,N). */ + +/* TAU (input) DOUBLE PRECISION array, dimension (K) */ +/* TAU(i) must contain the scalar factor of the elementary */ +/* reflector H(i), as returned by DGEQRF. */ + +/* C (input/output) DOUBLE PRECISION array, dimension (LDC,N) */ +/* On entry, the M-by-N matrix C. */ +/* On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q. */ + +/* LDC (input) INTEGER */ +/* The leading dimension of the array C. LDC >= max(1,M). */ + +/* WORK (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) */ +/* On exit, if INFO = 0, WORK(1) returns the optimal LWORK. */ + +/* LWORK (input) INTEGER */ +/* The dimension of the array WORK. */ +/* If SIDE = 'L', LWORK >= max(1,N); */ +/* if SIDE = 'R', LWORK >= max(1,M). */ +/* For optimum performance LWORK >= N*NB if SIDE = 'L', and */ +/* LWORK >= M*NB if SIDE = 'R', where NB is the optimal */ +/* blocksize. */ + +/* If LWORK = -1, then a workspace query is assumed; the routine */ +/* only calculates the optimal size of the WORK array, returns */ +/* this value as the first entry of the WORK array, and no error */ +/* message related to LWORK is issued by XERBLA. */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit */ +/* < 0: if INFO = -i, the i-th argument had an illegal value */ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, + long NC1, long NC2, long NC3, + typename MM, + typename C_LAYOUT + > + int ormqr ( + char side, + char trans, + const matrix<T,NR1,NC1,MM,column_major_layout>& a, + const matrix<T,NR2,NC2,MM,column_major_layout>& tau, + matrix<T,NR3,NC3,MM,C_LAYOUT>& c + ) + { + long m = c.nr(); + long n = c.nc(); + const long k = a.nc(); + long ldc; + if (is_same_type<C_LAYOUT,column_major_layout>::value) + { + ldc = c.nr(); + } + else + { + // Since lapack expects c to be in column major layout we have to + // do something to make this work. Since a row major layout matrix + // will look just like a transposed C we can just swap a few things around. + + ldc = c.nc(); + swap(m,n); + + if (side == 'L') + side = 'R'; + else + side = 'L'; + + if (trans == 'T') + trans = 'N'; + else + trans = 'T'; + } + + matrix<T,0,1,MM,column_major_layout> work; + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::ormqr(side, trans, m, n, + k, &a(0,0), a.nr(), &tau(0,0), + &c(0,0), ldc, &work_size, -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual result + info = binding::ormqr(side, trans, m, n, + k, &a(0,0), a.nr(), &tau(0,0), + &c(0,0), ldc, &work(0,0), work.size()); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_ORMQR_Hh_ + diff --git a/ml/dlib/dlib/matrix/lapack/pbtrf.h b/ml/dlib/dlib/matrix/lapack/pbtrf.h new file mode 100644 index 000000000..23bcc127b --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/pbtrf.h @@ -0,0 +1,178 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_BDC_Hh_ +#define DLIB_LAPACk_BDC_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dpbtrf) (const char* uplo, const integer* n, const integer* kd, + double* ab, const integer* ldab, integer* info); + + void DLIB_FORTRAN_ID(spbtrf) (const char* uplo, const integer* n, const integer* kd, + float* ab, const integer* ldab, integer* info); + + } + + inline integer pbtrf (const char uplo, const integer n, const integer kd, + double* ab, const integer ldab) + { + integer info = 0; + DLIB_FORTRAN_ID(dpbtrf)(&uplo, &n, &kd, ab, &ldab, &info); + return info; + } + + inline integer pbtrf (const char uplo, const integer n, const integer kd, + float* ab, const integer ldab) + { + integer info = 0; + DLIB_FORTRAN_ID(spbtrf)(&uplo, &n, &kd, ab, &ldab, &info); + return info; + } + } + + // ------------------------------------------------------------------------------------ +/* DPBTRF(l) LAPACK routine (version 1.1) DPBTRF(l) + +NAME + DPBTRF - compute the Cholesky factorization of a real symmetric positive + definite band matrix A + +SYNOPSIS + + SUBROUTINE DPBTRF( UPLO, N, KD, AB, LDAB, INFO ) + + CHARACTER UPLO + + INTEGER INFO, KD, LDAB, N + + DOUBLE PRECISION AB( LDAB, * ) + +PURPOSE + DPBTRF computes the Cholesky factorization of a real symmetric positive + definite band matrix A. + + The factorization has the form + A = U**T * U, if UPLO = 'U', or + A = L * L**T, if UPLO = 'L', + where U is an upper triangular matrix and L is lower triangular. + +ARGUMENTS + + UPLO (input) CHARACTER*1 + = 'U': Upper triangle of A is stored; + = 'L': Lower triangle of A is stored. + + N (input) INTEGER + The order of the matrix A. N >= 0. + + KD (input) INTEGER + The number of superdiagonals of the matrix A if UPLO = 'U', or the + number of subdiagonals if UPLO = 'L'. KD >= 0. + + AB (input/output) DOUBLE PRECISION array, dimension (LDAB,N) + On entry, the upper or lower triangle of the symmetric band matrix + A, stored in the first KD+1 rows of the array. The j-th column of + A is stored in the j-th column of the array AB as follows: if UPLO + = 'U', AB(kd+1+i-j,j) = A(i,j) for max(1,j-kd)<=i<=j; if UPLO = + 'L', AB(1+i-j,j) = A(i,j) for j<=i<=min(n,j+kd). + + On exit, if INFO = 0, the triangular factor U or L from the Chole- + sky factorization A = U**T*U or A = L*L**T of the band matrix A, in + the same storage format as A. + + LDAB (input) INTEGER + The leading dimension of the array AB. LDAB >= KD+1. + + INFO (output) INTEGER + = 0: successful exit + < 0: if INFO = -i, the i-th argument had an illegal value + > 0: if INFO = i, the leading minor of order i is not positive + definite, and the factorization could not be completed. + +FURTHER DETAILS + The band storage scheme is illustrated by the following example, when N = + 6, KD = 2, and UPLO = 'U': + + On entry: On exit: + + * * a13 a24 a35 a46 * * u13 u24 u35 u46 + * a12 a23 a34 a45 a56 * u12 u23 u34 u45 u56 + a11 a22 a33 a44 a55 a66 u11 u22 u33 u44 u55 u66 + + Similarly, if UPLO = 'L' the format of A is as follows: + + On entry: On exit: + + a11 a22 a33 a44 a55 a66 l11 l22 l33 l44 l55 l66 + a21 a32 a43 a54 a65 * l21 l32 l43 l54 l65 * + a31 a42 a53 a64 * * l31 l42 l53 l64 * * + + Array elements marked * are not used by the routine. + + Contributed by + Peter Mayes and Giuseppe Radicati, IBM ECSEC, Rome, March 23, 1989 */ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NC1, + typename MM + > + int pbtrf ( + char uplo, matrix<T,NR1,NC1,MM,column_major_layout>& ab + ) + { + const long ldab = ab.nr(); + const long n = ab.nc(); + const long kd = ldab - 1; // assume fully packed + + int info = binding::pbtrf(uplo, n, kd, &ab(0,0), ldab); + + return info; + } + + // ------------------------------------------------------------------------------------ + + + template < + typename T, + long NR1, long NC1, + typename MM + > + int pbtrf ( + char uplo, matrix<T,NR1,NC1,MM,row_major_layout>& ab + ) + { + const long ldab = ab.nr(); + const long n = ab.nc(); + const long kd = ldab - 1; // assume fully packed + + matrix<T,NC1,NR1,MM,row_major_layout> abt = trans(ab); + + int info = binding::pbtrf(uplo, n, kd, &abt(0,0), ldab); + + ab = trans(abt); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_BDC_Hh_ + + diff --git a/ml/dlib/dlib/matrix/lapack/potrf.h b/ml/dlib/dlib/matrix/lapack/potrf.h new file mode 100644 index 000000000..b9d6a7cc8 --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/potrf.h @@ -0,0 +1,174 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_POTRF_Hh_ +#define DLIB_LAPACk_POTRF_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dpotrf) (char *uplo, integer *n, double *a, + integer* lda, integer *info); + + void DLIB_FORTRAN_ID(spotrf) (char *uplo, integer *n, float *a, + integer* lda, integer *info); + + } + + inline int potrf (char uplo, integer n, double *a, integer lda) + { + integer info = 0; + DLIB_FORTRAN_ID(dpotrf)(&uplo, &n, a, &lda, &info); + return info; + } + + inline int potrf (char uplo, integer n, float *a, integer lda) + { + integer info = 0; + DLIB_FORTRAN_ID(spotrf)(&uplo, &n, a, &lda, &info); + return info; + } + + } + + // ------------------------------------------------------------------------------------ + +/* -- LAPACK routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DPOTRF computes the Cholesky factorization of a real symmetric */ +/* positive definite matrix A. */ + +/* The factorization has the form */ +/* A = U**T * U, if UPLO = 'U', or */ +/* A = L * L**T, if UPLO = 'L', */ +/* where U is an upper triangular matrix and L is lower triangular. */ + +/* This is the block version of the algorithm, calling Level 3 BLAS. */ + +/* Arguments */ +/* ========= */ + +/* UPLO (input) CHARACTER*1 */ +/* = 'U': Upper triangle of A is stored; */ +/* = 'L': Lower triangle of A is stored. */ + +/* N (input) INTEGER */ +/* The order of the matrix A. N >= 0. */ + +/* A (input/output) DOUBLE PRECISION array, dimension (LDA,N) */ +/* On entry, the symmetric matrix A. If UPLO = 'U', the leading */ +/* N-by-N upper triangular part of A contains the upper */ +/* triangular part of the matrix A, and the strictly lower */ +/* triangular part of A is not referenced. If UPLO = 'L', the */ +/* leading N-by-N lower triangular part of A contains the lower */ +/* triangular part of the matrix A, and the strictly upper */ +/* triangular part of A is not referenced. */ + +/* On exit, if INFO = 0, the factor U or L from the Cholesky */ +/* factorization A = U**T*U or A = L*L**T. */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. LDA >= max(1,N). */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit */ +/* < 0: if INFO = -i, the i-th argument had an illegal value */ +/* > 0: if INFO = i, the leading minor of order i is not */ +/* positive definite, and the factorization could not be */ +/* completed. */ + + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, + long NC1, + typename MM + > + int potrf ( + char uplo, + matrix<T,NR1,NC1,MM,column_major_layout>& a + ) + { + // compute the actual decomposition + int info = binding::potrf(uplo, a.nr(), &a(0,0), a.nr()); + + // If it fails part way though the factorization then make sure + // the end of the matrix gets properly initialized with zeros. + if (info > 0) + { + if (uplo == 'L') + set_colm(a, range(info-1, a.nc()-1)) = 0; + else + set_rowm(a, range(info-1, a.nr()-1)) = 0; + } + + return info; + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, + long NC1, + typename MM + > + int potrf ( + char uplo, + matrix<T,NR1,NC1,MM,row_major_layout>& a + ) + { + // since we are working on a row major order matrix we need to ask + // LAPACK for the transpose of whatever the user asked for. + + if (uplo == 'L') + uplo = 'U'; + else + uplo = 'L'; + + // compute the actual decomposition + int info = binding::potrf(uplo, a.nr(), &a(0,0), a.nr()); + + // If it fails part way though the factorization then make sure + // the end of the matrix gets properly initialized with zeros. + if (info > 0) + { + if (uplo == 'U') + set_colm(a, range(info-1, a.nc()-1)) = 0; + else + set_rowm(a, range(info-1, a.nr()-1)) = 0; + } + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_POTRF_Hh_ + + diff --git a/ml/dlib/dlib/matrix/lapack/syev.h b/ml/dlib/dlib/matrix/lapack/syev.h new file mode 100644 index 000000000..0c9fd251a --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/syev.h @@ -0,0 +1,218 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_EV_Hh_ +#define DLIB_LAPACk_EV_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dsyev) (char *jobz, char *uplo, integer *n, double *a, + integer *lda, double *w, double *work, integer *lwork, + integer *info); + + void DLIB_FORTRAN_ID(ssyev) (char *jobz, char *uplo, integer *n, float *a, + integer *lda, float *w, float *work, integer *lwork, + integer *info); + + } + + inline int syev (char jobz, char uplo, integer n, double *a, + integer lda, double *w, double *work, integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(dsyev)(&jobz, &uplo, &n, a, + &lda, w, work, &lwork, &info); + return info; + } + + inline int syev (char jobz, char uplo, integer n, float *a, + integer lda, float *w, float *work, integer lwork) + { + integer info = 0; + DLIB_FORTRAN_ID(ssyev)(&jobz, &uplo, &n, a, + &lda, w, work, &lwork, &info); + return info; + } + + + } + + // ------------------------------------------------------------------------------------ + +/* -- LAPACK driver routine (version 3.1) -- */ +/* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. */ +/* November 2006 */ + +/* .. Scalar Arguments .. */ +/* .. */ +/* .. Array Arguments .. */ +/* .. */ + +/* Purpose */ +/* ======= */ + +/* DSYEV computes all eigenvalues and, optionally, eigenvectors of a */ +/* real symmetric matrix A. */ + +/* Arguments */ +/* ========= */ + +/* JOBZ (input) CHARACTER*1 */ +/* = 'N': Compute eigenvalues only; */ +/* = 'V': Compute eigenvalues and eigenvectors. */ + +/* UPLO (input) CHARACTER*1 */ +/* = 'U': Upper triangle of A is stored; */ +/* = 'L': Lower triangle of A is stored. */ + +/* N (input) INTEGER */ +/* The order of the matrix A. N >= 0. */ + +/* A (input/output) DOUBLE PRECISION array, dimension (LDA, N) */ +/* On entry, the symmetric matrix A. If UPLO = 'U', the */ +/* leading N-by-N upper triangular part of A contains the */ +/* upper triangular part of the matrix A. If UPLO = 'L', */ +/* the leading N-by-N lower triangular part of A contains */ +/* the lower triangular part of the matrix A. */ +/* On exit, if JOBZ = 'V', then if INFO = 0, A contains the */ +/* orthonormal eigenvectors of the matrix A. */ +/* If JOBZ = 'N', then on exit the lower triangle (if UPLO='L') */ +/* or the upper triangle (if UPLO='U') of A, including the */ +/* diagonal, is destroyed. */ + +/* LDA (input) INTEGER */ +/* The leading dimension of the array A. LDA >= max(1,N). */ + +/* W (output) DOUBLE PRECISION array, dimension (N) */ +/* If INFO = 0, the eigenvalues in ascending order. */ + +/* WORK (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) */ +/* On exit, if INFO = 0, WORK(1) returns the optimal LWORK. */ + +/* LWORK (input) INTEGER */ +/* The length of the array WORK. LWORK >= max(1,3*N-1). */ +/* For optimal efficiency, LWORK >= (NB+2)*N, */ +/* where NB is the blocksize for DSYTRD returned by ILAENV. */ + +/* If LWORK = -1, then a workspace query is assumed; the routine */ +/* only calculates the optimal size of the WORK array, returns */ +/* this value as the first entry of the WORK array, and no error */ +/* message related to LWORK is issued by XERBLA. */ + +/* INFO (output) INTEGER */ +/* = 0: successful exit */ +/* < 0: if INFO = -i, the i-th argument had an illegal value */ +/* > 0: if INFO = i, the algorithm failed to converge; i */ +/* off-diagonal elements of an intermediate tridiagonal */ +/* form did not converge to zero. */ + + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, + long NC1, long NC2, + typename MM + > + int syev ( + const char jobz, + const char uplo, + matrix<T,NR1,NC1,MM,column_major_layout>& a, + matrix<T,NR2,NC2,MM,column_major_layout>& w + ) + { + matrix<T,0,1,MM,column_major_layout> work; + + const long n = a.nr(); + + w.set_size(n,1); + + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::syev(jobz, uplo, n, &a(0,0), + a.nr(), &w(0,0), &work_size, -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual decomposition + info = binding::syev(jobz, uplo, n, &a(0,0), + a.nr(), &w(0,0), &work(0,0), work.size()); + + return info; + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, + long NC1, long NC2, + typename MM + > + int syev ( + char jobz, + char uplo, + matrix<T,NR1,NC1,MM,row_major_layout>& a, + matrix<T,NR2,NC2,MM,row_major_layout>& w + ) + { + matrix<T,0,1,MM,row_major_layout> work; + + if (uplo == 'L') + uplo = 'U'; + else + uplo = 'L'; + + const long n = a.nr(); + + w.set_size(n,1); + + + // figure out how big the workspace needs to be. + T work_size = 1; + int info = binding::syev(jobz, uplo, n, &a(0,0), + a.nc(), &w(0,0), &work_size, -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + + // compute the actual decomposition + info = binding::syev(jobz, uplo, n, &a(0,0), + a.nc(), &w(0,0), &work(0,0), work.size()); + + + a = trans(a); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_EV_Hh_ + + + + diff --git a/ml/dlib/dlib/matrix/lapack/syevr.h b/ml/dlib/dlib/matrix/lapack/syevr.h new file mode 100644 index 000000000..65190b3d8 --- /dev/null +++ b/ml/dlib/dlib/matrix/lapack/syevr.h @@ -0,0 +1,445 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LAPACk_EVR_Hh_ +#define DLIB_LAPACk_EVR_Hh_ + +#include "fortran_id.h" +#include "../matrix.h" + +namespace dlib +{ + namespace lapack + { + namespace binding + { + extern "C" + { + void DLIB_FORTRAN_ID(dsyevr) (char *jobz, char *range, char *uplo, integer *n, + double *a, integer *lda, double *vl, double *vu, integer * il, + integer *iu, double *abstol, integer *m, double *w, + double *z_, integer *ldz, integer *isuppz, double *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + + void DLIB_FORTRAN_ID(ssyevr) (char *jobz, char *range, char *uplo, integer *n, + float *a, integer *lda, float *vl, float *vu, integer * il, + integer *iu, float *abstol, integer *m, float *w, + float *z_, integer *ldz, integer *isuppz, float *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + } + + inline int syevr (char jobz, char range, char uplo, integer n, + double* a, integer lda, double vl, double vu, integer il, + integer iu, double abstol, integer *m, double *w, + double *z, integer ldz, integer *isuppz, double *work, + integer lwork, integer *iwork, integer liwork) + { + integer info = 0; + DLIB_FORTRAN_ID(dsyevr)(&jobz, &range, &uplo, &n, + a, &lda, &vl, &vu, &il, + &iu, &abstol, m, w, + z, &ldz, isuppz, work, + &lwork, iwork, &liwork, &info); + return info; + } + + inline int syevr (char jobz, char range, char uplo, integer n, + float* a, integer lda, float vl, float vu, integer il, + integer iu, float abstol, integer *m, float *w, + float *z, integer ldz, integer *isuppz, float *work, + integer lwork, integer *iwork, integer liwork) + { + integer info = 0; + DLIB_FORTRAN_ID(ssyevr)(&jobz, &range, &uplo, &n, + a, &lda, &vl, &vu, &il, + &iu, &abstol, m, w, + z, &ldz, isuppz, work, + &lwork, iwork, &liwork, &info); + return info; + } + + } + + // ------------------------------------------------------------------------------------ + + /* + +* -- LAPACK driver routine (version 3.1) -- +* Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. +* November 2006 +* +* .. Scalar Arguments .. + CHARACTER JOBZ, RANGE, UPLO + INTEGER IL, INFO, IU, LDA, LDZ, LIWORK, LWORK, M, N + DOUBLE PRECISION ABSTOL, VL, VU +* .. +* .. Array Arguments .. + INTEGER ISUPPZ( * ), IWORK( * ) + DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ), Z( LDZ, * ) +* .. +* +* Purpose +* ======= +* +* DSYEVR computes selected eigenvalues and, optionally, eigenvectors +* of a real symmetric matrix A. Eigenvalues and eigenvectors can be +* selected by specifying either a range of values or a range of +* indices for the desired eigenvalues. +* +* DSYEVR first reduces the matrix A to tridiagonal form T with a call +* to DSYTRD. Then, whenever possible, DSYEVR calls DSTEMR to compute +* the eigenspectrum using Relatively Robust Representations. DSTEMR +* computes eigenvalues by the dqds algorithm, while orthogonal +* eigenvectors are computed from various "good" L D L^T representations +* (also known as Relatively Robust Representations). Gram-Schmidt +* orthogonalization is avoided as far as possible. More specifically, +* the various steps of the algorithm are as follows. +* +* For each unreduced block (submatrix) of T, +* (a) Compute T - sigma I = L D L^T, so that L and D +* define all the wanted eigenvalues to high relative accuracy. +* This means that small relative changes in the entries of D and L +* cause only small relative changes in the eigenvalues and +* eigenvectors. The standard (unfactored) representation of the +* tridiagonal matrix T does not have this property in general. +* (b) Compute the eigenvalues to suitable accuracy. +* If the eigenvectors are desired, the algorithm attains full +* accuracy of the computed eigenvalues only right before +* the corresponding vectors have to be computed, see steps c) and d). +* (c) For each cluster of close eigenvalues, select a new +* shift close to the cluster, find a new factorization, and refine +* the shifted eigenvalues to suitable accuracy. +* (d) For each eigenvalue with a large enough relative separation compute +* the corresponding eigenvector by forming a rank revealing twisted +* factorization. Go back to (c) for any clusters that remain. +* +* The desired accuracy of the output can be specified by the input +* parameter ABSTOL. +* +* For more details, see DSTEMR's documentation and: +* - Inderjit S. Dhillon and Beresford N. Parlett: "Multiple representations +* to compute orthogonal eigenvectors of symmetric tridiagonal matrices," +* Linear Algebra and its Applications, 387(1), pp. 1-28, August 2004. +* - Inderjit Dhillon and Beresford Parlett: "Orthogonal Eigenvectors and +* Relative Gaps," SIAM Journal on Matrix Analysis and Applications, Vol. 25, +* 2004. Also LAPACK Working Note 154. +* - Inderjit Dhillon: "A new O(n^2) algorithm for the symmetric +* tridiagonal eigenvalue/eigenvector problem", +* Computer Science Division Technical Report No. UCB/CSD-97-971, +* UC Berkeley, May 1997. +* +* +* Note 1 : DSYEVR calls DSTEMR when the full spectrum is requested +* on machines which conform to the ieee-754 floating point standard. +* DSYEVR calls DSTEBZ and SSTEIN on non-ieee machines and +* when partial spectrum requests are made. +* +* Normal execution of DSTEMR may create NaNs and infinities and +* hence may abort due to a floating point exception in environments +* which do not handle NaNs and infinities in the ieee standard default +* manner. +* +* Arguments +* ========= +* +* JOBZ (input) CHARACTER*1 +* = 'N': Compute eigenvalues only; +* = 'V': Compute eigenvalues and eigenvectors. +* +* RANGE (input) CHARACTER*1 +* = 'A': all eigenvalues will be found. +* = 'V': all eigenvalues in the half-open interval (VL,VU] +* will be found. +* = 'I': the IL-th through IU-th eigenvalues will be found. +********** For RANGE = 'V' or 'I' and IU - IL < N - 1, DSTEBZ and +********** DSTEIN are called +* +* UPLO (input) CHARACTER*1 +* = 'U': Upper triangle of A is stored; +* = 'L': Lower triangle of A is stored. +* +* N (input) INTEGER +* The order of the matrix A. N >= 0. +* +* A (input/output) DOUBLE PRECISION array, dimension (LDA, N) +* On entry, the symmetric matrix A. If UPLO = 'U', the +* leading N-by-N upper triangular part of A contains the +* upper triangular part of the matrix A. If UPLO = 'L', +* the leading N-by-N lower triangular part of A contains +* the lower triangular part of the matrix A. +* On exit, the lower triangle (if UPLO='L') or the upper +* triangle (if UPLO='U') of A, including the diagonal, is +* destroyed. +* +* LDA (input) INTEGER +* The leading dimension of the array A. LDA >= max(1,N). +* +* VL (input) DOUBLE PRECISION +* VU (input) DOUBLE PRECISION +* If RANGE='V', the lower and upper bounds of the interval to +* be searched for eigenvalues. VL < VU. +* Not referenced if RANGE = 'A' or 'I'. +* +* IL (input) INTEGER +* IU (input) INTEGER +* If RANGE='I', the indices (in ascending order) of the +* smallest and largest eigenvalues to be returned. +* 1 <= IL <= IU <= N, if N > 0; IL = 1 and IU = 0 if N = 0. +* Not referenced if RANGE = 'A' or 'V'. +* +* ABSTOL (input) DOUBLE PRECISION +* The absolute error tolerance for the eigenvalues. +* An approximate eigenvalue is accepted as converged +* when it is determined to lie in an interval [a,b] +* of width less than or equal to +* +* ABSTOL + EPS * max( |a|,|b| ) , +* +* where EPS is the machine precision. If ABSTOL is less than +* or equal to zero, then EPS*|T| will be used in its place, +* where |T| is the 1-norm of the tridiagonal matrix obtained +* by reducing A to tridiagonal form. +* +* See "Computing Small Singular Values of Bidiagonal Matrices +* with Guaranteed High Relative Accuracy," by Demmel and +* Kahan, LAPACK Working Note #3. +* +* If high relative accuracy is important, set ABSTOL to +* DLAMCH( 'Safe minimum' ). Doing so will guarantee that +* eigenvalues are computed to high relative accuracy when +* possible in future releases. The current code does not +* make any guarantees about high relative accuracy, but +* future releases will. See J. Barlow and J. Demmel, +* "Computing Accurate Eigensystems of Scaled Diagonally +* Dominant Matrices", LAPACK Working Note #7, for a discussion +* of which matrices define their eigenvalues to high relative +* accuracy. +* +* M (output) INTEGER +* The total number of eigenvalues found. 0 <= M <= N. +* If RANGE = 'A', M = N, and if RANGE = 'I', M = IU-IL+1. +* +* W (output) DOUBLE PRECISION array, dimension (N) +* The first M elements contain the selected eigenvalues in +* ascending order. +* +* Z (output) DOUBLE PRECISION array, dimension (LDZ, max(1,M)) +* If JOBZ = 'V', then if INFO = 0, the first M columns of Z +* contain the orthonormal eigenvectors of the matrix A +* corresponding to the selected eigenvalues, with the i-th +* column of Z holding the eigenvector associated with W(i). +* If JOBZ = 'N', then Z is not referenced. +* Note: the user must ensure that at least max(1,M) columns are +* supplied in the array Z; if RANGE = 'V', the exact value of M +* is not known in advance and an upper bound must be used. +* Supplying N columns is always safe. +* +* LDZ (input) INTEGER +* The leading dimension of the array Z. LDZ >= 1, and if +* JOBZ = 'V', LDZ >= max(1,N). +* +* ISUPPZ (output) INTEGER array, dimension ( 2*max(1,M) ) +* The support of the eigenvectors in Z, i.e., the indices +* indicating the nonzero elements in Z. The i-th eigenvector +* is nonzero only in elements ISUPPZ( 2*i-1 ) through +* ISUPPZ( 2*i ). +********** Implemented only for RANGE = 'A' or 'I' and IU - IL = N - 1 +* +* WORK (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) +* On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +* +* LWORK (input) INTEGER +* The dimension of the array WORK. LWORK >= max(1,26*N). +* For optimal efficiency, LWORK >= (NB+6)*N, +* where NB is the max of the blocksize for DSYTRD and DORMTR +* returned by ILAENV. +* +* If LWORK = -1, then a workspace query is assumed; the routine +* only calculates the optimal size of the WORK array, returns +* this value as the first entry of the WORK array, and no error +* message related to LWORK is issued by XERBLA. +* +* IWORK (workspace/output) INTEGER array, dimension (MAX(1,LIWORK)) +* On exit, if INFO = 0, IWORK(1) returns the optimal LWORK. +* +* LIWORK (input) INTEGER +* The dimension of the array IWORK. LIWORK >= max(1,10*N). +* +* If LIWORK = -1, then a workspace query is assumed; the +* routine only calculates the optimal size of the IWORK array, +* returns this value as the first entry of the IWORK array, and +* no error message related to LIWORK is issued by XERBLA. +* +* INFO (output) INTEGER +* = 0: successful exit +* < 0: if INFO = -i, the i-th argument had an illegal value +* > 0: Internal error +* +* Further Details +* =============== +* +* Based on contributions by +* Inderjit Dhillon, IBM Almaden, USA +* Osni Marques, LBNL/NERSC, USA +* Ken Stanley, Computer Science Division, University of +* California at Berkeley, USA +* Jason Riedy, Computer Science Division, University of +* California at Berkeley, USA +* +* ===================================================================== + + */ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, long NR4, + long NC1, long NC2, long NC3, long NC4, + typename MM + > + int syevr ( + const char jobz, + const char range, + const char uplo, + matrix<T,NR1,NC1,MM,column_major_layout>& a, + const double vl, + const double vu, + const integer il, + const integer iu, + const double abstol, + integer& num_eigenvalues_found, + matrix<T,NR2,NC2,MM,column_major_layout>& w, + matrix<T,NR3,NC3,MM,column_major_layout>& z, + matrix<integer,NR4,NC4,MM,column_major_layout>& isuppz + ) + { + matrix<T,0,1,MM,column_major_layout> work; + matrix<integer,0,1,MM,column_major_layout> iwork; + + const long n = a.nr(); + + w.set_size(n,1); + + isuppz.set_size(2*n, 1); + + if (jobz == 'V') + { + z.set_size(n,n); + } + else + { + z.set_size(NR3?NR3:1, NC3?NC3:1); + } + + // figure out how big the workspace needs to be. + T work_size = 1; + integer iwork_size = 1; + int info = binding::syevr(jobz, range, uplo, n, &a(0,0), + a.nr(), vl, vu, il, iu, abstol, &num_eigenvalues_found, + &w(0,0), &z(0,0), z.nr(), &isuppz(0,0), &work_size, -1, + &iwork_size, -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + if (iwork.size() < iwork_size) + iwork.set_size(iwork_size, 1); + + // compute the actual decomposition + info = binding::syevr(jobz, range, uplo, n, &a(0,0), + a.nr(), vl, vu, il, iu, abstol, &num_eigenvalues_found, + &w(0,0), &z(0,0), z.nr(), &isuppz(0,0), &work(0,0), work.size(), + &iwork(0,0), iwork.size()); + + + return info; + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, long NR3, long NR4, + long NC1, long NC2, long NC3, long NC4, + typename MM + > + int syevr ( + const char jobz, + const char range, + char uplo, + matrix<T,NR1,NC1,MM,row_major_layout>& a, + const double vl, + const double vu, + const integer il, + const integer iu, + const double abstol, + integer& num_eigenvalues_found, + matrix<T,NR2,NC2,MM,row_major_layout>& w, + matrix<T,NR3,NC3,MM,row_major_layout>& z, + matrix<integer,NR4,NC4,MM,row_major_layout>& isuppz + ) + { + matrix<T,0,1,MM,row_major_layout> work; + matrix<integer,0,1,MM,row_major_layout> iwork; + + if (uplo == 'L') + uplo = 'U'; + else + uplo = 'L'; + + const long n = a.nr(); + + w.set_size(n,1); + + isuppz.set_size(2*n, 1); + + if (jobz == 'V') + { + z.set_size(n,n); + } + else + { + z.set_size(NR3?NR3:1, NC3?NC3:1); + } + + // figure out how big the workspace needs to be. + T work_size = 1; + integer iwork_size = 1; + int info = binding::syevr(jobz, range, uplo, n, &a(0,0), + a.nc(), vl, vu, il, iu, abstol, &num_eigenvalues_found, + &w(0,0), &z(0,0), z.nc(), &isuppz(0,0), &work_size, -1, + &iwork_size, -1); + + if (info != 0) + return info; + + if (work.size() < work_size) + work.set_size(static_cast<long>(work_size), 1); + if (iwork.size() < iwork_size) + iwork.set_size(iwork_size, 1); + + // compute the actual decomposition + info = binding::syevr(jobz, range, uplo, n, &a(0,0), + a.nc(), vl, vu, il, iu, abstol, &num_eigenvalues_found, + &w(0,0), &z(0,0), z.nc(), &isuppz(0,0), &work(0,0), work.size(), + &iwork(0,0), iwork.size()); + + z = trans(z); + + return info; + } + + // ------------------------------------------------------------------------------------ + + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LAPACk_EVR_Hh_ + + + 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_ + diff --git a/ml/dlib/dlib/matrix/matrix_abstract.h b/ml/dlib/dlib/matrix/matrix_abstract.h new file mode 100644 index 000000000..0d05ce981 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_abstract.h @@ -0,0 +1,857 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_ABSTRACT_ +#ifdef DLIB_MATRIx_ABSTRACT_ + +#include "matrix_exp_abstract.h" +#include "../serialize.h" +#include "../algs.h" +#include "matrix_data_layout_abstract.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + /* + Note that these operator prototypes are not correct C++ (the real versions, which + you can see in the implementation are really complex and so probably would + distract/confuse people if shown here). Think of this as just a list of the + operators available to you and what they do. + */ + + const matrix_exp operator* ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1.nc() == m2.nr() + - m1.size() > 0 && m2.size() > 0 + (you can't multiply any sort of empty matrices together) + - m1 and m2 both contain elements of the same type + ensures + - returns the result of doing the matrix multiplication m1*m2. The resulting + matrix will have m1.nr() rows and m2.nc() columns. + !*/ + + const matrix_exp operator+ ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1.nr() == m2.nr() + - m1.nc() == m2.nc() + - m1 and m2 both contain elements of the same type + ensures + - returns a matrix R such that for all valid r and c: + R(r,c) == m1(r,c) + m2(r,c) + (i.e. returns the result of doing a pairwise addition of the matrices m1 and m2.) + The resulting matrix will have the same dimensions as the originals. + !*/ + + const matrix_exp operator- ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1.nr() == m2.nr() + - m1.nc() == m2.nc() + - m1 and m2 both contain elements of the same type + ensures + - returns a matrix R such that for all valid r and c: + R(r,c) == m1(r,c) - m2(r,c) + (i.e. returns the result of doing a pairwise subtraction of the matrices m1 and m2.) + The resulting matrix will have the same dimensions as the originals. + !*/ + + template <typename T> + const matrix_exp operator* ( + const matrix_exp& m, + const T& value + ); + /*! + ensures + - returns the result of multiplying all the elements of matrix m by the given + scalar value. The resulting matrix will have the same dimensions as m. + !*/ + + template <typename T> + const matrix_exp operator* ( + const T& value, + const matrix_exp& m + ); + /*! + ensures + - returns the result of multiplying all the elements of matrix m by the given + scalar value. The resulting matrix will have the same dimensions as m. + !*/ + + const matrix_exp operator- ( + const matrix_exp& m + ); + /*! + ensures + - returns -1*m + !*/ + + template <typename T> + const matrix_exp operator/ ( + const matrix_exp& m, + const T& value + ); + /*! + ensures + - returns the result of dividing all the elements of matrix m by the given + scalar value. The resulting matrix will have the same dimensions as m. + !*/ + + template <typename T> + const matrix_exp operator/ ( + const T& value, + const matrix_exp& m + ); + /*! + ensures + - returns the result of dividing the given scalar value by all the elements + of matrix m. The resulting matrix will have the same dimensions as m. + !*/ + + template <typename T> + const matrix_exp operator+ ( + const matrix_exp& m, + const T& value + ); + /*! + ensures + - returns the result of adding value to all the elements of matrix m. + The resulting matrix will have the same dimensions as m. + !*/ + + template <typename T> + const matrix_exp operator+ ( + const T& value, + const matrix_exp& m + ); + /*! + ensures + - returns the result of adding value to all the elements of matrix m. + The resulting matrix will have the same dimensions as m. + !*/ + + template <typename T> + const matrix_exp operator- ( + const matrix_exp& m, + const T& value + ); + /*! + ensures + - returns the result of subtracting value from all the elements of matrix m. + The resulting matrix will have the same dimensions as m. + !*/ + + template <typename T> + const matrix_exp operator- ( + const T& value, + const matrix_exp& m + ); + /*! + ensures + - Returns a matrix M such that: + - M has the same dimensions as m + - M contains the same type of element as m + - for all valid r and c: + - M(r,c) == value - m(r,c) + !*/ + + bool operator== ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + ensures + - if (m1.nr() == m2.nr() && m1.nc() == m2.nc() && + for all valid r and c: m1(r,c) == m2(r,c) ) then + - returns true + - else + - returns false + !*/ + + bool operator!= ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + ensures + - returns !(m1 == m2) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long num_rows = 0, + long num_cols = 0, + typename mem_manager = default_memory_manager, + typename layout = row_major_layout + > + class matrix : public matrix_exp<matrix<T,num_rows,num_cols,mem_manager,layout> > + { + /*! + REQUIREMENTS ON num_rows and num_cols + both must be bigger than or equal to 0 + + REQUIREMENTS ON mem_manager + must be an implementation of memory_manager/memory_manager_kernel_abstract.h or + must be an implementation of memory_manager_global/memory_manager_global_kernel_abstract.h or + must be an implementation of memory_manager_stateless/memory_manager_stateless_kernel_abstract.h + mem_manager::type can be set to anything. + + REQUIREMENTS ON layout + Must be either row_major_layout or column_major_layout + + INITIAL VALUE + - if (num_rows > 0) then + - nr() == num_rows + - else + - nr() == 0 + + - if (num_cols > 0) then + - nc() == num_cols + - else + - nc() == 0 + + WHAT THIS OBJECT REPRESENTS + This object represents a matrix of nr() rows and nc() columns. This object + is also a matrix_exp. Thus it can be used in all of the above + global operators. + + The number of rows and columns of this object are determined by the template + arguments num_rows and num_cols. If num_rows or num_cols are 0 then + the matrix starts out empty (i.e. nr() == 0 and nc() == 0) and you may change + its size via the set_size() member function. + + Setting num_rows or num_cols to something other than 0 causes that dimension + to have a fixed size. Setting a fixed size at compile time is useful because + any errors related to operating on matrices with incompatible dimensions will + be detected at compile time. It also allows the compiler to perform loop + unrolling which can result in substantially faster code. + + Also note that the elements of this matrix are laid out in memory by the layout + object supplied as a template argument to this class. The row_major_layout + sets elements down contiguously in memory and in row major order. Additionally, + all memory allocations are performed using the memory manager object supplied as + a template argument to this class. + !*/ + + public: + typedef T type; + typedef mem_manager mem_manager_type; + typedef layout layout_type; + const static long NR = num_rows; + const static long NC = num_cols; + const static long cost = 1; + typedef T* iterator; + typedef const T* const_iterator; + + matrix ( + ); + /*! + ensures + - #*this is properly initialized + - #aliases(*this) == true + - #ref().aliases(*this) == true + !*/ + + explicit matrix ( + long length + ); + /*! + requires + - NR == 1 || NC == 1 (i.e. this must be a column or row vector) + - length >= 0 + - if (NR == 1 && NC > 0) then + - length == NC + - if (NC == 1 && NR > 0) then + - length == NR + ensures + - #*this is properly initialized + - #aliases(*this) == true + - #ref().aliases(*this) == true + - if (NR == 1) then + - #nr() == 1 + - #nc() == length + - else + - #nr() == length + - #nc() == 1 + !*/ + + matrix ( + long rows, + long cols + ); + /*! + requires + - rows == NR || NR == 0 + - cols == NC || NC == 0 + - rows >= 0 && cols >= 0 + ensures + - #*this is properly initialized + - #aliases(*this) == true + - #ref().aliases(*this) == true + - #nr() == rows + - #nc() == cols + !*/ + + template <typename EXP> + matrix ( + const matrix_exp<EXP>& m + ); + /*! + requires + - matrix_exp<EXP>::type == T + (i.e. m contains the same type as *this does) + - if (NR != 0) then NR == m.nr() + - if (NC != 0) then NC == m.nc() + ensures + - #*this == m + - #aliases(*this) == true + - #ref().aliases(*this) == true + !*/ + + template <typename U, size_t len> + explicit matrix ( + U (&array)[len] + ); + /*! + requires + - NR != 0 && NC != 0 (i.e. you can only use this constructor on statically sized matrices) + - len == nr()*nc() (i.e. the array you give here must be the right size) + ensures + - for all valid r and c: + #(*this)(r,c) == array[r*nc() + c] + (i.e. initializes this matrix with the contents of the given array) + - #aliases(*this) == true + - #ref().aliases(*this) == true + !*/ + + matrix( + const std::initializer_list<T>& l + ); + /*! + requires + - This matrix is capable of having a size() == l.size(). Therefore, if + NR*NC != 0 then l.size() must equal NR*NC. Alternatively, if NR or NC is + != 0 then l.size() must be a multiple of the non-zero NR or NC. + ensures + - #size() == l.size() + - The contents of l are enumerated and read into the matrix in row major order. + - if (NR != 0) then + - #nr() == NR + - #nc() == l.size()/NR + - if (NC != 0) then + - #nr() == l.size()/NC + - #nc() == NC + - if (NR*NC==0) then + - #nr() == l.size() + - #nc() == 1 + - #aliases(*this) == true + - #ref().aliases(*this) == true + !*/ + + T& operator() ( + long r, + long c + ); + /*! + requires + - 0 <= r < nr() + - 0 <= c < nc() + ensures + - returns a reference to the value at the given row and column in + this matrix. + !*/ + + const T& operator() ( + long r, + long c + ) const; + /*! + requires + - 0 <= r < nr() + - 0 <= c < nc() + ensures + - returns a const reference to the value at the given row and column in + this matrix. + !*/ + + T& operator() ( + long i + ); + /*! + requires + - nc() == 1 || nr() == 1 (i.e. this must be a column or row vector) + - 0 <= i < size() + ensures + - if (nc() == 1) then + - returns a reference to (*this)(i,0) + - else + - returns a reference to (*this)(0,i) + !*/ + + const T& operator() ( + long i + ) const; + /*! + requires + - nc() == 1 || nr() == 1 (i.e. this must be a column or row vector) + - 0 <= i < size() + ensures + - if (nc() == 1) then + - returns a reference to (*this)(i,0) + - else + - returns a reference to (*this)(0,i) + !*/ + + operator const type ( + ) const; + /*! + requires + - nr() == 1 + - nc() == 1 + ensures + - returns (*this)(0,0) + !*/ + + long nr( + ) const; + /*! + ensures + - returns the number of rows in this matrix + !*/ + + long nc( + ) const; + /*! + ensures + - returns the number of columns in this matrix + !*/ + + long size ( + ) const; + /*! + ensures + - returns nr()*nc() + !*/ + + void set_size ( + long rows, + long cols + ); + /*! + requires + - rows == NR || NR == 0 + - cols == NC || NC == 0 + - rows >= 0 && cols >= 0 + ensures + - #nr() == rows + - #nc() == cols + !*/ + + void set_size ( + long length + ); + /*! + requires + - NR == 1 || NC == 1 (i.e. this must be a column or row vector) + - length >= 0 + - if (NR == 1 && NC > 0) then + - length == NC + - if (NC == 1 && NR > 0) then + - length == NR + ensures + - if (NR == 1) then + - #nr() == 1 + - #nc() == length + - else + - #nr() == length + - #nc() == 1 + !*/ + + template <typename U, size_t len> + matrix& operator= ( + U (&array)[len] + ); + /*! + requires + - len == nr()*nc() (i.e. the array you give here must be the right size) + ensures + - for all valid r and c: + #(*this)(r,c) == array[r*nc() + c] + (i.e. loads this matrix with the contents of the given array) + - returns *this + !*/ + + matrix& operator=( + const std::initializer_list<T>& l + ); + /*! + requires + - This matrix is capable of having a size() == l.size(). Therefore, if + NR*NC != 0 then l.size() must equal NR*NC. Alternatively, if NR or NC is + != 0 then l.size() must be a multiple of the non-zero NR or NC. + ensures + - Assigns the contents of l to *this by performing: matrix(l).swap(*this) + - returns *this + !*/ + + template <typename EXP> + matrix& operator= ( + const matrix_exp<EXP>& m + ); + /*! + requires + - matrix_exp<EXP>::type == T + (i.e. m contains the same type as *this does) + - if (NR != 0) then NR == m.nr() + - if (NC != 0) then NC == m.nc() + ensures + - copies the given matrix expression m to *this + - returns *this + !*/ + + template <typename EXP> + matrix& operator += ( + const matrix_exp<EXP>& m + ); + /*! + requires + - matrix_exp<EXP>::type == T + - One of the following is true: + - nr() == m.nr() && nc() == m.nc() + - size() == 0 + (i.e. this matrix must have matching dimensions or it must be empty) + ensures + - if (nr() == m.nr() && nc() == m.nc()) then + - #(*this) == *this + m + - else + - #(*this) == m + (i.e. if the dimensions don't match then this function performs a + normal assignment) + - returns *this + !*/ + + template <typename EXP> + matrix& operator -= ( + const matrix_exp<EXP>& m + ); + /*! + requires + - matrix_exp<EXP>::type == T + - One of the following is true: + - nr() == m.nr() && nc() == m.nc() + - size() == 0 + (i.e. this matrix must have matching dimensions or it must be empty) + ensures + - if (nr() == m.nr() && nc() == m.nc()) then + - #(*this) == *this - m + - else + - #(*this) == -m + - returns *this + !*/ + + template <typename EXP> + matrix& operator *= ( + const matrix_exp<EXP>& m + ); + /*! + requires + - matrix_exp<EXP>::type == T + (i.e. m must contain the same type of element as *this) + - nc() == m.nr() + - size() > 0 && m.size() > 0 + (you can't multiply any sort of empty matrices together) + ensures + - #(*this) == *this * m + - returns *this + !*/ + + matrix& operator *= ( + const T& a + ); + /*! + ensures + - #(*this) == *this * a + - returns *this + !*/ + + matrix& operator /= ( + const T& a + ); + /*! + ensures + - #(*this) == *this / a + - returns *this + !*/ + + matrix& operator += ( + const T& a + ); + /*! + ensures + - #(*this) == *this + a + - returns *this + !*/ + + matrix& operator -= ( + const T& a + ); + /*! + ensures + - #(*this) == *this - a + - returns *this + !*/ + + const literal_assign_helper operator = ( + const T& val + ); + /*! + This function is somewhat different than all the others defined in this file. + The purpose of this function is to enable you to easily initialize a matrix object. + For example: + matrix<double> m(2,3); + m = 1,2,3, + 4,5,6; + + The above code creates a matrix m with 2 rows and 3 columns and sets it so that + it contains the matrix | 1 2 3 | + | 4 5 6 | + + You can also use this function to assign to all elements of a matrix. So + saying m = 3; would assign all elements of m equal to 3. + + Note that to use this method of assignment it is required that you supply + exactly m.size() or 1 values so that the matrix is fully initialized. Supplying + fewer or more than that is an error that will cause a dlib::fatal_error to be + thrown. + + Note also that using an expression of the form m = scalar; when m.size() == 0 + is legal but has no effect on m. + !*/ + + void swap ( + matrix& item + ); + /*! + ensures + - swaps *this and item + !*/ + + iterator begin( + ); + /*! + ensures + - returns a random access iterator pointing to the first element in this + matrix. + - The iterator will iterate over the elements of the matrix in row major + order if layout is row_major_layout or in column major order if layout is + column_major_layout. + !*/ + + iterator end( + ); + /*! + ensures + - returns a random access iterator pointing to one past the end of the last + element in this matrix. + !*/ + + const_iterator begin( + ) const; + /*! + ensures + - returns a random access iterator pointing to the first element in this + matrix. + - The iterator will iterate over the elements of the matrix in row major + order if layout is row_major_layout or in column major order if layout is + column_major_layout. + !*/ + + const_iterator end( + ) const; + /*! + ensures + - returns a random access iterator pointing to one past the end of the last + element in this matrix. + !*/ + }; + +// ---------------------------------------------------------------------------------------- + + /*!A matrix_colmajor + This is just a typedef of the matrix object that uses column major layout. + !*/ + typedef matrix<double,0,0,default_memory_manager,column_major_layout> matrix_colmajor; + + /*!A fmatrix_colmajor + This is just a typedef of the matrix object that uses column major layout. + !*/ + typedef matrix<float,0,0,default_memory_manager,column_major_layout> fmatrix_colmajor; + +// ---------------------------------------------------------------------------------------- +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); } + /*! + Provides a global swap function + !*/ + + template < + typename T, + long NR, + long NC, + typename mm, + typename l + > + void serialize ( + const matrix<T,NR,NC,mm,l>& item, + std::ostream& out + ); + /*! + Provides serialization support. Note that the serialization formats used by the + dlib::matrix and dlib::array2d objects are compatible. That means you can load the + serialized data from one into another and it will work properly. + !*/ + + template < + typename T, + long NR, + long NC, + typename mm, + typename l + > + void deserialize ( + matrix<T,NR,NC,mm,l>& item, + std::istream& in + ); + /*! + Provides deserialization support + !*/ + + template < + typename EXP + > + std::ostream& operator<< ( + std::ostream& out, + const matrix_exp<EXP>& m + ); + /*! + ensures + - writes m to the given out stream in a form suitable for human consumption. + - returns 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 + ); + /*! + ensures + - Tries to read a matrix from the given input stream and store it into #m. + - The format expected is the text format output by the above operator<<(). + That is, the format should be a grid of text such as: + 2 3 4 + 5 2 6 + - The separation between numbers can be any number of whitespace characters or + commas. + - The matrix data is assumed to end upon the first blank line or end-of-file, + whichever comes first. This means you can create an input stream with + multiple matrices in it by separating them with empty lines. + - returns in. + - If there was a formatting error or something which prevents the input data + from being parsed into a matrix then #in.fail() == true. + !*/ + + /*!A csv + This object is used to define an io manipulator for matrix expressions. In + particular, you can write statements like: + cout << csv << yourmatrix; + and have it print the matrix with commas separating each element. + !*/ + some_undefined_iomnaip_type csv; + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + class const_temp_matrix : public matrix_exp<const_temp_matrix<EXP> >, noncopyable + { + /*! + REQUIREMENTS ON EXP + - must be an object that inherits publicly from matrix_exp. + + WHAT THIS OBJECT REPRESENTS + This object represents a copy of a matrix expression. The twist + is that it only actually makes a copy of its input matrix expression + if that matrix expression is costly to evaluate. If it has + low cost then this object just stores a reference. + + This class is useful in cases where you write a function that + takes a matrix_exp object as input and you want to do some + intensive computation that looks at each element of that matrix_exp + many times. If the input matrix_exp has a high cost then you want + to store it into a temporary matrix. But if it has low cost then + it is faster if you just use a reference to it. The const_temp_matrix + makes doing this easy. + !*/ + public: + + const_temp_matrix ( + const matrix_exp<EXP>& item + ); + /*! + ensures + - #*this == item + - if (EXP::cost <= 1) then + - this const_temp_matrix stores a reference to the item matrix + - else + - this const_temp_matrix creates a temporary matrix and copies + item into it + !*/ + + const_temp_matrix ( + const EXP& item + ); + /*! + ensures + - #*this == item + - if (EXP::cost <= 1) then + - this const_temp_matrix stores a reference to the item matrix + - else + - this const_temp_matrix creates a temporary matrix and copies + item into it + !*/ + }; + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_ABSTRACT_ + diff --git a/ml/dlib/dlib/matrix/matrix_assign.h b/ml/dlib/dlib/matrix/matrix_assign.h new file mode 100644 index 000000000..da53050b1 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_assign.h @@ -0,0 +1,978 @@ +// Copyright (C) 2008 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_ASSIGn_ +#define DLIB_MATRIx_ASSIGn_ + +#include "matrix.h" +#include "matrix_utilities.h" +#include "matrix_subexp.h" +#include "../enable_if.h" +#include "matrix_assign_fwd.h" +#include "matrix_default_mul.h" +#include "matrix_conj_trans.h" +#include "matrix_mat.h" + +namespace dlib +{ + /* + This file contains some templates that are used inside the matrix_blas_bindings.h + file to bind various matrix expressions to optimized code for carrying them out. + */ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + namespace blas_bindings + { + + // ------------------------------------------------------------------------------------ + + template <typename T> + void zero_matrix ( + T& m + ) + { + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + m(r,c) = 0; + } + } + } + + // ------------------------------------------------------------------------------------ + + // This template struct is used to tell us if a matrix expression contains a matrix multiply. + template <typename T> + struct has_matrix_multiply + { + const static bool value = false; + }; + + template <typename T, typename U> + struct has_matrix_multiply<matrix_multiply_exp<T,U> > + { const static bool value = true; }; + + template <typename T, typename U> + struct has_matrix_multiply<matrix_add_exp<T,U> > + { const static bool value = has_matrix_multiply<T>::value || has_matrix_multiply<U>::value; }; + + template <typename T, typename U> + struct has_matrix_multiply<matrix_subtract_exp<T,U> > + { const static bool value = has_matrix_multiply<T>::value || has_matrix_multiply<U>::value; }; + + template <typename T, bool Tb> + struct has_matrix_multiply<matrix_mul_scal_exp<T,Tb> > + { const static bool value = true; }; + + template <typename T> + struct has_matrix_multiply<matrix_div_scal_exp<T> > + { const static bool value = has_matrix_multiply<T>::value; }; + + template <typename T> + struct has_matrix_multiply<matrix_op<T> > + { const static bool value = has_matrix_multiply<T>::value; }; + + template <typename T> + struct has_matrix_multiply<op_trans<T> > + { const static bool value = has_matrix_multiply<T>::value; }; + + template <typename T> + struct has_matrix_multiply<op_conj_trans<T> > + { const static bool value = has_matrix_multiply<T>::value; }; + + template <typename T> + struct has_matrix_multiply<op_conj<T> > + { const static bool value = has_matrix_multiply<T>::value; }; + + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + + const int unknown_matrix = 0; + const int general_matrix = 1; + const int row_matrix = 2; + const int column_matrix = 3; + + // ------------------------------------------------------------------------------------ + + template <typename T> + struct matrix_type_id + { + const static int value = unknown_matrix; + }; + + template <typename T, long NR, long NC, typename MM, typename L> + struct matrix_type_id<matrix<T,NR,NC,MM,L> > + { + const static int value = general_matrix; + }; + + template <typename T, long NR, typename MM, typename L> + struct matrix_type_id<matrix<T,NR,1,MM,L> > + { + const static int value = column_matrix; + }; + + template <typename T, typename MM, typename L> + struct matrix_type_id<matrix<T,1,1,MM,L> > + { + const static int value = column_matrix; + }; + + template <typename T, long NC, typename MM, typename L> + struct matrix_type_id<matrix<T,1,NC,MM,L> > + { + const static int value = row_matrix; + }; + + // ------------------------------------------------------------------------------------ + + template <typename T, long NR, long NC, typename MM, typename L> + struct matrix_type_id<matrix_op<op_colm<matrix<T,NR,NC,MM,L> > > > + { + const static int value = column_matrix; + }; + + template <typename T, long NR, long NC, typename MM, typename L> + struct matrix_type_id<matrix_op<op_rowm<matrix<T,NR,NC,MM,L> > > > + { + const static int value = row_matrix; + }; + + template <typename T, long NR, long NC, typename MM, typename L> + struct matrix_type_id<matrix_op<op_colm2<matrix<T,NR,NC,MM,L> > > > + { + const static int value = column_matrix; + }; + + template <typename T, long NR, long NC, typename MM, typename L> + struct matrix_type_id<matrix_op<op_rowm2<matrix<T,NR,NC,MM,L> > > > + { + const static int value = row_matrix; + }; + + template <typename T, long NR, long NC, typename MM, typename L> + struct matrix_type_id<matrix_op<op_subm<matrix<T,NR,NC,MM,L> > > > + { + const static int value = general_matrix; + }; + + template < typename T, typename MM > + struct matrix_type_id<matrix_op<op_array2d_to_mat<array2d<T,MM> > > > + { const static int value = general_matrix; }; + + template < typename T, typename MM > + struct matrix_type_id<matrix_op<op_array_to_mat<array<T,MM> > > > + { const static int value = column_matrix; }; + + template < typename value_type, typename alloc > + struct matrix_type_id<matrix_op<op_std_vect_to_mat<std::vector<value_type,alloc> > > > + { const static int value = column_matrix; }; + + template < typename value_type, typename alloc > + struct matrix_type_id<matrix_op<op_std_vect_to_mat<std_vector_c<value_type,alloc> > > > + { const static int value = column_matrix; }; + + template < typename T > + struct matrix_type_id<matrix_op<op_pointer_to_col_vect<T> > > + { const static int value = column_matrix; }; + template < typename T > + struct matrix_type_id<matrix_op<op_pointer_to_mat<T> > > + { const static int value = general_matrix; }; + + // ------------------------------------------------------------------------------------ + + template <typename T, typename U> + struct same_matrix + { + const static int T_id = matrix_type_id<T>::value; + const static int U_id = matrix_type_id<U>::value; + // The check for unknown_matrix is here so that we can be sure that matrix types + // other than the ones specifically enumerated above never get pushed into + // any of the BLAS bindings. So saying they are never the same as anything + // else prevents them from matching any of the BLAS bindings. + const static bool value = (T_id == U_id) && (T_id != unknown_matrix); + }; + + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + + // This template struct is used to tell us if two matrix expressions both contain the same + // sequence of operators, expressions. It also only has a value of true if the T expression + // contains only matrices with the given layout. + template <typename T, typename U, typename layout> + struct same_exp + { + const static bool value = (is_same_type<typename T::exp_type, typename U::exp_type>::value || + same_matrix<typename T::exp_type, typename U::exp_type>::value) && + is_same_type<typename T::layout_type,layout>::value; + + }; + + // Used only below. They help strip off the const and & qualifiers that can show up + // in the LHS_ref_type and RHS_ref_type typedefs. + template <typename T> struct noref{ typedef T type;}; + template <typename T> struct noref<T&>{ typedef T type;}; + template <typename T> struct noref<const T&>{ typedef T type;}; + template <typename T> struct noref<const T>{ typedef T type;}; + + template <typename Tlhs, typename Ulhs, typename Trhs, typename Urhs, typename layout> + struct same_exp<matrix_multiply_exp<Tlhs,Trhs>, matrix_multiply_exp<Ulhs,Urhs>,layout > + { + // The reason this case is more complex than the others is because the matrix_multiply_exp + // will use a temporary matrix instead of Tlhs or Trhs in the event that one of these + // types corresponds to an expensive expression. So we have to use the type that really + // gets used. The following typedefs are here to pick out that true type. + typedef typename matrix_multiply_exp<Tlhs,Trhs>::LHS_ref_type T_LHS_ref_type; + typedef typename matrix_multiply_exp<Tlhs,Trhs>::RHS_ref_type T_RHS_ref_type; + typedef typename noref<T_LHS_ref_type>::type T_lhs_type; + typedef typename noref<T_RHS_ref_type>::type T_rhs_type; + + typedef typename matrix_multiply_exp<Ulhs,Urhs>::LHS_ref_type U_LHS_ref_type; + typedef typename matrix_multiply_exp<Ulhs,Urhs>::RHS_ref_type U_RHS_ref_type; + typedef typename noref<U_LHS_ref_type>::type U_lhs_type; + typedef typename noref<U_RHS_ref_type>::type U_rhs_type; + + const static bool value = same_exp<T_lhs_type,U_lhs_type,layout>::value && + same_exp<T_rhs_type,U_rhs_type,layout>::value; + }; + + template <typename Tlhs, typename Ulhs, typename Trhs, typename Urhs, typename layout> + struct same_exp<matrix_add_exp<Tlhs,Trhs>, matrix_add_exp<Ulhs,Urhs>, layout > + { const static bool value = same_exp<Tlhs,Ulhs,layout>::value && same_exp<Trhs,Urhs,layout>::value; }; + + template <typename Tlhs, typename Ulhs, typename Trhs, typename Urhs, typename layout> + struct same_exp<matrix_subtract_exp<Tlhs,Trhs>, matrix_subtract_exp<Ulhs,Urhs>, layout > + { const static bool value = same_exp<Tlhs,Ulhs,layout>::value && same_exp<Trhs,Urhs,layout>::value; }; + + template <typename T, typename U, bool Tb, bool Ub, typename layout> + struct same_exp<matrix_mul_scal_exp<T,Tb>, matrix_mul_scal_exp<U,Ub>, layout > + { const static bool value = same_exp<T,U,layout>::value; }; + + template <typename T, typename U, typename layout> + struct same_exp<matrix_div_scal_exp<T>, matrix_div_scal_exp<U>, layout > + { const static bool value = same_exp<T,U,layout>::value; }; + + template <typename T, typename U, typename layout> + struct same_exp<matrix_op<op_trans<T> >, matrix_op<op_trans<U> >, layout > + { const static bool value = same_exp<T,U,layout>::value; }; + + template <typename T, typename U, typename layout> + struct same_exp<matrix_op<op_conj<T> >, matrix_op<op_conj<U> >, layout > + { const static bool value = same_exp<T,U,layout>::value; }; + + template <typename T, typename U, typename layout> + struct same_exp<matrix_op<op_conj_trans<T> >, matrix_op<op_conj_trans<U> >, layout > + { const static bool value = same_exp<T,U,layout>::value; }; + + // ------------------------------------------------------------------------------------ + + struct yes_type + { + char ch; + }; + struct no_type + { + yes_type a, b; + }; + + // This is a helper that is used below to apply the same_exp template to matrix expressions. + template <typename T, typename layout, typename U> + typename enable_if<same_exp<T,U,layout>,yes_type>::type test(U); + template <typename T, typename layout, typename U> + typename disable_if<same_exp<T,U,layout>,no_type>::type test(U); + + // ------------------------------------------------------------------------------------ + + template < + typename dest_exp, + typename src_exp, + typename enabled = void + > + struct matrix_assign_blas_helper + { + // We are in the default version of the blas helper so this + // means there wasn't any more specific overload. So just + // let the default matrix assignment happen. + template <typename EXP> + static void assign ( + dest_exp& dest, + const EXP& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ) + { + if (transpose == false) + matrix_assign_default(dest,src,alpha,add_to); + else + matrix_assign_default(dest,trans(src),alpha,add_to); + } + + // If we know this is a matrix multiply then apply the + // default dlib matrix multiply to speed things up a bit more + // than the above default function would. + template <typename EXP1, typename EXP2> + static void assign ( + dest_exp& dest, + const matrix_multiply_exp<EXP1,EXP2>& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ) + { + // At some point I need to improve the default (i.e. non BLAS) matrix + // multiplication algorithm... + + if (alpha == static_cast<typename src_exp::type>(1)) + { + if (add_to == false) + { + zero_matrix(dest); + } + + if (transpose == false) + default_matrix_multiply(dest, src.lhs, src.rhs); + else + default_matrix_multiply(dest, trans(src.rhs), trans(src.lhs)); + } + else + { + if (add_to) + { + typename dest_exp::matrix_type temp(dest.nr(),dest.nc()); + zero_matrix(temp); + + if (transpose == false) + default_matrix_multiply(temp, src.lhs, src.rhs); + else + default_matrix_multiply(temp, trans(src.rhs), trans(src.lhs)); + + matrix_assign_default(dest,temp, alpha,true); + } + else + { + zero_matrix(dest); + + if (transpose == false) + default_matrix_multiply(dest, src.lhs, src.rhs); + else + default_matrix_multiply(dest, trans(src.rhs), trans(src.lhs)); + + matrix_assign_default(dest,dest, alpha, false); + } + } + } + }; + + // This is a macro to help us add overloads for the matrix_assign_blas_helper template. + // Using this macro it is easy to add overloads for arbitrary matrix expressions. +#define DLIB_ADD_BLAS_BINDING(src_expression) \ + template <typename T, typename L> struct BOOST_JOIN(blas,__LINE__) \ + { const static bool value = sizeof(yes_type) == sizeof(test<T,L>(src_expression)); }; \ + \ + template < typename dest_exp, typename src_exp > \ + struct matrix_assign_blas_helper<dest_exp, src_exp, \ + typename enable_if<BOOST_JOIN(blas,__LINE__)<src_exp,typename dest_exp::layout_type> >::type > { \ + static void assign ( \ + dest_exp& dest, \ + const src_exp& src, \ + typename src_exp::type alpha, \ + bool add_to, \ + bool DLIB_NO_WARN_UNUSED transpose \ + ) { \ + DLIB_NO_WARN_UNUSED typedef typename dest_exp::type T; + +#define DLIB_END_BLAS_BINDING }}; + + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + + // ------------------- Forward Declarations ------------------- + + template < + typename dest_exp, + typename src_exp + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const src_exp& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ); + /*! + requires + - src.aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + !*/ + + template < + typename dest_exp, + typename src_exp, typename src_exp2 + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const matrix_add_exp<src_exp, src_exp2>& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ); + /*! + requires + - src.aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + !*/ + + template < + typename dest_exp, + typename src_exp, bool Sb + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const matrix_mul_scal_exp<src_exp,Sb>& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ); + /*! + requires + - src.aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + !*/ + + template < + typename dest_exp, + typename src_exp + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const matrix_op<op_trans<src_exp> >& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ); + /*! + requires + - src.aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + !*/ + + template < + typename dest_exp, + typename src_exp, typename src_exp2 + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const matrix_subtract_exp<src_exp, src_exp2>& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ); + /*! + requires + - src.aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + !*/ + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ); + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const matrix_add_exp<matrix<T,NR,NC,MM,L> ,src_exp>& src + ); + /*! + This function catches the expressions of the form: + M = M + exp; + and converts them into the appropriate matrix_assign_blas() call. + This is an important case to catch because it is the expression used + to represent the += matrix operator. + !*/ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const matrix_add_exp<src_exp, matrix<T,NR,NC,MM,L> >& src + ); + /*! + This function catches the expressions of the form: + M = exp + M; + and converts them into the appropriate matrix_assign_blas() call. + This is an important case to catch because it is the expression used + to represent the += matrix operator. + !*/ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const matrix_subtract_exp<matrix<T,NR,NC,MM,L> ,src_exp>& src + ); + /*! + This function catches the expressions of the form: + M = M - exp; + and converts them into the appropriate matrix_assign_blas() call. + This is an important case to catch because it is the expression used + to represent the -= matrix operator. + !*/ + + + // End of forward declarations for overloaded matrix_assign_blas functions + + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + + template < + typename dest_exp, + typename src_exp + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const src_exp& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ) + { + matrix_assign_blas_helper<dest_exp,src_exp>::assign(dest,src,alpha,add_to, transpose); + } + + // ------------------------------------------------------------------------------------ + + template < + typename dest_exp, + typename src_exp, typename src_exp2 + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const matrix_add_exp<src_exp, src_exp2>& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ) + { + if (has_matrix_multiply<src_exp>::value || has_matrix_multiply<src_exp2>::value) + { + matrix_assign_blas_proxy(dest, src.lhs, alpha, add_to, transpose); + matrix_assign_blas_proxy(dest, src.rhs, alpha, true, transpose); + } + else + { + if (transpose == false) + matrix_assign_default(dest, src, alpha, add_to); + else + matrix_assign_default(dest, trans(src), alpha, add_to); + } + } + + // ------------------------------------------------------------------------------------ + + template < + typename dest_exp, + typename src_exp, bool Sb + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const matrix_mul_scal_exp<src_exp,Sb>& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ) + { + matrix_assign_blas_proxy(dest, src.m, alpha*src.s, add_to, transpose); + } + + // ------------------------------------------------------------------------------------ + + template < + typename dest_exp, + typename src_exp + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const matrix_op<op_trans<src_exp> >& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ) + { + matrix_assign_blas_proxy(dest, src.op.m, alpha, add_to, !transpose); + } + + // ------------------------------------------------------------------------------------ + + template < + typename dest_exp, + typename src_exp, typename src_exp2 + > + void matrix_assign_blas_proxy ( + dest_exp& dest, + const matrix_subtract_exp<src_exp, src_exp2>& src, + typename src_exp::type alpha, + bool add_to, + bool transpose + ) + { + + if (has_matrix_multiply<src_exp>::value || has_matrix_multiply<src_exp2>::value) + { + matrix_assign_blas_proxy(dest, src.lhs, alpha, add_to, transpose); + matrix_assign_blas_proxy(dest, src.rhs, -alpha, true, transpose); + } + else + { + if (transpose == false) + matrix_assign_default(dest, src, alpha, add_to); + else + matrix_assign_default(dest, trans(src), alpha, add_to); + } + } + + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + + // Once we get into this function it means that we are dealing with a matrix of float, + // double, complex<float>, or complex<double> and the src_exp contains at least one + // matrix multiply. + + template < + typename T, long NR, long NC, typename MM, typename L, + long NR2, long NC2, bool Sb + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const matrix_mul_scal_exp<matrix<T,NR2,NC2,MM,L>,Sb>& src + ) + { + // It's ok that we don't check for aliasing in this case because there isn't + // any complex unrolling of successive + or - operators in this expression. + matrix_assign_blas_proxy(dest,src.m,src.s,false, false); + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ) + { + if (src.aliases(dest)) + { + matrix<T,NR,NC,MM,L> temp(dest.nr(),dest.nc()); + matrix_assign_blas_proxy(temp,src,1,false, false); + temp.swap(dest); + } + else + { + matrix_assign_blas_proxy(dest,src,1,false, false); + } + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + assignable_sub_matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ) + { + if (src.aliases(dest.m)) + { + matrix<T,NR,NC,MM,L> temp(dest.nr(),dest.nc()); + matrix_assign_blas_proxy(temp,src,1,false, false); + matrix_assign_default(dest,temp); + } + else + { + matrix_assign_blas_proxy(dest,src,1,false, false); + } + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + typename src_exp + > + void matrix_assign_blas ( + assignable_ptr_matrix<T>& dest, + const src_exp& src + ) + { + if (src.aliases(mat(dest.ptr,dest.height,dest.width))) + { + matrix<T> temp(dest.nr(),dest.nc()); + matrix_assign_blas_proxy(temp,src,1,false, false); + matrix_assign_default(dest,temp); + } + else + { + matrix_assign_blas_proxy(dest,src,1,false, false); + } + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + assignable_row_matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ) + { + if (src.aliases(dest.m)) + { + matrix<T,NR,NC,MM,L> temp(dest.nr(),dest.nc()); + matrix_assign_blas_proxy(temp,src,1,false, false); + matrix_assign_default(dest,temp); + } + else + { + matrix_assign_blas_proxy(dest,src,1,false, false); + } + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + assignable_col_matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ) + { + if (src.aliases(dest.m)) + { + matrix<T,NR,NC,MM,L> temp(dest.nr(),dest.nc()); + matrix_assign_blas_proxy(temp,src,1,false, false); + matrix_assign_default(dest,temp); + } + else + { + matrix_assign_blas_proxy(dest,src,1,false, false); + } + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const matrix_add_exp<matrix<T,NR,NC,MM,L> ,src_exp>& src + ) + { + if (src.rhs.aliases(dest) == false) + { + if (&src.lhs != &dest) + { + dest = src.lhs; + } + + matrix_assign_blas_proxy(dest, src.rhs, 1, true, false); + } + else + { + matrix<T,NR,NC,MM,L> temp(src.lhs); + matrix_assign_blas_proxy(temp, src.rhs, 1, true, false); + temp.swap(dest); + } + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const matrix_add_exp<src_exp, matrix<T,NR,NC,MM,L> >& src + ) + { + // Just switch around the left and right hand sides of the incoming + // add expression and pass it back into matrix_assign_blas() so that + // the above function will be called. + typedef matrix_add_exp<matrix<T,NR,NC,MM,L> ,src_exp> swapped_add_exp; + matrix_assign_blas(dest, swapped_add_exp(src.rhs, src.lhs)); + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + void matrix_assign_blas ( + matrix<T,NR,NC,MM,L>& dest, + const matrix_subtract_exp<matrix<T,NR,NC,MM,L> ,src_exp>& src + ) + { + if (src.rhs.aliases(dest) == false) + { + if (&src.lhs != &dest) + { + dest = src.lhs; + } + + matrix_assign_blas_proxy(dest, src.rhs, -1, true, false); + } + else + { + matrix<T,NR,NC,MM,L> temp(src.lhs); + matrix_assign_blas_proxy(temp, src.rhs, -1, true, false); + temp.swap(dest); + } + } + + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + + } // end of namespace blas_bindings + + // ------------------------------------------------------------------------------------ + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + inline typename enable_if_c<(is_same_type<T,float>::value || + is_same_type<T,double>::value || + is_same_type<T,std::complex<float> >::value || + is_same_type<T,std::complex<double> >::value) && + blas_bindings::has_matrix_multiply<src_exp>::value + >::type matrix_assign_big ( + matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ) + { + blas_bindings::matrix_assign_blas(dest,src); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + inline typename enable_if_c<(is_same_type<T,float>::value || + is_same_type<T,double>::value || + is_same_type<T,std::complex<float> >::value || + is_same_type<T,std::complex<double> >::value) && + blas_bindings::has_matrix_multiply<src_exp>::value + >::type matrix_assign_big ( + assignable_sub_matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ) + { + blas_bindings::matrix_assign_blas(dest,src); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + typename src_exp + > + inline typename enable_if_c<(is_same_type<T,float>::value || + is_same_type<T,double>::value || + is_same_type<T,std::complex<float> >::value || + is_same_type<T,std::complex<double> >::value) && + blas_bindings::has_matrix_multiply<src_exp>::value + >::type matrix_assign_big ( + assignable_ptr_matrix<T>& dest, + const src_exp& src + ) + { + blas_bindings::matrix_assign_blas(dest,src); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + inline typename enable_if_c<(is_same_type<T,float>::value || + is_same_type<T,double>::value || + is_same_type<T,std::complex<float> >::value || + is_same_type<T,std::complex<double> >::value) && + blas_bindings::has_matrix_multiply<src_exp>::value + >::type matrix_assign_big ( + assignable_row_matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ) + { + blas_bindings::matrix_assign_blas(dest,src); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, long NR, long NC, typename MM, typename L, + typename src_exp + > + inline typename enable_if_c<(is_same_type<T,float>::value || + is_same_type<T,double>::value || + is_same_type<T,std::complex<float> >::value || + is_same_type<T,std::complex<double> >::value) && + blas_bindings::has_matrix_multiply<src_exp>::value + >::type matrix_assign_big ( + assignable_col_matrix<T,NR,NC,MM,L>& dest, + const src_exp& src + ) + { + blas_bindings::matrix_assign_blas(dest,src); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_ASSIGn_ + diff --git a/ml/dlib/dlib/matrix/matrix_assign_fwd.h b/ml/dlib/dlib/matrix/matrix_assign_fwd.h new file mode 100644 index 000000000..7d29baf0a --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_assign_fwd.h @@ -0,0 +1,413 @@ +// Copyright (C) 2008 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_ASSIGn_FWD_ +#define DLIB_MATRIx_ASSIGn_FWD_ + +// GCC 4.8 gives false alarms about some variables being uninitialized. Disable these +// false warnings. +#if defined(__GNUC__) && ((__GNUC__ >= 4 && __GNUC_MINOR__ >= 8) || (__GNUC__ > 4)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +#include "../enable_if.h" +#include "matrix_data_layout.h" +#include "../algs.h" + +namespace dlib +{ + + /* + The point of the matrix_assign() functions is to contain all the various + optimizations that help the matrix assign a matrix_exp to an actual matrix + object quickly. + */ + +// ---------------------------------------------------------------------------------------- + + namespace ma + { + // This template here controls how big a compile time sized matrix needs + // to be for it to get passed into the optimized versions of the + // matrix_assign() function. So small matrices are evaluated with a simple + // loop like the ones in this file and bigger matrices may get sent to BLAS + // routines or some other kind of optimized thing. + template < typename EXP, typename enable = void > + struct is_small_matrix { static const bool value = false; }; + template < typename EXP > + struct is_small_matrix<EXP, typename enable_if_c<EXP::NR>=1 && EXP::NC>=1 && + EXP::NR<=17 && EXP::NC<=17 && (EXP::cost <= 70)>::type> { static const bool value = true; }; + + // I wouldn't use this mul object to do the multiply but visual studio 7.1 wouldn't + // compile otherwise. + template <long a, long b> + struct mul { const static long value = a*b; }; + + template < typename EXP, typename enable = void > + struct is_very_small_matrix { static const bool value = false; }; + template < typename EXP > + struct is_very_small_matrix<EXP, typename enable_if_c<EXP::NR>=1 && EXP::NC>=1 && + (mul<EXP::NR,EXP::NC>::value <= 16) && (EXP::cost <= 70)>::type> { static const bool value = true; }; + + + template < typename EXP, typename enable = void > + struct has_column_major_layout { static const bool value = false; }; + template < typename EXP > + struct has_column_major_layout<EXP, typename enable_if<is_same_type<typename EXP::layout_type, column_major_layout> >::type > + { static const bool value = true; }; + + + + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + class matrix_exp; + +// ---------------------------------------------------------------------------------------- + + template <typename EXP1, typename EXP2> + inline typename disable_if<ma::has_column_major_layout<EXP1> >::type + matrix_assign_default ( + EXP1& dest, + const EXP2& src + ) + /*! + requires + - src.destructively_aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + ensures + - #dest == src + !*/ + { + for (long r = 0; r < src.nr(); ++r) + { + for (long c = 0; c < src.nc(); ++c) + { + dest(r,c) = src(r,c); + } + } + } + +// ---------------------------------------------------------------------------------------- + + template <typename EXP1, typename EXP2> + inline typename enable_if<ma::has_column_major_layout<EXP1> >::type + matrix_assign_default ( + EXP1& dest, + const EXP2& src + ) + /*! + requires + - src.destructively_aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + ensures + - #dest == src + !*/ + { + for (long c = 0; c < src.nc(); ++c) + { + for (long r = 0; r < src.nr(); ++r) + { + dest(r,c) = src(r,c); + } + } + } + +// ---------------------------------------------------------------------------------------- + + template <typename EXP1, typename EXP2> + inline typename disable_if<ma::has_column_major_layout<EXP1> >::type + matrix_assign_default ( + EXP1& dest, + const EXP2& src, + typename EXP2::type alpha, + bool add_to + ) + /*! + requires + - src.destructively_aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + ensures + - if (add_to == false) then + - #dest == alpha*src + - else + - #dest == dest + alpha*src + !*/ + { + if (add_to) + { + if (alpha == static_cast<typename EXP2::type>(1)) + { + for (long r = 0; r < src.nr(); ++r) + { + for (long c = 0; c < src.nc(); ++c) + { + dest(r,c) += src(r,c); + } + } + } + else if (alpha == static_cast<typename EXP2::type>(-1)) + { + for (long r = 0; r < src.nr(); ++r) + { + for (long c = 0; c < src.nc(); ++c) + { + dest(r,c) -= src(r,c); + } + } + } + else + { + for (long r = 0; r < src.nr(); ++r) + { + for (long c = 0; c < src.nc(); ++c) + { + dest(r,c) += alpha*src(r,c); + } + } + } + } + else + { + if (alpha == static_cast<typename EXP2::type>(1)) + { + for (long r = 0; r < src.nr(); ++r) + { + for (long c = 0; c < src.nc(); ++c) + { + dest(r,c) = src(r,c); + } + } + } + else + { + for (long r = 0; r < src.nr(); ++r) + { + for (long c = 0; c < src.nc(); ++c) + { + dest(r,c) = alpha*src(r,c); + } + } + } + } + } + +// ---------------------------------------------------------------------------------------- + + template <typename EXP1, typename EXP2> + inline typename enable_if<ma::has_column_major_layout<EXP1> >::type + matrix_assign_default ( + EXP1& dest, + const EXP2& src, + typename EXP2::type alpha, + bool add_to + ) + /*! + requires + - src.destructively_aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + ensures + - if (add_to == false) then + - #dest == alpha*src + - else + - #dest == dest + alpha*src + !*/ + { + if (add_to) + { + if (alpha == static_cast<typename EXP2::type>(1)) + { + for (long c = 0; c < src.nc(); ++c) + { + for (long r = 0; r < src.nr(); ++r) + { + dest(r,c) += src(r,c); + } + } + } + else if (alpha == static_cast<typename EXP2::type>(-1)) + { + for (long c = 0; c < src.nc(); ++c) + { + for (long r = 0; r < src.nr(); ++r) + { + dest(r,c) -= src(r,c); + } + } + } + else + { + for (long c = 0; c < src.nc(); ++c) + { + for (long r = 0; r < src.nr(); ++r) + { + dest(r,c) += alpha*src(r,c); + } + } + } + } + else + { + if (alpha == static_cast<typename EXP2::type>(1)) + { + for (long c = 0; c < src.nc(); ++c) + { + for (long r = 0; r < src.nr(); ++r) + { + dest(r,c) = src(r,c); + } + } + } + else + { + for (long c = 0; c < src.nc(); ++c) + { + for (long r = 0; r < src.nr(); ++r) + { + dest(r,c) = alpha*src(r,c); + } + } + } + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename matrix_dest_type, + typename src_exp + > + void matrix_assign_big ( + matrix_dest_type& dest, + const matrix_exp<src_exp>& src + ) + { + matrix_assign_default(dest,src); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename matrix_dest_type, + typename src_exp + > + inline typename disable_if<ma::is_small_matrix<src_exp> >::type matrix_assign ( + matrix_dest_type& dest, + const matrix_exp<src_exp>& src + ) + /*! + requires + - src.destructively_aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + ensures + - #dest == src + !*/ + { + // Call src.ref() here so that the derived type of the matrix_exp shows + // up so we can overload matrix_assign_big() based on various matrix expression + // types. + matrix_assign_big(dest,src.ref()); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +// this code is here to perform an unrolled version of the matrix_assign() function + template < typename DEST, typename SRC, long NR, long NC, + long R = 0, long C = 0, bool base_case = (R==NR) > + struct matrix_unroll_helper + { + inline static void go ( DEST& dest, const SRC& src) + { + dest(R,C) = src(R,C); + matrix_unroll_helper<DEST,SRC,NR,NC, R + (C+1)/NC, (C+1)%NC>::go(dest,src); + } + }; + + template < typename DEST, typename SRC, long NR, long NC, long R, long C > + struct matrix_unroll_helper<DEST,SRC,NR,NC,R,C,true> + { inline static void go ( DEST& , const SRC& ) {} }; + + template <typename DEST, typename SRC> + inline void matrix_assign_unrolled ( + DEST& dest, + const SRC& src + ) + /*! + requires + - src.destructively_aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + ensures + - #dest == src + !*/ + { + COMPILE_TIME_ASSERT(SRC::NR*SRC::NC != 0); + matrix_unroll_helper<DEST,SRC, SRC::NR, SRC::NC>::go(dest,src); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template < + typename matrix_dest_type, + typename src_exp + > + inline typename enable_if_c<ma::is_small_matrix<src_exp>::value && ma::is_very_small_matrix<src_exp>::value==false >::type matrix_assign ( + matrix_dest_type& dest, + const matrix_exp<src_exp>& src + ) + /*! + requires + - src.destructively_aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + ensures + - #dest == src + !*/ + { + matrix_assign_default(dest,src.ref()); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename matrix_dest_type, + typename src_exp + > + inline typename enable_if_c<ma::is_small_matrix<src_exp>::value && ma::is_very_small_matrix<src_exp>::value==true >::type matrix_assign ( + matrix_dest_type& dest, + const matrix_exp<src_exp>& src + ) + /*! + requires + - src.destructively_aliases(dest) == false + - dest.nr() == src.nr() + - dest.nc() == src.nc() + ensures + - #dest == src + !*/ + { + matrix_assign_unrolled(dest,src.ref()); + } + +// ---------------------------------------------------------------------------------------- + +} + +#if defined(__GNUC__) && ((__GNUC__ >= 4 && __GNUC_MINOR__ >= 8) || (__GNUC__ > 4)) +#pragma GCC diagnostic pop +#endif + +#endif // DLIB_MATRIx_ASSIGn_FWD_ + + diff --git a/ml/dlib/dlib/matrix/matrix_blas_bindings.h b/ml/dlib/dlib/matrix/matrix_blas_bindings.h new file mode 100644 index 000000000..b65e29cdd --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_blas_bindings.h @@ -0,0 +1,1637 @@ +// Copyright (C) 2008 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_BLAS_BINDINGS_ +#define DLIB_MATRIx_BLAS_BINDINGS_ + +#ifndef DLIB_USE_BLAS +#error "DLIB_USE_BLAS should be defined if you want to use the BLAS bindings" +#endif + +#include "matrix_assign.h" +#include "matrix_conj_trans.h" +#include "cblas_constants.h" + +//#include <iostream> +//using namespace std; + +namespace dlib +{ + + + namespace blas_bindings + { + +#ifdef DLIB_TEST_BLAS_BINDINGS + int& counter_gemm(); + int& counter_gemv(); + int& counter_ger(); + int& counter_dot(); + int& counter_axpy(); + int& counter_scal(); + + #define DLIB_TEST_BLAS_BINDING_GEMM ++counter_gemm(); + #define DLIB_TEST_BLAS_BINDING_GEMV ++counter_gemv(); + #define DLIB_TEST_BLAS_BINDING_GER ++counter_ger(); + #define DLIB_TEST_BLAS_BINDING_DOT ++counter_dot(); + #define DLIB_TEST_BLAS_BINDING_AXPY ++counter_axpy(); + #define DLIB_TEST_BLAS_BINDING_SCAL ++counter_scal(); +#else + #define DLIB_TEST_BLAS_BINDING_GEMM + #define DLIB_TEST_BLAS_BINDING_GEMV + #define DLIB_TEST_BLAS_BINDING_GER + #define DLIB_TEST_BLAS_BINDING_DOT + #define DLIB_TEST_BLAS_BINDING_AXPY + #define DLIB_TEST_BLAS_BINDING_SCAL +#endif + +#ifndef CBLAS_H + extern "C" + { + // Here we declare the prototypes for the CBLAS calls used by the BLAS bindings below + + void cblas_saxpy(const int N, const float alpha, const float *X, + const int incX, float *Y, const int incY); + void cblas_daxpy(const int N, const double alpha, const double *X, + const int incX, double *Y, const int incY); + void cblas_caxpy(const int N, const void *alpha, const void *X, + const int incX, void *Y, const int incY); + void cblas_zaxpy(const int N, const void *alpha, const void *X, + const int incX, void *Y, const int incY); + + void cblas_sscal(const int N, const float alpha, float *X, const int incX); + void cblas_dscal(const int N, const double alpha, double *X, const int incX); + void cblas_cscal(const int N, const void *alpha, void *X, const int incX); + void cblas_zscal(const int N, const void *alpha, void *X, const int incX); + + void cblas_sgemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, + const CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const float alpha, const float *A, + const int lda, const float *B, const int ldb, + const float beta, float *C, const int ldc); + void cblas_dgemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, + const CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const double alpha, const double *A, + const int lda, const double *B, const int ldb, + const double beta, double *C, const int ldc); + void cblas_cgemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, + const CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const void *alpha, const void *A, + const int lda, const void *B, const int ldb, + const void *beta, void *C, const int ldc); + void cblas_zgemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, + const CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const void *alpha, const void *A, + const int lda, const void *B, const int ldb, + const void *beta, void *C, const int ldc); + void cblas_sgemv(const CBLAS_ORDER order, + const CBLAS_TRANSPOSE TransA, const int M, const int N, + const float alpha, const float *A, const int lda, + const float *X, const int incX, const float beta, + float *Y, const int incY); + void cblas_dgemv(const CBLAS_ORDER order, + const CBLAS_TRANSPOSE TransA, const int M, const int N, + const double alpha, const double *A, const int lda, + const double *X, const int incX, const double beta, + double *Y, const int incY); + void cblas_cgemv(const CBLAS_ORDER order, + const CBLAS_TRANSPOSE TransA, const int M, const int N, + const void *alpha, const void *A, const int lda, + const void *X, const int incX, const void *beta, + void *Y, const int incY); + void cblas_zgemv(const CBLAS_ORDER order, + const CBLAS_TRANSPOSE TransA, const int M, const int N, + const void *alpha, const void *A, const int lda, + const void *X, const int incX, const void *beta, + void *Y, const int incY); + void cblas_sger(const CBLAS_ORDER order, const int M, const int N, + const float alpha, const float *X, const int incX, + const float *Y, const int incY, float *A, const int lda); + void cblas_dger(const CBLAS_ORDER order, const int M, const int N, + const double alpha, const double *X, const int incX, + const double *Y, const int incY, double *A, const int lda); + void cblas_cgerc(const CBLAS_ORDER order, const int M, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); + void cblas_zgerc(const CBLAS_ORDER order, const int M, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); + float cblas_sdot(const int N, const float *X, const int incX, + const float *Y, const int incY); + double cblas_ddot(const int N, const double *X, const int incX, + const double *Y, const int incY); + void cblas_cdotu_sub(const int N, const void *X, const int incX, + const void *Y, const int incY, void *dotu); + void cblas_zdotu_sub(const int N, const void *X, const int incX, + const void *Y, const int incY, void *dotu); + void cblas_cdotc_sub(const int N, const void *X, const int incX, + const void *Y, const int incY, void *dotc); + void cblas_zdotc_sub(const int N, const void *X, const int incX, + const void *Y, const int incY, void *dotc); + void cblas_cgeru(const CBLAS_ORDER order, const int M, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); + void cblas_zgeru(const CBLAS_ORDER order, const int M, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); + } +#endif // if not CBLAS_H + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + inline void cblas_axpy(const int N, const float alpha, const float *X, + const int incX, float *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_AXPY; + cblas_saxpy(N, alpha, X, incX, Y, incY); + } + + inline void cblas_axpy(const int N, const double alpha, const double *X, + const int incX, double *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_AXPY; + cblas_daxpy(N, alpha, X, incX, Y, incY); + } + + inline void cblas_axpy(const int N, const std::complex<float>& alpha, const std::complex<float> *X, + const int incX, std::complex<float> *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_AXPY; + cblas_caxpy(N, &alpha, X, incX, Y, incY); + } + + inline void cblas_axpy(const int N, const std::complex<double>& alpha, const std::complex<double> *X, + const int incX, std::complex<double> *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_AXPY; + cblas_zaxpy(N, &alpha, X, incX, Y, incY); + } + + // ---------------------------------------------------------------------------------------- + + inline void cblas_scal(const int N, const float alpha, float *X) + { + DLIB_TEST_BLAS_BINDING_SCAL; + cblas_sscal(N, alpha, X, 1); + } + + inline void cblas_scal(const int N, const double alpha, double *X) + { + DLIB_TEST_BLAS_BINDING_SCAL; + cblas_dscal(N, alpha, X, 1); + } + + inline void cblas_scal(const int N, const std::complex<float>& alpha, std::complex<float> *X) + { + DLIB_TEST_BLAS_BINDING_SCAL; + cblas_cscal(N, &alpha, X, 1); + } + + inline void cblas_scal(const int N, const std::complex<double>& alpha, std::complex<double> *X) + { + DLIB_TEST_BLAS_BINDING_SCAL; + cblas_zscal(N, &alpha, X, 1); + } + + // ---------------------------------------------------------------------------------------- + + inline void cblas_gemm( const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, + const CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const float alpha, const float *A, + const int lda, const float *B, const int ldb, + const float beta, float *C, const int ldc) + { + DLIB_TEST_BLAS_BINDING_GEMM; + cblas_sgemm( Order, TransA, TransB, M, N, + K, alpha, A, lda, B, ldb, beta, C, ldc); + } + + inline void cblas_gemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, + const CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const double alpha, const double *A, + const int lda, const double *B, const int ldb, + const double beta, double *C, const int ldc) + { + DLIB_TEST_BLAS_BINDING_GEMM; + cblas_dgemm( Order, TransA, TransB, M, N, + K, alpha, A, lda, B, ldb, beta, C, ldc); + } + + inline void cblas_gemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, + const CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const std::complex<float>& alpha, const std::complex<float> *A, + const int lda, const std::complex<float> *B, const int ldb, + const std::complex<float>& beta, std::complex<float> *C, const int ldc) + { + DLIB_TEST_BLAS_BINDING_GEMM; + cblas_cgemm( Order, TransA, TransB, M, N, + K, &alpha, A, lda, B, ldb, &beta, C, ldc); + } + + inline void cblas_gemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, + const CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const std::complex<double>& alpha, const std::complex<double> *A, + const int lda, const std::complex<double> *B, const int ldb, + const std::complex<double>& beta, std::complex<double> *C, const int ldc) + { + DLIB_TEST_BLAS_BINDING_GEMM; + cblas_zgemm( Order, TransA, TransB, M, N, + K, &alpha, A, lda, B, ldb, &beta, C, ldc); + } + + // ---------------------------------------------------------------------------------------- + + inline void cblas_gemv(const CBLAS_ORDER order, + const CBLAS_TRANSPOSE TransA, const int M, const int N, + const float alpha, const float *A, const int lda, + const float *X, const int incX, const float beta, + float *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_GEMV; + cblas_sgemv(order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } + + inline void cblas_gemv(const CBLAS_ORDER order, + const CBLAS_TRANSPOSE TransA, const int M, const int N, + const double alpha, const double *A, const int lda, + const double *X, const int incX, const double beta, + double *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_GEMV; + cblas_dgemv(order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } + + inline void cblas_gemv(const CBLAS_ORDER order, + const CBLAS_TRANSPOSE TransA, const int M, const int N, + const std::complex<float>& alpha, const std::complex<float> *A, const int lda, + const std::complex<float> *X, const int incX, const std::complex<float>& beta, + std::complex<float> *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_GEMV; + cblas_cgemv(order, TransA, M, N, &alpha, A, lda, X, incX, &beta, Y, incY); + } + + inline void cblas_gemv(const CBLAS_ORDER order, + const CBLAS_TRANSPOSE TransA, const int M, const int N, + const std::complex<double>& alpha, const std::complex<double> *A, const int lda, + const std::complex<double> *X, const int incX, const std::complex<double>& beta, + std::complex<double> *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_GEMV; + cblas_zgemv(order, TransA, M, N, &alpha, A, lda, X, incX, &beta, Y, incY); + } + + // ---------------------------------------------------------------------------------------- + + inline void cblas_ger(const CBLAS_ORDER order, const int M, const int N, + const std::complex<float>& alpha, const std::complex<float> *X, const int incX, + const std::complex<float> *Y, const int incY, std::complex<float> *A, const int lda) + { + DLIB_TEST_BLAS_BINDING_GER; + cblas_cgeru (order, M, N, &alpha, X, incX, Y, incY, A, lda); + } + + inline void cblas_ger(const CBLAS_ORDER order, const int M, const int N, + const std::complex<double>& alpha, const std::complex<double> *X, const int incX, + const std::complex<double> *Y, const int incY, std::complex<double> *A, const int lda) + { + DLIB_TEST_BLAS_BINDING_GER; + cblas_zgeru (order, M, N, &alpha, X, incX, Y, incY, A, lda); + } + + inline void cblas_ger(const CBLAS_ORDER order, const int M, const int N, + const float alpha, const float *X, const int incX, + const float *Y, const int incY, float *A, const int lda) + { + DLIB_TEST_BLAS_BINDING_GER; + cblas_sger (order, M, N, alpha, X, incX, Y, incY, A, lda); + } + + inline void cblas_ger(const CBLAS_ORDER order, const int M, const int N, + const double alpha, const double *X, const int incX, + const double *Y, const int incY, double *A, const int lda) + { + DLIB_TEST_BLAS_BINDING_GER; + cblas_dger (order, M, N, alpha, X, incX, Y, incY, A, lda); + } + + // ---------------------------------------------------------------------------------------- + + inline void cblas_gerc(const CBLAS_ORDER order, const int M, const int N, + const std::complex<float>& alpha, const std::complex<float> *X, const int incX, + const std::complex<float> *Y, const int incY, std::complex<float> *A, const int lda) + { + DLIB_TEST_BLAS_BINDING_GER; + cblas_cgerc (order, M, N, &alpha, X, incX, Y, incY, A, lda); + } + + inline void cblas_gerc(const CBLAS_ORDER order, const int M, const int N, + const std::complex<double>& alpha, const std::complex<double> *X, const int incX, + const std::complex<double> *Y, const int incY, std::complex<double> *A, const int lda) + { + DLIB_TEST_BLAS_BINDING_GER; + cblas_zgerc (order, M, N, &alpha, X, incX, Y, incY, A, lda); + } + + // ---------------------------------------------------------------------------------------- + + inline float cblas_dot(const int N, const float *X, const int incX, + const float *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_DOT; + return cblas_sdot(N, X, incX, Y, incY); + } + + inline double cblas_dot(const int N, const double *X, const int incX, + const double *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_DOT; + return cblas_ddot(N, X, incX, Y, incY); + } + + inline std::complex<float> cblas_dot(const int N, const std::complex<float> *X, const int incX, + const std::complex<float> *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_DOT; + std::complex<float> result; + cblas_cdotu_sub(N, X, incX, Y, incY, &result); + return result; + } + + inline std::complex<double> cblas_dot(const int N, const std::complex<double> *X, const int incX, + const std::complex<double> *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_DOT; + std::complex<double> result; + cblas_zdotu_sub(N, X, incX, Y, incY, &result); + return result; + } + + // ---------------------------------------------------------------------------------------- + + inline std::complex<float> cblas_dotc(const int N, const std::complex<float> *X, const int incX, + const std::complex<float> *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_DOT; + std::complex<float> result; + cblas_cdotc_sub(N, X, incX, Y, incY, &result); + return result; + } + + inline std::complex<double> cblas_dotc(const int N, const std::complex<double> *X, const int incX, + const std::complex<double> *Y, const int incY) + { + DLIB_TEST_BLAS_BINDING_DOT; + std::complex<double> result; + cblas_zdotc_sub(N, X, incX, Y, incY, &result); + return result; + } + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + // Helpers for determining the data pointer, LDA, and incX arguments to BLAS functions. + + template <typename T, long NR, long NC, typename MM> + int get_ld (const matrix<T,NR,NC,MM,row_major_layout>& m) { return m.nc(); } + + template <typename T, long NR, long NC, typename MM> + int get_ld (const matrix<T,NR,NC,MM,column_major_layout>& m) { return m.nr(); } + + + template <typename T, long NR, long NC, typename MM> + int get_ld (const matrix_op<op_subm<matrix<T,NR,NC,MM,row_major_layout> > >& m) { return m.op.m.nc(); } + + template <typename T, long NR, long NC, typename MM> + int get_ld (const matrix_op<op_subm<matrix<T,NR,NC,MM,column_major_layout> > >& m) { return m.op.m.nr(); } + + template <typename T, long NR, long NC, typename MM> + int get_ld (const assignable_sub_matrix<T,NR,NC,MM,row_major_layout>& m) { return m.m.nc(); } + + template <typename T, long NR, long NC, typename MM> + int get_ld (const assignable_sub_matrix<T,NR,NC,MM,column_major_layout>& m) { return m.m.nr(); } + + template <typename T, long NR, long NC, typename MM> + int get_ld (const assignable_col_matrix<T,NR,NC,MM,row_major_layout>& m) { return m.m.nc(); } + + template <typename T, long NR, long NC, typename MM> + int get_ld (const assignable_col_matrix<T,NR,NC,MM,column_major_layout>& m) { return m.m.nr(); } + + template <typename T, long NR, long NC, typename MM> + int get_ld (const assignable_row_matrix<T,NR,NC,MM,row_major_layout>& m) { return m.m.nc(); } + + template <typename T, long NR, long NC, typename MM> + int get_ld (const assignable_row_matrix<T,NR,NC,MM,column_major_layout>& m) { return m.m.nr(); } + + template <typename T> + int get_ld (const assignable_ptr_matrix<T>& m) { return m.nc(); } + + template <typename T, typename MM> + int get_ld (const matrix_op<op_array2d_to_mat<array2d<T,MM> > >& m) { return m.nc(); } + template <typename T, typename MM> + int get_ld (const matrix_op<op_array_to_mat<array<T,MM> > >& m) { return m.nc(); } + template < typename value_type, typename alloc > + int get_ld (const matrix_op<op_std_vect_to_mat<std::vector<value_type,alloc> > >& m) { return m.nc(); } + template < typename value_type, typename alloc > + int get_ld (const matrix_op<op_std_vect_to_mat<std_vector_c<value_type,alloc> > >& m) { return m.nc(); } + template <typename T> + int get_ld (const matrix_op<op_pointer_to_col_vect<T> >& m) { return m.nc(); } + template <typename T> + int get_ld (const matrix_op<op_pointer_to_mat<T> >& m) { return m.op.stride; } + + // -------- + + // get_inc() returns the offset from one element to another. If an object has a + // non-uniform offset between elements then returns 0 (e.g. a subm() view could + // have a non-uniform offset between elements). + + template <typename T, typename MM> + int get_inc (const matrix_op<op_array2d_to_mat<array2d<T,MM> > >& ) { return 1; } + template <typename T, typename MM> + int get_inc (const matrix_op<op_array_to_mat<array<T,MM> > >& ) { return 1; } + template < typename value_type, typename alloc > + int get_inc (const matrix_op<op_std_vect_to_mat<std::vector<value_type,alloc> > >& ) { return 1; } + template < typename value_type, typename alloc > + int get_inc (const matrix_op<op_std_vect_to_mat<std_vector_c<value_type,alloc> > >& ) { return 1; } + template <typename T> + int get_inc (const matrix_op<op_pointer_to_col_vect<T> >& ) { return 1; } + template <typename T> + int get_inc (const matrix_op<op_pointer_to_mat<T> >& m) { return m.op.stride==m.op.cols ? 1 : 0; } + + template <typename T, long NR, long NC, typename MM, typename L> + int get_inc (const matrix<T,NR,NC,MM,L>& ) { return 1; } + + template <typename T, long NR, long NC, typename MM> + int get_inc (const matrix_op<op_subm<matrix<T,NR,NC,MM,row_major_layout> > >& m) + { + // if the sub-view doesn't cover all the columns then it can't have a uniform + // layout. + if (m.nc() < m.op.m.nc()) + return 0; + else + return 1; + } + + template <typename T, long NR, long NC, typename MM> + int get_inc (const matrix_op<op_subm<matrix<T,NR,NC,MM,column_major_layout> > >& m) + { + if (m.nr() < m.op.m.nr()) + return 0; + else + return 1; + } + + template <typename T, long NR, long NC, typename MM> + int get_inc (const assignable_sub_matrix<T,NR,NC,MM,row_major_layout>& m) + { + if (m.nc() < m.m.nc()) + return 0; + else + return 1; + } + template <typename T, long NR, long NC, typename MM> + int get_inc (const assignable_sub_matrix<T,NR,NC,MM,column_major_layout>& m) + { + if (m.nr() < m.m.nr()) + return 0; + else + return 1; + } + + template <typename T> + int get_inc (const assignable_ptr_matrix<T>& ) { return 1; } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const matrix_op<op_colm<matrix<T,NR,NC,MM,row_major_layout> > >& m) + { + return m.op.m.nc(); + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const matrix_op<op_rowm<matrix<T,NR,NC,MM,row_major_layout> > >& ) + { + return 1; + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const matrix_op<op_colm2<matrix<T,NR,NC,MM,row_major_layout> > >& m) + { + return m.op.m.nc(); + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const matrix_op<op_rowm2<matrix<T,NR,NC,MM,row_major_layout> > >& ) + { + return 1; + } + + + + template <typename T, long NR, long NC, typename MM> + int get_inc(const matrix_op<op_colm<matrix<T,NR,NC,MM,column_major_layout> > >& ) + { + return 1; + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const matrix_op<op_rowm<matrix<T,NR,NC,MM,column_major_layout> > >& m) + { + return m.op.m.nr(); + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const matrix_op<op_colm2<matrix<T,NR,NC,MM,column_major_layout> > >& ) + { + return 1; + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const matrix_op<op_rowm2<matrix<T,NR,NC,MM,column_major_layout> > >& m) + { + return m.op.m.nr(); + } + + + + template <typename T, long NR, long NC, typename MM> + int get_inc(const assignable_row_matrix<T,NR,NC,MM,row_major_layout>& ) + { + return 1; + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const assignable_row_matrix<T,NR,NC,MM,column_major_layout>& m) + { + return m.m.nr(); + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const assignable_col_matrix<T,NR,NC,MM,row_major_layout>& m) + { + return m.m.nc(); + } + + template <typename T, long NR, long NC, typename MM> + int get_inc(const assignable_col_matrix<T,NR,NC,MM,column_major_layout>& ) + { + return 1; + } + + // -------- + + template <typename T, long NR, long NC, typename MM, typename L> + const T* get_ptr (const matrix<T,NR,NC,MM,L>& m) { return &m(0,0); } + + template <typename T, long NR, long NC, typename MM, typename L> + T* get_ptr (matrix<T,NR,NC,MM,L>& m) { return &m(0,0); } + + template <typename T, long NR, long NC, typename MM, typename L> + const T* get_ptr (const matrix_op<op_subm<matrix<T,NR,NC,MM,L> > >& m) { return &m.op.m(m.op.r_,m.op.c_); } + + template <typename T, long NR, long NC, typename MM, typename L> + const T* get_ptr (const matrix_op<op_colm<matrix<T,NR,NC,MM,L> > >& m) { return &m.op.m(0,m.op.col); } + + template <typename T, long NR, long NC, typename MM, typename L> + const T* get_ptr (const matrix_op<op_rowm<matrix<T,NR,NC,MM,L> > >& m) { return &m.op.m(m.op.row,0); } + + template <typename T, long NR, long NC, typename MM, typename L> + const T* get_ptr (const matrix_op<op_colm2<matrix<T,NR,NC,MM,L> > >& m) { return &m.op.m(0,m.op.col); } + + template <typename T, long NR, long NC, typename MM, typename L> + const T* get_ptr (const matrix_op<op_rowm2<matrix<T,NR,NC,MM,L> > >& m) { return &m.op.m(m.op.row,0); } + + + template <typename T, long NR, long NC, typename MM, typename L> + T* get_ptr (assignable_col_matrix<T,NR,NC,MM,L>& m) { return &m(0,0); } + + template <typename T, long NR, long NC, typename MM, typename L> + T* get_ptr (assignable_row_matrix<T,NR,NC,MM,L>& m) { return &m(0,0); } + + template <typename T, long NR, long NC, typename MM, typename L> + T* get_ptr (assignable_sub_matrix<T,NR,NC,MM,L>& m) { return &m(0,0); } + + template <typename T> + T* get_ptr (assignable_ptr_matrix<T>& m) { return m.ptr; } + + template <typename T, typename MM> + const T* get_ptr (const matrix_op<op_array2d_to_mat<array2d<T,MM> > >& m) { return &m.op.array[0][0]; } + template <typename T, typename MM> + const T* get_ptr (const matrix_op<op_array_to_mat<array<T,MM> > >& m) { return &m.op.vect[0]; } + template < typename T, typename alloc > + const T* get_ptr (const matrix_op<op_std_vect_to_mat<std::vector<T,alloc> > >& m) { return &m.op.vect[0]; } + template < typename T, typename alloc > + const T* get_ptr (const matrix_op<op_std_vect_to_mat<std_vector_c<T,alloc> > >& m) { return &m.op.vect[0]; } + template <typename T> + const T* get_ptr (const matrix_op<op_pointer_to_col_vect<T> >& m) { return m.op.ptr; } + template <typename T> + const T* get_ptr (const matrix_op<op_pointer_to_mat<T> >& m) { return m.op.ptr; } + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + // Here we declare some matrix objects for use in the DLIB_ADD_BLAS_BINDING macro. These + // extern declarations don't actually correspond to any real matrix objects. They are + // simply here so we can build matrix expressions with the DLIB_ADD_BLAS_BINDING marco. + + + // Note that the fact that these are double matrices isn't important, it is just a placeholder in this case. + extern matrix<double> m; // general matrix + extern matrix<double,1,0> rv; // general row vector + extern matrix<double,0,1> cv; // general column vector + extern const double s; + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + // AXPY/SCAL overloads + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + DLIB_ADD_BLAS_BINDING(m) + { + + const int N = static_cast<int>(src.size()); + if (transpose == false && N != 0) + { + if (add_to) + { + if (get_inc(src) && get_inc(dest)) + cblas_axpy(N, alpha, get_ptr(src), get_inc(src), get_ptr(dest), get_inc(dest)); + else + matrix_assign_default(dest, src, alpha, add_to); + } + else + { + if (get_ptr(src) == get_ptr(dest)) + cblas_scal(N, alpha, get_ptr(dest)); + else + matrix_assign_default(dest, src, alpha, add_to); + } + } + else + { + matrix_assign_default(dest, trans(src), alpha, add_to); + } + + } DLIB_END_BLAS_BINDING + + DLIB_ADD_BLAS_BINDING(rv) + { + + const int N = static_cast<int>(src.size()); + if (transpose == false && N != 0) + { + if (add_to) + { + if (get_inc(src) && get_inc(dest)) + cblas_axpy(N, alpha, get_ptr(src), get_inc(src), get_ptr(dest), get_inc(dest)); + else + matrix_assign_default(dest, src, alpha, add_to); + } + else + { + if (get_ptr(src) == get_ptr(dest)) + cblas_scal(N, alpha, get_ptr(dest)); + else + matrix_assign_default(dest, src, alpha, add_to); + } + } + else + { + matrix_assign_default(dest, trans(src), alpha, add_to); + } + + } DLIB_END_BLAS_BINDING + + DLIB_ADD_BLAS_BINDING(cv) + { + + const int N = static_cast<int>(src.size()); + if (transpose == false && N != 0) + { + if (add_to) + { + if (get_inc(src) && get_inc(dest)) + cblas_axpy(N, alpha, get_ptr(src), get_inc(src), get_ptr(dest), get_inc(dest)); + else + matrix_assign_default(dest, src, alpha, add_to); + } + else + { + if (get_ptr(src) == get_ptr(dest)) + cblas_scal(N, alpha, get_ptr(dest)); + else + matrix_assign_default(dest, src, alpha, add_to); + } + } + else + { + matrix_assign_default(dest, trans(src), alpha, add_to); + } + + } DLIB_END_BLAS_BINDING + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + // GEMM overloads + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + DLIB_ADD_BLAS_BINDING(m*m) + { + //cout << "BLAS GEMM: m*m" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs); + const int lda = get_ld(src.lhs); + const T* B = get_ptr(src.rhs); + const int ldb = get_ld(src.rhs); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, CblasNoTrans, CblasNoTrans, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + cblas_gemm(Order, CblasTrans, CblasTrans, N, M, K, alpha, B, ldb, A, lda, beta, C, ldc); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(m)*m) + { + //cout << "BLAS GEMM: trans(m)*m" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasTrans; + const CBLAS_TRANSPOSE TransB = CblasNoTrans; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* B = get_ptr(src.rhs); + const int ldb = get_ld(src.rhs); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, TransA, TransB, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + cblas_gemm(Order, TransA, TransB, N, M, K, alpha, B, ldb, A, lda, beta, C, ldc); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(m*trans(m)) + { + //cout << "BLAS GEMM: m*trans(m)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasNoTrans; + const CBLAS_TRANSPOSE TransB = CblasTrans; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs); + const int lda = get_ld(src.lhs); + const T* B = get_ptr(src.rhs.op.m); + const int ldb = get_ld(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, TransA, TransB, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + cblas_gemm(Order, TransA, TransB, N, M, K, alpha, B, ldb, A, lda, beta, C, ldc); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(m)*trans(m)) + { + //cout << "BLAS GEMM: trans(m)*trans(m)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* B = get_ptr(src.rhs.op.m); + const int ldb = get_ld(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, CblasTrans, CblasTrans, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + cblas_gemm(Order, CblasNoTrans, CblasNoTrans, N, M, K, alpha, B, ldb, A, lda, beta, C, ldc); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + // -------------------------------------- + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(conj(m))*m) + { + //cout << "BLAS GEMM: trans(conj(m))*m" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasConjTrans; + const CBLAS_TRANSPOSE TransB = CblasNoTrans; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* B = get_ptr(src.rhs); + const int ldb = get_ld(src.rhs); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, TransA, TransB, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + matrix_assign_default(dest, trans(src), alpha, add_to); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(conj(m))*trans(m)) + { + //cout << "BLAS GEMM: trans(conj(m))*trans(m)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasConjTrans; + const CBLAS_TRANSPOSE TransB = CblasTrans; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* B = get_ptr(src.rhs.op.m); + const int ldb = get_ld(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, TransA, TransB, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + matrix_assign_default(dest, trans(src), alpha, add_to); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(m*trans(conj(m))) + { + //cout << "BLAS GEMM: m*trans(conj(m))" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasNoTrans; + const CBLAS_TRANSPOSE TransB = CblasConjTrans; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs); + const int lda = get_ld(src.lhs); + const T* B = get_ptr(src.rhs.op.m); + const int ldb = get_ld(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, TransA, TransB, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + matrix_assign_default(dest, trans(src), alpha, add_to); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(m)*trans(conj(m))) + { + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasTrans; + const CBLAS_TRANSPOSE TransB = CblasConjTrans; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* B = get_ptr(src.rhs.op.m); + const int ldb = get_ld(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, TransA, TransB, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + matrix_assign_default(dest, trans(src), alpha, add_to); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(conj(m))*trans(conj(m))) + { + //cout << "BLAS GEMM: trans(conj(m))*trans(conj(m))" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasConjTrans; + const CBLAS_TRANSPOSE TransB = CblasConjTrans; + const int M = static_cast<int>(src.nr()); + const int N = static_cast<int>(src.nc()); + const int K = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* B = get_ptr(src.rhs.op.m); + const int ldb = get_ld(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* C = get_ptr(dest); + const int ldc = get_ld(dest); + + if (transpose == false) + cblas_gemm(Order, TransA, TransB, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc); + else + matrix_assign_default(dest, trans(src), alpha, add_to); + } DLIB_END_BLAS_BINDING + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + // GEMV overloads + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + DLIB_ADD_BLAS_BINDING(m*cv) + { + //cout << "BLAS GEMV: m*cv" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasNoTrans; + const int M = static_cast<int>(src.lhs.nr()); + const int N = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs); + const int lda = get_ld(src.lhs); + const T* X = get_ptr(src.rhs); + const int incX = get_inc(src.rhs); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(rv*m) + { + // Note that rv*m is the same as trans(m)*trans(rv) + + //cout << "BLAS GEMV: rv*m" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasTrans; + const int M = static_cast<int>(src.rhs.nr()); + const int N = static_cast<int>(src.rhs.nc()); + const T* A = get_ptr(src.rhs); + const int lda = get_ld(src.rhs); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(cv)*m) + { + // Note that trans(cv)*m is the same as trans(m)*cv + + //cout << "BLAS GEMV: trans(cv)*m" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasTrans; + const int M = static_cast<int>(src.rhs.nr()); + const int N = static_cast<int>(src.rhs.nc()); + const T* A = get_ptr(src.rhs); + const int lda = get_ld(src.rhs); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(m*trans(rv)) + { + //cout << "BLAS GEMV: m*trans(rv)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasNoTrans; + const int M = static_cast<int>(src.lhs.nr()); + const int N = static_cast<int>(src.lhs.nc()); + const T* A = get_ptr(src.lhs); + const int lda = get_ld(src.lhs); + const T* X = get_ptr(src.rhs.op.m); + const int incX = get_inc(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + // -------------------------------------- + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(m)*cv) + { + //cout << "BLAS GEMV: trans(m)*cv" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasTrans; + const int M = static_cast<int>(src.lhs.op.m.nr()); + const int N = static_cast<int>(src.lhs.op.m.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* X = get_ptr(src.rhs); + const int incX = get_inc(src.rhs); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(rv*trans(m)) + { + // Note that rv*trans(m) is the same as m*trans(rv) + + //cout << "BLAS GEMV: rv*trans(m)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasNoTrans; + const int M = static_cast<int>(src.rhs.op.m.nr()); + const int N = static_cast<int>(src.rhs.op.m.nc()); + const T* A = get_ptr(src.rhs.op.m); + const int lda = get_ld(src.rhs.op.m); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(cv)*trans(m)) + { + // Note that trans(cv)*trans(m) is the same as m*cv + + //cout << "BLAS GEMV: trans(cv)*trans(m)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasNoTrans; + const int M = static_cast<int>(src.rhs.op.m.nr()); + const int N = static_cast<int>(src.rhs.op.m.nc()); + const T* A = get_ptr(src.rhs.op.m); + const int lda = get_ld(src.rhs.op.m); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(m)*trans(rv)) + { + //cout << "BLAS GEMV: trans(m)*trans(rv)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasTrans; + const int M = static_cast<int>(src.lhs.op.m.nr()); + const int N = static_cast<int>(src.lhs.op.m.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* X = get_ptr(src.rhs.op.m); + const int incX = get_inc(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + // -------------------------------------- + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(cv)*conj(m)) + { + // Note that trans(cv)*conj(m) == conj(trans(m))*cv + //cout << "BLAS GEMV: trans(cv)*conj(m)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasConjTrans; + const int M = static_cast<int>(src.rhs.op.m.nr()); + const int N = static_cast<int>(src.rhs.op.m.nc()); + const T* A = get_ptr(src.rhs.op.m); + const int lda = get_ld(src.rhs.op.m); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(rv*conj(m)) + { + // Note that rv*conj(m) == conj(trans(m))*cv + //cout << "BLAS GEMV: rv*conj(m)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasConjTrans; + const int M = static_cast<int>(src.rhs.op.m.nr()); + const int N = static_cast<int>(src.rhs.op.m.nc()); + const T* A = get_ptr(src.rhs.op.m); + const int lda = get_ld(src.rhs.op.m); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(conj(m))*cv) + { + //cout << "BLAS GEMV: trans(conj(m))*cv" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasConjTrans; + const int M = static_cast<int>(src.lhs.op.m.nr()); + const int N = static_cast<int>(src.lhs.op.m.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* X = get_ptr(src.rhs); + const int incX = get_inc(src.rhs); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(conj(m))*trans(rv)) + { + //cout << "BLAS GEMV: trans(conj(m))*trans(rv)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const CBLAS_TRANSPOSE TransA = CblasConjTrans; + const int M = static_cast<int>(src.lhs.op.m.nr()); + const int N = static_cast<int>(src.lhs.op.m.nc()); + const T* A = get_ptr(src.lhs.op.m); + const int lda = get_ld(src.lhs.op.m); + const T* X = get_ptr(src.rhs.op.m); + const int incX = get_inc(src.rhs.op.m); + + const T beta = static_cast<T>(add_to?1:0); + T* Y = get_ptr(dest); + const int incY = get_inc(dest); + + cblas_gemv(Order, TransA, M, N, alpha, A, lda, X, incX, beta, Y, incY); + } DLIB_END_BLAS_BINDING + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + // GER overloads + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + DLIB_ADD_BLAS_BINDING(cv*rv) + { + //cout << "BLAS GER: cv*rv" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(dest.nr()); + const int N = static_cast<int>(dest.nc()); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + const T* Y = get_ptr(src.rhs); + const int incY = get_inc(src.rhs); + + if (add_to == false) + zero_matrix(dest); + + T* A = get_ptr(dest); + const int lda = get_ld(dest); + + if (transpose == false) + cblas_ger(Order, M, N, alpha, X, incX, Y, incY, A, lda); + else + cblas_ger(Order, M, N, alpha, Y, incY, X, incX, A, lda); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(rv)*rv) + { + //cout << "BLAS GER: trans(rv)*rv" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(dest.nr()); + const int N = static_cast<int>(dest.nc()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs); + const int incY = get_inc(src.rhs); + + if (add_to == false) + zero_matrix(dest); + + T* A = get_ptr(dest); + const int lda = get_ld(dest); + + if (transpose == false) + cblas_ger(Order, M, N, alpha, X, incX, Y, incY, A, lda); + else + cblas_ger(Order, M, N, alpha, Y, incY, X, incX, A, lda); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(cv*trans(cv)) + { + //cout << "BLAS GER: cv*trans(cv)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(dest.nr()); + const int N = static_cast<int>(dest.nc()); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + if (add_to == false) + zero_matrix(dest); + + T* A = get_ptr(dest); + const int lda = get_ld(dest); + + if (transpose == false) + cblas_ger(Order, M, N, alpha, X, incX, Y, incY, A, lda); + else + cblas_ger(Order, M, N, alpha, Y, incY, X, incX, A, lda); + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(rv)*trans(cv)) + { + //cout << "BLAS GER: trans(rv)*trans(cv)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(dest.nr()); + const int N = static_cast<int>(dest.nc()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + if (add_to == false) + zero_matrix(dest); + + T* A = get_ptr(dest); + const int lda = get_ld(dest); + + if (transpose == false) + cblas_ger(Order, M, N, alpha, X, incX, Y, incY, A, lda); + else + cblas_ger(Order, M, N, alpha, Y, incY, X, incX, A, lda); + } DLIB_END_BLAS_BINDING + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + // GERC overloads + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + /* + DLIB_ADD_BLAS_BINDING(cv*conj(rv)) + { + //cout << "BLAS GERC: cv*conj(rv)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(dest.nr()); + const int N = static_cast<int>(dest.nc()); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + if (add_to == false) + zero_matrix(dest); + + T* A = get_ptr(dest); + const int lda = get_ld(dest); + + if (transpose == false) + cblas_gerc(Order, M, N, alpha, X, incX, Y, incY, A, lda); + else + cblas_gerc(Order, M, N, alpha, Y, incY, X, incX, A, lda); + } DLIB_END_BLAS_BINDING + */ + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(cv*conj(trans(cv))) + { + //cout << "BLAS GERC: cv*conj(trans(cv))" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(dest.nr()); + const int N = static_cast<int>(dest.nc()); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + + if (transpose == false) + { + T* A = get_ptr(dest); + const int lda = get_ld(dest); + + if (add_to == false) + zero_matrix(dest); + + cblas_gerc(Order, M, N, alpha, X, incX, Y, incY, A, lda); + } + else + { + matrix_assign_default(dest,trans(src),alpha,add_to); + //cblas_gerc(Order, M, N, alpha, Y, incY, X, incX, A, lda); + } + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(rv)*conj(trans(cv))) + { + //cout << "BLAS GERC: trans(rv)*conj(trans(cv))" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(dest.nr()); + const int N = static_cast<int>(dest.nc()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + + if (transpose == false) + { + if (add_to == false) + zero_matrix(dest); + + T* A = get_ptr(dest); + const int lda = get_ld(dest); + + cblas_gerc(Order, M, N, alpha, X, incX, Y, incY, A, lda); + } + else + { + matrix_assign_default(dest,trans(src),alpha,add_to); + //cblas_gerc(Order, M, N, alpha, Y, incY, X, incX, A, lda); + } + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + /* + DLIB_ADD_BLAS_BINDING(trans(rv)*conj(rv)) + { + //cout << "BLAS GERC: trans(rv)*conj(rv)" << endl; + const bool is_row_major_order = is_same_type<typename dest_exp::layout_type,row_major_layout>::value; + const CBLAS_ORDER Order = is_row_major_order ? CblasRowMajor : CblasColMajor; + const int M = static_cast<int>(dest.nr()); + const int N = static_cast<int>(dest.nc()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + if (add_to == false) + zero_matrix(dest); + + T* A = get_ptr(dest); + const int lda = get_ld(dest); + + if (transpose == false) + cblas_gerc(Order, M, N, alpha, X, incX, Y, incY, A, lda); + else + cblas_gerc(Order, M, N, alpha, Y, incY, X, incX, A, lda); + } DLIB_END_BLAS_BINDING + */ + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + // DOT overloads + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + DLIB_ADD_BLAS_BINDING(rv*cv) + { + //cout << "BLAS DOT: rv*cv" << endl; + const int N = static_cast<int>(src.lhs.size()); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + const T* Y = get_ptr(src.rhs); + const int incY = get_inc(src.rhs); + + if (add_to == false) + dest(0) = alpha*cblas_dot(N, X, incX, Y, incY); + else + dest(0) += alpha*cblas_dot(N, X, incX, Y, incY); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(cv)*cv) + { + //cout << "BLAS DOT: trans(cv)*cv" << endl; + const int N = static_cast<int>(src.lhs.size()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs); + const int incY = get_inc(src.rhs); + + if (add_to == false) + dest(0) = alpha*cblas_dot(N, X, incX, Y, incY); + else + dest(0) += alpha*cblas_dot(N, X, incX, Y, incY); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(rv*trans(rv)) + { + //cout << "BLAS DOT: rv*trans(rv)" << endl; + const int N = static_cast<int>(src.lhs.size()); + const T* X = get_ptr(src.lhs); + const int incX = get_inc(src.lhs); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + if (add_to == false) + dest(0) = alpha*cblas_dot(N, X, incX, Y, incY); + else + dest(0) += alpha*cblas_dot(N, X, incX, Y, incY); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(cv)*trans(rv)) + { + //cout << "BLAS DOT: trans(cv)*trans(rv)" << endl; + const int N = static_cast<int>(src.lhs.op.m.size()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + if (add_to == false) + dest(0) = alpha*cblas_dot(N, X, incX, Y, incY); + else + dest(0) += alpha*cblas_dot(N, X, incX, Y, incY); + + } DLIB_END_BLAS_BINDING + + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + // DOTC overloads + // ---------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------- + + DLIB_ADD_BLAS_BINDING(conj(rv)*cv) + { + //cout << "BLAS DOTC: conj(rv)*cv" << endl; + const int N = static_cast<int>(src.lhs.op.m.size()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs); + const int incY = get_inc(src.rhs); + + if (add_to == false) + dest(0) = alpha*cblas_dotc(N, X, incX, Y, incY); + else + dest(0) += alpha*cblas_dotc(N, X, incX, Y, incY); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(conj(trans(cv))*cv) + { + //cout << "BLAS DOTC: conj(trans(cv))*cv" << endl; + const int N = static_cast<int>(src.lhs.op.m.size()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs); + const int incY = get_inc(src.rhs); + + if (add_to == false) + dest(0) = alpha*cblas_dotc(N, X, incX, Y, incY); + else + dest(0) += alpha*cblas_dotc(N, X, incX, Y, incY); + + } DLIB_END_BLAS_BINDING + + // -------------------------------------- + + DLIB_ADD_BLAS_BINDING(trans(conj(cv))*trans(rv)) + { + //cout << "BLAS DOTC: trans(conj(cv))*trans(rv)" << endl; + const int N = static_cast<int>(src.lhs.op.m.size()); + const T* X = get_ptr(src.lhs.op.m); + const int incX = get_inc(src.lhs.op.m); + const T* Y = get_ptr(src.rhs.op.m); + const int incY = get_inc(src.rhs.op.m); + + if (add_to == false) + dest(0) = alpha*cblas_dotc(N, X, incX, Y, incY); + else + dest(0) += alpha*cblas_dotc(N, X, incX, Y, incY); + + } DLIB_END_BLAS_BINDING + + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_BLAS_BINDINGS_ + diff --git a/ml/dlib/dlib/matrix/matrix_cholesky.h b/ml/dlib/dlib/matrix/matrix_cholesky.h new file mode 100644 index 000000000..fc1140692 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_cholesky.h @@ -0,0 +1,231 @@ +// Copyright (C) 2009 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +// This code was adapted from code from the JAMA part of NIST's TNT library. +// See: http://math.nist.gov/tnt/ +#ifndef DLIB_MATRIX_CHOLESKY_DECOMPOSITION_H +#define DLIB_MATRIX_CHOLESKY_DECOMPOSITION_H + +#include "matrix.h" +#include "matrix_utilities.h" +#include "matrix_subexp.h" +#include <cmath> + +#ifdef DLIB_USE_LAPACK +#include "lapack/potrf.h" +#endif + +#include "matrix_trsm.h" + +namespace dlib +{ + + template < + typename matrix_exp_type + > + class cholesky_decomposition + { + + public: + + const static long NR = matrix_exp_type::NR; + const static long NC = matrix_exp_type::NC; + typedef typename matrix_exp_type::type type; + typedef typename matrix_exp_type::mem_manager_type mem_manager_type; + typedef typename matrix_exp_type::layout_type layout_type; + + typedef matrix<type,0,0,mem_manager_type,layout_type> matrix_type; + typedef matrix<type,NR,1,mem_manager_type,layout_type> column_vector_type; + + // You have supplied an invalid type of matrix_exp_type. You have + // to use this object with matrices that contain float or double type data. + COMPILE_TIME_ASSERT((is_same_type<float, type>::value || + is_same_type<double, type>::value )); + + + + template <typename EXP> + cholesky_decomposition( + const matrix_exp<EXP>& A + ); + + bool is_spd( + ) const; + + const matrix_type& get_l( + ) const; + + template <typename EXP> + const typename EXP::matrix_type solve ( + const matrix_exp<EXP>& B + ) const; + + private: + + matrix_type L_; // lower triangular factor + bool isspd; // true if matrix to be factored was SPD + }; + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Member functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + bool cholesky_decomposition<matrix_exp_type>:: + is_spd( + ) const + { + return isspd; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename cholesky_decomposition<matrix_exp_type>::matrix_type& cholesky_decomposition<matrix_exp_type>:: + get_l( + ) const + { + return L_; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + cholesky_decomposition<matrix_exp_type>:: + cholesky_decomposition( + const matrix_exp<EXP>& A_ + ) + { + using std::sqrt; + COMPILE_TIME_ASSERT((is_same_type<type, typename EXP::type>::value)); + + // make sure requires clause is not broken + DLIB_ASSERT(A_.nr() == A_.nc() && A_.size() > 0, + "\tcholesky_decomposition::cholesky_decomposition(A_)" + << "\n\tYou can only use this on square matrices" + << "\n\tA_.nr(): " << A_.nr() + << "\n\tA_.nc(): " << A_.nc() + << "\n\tA_.size(): " << A_.size() + << "\n\tthis: " << this + ); + +#ifdef DLIB_USE_LAPACK + L_ = A_; + const type eps = max(abs(diag(L_)))*std::sqrt(std::numeric_limits<type>::epsilon())/100; + + // check if the matrix is actually symmetric + bool is_symmetric = true; + for (long r = 0; r < L_.nr() && is_symmetric; ++r) + { + for (long c = r+1; c < L_.nc() && is_symmetric; ++c) + { + // this is approximately doing: is_symmetric = is_symmetric && ( L_(k,j) == L_(j,k)) + is_symmetric = is_symmetric && (std::abs(L_(r,c) - L_(c,r)) < eps ); + } + } + + // now compute the actual cholesky decomposition + int info = lapack::potrf('L', L_); + + // check if it's really SPD + if (info == 0 && is_symmetric && min(abs(diag(L_))) > eps*100) + isspd = true; + else + isspd = false; + + L_ = lowerm(L_); +#else + const_temp_matrix<EXP> A(A_); + + + isspd = true; + + const long n = A.nc(); + L_.set_size(n,n); + + const type eps = max(abs(diag(A)))*std::sqrt(std::numeric_limits<type>::epsilon())/100; + + // Main loop. + for (long j = 0; j < n; j++) + { + type d(0.0); + for (long k = 0; k < j; k++) + { + type s(0.0); + for (long i = 0; i < k; i++) + { + s += L_(k,i)*L_(j,i); + } + + // if L_(k,k) != 0 + if (std::abs(L_(k,k)) > eps) + { + s = (A(j,k) - s)/L_(k,k); + } + else + { + s = (A(j,k) - s); + isspd = false; + } + + L_(j,k) = s; + + d = d + s*s; + + // this is approximately doing: isspd = isspd && ( A(k,j) == A(j,k)) + isspd = isspd && (std::abs(A(k,j) - A(j,k)) < eps ); + } + d = A(j,j) - d; + isspd = isspd && (d > eps); + L_(j,j) = sqrt(d > 0.0 ? d : 0.0); + for (long k = j+1; k < n; k++) + { + L_(j,k) = 0.0; + } + } +#endif + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + const typename EXP::matrix_type cholesky_decomposition<matrix_exp_type>:: + solve( + const matrix_exp<EXP>& B + ) const + { + COMPILE_TIME_ASSERT((is_same_type<type, typename EXP::type>::value)); + + // make sure requires clause is not broken + DLIB_ASSERT(L_.nr() == B.nr(), + "\tconst matrix cholesky_decomposition::solve(B)" + << "\n\tInvalid arguments were given to this function." + << "\n\tL_.nr(): " << L_.nr() + << "\n\tB.nr(): " << B.nr() + << "\n\tthis: " << this + ); + + matrix<type, NR, EXP::NC, mem_manager_type, layout_type> X(B); + + using namespace blas_bindings; + // Solve L*y = b; + triangular_solver(CblasLeft, CblasLower, CblasNoTrans, CblasNonUnit, L_, X); + // Solve L'*X = Y; + triangular_solver(CblasLeft, CblasLower, CblasTrans, CblasNonUnit, L_, X); + return X; + } + +// ---------------------------------------------------------------------------------------- + + + +} + +#endif // DLIB_MATRIX_CHOLESKY_DECOMPOSITION_H + + + + diff --git a/ml/dlib/dlib/matrix/matrix_conj_trans.h b/ml/dlib/dlib/matrix/matrix_conj_trans.h new file mode 100644 index 000000000..3c319ccaf --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_conj_trans.h @@ -0,0 +1,71 @@ +// Copyright (C) 2009 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_CONJ_TRANS_FUNCTIONS +#define DLIB_MATRIx_CONJ_TRANS_FUNCTIONS + +#include "matrix_utilities.h" +#include "matrix_math_functions.h" +#include "matrix.h" +#include "../algs.h" +#include <cmath> +#include <complex> +#include <limits> + + +namespace dlib +{ + /*! + The point of the two functions defined in this file is to make statements + of the form conj(trans(m)) and trans(conj(m)) look the same so that it is + easier to map them to BLAS functions later on. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_conj_trans + { + op_conj_trans( const M& m_) : m(m_){} + const M& m; + + const static long cost = M::cost+1; + const static long NR = M::NC; + const static long NC = M::NR; + 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_ret_type apply (long r, long c) const { return std::conj(m(c,r)); } + + long nr () const { return m.nc(); } + long nc () const { return m.nr(); } + + 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.aliases(item); } + }; + + template <typename EXP> + const matrix_op<op_conj_trans<EXP> > trans ( + const matrix_op<op_conj<EXP> >& m + ) + { + typedef op_conj_trans<EXP> op; + return matrix_op<op>(op(m.op.m)); + } + + template <typename EXP> + const matrix_op<op_conj_trans<EXP> > conj ( + const matrix_op<op_trans<EXP> >& m + ) + { + typedef op_conj_trans<EXP> op; + return matrix_op<op>(op(m.op.m)); + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_CONJ_TRANS_FUNCTIONS + + diff --git a/ml/dlib/dlib/matrix/matrix_conv.h b/ml/dlib/dlib/matrix/matrix_conv.h new file mode 100644 index 000000000..b90c388bc --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_conv.h @@ -0,0 +1,358 @@ +// Copyright (C) 2011 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_CONV_Hh_ +#define DLIB_MATRIx_CONV_Hh_ + +#include "matrix_conv_abstract.h" +#include "matrix.h" +#include "matrix_fft.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename T> + const T& conj(const T& item) { return item; } + template <typename T> + std::complex<T> conj(const std::complex<T>& item) { return std::conj(item); } + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, bool flip_m2 = false> + struct op_conv + { + op_conv( const M1& m1_, const M2& m2_) : + m1(m1_), + m2(m2_), + nr_(m1.nr()+m2.nr()-1), + nc_(m1.nc()+m2.nc()-1) + { + if (nr_ < 0 || m1.size() == 0 || m2.size() == 0) + nr_ = 0; + if (nc_ < 0 || m1.size() == 0 || m2.size() == 0) + nc_ = 0; + } + + const M1& m1; + const M2& m2; + long nr_; + long nc_; + + const static long cost = (M1::cost+M2::cost)*10; + const static long NR = (M1::NR*M2::NR==0) ? (0) : (M1::NR+M2::NR-1); + const static long NC = (M1::NC*M2::NC==0) ? (0) : (M1::NC+M2::NC-1); + typedef typename M1::type type; + typedef type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + const_ret_type apply (long r, long c) const + { + type temp = 0; + + const long min_rr = std::max<long>(r-m2.nr()+1, 0); + const long max_rr = std::min<long>(m1.nr()-1, r); + + const long min_cc = std::max<long>(c-m2.nc()+1, 0); + const long max_cc = std::min<long>(m1.nc()-1, c); + + for (long rr = min_rr; rr <= max_rr; ++rr) + { + for (long cc = min_cc; cc <= max_cc; ++cc) + { + if (flip_m2) + temp += m1(rr,cc)*dlib::impl::conj(m2(m2.nr()-r+rr-1, m2.nc()-c+cc-1)); + else + temp += m1(rr,cc)*m2(r-rr,c-cc); + } + } + + return temp; + } + + long nr () const { return nr_; } + long nc () const { return nc_; } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const { return m1.aliases(item) || m2.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const { return m1.aliases(item) || m2.aliases(item); } + + }; + + template < + typename M1, + typename M2 + > + const matrix_op<op_conv<M1,M2> > conv ( + const matrix_exp<M1>& m1, + const matrix_exp<M2>& m2 + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename M1::type,typename M2::type>::value == true)); + + typedef op_conv<M1,M2> op; + return matrix_op<op>(op(m1.ref(),m2.ref())); + } + + template < + typename M1, + typename M2 + > + const matrix_op<op_conv<M1,M2,true> > xcorr ( + const matrix_exp<M1>& m1, + const matrix_exp<M2>& m2 + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename M1::type,typename M2::type>::value == true)); + + typedef op_conv<M1,M2,true> op; + return matrix_op<op>(op(m1.ref(),m2.ref())); + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + inline size_t bounding_power_of_two ( + size_t n + ) + { + size_t s = 1; + for (unsigned int i = 0; i < sizeof(s)*8 && s < n; ++i) + s <<= 1; + return s; + } + } + + template < + typename EXP1, + typename EXP2 + > + typename EXP1::matrix_type xcorr_fft( + const matrix_exp<EXP1>& u, + const matrix_exp<EXP2>& v + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type, typename EXP2::type>::value == true)); + using T = typename EXP1::type; + COMPILE_TIME_ASSERT((is_same_type<double,T>::value || is_same_type<float,T>::value || is_same_type<long double,T>::value )); + + const long pad_nr = impl::bounding_power_of_two(u.nr() + v.nr() - 1); + const long pad_nc = impl::bounding_power_of_two(u.nc() + v.nc() - 1); + + matrix<std::complex<T>> U(pad_nr, pad_nc), V(pad_nr,pad_nc); + + U = 0; + V = 0; + set_subm(U,U.nr()-u.nr(),U.nc()-u.nc(),u.nr(),u.nc()) = u; + set_subm(V,get_rect(v)) = v; + + fft_inplace(U); + fft_inplace(V); + + return subm(real(ifft(pointwise_multiply(U, conj(V)))), + U.nr()-u.nr()-v.nr()+1, + U.nc()-u.nc()-v.nc()+1, + u.nr()+v.nr()-1, + u.nc()+v.nc()-1 + ); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, bool flip_m2 = false> + struct op_conv_same + { + op_conv_same( const M1& m1_, const M2& m2_) : m1(m1_),m2(m2_),nr_(m1.nr()),nc_(m1.nc()) + { + if (m1.size() == 0 || m2.size() == 0) + nr_ = 0; + if (m1.size() == 0 || m2.size() == 0) + nc_ = 0; + } + + const M1& m1; + const M2& m2; + long nr_; + long nc_; + + const static long cost = (M1::cost+M2::cost)*10; + const static long NR = M1::NR; + const static long NC = M1::NC; + typedef typename M1::type type; + typedef type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + const_ret_type apply (long r, long c) const + { + r += m2.nr()/2; + c += m2.nc()/2; + + type temp = 0; + + const long min_rr = std::max<long>(r-m2.nr()+1, 0); + const long max_rr = std::min<long>(m1.nr()-1, r); + + const long min_cc = std::max<long>(c-m2.nc()+1, 0); + const long max_cc = std::min<long>(m1.nc()-1, c); + + for (long rr = min_rr; rr <= max_rr; ++rr) + { + for (long cc = min_cc; cc <= max_cc; ++cc) + { + if (flip_m2) + temp += m1(rr,cc)*dlib::impl::conj(m2(m2.nr()-r+rr-1, m2.nc()-c+cc-1)); + else + temp += m1(rr,cc)*m2(r-rr,c-cc); + } + } + + return temp; + } + + long nr () const { return nr_; } + long nc () const { return nc_; } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const { return m1.aliases(item) || m2.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const { return m1.aliases(item) || m2.aliases(item); } + + }; + + template < + typename M1, + typename M2 + > + const matrix_op<op_conv_same<M1,M2> > conv_same ( + const matrix_exp<M1>& m1, + const matrix_exp<M2>& m2 + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename M1::type,typename M2::type>::value == true)); + + typedef op_conv_same<M1,M2> op; + return matrix_op<op>(op(m1.ref(),m2.ref())); + } + + template < + typename M1, + typename M2 + > + const matrix_op<op_conv_same<M1,M2,true> > xcorr_same ( + const matrix_exp<M1>& m1, + const matrix_exp<M2>& m2 + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename M1::type,typename M2::type>::value == true)); + + typedef op_conv_same<M1,M2,true> op; + return matrix_op<op>(op(m1.ref(),m2.ref())); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, bool flip_m2 = false> + struct op_conv_valid + { + op_conv_valid( const M1& m1_, const M2& m2_) : + m1(m1_),m2(m2_), + nr_(m1.nr()-m2.nr()+1), + nc_(m1.nc()-m2.nc()+1) + { + if (nr_ < 0 || nc_ <= 0 || m1.size() == 0 || m2.size() == 0) + nr_ = 0; + if (nc_ < 0 || nr_ <= 0 || m1.size() == 0 || m2.size() == 0) + nc_ = 0; + } + + const M1& m1; + const M2& m2; + long nr_; + long nc_; + + const static long cost = (M1::cost+M2::cost)*10; + const static long NR = (M1::NR*M2::NR==0) ? (0) : (M1::NR-M2::NR+1); + const static long NC = (M1::NC*M2::NC==0) ? (0) : (M1::NC-M2::NC+1); + typedef typename M1::type type; + typedef type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + const_ret_type apply (long r, long c) const + { + r += m2.nr()-1; + c += m2.nc()-1; + + type temp = 0; + + const long min_rr = std::max<long>(r-m2.nr()+1, 0); + const long max_rr = std::min<long>(m1.nr()-1, r); + + const long min_cc = std::max<long>(c-m2.nc()+1, 0); + const long max_cc = std::min<long>(m1.nc()-1, c); + + for (long rr = min_rr; rr <= max_rr; ++rr) + { + for (long cc = min_cc; cc <= max_cc; ++cc) + { + if (flip_m2) + temp += m1(rr,cc)*dlib::impl::conj(m2(m2.nr()-r+rr-1, m2.nc()-c+cc-1)); + else + temp += m1(rr,cc)*m2(r-rr,c-cc); + } + } + + return temp; + } + + long nr () const { return nr_; } + long nc () const { return nc_; } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const { return m1.aliases(item) || m2.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const { return m1.aliases(item) || m2.aliases(item); } + + }; + + template < + typename M1, + typename M2 + > + const matrix_op<op_conv_valid<M1,M2> > conv_valid ( + const matrix_exp<M1>& m1, + const matrix_exp<M2>& m2 + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename M1::type,typename M2::type>::value == true)); + + typedef op_conv_valid<M1,M2> op; + return matrix_op<op>(op(m1.ref(),m2.ref())); + } + + template < + typename M1, + typename M2 + > + const matrix_op<op_conv_valid<M1,M2,true> > xcorr_valid ( + const matrix_exp<M1>& m1, + const matrix_exp<M2>& m2 + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename M1::type,typename M2::type>::value == true)); + + typedef op_conv_valid<M1,M2,true> op; + return matrix_op<op>(op(m1.ref(),m2.ref())); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_CONV_Hh_ + diff --git a/ml/dlib/dlib/matrix/matrix_conv_abstract.h b/ml/dlib/dlib/matrix/matrix_conv_abstract.h new file mode 100644 index 000000000..b342f2668 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_conv_abstract.h @@ -0,0 +1,158 @@ +// Copyright (C) 2011 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_CONV_ABSTRACT_Hh_ +#ifdef DLIB_MATRIx_CONV_ABSTRACT_Hh_ + +#include "matrix_abstract.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp conv ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1 and m2 both contain elements of the same type + ensures + - returns a matrix R such that: + - R is the convolution of m1 with m2. In particular, this function is + equivalent to performing the following in matlab: R = conv2(m1,m2). + - R::type == the same type that was in m1 and m2. + - R.nr() == m1.nr()+m2.nr()-1 + - R.nc() == m1.nc()+m2.nc()-1 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp xcorr ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1 and m2 both contain elements of the same type + ensures + - returns a matrix R such that: + - R is the cross-correlation of m1 with m2. In particular, this + function returns conv(m1,flip(m2)) if the matrices contain real + elements and conv(m1,flip(conj(m2))) if they are complex. + - R::type == the same type that was in m1 and m2. + - R.nr() == m1.nr()+m2.nr()-1 + - R.nc() == m1.nc()+m2.nc()-1 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp xcorr_fft ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1 and m2 both contain elements of the same type + - m1 and m2 contain real or complex values and must be double, float, or long + double valued. (e.g. not integers) + ensures + - This function is identical to xcorr() except that it uses a fast Fourier + transform to do the convolution and is therefore much faster when both m1 and + m2 are large. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp conv_same ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1 and m2 both contain elements of the same type + ensures + - returns a matrix R such that: + - R is the convolution of m1 with m2. In particular, this function is + equivalent to performing the following in matlab: R = conv2(m1,m2,'same'). + In particular, this means the result will have the same dimensions as m1 and will + contain the central part of the full convolution. Therefore, conv_same(m1,m2) is + equivalent to subm(conv(m1,m2), m2.nr()/2, m2.nc()/2, m1.nr(), m1.nc()). + - R::type == the same type that was in m1 and m2. + - R.nr() == m1.nr() + - R.nc() == m1.nc() + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp xcorr_same ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1 and m2 both contain elements of the same type + ensures + - returns a matrix R such that: + - R is the cross-correlation of m1 with m2. In particular, this + function returns conv_same(m1,flip(m2)) if the matrices contain real + elements and conv_same(m1,flip(conj(m2))) if they are complex. + - R::type == the same type that was in m1 and m2. + - R.nr() == m1.nr() + - R.nc() == m1.nc() + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp conv_valid ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1 and m2 both contain elements of the same type + ensures + - returns a matrix R such that: + - R is the convolution of m1 with m2. In particular, this function is + equivalent to performing the following in matlab: R = conv2(m1,m2,'valid'). + In particular, this means only elements of the convolution which don't require + zero padding are included in the result. + - R::type == the same type that was in m1 and m2. + - if (m1 has larger dimensions than m2) then + - R.nr() == m1.nr()-m2.nr()+1 + - R.nc() == m1.nc()-m2.nc()+1 + - else + - R.nr() == 0 + - R.nc() == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp xcorr_valid ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - m1 and m2 both contain elements of the same type + ensures + - returns a matrix R such that: + - R is the cross-correlation of m1 with m2. In particular, this + function returns conv_valid(m1,flip(m2)) if the matrices contain real + elements and conv_valid(m1,flip(conj(m2))) if they are complex. + - R::type == the same type that was in m1 and m2. + - if (m1 has larger dimensions than m2) then + - R.nr() == m1.nr()-m2.nr()+1 + - R.nc() == m1.nc()-m2.nc()+1 + - else + - R.nr() == 0 + - R.nc() == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_CONV_ABSTRACT_Hh_ + + diff --git a/ml/dlib/dlib/matrix/matrix_data_layout.h b/ml/dlib/dlib/matrix/matrix_data_layout.h new file mode 100644 index 000000000..22891c228 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_data_layout.h @@ -0,0 +1,1271 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_DATA_LAYOUT_ +#define DLIB_MATRIx_DATA_LAYOUT_ + +#include "../algs.h" +#include "matrix_fwd.h" +#include "matrix_data_layout_abstract.h" +#ifdef MATLAB_MEX_FILE +#include <mex.h> +#endif + +// GCC 4.8 gives false alarms about some matrix operations going out of bounds. Disable +// these false warnings. +#if defined(__GNUC__) && ((__GNUC__ >= 4 && __GNUC_MINOR__ >= 8) || (__GNUC__ > 4)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + /*! + A matrix layout object is any object that contains a templated class called "layout" + with an interface identical to one below: + (Note that all the template arguments are just the template arguments from the dlib::matrix + object and the member functions are defined identically to the ones with the same + signatures inside the matrix object.) + + struct matrix_layout + { + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout + { + public: + + T& operator() ( + long r, + long c + ); + + const T& operator() ( + long r, + long c + ); + + T& operator() ( + long i + ); + + const T& operator() ( + long i + ) const; + + void swap( + layout& item + ); + + long nr ( + ) const; + + long nc ( + ) const; + + void set_size ( + long nr_, + long nc_ + ); + }; + }; + !*/ + +// ---------------------------------------------------------------------------------------- + + struct row_major_layout + { + // if a matrix is bigger than this many bytes then don't put it on the stack + const static size_t max_stack_based_size = 256; + + // this is a hack to avoid a compile time error in visual studio 8. I would just + // use sizeof(T) and be done with it but that won't compile. The idea here + // is to avoid using the stack allocation of the layout object if it + // is going to contain another matrix and also avoid asking for the sizeof() + // the contained matrix. + template <typename T> + struct get_sizeof_helper + { + const static std::size_t val = sizeof(T); + }; + + template <typename T, long NR, long NC, typename mm, typename l> + struct get_sizeof_helper<matrix<T,NR,NC,mm,l> > + { + const static std::size_t val = 1000000; + }; + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager, + int val = static_switch < + // when the sizes are all non zero and small + (num_rows*num_cols*get_sizeof_helper<T>::val <= max_stack_based_size) && (num_rows != 0 && num_cols != 0), + // when the sizes are all non zero and big + (num_rows*num_cols*get_sizeof_helper<T>::val > max_stack_based_size) && (num_rows != 0 && num_cols != 0), + num_rows == 0 && num_cols != 0, + num_rows != 0 && num_cols == 0, + num_rows == 0 && num_cols == 0 + >::value + > + class layout ; + /*! + WHAT THIS OBJECT REPRESENTS + This object represents the actual allocation of space for a matrix. + Small matrices allocate all their data on the stack and bigger ones + use a memory_manager to get their memory. + !*/ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,1> : noncopyable // when the sizes are all non zero and small + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout() {} + + T& operator() ( + long r, + long c + ) { return *(data+r*num_cols + c); } + + const T& operator() ( + long r, + long c + ) const { return *(data+r*num_cols + c); } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + for (long r = 0; r < num_rows; ++r) + { + for (long c = 0; c < num_cols; ++c) + { + exchange((*this)(r,c),item(r,c)); + } + } + } + + long nr ( + ) const { return num_rows; } + + long nc ( + ) const { return num_cols; } + + void set_size ( + long , + long + ) + { + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + private: + T data[num_rows*num_cols]; + }; + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,2> : noncopyable // when the sizes are all non zero and big + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ) { data = pool.allocate_array(num_rows*num_cols); } + + ~layout () + { pool.deallocate_array(data); } + + T& operator() ( + long r, + long c + ) { return data[r*num_cols + c]; } + + const T& operator() ( + long r, + long c + ) const { return data[r*num_cols + c]; } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + std::swap(item.data,data); + pool.swap(item.pool); + } + + long nr ( + ) const { return num_rows; } + + long nc ( + ) const { return num_cols; } + + void set_size ( + long , + long + ) + { + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + private: + + T* data; + typename mem_manager::template rebind<T>::other pool; + }; + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,3> : noncopyable // when num_rows == 0 && num_cols != 0, + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ):data(0), nr_(0) { } + + ~layout () + { + if (data) + pool.deallocate_array(data); + } + + T& operator() ( + long r, + long c + ) { return data[r*num_cols + c]; } + + const T& operator() ( + long r, + long c + ) const { return data[r*num_cols + c]; } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + std::swap(item.data,data); + std::swap(item.nr_,nr_); + pool.swap(item.pool); + } + + long nr ( + ) const { return nr_; } + + long nc ( + ) const { return num_cols; } + + void set_size ( + long nr, + long nc + ) + { + if (data) + { + pool.deallocate_array(data); + } + data = pool.allocate_array(nr*nc); + nr_ = nr; + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + private: + + T* data; + long nr_; + typename mem_manager::template rebind<T>::other pool; + }; + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,4> : noncopyable // when num_rows != 0 && num_cols == 0 + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ):data(0), nc_(0) { } + + ~layout () + { + if (data) + { + pool.deallocate_array(data); + } + } + + T& operator() ( + long r, + long c + ) { return data[r*nc_ + c]; } + + const T& operator() ( + long r, + long c + ) const { return data[r*nc_ + c]; } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + std::swap(item.data,data); + std::swap(item.nc_,nc_); + pool.swap(item.pool); + } + + long nr ( + ) const { return num_rows; } + + long nc ( + ) const { return nc_; } + + void set_size ( + long nr, + long nc + ) + { + if (data) + { + pool.deallocate_array(data); + } + data = pool.allocate_array(nr*nc); + nc_ = nc; + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + private: + + T* data; + long nc_; + typename mem_manager::template rebind<T>::other pool; + }; + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,5> : noncopyable // when num_rows == 0 && num_cols == 0 + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ):data(0), nr_(0), nc_(0) { } + + ~layout () + { + if (data) + { + pool.deallocate_array(data); + } + } + + T& operator() ( + long r, + long c + ) { return data[r*nc_ + c]; } + + const T& operator() ( + long r, + long c + ) const { return data[r*nc_ + c]; } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + std::swap(item.data,data); + std::swap(item.nc_,nc_); + std::swap(item.nr_,nr_); + pool.swap(item.pool); + } + + long nr ( + ) const { return nr_; } + + long nc ( + ) const { return nc_; } + + void set_size ( + long nr, + long nc + ) + { + if (data) + { + pool.deallocate_array(data); + } + data = pool.allocate_array(nr*nc); + nr_ = nr; + nc_ = nc; + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + private: + T* data; + long nr_; + long nc_; + typename mem_manager::template rebind<T>::other pool; + }; + + }; + +// ---------------------------------------------------------------------------------------- + + struct column_major_layout + { + // if a matrix is bigger than this many bytes then don't put it on the stack + const static size_t max_stack_based_size = 256; + + + // this is a hack to avoid a compile time error in visual studio 8. I would just + // use sizeof(T) and be done with it but that won't compile. The idea here + // is to avoid using the stack allocation of the layout object if it + // is going to contain another matrix and also avoid asking for the sizeof() + // the contained matrix. + template <typename T> + struct get_sizeof_helper + { + const static std::size_t val = sizeof(T); + }; + + template <typename T, long NR, long NC, typename mm, typename l> + struct get_sizeof_helper<matrix<T,NR,NC,mm,l> > + { + const static std::size_t val = 1000000; + }; + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager, + int val = static_switch < + // when the sizes are all non zero and small + (num_rows*num_cols*get_sizeof_helper<T>::val <= max_stack_based_size) && (num_rows != 0 && num_cols != 0), + // when the sizes are all non zero and big + (num_rows*num_cols*get_sizeof_helper<T>::val > max_stack_based_size) && (num_rows != 0 && num_cols != 0), + num_rows == 0 && num_cols != 0, + num_rows != 0 && num_cols == 0, + num_rows == 0 && num_cols == 0 + >::value + > + class layout ; + /*! + WHAT THIS OBJECT REPRESENTS + This object represents the actual allocation of space for a matrix. + Small matrices allocate all their data on the stack and bigger ones + use a memory_manager to get their memory. + !*/ + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,1> : noncopyable // when the sizes are all non zero and small + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout() {} + + T& operator() ( + long r, + long c + ) { return *(data+c*num_rows + r); } + + const T& operator() ( + long r, + long c + ) const { return *(data+c*num_rows + r); } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + for (long r = 0; r < num_rows; ++r) + { + for (long c = 0; c < num_cols; ++c) + { + exchange((*this)(r,c),item(r,c)); + } + } + } + + long nr ( + ) const { return num_rows; } + + long nc ( + ) const { return num_cols; } + + void set_size ( + long, + long + ) + { + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + private: + T data[num_cols*num_rows]; + }; + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,2> : noncopyable // when the sizes are all non zero and big + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ) { data = pool.allocate_array(num_rows*num_cols); } + + ~layout () + { pool.deallocate_array(data); } + + T& operator() ( + long r, + long c + ) { return data[c*num_rows + r]; } + + const T& operator() ( + long r, + long c + ) const { return data[c*num_rows + r]; } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + std::swap(item.data,data); + pool.swap(item.pool); + } + + long nr ( + ) const { return num_rows; } + + long nc ( + ) const { return num_cols; } + + void set_size ( + long , + long + ) + { + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + private: + + T* data; + typename mem_manager::template rebind<T>::other pool; + }; + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,3> : noncopyable // when num_rows == 0 && num_cols != 0, + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ):data(0), nr_(0) { } + + ~layout () + { + if (data) + pool.deallocate_array(data); + } + + T& operator() ( + long r, + long c + ) { return data[c*nr_ + r]; } + + const T& operator() ( + long r, + long c + ) const { return data[c*nr_ + r]; } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + std::swap(item.data,data); + std::swap(item.nr_,nr_); + pool.swap(item.pool); + } + + long nr ( + ) const { return nr_; } + + long nc ( + ) const { return num_cols; } + + void set_size ( + long nr, + long nc + ) + { + if (data) + { + pool.deallocate_array(data); + } + data = pool.allocate_array(nr*nc); + nr_ = nr; + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + private: + + T* data; + long nr_; + typename mem_manager::template rebind<T>::other pool; + }; + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,4> : noncopyable // when num_rows != 0 && num_cols == 0 + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ):data(0), nc_(0) { } + + ~layout () + { + if (data) + { + pool.deallocate_array(data); + } + } + + T& operator() ( + long r, + long c + ) { return data[c*num_rows + r]; } + + const T& operator() ( + long r, + long c + ) const { return data[c*num_rows + r]; } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + std::swap(item.data,data); + std::swap(item.nc_,nc_); + pool.swap(item.pool); + } + + long nr ( + ) const { return num_rows; } + + long nc ( + ) const { return nc_; } + + void set_size ( + long nr, + long nc + ) + { + if (data) + { + pool.deallocate_array(data); + } + data = pool.allocate_array(nr*nc); + nc_ = nc; + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + private: + + T* data; + long nc_; + typename mem_manager::template rebind<T>::other pool; + }; + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager + > + class layout<T,num_rows,num_cols,mem_manager,5> : noncopyable // when num_rows == 0 && num_cols == 0 + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ):data(0), nr_(0), nc_(0) { } + + ~layout () + { + if (data) + { + pool.deallocate_array(data); + } + } + + T& operator() ( + long r, + long c + ) { return data[c*nr_ + r]; } + + const T& operator() ( + long r, + long c + ) const { return data[c*nr_ + r]; } + + T& operator() ( + long i + ) { return data[i]; } + + const T& operator() ( + long i + ) const { return data[i]; } + + void swap( + layout& item + ) + { + std::swap(item.data,data); + std::swap(item.nc_,nc_); + std::swap(item.nr_,nr_); + pool.swap(item.pool); + } + +#ifdef MATLAB_MEX_FILE + void _private_set_mxArray ( mxArray* ) { DLIB_CASSERT(false, "This function should never be called."); } + mxArray* _private_release_mxArray(){DLIB_CASSERT(false, "This function should never be called."); } + void _private_mark_owned_by_matlab() {DLIB_CASSERT(false, "This function should never be called."); } + bool _private_is_owned_by_matlab() const { return false; } +#endif + + long nr ( + ) const { return nr_; } + + long nc ( + ) const { return nc_; } + + void set_size ( + long nr, + long nc + ) + { + if (data) + { + pool.deallocate_array(data); + } + data = pool.allocate_array(nr*nc); + nr_ = nr; + nc_ = nc; + } + + private: + T* data; + long nr_; + long nc_; + typename mem_manager::template rebind<T>::other pool; + }; + +#ifdef MATLAB_MEX_FILE + template < + long num_rows, + long num_cols + > + class layout<double,num_rows,num_cols,default_memory_manager,5> : noncopyable // when num_rows == 0 && num_cols == 0 + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ): data(0), nr_(0), nc_(0), owned_by_matlab(false),set_by_private_set_mxArray(false),mem(0) { } + + ~layout () + { + if (owned_by_matlab) + { + if (!set_by_private_set_mxArray && mem) + { + mxDestroyArray(mem); + mem = 0; + data = 0; + } + } + else if (data) + { + delete [] data; + data = 0; + } + } + + double& operator() ( + long r, + long c + ) { return data[c*nr_ + r]; } + + const double& operator() ( + long r, + long c + ) const { return data[c*nr_ + r]; } + + double& operator() ( + long i + ) { return data[i]; } + + const double& operator() ( + long i + ) const { return data[i]; } + + void _private_set_mxArray ( + mxArray* mem_ + ) + { + DLIB_CASSERT(mem == 0 && data == 0,"You can't call this function on an already allocated matrix."); + // We don't own the pointer, so make note of that so we won't try to free + // it. + set_by_private_set_mxArray = true; + owned_by_matlab = true; + mem = mem_; + data = mxGetPr(mem); + nr_ = mxGetM(mem); + nc_ = mxGetN(mem); + } + + mxArray* _private_release_mxArray() + { + DLIB_CASSERT(owned_by_matlab,""); + mxArray* temp = mem; + mem = 0; + set_by_private_set_mxArray = false; + data = 0; + nr_ = 0; + nc_ = 0; + return temp; + } + + void _private_mark_owned_by_matlab() + { + DLIB_CASSERT(mem == 0 && data == 0,"You can't say a matrix should be owned by matlab after it's been allocated."); + owned_by_matlab = true; + } + bool _private_is_owned_by_matlab() const + { + return owned_by_matlab; + } + + void swap( + layout& item + ) + { + std::swap(item.owned_by_matlab,owned_by_matlab); + std::swap(item.set_by_private_set_mxArray,set_by_private_set_mxArray); + std::swap(item.mem,mem); + std::swap(item.data,data); + std::swap(item.nc_,nc_); + std::swap(item.nr_,nr_); + } + + long nr ( + ) const { return nr_; } + + long nc ( + ) const { return nc_; } + + void set_size ( + long nr, + long nc + ) + { + if (owned_by_matlab) + { + if (!set_by_private_set_mxArray && mem) + { + mxDestroyArray(mem); + mem = 0; + data = 0; + } + set_by_private_set_mxArray = false; + + mem = mxCreateDoubleMatrix(nr, nc, mxREAL); + if (mem == 0) + throw std::bad_alloc(); + data = mxGetPr(mem); + } + else + { + if (data) + delete [] data; + data = new double[nr*nc]; + } + nr_ = nr; + nc_ = nc; + } + + private: + double* data; + long nr_; + long nc_; + bool owned_by_matlab; + bool set_by_private_set_mxArray; + mxArray* mem; + }; + + template < + long num_rows, + long num_cols + > + class layout<float,num_rows,num_cols,default_memory_manager,5> : noncopyable // when num_rows == 0 && num_cols == 0 + { + public: + const static long NR = num_rows; + const static long NC = num_cols; + + layout ( + ): data(0), nr_(0), nc_(0), owned_by_matlab(false),set_by_private_set_mxArray(false),mem(0) { } + + ~layout () + { + if (owned_by_matlab) + { + if (!set_by_private_set_mxArray && mem) + { + mxDestroyArray(mem); + mem = 0; + data = 0; + } + } + else if (data) + { + delete [] data; + data = 0; + } + } + + float& operator() ( + long r, + long c + ) { return data[c*nr_ + r]; } + + const float& operator() ( + long r, + long c + ) const { return data[c*nr_ + r]; } + + float& operator() ( + long i + ) { return data[i]; } + + const float& operator() ( + long i + ) const { return data[i]; } + + void _private_set_mxArray ( + mxArray* mem_ + ) + { + DLIB_CASSERT(mem == 0 && data == 0,"You can't call this function on an already allocated matrix."); + // We don't own the pointer, so make note of that so we won't try to free + // it. + set_by_private_set_mxArray = true; + owned_by_matlab = true; + mem = mem_; + data = (float*)mxGetData(mem); + nr_ = mxGetM(mem); + nc_ = mxGetN(mem); + } + + mxArray* _private_release_mxArray() + { + DLIB_CASSERT(owned_by_matlab,""); + mxArray* temp = mem; + mem = 0; + set_by_private_set_mxArray = false; + data = 0; + nr_ = 0; + nc_ = 0; + return temp; + } + + void _private_mark_owned_by_matlab() + { + DLIB_CASSERT(mem == 0 && data == 0,"You can't say a matrix should be owned by matlab after it's been allocated."); + owned_by_matlab = true; + } + bool _private_is_owned_by_matlab() const + { + return owned_by_matlab; + } + + void swap( + layout& item + ) + { + std::swap(item.owned_by_matlab,owned_by_matlab); + std::swap(item.set_by_private_set_mxArray,set_by_private_set_mxArray); + std::swap(item.mem,mem); + std::swap(item.data,data); + std::swap(item.nc_,nc_); + std::swap(item.nr_,nr_); + } + + long nr ( + ) const { return nr_; } + + long nc ( + ) const { return nc_; } + + void set_size ( + long nr, + long nc + ) + { + if (owned_by_matlab) + { + if (!set_by_private_set_mxArray && mem) + { + mxDestroyArray(mem); + mem = 0; + data = 0; + } + set_by_private_set_mxArray = false; + + mem = mxCreateNumericMatrix(nr, nc, mxSINGLE_CLASS, mxREAL); + if (mem == 0) + throw std::bad_alloc(); + data = (float*)mxGetData(mem); + } + else + { + if (data) + delete [] data; + data = new float[nr*nc]; + } + nr_ = nr; + nc_ = nc; + } + + private: + float* data; + long nr_; + long nc_; + bool owned_by_matlab; + bool set_by_private_set_mxArray; + mxArray* mem; + }; +#endif + + }; + +// ---------------------------------------------------------------------------------------- + +} + +#if defined(__GNUC__) && ((__GNUC__ >= 4 && __GNUC_MINOR__ >= 8) || (__GNUC__ > 4)) +#pragma GCC diagnostic pop +#endif + +#endif // DLIB_MATRIx_DATA_LAYOUT_ + diff --git a/ml/dlib/dlib/matrix/matrix_data_layout_abstract.h b/ml/dlib/dlib/matrix/matrix_data_layout_abstract.h new file mode 100644 index 000000000..c3fa02be2 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_data_layout_abstract.h @@ -0,0 +1,40 @@ +// Copyright (C) 2008 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_DATA_LAYOUT_ABSTRACT_ +#ifdef DLIB_MATRIx_DATA_LAYOUT_ABSTRACT_ + +#include "../algs.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + struct row_major_layout + { + /*! + This is the default matrix layout. Any matrix object that uses this + layout will be laid out in memory in row major order. Additionally, + all elements are contiguous (e.g. there isn't any padding at the ends of + rows or anything like that) + !*/ + }; + +// ---------------------------------------------------------------------------------------- + + struct column_major_layout + { + /*! + Any matrix object that uses this layout will be laid out in memory in + column major order. Additionally, all elements are contiguous (e.g. + there isn't any padding at the ends of rows or anything like that) + !*/ + }; + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_DATA_LAYOUT_ABSTRACT_ + + diff --git a/ml/dlib/dlib/matrix/matrix_default_mul.h b/ml/dlib/dlib/matrix/matrix_default_mul.h new file mode 100644 index 000000000..493c641a8 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_default_mul.h @@ -0,0 +1,134 @@ +// Copyright (C) 2008 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_DEFAULT_MULTIPLY_ +#define DLIB_MATRIx_DEFAULT_MULTIPLY_ + +#include "../geometry/rectangle.h" +#include "matrix.h" +#include "matrix_utilities.h" +#include "../enable_if.h" + +namespace dlib +{ + +// ------------------------------------------------------------------------------------ + + namespace ma + { + template < typename EXP, typename enable = void > + struct matrix_is_vector { static const bool value = false; }; + template < typename EXP > + struct matrix_is_vector<EXP, typename enable_if_c<EXP::NR==1 || EXP::NC==1>::type > { static const bool value = true; }; + } + +// ------------------------------------------------------------------------------------ + + /*! This file defines the default_matrix_multiply() function. It is a function + that conforms to the following definition: + + template < + typename matrix_dest_type, + typename EXP1, + typename EXP2 + > + void default_matrix_multiply ( + matrix_dest_type& dest, + const EXP1& lhs, + const EXP2& rhs + ); + requires + - (lhs*rhs).destructively_aliases(dest) == false + - dest.nr() == (lhs*rhs).nr() + - dest.nc() == (lhs*rhs).nc() + ensures + - #dest == dest + lhs*rhs + !*/ + +// ------------------------------------------------------------------------------------ + + template < + typename matrix_dest_type, + typename EXP1, + typename EXP2 + > + typename enable_if_c<ma::matrix_is_vector<EXP1>::value == true || ma::matrix_is_vector<EXP2>::value == true>::type + default_matrix_multiply ( + matrix_dest_type& dest, + const EXP1& lhs, + const EXP2& rhs + ) + { + matrix_assign_default(dest, lhs*rhs, 1, true); + } + +// ------------------------------------------------------------------------------------ + + template < + typename matrix_dest_type, + typename EXP1, + typename EXP2 + > + typename enable_if_c<ma::matrix_is_vector<EXP1>::value == false && ma::matrix_is_vector<EXP2>::value == false>::type + default_matrix_multiply ( + matrix_dest_type& dest, + const EXP1& lhs, + const EXP2& rhs + ) + { + const long bs = 90; + + // if the matrices are small enough then just use the simple multiply algorithm + if (lhs.nc() <= 2 || rhs.nc() <= 2 || lhs.nr() <= 2 || rhs.nr() <= 2 || (lhs.size() <= bs*10 && rhs.size() <= bs*10) ) + { + matrix_assign_default(dest, lhs*rhs, 1, true); + } + else + { + // if the lhs and rhs matrices are big enough we should use a cache friendly + // algorithm that computes the matrix multiply in blocks. + + + // Loop over all the blocks in the lhs matrix + for (long r = 0; r < lhs.nr(); r+=bs) + { + for (long c = 0; c < lhs.nc(); c+=bs) + { + // make a rect for the block from lhs + rectangle lhs_block(c, r, std::min(c+bs-1,lhs.nc()-1), std::min(r+bs-1,lhs.nr()-1)); + + // now loop over all the rhs blocks we have to multiply with the current lhs block + for (long i = 0; i < rhs.nc(); i += bs) + { + // make a rect for the block from rhs + rectangle rhs_block(i, c, std::min(i+bs-1,rhs.nc()-1), std::min(c+bs-1,rhs.nr()-1)); + + // make a target rect in res + rectangle res_block(rhs_block.left(),lhs_block.top(), rhs_block.right(), lhs_block.bottom()); + + // This loop is optimized assuming that the data is laid out in + // row major order in memory. + for (long r = lhs_block.top(); r <= lhs_block.bottom(); ++r) + { + for (long c = lhs_block.left(); c<= lhs_block.right(); ++c) + { + const typename EXP2::type temp = lhs(r,c); + for (long i = rhs_block.left(); i <= rhs_block.right(); ++i) + { + dest(r,i) += rhs(c,i)*temp; + } + } + } + } + } + } + } + + + } + +// ------------------------------------------------------------------------------------ + +} + +#endif // DLIB_MATRIx_DEFAULT_MULTIPLY_ + diff --git a/ml/dlib/dlib/matrix/matrix_eigenvalue.h b/ml/dlib/dlib/matrix/matrix_eigenvalue.h new file mode 100644 index 000000000..3dc47e105 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_eigenvalue.h @@ -0,0 +1,1379 @@ +// Copyright (C) 2009 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +// This code was adapted from code from the JAMA part of NIST's TNT library. +// See: http://math.nist.gov/tnt/ +#ifndef DLIB_MATRIX_EIGENVALUE_DECOMPOSITION_H +#define DLIB_MATRIX_EIGENVALUE_DECOMPOSITION_H + +#include "matrix.h" +#include "matrix_utilities.h" +#include "matrix_subexp.h" +#include <algorithm> +#include <complex> +#include <cmath> + +#ifdef DLIB_USE_LAPACK +#include "lapack/geev.h" +#include "lapack/syev.h" +#include "lapack/syevr.h" +#endif + +#define DLIB_LAPACK_EIGENVALUE_DECOMP_SIZE_THRESH 4 + +namespace dlib +{ + + template < + typename matrix_exp_type + > + class eigenvalue_decomposition + { + + public: + + const static long NR = matrix_exp_type::NR; + const static long NC = matrix_exp_type::NC; + typedef typename matrix_exp_type::type type; + typedef typename matrix_exp_type::mem_manager_type mem_manager_type; + typedef typename matrix_exp_type::layout_type layout_type; + + typedef typename matrix_exp_type::matrix_type matrix_type; + typedef matrix<type,NR,1,mem_manager_type,layout_type> column_vector_type; + + typedef matrix<std::complex<type>,0,0,mem_manager_type,layout_type> complex_matrix_type; + typedef matrix<std::complex<type>,NR,1,mem_manager_type,layout_type> complex_column_vector_type; + + + // You have supplied an invalid type of matrix_exp_type. You have + // to use this object with matrices that contain float or double type data. + COMPILE_TIME_ASSERT((is_same_type<float, type>::value || + is_same_type<double, type>::value )); + + + template <typename EXP> + eigenvalue_decomposition( + const matrix_exp<EXP>& A + ); + + template <typename EXP> + eigenvalue_decomposition( + const matrix_op<op_make_symmetric<EXP> >& A + ); + + long dim ( + ) const; + + const complex_column_vector_type get_eigenvalues ( + ) const; + + const column_vector_type& get_real_eigenvalues ( + ) const; + + const column_vector_type& get_imag_eigenvalues ( + ) const; + + const complex_matrix_type get_v ( + ) const; + + const complex_matrix_type get_d ( + ) const; + + const matrix_type& get_pseudo_v ( + ) const; + + const matrix_type get_pseudo_d ( + ) const; + + private: + + /** Row and column dimension (square matrix). */ + long n; + + bool issymmetric; + + /** Arrays for internal storage of eigenvalues. */ + + column_vector_type d; /* real part */ + column_vector_type e; /* img part */ + + /** Array for internal storage of eigenvectors. */ + matrix_type V; + + /** Array for internal storage of nonsymmetric Hessenberg form. + @serial internal storage of nonsymmetric Hessenberg form. + */ + matrix_type H; + + + /** Working storage for nonsymmetric algorithm. + @serial working storage for nonsymmetric algorithm. + */ + column_vector_type ort; + + // Symmetric Householder reduction to tridiagonal form. + void tred2(); + + + // Symmetric tridiagonal QL algorithm. + void tql2 (); + + + // Nonsymmetric reduction to Hessenberg form. + void orthes (); + + + // Complex scalar division. + type cdivr, cdivi; + void cdiv_(type xr, type xi, type yr, type yi); + + + // Nonsymmetric reduction from Hessenberg to real Schur form. + void hqr2 (); + }; + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Public member functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + eigenvalue_decomposition<matrix_exp_type>:: + eigenvalue_decomposition( + const matrix_exp<EXP>& A_ + ) + { + COMPILE_TIME_ASSERT((is_same_type<type, typename EXP::type>::value)); + + + const_temp_matrix<EXP> A(A_); + + // make sure requires clause is not broken + DLIB_ASSERT(A.nr() == A.nc() && A.size() > 0, + "\teigenvalue_decomposition::eigenvalue_decomposition(A)" + << "\n\tYou can only use this on square matrices" + << "\n\tA.nr(): " << A.nr() + << "\n\tA.nc(): " << A.nc() + << "\n\tA.size(): " << A.size() + << "\n\tthis: " << this + ); + + + n = A.nc(); + V.set_size(n,n); + d.set_size(n); + e.set_size(n); + + + issymmetric = true; + for (long j = 0; (j < n) && issymmetric; j++) + { + for (long i = 0; (i < n) && issymmetric; i++) + { + issymmetric = (A(i,j) == A(j,i)); + } + } + + if (issymmetric) + { + V = A; + +#ifdef DLIB_USE_LAPACK + if (A.nr() > DLIB_LAPACK_EIGENVALUE_DECOMP_SIZE_THRESH) + { + e = 0; + + // We could compute the result using syev() + //lapack::syev('V', 'L', V, d); + + // Instead, we use syevr because its faster and maybe more stable. + matrix_type tempA(A); + matrix<lapack::integer,0,0,mem_manager_type,layout_type> isupz; + + lapack::integer temp; + lapack::syevr('V','A','L',tempA,0,0,0,0,-1,temp,d,V,isupz); + return; + } +#endif + // Tridiagonalize. + tred2(); + + // Diagonalize. + tql2(); + + } + else + { + +#ifdef DLIB_USE_LAPACK + if (A.nr() > DLIB_LAPACK_EIGENVALUE_DECOMP_SIZE_THRESH) + { + matrix<type,0,0,mem_manager_type, column_major_layout> temp, vl, vr; + temp = A; + lapack::geev('N', 'V', temp, d, e, vl, vr); + V = vr; + return; + } +#endif + H = A; + + ort.set_size(n); + + // Reduce to Hessenberg form. + orthes(); + + // Reduce Hessenberg to real Schur form. + hqr2(); + } + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + eigenvalue_decomposition<matrix_exp_type>:: + eigenvalue_decomposition( + const matrix_op<op_make_symmetric<EXP> >& A + ) + { + COMPILE_TIME_ASSERT((is_same_type<type, typename EXP::type>::value)); + + + // make sure requires clause is not broken + DLIB_ASSERT(A.nr() == A.nc() && A.size() > 0, + "\teigenvalue_decomposition::eigenvalue_decomposition(A)" + << "\n\tYou can only use this on square matrices" + << "\n\tA.nr(): " << A.nr() + << "\n\tA.nc(): " << A.nc() + << "\n\tA.size(): " << A.size() + << "\n\tthis: " << this + ); + + + n = A.nc(); + V.set_size(n,n); + d.set_size(n); + e.set_size(n); + + + V = A; + +#ifdef DLIB_USE_LAPACK + if (A.nr() > DLIB_LAPACK_EIGENVALUE_DECOMP_SIZE_THRESH) + { + e = 0; + + // We could compute the result using syev() + //lapack::syev('V', 'L', V, d); + + // Instead, we use syevr because its faster and maybe more stable. + matrix_type tempA(A); + matrix<lapack::integer,0,0,mem_manager_type,layout_type> isupz; + + lapack::integer temp; + lapack::syevr('V','A','L',tempA,0,0,0,0,-1,temp,d,V,isupz); + return; + } +#endif + // Tridiagonalize. + tred2(); + + // Diagonalize. + tql2(); + + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename eigenvalue_decomposition<matrix_exp_type>::matrix_type& eigenvalue_decomposition<matrix_exp_type>:: + get_pseudo_v ( + ) const + { + return V; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + long eigenvalue_decomposition<matrix_exp_type>:: + dim ( + ) const + { + return V.nr(); + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename eigenvalue_decomposition<matrix_exp_type>::complex_column_vector_type eigenvalue_decomposition<matrix_exp_type>:: + get_eigenvalues ( + ) const + { + return complex_matrix(get_real_eigenvalues(), get_imag_eigenvalues()); + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename eigenvalue_decomposition<matrix_exp_type>::column_vector_type& eigenvalue_decomposition<matrix_exp_type>:: + get_real_eigenvalues ( + ) const + { + return d; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename eigenvalue_decomposition<matrix_exp_type>::column_vector_type& eigenvalue_decomposition<matrix_exp_type>:: + get_imag_eigenvalues ( + ) const + { + return e; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename eigenvalue_decomposition<matrix_exp_type>::complex_matrix_type eigenvalue_decomposition<matrix_exp_type>:: + get_d ( + ) const + { + return diagm(complex_matrix(get_real_eigenvalues(), get_imag_eigenvalues())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename eigenvalue_decomposition<matrix_exp_type>::complex_matrix_type eigenvalue_decomposition<matrix_exp_type>:: + get_v ( + ) const + { + complex_matrix_type CV(n,n); + + for (long i = 0; i < n; i++) + { + if (e(i) > 0) + { + set_colm(CV,i) = complex_matrix(colm(V,i), colm(V,i+1)); + } + else if (e(i) < 0) + { + set_colm(CV,i) = complex_matrix(colm(V,i), colm(V,i-1)); + } + else + { + set_colm(CV,i) = complex_matrix(colm(V,i), uniform_matrix<type>(n,1,0)); + } + } + + return CV; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename eigenvalue_decomposition<matrix_exp_type>::matrix_type eigenvalue_decomposition<matrix_exp_type>:: + get_pseudo_d ( + ) const + { + matrix_type D(n,n); + + for (long i = 0; i < n; i++) + { + for (long j = 0; j < n; j++) + { + D(i,j) = 0.0; + } + D(i,i) = d(i); + if (e(i) > 0) + { + D(i,i+1) = e(i); + } + else if (e(i) < 0) + { + D(i,i-1) = e(i); + } + } + + return D; + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Private member functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +// Symmetric Householder reduction to tridiagonal form. + template <typename matrix_exp_type> + void eigenvalue_decomposition<matrix_exp_type>:: + tred2() + { + using std::abs; + using std::sqrt; + + // This is derived from the Algol procedures tred2 by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + for (long j = 0; j < n; j++) + { + d(j) = V(n-1,j); + } + + // Householder reduction to tridiagonal form. + + for (long i = n-1; i > 0; i--) + { + + // Scale to avoid under/overflow. + + type scale = 0.0; + type h = 0.0; + for (long k = 0; k < i; k++) + { + scale = scale + abs(d(k)); + } + if (scale == 0.0) + { + e(i) = d(i-1); + for (long j = 0; j < i; j++) + { + d(j) = V(i-1,j); + V(i,j) = 0.0; + V(j,i) = 0.0; + } + } + else + { + + // Generate Householder vector. + + for (long k = 0; k < i; k++) + { + d(k) /= scale; + h += d(k) * d(k); + } + type f = d(i-1); + type g = sqrt(h); + if (f > 0) + { + g = -g; + } + e(i) = scale * g; + h = h - f * g; + d(i-1) = f - g; + for (long j = 0; j < i; j++) + { + e(j) = 0.0; + } + + // Apply similarity transformation to remaining columns. + + for (long j = 0; j < i; j++) + { + f = d(j); + V(j,i) = f; + g = e(j) + V(j,j) * f; + for (long k = j+1; k <= i-1; k++) + { + g += V(k,j) * d(k); + e(k) += V(k,j) * f; + } + e(j) = g; + } + f = 0.0; + for (long j = 0; j < i; j++) + { + e(j) /= h; + f += e(j) * d(j); + } + type hh = f / (h + h); + for (long j = 0; j < i; j++) + { + e(j) -= hh * d(j); + } + for (long j = 0; j < i; j++) + { + f = d(j); + g = e(j); + for (long k = j; k <= i-1; k++) + { + V(k,j) -= (f * e(k) + g * d(k)); + } + d(j) = V(i-1,j); + V(i,j) = 0.0; + } + } + d(i) = h; + } + + // Accumulate transformations. + + for (long i = 0; i < n-1; i++) + { + V(n-1,i) = V(i,i); + V(i,i) = 1.0; + type h = d(i+1); + if (h != 0.0) + { + for (long k = 0; k <= i; k++) + { + d(k) = V(k,i+1) / h; + } + for (long j = 0; j <= i; j++) + { + type g = 0.0; + for (long k = 0; k <= i; k++) + { + g += V(k,i+1) * V(k,j); + } + for (long k = 0; k <= i; k++) + { + V(k,j) -= g * d(k); + } + } + } + for (long k = 0; k <= i; k++) + { + V(k,i+1) = 0.0; + } + } + for (long j = 0; j < n; j++) + { + d(j) = V(n-1,j); + V(n-1,j) = 0.0; + } + V(n-1,n-1) = 1.0; + e(0) = 0.0; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + void eigenvalue_decomposition<matrix_exp_type>:: + tql2 () + { + using std::pow; + using std::min; + using std::max; + using std::abs; + + // This is derived from the Algol procedures tql2, by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + for (long i = 1; i < n; i++) + { + e(i-1) = e(i); + } + e(n-1) = 0.0; + + type f = 0.0; + type tst1 = 0.0; + const type eps = std::numeric_limits<type>::epsilon(); + for (long l = 0; l < n; l++) + { + + // Find small subdiagonal element + + tst1 = max(tst1,abs(d(l)) + abs(e(l))); + long m = l; + + // Original while-loop from Java code + while (m < n) + { + if (abs(e(m)) <= eps*tst1) + { + break; + } + m++; + } + if (m == n) + --m; + + + // If m == l, d(l) is an eigenvalue, + // otherwise, iterate. + + if (m > l) + { + long iter = 0; + do + { + iter = iter + 1; // (Could check iteration count here.) + + // Compute implicit shift + + type g = d(l); + type p = (d(l+1) - g) / (2.0 * e(l)); + type r = hypot(p,(type)1.0); + if (p < 0) + { + r = -r; + } + d(l) = e(l) / (p + r); + d(l+1) = e(l) * (p + r); + type dl1 = d(l+1); + type h = g - d(l); + for (long i = l+2; i < n; i++) + { + d(i) -= h; + } + f = f + h; + + // Implicit QL transformation. + + p = d(m); + type c = 1.0; + type c2 = c; + type c3 = c; + type el1 = e(l+1); + type s = 0.0; + type s2 = 0.0; + for (long i = m-1; i >= l; i--) + { + c3 = c2; + c2 = c; + s2 = s; + g = c * e(i); + h = c * p; + r = hypot(p,e(i)); + e(i+1) = s * r; + s = e(i) / r; + c = p / r; + p = c * d(i) - s * g; + d(i+1) = h + s * (c * g + s * d(i)); + + // Accumulate transformation. + + for (long k = 0; k < n; k++) + { + h = V(k,i+1); + V(k,i+1) = s * V(k,i) + c * h; + V(k,i) = c * V(k,i) - s * h; + } + } + p = -s * s2 * c3 * el1 * e(l) / dl1; + e(l) = s * p; + d(l) = c * p; + + // Check for convergence. + + } while (abs(e(l)) > eps*tst1); + } + d(l) = d(l) + f; + e(l) = 0.0; + } + + /* + The code to sort the eigenvalues and eigenvectors + has been removed from here since, in the non-symmetric case, + we can't sort the eigenvalues in a meaningful way. If we left this + code in here then the user might supply what they thought was a symmetric + matrix but was actually slightly non-symmetric due to rounding error + and then they would end up in the non-symmetric eigenvalue solver + where the eigenvalues don't end up getting sorted. So to avoid + any possible user confusion I'm just removing this. + */ + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + void eigenvalue_decomposition<matrix_exp_type>:: + orthes () + { + using std::abs; + using std::sqrt; + + // This is derived from the Algol procedures orthes and ortran, + // by Martin and Wilkinson, Handbook for Auto. Comp., + // Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutines in EISPACK. + + long low = 0; + long high = n-1; + + for (long m = low+1; m <= high-1; m++) + { + + // Scale column. + + type scale = 0.0; + for (long i = m; i <= high; i++) + { + scale = scale + abs(H(i,m-1)); + } + if (scale != 0.0) + { + + // Compute Householder transformation. + + type h = 0.0; + for (long i = high; i >= m; i--) + { + ort(i) = H(i,m-1)/scale; + h += ort(i) * ort(i); + } + type g = sqrt(h); + if (ort(m) > 0) + { + g = -g; + } + h = h - ort(m) * g; + ort(m) = ort(m) - g; + + // Apply Householder similarity transformation + // H = (I-u*u'/h)*H*(I-u*u')/h) + + for (long j = m; j < n; j++) + { + type f = 0.0; + for (long i = high; i >= m; i--) + { + f += ort(i)*H(i,j); + } + f = f/h; + for (long i = m; i <= high; i++) + { + H(i,j) -= f*ort(i); + } + } + + for (long i = 0; i <= high; i++) + { + type f = 0.0; + for (long j = high; j >= m; j--) + { + f += ort(j)*H(i,j); + } + f = f/h; + for (long j = m; j <= high; j++) + { + H(i,j) -= f*ort(j); + } + } + ort(m) = scale*ort(m); + H(m,m-1) = scale*g; + } + } + + // Accumulate transformations (Algol's ortran). + + for (long i = 0; i < n; i++) + { + for (long j = 0; j < n; j++) + { + V(i,j) = (i == j ? 1.0 : 0.0); + } + } + + for (long m = high-1; m >= low+1; m--) + { + if (H(m,m-1) != 0.0) + { + for (long i = m+1; i <= high; i++) + { + ort(i) = H(i,m-1); + } + for (long j = m; j <= high; j++) + { + type g = 0.0; + for (long i = m; i <= high; i++) + { + g += ort(i) * V(i,j); + } + // Double division avoids possible underflow + g = (g / ort(m)) / H(m,m-1); + for (long i = m; i <= high; i++) + { + V(i,j) += g * ort(i); + } + } + } + } + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + void eigenvalue_decomposition<matrix_exp_type>:: + cdiv_(type xr, type xi, type yr, type yi) + { + using std::abs; + type r,d; + if (abs(yr) > abs(yi)) + { + r = yi/yr; + d = yr + r*yi; + cdivr = (xr + r*xi)/d; + cdivi = (xi - r*xr)/d; + } + else + { + r = yr/yi; + d = yi + r*yr; + cdivr = (r*xr + xi)/d; + cdivi = (r*xi - xr)/d; + } + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + void eigenvalue_decomposition<matrix_exp_type>:: + hqr2 () + { + using std::pow; + using std::min; + using std::max; + using std::abs; + using std::sqrt; + + // This is derived from the Algol procedure hqr2, + // by Martin and Wilkinson, Handbook for Auto. Comp., + // Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + // Initialize + + long nn = this->n; + long n = nn-1; + long low = 0; + long high = nn-1; + const type eps = std::numeric_limits<type>::epsilon(); + type exshift = 0.0; + type p=0,q=0,r=0,s=0,z=0,t,w,x,y; + + // Store roots isolated by balanc and compute matrix norm + + type norm = 0.0; + for (long i = 0; i < nn; i++) + { + if ((i < low) || (i > high)) + { + d(i) = H(i,i); + e(i) = 0.0; + } + for (long j = max(i-1,0L); j < nn; j++) + { + norm = norm + abs(H(i,j)); + } + } + + // Outer loop over eigenvalue index + + long iter = 0; + while (n >= low) + { + + // Look for single small sub-diagonal element + + long l = n; + while (l > low) + { + s = abs(H(l-1,l-1)) + abs(H(l,l)); + if (s == 0.0) + { + s = norm; + } + if (abs(H(l,l-1)) < eps * s) + { + break; + } + l--; + } + + // Check for convergence + // One root found + + if (l == n) + { + H(n,n) = H(n,n) + exshift; + d(n) = H(n,n); + e(n) = 0.0; + n--; + iter = 0; + + // Two roots found + + } + else if (l == n-1) + { + w = H(n,n-1) * H(n-1,n); + p = (H(n-1,n-1) - H(n,n)) / 2.0; + q = p * p + w; + z = sqrt(abs(q)); + H(n,n) = H(n,n) + exshift; + H(n-1,n-1) = H(n-1,n-1) + exshift; + x = H(n,n); + + // type pair + + if (q >= 0) + { + if (p >= 0) + { + z = p + z; + } + else + { + z = p - z; + } + d(n-1) = x + z; + d(n) = d(n-1); + if (z != 0.0) + { + d(n) = x - w / z; + } + e(n-1) = 0.0; + e(n) = 0.0; + x = H(n,n-1); + s = abs(x) + abs(z); + p = x / s; + q = z / s; + r = sqrt(p * p+q * q); + p = p / r; + q = q / r; + + // Row modification + + for (long j = n-1; j < nn; j++) + { + z = H(n-1,j); + H(n-1,j) = q * z + p * H(n,j); + H(n,j) = q * H(n,j) - p * z; + } + + // Column modification + + for (long i = 0; i <= n; i++) + { + z = H(i,n-1); + H(i,n-1) = q * z + p * H(i,n); + H(i,n) = q * H(i,n) - p * z; + } + + // Accumulate transformations + + for (long i = low; i <= high; i++) + { + z = V(i,n-1); + V(i,n-1) = q * z + p * V(i,n); + V(i,n) = q * V(i,n) - p * z; + } + + // Complex pair + + } + else + { + d(n-1) = x + p; + d(n) = x + p; + e(n-1) = z; + e(n) = -z; + } + n = n - 2; + iter = 0; + + // No convergence yet + + } + else + { + + // Form shift + + x = H(n,n); + y = 0.0; + w = 0.0; + if (l < n) + { + y = H(n-1,n-1); + w = H(n,n-1) * H(n-1,n); + } + + // Wilkinson's original ad hoc shift + + if (iter == 10) + { + exshift += x; + for (long i = low; i <= n; i++) + { + H(i,i) -= x; + } + s = abs(H(n,n-1)) + abs(H(n-1,n-2)); + x = y = 0.75 * s; + w = -0.4375 * s * s; + } + + // MATLAB's new ad hoc shift + + if (iter == 30) + { + s = (y - x) / 2.0; + s = s * s + w; + if (s > 0) + { + s = sqrt(s); + if (y < x) + { + s = -s; + } + s = x - w / ((y - x) / 2.0 + s); + for (long i = low; i <= n; i++) + { + H(i,i) -= s; + } + exshift += s; + x = y = w = 0.964; + } + } + + iter = iter + 1; // (Could check iteration count here.) + + // Look for two consecutive small sub-diagonal elements + + long m = n-2; + while (m >= l) + { + z = H(m,m); + r = x - z; + s = y - z; + p = (r * s - w) / H(m+1,m) + H(m,m+1); + q = H(m+1,m+1) - z - r - s; + r = H(m+2,m+1); + s = abs(p) + abs(q) + abs(r); + p = p / s; + q = q / s; + r = r / s; + if (m == l) + { + break; + } + if (abs(H(m,m-1)) * (abs(q) + abs(r)) < + eps * (abs(p) * (abs(H(m-1,m-1)) + abs(z) + + abs(H(m+1,m+1))))) + { + break; + } + m--; + } + + for (long i = m+2; i <= n; i++) + { + H(i,i-2) = 0.0; + if (i > m+2) + { + H(i,i-3) = 0.0; + } + } + + // Double QR step involving rows l:n and columns m:n + + for (long k = m; k <= n-1; k++) + { + long notlast = (k != n-1); + if (k != m) + { + p = H(k,k-1); + q = H(k+1,k-1); + r = (notlast ? H(k+2,k-1) : 0.0); + x = abs(p) + abs(q) + abs(r); + if (x != 0.0) + { + p = p / x; + q = q / x; + r = r / x; + } + } + if (x == 0.0) + { + break; + } + s = sqrt(p * p + q * q + r * r); + if (p < 0) + { + s = -s; + } + if (s != 0) + { + if (k != m) + { + H(k,k-1) = -s * x; + } + else if (l != m) + { + H(k,k-1) = -H(k,k-1); + } + p = p + s; + x = p / s; + y = q / s; + z = r / s; + q = q / p; + r = r / p; + + // Row modification + + for (long j = k; j < nn; j++) + { + p = H(k,j) + q * H(k+1,j); + if (notlast) + { + p = p + r * H(k+2,j); + H(k+2,j) = H(k+2,j) - p * z; + } + H(k,j) = H(k,j) - p * x; + H(k+1,j) = H(k+1,j) - p * y; + } + + // Column modification + + for (long i = 0; i <= min(n,k+3); i++) + { + p = x * H(i,k) + y * H(i,k+1); + if (notlast) + { + p = p + z * H(i,k+2); + H(i,k+2) = H(i,k+2) - p * r; + } + H(i,k) = H(i,k) - p; + H(i,k+1) = H(i,k+1) - p * q; + } + + // Accumulate transformations + + for (long i = low; i <= high; i++) + { + p = x * V(i,k) + y * V(i,k+1); + if (notlast) + { + p = p + z * V(i,k+2); + V(i,k+2) = V(i,k+2) - p * r; + } + V(i,k) = V(i,k) - p; + V(i,k+1) = V(i,k+1) - p * q; + } + } // (s != 0) + } // k loop + } // check convergence + } // while (n >= low) + + // Backsubstitute to find vectors of upper triangular form + + if (norm == 0.0) + { + return; + } + + for (n = nn-1; n >= 0; n--) + { + p = d(n); + q = e(n); + + // Real vector + + if (q == 0) + { + long l = n; + H(n,n) = 1.0; + for (long i = n-1; i >= 0; i--) + { + w = H(i,i) - p; + r = 0.0; + for (long j = l; j <= n; j++) + { + r = r + H(i,j) * H(j,n); + } + if (e(i) < 0.0) + { + z = w; + s = r; + } + else + { + l = i; + if (e(i) == 0.0) + { + if (w != 0.0) + { + H(i,n) = -r / w; + } + else + { + H(i,n) = -r / (eps * norm); + } + + // Solve real equations + + } + else + { + x = H(i,i+1); + y = H(i+1,i); + q = (d(i) - p) * (d(i) - p) + e(i) * e(i); + t = (x * s - z * r) / q; + H(i,n) = t; + if (abs(x) > abs(z)) + { + H(i+1,n) = (-r - w * t) / x; + } + else + { + H(i+1,n) = (-s - y * t) / z; + } + } + + // Overflow control + + t = abs(H(i,n)); + if ((eps * t) * t > 1) + { + for (long j = i; j <= n; j++) + { + H(j,n) = H(j,n) / t; + } + } + } + } + + // Complex vector + + } + else if (q < 0) + { + long l = n-1; + + // Last vector component imaginary so matrix is triangular + + if (abs(H(n,n-1)) > abs(H(n-1,n))) + { + H(n-1,n-1) = q / H(n,n-1); + H(n-1,n) = -(H(n,n) - p) / H(n,n-1); + } + else + { + cdiv_(0.0,-H(n-1,n),H(n-1,n-1)-p,q); + H(n-1,n-1) = cdivr; + H(n-1,n) = cdivi; + } + H(n,n-1) = 0.0; + H(n,n) = 1.0; + for (long i = n-2; i >= 0; i--) + { + type ra,sa,vr,vi; + ra = 0.0; + sa = 0.0; + for (long j = l; j <= n; j++) + { + ra = ra + H(i,j) * H(j,n-1); + sa = sa + H(i,j) * H(j,n); + } + w = H(i,i) - p; + + if (e(i) < 0.0) + { + z = w; + r = ra; + s = sa; + } + else + { + l = i; + if (e(i) == 0) + { + cdiv_(-ra,-sa,w,q); + H(i,n-1) = cdivr; + H(i,n) = cdivi; + } + else + { + + // Solve complex equations + + x = H(i,i+1); + y = H(i+1,i); + vr = (d(i) - p) * (d(i) - p) + e(i) * e(i) - q * q; + vi = (d(i) - p) * 2.0 * q; + if ((vr == 0.0) && (vi == 0.0)) + { + vr = eps * norm * (abs(w) + abs(q) + + abs(x) + abs(y) + abs(z)); + } + cdiv_(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi); + H(i,n-1) = cdivr; + H(i,n) = cdivi; + if (abs(x) > (abs(z) + abs(q))) + { + H(i+1,n-1) = (-ra - w * H(i,n-1) + q * H(i,n)) / x; + H(i+1,n) = (-sa - w * H(i,n) - q * H(i,n-1)) / x; + } + else + { + cdiv_(-r-y*H(i,n-1),-s-y*H(i,n),z,q); + H(i+1,n-1) = cdivr; + H(i+1,n) = cdivi; + } + } + + // Overflow control + + t = max(abs(H(i,n-1)),abs(H(i,n))); + if ((eps * t) * t > 1) + { + for (long j = i; j <= n; j++) + { + H(j,n-1) = H(j,n-1) / t; + H(j,n) = H(j,n) / t; + } + } + } + } + } + } + + // Vectors of isolated roots + + for (long i = 0; i < nn; i++) + { + if (i < low || i > high) + { + for (long j = i; j < nn; j++) + { + V(i,j) = H(i,j); + } + } + } + + // Back transformation to get eigenvectors of original matrix + + for (long j = nn-1; j >= low; j--) + { + for (long i = low; i <= high; i++) + { + z = 0.0; + for (long k = low; k <= min(j,high); k++) + { + z = z + V(i,k) * H(k,j); + } + V(i,j) = z; + } + } + } + +// ---------------------------------------------------------------------------------------- + + +} + +#endif // DLIB_MATRIX_EIGENVALUE_DECOMPOSITION_H + + + + diff --git a/ml/dlib/dlib/matrix/matrix_exp.h b/ml/dlib/dlib/matrix/matrix_exp.h new file mode 100644 index 000000000..c0afb54c0 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_exp.h @@ -0,0 +1,271 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_EXP_h_ +#define DLIB_MATRIx_EXP_h_ + +#include "../algs.h" +#include "../is_kind.h" +#include "matrix_fwd.h" +#include "matrix_exp_abstract.h" +#include <iterator> + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + // We want to return the compile time constant if our NR and NC dimensions + // aren't zero but if they are then we want to call ref_.nx() and return + // the correct values. + template < typename exp_type, long NR > + struct get_nr_helper + { + static inline long get(const exp_type&) { return NR; } + }; + + template < typename exp_type > + struct get_nr_helper<exp_type,0> + { + static inline long get(const exp_type& m) { return m.nr(); } + }; + + template < typename exp_type, long NC > + struct get_nc_helper + { + static inline long get(const exp_type&) { return NC; } + }; + + template < typename exp_type > + struct get_nc_helper<exp_type,0> + { + static inline long get(const exp_type& m) { return m.nc(); } + }; + + 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 = EXP::cost; + }; + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> class matrix_exp; + template <typename EXP> + class matrix_exp_iterator : public std::iterator<std::forward_iterator_tag, typename matrix_traits<EXP>::type> + { + friend class matrix_exp<EXP>; + matrix_exp_iterator(const EXP& m_, long r_, long c_) + { + r = r_; + c = c_; + nc = m_.nc(); + m = &m_; + } + + public: + + matrix_exp_iterator() : r(0), c(0), nc(0), m(0) {} + + typedef typename matrix_traits<EXP>::type type; + typedef type value_type; + typedef typename matrix_traits<EXP>::const_ret_type const_ret_type; + + + bool operator == ( const matrix_exp_iterator& itr) const + { return r == itr.r && c == itr.c; } + + bool operator != ( const matrix_exp_iterator& itr) const + { return !(*this == itr); } + + matrix_exp_iterator& operator++() + { + ++c; + if (c==nc) + { + c = 0; + ++r; + } + return *this; + } + + matrix_exp_iterator operator++(int) + { + matrix_exp_iterator temp(*this); + ++(*this); + return temp; + } + + const_ret_type operator* () const { return (*m)(r,c); } + + private: + long r, c; + long nc; + const EXP* m; + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + class matrix_exp + { + /*! + REQUIREMENTS ON EXP + EXP should be something convertible to a matrix_exp. That is, + it should inherit from matrix_exp + !*/ + + public: + typedef typename matrix_traits<EXP>::type type; + typedef type value_type; + typedef typename matrix_traits<EXP>::const_ret_type const_ret_type; + typedef typename matrix_traits<EXP>::mem_manager_type mem_manager_type; + typedef typename matrix_traits<EXP>::layout_type layout_type; + const static long NR = matrix_traits<EXP>::NR; + const static long NC = matrix_traits<EXP>::NC; + const static long cost = matrix_traits<EXP>::cost; + + typedef matrix<type,NR,NC,mem_manager_type,layout_type> matrix_type; + typedef EXP exp_type; + typedef matrix_exp_iterator<EXP> iterator; + typedef matrix_exp_iterator<EXP> const_iterator; + + inline const_ret_type operator() ( + long r, + long c + ) const + { + DLIB_ASSERT(r < nr() && c < nc() && r >= 0 && c >= 0, + "\tconst type matrix_exp::operator(r,c)" + << "\n\tYou must give a valid row and column" + << "\n\tr: " << r + << "\n\tc: " << c + << "\n\tnr(): " << nr() + << "\n\tnc(): " << nc() + << "\n\tthis: " << this + ); + return ref()(r,c); + } + + const_ret_type operator() ( + long i + ) const + { + COMPILE_TIME_ASSERT(NC == 1 || NC == 0 || NR == 1 || NR == 0); + DLIB_ASSERT(nc() == 1 || nr() == 1, + "\tconst type matrix_exp::operator(i)" + << "\n\tYou can only use this operator on column or row vectors" + << "\n\ti: " << i + << "\n\tnr(): " << nr() + << "\n\tnc(): " << nc() + << "\n\tthis: " << this + ); + DLIB_ASSERT( ((nc() == 1 && i < nr()) || (nr() == 1 && i < nc())) && i >= 0, + "\tconst type matrix_exp::operator(i)" + << "\n\tYou must give a valid row/column number" + << "\n\ti: " << i + << "\n\tnr(): " << nr() + << "\n\tnc(): " << nc() + << "\n\tthis: " << this + ); + if (nc() == 1) + return ref()(i,0); + else + return ref()(0,i); + } + + long size ( + ) const { return nr()*nc(); } + + long nr ( + ) const { return get_nr_helper<exp_type,NR>::get(ref()); } + + long nc ( + ) const { return get_nc_helper<exp_type,NC>::get(ref()); } + + 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); } + + inline const exp_type& ref ( + ) const { return *static_cast<const exp_type*>(this); } + + inline operator const type ( + ) const + { + COMPILE_TIME_ASSERT(NC == 1 || NC == 0); + COMPILE_TIME_ASSERT(NR == 1 || NR == 0); + DLIB_ASSERT(nr() == 1 && nc() == 1, + "\tmatrix_exp::operator const type() const" + << "\n\tYou can only use this operator on a 1x1 matrix" + << "\n\tnr(): " << nr() + << "\n\tnc(): " << nc() + << "\n\tthis: " << this + ); + + // Put the expression contained in this matrix_exp into + // a temporary 1x1 matrix so that the expression will encounter + // all the overloads of matrix_assign() and have the chance to + // go through any applicable optimizations. + matrix<type,1,1,mem_manager_type,layout_type> temp(ref()); + return temp(0); + } + + const_iterator begin() const { return matrix_exp_iterator<EXP>(ref(),0,0); } + const_iterator end() const { return matrix_exp_iterator<EXP>(ref(),nr(),0); } + + protected: + matrix_exp() {} + matrix_exp(const matrix_exp& ) {} + + private: + + matrix_exp& operator= (const matrix_exp&); + }; + +// ---------------------------------------------------------------------------------------- + + // something is a matrix if it is convertible to a matrix_exp object + template <typename T> + struct is_matrix<T, typename enable_if<is_convertible<T, const matrix_exp<typename T::exp_type>& > >::type > + { static const bool value = true; }; + /* + is_matrix<T>::value == 1 if T is a matrix type else 0 + */ + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + class matrix_diag_exp : public matrix_exp<EXP> + { + /*! + This is a matrix expression type used to represent diagonal matrices. + That is, square matrices with all off diagonal elements equal to 0. + !*/ + + protected: + matrix_diag_exp() {} + matrix_diag_exp(const matrix_diag_exp& item ):matrix_exp<EXP>(item) {} + }; + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_EXP_h_ + diff --git a/ml/dlib/dlib/matrix/matrix_exp_abstract.h b/ml/dlib/dlib/matrix/matrix_exp_abstract.h new file mode 100644 index 000000000..14ad143c2 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_exp_abstract.h @@ -0,0 +1,210 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_EXP_ABSTRACT_ +#ifdef DLIB_MATRIx_EXP_ABSTRACT_ + +#include "matrix_fwd.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + class matrix_exp + { + /*! + REQUIREMENTS ON EXP + - must be an object that inherits publicly from matrix_exp (this class). + + WHAT THIS OBJECT REPRESENTS + This object represents an expression that evaluates to a matrix + of nr() rows and nc() columns. + + The reason for having an object that represents an expression is that it + allows us to use the "expression templates" technique to eliminate the + temporary matrix objects that would normally be returned from expressions + such as M = A+B+C+D; Normally each invocation of the + operator would + construct and return a temporary matrix object but using this technique we + can avoid creating all of these temporary objects and receive a large + speed boost. + + Note that every time you invoke operator() on this object it recomputes + its result which may not be what you want to do. For example, if you + are going to be accessing the same element over and over it might + be faster to assign the matrix_exp to a temporary matrix and then + use that temporary. + + + const_ret_type typedef (defined below) + The purpose of the const_ret_type typedef is to allow matrix expressions + to return their elements by reference when appropriate. So const_ret_type + should be one of the following types: + - const type + - const type& + !*/ + + public: + typedef typename EXP::type type; + typedef type value_type; // Redefined for compatibility with the STL + 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 cost = EXP::cost; + const static long NR = EXP::NR; + const static long NC = EXP::NC; + typedef matrix<type,NR,NC, mem_manager_type,layout_type> matrix_type; + typedef EXP exp_type; + typedef matrix_exp_iterator<EXP> iterator; + typedef matrix_exp_iterator<EXP> const_iterator; + + const_ret_type operator() ( + long r, + long c + ) const; + /*! + requires + - 0 <= r < nr() + - 0 <= c < nc() + ensures + - returns ref()(r,c) + (i.e. returns the value at the given row and column that would be in + the matrix represented by this matrix expression) + !*/ + + const_ret_type operator() ( + long i + ) const; + /*! + requires + - nc() == 1 || nr() == 1 (i.e. this must be a column or row vector) + - if (nc() == 1) then + - 0 <= i < nr() + - else + - 0 <= i < nc() + ensures + - if (nc() == 1) then + - returns (*this)(i,0) + - else + - returns (*this)(0,i) + !*/ + + operator const type ( + ) const; + /*! + requires + - nr() == 1 + - nc() == 1 + ensures + - returns (*this)(0,0) + !*/ + + long nr ( + ) const; + /*! + ensures + - returns the number of rows in this matrix expression. + !*/ + + long nc ( + ) const; + /*! + ensures + - returns the number of columns in this matrix expression. + !*/ + + long size ( + ) const; + /*! + ensures + - returns nr()*nc() + !*/ + + template <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const; + /*! + ensures + - if (A change to the state of item could cause a change to the state of *this + matrix_exp object. ) then + - returns true + - This happens when this matrix_exp contains item in some way. + - else + - returns false + !*/ + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& item + ) const; + /*! + ensures + - if (aliases(item)) then + - if (nr() != item.nr() || nc() != item.nc() + - returns true + (i.e. if this expression has different dimensions than item then + we have destructive aliasing) + + - returns true if the following assignment would evaluate incorrectly: + for (long r = 0; r < nr(); ++r) + for (long c = 0; c < nc(); ++c) + item(r,c) = (*this)(r,c) + - That is, if this matrix expression aliases item in such a way that a modification + to element item(r,c) causes a change in the value of something other than + (*this)(r,c) then this function returns true. + + - returns false if none of the above conditions say we should return true + - else + - returns false + !*/ + + inline const exp_type& ref ( + ) const; + /*! + ensures + - returns a reference to the expression contained in *this. + (i.e. returns *static_cast<const exp_type*>(this) ) + !*/ + + const_iterator begin( + ) const; + /*! + ensures + - returns a forward access iterator pointing to the first element in this + matrix expression. + - Since matrix_exp objects represent immutable views of a matrix, the + returned iterator does not allow the user to modify the matrix + expression's elements. + - The iterator will iterate over the elements of the matrix in row major + order. + !*/ + + const_iterator end( + ) const; + /*! + ensures + - returns a forward access iterator pointing to one past the end of the + last element in this matrix expression. + !*/ + + protected: + + // Only derived classes of matrix_exp may call the matrix_exp constructors. + matrix_exp(const matrix_exp&); + matrix_exp(); + + private: + // no one may ever use the assignment operator on a matrix_exp + matrix_exp& operator= (const matrix_exp&); + }; + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_EXP_ABSTRACT_ + + diff --git a/ml/dlib/dlib/matrix/matrix_expressions.h b/ml/dlib/dlib/matrix/matrix_expressions.h new file mode 100644 index 000000000..9f057d076 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_expressions.h @@ -0,0 +1,280 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_EXPRESSIONS_H_ +#define DLIB_MATRIx_EXPRESSIONS_H_ + +#include "matrix_fwd.h" + +#ifdef _MSC_VER +// This #pragma directive is also located in the algs.h file but for whatever +// reason visual studio 9 just ignores it when it is only there. + +// this is to disable the "'this' : used in base member initializer list" +// warning you get from some of the GUI objects since all the objects +// require that their parent class be passed into their constructor. +// In this case though it is totally safe so it is ok to disable this warning. +#pragma warning(disable : 4355) +#endif + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Helper templates for making operators used by expression objects +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename T> + class matrix_range_exp; + + template <typename T> + struct matrix_traits<matrix_range_exp<T> > + { + typedef T type; + typedef const T const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + const static long NR = 1; + const static long NC = 0; + const static long cost = 1; + }; + + template <typename T> + class matrix_range_exp : public matrix_exp<matrix_range_exp<T> > + { + public: + typedef typename matrix_traits<matrix_range_exp>::type type; + typedef typename matrix_traits<matrix_range_exp>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_range_exp>::mem_manager_type mem_manager_type; + const static long NR = matrix_traits<matrix_range_exp>::NR; + const static long NC = matrix_traits<matrix_range_exp>::NC; + const static long cost = matrix_traits<matrix_range_exp>::cost; + typedef typename matrix_traits<matrix_range_exp>::layout_type layout_type; + + + matrix_range_exp ( + T start_, + T end_ + ) + { + start = start_; + if (start_ <= end_) + inc = 1; + else + inc = -1; + nc_ = std::abs(end_ - start_) + 1; + } + matrix_range_exp ( + T start_, + T inc_, + T end_ + ) + { + start = start_; + nc_ = std::abs(end_ - start_)/inc_ + 1; + if (start_ <= end_) + inc = inc_; + else + inc = -inc_; + } + + matrix_range_exp ( + T start_, + T end_, + long num, + bool + ) + { + start = start_; + nc_ = num; + if (num > 1) + { + inc = (end_-start_)/(num-1); + } + else + { + inc = 0; + start = end_; + } + + } + + const_ret_type operator() ( + long, + long c + ) const { return start + c*inc; } + + const_ret_type operator() ( + long c + ) const { return start + c*inc; } + + template <typename U> + bool aliases ( + const matrix_exp<U>& + ) const { return false; } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& + ) const { return false; } + + long nr ( + ) const { return NR; } + + long nc ( + ) const { return nc_; } + + long nc_; + T start; + T inc; + }; + +// ---------------------------------------------------------------------------------------- + + template <typename T> + class matrix_log_range_exp; + + template <typename T> + struct matrix_traits<matrix_log_range_exp<T> > + { + typedef T type; + typedef const T const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + const static long NR = 1; + const static long NC = 0; + const static long cost = 1; + }; + + template <typename T> + class matrix_log_range_exp : public matrix_exp<matrix_log_range_exp<T> > + { + public: + typedef typename matrix_traits<matrix_log_range_exp>::type type; + typedef typename matrix_traits<matrix_log_range_exp>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_log_range_exp>::mem_manager_type mem_manager_type; + const static long NR = matrix_traits<matrix_log_range_exp>::NR; + const static long NC = matrix_traits<matrix_log_range_exp>::NC; + const static long cost = matrix_traits<matrix_log_range_exp>::cost; + typedef typename matrix_traits<matrix_log_range_exp>::layout_type layout_type; + + + matrix_log_range_exp ( + T start_, + T end_, + long num + ) + { + start = start_; + nc_ = num; + if (num > 1) + { + inc = (end_-start_)/(num-1); + } + else + { + inc = 0; + start = end_; + } + + } + + const_ret_type operator() ( + long, + long c + ) const { return std::pow((T)10,start + c*inc); } + + const_ret_type operator() ( + long c + ) const { return std::pow((T)10,start + c*inc); } + + template <typename U> + bool aliases ( + const matrix_exp<U>& + ) const { return false; } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& + ) const { return false; } + + long nr ( + ) const { return NR; } + + long nc ( + ) const { return nc_; } + + long nc_; + T start; + T inc; + }; + +// ---------------------------------------------------------------------------------------- + + template <long start, long inc_, long end> + class matrix_range_static_exp; + + template <long start, long inc_, long end> + struct matrix_traits<matrix_range_static_exp<start,inc_,end> > + { + typedef long type; + typedef const long const_ret_type; + typedef default_memory_manager mem_manager_type; + const static long NR = 1; + const static long NC = tabs<(end - start)>::value/inc_ + 1; + const static long cost = 1; + typedef row_major_layout layout_type; + }; + + template <long start, long inc_, long end_> + class matrix_range_static_exp : public matrix_exp<matrix_range_static_exp<start,inc_,end_> > + { + public: + typedef typename matrix_traits<matrix_range_static_exp>::type type; + typedef typename matrix_traits<matrix_range_static_exp>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_range_static_exp>::mem_manager_type mem_manager_type; + const static long NR = matrix_traits<matrix_range_static_exp>::NR; + const static long NC = matrix_traits<matrix_range_static_exp>::NC; + const static long cost = matrix_traits<matrix_range_static_exp>::cost; + typedef typename matrix_traits<matrix_range_static_exp>::layout_type layout_type; + + const static long inc = (start <= end_)?inc_:-inc_; + + + matrix_range_static_exp ( + ) {} + + const_ret_type operator() ( + long , + long c + ) const { return start + c*inc; } + + const_ret_type operator() ( + long c + ) const { return start + c*inc; } + + template <typename U> + bool aliases ( + const matrix_exp<U>& + ) const { return false; } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& + ) const { return false; } + + long nr ( + ) const { return NR; } + + long nc ( + ) const { return NC; } + + }; + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_EXPRESSIONS_H_ + diff --git a/ml/dlib/dlib/matrix/matrix_fft.h b/ml/dlib/dlib/matrix/matrix_fft.h new file mode 100644 index 000000000..fbca6d344 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_fft.h @@ -0,0 +1,846 @@ +// Copyright (C) 2013 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_FFt_Hh_ +#define DLIB_FFt_Hh_ + +#include "matrix_fft_abstract.h" +#include "matrix_utilities.h" +#include "../hash.h" +#include "../algs.h" + +#ifdef DLIB_USE_MKL_FFT +#include <mkl_dfti.h> +#endif + +// No using FFTW until it becomes thread safe! +#if 0 +#ifdef DLIB_USE_FFTW +#include <fftw3.h> +#endif // DLIB_USE_FFTW +#endif + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + inline bool is_power_of_two ( + const unsigned long& value + ) + { + if (value == 0) + return true; + else + return count_bits(value) == 1; + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + + // ------------------------------------------------------------------------------------ + + /* + The next few functions related to doing FFTs are derived from Stefan + Gustavson's (stegu@itn.liu.se) public domain 2D Fourier transformation code. + The code has a long history, originally a FORTRAN implementation published in: + Programming for Digital Signal Processing, IEEE Press 1979, Section 1, by G. D. + Bergland and M. T. Dolan. In 2003 it was cleaned up and turned into modern C + by Steven Gustavson. Davis King then rewrote it in modern C++ in 2014 and also + changed the transform so that the outputs are identical to those given from FFTW. + */ + + // ------------------------------------------------------------------------------------ + + /* Get binary log of integer argument - exact if n is a power of 2 */ + inline long fastlog2(long n) + { + long log = -1; + while(n) { + log++; + n >>= 1; + } + return log ; + } + + // ------------------------------------------------------------------------------------ + + /* Radix-2 iteration subroutine */ + template <typename T> + void R2TX(int nthpo, std::complex<T> *c0, std::complex<T> *c1) + { + for(int k=0; k<nthpo; k+=2) + { + std::complex<T> temp = c0[k] + c1[k]; + c1[k] = c0[k] - c1[k]; + c0[k] = temp; + } + } + + // ------------------------------------------------------------------------------------ + + /* Radix-4 iteration subroutine */ + template <typename T> + void R4TX(int nthpo, std::complex<T> *c0, std::complex<T> *c1, + std::complex<T> *c2, std::complex<T> *c3) + { + for(int k=0;k<nthpo;k+=4) + { + std::complex<T> t1, t2, t3, t4; + t1 = c0[k] + c2[k]; + t2 = c0[k] - c2[k]; + t3 = c1[k] + c3[k]; + t4 = c1[k] - c3[k]; + + c0[k] = t1 + t3; + c1[k] = t1 - t3; + c2[k] = std::complex<T>(t2.real()-t4.imag(), t2.imag()+t4.real()); + c3[k] = std::complex<T>(t2.real()+t4.imag(), t2.imag()-t4.real()); + } + } + + // ------------------------------------------------------------------------------------ + + template <typename T> + class twiddles + { + /*! + The point of this object is to cache the twiddle values so we don't + recompute them over and over inside R8TX(). + !*/ + public: + + twiddles() + { + data.resize(64); + } + + const std::complex<T>* get_twiddles ( + int p + ) + /*! + requires + - 0 <= p <= 64 + ensures + - returns a pointer to the twiddle factors needed by R8TX if nxtlt == 2^p + !*/ + { + // Compute the twiddle factors for this p value if we haven't done so + // already. + if (data[p].size() == 0) + { + const int nxtlt = 0x1 << p; + data[p].reserve(nxtlt*7); + const T twopi = 6.2831853071795865; /* 2.0 * pi */ + const T scale = twopi/(nxtlt*8.0); + std::complex<T> cs[7]; + for (int j = 0; j < nxtlt; ++j) + { + const T arg = j*scale; + cs[0] = std::complex<T>(std::cos(arg),std::sin(arg)); + cs[1] = cs[0]*cs[0]; + cs[2] = cs[1]*cs[0]; + cs[3] = cs[1]*cs[1]; + cs[4] = cs[2]*cs[1]; + cs[5] = cs[2]*cs[2]; + cs[6] = cs[3]*cs[2]; + data[p].insert(data[p].end(), cs, cs+7); + } + } + + return &data[p][0]; + } + + private: + std::vector<std::vector<std::complex<T> > > data; + }; + + // ---------------------------------------------------------------------------------------- + + /* Radix-8 iteration subroutine */ + template <typename T> + void R8TX(int nxtlt, int nthpo, int length, const std::complex<T>* cs, + std::complex<T> *cc0, std::complex<T> *cc1, std::complex<T> *cc2, std::complex<T> *cc3, + std::complex<T> *cc4, std::complex<T> *cc5, std::complex<T> *cc6, std::complex<T> *cc7) + { + const T irt2 = 0.707106781186548; /* 1.0/sqrt(2.0) */ + + for(int j=0; j<nxtlt; j++) + { + for(int k=j;k<nthpo;k+=length) + { + std::complex<T> a0, a1, a2, a3, a4, a5, a6, a7; + std::complex<T> b0, b1, b2, b3, b4, b5, b6, b7; + a0 = cc0[k] + cc4[k]; + a1 = cc1[k] + cc5[k]; + a2 = cc2[k] + cc6[k]; + a3 = cc3[k] + cc7[k]; + a4 = cc0[k] - cc4[k]; + a5 = cc1[k] - cc5[k]; + a6 = cc2[k] - cc6[k]; + a7 = cc3[k] - cc7[k]; + + b0 = a0 + a2; + b1 = a1 + a3; + b2 = a0 - a2; + b3 = a1 - a3; + + b4 = std::complex<T>(a4.real()-a6.imag(), a4.imag()+a6.real()); + b5 = std::complex<T>(a5.real()-a7.imag(), a5.imag()+a7.real()); + b6 = std::complex<T>(a4.real()+a6.imag(), a4.imag()-a6.real()); + b7 = std::complex<T>(a5.real()+a7.imag(), a5.imag()-a7.real()); + + const std::complex<T> tmp0(-b3.imag(), b3.real()); + const std::complex<T> tmp1(irt2*(b5.real()-b5.imag()), irt2*(b5.real()+b5.imag())); + const std::complex<T> tmp2(-irt2*(b7.real()+b7.imag()), irt2*(b7.real()-b7.imag())); + + cc0[k] = b0 + b1; + cc1[k] = b0 - b1; + cc2[k] = b2 + tmp0; + cc3[k] = b2 - tmp0; + cc4[k] = b4 + tmp1; + cc5[k] = b4 - tmp1; + cc6[k] = b6 + tmp2; + cc7[k] = b6 - tmp2; + if(j>0) + { + cc1[k] *= cs[3]; + cc2[k] *= cs[1]; + cc3[k] *= cs[5]; + cc4[k] *= cs[0]; + cc5[k] *= cs[4]; + cc6[k] *= cs[2]; + cc7[k] *= cs[6]; + } + } + + cs += 7; + } + } + + // ------------------------------------------------------------------------------------ + + template <typename T, long NR, long NC, typename MM, typename layout> + void fft1d_inplace(matrix<std::complex<T>,NR,NC,MM,layout>& data, bool do_backward_fft, twiddles<T>& cs) + /*! + requires + - is_vector(data) == true + - is_power_of_two(data.size()) == true + ensures + - This routine replaces the input std::complex<double> vector by its finite + discrete complex fourier transform if do_backward_fft==true. It replaces + the input std::complex<double> vector by its finite discrete complex + inverse fourier transform if do_backward_fft==false. + + The implementation is a radix-2 FFT, but with faster shortcuts for + radix-4 and radix-8. It performs as many radix-8 iterations as possible, + and then finishes with a radix-2 or -4 iteration if needed. + !*/ + { + COMPILE_TIME_ASSERT((is_same_type<double,T>::value || is_same_type<float,T>::value || is_same_type<long double,T>::value )); + + if (data.size() == 0) + return; + + std::complex<T>* const b = &data(0); + int L[16],L1,L2,L3,L4,L5,L6,L7,L8,L9,L10,L11,L12,L13,L14,L15; + int j1,j2,j3,j4,j5,j6,j7,j8,j9,j10,j11,j12,j13,j14; + int j, ij, ji; + int n2pow, n8pow, nthpo, ipass, nxtlt, length; + + n2pow = fastlog2(data.size()); + nthpo = data.size(); + + n8pow = n2pow/3; + + if(n8pow) + { + /* Radix 8 iterations */ + for(ipass=1;ipass<=n8pow;ipass++) + { + const int p = n2pow - 3*ipass; + nxtlt = 0x1 << p; + length = 8*nxtlt; + R8TX(nxtlt, nthpo, length, cs.get_twiddles(p), + b, b+nxtlt, b+2*nxtlt, b+3*nxtlt, + b+4*nxtlt, b+5*nxtlt, b+6*nxtlt, b+7*nxtlt); + } + } + + if(n2pow%3 == 1) + { + /* A final radix 2 iteration is needed */ + R2TX(nthpo, b, b+1); + } + + if(n2pow%3 == 2) + { + /* A final radix 4 iteration is needed */ + R4TX(nthpo, b, b+1, b+2, b+3); + } + + for(j=1;j<=15;j++) + { + L[j] = 1; + if(j-n2pow <= 0) L[j] = 0x1 << (n2pow + 1 - j); + } + + L15=L[1];L14=L[2];L13=L[3];L12=L[4];L11=L[5];L10=L[6];L9=L[7]; + L8=L[8];L7=L[9];L6=L[10];L5=L[11];L4=L[12];L3=L[13];L2=L[14];L1=L[15]; + + ij = 0; + + for(j1=0;j1<L1;j1++) + for(j2=j1;j2<L2;j2+=L1) + for(j3=j2;j3<L3;j3+=L2) + for(j4=j3;j4<L4;j4+=L3) + for(j5=j4;j5<L5;j5+=L4) + for(j6=j5;j6<L6;j6+=L5) + for(j7=j6;j7<L7;j7+=L6) + for(j8=j7;j8<L8;j8+=L7) + for(j9=j8;j9<L9;j9+=L8) + for(j10=j9;j10<L10;j10+=L9) + for(j11=j10;j11<L11;j11+=L10) + for(j12=j11;j12<L12;j12+=L11) + for(j13=j12;j13<L13;j13+=L12) + for(j14=j13;j14<L14;j14+=L13) + for(ji=j14;ji<L15;ji+=L14) + { + if(ij<ji) + swap(b[ij], b[ji]); + ij++; + } + + + // unscramble outputs + if(!do_backward_fft) + { + for(long i=1, j=data.size()-1; i<data.size()/2; i++,j--) + { + swap(b[j], b[i]); + } + } + } + + // ------------------------------------------------------------------------------------ + + template < typename T, long NR, long NC, typename MM, typename L > + void fft2d_inplace( + matrix<std::complex<T>,NR,NC,MM,L>& data, + bool do_backward_fft + ) + { + if (data.size() == 0) + return; + + matrix<std::complex<double> > buff; + twiddles<double> cs; + + // Compute transform row by row + for(long r=0; r<data.nr(); ++r) + { + buff = matrix_cast<std::complex<double> >(rowm(data,r)); + fft1d_inplace(buff, do_backward_fft, cs); + set_rowm(data,r) = matrix_cast<std::complex<T> >(buff); + } + + // Compute transform column by column + for(long c=0; c<data.nc(); ++c) + { + buff = matrix_cast<std::complex<double> >(colm(data,c)); + fft1d_inplace(buff, do_backward_fft, cs); + set_colm(data,c) = matrix_cast<std::complex<T> >(buff); + } + } + + // ---------------------------------------------------------------------------------------- + + template < + typename EXP, + typename T + > + void fft2d( + const matrix_exp<EXP>& data, + matrix<std::complex<T> >& data_out, + bool do_backward_fft + ) + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t matrix fft(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + if (data.size() == 0) + return; + + matrix<std::complex<double> > buff; + data_out.set_size(data.nr(), data.nc()); + twiddles<double> cs; + + // Compute transform row by row + for(long r=0; r<data.nr(); ++r) + { + buff = matrix_cast<std::complex<double> >(rowm(data,r)); + fft1d_inplace(buff, do_backward_fft, cs); + set_rowm(data_out,r) = matrix_cast<std::complex<T> >(buff); + } + + // Compute transform column by column + for(long c=0; c<data_out.nc(); ++c) + { + buff = matrix_cast<std::complex<double> >(colm(data_out,c)); + fft1d_inplace(buff, do_backward_fft, cs); + set_colm(data_out,c) = matrix_cast<std::complex<T> >(buff); + } + } + + // ------------------------------------------------------------------------------------ + + } // end namespace impl + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + matrix<typename EXP::type> fft (const matrix_exp<EXP>& data) + { + // You have to give a complex matrix + COMPILE_TIME_ASSERT(is_complex<typename EXP::type>::value); + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t matrix fft(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + if (data.nr() == 1 || data.nc() == 1) + { + matrix<typename EXP::type> temp(data); + impl::twiddles<typename EXP::type::value_type> cs; + impl::fft1d_inplace(temp, false, cs); + return temp; + } + else + { + matrix<typename EXP::type> temp; + impl::fft2d(data, temp, false); + return temp; + } + } + + template <typename EXP> + matrix<typename EXP::type> ifft (const matrix_exp<EXP>& data) + { + // You have to give a complex matrix + COMPILE_TIME_ASSERT(is_complex<typename EXP::type>::value); + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t matrix ifft(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + matrix<typename EXP::type> temp; + if (data.size() == 0) + return temp; + + if (data.nr() == 1 || data.nc() == 1) + { + temp = data; + impl::twiddles<typename EXP::type::value_type> cs; + impl::fft1d_inplace(temp, true, cs); + } + else + { + impl::fft2d(data, temp, true); + } + temp /= data.size(); + return temp; + } + +// ---------------------------------------------------------------------------------------- + + template < typename T, long NR, long NC, typename MM, typename L > + typename enable_if_c<NR==1||NC==1>::type fft_inplace (matrix<std::complex<T>,NR,NC,MM,L>& data) + // Note that we don't divide the outputs by data.size() so this isn't quite the inverse. + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t void fft_inplace(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + impl::twiddles<T> cs; + impl::fft1d_inplace(data, false, cs); + } + + template < typename T, long NR, long NC, typename MM, typename L > + typename disable_if_c<NR==1||NC==1>::type fft_inplace (matrix<std::complex<T>,NR,NC,MM,L>& data) + // Note that we don't divide the outputs by data.size() so this isn't quite the inverse. + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t void fft_inplace(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + impl::fft2d_inplace(data, false); + } + +// ---------------------------------------------------------------------------------------- + + template < typename T, long NR, long NC, typename MM, typename L > + typename enable_if_c<NR==1||NC==1>::type ifft_inplace (matrix<std::complex<T>,NR,NC,MM,L>& data) + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t void ifft_inplace(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + impl::twiddles<T> cs; + impl::fft1d_inplace(data, true, cs); + } + + template < typename T, long NR, long NC, typename MM, typename L > + typename disable_if_c<NR==1||NC==1>::type ifft_inplace (matrix<std::complex<T>,NR,NC,MM,L>& data) + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t void ifft_inplace(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + impl::fft2d_inplace(data, true); + } + +// ---------------------------------------------------------------------------------------- + + /* + I'm disabling any use of the FFTW bindings because FFTW is, as of this writing, not + threadsafe as a library. This means that if multiple threads were to make + concurrent calls to these fft routines then the program could crash. If at some + point FFTW is fixed I'll turn these bindings back on. + + See https://github.com/FFTW/fftw3/issues/16 + */ +#if 0 +#ifdef DLIB_USE_FFTW + + template <long NR, long NC, typename MM, typename L> + matrix<std::complex<double>,NR,NC,MM,L> call_fftw_fft( + const matrix<std::complex<double>,NR,NC,MM,L>& data + ) + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t matrix fft(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + if (data.size() == 0) + return data; + + matrix<std::complex<double>,NR,NC,MM,L> m2(data.nr(),data.nc()); + fftw_complex *in, *out; + fftw_plan p; + in = (fftw_complex*)&data(0,0); + out = (fftw_complex*)&m2(0,0); + if (data.nr() == 1 || data.nc() == 1) + p = fftw_plan_dft_1d(data.size(), in, out, FFTW_FORWARD, FFTW_ESTIMATE); + else + p = fftw_plan_dft_2d(data.nr(), data.nc(), in, out, FFTW_FORWARD, FFTW_ESTIMATE); + fftw_execute(p); + fftw_destroy_plan(p); + return m2; + } + + template <long NR, long NC, typename MM, typename L> + matrix<std::complex<double>,NR,NC,MM,L> call_fftw_ifft( + const matrix<std::complex<double>,NR,NC,MM,L>& data + ) + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t matrix ifft(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + if (data.size() == 0) + return data; + + matrix<std::complex<double>,NR,NC,MM,L> m2(data.nr(),data.nc()); + fftw_complex *in, *out; + fftw_plan p; + in = (fftw_complex*)&data(0,0); + out = (fftw_complex*)&m2(0,0); + if (data.nr() == 1 || data.nc() == 1) + p = fftw_plan_dft_1d(data.size(), in, out, FFTW_BACKWARD, FFTW_ESTIMATE); + else + p = fftw_plan_dft_2d(data.nr(), data.nc(), in, out, FFTW_BACKWARD, FFTW_ESTIMATE); + fftw_execute(p); + fftw_destroy_plan(p); + return m2; + } + +// ---------------------------------------------------------------------------------------- + +// call FFTW for these cases: + inline matrix<std::complex<double>,0,1> fft (const matrix<std::complex<double>,0,1>& data) {return call_fftw_fft(data);} + inline matrix<std::complex<double>,0,1> ifft(const matrix<std::complex<double>,0,1>& data) {return call_fftw_ifft(data)/data.size();} + inline matrix<std::complex<double>,1,0> fft (const matrix<std::complex<double>,1,0>& data) {return call_fftw_fft(data);} + inline matrix<std::complex<double>,1,0> ifft(const matrix<std::complex<double>,1,0>& data) {return call_fftw_ifft(data)/data.size();} + inline matrix<std::complex<double> > fft (const matrix<std::complex<double> >& data) {return call_fftw_fft(data);} + inline matrix<std::complex<double> > ifft(const matrix<std::complex<double> >& data) {return call_fftw_ifft(data)/data.size();} + + inline void fft_inplace (matrix<std::complex<double>,0,1>& data) {data = call_fftw_fft(data);} + inline void ifft_inplace(matrix<std::complex<double>,0,1>& data) {data = call_fftw_ifft(data);} + inline void fft_inplace (matrix<std::complex<double>,1,0>& data) {data = call_fftw_fft(data);} + inline void ifft_inplace(matrix<std::complex<double>,1,0>& data) {data = call_fftw_ifft(data);} + inline void fft_inplace (matrix<std::complex<double> >& data) {data = call_fftw_fft(data);} + inline void ifft_inplace(matrix<std::complex<double> >& data) {data = call_fftw_ifft(data);} + +#endif // DLIB_USE_FFTW +#endif // end of #if 0 + +// ---------------------------------------------------------------------------------------- + +#ifdef DLIB_USE_MKL_FFT + +#define DLIB_DFTI_CHECK_STATUS(s) \ + if((s) != 0 && !DftiErrorClass((s), DFTI_NO_ERROR)) \ + { \ + throw dlib::error(DftiErrorMessage((s))); \ + } + + template < long NR, long NC, typename MM, typename L > + matrix<std::complex<double>,NR,NC,MM,L> call_mkl_fft( + const matrix<std::complex<double>,NR,NC,MM,L>& data, + bool do_backward_fft) + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t matrix fft(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + if (data.size() == 0) + return data; + + DFTI_DESCRIPTOR_HANDLE h; + MKL_LONG status; + + if (data.nr() == 1 || data.nc() == 1) + { + status = DftiCreateDescriptor(&h, DFTI_DOUBLE, DFTI_COMPLEX, 1, data.size()); + DLIB_DFTI_CHECK_STATUS(status); + } + else + { + MKL_LONG size[2]; + size[0] = data.nr(); + size[1] = data.nc(); + + status = DftiCreateDescriptor(&h, DFTI_DOUBLE, DFTI_COMPLEX, 2, size); + DLIB_DFTI_CHECK_STATUS(status); + + MKL_LONG strides[3]; + strides[0] = 0; + strides[1] = size[1]; + strides[2] = 1; + + status = DftiSetValue(h, DFTI_INPUT_STRIDES, strides); + DLIB_DFTI_CHECK_STATUS(status); + status = DftiSetValue(h, DFTI_OUTPUT_STRIDES, strides); + DLIB_DFTI_CHECK_STATUS(status); + } + + status = DftiSetValue(h, DFTI_PLACEMENT, DFTI_NOT_INPLACE); + DLIB_DFTI_CHECK_STATUS(status); + + // Unless we use sequential mode, the fft results are not correct. + status = DftiSetValue(h, DFTI_THREAD_LIMIT, 1); + DLIB_DFTI_CHECK_STATUS(status); + + status = DftiCommitDescriptor(h); + DLIB_DFTI_CHECK_STATUS(status); + + matrix<std::complex<double>,NR,NC,MM,L> out(data.nr(), data.nc()); + + if (do_backward_fft) + status = DftiComputeBackward(h, (void *)(&data(0, 0)), &out(0,0)); + else + status = DftiComputeForward(h, (void *)(&data(0, 0)), &out(0,0)); + DLIB_DFTI_CHECK_STATUS(status); + + status = DftiFreeDescriptor(&h); + DLIB_DFTI_CHECK_STATUS(status); + + return out; + } + + template < long NR, long NC, typename MM, typename L > + void call_mkl_fft_inplace( + matrix<std::complex<double>,NR,NC,MM,L>& data, + bool do_backward_fft + ) + { + // make sure requires clause is not broken + DLIB_CASSERT(is_power_of_two(data.nr()) && is_power_of_two(data.nc()), + "\t void ifft_inplace(data)" + << "\n\t The number of rows and columns must be powers of two." + << "\n\t data.nr(): "<< data.nr() + << "\n\t data.nc(): "<< data.nc() + << "\n\t is_power_of_two(data.nr()): " << is_power_of_two(data.nr()) + << "\n\t is_power_of_two(data.nc()): " << is_power_of_two(data.nc()) + ); + + if (data.size() == 0) + return; + + DFTI_DESCRIPTOR_HANDLE h; + MKL_LONG status; + + if (data.nr() == 1 || data.nc() == 1) + { + status = DftiCreateDescriptor(&h, DFTI_DOUBLE, DFTI_COMPLEX, 1, data.size()); + DLIB_DFTI_CHECK_STATUS(status); + } + else + { + MKL_LONG size[2]; + size[0] = data.nr(); + size[1] = data.nc(); + + status = DftiCreateDescriptor(&h, DFTI_DOUBLE, DFTI_COMPLEX, 2, size); + DLIB_DFTI_CHECK_STATUS(status); + + MKL_LONG strides[3]; + strides[0] = 0; + strides[1] = size[1]; + strides[2] = 1; + + status = DftiSetValue(h, DFTI_INPUT_STRIDES, strides); + DLIB_DFTI_CHECK_STATUS(status); + } + + // Unless we use sequential mode, the fft results are not correct. + status = DftiSetValue(h, DFTI_THREAD_LIMIT, 1); + DLIB_DFTI_CHECK_STATUS(status); + + status = DftiCommitDescriptor(h); + DLIB_DFTI_CHECK_STATUS(status); + + if (do_backward_fft) + status = DftiComputeBackward(h, &data(0, 0)); + else + status = DftiComputeForward(h, &data(0, 0)); + DLIB_DFTI_CHECK_STATUS(status); + + status = DftiFreeDescriptor(&h); + DLIB_DFTI_CHECK_STATUS(status); + + return; + } + +// ---------------------------------------------------------------------------------------- + + // Call the MKL DFTI implementation in these cases + + inline matrix<std::complex<double>,0,1> fft (const matrix<std::complex<double>,0,1>& data) + { + return call_mkl_fft(data, false); + } + inline matrix<std::complex<double>,0,1> ifft(const matrix<std::complex<double>,0,1>& data) + { + return call_mkl_fft(data, true) / data.size(); + } + inline matrix<std::complex<double>,1,0> fft (const matrix<std::complex<double>,1,0>& data) + { + return call_mkl_fft(data, false); + } + inline matrix<std::complex<double>,1,0> ifft(const matrix<std::complex<double>,1,0>& data) + { + return call_mkl_fft(data, true) / data.size(); + } + inline matrix<std::complex<double> > fft (const matrix<std::complex<double> >& data) + { + return call_mkl_fft(data, false); + } + inline matrix<std::complex<double> > ifft(const matrix<std::complex<double> >& data) + { + return call_mkl_fft(data, true) / data.size(); + } + + inline void fft_inplace (matrix<std::complex<double>,0,1>& data) + { + call_mkl_fft_inplace(data, false); + } + inline void ifft_inplace(matrix<std::complex<double>,0,1>& data) + { + call_mkl_fft_inplace(data, true); + } + inline void fft_inplace (matrix<std::complex<double>,1,0>& data) + { + call_mkl_fft_inplace(data, false); + } + inline void ifft_inplace(matrix<std::complex<double>,1,0>& data) + { + call_mkl_fft_inplace(data, true); + } + + inline void fft_inplace (matrix<std::complex<double> >& data) + { + call_mkl_fft_inplace(data, false); + } + inline void ifft_inplace(matrix<std::complex<double> >& data) + { + call_mkl_fft_inplace(data, true); + } + +#endif // DLIB_USE_MKL_FFT + +// ---------------------------------------------------------------------------------------- +} + +#endif // DLIB_FFt_Hh_ + diff --git a/ml/dlib/dlib/matrix/matrix_fft_abstract.h b/ml/dlib/dlib/matrix/matrix_fft_abstract.h new file mode 100644 index 000000000..25cdfcaee --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_fft_abstract.h @@ -0,0 +1,118 @@ +// Copyright (C) 2013 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_FFt_ABSTRACT_Hh_ +#ifdef DLIB_FFt_ABSTRACT_Hh_ + +#include "matrix_abstract.h" +#include "../algs.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + bool is_power_of_two ( + const unsigned long& value + ); + /*! + ensures + - returns true if value contains a power of two and false otherwise. As a + special case, we also consider 0 to be a power of two. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + typename EXP::matrix_type fft ( + const matrix_exp<EXP>& data + ); + /*! + requires + - data contains elements of type std::complex<> that itself contains double, float, or long double. + - is_power_of_two(data.nr()) == true + - is_power_of_two(data.nc()) == true + ensures + - Computes the 1 or 2 dimensional discrete Fourier transform of the given data + matrix and returns it. In particular, we return a matrix D such that: + - D.nr() == data.nr() + - D.nc() == data.nc() + - D(0,0) == the DC term of the Fourier transform. + - starting with D(0,0), D contains progressively higher frequency components + of the input data. + - ifft(D) == D + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + typename EXP::matrix_type ifft ( + const matrix_exp<EXP>& data + ); + /*! + requires + - data contains elements of type std::complex<> that itself contains double, float, or long double. + - is_power_of_two(data.nr()) == true + - is_power_of_two(data.nc()) == true + ensures + - Computes the 1 or 2 dimensional inverse discrete Fourier transform of the + given data vector and returns it. In particular, we return a matrix D such + that: + - D.nr() == data.nr() + - D.nc() == data.nc() + - fft(D) == data + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename MM, + typename L + > + void fft_inplace ( + matrix<std::complex<T>,NR,NC,MM,L>& data + ); + /*! + requires + - data contains elements of type std::complex<> that itself contains double, float, or long double. + - is_power_of_two(data.nr()) == true + - is_power_of_two(data.nc()) == true + ensures + - This function is identical to fft() except that it does the FFT in-place. + That is, after this function executes we will have: + - #data == fft(data) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename MM, + typename L + > + void ifft_inplace ( + matrix<std::complex<T>,NR,NC,MM,L>& data + ); + /*! + requires + - data contains elements of type std::complex<> that itself contains double, float, or long double. + - is_power_of_two(data.nr()) == true + - is_power_of_two(data.nc()) == true + ensures + - This function is identical to ifft() except that it does the inverse FFT + in-place. That is, after this function executes we will have: + - #data == ifft(data)*data.size() + - Note that the output needs to be divided by data.size() to complete the + inverse transformation. + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_FFt_ABSTRACT_Hh_ + diff --git a/ml/dlib/dlib/matrix/matrix_fwd.h b/ml/dlib/dlib/matrix/matrix_fwd.h new file mode 100644 index 000000000..1f40a17a8 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_fwd.h @@ -0,0 +1,31 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_FWD +#define DLIB_MATRIx_FWD + +#include "../algs.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + struct row_major_layout; + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long num_rows = 0, + long num_cols = 0, + typename mem_manager = default_memory_manager, + typename layout = row_major_layout + > + class matrix; + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_FWD + diff --git a/ml/dlib/dlib/matrix/matrix_generic_image.h b/ml/dlib/dlib/matrix/matrix_generic_image.h new file mode 100644 index 000000000..0455af205 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_generic_image.h @@ -0,0 +1,110 @@ +// Copyright (C) 2014 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIX_GENERIC_iMAGE_Hh_ +#define DLIB_MATRIX_GENERIC_iMAGE_Hh_ + +#include "matrix.h" +#include "../image_processing/generic_image.h" + +namespace dlib +{ + template < + typename T, + long NR, + long NC, + typename MM + > + struct image_traits<matrix<T,NR,NC,MM> > + { + typedef T pixel_type; + }; + + template < + typename T, + long NR, + long NC, + typename MM + > + struct image_traits<const matrix<T,NR,NC,MM> > + { + typedef T pixel_type; + }; + + template < + typename T, + long NR, + long NC, + typename MM + > + inline long num_rows( const matrix<T,NR,NC,MM>& img) { return img.nr(); } + + template < + typename T, + long NR, + long NC, + typename MM + > + inline long num_columns( const matrix<T,NR,NC,MM>& img) { return img.nc(); } + + template < + typename T, + long NR, + long NC, + typename MM + > + inline void set_image_size( + matrix<T,NR,NC,MM>& img, + long rows, + long cols + ) { img.set_size(rows,cols); } + + template < + typename T, + long NR, + long NC, + typename MM + > + inline void* image_data( + matrix<T,NR,NC,MM>& img + ) + { + if (img.size() != 0) + return &img(0,0); + else + return 0; + } + + template < + typename T, + long NR, + long NC, + typename MM + > + inline const void* image_data( + const matrix<T,NR,NC,MM>& img + ) + { + if (img.size() != 0) + return &img(0,0); + else + return 0; + } + + template < + typename T, + long NR, + long NC, + typename MM + > + inline long width_step( + const matrix<T,NR,NC,MM>& img + ) + { + return img.nc()*sizeof(T); + } + +} + +#endif // DLIB_MATRIX_GENERIC_iMAGE_Hh_ + + diff --git a/ml/dlib/dlib/matrix/matrix_la.h b/ml/dlib/dlib/matrix/matrix_la.h new file mode 100644 index 000000000..35b5b42e2 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_la.h @@ -0,0 +1,1807 @@ +// Copyright (C) 2009 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_LA_FUNCTS_ +#define DLIB_MATRIx_LA_FUNCTS_ + +#include "matrix_la_abstract.h" +#include "matrix_utilities.h" +#include "../sparse_vector.h" +#include "../optimization/optimization_line_search.h" + +// The 4 decomposition objects described in the matrix_la_abstract.h file are +// actually implemented in the following 4 files. +#include "matrix_lu.h" +#include "matrix_qr.h" +#include "matrix_cholesky.h" +#include "matrix_eigenvalue.h" + +#ifdef DLIB_USE_LAPACK +#include "lapack/potrf.h" +#include "lapack/pbtrf.h" +#include "lapack/gesdd.h" +#include "lapack/gesvd.h" +#endif + +#include "../threads.h" + +#include <iostream> + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + enum svd_u_mode + { + SVD_NO_U, + SVD_SKINNY_U, + SVD_FULL_U + }; + + template < + typename EXP, + long qN, long qX, + long uM, long uN, + long vM, long vN, + typename MM1, + typename MM2, + typename MM3, + typename L1 + > + long svd4 ( + svd_u_mode u_mode, + bool withv, + const matrix_exp<EXP>& a, + matrix<typename EXP::type,uM,uN,MM1,L1>& u, + matrix<typename EXP::type,qN,qX,MM2,L1>& q, + matrix<typename EXP::type,vM,vN,MM3,L1>& v + ) + { + /* + Singular value decomposition. Translated to 'C' from the + original Algol code in "Handbook for Automatic Computation, + vol. II, Linear Algebra", Springer-Verlag. Note that this + published algorithm is considered to be the best and numerically + stable approach to computing the real-valued svd and is referenced + repeatedly in ieee journal papers, etc where the svd is used. + + This is almost an exact translation from the original, except that + an iteration counter is added to prevent stalls. This corresponds + to similar changes in other translations. + + Returns an error code = 0, if no errors and 'k' if a failure to + converge at the 'kth' singular value. + + USAGE: given the singular value decomposition a = u * diagm(q) * trans(v) for an m*n + matrix a with m >= n ... + After the svd call u is an m x m matrix which is columnwise + orthogonal. q will be an n element vector consisting of singular values + and v an n x n orthogonal matrix. eps and tol are tolerance constants. + Suitable values are eps=1e-16 and tol=(1e-300)/eps if T == double. + + If u_mode == SVD_NO_U then u won't be computed and similarly if withv == false + then v won't be computed. If u_mode == SVD_SKINNY_U then u will be m x n instead of m x m. + */ + + + DLIB_ASSERT(a.nr() >= a.nc(), + "\tconst matrix_exp svd4()" + << "\n\tYou have given an invalidly sized matrix" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + ); + + + typedef typename EXP::type T; + +#ifdef DLIB_USE_LAPACK + matrix<typename EXP::type,0,0,MM1,L1> temp(a), vtemp; + + char jobu = 'A'; + char jobvt = 'A'; + if (u_mode == SVD_NO_U) + jobu = 'N'; + else if (u_mode == SVD_SKINNY_U) + jobu = 'S'; + if (withv == false) + jobvt = 'N'; + + int info; + if (jobu == jobvt) + { + info = lapack::gesdd(jobu, temp, q, u, vtemp); + } + else + { + info = lapack::gesvd(jobu, jobvt, temp, q, u, vtemp); + } + + // pad q with zeros if it isn't the length we want + if (q.nr() < a.nc()) + q = join_cols(q, zeros_matrix<T>(a.nc()-q.nr(),1)); + + if (withv) + v = trans(vtemp); + + return info; +#else + using std::abs; + using std::sqrt; + + T eps = std::numeric_limits<T>::epsilon(); + T tol = std::numeric_limits<T>::min()/eps; + + const long m = a.nr(); + const long n = a.nc(); + long i, j, k, l = 0, l1, iter, retval; + T c, f, g, h, s, x, y, z; + + matrix<T,qN,1,MM2> e(n,1); + q.set_size(n,1); + if (u_mode == SVD_FULL_U) + u.set_size(m,m); + else + u.set_size(m,n); + retval = 0; + + if (withv) + { + v.set_size(n,n); + } + + /* Copy 'a' to 'u' */ + for (i=0; i<m; i++) + { + for (j=0; j<n; j++) + u(i,j) = a(i,j); + } + + /* Householder's reduction to bidiagonal form. */ + g = x = 0.0; + for (i=0; i<n; i++) + { + e(i) = g; + s = 0.0; + l = i + 1; + + for (j=i; j<m; j++) + s += (u(j,i) * u(j,i)); + + if (s < tol) + g = 0.0; + else + { + f = u(i,i); + g = (f < 0) ? sqrt(s) : -sqrt(s); + h = f * g - s; + u(i,i) = f - g; + + for (j=l; j<n; j++) + { + s = 0.0; + + for (k=i; k<m; k++) + s += (u(k,i) * u(k,j)); + + f = s / h; + + for (k=i; k<m; k++) + u(k,j) += (f * u(k,i)); + } /* end j */ + } /* end s */ + + q(i) = g; + s = 0.0; + + for (j=l; j<n; j++) + s += (u(i,j) * u(i,j)); + + if (s < tol) + g = 0.0; + else + { + f = u(i,i+1); + g = (f < 0) ? sqrt(s) : -sqrt(s); + h = f * g - s; + u(i,i+1) = f - g; + + for (j=l; j<n; j++) + e(j) = u(i,j) / h; + + for (j=l; j<m; j++) + { + s = 0.0; + + for (k=l; k<n; k++) + s += (u(j,k) * u(i,k)); + + for (k=l; k<n; k++) + u(j,k) += (s * e(k)); + } /* end j */ + } /* end s */ + + y = abs(q(i)) + abs(e(i)); + if (y > x) + x = y; + } /* end i */ + + /* accumulation of right-hand transformations */ + if (withv) + { + for (i=n-1; i>=0; i--) + { + if (g != 0.0) + { + h = u(i,i+1) * g; + + for (j=l; j<n; j++) + v(j,i) = u(i,j)/h; + + for (j=l; j<n; j++) + { + s = 0.0; + + for (k=l; k<n; k++) + s += (u(i,k) * v(k,j)); + + for (k=l; k<n; k++) + v(k,j) += (s * v(k,i)); + } /* end j */ + } /* end g */ + + for (j=l; j<n; j++) + v(i,j) = v(j,i) = 0.0; + + v(i,i) = 1.0; + g = e(i); + l = i; + } /* end i */ + } /* end withv, parens added for clarity */ + + /* accumulation of left-hand transformations */ + if (u_mode != SVD_NO_U) + { + for (i=n; i<u.nr(); i++) + { + for (j=n;j<u.nc();j++) + u(i,j) = 0.0; + + if (i < u.nc()) + u(i,i) = 1.0; + } + } + + if (u_mode != SVD_NO_U) + { + for (i=n-1; i>=0; i--) + { + l = i + 1; + g = q(i); + + for (j=l; j<u.nc(); j++) + u(i,j) = 0.0; + + if (g != 0.0) + { + h = u(i,i) * g; + + for (j=l; j<u.nc(); j++) + { + s = 0.0; + + for (k=l; k<m; k++) + s += (u(k,i) * u(k,j)); + + f = s / h; + + for (k=i; k<m; k++) + u(k,j) += (f * u(k,i)); + } /* end j */ + + for (j=i; j<m; j++) + u(j,i) /= g; + } /* end g */ + else + { + for (j=i; j<m; j++) + u(j,i) = 0.0; + } + + u(i,i) += 1.0; + } /* end i*/ + } + + /* diagonalization of the bidiagonal form */ + eps *= x; + + for (k=n-1; k>=0; k--) + { + iter = 0; + +test_f_splitting: + + for (l=k; l>=0; l--) + { + if (abs(e(l)) <= eps) + goto test_f_convergence; + + if (abs(q(l-1)) <= eps) + goto cancellation; + } /* end l */ + + /* cancellation of e(l) if l > 0 */ + +cancellation: + + c = 0.0; + s = 1.0; + l1 = l - 1; + + for (i=l; i<=k; i++) + { + f = s * e(i); + e(i) *= c; + + if (abs(f) <= eps) + goto test_f_convergence; + + g = q(i); + h = q(i) = sqrt(f*f + g*g); + c = g / h; + s = -f / h; + + if (u_mode != SVD_NO_U) + { + for (j=0; j<m; j++) + { + y = u(j,l1); + z = u(j,i); + u(j,l1) = y * c + z * s; + u(j,i) = -y * s + z * c; + } /* end j */ + } + } /* end i */ + +test_f_convergence: + + z = q(k); + if (l == k) + goto convergence; + + /* shift from bottom 2x2 minor */ + iter++; + if (iter > 300) + { + retval = k; + break; + } + x = q(l); + y = q(k-1); + g = e(k-1); + h = e(k); + f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2 * h * y); + g = sqrt(f * f + 1.0); + f = ((x - z) * (x + z) + h * (y / ((f < 0)?(f - g) : (f + g)) - h)) / x; + + /* next QR transformation */ + c = s = 1.0; + + for (i=l+1; i<=k; i++) + { + g = e(i); + y = q(i); + h = s * g; + g *= c; + e(i-1) = z = sqrt(f * f + h * h); + c = f / z; + s = h / z; + f = x * c + g * s; + g = -x * s + g * c; + h = y * s; + y *= c; + + if (withv) + { + for (j=0;j<n;j++) + { + x = v(j,i-1); + z = v(j,i); + v(j,i-1) = x * c + z * s; + v(j,i) = -x * s + z * c; + } /* end j */ + } /* end withv, parens added for clarity */ + + q(i-1) = z = sqrt(f * f + h * h); + if (z != 0) + { + c = f / z; + s = h / z; + } + f = c * g + s * y; + x = -s * g + c * y; + if (u_mode != SVD_NO_U) + { + for (j=0; j<m; j++) + { + y = u(j,i-1); + z = u(j,i); + u(j,i-1) = y * c + z * s; + u(j,i) = -y * s + z * c; + } /* end j */ + } + } /* end i */ + + e(l) = 0.0; + e(k) = f; + q(k) = x; + + goto test_f_splitting; + +convergence: + + if (z < 0.0) + { + /* q(k) is made non-negative */ + q(k) = -z; + if (withv) + { + for (j=0; j<n; j++) + v(j,k) = -v(j,k); + } /* end withv, parens added for clarity */ + } /* end z */ + } /* end k */ + + return retval; +#endif + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + long qN, long qX, + long uM, + long vN, + typename MM1, + typename MM2, + typename MM3, + typename L1 + > + long svd2 ( + bool withu, + bool withv, + const matrix_exp<EXP>& a, + matrix<typename EXP::type,uM,uM,MM1,L1>& u, + matrix<typename EXP::type,qN,qX,MM2,L1>& q, + matrix<typename EXP::type,vN,vN,MM3,L1>& v + ) + { + const long NR = matrix_exp<EXP>::NR; + const long NC = matrix_exp<EXP>::NC; + + // make sure the output matrices have valid dimensions if they are statically dimensioned + COMPILE_TIME_ASSERT(qX == 0 || qX == 1); + COMPILE_TIME_ASSERT(NR == 0 || uM == 0 || NR == uM); + COMPILE_TIME_ASSERT(NC == 0 || vN == 0 || NC == vN); + + DLIB_ASSERT(a.nr() >= a.nc(), + "\tconst matrix_exp svd4()" + << "\n\tYou have given an invalidly sized matrix" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + ); + + if (withu) + return svd4(SVD_FULL_U, withv, a,u,q,v); + else + return svd4(SVD_NO_U, withv, a,u,q,v); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename MM + > + void orthogonalize ( + matrix<T,NR,NC,MM,row_major_layout>& m + ) + { + // We don't really need to use this temporary, but doing it this way runs a lot + // faster. + matrix<T,NR,NC,MM,column_major_layout> temp; + qr_decomposition<matrix<T,NR,NC,MM,row_major_layout>>(m).get_q(temp); + m = temp; + } + + template < + typename T, + long NR, + long NC, + typename MM + > + void orthogonalize ( + matrix<T,NR,NC,MM,column_major_layout>& m + ) + { + qr_decomposition<matrix<T,NR,NC,MM,column_major_layout>>(m).get_q(m); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long Anr, long Anc, + typename MM, + typename L + > + void find_matrix_range ( + const matrix<T,Anr,Anc,MM,L>& A, + unsigned long l, + matrix<T,Anr,0,MM,L>& Q, + unsigned long q + ) + /*! + requires + - A.nr() >= l + ensures + - #Q.nr() == A.nr() + - #Q.nc() == l + - #Q == an orthonormal matrix whose range approximates the range of the + matrix A. + - This function implements the randomized subspace iteration defined + in the algorithm 4.4 box of the paper: + Finding Structure with Randomness: Probabilistic Algorithms for + Constructing Approximate Matrix Decompositions by Halko et al. + - q defines the number of extra subspace iterations this algorithm will + perform. Often q == 0 is fine, but performing more iterations can lead to a + more accurate approximation of the range of A if A has slowly decaying + singular values. In these cases, using a q of 1 or 2 is good. + !*/ + { + DLIB_ASSERT(A.nr() >= (long)l, "Invalid inputs were given to this function."); + Q = A*matrix_cast<T>(gaussian_randm(A.nc(), l)); + + orthogonalize(Q); + + // Do some extra iterations of the power method to make sure we get Q into the + // span of the most important singular vectors of A. + if (q != 0) + { + for (unsigned long itr = 0; itr < q; ++itr) + { + Q = trans(A)*Q; + orthogonalize(Q); + + Q = A*Q; + orthogonalize(Q); + } + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long Anr, long Anc, + long Unr, long Unc, + long Wnr, long Wnc, + long Vnr, long Vnc, + typename MM, + typename L + > + void svd_fast ( + const matrix<T,Anr,Anc,MM,L>& A, + matrix<T,Unr,Unc,MM,L>& u, + matrix<T,Wnr,Wnc,MM,L>& w, + matrix<T,Vnr,Vnc,MM,L>& v, + unsigned long l, + unsigned long q = 1 + ) + { + const unsigned long k = std::min(l, std::min<unsigned long>(A.nr(),A.nc())); + + DLIB_ASSERT(l > 0 && A.size() > 0, + "\t void svd_fast()" + << "\n\t Invalid inputs were given to this function." + << "\n\t l: " << l + << "\n\t A.size(): " << A.size() + ); + + matrix<T,Anr,0,MM,L> Q; + find_matrix_range(A, k, Q, q); + + // Compute trans(B) = trans(Q)*A. The reason we store B transposed + // is so that when we take its SVD later using svd3() it doesn't consume + // a whole lot of RAM. That is, we make sure the square matrix coming out + // of svd3() has size lxl rather than the potentially much larger nxn. + matrix<T,0,0,MM,L> B = trans(A)*Q; + svd3(B, v,w,u); + u = Q*u; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename sparse_vector_type, + typename T, + typename MM, + typename L + > + void find_matrix_range ( + const std::vector<sparse_vector_type>& A, + unsigned long l, + matrix<T,0,0,MM,L>& Q, + unsigned long q + ) + /*! + requires + - A.size() >= l + ensures + - #Q.nr() == A.size() + - #Q.nc() == l + - #Q == an orthonormal matrix whose range approximates the range of the + matrix A. In this case, we interpret A as a matrix of A.size() rows, + where each row is defined by a sparse vector. + - This function implements the randomized subspace iteration defined + in the algorithm 4.4 box of the paper: + Finding Structure with Randomness: Probabilistic Algorithms for + Constructing Approximate Matrix Decompositions by Halko et al. + - q defines the number of extra subspace iterations this algorithm will + perform. Often q == 0 is fine, but performing more iterations can lead to a + more accurate approximation of the range of A if A has slowly decaying + singular values. In these cases, using a q of 1 or 2 is good. + !*/ + { + DLIB_ASSERT(A.size() >= l, "Invalid inputs were given to this function."); + Q.set_size(A.size(), l); + + // Compute Q = A*gaussian_randm() + parallel_for(0, Q.nr(), [&](long r) + { + for (long c = 0; c < Q.nc(); ++c) + { + Q(r,c) = dot(A[r], gaussian_randm(std::numeric_limits<long>::max(), 1, c)); + } + }); + + orthogonalize(Q); + + // Do some extra iterations of the power method to make sure we get Q into the + // span of the most important singular vectors of A. + if (q != 0) + { + dlib::mutex mut; + const unsigned long n = max_index_plus_one(A); + for (unsigned long itr = 0; itr < q; ++itr) + { + matrix<T,0,0,MM> Z; + // Compute Z = trans(A)*Q + parallel_for_blocked(0, A.size(), [&](long begin, long end) + { + matrix<T,0,0,MM> Zlocal(n,l); + Zlocal = 0; + for (long m = begin; m < end; ++m) + { + for (unsigned long r = 0; r < l; ++r) + { + for (auto& i : A[m]) + { + const auto c = i.first; + const auto val = i.second; + + Zlocal(c,r) += Q(m,r)*val; + } + } + } + auto_mutex lock(mut); + Z += Zlocal; + },1); + + Q.set_size(0,0); // free RAM + orthogonalize(Z); + + // Compute Q = A*Z + Q.set_size(A.size(), l); + parallel_for(0, Q.nr(), [&](long r) + { + for (long c = 0; c < Q.nc(); ++c) + { + Q(r,c) = dot(A[r], colm(Z,c)); + } + }); + + Z.set_size(0,0); // free RAM + orthogonalize(Q); + } + } + } + +// ---------------------------------------------------------------------------------------- + + namespace simpl + { + template < + typename sparse_vector_type, + typename T, + long Unr, long Unc, + long Wnr, long Wnc, + long Vnr, long Vnc, + typename MM, + typename L + > + void svd_fast ( + bool compute_u, + const std::vector<sparse_vector_type>& A, + matrix<T,Unr,Unc,MM,L>& u, + matrix<T,Wnr,Wnc,MM,L>& w, + matrix<T,Vnr,Vnc,MM,L>& v, + unsigned long l, + unsigned long q + ) + { + const long n = max_index_plus_one(A); + const unsigned long k = std::min(l, std::min<unsigned long>(A.size(),n)); + + DLIB_ASSERT(l > 0 && A.size() > 0 && n > 0, + "\t void svd_fast()" + << "\n\t Invalid inputs were given to this function." + << "\n\t l: " << l + << "\n\t n (i.e. max_index_plus_one(A)): " << n + << "\n\t A.size(): " << A.size() + ); + + matrix<T,0,0,MM,L> Q; + find_matrix_range(A, k, Q, q); + + // Compute trans(B) = trans(Q)*A. The reason we store B transposed + // is so that when we take its SVD later using svd3() it doesn't consume + // a whole lot of RAM. That is, we make sure the square matrix coming out + // of svd3() has size lxl rather than the potentially much larger nxn. + matrix<T,0,0,MM> B; + dlib::mutex mut; + parallel_for_blocked(0, A.size(), [&](long begin, long end) + { + matrix<T,0,0,MM> Blocal(n,k); + Blocal = 0; + for (long m = begin; m < end; ++m) + { + for (unsigned long r = 0; r < k; ++r) + { + for (auto& i : A[m]) + { + const auto c = i.first; + const auto val = i.second; + + Blocal(c,r) += Q(m,r)*val; + } + } + } + auto_mutex lock(mut); + B += Blocal; + },1); + + svd3(B, v,w,u); + if (compute_u) + u = Q*u; + } + } + + template < + typename sparse_vector_type, + typename T, + long Unr, long Unc, + long Wnr, long Wnc, + long Vnr, long Vnc, + typename MM, + typename L + > + void svd_fast ( + const std::vector<sparse_vector_type>& A, + matrix<T,Unr,Unc,MM,L>& u, + matrix<T,Wnr,Wnc,MM,L>& w, + matrix<T,Vnr,Vnc,MM,L>& v, + unsigned long l, + unsigned long q = 1 + ) + { + simpl::svd_fast(true, A,u,w,v,l,q); + } + + template < + typename sparse_vector_type, + typename T, + long Wnr, long Wnc, + long Vnr, long Vnc, + typename MM, + typename L + > + void svd_fast ( + const std::vector<sparse_vector_type>& A, + matrix<T,Wnr,Wnc,MM,L>& w, + matrix<T,Vnr,Vnc,MM,L>& v, + unsigned long l, + unsigned long q = 1 + ) + { + matrix<T,0,0,MM,L> u; + simpl::svd_fast(false, A,u,w,v,l,q); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + long N + > + struct inv_helper + { + static const typename matrix_exp<EXP>::matrix_type inv ( + const matrix_exp<EXP>& m + ) + { + // you can't invert a non-square matrix + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC || + matrix_exp<EXP>::NR == 0 || + matrix_exp<EXP>::NC == 0); + DLIB_ASSERT(m.nr() == m.nc(), + "\tconst matrix_exp::type inv(const matrix_exp& m)" + << "\n\tYou can only apply inv() to a square matrix" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + typedef typename matrix_exp<EXP>::type type; + + lu_decomposition<EXP> lu(m); + return lu.solve(identity_matrix<type>(m.nr())); + } + }; + + template < + typename EXP + > + struct inv_helper<EXP,1> + { + static const typename matrix_exp<EXP>::matrix_type inv ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC); + typedef typename matrix_exp<EXP>::type type; + + matrix<type, 1, 1, typename EXP::mem_manager_type> a; + // if m is invertible + if (m(0) != 0) + a(0) = 1/m(0); + else + a(0) = 1; + return a; + } + }; + + template < + typename EXP + > + struct inv_helper<EXP,2> + { + static const typename matrix_exp<EXP>::matrix_type inv ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC); + typedef typename matrix_exp<EXP>::type type; + + matrix<type, 2, 2, typename EXP::mem_manager_type> a; + type d = det(m); + if (d != 0) + { + d = static_cast<type>(1.0/d); + a(0,0) = m(1,1)*d; + a(0,1) = m(0,1)*-d; + a(1,0) = m(1,0)*-d; + a(1,1) = m(0,0)*d; + } + else + { + // Matrix isn't invertible so just return the identity matrix. + a = identity_matrix<type,2>(); + } + return a; + } + }; + + template < + typename EXP + > + struct inv_helper<EXP,3> + { + static const typename matrix_exp<EXP>::matrix_type inv ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC); + typedef typename matrix_exp<EXP>::type type; + + matrix<type, 3, 3, typename EXP::mem_manager_type> ret; + type de = det(m); + if (de != 0) + { + de = static_cast<type>(1.0/de); + const type a = m(0,0); + const type b = m(0,1); + const type c = m(0,2); + const type d = m(1,0); + const type e = m(1,1); + const type f = m(1,2); + const type g = m(2,0); + const type h = m(2,1); + const type i = m(2,2); + + ret(0,0) = (e*i - f*h)*de; + ret(1,0) = (f*g - d*i)*de; + ret(2,0) = (d*h - e*g)*de; + + ret(0,1) = (c*h - b*i)*de; + ret(1,1) = (a*i - c*g)*de; + ret(2,1) = (b*g - a*h)*de; + + ret(0,2) = (b*f - c*e)*de; + ret(1,2) = (c*d - a*f)*de; + ret(2,2) = (a*e - b*d)*de; + } + else + { + ret = identity_matrix<type,3>(); + } + + return ret; + } + }; + + template < + typename EXP + > + struct inv_helper<EXP,4> + { + static const typename matrix_exp<EXP>::matrix_type inv ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC); + typedef typename matrix_exp<EXP>::type type; + + matrix<type, 4, 4, typename EXP::mem_manager_type> ret; + type de = det(m); + if (de != 0) + { + de = static_cast<type>(1.0/de); + ret(0,0) = det(removerc<0,0>(m)); + ret(0,1) = -det(removerc<0,1>(m)); + ret(0,2) = det(removerc<0,2>(m)); + ret(0,3) = -det(removerc<0,3>(m)); + + ret(1,0) = -det(removerc<1,0>(m)); + ret(1,1) = det(removerc<1,1>(m)); + ret(1,2) = -det(removerc<1,2>(m)); + ret(1,3) = det(removerc<1,3>(m)); + + ret(2,0) = det(removerc<2,0>(m)); + ret(2,1) = -det(removerc<2,1>(m)); + ret(2,2) = det(removerc<2,2>(m)); + ret(2,3) = -det(removerc<2,3>(m)); + + ret(3,0) = -det(removerc<3,0>(m)); + ret(3,1) = det(removerc<3,1>(m)); + ret(3,2) = -det(removerc<3,2>(m)); + ret(3,3) = det(removerc<3,3>(m)); + + return trans(ret)*de; + } + else + { + return identity_matrix<type,4>(); + } + } + }; + + template < + typename EXP + > + inline const typename matrix_exp<EXP>::matrix_type inv ( + const matrix_exp<EXP>& m + ) { return inv_helper<EXP,matrix_exp<EXP>::NR>::inv(m); } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_diag_inv + { + template <typename EXP> + op_diag_inv( const matrix_exp<EXP>& m_) : m(m_){} + + + const static long cost = 1; + const static long NR = ((M::NC!=0)&&(M::NR!=0))? (tmax<M::NR,M::NC>::value) : (0); + const static long NC = NR; + typedef typename M::type type; + typedef const type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + + // hold the matrix by value + const matrix<type,NR,1,mem_manager_type,layout_type> m; + + const_ret_type apply ( long r, long c) const + { + if (r==c) + return m(r); + else + return 0; + } + + long nr () const { return m.size(); } + long nc () const { return m.size(); } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_diag_op<op_diag_inv<EXP> > inv ( + const matrix_diag_exp<EXP>& m + ) + { + typedef op_diag_inv<EXP> op; + return matrix_diag_op<op>(op(reciprocal(diag(m)))); + } + + template < + typename EXP + > + const matrix_diag_op<op_diag_inv<EXP> > pinv ( + const matrix_diag_exp<EXP>& m + ) + { + typedef op_diag_inv<EXP> op; + return matrix_diag_op<op>(op(reciprocal(diag(m)))); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix_diag_op<op_diag_inv<EXP> > pinv ( + const matrix_diag_exp<EXP>& m, + double tol + ) + { + DLIB_ASSERT(tol >= 0, + "\tconst matrix_exp::type pinv(const matrix_exp& m)" + << "\n\t tol can't be negative" + << "\n\t tol: "<<tol + ); + typedef op_diag_inv<EXP> op; + return matrix_diag_op<op>(op(reciprocal(round_zeros(diag(m),tol)))); + } + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + const typename matrix_exp<EXP>::matrix_type inv_lower_triangular ( + const matrix_exp<EXP>& A + ) + { + DLIB_ASSERT(A.nr() == A.nc(), + "\tconst matrix inv_lower_triangular(const matrix_exp& A)" + << "\n\tA must be a square matrix" + << "\n\tA.nr(): " << A.nr() + << "\n\tA.nc(): " << A.nc() + ); + + typedef typename matrix_exp<EXP>::matrix_type matrix_type; + + matrix_type m(A); + + for(long c = 0; c < m.nc(); ++c) + { + if( m(c,c) == 0 ) + { + // there isn't an inverse so just give up + return m; + } + + // compute m(c,c) + m(c,c) = 1/m(c,c); + + // compute the values in column c that are below m(c,c). + // We do this by just doing the same thing we do for upper triangular + // matrices because we take the transpose of m which turns m into an + // upper triangular matrix. + for(long r = 0; r < c; ++r) + { + const long n = c-r; + m(c,r) = -m(c,c)*subm(trans(m),r,r,1,n)*subm(trans(m),r,c,n,1); + } + } + + return m; + + } + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + const typename matrix_exp<EXP>::matrix_type inv_upper_triangular ( + const matrix_exp<EXP>& A + ) + { + DLIB_ASSERT(A.nr() == A.nc(), + "\tconst matrix inv_upper_triangular(const matrix_exp& A)" + << "\n\tA must be a square matrix" + << "\n\tA.nr(): " << A.nr() + << "\n\tA.nc(): " << A.nc() + ); + + typedef typename matrix_exp<EXP>::matrix_type matrix_type; + + matrix_type m(A); + + for(long c = 0; c < m.nc(); ++c) + { + if( m(c,c) == 0 ) + { + // there isn't an inverse so just give up + return m; + } + + // compute m(c,c) + m(c,c) = 1/m(c,c); + + // compute the values in column c that are above m(c,c) + for(long r = 0; r < c; ++r) + { + const long n = c-r; + m(r,c) = -m(c,c)*subm(m,r,r,1,n)*subm(m,r,c,n,1); + } + } + + return m; + + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + inline const typename matrix_exp<EXP>::matrix_type chol ( + const matrix_exp<EXP>& A + ) + { + DLIB_ASSERT(A.nr() == A.nc(), + "\tconst matrix chol(const matrix_exp& A)" + << "\n\tYou can only apply the chol to a square matrix" + << "\n\tA.nr(): " << A.nr() + << "\n\tA.nc(): " << A.nc() + ); + typename matrix_exp<EXP>::matrix_type L(A.nr(),A.nc()); + + typedef typename EXP::type T; + + bool banded = false; + long bandwidth = 0; + + if (A.nr() > 4) // Only test for banded matrix if matrix is big enough + { + // Detect if matrix is banded and, if so, matrix bandwidth + banded = true; + for (long r = 0; r < A.nr(); ++r) + for (long c = (r + bandwidth + 1); c < A.nc(); ++c) + if (A(r, c) != 0) + { + bandwidth = c - r; + if (bandwidth > A.nr() / 2) + { + banded = false; + goto escape_banded_detection; + } + } + } +escape_banded_detection: + + if (banded) + { + // Store in compact form - use column major for LAPACK + matrix<T,0,0,default_memory_manager,column_major_layout> B(bandwidth + 1, A.nc()); + set_all_elements(B, 0); + + for (long r = 0; r < A.nr(); ++r) + for (long c = r; c < std::min(r + bandwidth + 1, A.nc()); ++c) + B(c - r, r) = A(r, c); + +#ifdef DLIB_USE_LAPACK + + lapack::pbtrf('L', B); + +#else + + // Peform compact Cholesky + for (long k = 0; k < A.nr(); ++k) + { + long last = std::min(k + bandwidth, A.nr() - 1) - k; + for (long j = 1; j <= last; ++j) + { + long i = k + j; + for (long c = 0; c <= (last - j); ++c) + B(c, i) -= B(j, k) / B(0, k) * B(c + j, k); + } + T norm = std::sqrt(B(0, k)); + for (long i = 0; i <= bandwidth; ++i) + B(i, k) /= norm; + } + for (long c = A.nc() - bandwidth + 1; c < A.nc(); ++c) + B(bandwidth, c) = 0; + +#endif + + // Unpack lower triangular area + set_all_elements(L, 0); + for (long c = 0; c < A.nc(); ++c) + for (long i = 0; i <= bandwidth; ++i) + { + long ind = c + i; + if (ind < A.nc()) + L(ind, c) = B(i, c); + } + + return L; + } + +#ifdef DLIB_USE_LAPACK + // Only call LAPACK if the matrix is big enough. Otherwise, + // our own code is faster, especially for statically dimensioned + // matrices. + if (A.nr() > 4) + { + L = A; + lapack::potrf('L', L); + // mask out upper triangular area + return lowerm(L); + } +#endif + set_all_elements(L,0); + + // do nothing if the matrix is empty + if (A.size() == 0) + return L; + + const T eps = std::numeric_limits<T>::epsilon(); + + // compute the upper left corner + if (A(0,0) > 0) + L(0,0) = std::sqrt(A(0,0)); + + // compute the first column + for (long r = 1; r < A.nr(); ++r) + { + // if (L(0,0) > 0) + if (L(0,0) > eps*std::abs(A(r,0))) + L(r,0) = A(r,0)/L(0,0); + else + return L; + } + + // now compute all the other columns + for (long c = 1; c < A.nc(); ++c) + { + // compute the diagonal element + T temp = A(c,c); + for (long i = 0; i < c; ++i) + { + temp -= L(c,i)*L(c,i); + } + if (temp > 0) + L(c,c) = std::sqrt(temp); + + // compute the non diagonal elements + for (long r = c+1; r < A.nr(); ++r) + { + temp = A(r,c); + for (long i = 0; i < c; ++i) + { + temp -= L(r,i)*L(c,i); + } + + // if (L(c,c) > 0) + if (L(c,c) > eps*std::abs(temp)) + L(r,c) = temp/L(c,c); + else + return L; + } + } + + return L; + + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + long uNR, + long uNC, + long wN, + long vN, + long wX, + typename MM1, + typename MM2, + typename MM3, + typename L1 + > + inline void svd3 ( + const matrix_exp<EXP>& m, + matrix<typename matrix_exp<EXP>::type, uNR, uNC,MM1,L1>& u, + matrix<typename matrix_exp<EXP>::type, wN, wX,MM2,L1>& w, + matrix<typename matrix_exp<EXP>::type, vN, vN,MM3,L1>& v + ) + { + typedef typename matrix_exp<EXP>::type T; + const long NR = matrix_exp<EXP>::NR; + const long NC = matrix_exp<EXP>::NC; + + // make sure the output matrices have valid dimensions if they are statically dimensioned + COMPILE_TIME_ASSERT(NR == 0 || uNR == 0 || NR == uNR); + COMPILE_TIME_ASSERT(NC == 0 || uNC == 0 || NC == uNC); + COMPILE_TIME_ASSERT(NC == 0 || wN == 0 || NC == wN); + COMPILE_TIME_ASSERT(NC == 0 || vN == 0 || NC == vN); + COMPILE_TIME_ASSERT(wX == 0 || wX == 1); + +#ifdef DLIB_USE_LAPACK + // use LAPACK but only if it isn't a really small matrix we are taking the SVD of. + if (NR*NC == 0 || NR*NC > 3*3) + { + matrix<typename matrix_exp<EXP>::type, uNR, uNC,MM1,L1> temp(m); + lapack::gesvd('S','A', temp, w, u, v); + v = trans(v); + // if u isn't the size we want then pad it (and v) with zeros + if (u.nc() < m.nc()) + { + w = join_cols(w, zeros_matrix<T>(m.nc()-u.nc(),1)); + u = join_rows(u, zeros_matrix<T>(u.nr(), m.nc()-u.nc())); + } + return; + } +#endif + if (m.nr() >= m.nc()) + { + svd4(SVD_SKINNY_U,true, m, u,w,v); + } + else + { + svd4(SVD_FULL_U,true, trans(m), v,w,u); + + // if u isn't the size we want then pad it (and v) with zeros + if (u.nc() < m.nc()) + { + w = join_cols(w, zeros_matrix<T>(m.nc()-u.nc(),1)); + u = join_rows(u, zeros_matrix<T>(u.nr(), m.nc()-u.nc())); + } + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix<typename EXP::type,EXP::NC,EXP::NR,typename EXP::mem_manager_type> pinv_helper ( + const matrix_exp<EXP>& m, + double tol + ) + /*! + ensures + - computes the results of pinv(m) but does so using a method that is fastest + when m.nc() <= m.nr(). So if m.nc() > m.nr() then it is best to use + trans(pinv_helper(trans(m))) to compute pinv(m). + !*/ + { + typename matrix_exp<EXP>::matrix_type u; + typedef typename EXP::mem_manager_type MM1; + typedef typename EXP::layout_type layout_type; + matrix<typename EXP::type, EXP::NC, EXP::NC,MM1, layout_type > v; + + typedef typename matrix_exp<EXP>::type T; + + matrix<T,matrix_exp<EXP>::NC,1,MM1, layout_type> w; + + svd3(m, u,w,v); + + const double machine_eps = std::numeric_limits<typename EXP::type>::epsilon(); + // compute a reasonable epsilon below which we round to zero before doing the + // reciprocal. Unless a non-zero tol is given then we just use tol*max(w). + const double eps = (tol!=0) ? tol*max(w) : machine_eps*std::max(m.nr(),m.nc())*max(w); + + // now compute the pseudoinverse + return tmp(scale_columns(v,reciprocal(round_zeros(w,eps))))*trans(u); + } + + template < + typename EXP + > + const matrix<typename EXP::type,EXP::NC,EXP::NR,typename EXP::mem_manager_type> pinv ( + const matrix_exp<EXP>& m, + double tol = 0 + ) + { + DLIB_ASSERT(tol >= 0, + "\tconst matrix_exp::type pinv(const matrix_exp& m)" + << "\n\t tol can't be negative" + << "\n\t tol: "<<tol + ); + // if m has more columns then rows then it is more efficient to + // compute the pseudo-inverse of its transpose (given the way I'm doing it below). + if (m.nc() > m.nr()) + return trans(pinv_helper(trans(m),tol)); + else + return pinv_helper(m,tol); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + long uNR, + long uNC, + long wN, + long vN, + typename MM1, + typename MM2, + typename MM3, + typename L1 + > + inline void svd ( + const matrix_exp<EXP>& m, + matrix<typename matrix_exp<EXP>::type, uNR, uNC,MM1,L1>& u, + matrix<typename matrix_exp<EXP>::type, wN, wN,MM2,L1>& w, + matrix<typename matrix_exp<EXP>::type, vN, vN,MM3,L1>& v + ) + { + typedef typename matrix_exp<EXP>::type T; + const long NR = matrix_exp<EXP>::NR; + const long NC = matrix_exp<EXP>::NC; + + // make sure the output matrices have valid dimensions if they are statically dimensioned + COMPILE_TIME_ASSERT(NR == 0 || uNR == 0 || NR == uNR); + COMPILE_TIME_ASSERT(NC == 0 || uNC == 0 || NC == uNC); + COMPILE_TIME_ASSERT(NC == 0 || wN == 0 || NC == wN); + COMPILE_TIME_ASSERT(NC == 0 || vN == 0 || NC == vN); + + matrix<T,matrix_exp<EXP>::NC,1,MM1, L1> W; + svd3(m,u,W,v); + w = diagm(W); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const typename matrix_exp<EXP>::type trace ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC || + matrix_exp<EXP>::NR == 0 || + matrix_exp<EXP>::NC == 0 + ); + DLIB_ASSERT(m.nr() == m.nc(), + "\tconst matrix_exp::type trace(const matrix_exp& m)" + << "\n\tYou can only apply trace() to a square matrix" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + return sum(diag(m)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + long N = EXP::NR + > + struct det_helper + { + static const typename matrix_exp<EXP>::type det ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC || + matrix_exp<EXP>::NR == 0 || + matrix_exp<EXP>::NC == 0 + ); + DLIB_ASSERT(m.nr() == m.nc(), + "\tconst matrix_exp::type det(const matrix_exp& m)" + << "\n\tYou can only apply det() to a square matrix" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + + return lu_decomposition<EXP>(m).det(); + } + }; + + template < + typename EXP + > + struct det_helper<EXP,1> + { + static const typename matrix_exp<EXP>::type det ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC); + + return m(0); + } + }; + + template < + typename EXP + > + struct det_helper<EXP,2> + { + static const typename matrix_exp<EXP>::type det ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC); + + return m(0,0)*m(1,1) - m(0,1)*m(1,0); + } + }; + + template < + typename EXP + > + struct det_helper<EXP,3> + { + static const typename matrix_exp<EXP>::type det ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC); + typedef typename matrix_exp<EXP>::type type; + + type temp = m(0,0)*(m(1,1)*m(2,2) - m(1,2)*m(2,1)) - + m(0,1)*(m(1,0)*m(2,2) - m(1,2)*m(2,0)) + + m(0,2)*(m(1,0)*m(2,1) - m(1,1)*m(2,0)); + return temp; + } + }; + + + template < + typename EXP + > + inline const typename matrix_exp<EXP>::type det ( + const matrix_exp<EXP>& m + ) { return det_helper<EXP>::det(m); } + + + template < + typename EXP + > + struct det_helper<EXP,4> + { + static const typename matrix_exp<EXP>::type det ( + const matrix_exp<EXP>& m + ) + { + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NR == matrix_exp<EXP>::NC); + typedef typename matrix_exp<EXP>::type type; + + type temp = m(0,0)*(dlib::det(removerc<0,0>(m))) - + m(0,1)*(dlib::det(removerc<0,1>(m))) + + m(0,2)*(dlib::det(removerc<0,2>(m))) - + m(0,3)*(dlib::det(removerc<0,3>(m))); + return temp; + } + }; + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + const matrix<typename EXP::type, EXP::NR, 1, typename EXP::mem_manager_type, typename EXP::layout_type> real_eigenvalues ( + const matrix_exp<EXP>& m + ) + { + // You can only use this function with matrices that contain float or double values + COMPILE_TIME_ASSERT((is_same_type<typename EXP::type, float>::value || + is_same_type<typename EXP::type, double>::value)); + + DLIB_ASSERT(m.nr() == m.nc(), + "\tconst matrix real_eigenvalues()" + << "\n\tYou have given an invalidly sized matrix" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + + if (m.nr() == 2) + { + typedef typename EXP::type T; + const T m00 = m(0,0); + const T m01 = m(0,1); + const T m10 = m(1,0); + const T m11 = m(1,1); + + const T b = -(m00 + m11); + const T c = m00*m11 - m01*m10; + matrix<T,EXP::NR,1, typename EXP::mem_manager_type, typename EXP::layout_type> v(2); + + + T disc = b*b - 4*c; + if (disc >= 0) + disc = std::sqrt(disc); + else + disc = 0; + + v(0) = (-b + disc)/2; + v(1) = (-b - disc)/2; + return v; + } + else + { + // Call .ref() so that the symmetric matrix overload can take effect if m + // has the appropriate type. + return eigenvalue_decomposition<EXP>(m.ref()).get_real_eigenvalues(); + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + dlib::vector<double,2> max_point_interpolated ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0, + "\tdlib::vector<double,2> point max_point_interpolated(const matrix_exp& m)" + << "\n\tm can't be empty" + << "\n\tm.size(): " << m.size() + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + const point p = max_point(m); + + // If this is a column vector then just do interpolation along a line. + if (m.nc()==1) + { + const long pos = p.y(); + if (0 < pos && pos+1 < m.nr()) + { + double v1 = dlib::impl::magnitude(m(pos-1)); + double v2 = dlib::impl::magnitude(m(pos)); + double v3 = dlib::impl::magnitude(m(pos+1)); + double y = lagrange_poly_min_extrap(pos-1,pos,pos+1, -v1, -v2, -v3); + return vector<double,2>(0,y); + } + } + // If this is a row vector then just do interpolation along a line. + if (m.nr()==1) + { + const long pos = p.x(); + if (0 < pos && pos+1 < m.nc()) + { + double v1 = dlib::impl::magnitude(m(pos-1)); + double v2 = dlib::impl::magnitude(m(pos)); + double v3 = dlib::impl::magnitude(m(pos+1)); + double x = lagrange_poly_min_extrap(pos-1,pos,pos+1, -v1, -v2, -v3); + return vector<double,2>(x,0); + } + } + + + // If it's on the border then just return the regular max point. + if (shrink_rect(get_rect(m),1).contains(p) == false) + return p; + + //matrix<double> A(9,6); + //matrix<double,0,1> G(9); + + matrix<double,9,1> pix; + long i = 0; + for (long r = -1; r <= +1; ++r) + { + for (long c = -1; c <= +1; ++c) + { + pix(i) = dlib::impl::magnitude(m(p.y()+r,p.y()+c)); + /* + A(i,0) = c*c; + A(i,1) = c*r; + A(i,2) = r*r; + A(i,3) = c; + A(i,4) = r; + A(i,5) = 1; + G(i) = std::exp(-1*(r*r+c*c)/2.0); // Use a gaussian windowing function around p. + */ + ++i; + } + } + + // This bit of code is how we generated the derivative_filters matrix below. + //A = diagm(G)*A; + //std::cout << std::setprecision(20) << inv(trans(A)*A)*trans(A)*diagm(G) << std::endl; exit(1); + + const double m10 = 0.10597077880854270659; + const double m21 = 0.21194155761708535768; + const double m28 = 0.28805844238291455905; + const double m57 = 0.57611688476582878504; + // So this derivative_filters finds the parameters of the quadratic surface that best fits + // the 3x3 region around p. Then we find the maximizer of that surface within that + // small region and return that as the maximum location. + const double derivative_filters[] = { + // xx + m10,-m21,m10, + m28,-m57,m28, + m10,-m21,m10, + + // xy + 0.25 ,0,-0.25, + 0 ,0, 0, + -0.25,0,0.25, + + // yy + m10, m28, m10, + -m21,-m57,-m21, + m10, m28, m10, + + // x + -m10,0,m10, + -m28,0,m28, + -m10,0,m10, + + // y + -m10,-m28,-m10, + 0, 0, 0, + m10, m28, m10 + }; + const matrix<double,5,9> filt(derivative_filters); + // Now w contains the parameters of the quadratic surface + const matrix<double,5,1> w = filt*pix; + + + // Now newton step to the max point on the surface + matrix<double,2,2> H; + matrix<double,2,1> g; + H = 2*w(0), w(1), + w(1), 2*w(2); + g = w(3), + w(4); + const dlib::vector<double,2> delta = -inv(H)*g; + + // if delta isn't in an ascent direction then just use the normal max point. + if (dot(delta, g) < 0) + return p; + else + return vector<double,2>(p)+dlib::clamp(delta, -1, 1); + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_LA_FUNCTS_ + + diff --git a/ml/dlib/dlib/matrix/matrix_la_abstract.h b/ml/dlib/dlib/matrix/matrix_la_abstract.h new file mode 100644 index 000000000..df6a5fd33 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_la_abstract.h @@ -0,0 +1,1005 @@ +// Copyright (C) 2009 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_LA_FUNCTS_ABSTRACT_ +#ifdef DLIB_MATRIx_LA_FUNCTS_ABSTRACT_ + +#include "matrix_abstract.h" +#include <complex> + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Global linear algebra functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + const matrix_exp::matrix_type inv ( + const matrix_exp& m + ); + /*! + requires + - m is a square matrix + ensures + - returns the inverse of m + (Note that if m is singular or so close to being singular that there + is a lot of numerical error then the returned matrix will be bogus. + You can check by seeing if m*inv(m) is an identity matrix) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix pinv ( + const matrix_exp& m, + double tol = 0 + ); + /*! + requires + - tol >= 0 + ensures + - returns the Moore-Penrose pseudoinverse of m. + - The returned matrix has m.nc() rows and m.nr() columns. + - if (tol == 0) then + - singular values less than max(m.nr(),m.nc()) times the machine epsilon + times the largest singular value are ignored. + - else + - singular values less than tol*max(singular value in m) are ignored. + !*/ + +// ---------------------------------------------------------------------------------------- + + void svd ( + const matrix_exp& m, + matrix<matrix_exp::type>& u, + matrix<matrix_exp::type>& w, + matrix<matrix_exp::type>& v + ); + /*! + ensures + - computes the singular value decomposition of m + - m == #u*#w*trans(#v) + - trans(#u)*#u == identity matrix + - trans(#v)*#v == identity matrix + - diag(#w) == the singular values of the matrix m in no + particular order. All non-diagonal elements of #w are + set to 0. + - #u.nr() == m.nr() + - #u.nc() == m.nc() + - #w.nr() == m.nc() + - #w.nc() == m.nc() + - #v.nr() == m.nc() + - #v.nc() == m.nc() + - if DLIB_USE_LAPACK is #defined then the xGESVD routine + from LAPACK is used to compute the SVD. + !*/ + +// ---------------------------------------------------------------------------------------- + + long svd2 ( + bool withu, + bool withv, + const matrix_exp& m, + matrix<matrix_exp::type>& u, + matrix<matrix_exp::type>& w, + matrix<matrix_exp::type>& v + ); + /*! + requires + - m.nr() >= m.nc() + ensures + - computes the singular value decomposition of matrix m + - m == subm(#u,get_rect(m))*diagm(#w)*trans(#v) + - trans(#u)*#u == identity matrix + - trans(#v)*#v == identity matrix + - #w == the singular values of the matrix m in no + particular order. + - #u.nr() == m.nr() + - #u.nc() == m.nr() + - #w.nr() == m.nc() + - #w.nc() == 1 + - #v.nr() == m.nc() + - #v.nc() == m.nc() + - if (widthu == false) then + - ignore the above regarding #u, it isn't computed and its + output state is undefined. + - if (widthv == false) then + - ignore the above regarding #v, it isn't computed and its + output state is undefined. + - returns an error code of 0, if no errors and 'k' if we fail to + converge at the 'kth' singular value. + - if (DLIB_USE_LAPACK is #defined) then + - if (withu == withv) then + - the xGESDD routine from LAPACK is used to compute the SVD. + - else + - the xGESVD routine from LAPACK is used to compute the SVD. + !*/ + +// ---------------------------------------------------------------------------------------- + + void svd3 ( + const matrix_exp& m, + matrix<matrix_exp::type>& u, + matrix<matrix_exp::type>& w, + matrix<matrix_exp::type>& v + ); + /*! + ensures + - computes the singular value decomposition of m + - m == #u*diagm(#w)*trans(#v) + - trans(#u)*#u == identity matrix + - trans(#v)*#v == identity matrix + - #w == the singular values of the matrix m in no + particular order. + - #u.nr() == m.nr() + - #u.nc() == m.nc() + - #w.nr() == m.nc() + - #w.nc() == 1 + - #v.nr() == m.nc() + - #v.nc() == m.nc() + - if DLIB_USE_LAPACK is #defined then the xGESVD routine + from LAPACK is used to compute the SVD. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + void svd_fast ( + const matrix<T>& A, + matrix<T>& u, + matrix<T>& w, + matrix<T>& v, + unsigned long l, + unsigned long q = 1 + ); + /*! + requires + - l > 0 + - A.size() > 0 + (i.e. A can't be an empty matrix) + ensures + - computes the singular value decomposition of A. + - Lets define some constants we use to document the behavior of svd_fast(): + - Let m = A.nr() + - Let n = A.nc() + - Let k = min(l, min(m,n)) + - Therefore, A represents an m by n matrix and svd_fast() is designed + to find a rank-k representation of it. + - if (the rank of A is <= k) then + - A == #u*diagm(#w)*trans(#v) + - else + - A is approximated by #u*diagm(#w)*trans(#v) + (i.e. In this case A can't be represented with a rank-k matrix, so the + matrix you get by trying to reconstruct A from the output of the SVD is + not exactly the same.) + - trans(#u)*#u == identity matrix + - trans(#v)*#v == identity matrix + - #w == the top k singular values of the matrix A (in no particular order). + - #u.nr() == m + - #u.nc() == k + - #w.nr() == k + - #w.nc() == 1 + - #v.nr() == n + - #v.nc() == k + - This function implements the randomized subspace iteration defined in the + algorithm 4.4 and 5.1 boxes of the paper: + Finding Structure with Randomness: Probabilistic Algorithms for + Constructing Approximate Matrix Decompositions by Halko et al. + Therefore, it is very fast and suitable for use with very large matrices. + Moreover, q is the number of subspace iterations performed. Larger + values of q might increase the accuracy of the solution but the default + value should be good for many problems. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename sparse_vector_type, + typename T + > + void svd_fast ( + const std::vector<sparse_vector_type>& A, + matrix<T>& u, + matrix<T>& w, + matrix<T>& v, + unsigned long l, + unsigned long q = 1 + ); + /*! + requires + - A contains a set of sparse vectors. See dlib/svm/sparse_vector_abstract.h + for a definition of what constitutes a sparse vector. + - l > 0 + - max_index_plus_one(A) > 0 + (i.e. A can't be an empty matrix) + ensures + - computes the singular value decomposition of A. In this case, we interpret A + as a matrix of A.size() rows, where each row is defined by a sparse vector. + - Lets define some constants we use to document the behavior of svd_fast(): + - Let m = A.size() + - Let n = max_index_plus_one(A) + - Let k = min(l, min(m,n)) + - Therefore, A represents an m by n matrix and svd_fast() is designed + to find a rank-k representation of it. + - if (the rank of A is <= k) then + - A == #u*diagm(#w)*trans(#v) + - else + - A is approximated by #u*diagm(#w)*trans(#v) + (i.e. In this case A can't be represented with a rank-k matrix, so the + matrix you get by trying to reconstruct A from the output of the SVD is + not exactly the same.) + - trans(#u)*#u == identity matrix + - trans(#v)*#v == identity matrix + - #w == the top k singular values of the matrix A (in no particular order). + - #u.nr() == m + - #u.nc() == k + - #w.nr() == k + - #w.nc() == 1 + - #v.nr() == n + - #v.nc() == k + - This function implements the randomized subspace iteration defined in the + algorithm 4.4 and 5.1 boxes of the paper: + Finding Structure with Randomness: Probabilistic Algorithms for + Constructing Approximate Matrix Decompositions by Halko et al. + Therefore, it is very fast and suitable for use with very large matrices. + Moreover, q is the number of subspace iterations performed. Larger + values of q might increase the accuracy of the solution but the default + value should be good for many problems. + !*/ + + template < + typename sparse_vector_type, + typename T + > + void svd_fast ( + const std::vector<sparse_vector_type>& A, + matrix<T>& w, + matrix<T>& v, + unsigned long l, + unsigned long q = 1 + ); + /*! + This function is identical to the above svd_fast() except it doesn't compute u. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename MM, + typename L + > + void orthogonalize ( + matrix<T,NR,NC,MM,L>& m + ); + /*! + requires + - m.nr() >= m.nc() + - m.size() > 0 + ensures + - #m == an orthogonal matrix with the same dimensions as m. In particular, + the columns of #m have the same span as the columns of m. + - trans(#m)*#m == identity matrix + - This function is just shorthand for computing the QR decomposition of m + and then storing the Q factor into #m. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix real_eigenvalues ( + const matrix_exp& m + ); + /*! + requires + - m.nr() == m.nc() + - matrix_exp::type == float or double + ensures + - returns a matrix E such that: + - E.nr() == m.nr() + - E.nc() == 1 + - E contains the real part of all eigenvalues of the matrix m. + (note that the eigenvalues are not sorted) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type det ( + const matrix_exp& m + ); + /*! + requires + - m is a square matrix + ensures + - returns the determinant of m + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type trace ( + const matrix_exp& m + ); + /*! + requires + - m is a square matrix + ensures + - returns the trace of m + (i.e. returns sum(diag(m))) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::matrix_type chol ( + const matrix_exp& A + ); + /*! + requires + - A is a square matrix + ensures + - if (A has a Cholesky Decomposition) then + - returns the decomposition of A. That is, returns a matrix L + such that L*trans(L) == A. L will also be lower triangular. + - else + - returns a matrix with the same dimensions as A but it + will have a bogus value. I.e. it won't be a decomposition. + In this case the algorithm returns a partial decomposition. + - You can tell when chol fails by looking at the lower right + element of the returned matrix. If it is 0 then it means + A does not have a cholesky decomposition. + + - If DLIB_USE_LAPACK is defined then the LAPACK routine xPOTRF + is used to compute the cholesky decomposition. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::matrix_type inv_lower_triangular ( + const matrix_exp& A + ); + /*! + requires + - A is a square matrix + ensures + - if (A is lower triangular) then + - returns the inverse of A. + - else + - returns a matrix with the same dimensions as A but it + will have a bogus value. I.e. it won't be an inverse. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::matrix_type inv_upper_triangular ( + const matrix_exp& A + ); + /*! + requires + - A is a square matrix + ensures + - if (A is upper triangular) then + - returns the inverse of A. + - else + - returns a matrix with the same dimensions as A but it + will have a bogus value. I.e. it won't be an inverse. + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Matrix decomposition classes +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template < + typename matrix_exp_type + > + class lu_decomposition + { + /*! + REQUIREMENTS ON matrix_exp_type + must be some kind of matrix expression as defined in the + dlib/matrix/matrix_abstract.h file. (e.g. a dlib::matrix object) + The matrix type must also contain float or double values. + + WHAT THIS OBJECT REPRESENTS + This object represents something that can compute an LU + decomposition of a real valued matrix. That is, for any + matrix A it computes matrices L, U, and a pivot vector P such + that rowm(A,P) == L*U. + + The LU decomposition with pivoting always exists, even if the matrix is + singular, so the constructor will never fail. The primary use of the + LU decomposition is in the solution of square systems of simultaneous + linear equations. This will fail if is_singular() returns true (or + if A is very nearly singular). + + If DLIB_USE_LAPACK is defined then the LAPACK routine xGETRF + is used to compute the LU decomposition. + !*/ + + public: + + const static long NR = matrix_exp_type::NR; + const static long NC = matrix_exp_type::NC; + typedef typename matrix_exp_type::type type; + typedef typename matrix_exp_type::mem_manager_type mem_manager_type; + typedef typename matrix_exp_type::layout_type layout_type; + + typedef matrix<type,0,0,mem_manager_type,layout_type> matrix_type; + typedef matrix<type,NR,1,mem_manager_type,layout_type> column_vector_type; + typedef matrix<long,NR,1,mem_manager_type,layout_type> pivot_column_vector_type; + + template <typename EXP> + lu_decomposition ( + const matrix_exp<EXP> &A + ); + /*! + requires + - EXP::type == lu_decomposition::type + - A.size() > 0 + ensures + - #nr() == A.nr() + - #nc() == A.nc() + - #is_square() == (A.nr() == A.nc()) + - computes the LU factorization of the given A matrix. + !*/ + + bool is_square ( + ) const; + /*! + ensures + - if (the input A matrix was a square matrix) then + - returns true + - else + - returns false + !*/ + + bool is_singular ( + ) const; + /*! + requires + - is_square() == true + ensures + - if (the input A matrix is singular) then + - returns true + - else + - returns false + !*/ + + long nr( + ) const; + /*! + ensures + - returns the number of rows in the input matrix + !*/ + + long nc( + ) const; + /*! + ensures + - returns the number of columns in the input matrix + !*/ + + const matrix_type get_l ( + ) const; + /*! + ensures + - returns the lower triangular L factor of the LU factorization. + - L.nr() == nr() + - L.nc() == min(nr(),nc()) + !*/ + + const matrix_type get_u ( + ) const; + /*! + ensures + - returns the upper triangular U factor of the LU factorization. + - U.nr() == min(nr(),nc()) + - U.nc() == nc() + !*/ + + const pivot_column_vector_type& get_pivot ( + ) const; + /*! + ensures + - returns the pivot permutation vector. That is, + if A is the input matrix then this function + returns a vector P such that: + - rowm(A,P) == get_l()*get_u() + - P.nr() == A.nr() + !*/ + + type det ( + ) const; + /*! + requires + - is_square() == true + ensures + - computes and returns the determinant of the input + matrix using LU factors. + !*/ + + template <typename EXP> + const matrix_type solve ( + const matrix_exp<EXP> &B + ) const; + /*! + requires + - EXP::type == lu_decomposition::type + - is_square() == true + - B.nr() == nr() + ensures + - Let A denote the input matrix to this class's constructor. + Then this function solves A*X == B for X and returns X. + - Note that if A is singular (or very close to singular) then + the X returned by this function won't fit A*X == B very well (if at all). + !*/ + + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename matrix_exp_type + > + class cholesky_decomposition + { + /*! + REQUIREMENTS ON matrix_exp_type + must be some kind of matrix expression as defined in the + dlib/matrix/matrix_abstract.h file. (e.g. a dlib::matrix object) + The matrix type must also contain float or double values. + + WHAT THIS OBJECT REPRESENTS + This object represents something that can compute a cholesky + decomposition of a real valued matrix. That is, for any + symmetric, positive definite matrix A, it computes a lower + triangular matrix L such that A == L*trans(L). + + If the matrix is not symmetric or positive definite, the function + computes only a partial decomposition. This can be tested with + the is_spd() flag. + + If DLIB_USE_LAPACK is defined then the LAPACK routine xPOTRF + is used to compute the cholesky decomposition. + !*/ + + public: + + const static long NR = matrix_exp_type::NR; + const static long NC = matrix_exp_type::NC; + typedef typename matrix_exp_type::type type; + typedef typename matrix_exp_type::mem_manager_type mem_manager_type; + typedef typename matrix_exp_type::layout_type layout_type; + + typedef typename matrix_exp_type::matrix_type matrix_type; + typedef matrix<type,NR,1,mem_manager_type,layout_type> column_vector_type; + + template <typename EXP> + cholesky_decomposition( + const matrix_exp<EXP>& A + ); + /*! + requires + - EXP::type == cholesky_decomposition::type + - A.size() > 0 + - A.nr() == A.nc() + (i.e. A must be a square matrix) + ensures + - if (A is symmetric positive-definite) then + - #is_spd() == true + - Constructs a lower triangular matrix L, such that L*trans(L) == A. + and #get_l() == L + - else + - #is_spd() == false + !*/ + + bool is_spd( + ) const; + /*! + ensures + - if (the input matrix was symmetric positive-definite) then + - returns true + - else + - returns false + !*/ + + const matrix_type& get_l( + ) const; + /*! + ensures + - returns the lower triangular factor, L, such that L*trans(L) == A + (where A is the input matrix to this class's constructor) + - Note that if A is not symmetric positive definite or positive semi-definite + then the equation L*trans(L) == A won't hold. + !*/ + + template <typename EXP> + const matrix solve ( + const matrix_exp<EXP>& B + ) const; + /*! + requires + - EXP::type == cholesky_decomposition::type + - B.nr() == get_l().nr() + (i.e. the number of rows in B must match the number of rows in the + input matrix A) + ensures + - Let A denote the input matrix to this class's constructor. Then + this function solves A*X = B for X and returns X. + - Note that if is_spd() == false or A was really close to being + non-SPD then the solver will fail to find an accurate solution. + !*/ + + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename matrix_exp_type + > + class qr_decomposition + { + /*! + REQUIREMENTS ON matrix_exp_type + must be some kind of matrix expression as defined in the + dlib/matrix/matrix_abstract.h file. (e.g. a dlib::matrix object) + The matrix type must also contain float or double values. + + WHAT THIS OBJECT REPRESENTS + This object represents something that can compute a classical + QR decomposition of an m-by-n real valued matrix A with m >= n. + + The QR decomposition is an m-by-n orthogonal matrix Q and an + n-by-n upper triangular matrix R so that A == Q*R. The QR decomposition + always exists, even if the matrix does not have full rank, so the + constructor will never fail. The primary use of the QR decomposition + is in the least squares solution of non-square systems of simultaneous + linear equations. This will fail if is_full_rank() returns false or + A is very nearly not full rank. + + The Q and R factors can be retrieved via the get_q() and get_r() + methods. Furthermore, a solve() method is provided to find the + least squares solution of Ax=b using the QR factors. + + If DLIB_USE_LAPACK is #defined then the xGEQRF routine + from LAPACK is used to compute the QR decomposition. + !*/ + + public: + + const static long NR = matrix_exp_type::NR; + const static long NC = matrix_exp_type::NC; + typedef typename matrix_exp_type::type type; + typedef typename matrix_exp_type::mem_manager_type mem_manager_type; + typedef typename matrix_exp_type::layout_type layout_type; + + typedef matrix<type,0,0,mem_manager_type,layout_type> matrix_type; + + template <typename EXP> + qr_decomposition( + const matrix_exp<EXP>& A + ); + /*! + requires + - EXP::type == qr_decomposition::type + - A.nr() >= A.nc() + - A.size() > 0 + ensures + - #nr() == A.nr() + - #nc() == A.nc() + - computes the QR decomposition of the given A matrix. + !*/ + + bool is_full_rank( + ) const; + /*! + ensures + - if (the input A matrix had full rank) then + - returns true + - else + - returns false + !*/ + + long nr( + ) const; + /*! + ensures + - returns the number of rows in the input matrix + !*/ + + long nc( + ) const; + /*! + ensures + - returns the number of columns in the input matrix + !*/ + + const matrix_type get_r ( + ) const; + /*! + ensures + - returns a matrix R such that: + - R is the upper triangular factor, R, of the QR factorization + - get_q()*R == input matrix A + - R.nr() == nc() + - R.nc() == nc() + !*/ + + const matrix_type get_q ( + ) const; + /*! + ensures + - returns a matrix Q such that: + - Q is the economy-sized orthogonal factor Q from the QR + factorization. + - trans(Q)*Q == identity matrix + - Q*get_r() == input matrix A + - Q.nr() == nr() + - Q.nc() == nc() + !*/ + + void get_q ( + matrix_type& Q + ) const; + /*! + ensures + - #Q == get_q() + - This function exists to allow a user to get the Q matrix without the + overhead of returning a matrix by value. + !*/ + + template <typename EXP> + const matrix_type solve ( + const matrix_exp<EXP>& B + ) const; + /*! + requires + - EXP::type == qr_decomposition::type + - B.nr() == nr() + ensures + - Let A denote the input matrix to this class's constructor. + Then this function finds the least squares solution to the equation A*X = B + and returns X. X has the following properties: + - X is the matrix that minimizes the two norm of A*X-B. That is, it + minimizes sum(squared(A*X - B)). + - X.nr() == nc() + - X.nc() == B.nc() + - Note that this function will fail to output a good solution if is_full_rank() == false + or the A matrix is close to not being full rank. + !*/ + + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename matrix_exp_type + > + class eigenvalue_decomposition + { + /*! + REQUIREMENTS ON matrix_exp_type + must be some kind of matrix expression as defined in the + dlib/matrix/matrix_abstract.h file. (e.g. a dlib::matrix object) + The matrix type must also contain float or double values. + + WHAT THIS OBJECT REPRESENTS + This object represents something that can compute an eigenvalue + decomposition of a real valued matrix. So it gives + you the set of eigenvalues and eigenvectors for a matrix. + + Let A denote the input matrix to this object's constructor. Then + what this object does is it finds two matrices, D and V, such that + - A*V == V*D + Where V is a square matrix that contains all the eigenvectors + of the A matrix (each column of V is an eigenvector) and + D is a diagonal matrix containing the eigenvalues of A. + + + It is important to note that if A is symmetric or non-symmetric you + get somewhat different results. If A is a symmetric matrix (i.e. A == trans(A)) + then: + - All the eigenvalues and eigenvectors of A are real numbers. + - Because of this there isn't really any point in using the + part of this class's interface that returns complex matrices. + All you need are the get_real_eigenvalues() and + get_pseudo_v() functions. + - V*trans(V) should be equal to the identity matrix. That is, all the + eigenvectors in V should be orthonormal. + - So A == V*D*trans(V) + - If DLIB_USE_LAPACK is #defined then this object uses the xSYEVR LAPACK + routine. + + On the other hand, if A is not symmetric then: + - Some of the eigenvalues and eigenvectors might be complex numbers. + - An eigenvalue is complex if and only if its corresponding eigenvector + is complex. So you can check for this case by just checking + get_imag_eigenvalues() to see if any values are non-zero. You don't + have to check the V matrix as well. + - V*trans(V) won't be equal to the identity matrix but it is usually + invertible. So A == V*D*inv(V) is usually a valid statement but + A == V*D*trans(V) won't be. + - If DLIB_USE_LAPACK is #defined then this object uses the xGEEV LAPACK + routine. + !*/ + + public: + + const static long NR = matrix_exp_type::NR; + const static long NC = matrix_exp_type::NC; + typedef typename matrix_exp_type::type type; + typedef typename matrix_exp_type::mem_manager_type mem_manager_type; + typedef typename matrix_exp_type::layout_type layout_type; + + typedef typename matrix_exp_type::matrix_type matrix_type; + typedef matrix<type,NR,1,mem_manager_type,layout_type> column_vector_type; + + typedef matrix<std::complex<type>,0,0,mem_manager_type,layout_type> complex_matrix_type; + typedef matrix<std::complex<type>,NR,1,mem_manager_type,layout_type> complex_column_vector_type; + + + template <typename EXP> + eigenvalue_decomposition( + const matrix_exp<EXP>& A + ); + /*! + requires + - A.nr() == A.nc() + - A.size() > 0 + - EXP::type == eigenvalue_decomposition::type + ensures + - #dim() == A.nr() + - computes the eigenvalue decomposition of A. + - #get_eigenvalues() == the eigenvalues of A + - #get_v() == all the eigenvectors of A + !*/ + + template <typename EXP> + eigenvalue_decomposition( + const matrix_op<op_make_symmetric<EXP> >& A + ); + /*! + requires + - A.nr() == A.nc() + - A.size() > 0 + - EXP::type == eigenvalue_decomposition::type + ensures + - #dim() == A.nr() + - computes the eigenvalue decomposition of the symmetric matrix A. Does so + using a method optimized for symmetric matrices. + - #get_eigenvalues() == the eigenvalues of A + - #get_v() == all the eigenvectors of A + - moreover, since A is symmetric there won't be any imaginary eigenvalues. So + we will have: + - #get_imag_eigenvalues() == 0 + - #get_real_eigenvalues() == the eigenvalues of A + - #get_pseudo_v() == all the eigenvectors of A + - diagm(#get_real_eigenvalues()) == #get_pseudo_d() + + Note that the symmetric matrix operator is created by the + dlib::make_symmetric() function. This function simply reflects + the lower triangular part of a square matrix into the upper triangular + part to create a symmetric matrix. It can also be used to denote that a + matrix is already symmetric using the C++ type system. + !*/ + + long dim ( + ) const; + /*! + ensures + - dim() == the number of rows/columns in the input matrix A + !*/ + + const complex_column_vector_type get_eigenvalues ( + ) const; + /*! + ensures + - returns diag(get_d()). That is, returns a + vector that contains the eigenvalues of the input + matrix. + - the returned vector has dim() rows + - the eigenvalues are not sorted in any particular way + !*/ + + const column_vector_type& get_real_eigenvalues ( + ) const; + /*! + ensures + - returns the real parts of the eigenvalues. That is, + returns real(get_eigenvalues()) + - the returned vector has dim() rows + - the eigenvalues are not sorted in any particular way + !*/ + + const column_vector_type& get_imag_eigenvalues ( + ) const; + /*! + ensures + - returns the imaginary parts of the eigenvalues. That is, + returns imag(get_eigenvalues()) + - the returned vector has dim() rows + - the eigenvalues are not sorted in any particular way + !*/ + + const complex_matrix_type get_v ( + ) const; + /*! + ensures + - returns the eigenvector matrix V that is + dim() rows by dim() columns + - Each column in V is one of the eigenvectors of the input + matrix + !*/ + + const complex_matrix_type get_d ( + ) const; + /*! + ensures + - returns a matrix D such that: + - D.nr() == dim() + - D.nc() == dim() + - diag(D) == get_eigenvalues() + (i.e. the diagonal of D contains all the eigenvalues in the input matrix) + - all off diagonal elements of D are set to 0 + !*/ + + const matrix_type& get_pseudo_v ( + ) const; + /*! + ensures + - returns a matrix that is dim() rows by dim() columns + - Let A denote the input matrix given to this object's constructor. + - if (A has any imaginary eigenvalues) then + - returns the pseudo-eigenvector matrix V + - The matrix V returned by this function is structured such that: + - A*V == V*get_pseudo_d() + - else + - returns the eigenvector matrix V with A's eigenvectors as + the columns of V + - A*V == V*diagm(get_real_eigenvalues()) + !*/ + + const matrix_type get_pseudo_d ( + ) const; + /*! + ensures + - The returned matrix is dim() rows by dim() columns + - Computes and returns the block diagonal eigenvalue matrix. + If the original matrix A is not symmetric, then the eigenvalue + matrix D is block diagonal with the real eigenvalues in 1-by-1 + blocks and any complex eigenvalues, + a + i*b, in 2-by-2 blocks, (a, b; -b, a). That is, if the complex + eigenvalues look like + + u + iv . . . . . + . u - iv . . . . + . . a + ib . . . + . . . a - ib . . + . . . . x . + . . . . . y + + Then D looks like + + u v . . . . + -v u . . . . + . . a b . . + . . -b a . . + . . . . x . + . . . . . y + + This keeps V (The V you get from get_pseudo_v()) a real matrix in both + symmetric and non-symmetric cases, and A*V = V*D. + - the eigenvalues are not sorted in any particular way + !*/ + + }; + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_LA_FUNCTS_ABSTRACT_ + diff --git a/ml/dlib/dlib/matrix/matrix_lu.h b/ml/dlib/dlib/matrix/matrix_lu.h new file mode 100644 index 000000000..3e49cd653 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_lu.h @@ -0,0 +1,361 @@ +// Copyright (C) 2009 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +// This code was adapted from code from the JAMA part of NIST's TNT library. +// See: http://math.nist.gov/tnt/ +#ifndef DLIB_MATRIX_LU_DECOMPOSITION_H +#define DLIB_MATRIX_LU_DECOMPOSITION_H + +#include "matrix.h" +#include "matrix_utilities.h" +#include "matrix_subexp.h" +#include "matrix_trsm.h" +#include <algorithm> + +#ifdef DLIB_USE_LAPACK +#include "lapack/getrf.h" +#endif + + +namespace dlib +{ + + template < + typename matrix_exp_type + > + class lu_decomposition + { + public: + + const static long NR = matrix_exp_type::NR; + const static long NC = matrix_exp_type::NC; + typedef typename matrix_exp_type::type type; + typedef typename matrix_exp_type::mem_manager_type mem_manager_type; + typedef typename matrix_exp_type::layout_type layout_type; + + typedef matrix<type,0,0,mem_manager_type,layout_type> matrix_type; + typedef matrix<type,NR,1,mem_manager_type,layout_type> column_vector_type; + typedef matrix<long,NR,1,mem_manager_type,layout_type> pivot_column_vector_type; + + // You have supplied an invalid type of matrix_exp_type. You have + // to use this object with matrices that contain float or double type data. + COMPILE_TIME_ASSERT((is_same_type<float, type>::value || + is_same_type<double, type>::value )); + + template <typename EXP> + lu_decomposition ( + const matrix_exp<EXP> &A + ); + + bool is_square ( + ) const; + + bool is_singular ( + ) const; + + long nr( + ) const; + + long nc( + ) const; + + const matrix_type get_l ( + ) const; + + const matrix_type get_u ( + ) const; + + const pivot_column_vector_type& get_pivot ( + ) const; + + type det ( + ) const; + + template <typename EXP> + const matrix_type solve ( + const matrix_exp<EXP> &B + ) const; + + private: + + /* Array for internal storage of decomposition. */ + matrix<type,0,0,mem_manager_type,column_major_layout> LU; + long m, n, pivsign; + pivot_column_vector_type piv; + + + }; + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Public member functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + lu_decomposition<matrix_exp_type>:: + lu_decomposition ( + const matrix_exp<EXP>& A + ) : + LU(A), + m(A.nr()), + n(A.nc()) + { + using namespace std; + using std::abs; + + COMPILE_TIME_ASSERT((is_same_type<type, typename EXP::type>::value)); + + // make sure requires clause is not broken + DLIB_ASSERT(A.size() > 0, + "\tlu_decomposition::lu_decomposition(A)" + << "\n\tInvalid inputs were given to this function" + << "\n\tA.size(): " << A.size() + << "\n\tthis: " << this + ); + +#ifdef DLIB_USE_LAPACK + matrix<lapack::integer,0,1,mem_manager_type,layout_type> piv_temp; + lapack::getrf(LU, piv_temp); + + pivsign = 1; + + // Turn the piv_temp vector into a more useful form. This way we will have the identity + // rowm(A,piv) == L*U. The permutation vector that comes out of LAPACK is somewhat + // different. + piv = trans(range(0,m-1)); + for (long i = 0; i < piv_temp.size(); ++i) + { + // -1 because FORTRAN is indexed starting with 1 instead of 0 + if (piv(piv_temp(i)-1) != piv(i)) + { + std::swap(piv(i), piv(piv_temp(i)-1)); + pivsign = -pivsign; + } + } + +#else + + // Use a "left-looking", dot-product, Crout/Doolittle algorithm. + + + piv = trans(range(0,m-1)); + pivsign = 1; + + column_vector_type LUcolj(m); + + // Outer loop. + for (long j = 0; j < n; j++) + { + + // Make a copy of the j-th column to localize references. + LUcolj = colm(LU,j); + + // Apply previous transformations. + for (long i = 0; i < m; i++) + { + // Most of the time is spent in the following dot product. + const long kmax = std::min(i,j); + type s; + if (kmax > 0) + s = rowm(LU,i, kmax)*colm(LUcolj,0,kmax); + else + s = 0; + + LU(i,j) = LUcolj(i) -= s; + } + + // Find pivot and exchange if necessary. + long p = j; + for (long i = j+1; i < m; i++) + { + if (abs(LUcolj(i)) > abs(LUcolj(p))) + { + p = i; + } + } + if (p != j) + { + long k=0; + for (k = 0; k < n; k++) + { + type t = LU(p,k); + LU(p,k) = LU(j,k); + LU(j,k) = t; + } + k = piv(p); + piv(p) = piv(j); + piv(j) = k; + pivsign = -pivsign; + } + + // Compute multipliers. + if ((j < m) && (LU(j,j) != 0.0)) + { + for (long i = j+1; i < m; i++) + { + LU(i,j) /= LU(j,j); + } + } + } + +#endif + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + bool lu_decomposition<matrix_exp_type>:: + is_square ( + ) const + { + return m == n; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + long lu_decomposition<matrix_exp_type>:: + nr ( + ) const + { + return m; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + long lu_decomposition<matrix_exp_type>:: + nc ( + ) const + { + return n; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + bool lu_decomposition<matrix_exp_type>:: + is_singular ( + ) const + { + /* Is the matrix singular? + if upper triangular factor U (and hence A) is singular, false otherwise. + */ + // make sure requires clause is not broken + DLIB_ASSERT(is_square() == true, + "\tbool lu_decomposition::is_singular()" + << "\n\tYou can only use this on square matrices" + << "\n\tthis: " << this + ); + + type max_val, min_val; + find_min_and_max (abs(diag(LU)), min_val, max_val); + type eps = max_val; + if (eps != 0) + eps *= std::sqrt(std::numeric_limits<type>::epsilon())/10; + else + eps = 1; // there is no max so just use 1 + + return min_val < eps; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename lu_decomposition<matrix_exp_type>::matrix_type lu_decomposition<matrix_exp_type>:: + get_l ( + ) const + { + if (LU.nr() >= LU.nc()) + return lowerm(LU,1.0); + else + return lowerm(subm(LU,0,0,m,m), 1.0); + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename lu_decomposition<matrix_exp_type>::matrix_type lu_decomposition<matrix_exp_type>:: + get_u ( + ) const + { + if (LU.nr() >= LU.nc()) + return upperm(subm(LU,0,0,n,n)); + else + return upperm(LU); + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename lu_decomposition<matrix_exp_type>::pivot_column_vector_type& lu_decomposition<matrix_exp_type>:: + get_pivot ( + ) const + { + return piv; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + typename lu_decomposition<matrix_exp_type>::type lu_decomposition<matrix_exp_type>:: + det ( + ) const + { + // make sure requires clause is not broken + DLIB_ASSERT(is_square() == true, + "\ttype lu_decomposition::det()" + << "\n\tYou can only use this on square matrices" + << "\n\tthis: " << this + ); + + // Check if it is singular and if it is just return 0. + // We want to do this because a prod() operation can easily + // overcome a single diagonal element that is effectively 0 when + // LU is a big enough matrix. + if (is_singular()) + return 0; + + return prod(diag(LU))*static_cast<type>(pivsign); + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + const typename lu_decomposition<matrix_exp_type>::matrix_type lu_decomposition<matrix_exp_type>:: + solve ( + const matrix_exp<EXP> &B + ) const + { + COMPILE_TIME_ASSERT((is_same_type<type, typename EXP::type>::value)); + + // make sure requires clause is not broken + DLIB_ASSERT(is_square() == true && B.nr() == nr(), + "\ttype lu_decomposition::solve()" + << "\n\tInvalid arguments to this function" + << "\n\tis_square(): " << (is_square()? "true":"false" ) + << "\n\tB.nr(): " << B.nr() + << "\n\tnr(): " << nr() + << "\n\tthis: " << this + ); + + // Copy right hand side with pivoting + matrix<type,0,0,mem_manager_type,column_major_layout> X(rowm(B, piv)); + + using namespace blas_bindings; + // Solve L*Y = B(piv,:) + triangular_solver(CblasLeft, CblasLower, CblasNoTrans, CblasUnit, LU, X); + // Solve U*X = Y; + triangular_solver(CblasLeft, CblasUpper, CblasNoTrans, CblasNonUnit, LU, X); + return X; + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIX_LU_DECOMPOSITION_H + + diff --git a/ml/dlib/dlib/matrix/matrix_mat.h b/ml/dlib/dlib/matrix/matrix_mat.h new file mode 100644 index 000000000..803d7d999 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_mat.h @@ -0,0 +1,733 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_MAT_Hh_ +#define DLIB_MATRIx_MAT_Hh_ + +#include "matrix_mat_abstract.h" +#include "../stl_checked.h" +#include <vector> +#include "matrix_op.h" +#include "../array2d.h" +#include "../array.h" +#include "../image_processing/generic_image.h" + + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix_exp<EXP>& mat ( + const matrix_exp<EXP>& m + ) + { + return m; + } + +// ---------------------------------------------------------------------------------------- + + template <typename image_type, typename pixel_type> + struct op_image_to_mat : does_not_alias + { + op_image_to_mat( const image_type& img) : imgview(img){} + + const_image_view<image_type> imgview; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 0; + typedef pixel_type type; + typedef const pixel_type& const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long c ) const { return imgview[r][c]; } + + long nr () const { return imgview.nr(); } + long nc () const { return imgview.nc(); } + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename image_type + > // The reason we disable this if it is a matrix is because this matrix_op claims + // to not alias any matrix. But obviously that would be a problem if we let it + // take a matrix. + const typename disable_if<is_matrix<image_type>,matrix_op<op_image_to_mat<image_type, typename image_traits<image_type>::pixel_type> > >::type mat ( + const image_type& img + ) + { + typedef op_image_to_mat<image_type, typename image_traits<image_type>::pixel_type> op; + return matrix_op<op>(op(img)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename image_type> + struct op_image_view_to_mat : does_not_alias + { + op_image_view_to_mat( const image_view<image_type>& img) : imgview(img){} + + typedef typename image_traits<image_type>::pixel_type pixel_type; + + const image_view<image_type>& imgview; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 0; + typedef pixel_type type; + typedef const pixel_type& const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long c ) const { return imgview[r][c]; } + + long nr () const { return imgview.nr(); } + long nc () const { return imgview.nc(); } + }; + + template < + typename image_type + > + const matrix_op<op_image_view_to_mat<image_type> > mat ( + const image_view<image_type>& img + ) + { + typedef op_image_view_to_mat<image_type> op; + return matrix_op<op>(op(img)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename image_type> + struct op_const_image_view_to_mat : does_not_alias + { + op_const_image_view_to_mat( const const_image_view<image_type>& img) : imgview(img){} + + typedef typename image_traits<image_type>::pixel_type pixel_type; + + const const_image_view<image_type>& imgview; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 0; + typedef pixel_type type; + typedef const pixel_type& const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long c ) const { return imgview[r][c]; } + + long nr () const { return imgview.nr(); } + long nc () const { return imgview.nc(); } + }; + + template < + typename image_type + > + const matrix_op<op_const_image_view_to_mat<image_type> > mat ( + const const_image_view<image_type>& img + ) + { + typedef op_const_image_view_to_mat<image_type> op; + return matrix_op<op>(op(img)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T> + struct op_array_to_mat : does_not_alias + { + op_array_to_mat( const T& vect_) : vect(vect_){} + + const T& vect; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 1; + typedef typename T::type type; + typedef const typename T::type& const_ret_type; + typedef typename T::mem_manager_type mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long ) const { return vect[r]; } + + long nr () const { return vect.size(); } + long nc () const { return 1; } + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + typename MM + > + const matrix_op<op_array_to_mat<array<T,MM> > > mat ( + const array<T,MM>& m + ) + { + typedef op_array_to_mat<array<T,MM> > op; + return matrix_op<op>(op(m)); + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename U> + struct not_bool { typedef U type; }; + template <> + struct not_bool<const bool&> { typedef bool type; }; + } + + template <typename T> + struct op_std_vect_to_mat : does_not_alias + { + op_std_vect_to_mat( const T& vect_) : vect(vect_){} + + const T& vect; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 1; + typedef typename T::value_type type; + // Since std::vector returns a proxy for bool types we need to make sure we don't + // return an element by reference if it is a bool type. + typedef typename impl::not_bool<const typename T::value_type&>::type const_ret_type; + + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long ) const { return vect[r]; } + + long nr () const { return vect.size(); } + long nc () const { return 1; } + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename value_type, + typename alloc + > + const matrix_op<op_std_vect_to_mat<std::vector<value_type,alloc> > > mat ( + const std::vector<value_type,alloc>& vector + ) + { + typedef op_std_vect_to_mat<std::vector<value_type,alloc> > op; + return matrix_op<op>(op(vector)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename value_type, + typename alloc + > + const matrix_op<op_std_vect_to_mat<std_vector_c<value_type,alloc> > > mat ( + const std_vector_c<value_type,alloc>& vector + ) + { + typedef op_std_vect_to_mat<std_vector_c<value_type,alloc> > op; + return matrix_op<op>(op(vector)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T> + struct op_pointer_to_mat; + + template <typename T> + struct op_pointer_to_col_vect + { + op_pointer_to_col_vect( + const T* ptr_, + const long size_ + ) : ptr(ptr_), size(size_){} + + const T* ptr; + const long size; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 1; + typedef T type; + typedef const T& const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long ) const { return ptr[r]; } + + long nr () const { return size; } + long nc () const { return 1; } + + template <typename U> bool aliases ( const matrix_exp<U>& ) const { return false; } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& ) const { return false; } + + template <long num_rows, long num_cols, typename mem_manager, typename layout> + bool aliases ( + const matrix_exp<matrix<T,num_rows,num_cols, mem_manager,layout> >& item + ) const + { + if (item.size() == 0) + return false; + else + return (ptr == &item(0,0)); + } + + inline 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 + { + return item.ref().op.ptr == ptr; + } + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_op<op_pointer_to_col_vect<T> > mat ( + const T* ptr, + long nr + ) + { + DLIB_ASSERT(nr >= 0 , + "\tconst matrix_exp mat(ptr, nr)" + << "\n\t nr must be >= 0" + << "\n\t nr: " << nr + ); + typedef op_pointer_to_col_vect<T> op; + return matrix_op<op>(op(ptr, nr)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T> + struct op_pointer_to_mat + { + op_pointer_to_mat( + const T* ptr_, + const long nr_, + const long nc_ + ) : ptr(ptr_), rows(nr_), cols(nc_), stride(nc_){} + + op_pointer_to_mat( + const T* ptr_, + const long nr_, + const long nc_, + const long stride_ + ) : ptr(ptr_), rows(nr_), cols(nc_), stride(stride_){} + + const T* ptr; + const long rows; + const long cols; + const long stride; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 0; + typedef T type; + typedef const T& const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long c) const { return ptr[r*stride + c]; } + + long nr () const { return rows; } + long nc () const { return cols; } + + template <typename U> bool aliases ( const matrix_exp<U>& ) const { return false; } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& ) const { return false; } + + template <long num_rows, long num_cols, typename mem_manager, typename layout> + bool aliases ( + const matrix_exp<matrix<T,num_rows,num_cols, mem_manager,layout> >& item + ) const + { + if (item.size() == 0) + return false; + else + return (ptr == &item(0,0)); + } + + bool aliases ( + const matrix_exp<matrix_op<op_pointer_to_mat<T> > >& item + ) const + { + return item.ref().op.ptr == ptr; + } + + bool aliases ( + const matrix_exp<matrix_op<op_pointer_to_col_vect<T> > >& item + ) const + { + return item.ref().op.ptr == ptr; + } + }; + + template <typename T> + bool op_pointer_to_col_vect<T>:: + aliases ( + const matrix_exp<matrix_op<op_pointer_to_mat<T> > >& item + ) const + { + return item.ref().op.ptr == ptr; + } + + template <typename T, long NR, long NC, typename MM, typename L> + bool matrix<T,NR,NC,MM,L>::aliases ( + const matrix_exp<matrix_op<op_pointer_to_mat<T> > >& item + ) const + { + if (size() != 0) + return item.ref().op.ptr == &data(0,0); + else + return false; + } + + template <typename T, long NR, long NC, typename MM, typename L> + bool matrix<T,NR,NC,MM,L>::aliases ( + const matrix_exp<matrix_op<op_pointer_to_col_vect<T> > >& item + ) const + { + if (size() != 0) + return item.ref().op.ptr == &data(0,0); + else + return false; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_op<op_pointer_to_mat<T> > mat ( + const T* ptr, + long nr, + long nc + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0 , + "\tconst matrix_exp mat(ptr, nr, nc)" + << "\n\t nr and nc must be >= 0" + << "\n\t nr: " << nr + << "\n\t nc: " << nc + ); + typedef op_pointer_to_mat<T> op; + return matrix_op<op>(op(ptr,nr,nc)); + } + + template < + typename T + > + const matrix_op<op_pointer_to_mat<T> > mat ( + const T* ptr, + long nr, + long nc, + long stride + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0 && stride > 0 , + "\tconst matrix_exp mat(ptr, nr, nc, stride)" + << "\n\t nr and nc must be >= 0 while stride > 0" + << "\n\t nr: " << nr + << "\n\t nc: " << nc + << "\n\t stride: " << stride + ); + typedef op_pointer_to_mat<T> op; + return matrix_op<op>(op(ptr,nr,nc,stride)); + } + +// ---------------------------------------------------------------------------------------- + +} + +namespace arma +{ + template <typename T> class Mat; +} +namespace dlib +{ + template <typename T> + struct op_arma_Mat_to_mat : does_not_alias + { + op_arma_Mat_to_mat( const T& array_) : array(array_){} + + const T& array; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 0; + typedef typename T::elem_type type; + typedef typename T::elem_type const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long c ) const { return array(r,c); } + + long nr () const { return array.n_rows; } + long nc () const { return array.n_cols; } + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_op<op_arma_Mat_to_mat< ::arma::Mat<T> > > mat ( + const ::arma::Mat<T>& array + ) + { + typedef op_arma_Mat_to_mat< ::arma::Mat<T> > op; + return matrix_op<op>(op(array)); + } +} + +namespace Eigen +{ + template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> + class Matrix; +} + +namespace dlib +{ + template <typename T, int _Rows, int _Cols> + struct op_eigen_Matrix_to_mat : does_not_alias + { + op_eigen_Matrix_to_mat( const T& array_) : m(array_){} + + const T& m; + + const static long cost = 1; + const static long NR = (_Rows > 0) ? _Rows : 0; + const static long NC = (_Cols > 0) ? _Cols : 0; + typedef typename T::Scalar type; + typedef typename T::Scalar const_ret_type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long c ) const { return m(r,c); } + + long nr () const { return m.rows(); } + long nc () const { return m.cols(); } + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols + > + const matrix_op<op_eigen_Matrix_to_mat< ::Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>,_Rows,_Cols > > mat ( + const ::Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>& m + ) + { + typedef op_eigen_Matrix_to_mat< ::Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>,_Rows,_Cols > op; + return matrix_op<op>(op(m)); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// DEPRECATED FUNCTIONS +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +// vector_to_matrix(), array_to_matrix(), pointer_to_matrix(), and +// pointer_to_column_vector() have been deprecated in favor of the more uniform mat() +// function. But they are here for backwards compatibility. + + template < + typename vector_type + > + const typename disable_if<is_matrix<vector_type>, matrix_op<op_array_to_mat<vector_type> > >::type + vector_to_matrix ( + const vector_type& vector + ) + { + typedef op_array_to_mat<vector_type> op; + return matrix_op<op>(op(vector)); + } + + template < + typename vector_type + > + const typename enable_if<is_matrix<vector_type>,vector_type>::type& vector_to_matrix ( + const vector_type& vector + ) + /*! + This overload catches the case where the argument to this function is + already a matrix. + !*/ + { + return vector; + } + + template < + typename value_type, + typename alloc + > + const matrix_op<op_std_vect_to_mat<std::vector<value_type,alloc> > > vector_to_matrix ( + const std::vector<value_type,alloc>& vector + ) + { + typedef op_std_vect_to_mat<std::vector<value_type,alloc> > op; + return matrix_op<op>(op(vector)); + } + + template < + typename value_type, + typename alloc + > + const matrix_op<op_std_vect_to_mat<std_vector_c<value_type,alloc> > > vector_to_matrix ( + const std_vector_c<value_type,alloc>& vector + ) + { + typedef op_std_vect_to_mat<std_vector_c<value_type,alloc> > op; + return matrix_op<op>(op(vector)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename array_type + > + const typename enable_if<is_matrix<array_type>,array_type>::type& + array_to_matrix ( + const array_type& array + ) + { + return array; + } + +// ---------------------------------------------------------------------------------------- + + template <typename T> + struct op_array2d_to_mat : does_not_alias + { + op_array2d_to_mat( const T& array_) : array(array_){} + + const T& array; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 0; + typedef typename T::type type; + typedef const typename T::type& const_ret_type; + typedef typename T::mem_manager_type mem_manager_type; + typedef row_major_layout layout_type; + + const_ret_type apply (long r, long c ) const { return array[r][c]; } + + long nr () const { return array.nr(); } + long nc () const { return array.nc(); } + }; + + // Note that we have this version of mat() because it's slightly faster executing + // than the general one that handles any generic image. This is because it avoids + // calling image_data() which for array2d involves a single if statement but this + // version here has no if statement in its construction. + template < typename T, typename MM > + const matrix_op<op_array2d_to_mat<array2d<T,MM> > > mat ( + const array2d<T,MM>& array + ) + { + typedef op_array2d_to_mat<array2d<T,MM> > op; + return matrix_op<op>(op(array)); + } + + template < + typename array_type + > + const typename disable_if<is_matrix<array_type>,matrix_op<op_array2d_to_mat<array_type> > >::type + array_to_matrix ( + const array_type& array + ) + { + typedef op_array2d_to_mat<array_type> op; + return matrix_op<op>(op(array)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_op<op_pointer_to_mat<T> > pointer_to_matrix ( + const T* ptr, + long nr, + long nc + ) + { + DLIB_ASSERT(nr > 0 && nc > 0 , + "\tconst matrix_exp pointer_to_matrix(ptr, nr, nc)" + << "\n\t nr and nc must be bigger than 0" + << "\n\t nr: " << nr + << "\n\t nc: " << nc + ); + typedef op_pointer_to_mat<T> op; + return matrix_op<op>(op(ptr,nr,nc)); + } + + template < + typename T + > + const matrix_op<op_pointer_to_col_vect<T> > pointer_to_column_vector ( + const T* ptr, + long nr + ) + { + DLIB_ASSERT(nr > 0 , + "\tconst matrix_exp pointer_to_column_vector(ptr, nr)" + << "\n\t nr must be bigger than 0" + << "\n\t nr: " << nr + ); + typedef op_pointer_to_col_vect<T> op; + return matrix_op<op>(op(ptr, nr)); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + inline matrix<double,1,1> mat ( + double value + ) + { + matrix<double,1,1> temp; + temp(0) = value; + return temp; + } + + inline matrix<float,1,1> mat ( + float value + ) + { + matrix<float,1,1> temp; + temp(0) = value; + return temp; + } + + inline matrix<long double,1,1> mat ( + long double value + ) + { + matrix<long double,1,1> temp; + temp(0) = value; + return temp; + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_MAT_Hh_ + + diff --git a/ml/dlib/dlib/matrix/matrix_mat_abstract.h b/ml/dlib/dlib/matrix/matrix_mat_abstract.h new file mode 100644 index 000000000..7026f60a1 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_mat_abstract.h @@ -0,0 +1,243 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_MAT_ABSTRACT_Hh_ +#ifdef DLIB_MATRIx_MAT_ABSTRACT_Hh_ + +#include "matrix_abstract.h" +#inclue <vector> +#include "../array/array_kernel_abstract.h" +#include "../array2d/array2d_kernel_abstract.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix_exp<EXP>& mat ( + const matrix_exp<EXP>& m + ); + /*! + ensures + - returns m + (i.e. this function just returns the input matrix without any modifications) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename image_type + > + const matrix_exp mat ( + const image_type& img + ); + /*! + requires + - image_type == an image object that implements the interface defined in + dlib/image_processing/generic_image.h or image_type is a image_view or + const_image_view object. + ensures + - This function converts any kind of generic image object into a dlib::matrix + expression. Therefore, it is capable of converting objects like dlib::array2d + of dlib::cv_image. + - returns a matrix R such that: + - R.nr() == array.nr() + - R.nc() == array.nc() + - for all valid r and c: + R(r, c) == array[r][c] + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + typename MM + > + const matrix_exp mat ( + const array<T,MM>& m + ); + /*! + ensures + - returns a matrix R such that: + - is_col_vector(R) == true + - R.size() == m.size() + - for all valid r: + R(r) == m[r] + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename value_type, + typename alloc + > + const matrix_exp mat ( + const std::vector<value_type,alloc>& vector + ); + /*! + ensures + - returns a matrix R such that: + - is_col_vector(R) == true + - R.size() == vector.size() + - for all valid r: + R(r) == vector[r] + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename value_type, + typename alloc + > + const matrix_exp mat ( + const std_vector_c<value_type,alloc>& vector + ); + /*! + ensures + - returns a matrix R such that: + - is_col_vector(R) == true + - R.size() == vector.size() + - for all valid r: + R(r) == vector[r] + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp mat ( + const T* ptr, + long nr + ); + /*! + requires + - nr >= 0 + - ptr == a pointer to at least nr T objects (or the NULL pointer if nr==0) + ensures + - returns a matrix M such that: + - M.nr() == nr + - m.nc() == 1 + - for all valid i: + M(i) == ptr[i] + - Note that the returned matrix doesn't take "ownership" of + the pointer and thus will not delete or free it. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp mat ( + const T* ptr, + long nr, + long nc + ); + /*! + requires + - nr >= 0 + - nc >= 0 + - ptr == a pointer to at least nr*nc T objects (or the NULL pointer if nr*nc==0) + ensures + - returns a matrix M such that: + - M.nr() == nr + - m.nc() == nc + - for all valid r and c: + M(r,c) == ptr[r*nc + c] + (i.e. the pointer is interpreted as a matrix laid out in memory + in row major order) + - Note that the returned matrix doesn't take "ownership" of + the pointer and thus will not delete or free it. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp mat ( + const T* ptr, + long nr, + long nc, + long stride + ); + /*! + requires + - nr >= 0 + - nc >= 0 + - stride > 0 + - ptr == a pointer to at least (nr-1)*stride+nc T objects (or the NULL pointer if nr*nc==0) + ensures + - returns a matrix M such that: + - M.nr() == nr + - m.nc() == nc + - for all valid r and c: + M(r,c) == ptr[r*stride + c] + (i.e. the pointer is interpreted as a matrix laid out in memory + in row major order, with a row stride of the given stride amount.) + - Note that the returned matrix doesn't take "ownership" of + the pointer and thus will not delete or free it. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp mat ( + const ::arma::Mat<T>& m + ); + /*! + ensures + - Converts a matrix from the Armadillo library into a dlib matrix. + - returns a matrix R such that: + - R.nr() == m.n_rows + - R.nc() == m.n_cols + - for all valid r: + R(r,c) == m(r,c) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename _Scalar, + int _Rows, + int _Cols, + int _Options, + int _MaxRows, + int _MaxCols + > + const matrix_exp mat ( + const ::Eigen::Matrix<_Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>& m + ); + /*! + ensures + - Converts a matrix from the Eigen library into a dlib matrix. + - returns a matrix R such that: + - R.nr() == m.rows() + - R.nc() == m.cols() + - for all valid r: + R(r,c) == m(r,c) + !*/ + +// ---------------------------------------------------------------------------------------- + + matrix<double,1,1> mat (double value); + matrix<float,1,1> mat (float value); + matrix<long double,1,1> mat (long double value); + /*! + ensures + - Converts a scalar into a matrix containing just that scalar and returns the + results. + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_MAT_ABSTRACT_Hh_ + + diff --git a/ml/dlib/dlib/matrix/matrix_math_functions.h b/ml/dlib/dlib/matrix/matrix_math_functions.h new file mode 100644 index 000000000..d1db3ed14 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_math_functions.h @@ -0,0 +1,448 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_MATH_FUNCTIONS +#define DLIB_MATRIx_MATH_FUNCTIONS + +#include "matrix_math_functions_abstract.h" +#include "matrix_op.h" +#include "matrix_utilities.h" +#include "matrix.h" +#include "../algs.h" +#include <cmath> +#include <complex> +#include <limits> + + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + DLIB_DEFINE_FUNCTION_M(op_sqrt, sqrt, std::sqrt ,7); + DLIB_DEFINE_FUNCTION_M(op_log, log, std::log ,7); + DLIB_DEFINE_FUNCTION_M(op_log10, log10, std::log10 ,7); + DLIB_DEFINE_FUNCTION_M(op_exp, exp, std::exp ,7); + + DLIB_DEFINE_FUNCTION_M(op_conj, conj, std::conj ,2); + + DLIB_DEFINE_FUNCTION_M(op_ceil, ceil, std::ceil ,7); + DLIB_DEFINE_FUNCTION_M(op_floor, floor, std::floor ,7); + + DLIB_DEFINE_FUNCTION_M(op_sin, sin, std::sin ,7); + DLIB_DEFINE_FUNCTION_M(op_cos, cos, std::cos ,7); + DLIB_DEFINE_FUNCTION_M(op_tan, tan, std::tan ,7); + DLIB_DEFINE_FUNCTION_M(op_sinh, sinh, std::sinh ,7); + DLIB_DEFINE_FUNCTION_M(op_cosh, cosh, std::cosh ,7); + DLIB_DEFINE_FUNCTION_M(op_tanh, tanh, std::tanh ,7); + DLIB_DEFINE_FUNCTION_M(op_asin, asin, std::asin ,7); + DLIB_DEFINE_FUNCTION_M(op_acos, acos, std::acos ,7); + DLIB_DEFINE_FUNCTION_M(op_atan, atan, std::atan ,7); + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename type> + inline type sigmoid (const type& val) + { + return static_cast<type>(1/(1 + std::exp(-val))); + } + + template <typename type, typename S> + inline type round_zeros_eps (const type& val, const S& eps) + { + // you can only round matrices that contain built in scalar types like double, long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<type>::value); + + if (val >= eps || val <= -eps) + return val; + else + return 0; + } + + template <typename type> + inline type round_zeros (const type& val) + { + // you can only round matrices that contain built in scalar types like double, long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<type>::value); + + const type eps = 10*std::numeric_limits<type>::epsilon(); + if (val >= eps || val <= -eps) + return val; + else + return 0; + } + + template <typename type> + inline type squared (const type& val) + { + return val*val; + } + + template <typename type> + inline type sign (const type& val) + { + if (val >= 0) + return +1; + else + return -1; + } + + template <typename type> + type cubed (const type& val) + { + return val*val*val; + } + + template <typename type, typename S> + inline type pow1 (const type& val, const S& s) + { + // you can only call pow() on matrices that contain floats, doubles or long doubles. + COMPILE_TIME_ASSERT(( + is_same_type<type,float>::value == true || + is_same_type<type,double>::value == true || + is_same_type<type,long double>::value == true + )); + + return std::pow(val,static_cast<type>(s)); + } + + template <typename type, typename S> + inline type pow2 (const S& s, const type& val) + { + // you can only call pow() on matrices that contain floats, doubles or long doubles. + COMPILE_TIME_ASSERT(( + is_same_type<type,float>::value == true || + is_same_type<type,double>::value == true || + is_same_type<type,long double>::value == true + )); + + return std::pow(static_cast<type>(s),val); + } + + template <typename type> + inline type reciprocal (const type& val) + { + // you can only compute reciprocal matrices that contain floats, doubles or long doubles. + COMPILE_TIME_ASSERT(( + is_same_type<type,float>::value == true || + is_same_type<type,double>::value == true || + is_same_type<type,long double>::value == true || + is_same_type<type,std::complex<float> >::value == true || + is_same_type<type,std::complex<double> >::value == true || + is_same_type<type,std::complex<long double> >::value == true + )); + + if (val != static_cast<type>(0)) + return static_cast<type>((type)1.0/val); + else + return 0; + } + + template <typename type> + inline type reciprocal_max (const type& val) + { + // you can only compute reciprocal_max matrices that contain floats, doubles or long doubles. + COMPILE_TIME_ASSERT(( + is_same_type<type,float>::value == true || + is_same_type<type,double>::value == true || + is_same_type<type,long double>::value == true + )); + + if (val != static_cast<type>(0)) + return static_cast<type>((type)1.0/val); + else + return std::numeric_limits<type>::max(); + } + + } + + DLIB_DEFINE_FUNCTION_M(op_sigmoid, sigmoid, impl::sigmoid, 7); + DLIB_DEFINE_FUNCTION_MS(op_round_zeros, round_zeros, impl::round_zeros_eps, 7); + DLIB_DEFINE_FUNCTION_M(op_round_zeros2, round_zeros, impl::round_zeros, 7); + DLIB_DEFINE_FUNCTION_M(op_cubed, cubed, impl::cubed, 7); + DLIB_DEFINE_FUNCTION_M(op_squared, squared, impl::squared, 6); + DLIB_DEFINE_FUNCTION_M(op_sign, sign, impl::sign, 6); + DLIB_DEFINE_FUNCTION_MS(op_pow1, pow, impl::pow1, 7); + DLIB_DEFINE_FUNCTION_SM(op_pow2, pow, impl::pow2, 7); + DLIB_DEFINE_FUNCTION_M(op_reciprocal, reciprocal, impl::reciprocal, 6); + DLIB_DEFINE_FUNCTION_M(op_reciprocal_max, reciprocal_max, impl::reciprocal_max, 6); + +// ---------------------------------------------------------------------------------------- + + template <typename M, typename enabled = void> + struct op_round : basic_op_m<M> + { + op_round( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost+7; + typedef typename M::type type; + typedef const typename M::type const_ret_type; + const_ret_type apply (long r, long c) const + { + return static_cast<type>(std::floor(this->m(r,c)+0.5)); + } + }; + + template <typename M> + struct op_round<M,typename enable_if_c<std::numeric_limits<typename M::type>::is_integer>::type > + : basic_op_m<M> + { + op_round( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + const_ret_type apply (long r, long c) const + { + return this->m(r,c); + } + }; + + template < + typename EXP + > + const matrix_op<op_round<EXP> > round ( + const matrix_exp<EXP>& m + ) + { + // you can only round matrices that contain built in scalar types like double, long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_round<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_normalize : basic_op_m<M> + { + typedef typename M::type type; + + op_normalize( const M& m_, const type& s_) : basic_op_m<M>(m_), s(s_){} + + const type s; + + const static long cost = M::cost+5; + 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 + > + const matrix_op<op_normalize<EXP> > normalize ( + const matrix_exp<EXP>& m + ) + { + // you can only compute normalized matrices that contain floats, doubles or long doubles. + COMPILE_TIME_ASSERT(( + is_same_type<typename EXP::type,float>::value == true || + is_same_type<typename EXP::type,double>::value == true || + is_same_type<typename EXP::type,long double>::value == true + )); + + + typedef op_normalize<EXP> op; + typename EXP::type temp = std::sqrt(sum(squared(m))); + if (temp != 0.0) + temp = 1.0/temp; + + return matrix_op<op>(op(m.ref(),temp)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, typename return_type = typename M::type> + struct op_abs : basic_op_m<M> + { + op_abs( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost+7; + typedef typename M::type type; + typedef const typename M::type const_ret_type; + const_ret_type apply ( long r, long c) const + { + return static_cast<type>(std::abs(this->m(r,c))); + } + }; + + template <typename M, typename T> + struct op_abs<M, std::complex<T> > : basic_op_m<M> + { + op_abs( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost; + typedef T type; + typedef const T const_ret_type; + const_ret_type apply ( long r, long c) const + { + return static_cast<type>(std::abs(this->m(r,c))); + } + }; + + template < + typename EXP + > + const matrix_op<op_abs<EXP> > abs ( + const matrix_exp<EXP>& m + ) + { + typedef op_abs<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_complex_matrix : basic_op_m<M> + { + op_complex_matrix( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost+1; + typedef std::complex<typename M::type> type; + typedef const std::complex<typename M::type> const_ret_type; + const_ret_type apply ( long r, long c) const + { + return type(this->m(r,c)); + } + }; + + template < + typename EXP + > + const matrix_op<op_complex_matrix<EXP> > complex_matrix ( + const matrix_exp<EXP>& m + ) + { + typedef op_complex_matrix<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_complex_matrix2 : basic_op_mm<M1,M2> + { + op_complex_matrix2( const M1& m1_, const M2& m2_) : basic_op_mm<M1,M2>(m1_,m2_){} + + const static long cost = M1::cost+M2::cost+1; + typedef std::complex<typename M1::type> type; + typedef const std::complex<typename M1::type> const_ret_type; + + const_ret_type apply ( long r, long c) const + { return type(this->m1(r,c), this->m2(r,c)); } + }; + + template < + typename EXP1, + typename EXP2 + > + const matrix_op<op_complex_matrix2<EXP1,EXP2> > complex_matrix ( + const matrix_exp<EXP1>& real_part, + const matrix_exp<EXP2>& imag_part + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NC == 0 || EXP2::NC == 0); + + DLIB_ASSERT(real_part.nr() == imag_part.nr() && + real_part.nc() == imag_part.nc(), + "\tconst matrix_exp::type complex_matrix(real_part, imag_part)" + << "\n\tYou can only make a complex matrix from two equally sized matrices" + << "\n\treal_part.nr(): " << real_part.nr() + << "\n\treal_part.nc(): " << real_part.nc() + << "\n\timag_part.nr(): " << imag_part.nr() + << "\n\timag_part.nc(): " << imag_part.nc() + ); + + typedef op_complex_matrix2<EXP1,EXP2> op; + return matrix_op<op>(op(real_part.ref(),imag_part.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_norm : basic_op_m<M> + { + op_norm( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost+6; + typedef typename M::type::value_type type; + typedef const typename M::type::value_type const_ret_type; + const_ret_type apply ( long r, long c) const + { return std::norm(this->m(r,c)); } + }; + + template < + typename EXP + > + const matrix_op<op_norm<EXP> > norm ( + const matrix_exp<EXP>& m + ) + { + typedef op_norm<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_real : basic_op_m<M> + { + op_real( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost; + typedef typename M::type::value_type type; + typedef const typename M::type::value_type const_ret_type; + const_ret_type apply ( long r, long c) const + { return std::real(this->m(r,c)); } + }; + + template < + typename EXP + > + const matrix_op<op_real<EXP> > real ( + const matrix_exp<EXP>& m + ) + { + typedef op_real<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_imag : basic_op_m<M> + { + op_imag( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost; + typedef typename M::type::value_type type; + typedef const typename M::type::value_type const_ret_type; + const_ret_type apply (long r, long c) const + { return std::imag(this->m(r,c)); } + }; + + template < + typename EXP + > + const matrix_op<op_imag<EXP> > imag ( + const matrix_exp<EXP>& m + ) + { + typedef op_imag<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_MATH_FUNCTIONS + diff --git a/ml/dlib/dlib/matrix/matrix_math_functions_abstract.h b/ml/dlib/dlib/matrix/matrix_math_functions_abstract.h new file mode 100644 index 000000000..09210270d --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_math_functions_abstract.h @@ -0,0 +1,595 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_MATH_FUNCTIONS_ABSTRACT_ +#ifdef DLIB_MATRIx_MATH_FUNCTIONS_ABSTRACT_ + +#include "matrix_abstract.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Exponential Functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + const matrix_exp exp ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::exp(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp log10 ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::log10(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp log ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::log(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp sqrt ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == sqrt(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename T> + const matrix_exp pow ( + const matrix_exp& m, + const T& e + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == pow(m(r,c),e) + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename T> + const matrix_exp pow ( + const T& b, + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == pow(b, m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp squared ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == m(r,c)*m(r,c) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp cubed ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == m(r,c)*m(r,c)*m(r,c) + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Miscellaneous +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + const matrix_exp sign ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix that tells the sign of each element in m. In particular: + returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (m(r,c) >= 0) then + - R(r,c) == +1 + - else + - R(r,c) == -1 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp sigmoid ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == 1/(1 + exp(-m(r,c))) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp abs ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - if (m contains std::complex<T> objects) then + - R::type == T + - else + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::abs(m(r,c)) + (note that if m is complex then std::abs(val) performs sqrt(std::norm(val)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp reciprocal ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, long double, std::complex<float>, + std::complex<double>, or std::complex<long double> + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (m(r,c) != 0) then + - R(r,c) == 1.0/m(r,c) + - else + - R(r,c) == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp reciprocal_max ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (m(r,c) != 0) then + - R(r,c) == 1.0/m(r,c) + - else + - R(r,c) == std::numeric_limits<R::type>::max() + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp normalize ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - if (sqrt(sum(squared(m))) != 0) then + - returns m/sqrt(sum(squared(m))) + - else + - returns m + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Rounding numbers one way or another +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + const matrix_exp round ( + const matrix_exp& m + ); + /*! + requires + - is_built_in_scalar_type<matrix_exp::type>::value == true + (i.e. m must contain a type like int, float, double, long, etc.) + ensures + - if (m contains integers) then + - returns m unmodified + - else + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == m(r,c) rounded to the nearest integral value + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp ceil ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::ceil(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp floor ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::floor(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp round_zeros ( + const matrix_exp& m + ); + /*! + requires + - is_built_in_scalar_type<matrix_exp::type>::value == true + (i.e. m must contain a type like int, float, double, long, etc.) + ensures + - if (m contains integers) then + - returns m unmodified + - else + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - let eps == 10*std::numeric_limits<matrix_exp::type>::epsilon() + - for all valid r and c: + - if (abs(m(r,c)) >= eps) then + - R(r,c) == m(r,c) + - else + - R(r,c) == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp round_zeros ( + const matrix_exp& m, + matrix_exp::type eps + ); + /*! + requires + - is_built_in_scalar_type<matrix_exp::type>::value == true + (i.e. m must contain a type like int, float, double, long, etc.) + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (abs(m(r,c)) >= eps) then + - R(r,c) == m(r,c) + - else + - R(r,c) == 0 + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Complex number utility functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + const matrix_exp conj ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == std::complex<T> + ensures + - returns a matrix R such that: + - R::type == std::complex<T> + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::conj(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp norm ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == std::complex<T> + ensures + - returns a matrix R such that: + - R::type == T + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::norm(m(r,c)) + (note that std::norm(val) == val.real()*val.real() + val.imag()*val.imag()) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp imag ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == std::complex<T> + ensures + - returns a matrix R such that: + - R::type == T + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::imag(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp real ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == std::complex<T> + ensures + - returns a matrix R such that: + - R::type == T + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::real(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp complex_matrix ( + const matrix_exp& real_part + ); + /*! + ensures + - returns a matrix R such that: + - R::type == std::complex<T> where T is whatever type real_part used. + - R has the same dimensions as real_part. + - for all valid r and c: + R(r,c) == std::complex(real_part(r,c), 0) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp complex_matrix ( + const matrix_exp& real_part, + const matrix_exp& imag_part + ); + /*! + requires + - real_part.nr() == imag_part.nr() + - real_part.nc() == imag_part.nc() + - real_part and imag_part both contain the same type of element + ensures + - returns a matrix R such that: + - R::type == std::complex<T> where T is whatever type real_part and imag_part used. + - R has the same dimensions as real_part and imag_part + - for all valid r and c: + R(r,c) == std::complex(real_part(r,c),imag_part(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Trigonometric Functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + const matrix_exp sin ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::sin(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp cos ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::cos(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp tan ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::tan(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp asin ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::asin(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp acos ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::acos(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp atan ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::atan(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp sinh ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::sinh(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp cosh ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::cosh(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp tanh ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == float, double, or long double + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R(r,c) == std::tanh(m(r,c)) + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_MATH_FUNCTIONS_ABSTRACT_ + diff --git a/ml/dlib/dlib/matrix/matrix_op.h b/ml/dlib/dlib/matrix/matrix_op.h new file mode 100644 index 000000000..524a775eb --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_op.h @@ -0,0 +1,479 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_OP_H_ +#define DLIB_MATRIx_OP_H_ + +#include "matrix_exp.h" + + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + template <typename OP > + class matrix_op; + + template < typename OP > + struct matrix_traits<matrix_op<OP> > + { + typedef typename OP::type type; + typedef typename OP::const_ret_type const_ret_type; + typedef typename OP::mem_manager_type mem_manager_type; + typedef typename OP::layout_type layout_type; + const static long NR = OP::NR; + const static long NC = OP::NC; + const static long cost = OP::cost; + }; + + template < + typename OP + > + class matrix_op : public matrix_exp<matrix_op<OP> > + { + /*! + WHAT THIS OBJECT REPRESENTS + The matrix_op is simply a tool for reducing the amount of boilerplate + you need to write when creating matrix expressions. + !*/ + + public: + typedef typename matrix_traits<matrix_op>::type type; + typedef typename matrix_traits<matrix_op>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_op>::mem_manager_type mem_manager_type; + typedef typename matrix_traits<matrix_op>::layout_type layout_type; + const static long NR = matrix_traits<matrix_op>::NR; + const static long NC = matrix_traits<matrix_op>::NC; + const static long cost = matrix_traits<matrix_op>::cost; + + private: + // 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 object. + template <typename T1> + matrix_op (T1); + public: + + matrix_op ( + const OP& op_ + ) : + op(op_) + {} + + const_ret_type operator() ( + long r, + long c + ) const { return op.apply(r,c); } + + const_ret_type operator() ( long i ) const + { return matrix_exp<matrix_op>::operator()(i); } + + template <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const { return op.aliases(item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& item + ) const { return op.destructively_aliases(item); } + + long nr ( + ) const { return op.nr(); } + + long nc ( + ) const { return op.nc(); } + + + const OP op; + }; + +// ---------------------------------------------------------------------------------------- + + template <typename OP > + class matrix_diag_op; + + template < typename OP > + struct matrix_traits<matrix_diag_op<OP> > + { + typedef typename OP::type type; + typedef typename OP::const_ret_type const_ret_type; + typedef typename OP::mem_manager_type mem_manager_type; + typedef typename OP::layout_type layout_type; + const static long NR = OP::NR; + const static long NC = OP::NC; + const static long cost = OP::cost; + }; + + template < + typename OP + > + class matrix_diag_op : public matrix_diag_exp<matrix_diag_op<OP> > + { + /*! + WHAT THIS OBJECT REPRESENTS + The matrix_diag_op is simply a tool for reducing the amount of boilerplate + you need to write when creating matrix expressions. + !*/ + + public: + typedef typename matrix_traits<matrix_diag_op>::type type; + typedef typename matrix_traits<matrix_diag_op>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_diag_op>::mem_manager_type mem_manager_type; + typedef typename matrix_traits<matrix_diag_op>::layout_type layout_type; + const static long NR = matrix_traits<matrix_diag_op>::NR; + const static long NC = matrix_traits<matrix_diag_op>::NC; + const static long cost = matrix_traits<matrix_diag_op>::cost; + + private: + // 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 object. + template <typename T1> + matrix_diag_op (T1); + public: + + matrix_diag_op ( + const OP& op_ + ) : + op(op_) + {} + + const_ret_type operator() ( + long r, + long c + ) const { return op.apply(r,c); } + + const_ret_type operator() ( long i ) const + { return matrix_exp<matrix_diag_op>::operator()(i); } + + template <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const { return op.aliases(item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& item + ) const { return op.destructively_aliases(item); } + + long nr ( + ) const { return op.nr(); } + + long nc ( + ) const { return op.nc(); } + + + const OP op; + }; + +// ---------------------------------------------------------------------------------------- + + struct does_not_alias + { + /*! + This is a partial implementation of a matrix operator that never aliases + another expression. + !*/ + + template <typename U> bool aliases ( const U& ) const { return false; } + template <typename U> bool destructively_aliases ( const U& ) const { return false; } + }; + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct basic_op_m + { + /*! + This is a partial implementation of a matrix operator that preserves + the dimensions of its argument and doesn't have destructive aliasing. + !*/ + + private: + // 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 object. + template <typename T1> + basic_op_m (T1); + public: + + basic_op_m( + const M& m_ + ) : m(m_){} + + const M& m; + + const static long NR = M::NR; + const static long NC = M::NC; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + long nr () const { return m.nr(); } + long nc () const { return m.nc(); } + + 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); } + + }; + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct basic_op_mm + { + /*! + This is a partial implementation of a matrix operator that preserves + the dimensions of its arguments and doesn't have destructive aliasing. + !*/ + + private: + // 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 object. + template <typename T1, typename T2> + basic_op_mm (T1, T2); + public: + + basic_op_mm( + const M1& m1_, + const M2& m2_ + ) : m1(m1_), m2(m2_){} + + const M1& m1; + const M2& m2; + + const static long NR = M1::NR; + const static long NC = M1::NC; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + long nr () const { return m1.nr(); } + long nc () const { return m1.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.destructively_aliases(item) || m2.destructively_aliases(item); } + + }; + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, typename M3> + struct basic_op_mmm + { + /*! + This is a partial implementation of a matrix operator that preserves + the dimensions of its arguments and doesn't have destructive aliasing. + !*/ + + private: + // 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 object. + template <typename T1, typename T2, typename T3> + basic_op_mmm (T1, T2, T3); + public: + + basic_op_mmm( + const M1& m1_, + const M2& m2_, + const M3& m3_ + ) : m1(m1_), m2(m2_), m3(m3_){} + + const M1& m1; + const M2& m2; + const M3& m3; + + const static long NR = M1::NR; + const static long NC = M1::NC; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + long nr () const { return m1.nr(); } + long nc () const { return m1.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item) || m3.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.destructively_aliases(item) || m2.destructively_aliases(item) || + m3.destructively_aliases(item);} + + }; + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, typename M3, typename M4> + struct basic_op_mmmm + { + /*! + This is a partial implementation of a matrix operator that preserves + the dimensions of its arguments and doesn't have destructive aliasing. + !*/ + + private: + // 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 object. + template <typename T1, typename T2, typename T3, typename T4> + basic_op_mmmm (T1, T2, T3, T4); + public: + + basic_op_mmmm( + const M1& m1_, + const M2& m2_, + const M3& m3_, + const M4& m4_ + ) : m1(m1_), m2(m2_), m3(m3_), m4(m4_){} + + const M1& m1; + const M2& m2; + const M3& m3; + const M4& m4; + + const static long NR = M1::NR; + const static long NC = M1::NC; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + long nr () const { return m1.nr(); } + long nc () const { return m1.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item) || m3.aliases(item) || m4.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.destructively_aliases(item) || m2.destructively_aliases(item) || + m3.destructively_aliases(item) || m4.destructively_aliases(item);} + + }; + +// ---------------------------------------------------------------------------------------- + +#define DLIB_DEFINE_OP_M(op_name, function, extra_cost) \ + template <typename M> \ + struct op_name \ + { \ + op_name( \ + const M& m_ \ + ) : m(m_){} \ + \ + const M& m; \ + \ + const static long cost = M::cost+(extra_cost); \ + const static long NR = M::NR; \ + const static long NC = M::NC; \ + typedef typename M::type type; \ + typedef const typename M::type const_ret_type; \ + typedef typename M::mem_manager_type mem_manager_type; \ + typedef typename M::layout_type layout_type; \ + \ + const_ret_type apply (long r, long c) const { return function(m(r,c)); } \ + \ + long nr () const { return m.nr(); } \ + long nc () const { return m.nc(); } \ + \ + 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); } \ + \ + } + +#define DLIB_DEFINE_FUNCTION_M(op_name, name, function, extra_cost) \ + DLIB_DEFINE_OP_M(op_name, function, extra_cost); \ + template < typename M > \ + const matrix_op<op_name<M> > name ( const matrix_exp<M>& m) \ + { \ + typedef op_name<M> op; \ + return matrix_op<op>(op(m.ref())); \ + } + +// ---------------------------------------------------------------------------------------- + +#define DLIB_DEFINE_OP_MS(op_name, function, extra_cost) \ + template <typename M, typename S> \ + struct op_name \ + { \ + op_name( \ + const M& m_, \ + const S& s_ \ + ) : m(m_), s(s_){} \ + \ + const M& m; \ + const S s; \ + \ + const static long cost = M::cost+(extra_cost); \ + const static long NR = M::NR; \ + const static long NC = M::NC; \ + typedef typename M::type type; \ + typedef const typename M::type const_ret_type; \ + typedef typename M::mem_manager_type mem_manager_type; \ + typedef typename M::layout_type layout_type; \ + \ + const_ret_type apply (long r, long c) const { return function(m(r,c), s); } \ + \ + long nr () const { return m.nr(); } \ + long nc () const { return m.nc(); } \ + \ + 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); } \ + \ + } + +#define DLIB_DEFINE_FUNCTION_MS(op_name, name, function, extra_cost) \ + DLIB_DEFINE_OP_MS(op_name, function, extra_cost); \ + template < typename M, typename S > \ + const matrix_op<op_name<M, S> > name ( const matrix_exp<M>& m, const S& s) \ + { \ + typedef op_name<M, S> op; \ + return matrix_op<op>(op(m.ref(), s)); \ + } + +// ---------------------------------------------------------------------------------------- + +#define DLIB_DEFINE_OP_SM(op_name, function, extra_cost) \ + template <typename S, typename M> \ + struct op_name \ + { \ + op_name( \ + const S& s_, \ + const M& m_ \ + ) : m(m_), s(s_){} \ + \ + const M& m; \ + const S s; \ + \ + const static long cost = M::cost+(extra_cost); \ + const static long NR = M::NR; \ + const static long NC = M::NC; \ + typedef typename M::type type; \ + typedef const typename M::type const_ret_type; \ + typedef typename M::mem_manager_type mem_manager_type; \ + typedef typename M::layout_type layout_type; \ + \ + const_ret_type apply (long r, long c) const { return function(s, m(r,c)); } \ + \ + long nr () const { return m.nr(); } \ + long nc () const { return m.nc(); } \ + \ + 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); } \ + \ + } + +#define DLIB_DEFINE_FUNCTION_SM(op_name, name, function, extra_cost) \ + DLIB_DEFINE_OP_SM(op_name, function, extra_cost); \ + template < typename S, typename M > \ + const matrix_op<op_name<S, M> > name (const S& s, const matrix_exp<M>& m) \ + { \ + typedef op_name<S, M> op; \ + return matrix_op<op>(op(s, m.ref())); \ + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_OP_H_ + diff --git a/ml/dlib/dlib/matrix/matrix_qr.h b/ml/dlib/dlib/matrix/matrix_qr.h new file mode 100644 index 000000000..086d481f1 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_qr.h @@ -0,0 +1,466 @@ +// Copyright (C) 2009 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +// This code was adapted from code from the JAMA part of NIST's TNT library. +// See: http://math.nist.gov/tnt/ +#ifndef DLIB_MATRIX_QR_DECOMPOSITION_H +#define DLIB_MATRIX_QR_DECOMPOSITION_H + +#include "matrix.h" +#include "matrix_utilities.h" +#include "matrix_subexp.h" + +#ifdef DLIB_USE_LAPACK +#include "lapack/geqrf.h" +#include "lapack/ormqr.h" +#endif + +#include "matrix_trsm.h" + +namespace dlib +{ + + template < + typename matrix_exp_type + > + class qr_decomposition + { + + public: + + const static long NR = matrix_exp_type::NR; + const static long NC = matrix_exp_type::NC; + typedef typename matrix_exp_type::type type; + typedef typename matrix_exp_type::mem_manager_type mem_manager_type; + typedef typename matrix_exp_type::layout_type layout_type; + + typedef matrix<type,0,0,mem_manager_type,layout_type> matrix_type; + + // You have supplied an invalid type of matrix_exp_type. You have + // to use this object with matrices that contain float or double type data. + COMPILE_TIME_ASSERT((is_same_type<float, type>::value || + is_same_type<double, type>::value )); + + + + template <typename EXP> + qr_decomposition( + const matrix_exp<EXP>& A + ); + + bool is_full_rank( + ) const; + + long nr( + ) const; + + long nc( + ) const; + + const matrix_type get_r ( + ) const; + + const matrix_type get_q ( + ) const; + + template <typename T, long R, long C, typename MM, typename L> + void get_q ( + matrix<T,R,C,MM,L>& Q + ) const; + + template <typename EXP> + const matrix_type solve ( + const matrix_exp<EXP>& B + ) const; + + private: + +#ifndef DLIB_USE_LAPACK + template <typename EXP> + const matrix_type solve_mat ( + const matrix_exp<EXP>& B + ) const; + + template <typename EXP> + const matrix_type solve_vect ( + const matrix_exp<EXP>& B + ) const; +#endif + + + /** Array for internal storage of decomposition. + @serial internal array storage. + */ + matrix<type,0,0,mem_manager_type,column_major_layout> QR_; + + /** Row and column dimensions. + @serial column dimension. + @serial row dimension. + */ + long m, n; + + /** Array for internal storage of diagonal of R. + @serial diagonal of R. + */ + typedef matrix<type,0,1,mem_manager_type,column_major_layout> column_vector_type; + column_vector_type tau; + column_vector_type Rdiag; + + + }; + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Member functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + qr_decomposition<matrix_exp_type>:: + qr_decomposition( + const matrix_exp<EXP>& A + ) + { + COMPILE_TIME_ASSERT((is_same_type<type, typename EXP::type>::value)); + + // make sure requires clause is not broken + DLIB_ASSERT(A.nr() >= A.nc() && A.size() > 0, + "\tqr_decomposition::qr_decomposition(A)" + << "\n\tInvalid inputs were given to this function" + << "\n\tA.nr(): " << A.nr() + << "\n\tA.nc(): " << A.nc() + << "\n\tA.size(): " << A.size() + << "\n\tthis: " << this + ); + + + QR_ = A; + m = A.nr(); + n = A.nc(); + +#ifdef DLIB_USE_LAPACK + + lapack::geqrf(QR_, tau); + Rdiag = diag(QR_); + +#else + Rdiag.set_size(n); + long i=0, j=0, k=0; + + // Main loop. + for (k = 0; k < n; k++) + { + // Compute 2-norm of k-th column without under/overflow. + type nrm = 0; + for (i = k; i < m; i++) + { + nrm = hypot(nrm,QR_(i,k)); + } + + if (nrm != 0.0) + { + // Form k-th Householder vector. + if (QR_(k,k) < 0) + { + nrm = -nrm; + } + for (i = k; i < m; i++) + { + QR_(i,k) /= nrm; + } + QR_(k,k) += 1.0; + + // Apply transformation to remaining columns. + for (j = k+1; j < n; j++) + { + type s = 0.0; + for (i = k; i < m; i++) + { + s += QR_(i,k)*QR_(i,j); + } + s = -s/QR_(k,k); + for (i = k; i < m; i++) + { + QR_(i,j) += s*QR_(i,k); + } + } + } + Rdiag(k) = -nrm; + } +#endif + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + long qr_decomposition<matrix_exp_type>:: + nr ( + ) const + { + return m; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + long qr_decomposition<matrix_exp_type>:: + nc ( + ) const + { + return n; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + bool qr_decomposition<matrix_exp_type>:: + is_full_rank( + ) const + { + type eps = max(abs(Rdiag)); + if (eps != 0) + eps *= std::sqrt(std::numeric_limits<type>::epsilon())/100; + else + eps = 1; // there is no max so just use 1 + + // check if any of the elements of Rdiag are effectively 0 + return min(abs(Rdiag)) > eps; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename qr_decomposition<matrix_exp_type>::matrix_type qr_decomposition<matrix_exp_type>:: + get_r( + ) const + { + matrix_type R(n,n); + for (long i = 0; i < n; i++) + { + for (long j = 0; j < n; j++) + { + if (i < j) + { + R(i,j) = QR_(i,j); + } + else if (i == j) + { + R(i,j) = Rdiag(i); + } + else + { + R(i,j) = 0.0; + } + } + } + return R; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + const typename qr_decomposition<matrix_exp_type>::matrix_type qr_decomposition<matrix_exp_type>:: + get_q( + ) const + { + matrix_type Q; + get_q(Q); + return Q; + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename T, long R, long C, typename MM, typename L> + void qr_decomposition<matrix_exp_type>:: + get_q( + matrix<T,R,C,MM,L>& X + ) const + { +#ifdef DLIB_USE_LAPACK + // Take only the first n columns of an identity matrix. This way + // X ends up being an m by n matrix. + X = colm(identity_matrix<type>(m), range(0,n-1)); + + // Compute Y = Q*X + lapack::ormqr('L','N', QR_, tau, X); + +#else + long i=0, j=0, k=0; + + X.set_size(m,n); + for (k = n-1; k >= 0; k--) + { + for (i = 0; i < m; i++) + { + X(i,k) = 0.0; + } + X(k,k) = 1.0; + for (j = k; j < n; j++) + { + if (QR_(k,k) != 0) + { + type s = 0.0; + for (i = k; i < m; i++) + { + s += QR_(i,k)*X(i,j); + } + s = -s/QR_(k,k); + for (i = k; i < m; i++) + { + X(i,j) += s*QR_(i,k); + } + } + } + } +#endif + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + const typename qr_decomposition<matrix_exp_type>::matrix_type qr_decomposition<matrix_exp_type>:: + solve( + const matrix_exp<EXP>& B + ) const + { + COMPILE_TIME_ASSERT((is_same_type<type, typename EXP::type>::value)); + + // make sure requires clause is not broken + DLIB_ASSERT(B.nr() == nr(), + "\tconst matrix_type qr_decomposition::solve(B)" + << "\n\tInvalid inputs were given to this function" + << "\n\tB.nr(): " << B.nr() + << "\n\tnr(): " << nr() + << "\n\tthis: " << this + ); + +#ifdef DLIB_USE_LAPACK + + using namespace blas_bindings; + matrix<type,0,0,mem_manager_type,column_major_layout> X(B); + // Compute Y = transpose(Q)*B + lapack::ormqr('L','T',QR_, tau, X); + // Solve R*X = Y; + triangular_solver(CblasLeft, CblasUpper, CblasNoTrans, CblasNonUnit, QR_, X, n); + + /* return n x nx portion of X */ + return subm(X,0,0,n,B.nc()); + +#else + // just call the right version of the solve function + if (B.nc() == 1) + return solve_vect(B); + else + return solve_mat(B); +#endif + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Private member functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +#ifndef DLIB_USE_LAPACK + + template <typename matrix_exp_type> + template <typename EXP> + const typename qr_decomposition<matrix_exp_type>::matrix_type qr_decomposition<matrix_exp_type>:: + solve_vect( + const matrix_exp<EXP>& B + ) const + { + + column_vector_type x(B); + + // Compute Y = transpose(Q)*B + for (long k = 0; k < n; k++) + { + type s = 0.0; + for (long i = k; i < m; i++) + { + s += QR_(i,k)*x(i); + } + s = -s/QR_(k,k); + for (long i = k; i < m; i++) + { + x(i) += s*QR_(i,k); + } + } + // Solve R*X = Y; + for (long k = n-1; k >= 0; k--) + { + x(k) /= Rdiag(k); + for (long i = 0; i < k; i++) + { + x(i) -= x(k)*QR_(i,k); + } + } + + + /* return n x 1 portion of x */ + return colm(x,0,n); + } + +// ---------------------------------------------------------------------------------------- + + template <typename matrix_exp_type> + template <typename EXP> + const typename qr_decomposition<matrix_exp_type>::matrix_type qr_decomposition<matrix_exp_type>:: + solve_mat( + const matrix_exp<EXP>& B + ) const + { + const long nx = B.nc(); + matrix_type X(B); + long i=0, j=0, k=0; + + // Compute Y = transpose(Q)*B + for (k = 0; k < n; k++) + { + for (j = 0; j < nx; j++) + { + type s = 0.0; + for (i = k; i < m; i++) + { + s += QR_(i,k)*X(i,j); + } + s = -s/QR_(k,k); + for (i = k; i < m; i++) + { + X(i,j) += s*QR_(i,k); + } + } + } + // Solve R*X = Y; + for (k = n-1; k >= 0; k--) + { + for (j = 0; j < nx; j++) + { + X(k,j) /= Rdiag(k); + } + for (i = 0; i < k; i++) + { + for (j = 0; j < nx; j++) + { + X(i,j) -= X(k,j)*QR_(i,k); + } + } + } + + /* return n x nx portion of X */ + return subm(X,0,0,n,nx); + } + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_USE_LAPACK not defined + +} + +#endif // DLIB_MATRIX_QR_DECOMPOSITION_H + + + diff --git a/ml/dlib/dlib/matrix/matrix_read_from_istream.h b/ml/dlib/dlib/matrix/matrix_read_from_istream.h new file mode 100644 index 000000000..3aced3584 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_read_from_istream.h @@ -0,0 +1,108 @@ +// Copyright (C) 2013 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_READ_FROM_ISTREAM_H_h_ +#define DLIB_MATRIx_READ_FROM_ISTREAM_H_h_ + +#include "matrix.h" +#include <vector> +#include <iostream> + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + inline bool next_is_whitespace ( + std::istream& in + ) + { + return in.peek() == '\n' || + in.peek() == ' ' || + in.peek() == ',' || + in.peek() == '\t' || + in.peek() == '\r'; + } + } + + template <typename T, long NR, long NC, typename MM, typename L> + std::istream& operator>> ( + std::istream& in, + matrix<T,NR,NC,MM,L>& m + ) + { + using namespace dlib::impl; + long num_rows = 0; + std::vector<T> buf; + buf.reserve(100); + + // eat any leading whitespace + while (next_is_whitespace(in)) + in.get(); + + bool at_start_of_line = true; + bool stop = false; + while(!stop && in.peek() != EOF) + { + T temp; + in >> temp; + if (!in) + return in; + + buf.push_back(temp); + if (at_start_of_line) + { + at_start_of_line = false; + ++num_rows; + } + + // Eat next block of whitespace but also note if we hit the start of the next + // line. + while (next_is_whitespace(in)) + { + if (at_start_of_line && in.peek() == '\n') + { + stop = true; + break; + } + + if (in.get() == '\n') + at_start_of_line = true; + } + } + + // It's an error for there to not be any matrix data in the input stream + if (num_rows == 0) + { + in.clear(in.rdstate() | std::ios::failbit); + return in; + } + + const long num_cols = buf.size()/num_rows; + // It's also an error if the sizes don't make sense. + if (num_rows*num_cols != (long)buf.size() || + (NR != 0 && NR != num_rows) || + (NC != 0 && NC != num_cols)) + { + in.clear(in.rdstate() | std::ios::failbit); + return in; + } + + + m = reshape(mat(buf),num_rows, buf.size()/num_rows); + + if (in.eof()) + { + // Clear the eof and fail bits since this is caused by peeking at the EOF. + // But in the current case, we have successfully read the matrix. + in.clear(in.rdstate() & (~(std::ios::eofbit | std::ios::failbit))); + } + return in; + } +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_MATRIx_READ_FROM_ISTREAM_H_h_ + diff --git a/ml/dlib/dlib/matrix/matrix_subexp.h b/ml/dlib/dlib/matrix/matrix_subexp.h new file mode 100644 index 000000000..668e57496 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_subexp.h @@ -0,0 +1,1566 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_SUBEXP_ +#define DLIB_MATRIx_SUBEXP_ + +#include "matrix_subexp_abstract.h" +#include "matrix_op.h" +#include "matrix.h" +#include "../geometry/rectangle.h" +#include "matrix_expressions.h" +#include "matrix_mat.h" + + + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <long start, long inc, long end> + const matrix_range_static_exp<start,inc,end> range ( + ) + { + COMPILE_TIME_ASSERT(inc > 0); + return matrix_range_static_exp<start,inc,end>(); + } + + template <long start, long end> + const matrix_range_static_exp<start,1,end> range ( + ) + { + return matrix_range_static_exp<start,1,end>(); + } + + inline const matrix_range_exp<long> range ( + long start, + long end + ) + { + return matrix_range_exp<long>(start,end); + } + + inline const matrix_range_exp<long> range ( + long start, + long inc, + long end + ) + { + DLIB_ASSERT(inc > 0, + "\tconst matrix_exp range(start, inc, end)" + << "\n\tInvalid inputs to this function" + << "\n\tstart: " << start + << "\n\tinc: " << inc + << "\n\tend: " << end + ); + + return matrix_range_exp<long>(start,inc,end); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_subm + { + op_subm ( + const M& m_x, + const long& r_x, + const long& c_x, + const long& nr_x, + const long& nc_x + ) : m(m_x), r_(r_x), c_(c_x), nr_(nr_x), nc_(nc_x) { } + + const M& m; + const long r_; + const long c_; + const long nr_; + const long nc_; + + const static long cost = M::cost+1; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const static long NR = 0; + const static long NC = 0; + + const_ret_type apply ( long r, long c) const { return m(r+r_,c+c_); } + + long nr () const { return nr_; } + long nc () const { return nc_; } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_subm<EXP> > subm ( + const matrix_exp<EXP>& m, + long r, + long c, + long nr, + long nc + ) + { + DLIB_ASSERT(r >= 0 && c >= 0 && nr >= 0 && nc >= 0 && r+nr <= m.nr() && c+nc <= m.nc(), + "\tconst matrix_exp subm(const matrix_exp& m, r, c, nr, nc)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tr: " << r + << "\n\tc: " << c + << "\n\tnr: " << nr + << "\n\tnc: " << nc + ); + + typedef op_subm<EXP> op; + return matrix_op<op>(op(m.ref(),r,c,nr,nc)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix_op<op_subm<EXP> > subm_clipped ( + const matrix_exp<EXP>& m, + long r, + long c, + long nr, + long nc + ) + { + rectangle box(c,r,c+nc-1,r+nr-1); + box = box.intersect(get_rect(m)); + typedef op_subm<EXP> op; + return matrix_op<op>(op(m.ref(),box.top(),box.left(),box.height(),box.width())); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix_op<op_subm<EXP> > subm ( + const matrix_exp<EXP>& m, + const rectangle& rect + ) + { + DLIB_ASSERT(get_rect(m).contains(rect) == true, + "\tconst matrix_exp subm(const matrix_exp& m, const rectangle& rect)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\trect.left(): " << rect.left() + << "\n\trect.top(): " << rect.top() + << "\n\trect.right(): " << rect.right() + << "\n\trect.bottom(): " << rect.bottom() + ); + + typedef op_subm<EXP> op; + return matrix_op<op>(op(m.ref(),rect.top(),rect.left(),rect.height(),rect.width())); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix_op<op_subm<EXP> > subm_clipped ( + const matrix_exp<EXP>& m, + rectangle rect + ) + { + rect = rect.intersect(get_rect(m)); + + typedef op_subm<EXP> op; + return matrix_op<op>(op(m.ref(),rect.top(),rect.left(),rect.height(),rect.width())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, typename M3> + struct op_subm_range + { + op_subm_range( const M1& m1_, const M2& rows_, const M3& cols_) : + m1(m1_), rows(rows_), cols(cols_) {} + const M1& m1; + const M2& rows; + const M3& cols; + + const static long cost = M1::cost+M2::cost+M3::cost; + typedef typename M1::type type; + typedef typename M1::const_ret_type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + const static long NR = M2::NC*M2::NR; + const static long NC = M3::NC*M3::NR; + + const_ret_type apply ( long r, long c) const { return m1(rows(r),cols(c)); } + + long nr () const { return rows.size(); } + long nc () const { return cols.size(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || rows.aliases(item) || cols.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || rows.aliases(item) || cols.aliases(item); } + }; + + template < + typename EXP, + typename EXPr, + typename EXPc + > + const matrix_op<op_subm_range<EXP,EXPr,EXPc> > subm ( + const matrix_exp<EXP>& m, + const matrix_exp<EXPr>& rows, + const matrix_exp<EXPc>& cols + ) + { + // the rows and cols matrices must contain integer elements + COMPILE_TIME_ASSERT(std::numeric_limits<typename EXPr::type>::is_integer); + COMPILE_TIME_ASSERT(std::numeric_limits<typename EXPc::type>::is_integer); + + DLIB_ASSERT(0 <= min(rows) && max(rows) < m.nr() && 0 <= min(cols) && max(cols) < m.nc() && + (rows.nr() == 1 || rows.nc() == 1) && (cols.nr() == 1 || cols.nc() == 1), + "\tconst matrix_exp subm(const matrix_exp& m, const matrix_exp& rows, const matrix_exp& cols)" + << "\n\tYou have given invalid arguments to this function" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tmin(rows): " << min(rows) + << "\n\tmax(rows): " << max(rows) + << "\n\tmin(cols): " << min(cols) + << "\n\tmax(cols): " << max(cols) + << "\n\trows.nr(): " << rows.nr() + << "\n\trows.nc(): " << rows.nc() + << "\n\tcols.nr(): " << cols.nr() + << "\n\tcols.nc(): " << cols.nc() + ); + + typedef op_subm_range<EXP,EXPr,EXPc> op; + return matrix_op<op>(op(m.ref(),rows.ref(),cols.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_rowm + { + op_rowm(const M& m_, const long& row_) : m(m_), row(row_) {} + const M& m; + const long row; + + const static long cost = M::cost; + const static long NR = 1; + const static long NC = M::NC; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long, long c) const { return m(row,c); } + + long nr () const { return 1; } + long nc () const { return m.nc(); } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_rowm<EXP> > rowm ( + const matrix_exp<EXP>& m, + long row + ) + { + DLIB_ASSERT(row >= 0 && row < m.nr(), + "\tconst matrix_exp rowm(const matrix_exp& m, row)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\trow: " << row + ); + + typedef op_rowm<EXP> op; + return matrix_op<op>(op(m.ref(),row)); + } + + template <typename EXP> + struct rowm_exp + { + typedef matrix_op<op_rowm<EXP> > type; + }; + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_rowm2 + { + op_rowm2(const M& m_, const long& row_, const long& len) : m(m_), row(row_), length(len) {} + const M& m; + const long row; + const long length; + + const static long cost = M::cost; + const static long NR = 1; + const static long NC = 0; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long , long c) const { return m(row,c); } + + long nr () const { return 1; } + long nc () const { return length; } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_rowm2<EXP> > rowm ( + const matrix_exp<EXP>& m, + long row, + long length + ) + { + DLIB_ASSERT(row >= 0 && row < m.nr() && + length >= 0 && length <= m.nc(), + "\tconst matrix_exp rowm(const matrix_exp& m, row, length)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\trow: " << row + << "\n\tlength: " << length + ); + + typedef op_rowm2<EXP> op; + return matrix_op<op>(op(m.ref(), row, length)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_rowm_range + { + op_rowm_range( const M1& m1_, const M2& rows_) : m1(m1_), rows(rows_) {} + const M1& m1; + const M2& rows; + + const static long cost = M1::cost+M2::cost; + typedef typename M1::type type; + typedef typename M1::const_ret_type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + const static long NR = M2::NC*M2::NR; + const static long NC = M1::NC; + + const_ret_type apply ( long r, long c) const { return m1(rows(r),c); } + + long nr () const { return rows.size(); } + long nc () const { return m1.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || rows.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || rows.aliases(item); } + }; + + template < + typename EXP1, + typename EXP2 + > + const matrix_op<op_rowm_range<EXP1,EXP2> > rowm ( + const matrix_exp<EXP1>& m, + const matrix_exp<EXP2>& rows + ) + { + // the rows matrix must contain integer elements + COMPILE_TIME_ASSERT(std::numeric_limits<typename EXP2::type>::is_integer); + + DLIB_ASSERT(0 <= min(rows) && max(rows) < m.nr() && (rows.nr() == 1 || rows.nc() == 1), + "\tconst matrix_exp rowm(const matrix_exp& m, const matrix_exp& rows)" + << "\n\tYou have given invalid arguments to this function" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tmin(rows): " << min(rows) + << "\n\tmax(rows): " << max(rows) + << "\n\trows.nr(): " << rows.nr() + << "\n\trows.nc(): " << rows.nc() + ); + + typedef op_rowm_range<EXP1,EXP2> op; + return matrix_op<op>(op(m.ref(),rows.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_colm + { + op_colm(const M& m_, const long& col_) : m(m_), col(col_) {} + const M& m; + const long col; + + const static long cost = M::cost; + const static long NR = M::NR; + const static long NC = 1; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long) const { return m(r,col); } + + long nr () const { return m.nr(); } + long nc () const { return 1; } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_colm<EXP> > colm ( + const matrix_exp<EXP>& m, + long col + ) + { + DLIB_ASSERT(col >= 0 && col < m.nc(), + "\tconst matrix_exp colm(const matrix_exp& m, row)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tcol: " << col + ); + + typedef op_colm<EXP> op; + return matrix_op<op>(op(m.ref(),col)); + } + + template <typename EXP> + struct colm_exp + { + typedef matrix_op<op_colm<EXP> > type; + }; + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_colm2 + { + op_colm2(const M& m_, const long& col_, const long& len) : m(m_), col(col_), length(len) {} + const M& m; + const long col; + const long length; + + const static long cost = M::cost; + const static long NR = 0; + const static long NC = 1; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long ) const { return m(r,col); } + + long nr () const { return length; } + long nc () const { return 1; } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_colm2<EXP> > colm ( + const matrix_exp<EXP>& m, + long col, + long length + ) + { + DLIB_ASSERT(col >= 0 && col < m.nc() && + length >= 0 && length <= m.nr(), + "\tconst matrix_exp colm(const matrix_exp& m, col, length)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tcol: " << col + << "\n\tlength: " << length + ); + + typedef op_colm2<EXP> op; + return matrix_op<op>(op(m.ref(),col, length)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_colm_range + { + op_colm_range( const M1& m1_, const M2& cols_) : m1(m1_), cols(cols_) {} + const M1& m1; + const M2& cols; + + typedef typename M1::type type; + typedef typename M1::const_ret_type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + const static long NR = M1::NR; + const static long NC = M2::NC*M2::NR; + const static long cost = M1::cost+M2::cost; + + const_ret_type apply (long r, long c) const { return m1(r,cols(c)); } + + long nr () const { return m1.nr(); } + long nc () const { return cols.size(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || cols.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || cols.aliases(item); } + }; + + template < + typename EXP1, + typename EXP2 + > + const matrix_op<op_colm_range<EXP1,EXP2> > colm ( + const matrix_exp<EXP1>& m, + const matrix_exp<EXP2>& cols + ) + { + // the rows matrix must contain integer elements + COMPILE_TIME_ASSERT(std::numeric_limits<typename EXP2::type>::is_integer); + + DLIB_ASSERT(0 <= min(cols) && max(cols) < m.nc() && (cols.nr() == 1 || cols.nc() == 1), + "\tconst matrix_exp colm(const matrix_exp& m, const matrix_exp& cols)" + << "\n\tYou have given invalid arguments to this function" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tmin(cols): " << min(cols) + << "\n\tmax(cols): " << max(cols) + << "\n\tcols.nr(): " << cols.nr() + << "\n\tcols.nc(): " << cols.nc() + ); + + typedef op_colm_range<EXP1,EXP2> op; + return matrix_op<op>(op(m.ref(),cols.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T> + class assignable_ptr_matrix + { + public: + typedef T type; + typedef row_major_layout layout_type; + typedef matrix<T,0,0,default_memory_manager,layout_type> matrix_type; + + assignable_ptr_matrix( + T* ptr_, + long nr_, + long nc_ + ) : ptr(ptr_), height(nr_), width(nc_){} + + T& operator() ( + long r, + long c + ) + { + return ptr[r*width + c]; + } + + const T& operator() ( + long r, + long c + ) const + { + return ptr[r*width + c]; + } + + long nr() const { return height; } + long nc() const { return width; } + + template <typename EXP> + assignable_ptr_matrix& operator= ( + const matrix_exp<EXP>& exp + ) + { + // You can only assign to a set_ptrm() expression with a source matrix that + // contains the same type of elements as the target (i.e. you can't mix double + // and float types). + COMPILE_TIME_ASSERT((is_same_type<T, typename EXP::type>::value == true)); + + DLIB_ASSERT( exp.nr() == height && exp.nc() == width, + "\tassignable_matrix_expression set_ptrm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\twidth (target matrix): " << width + << "\n\theight (target matrix): " << height + ); + + if (exp.destructively_aliases(mat(ptr,height,width)) == false) + { + matrix_assign(*this, exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to ptr to + // avoid aliasing issues during the copy + this->operator=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_ptr_matrix& operator+= ( + const matrix_exp<EXP>& exp + ) + { + // You can only assign to a set_ptrm() expression with a source matrix that + // contains the same type of elements as the target (i.e. you can't mix double + // and float types). + COMPILE_TIME_ASSERT((is_same_type<T, typename EXP::type>::value == true)); + + DLIB_ASSERT( exp.nr() == height && exp.nc() == width, + "\tassignable_matrix_expression set_ptrm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\twidth (target matrix): " << width + << "\n\theight (target matrix): " << height + ); + + if (exp.destructively_aliases(mat(ptr,height,width)) == false) + { + matrix_assign(*this, mat(ptr,height,width)+exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to ptr to + // avoid aliasing issues during the copy + this->operator+=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_ptr_matrix& operator-= ( + const matrix_exp<EXP>& exp + ) + { + // You can only assign to a set_ptrm() expression with a source matrix that + // contains the same type of elements as the target (i.e. you can't mix double + // and float types). + COMPILE_TIME_ASSERT((is_same_type<T, typename EXP::type>::value == true)); + + DLIB_ASSERT( exp.nr() == height && exp.nc() == width, + "\tassignable_matrix_expression set_ptrm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\twidth (target matrix): " << width + << "\n\theight (target matrix): " << height + ); + + if (exp.destructively_aliases(mat(ptr,height,width)) == false) + { + matrix_assign(*this, mat(ptr,height,width)-exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to ptr to + // avoid aliasing issues during the copy + this->operator-=(tmp(exp)); + } + + return *this; + } + + assignable_ptr_matrix& operator= ( + const T& value + ) + { + const long size = width*height; + for (long i = 0; i < size; ++i) + ptr[i] = value; + + return *this; + } + + assignable_ptr_matrix& operator+= ( + const T& value + ) + { + const long size = width*height; + for (long i = 0; i < size; ++i) + ptr[i] += value; + + return *this; + } + + assignable_ptr_matrix& operator-= ( + const T& value + ) + { + const long size = width*height; + for (long i = 0; i < size; ++i) + ptr[i] -= value; + + return *this; + } + + + T* ptr; + const long height; + const long width; + }; + + + template <typename T> + assignable_ptr_matrix<T> set_ptrm ( + T* ptr, + long nr, + long nc = 1 + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0, + "\t assignable_matrix_expression set_ptrm(T* ptr, long nr, long nc)" + << "\n\t The dimensions can't be negative." + << "\n\t nr: " << nr + << "\n\t nc: " << nc + ); + + + return assignable_ptr_matrix<T>(ptr,nr,nc); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T, long NR, long NC, typename mm, typename l> + class assignable_sub_matrix + { + public: + typedef T type; + typedef l layout_type; + typedef matrix<T,NR,NC,mm,l> matrix_type; + + assignable_sub_matrix( + matrix<T,NR,NC,mm,l>& m_, + long top_, + long left_, + long height_, + long width_ + ) : m(m_), left(left_), top(top_), width(width_), height(height_) {} + + T& operator() ( + long r, + long c + ) + { + return m(r+top,c+left); + } + + const T& operator() ( + long r, + long c + ) const + { + return m(r+top,c+left); + } + + long nr() const { return height; } + long nc() const { return width; } + + template <typename EXP> + assignable_sub_matrix& operator= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == height && exp.nc() == width, + "\tassignable_matrix_expression set_subm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\twidth (target matrix): " << width + << "\n\theight (target matrix): " << height + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_sub_matrix& operator+= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == height && exp.nc() == width, + "\tassignable_matrix_expression set_subm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\twidth (target matrix): " << width + << "\n\theight (target matrix): " << height + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, subm(m,top,left,height,width)+exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator+=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_sub_matrix& operator-= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == height && exp.nc() == width, + "\tassignable_matrix_expression set_subm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\twidth (target matrix): " << width + << "\n\theight (target matrix): " << height + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, subm(m,top,left,height,width)-exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator-=(tmp(exp)); + } + + return *this; + } + + assignable_sub_matrix& operator= ( + const T& value + ) + { + const long bottom = top+height-1; + const long right = left+width-1; + for (long r = top; r <= bottom; ++r) + { + for (long c = left; c <= right; ++c) + { + m(r,c) = value; + } + } + + return *this; + } + + assignable_sub_matrix& operator+= ( + const T& value + ) + { + const long bottom = top+height-1; + const long right = left+width-1; + for (long r = top; r <= bottom; ++r) + { + for (long c = left; c <= right; ++c) + { + m(r,c) += value; + } + } + + return *this; + } + + assignable_sub_matrix& operator-= ( + const T& value + ) + { + const long bottom = top+height-1; + const long right = left+width-1; + for (long r = top; r <= bottom; ++r) + { + for (long c = left; c <= right; ++c) + { + m(r,c) -= value; + } + } + + return *this; + } + + + matrix<T,NR,NC,mm,l>& m; + const long left, top, width, height; + }; + + + template <typename T, long NR, long NC, typename mm, typename l> + assignable_sub_matrix<T,NR,NC,mm,l> set_subm ( + matrix<T,NR,NC,mm,l>& m, + const rectangle& rect + ) + { + DLIB_ASSERT(get_rect(m).contains(rect) == true, + "\tassignable_matrix_expression set_subm(matrix& m, const rectangle& rect)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\trect.left(): " << rect.left() + << "\n\trect.top(): " << rect.top() + << "\n\trect.right(): " << rect.right() + << "\n\trect.bottom(): " << rect.bottom() + ); + + + return assignable_sub_matrix<T,NR,NC,mm,l>(m,rect.top(), rect.left(), rect.height(), rect.width()); + } + + + template <typename T, long NR, long NC, typename mm, typename l> + assignable_sub_matrix<T,NR,NC,mm,l> set_subm ( + matrix<T,NR,NC,mm,l>& m, + long r, + long c, + long nr, + long nc + ) + { + DLIB_ASSERT(r >= 0 && c >= 0 && nr >= 0 && nc >= 0 && r+nr <= m.nr() && c+nc <= m.nc(), + "\tassignable_matrix_expression set_subm(matrix& m, r, c, nr, nc)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tr: " << r + << "\n\tc: " << c + << "\n\tnr: " << nr + << "\n\tnc: " << nc + ); + + return assignable_sub_matrix<T,NR,NC,mm,l>(m,r,c, nr, nc); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T, long NR, long NC, typename mm, typename l, typename EXPr, typename EXPc> + class assignable_sub_range_matrix + { + public: + typedef T type; + typedef l layout_type; + typedef matrix<T,NR,NC,mm,l> matrix_type; + + assignable_sub_range_matrix( + matrix<T,NR,NC,mm,l>& m_, + const EXPr& rows_, + const EXPc& cols_ + ) : m(m_), rows(rows_), cols(cols_) {} + + T& operator() ( + long r, + long c + ) + { + return m(rows(r),cols(c)); + } + + long nr() const { return rows.size(); } + long nc() const { return cols.size(); } + + + template <typename EXP> + assignable_sub_range_matrix& operator= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == rows.size() && exp.nc() == cols.size(), + "\tassignable_matrix_expression set_subm(matrix& m, const matrix_exp rows, const matrix_exp cols)" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\trows.size() (target matrix): " << rows.size() + << "\n\tcols.size() (target matrix): " << cols.size() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_sub_range_matrix& operator+= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == rows.size() && exp.nc() == cols.size(), + "\tassignable_matrix_expression set_subm(matrix& m, const matrix_exp rows, const matrix_exp cols)" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\trows.size() (target matrix): " << rows.size() + << "\n\tcols.size() (target matrix): " << cols.size() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, subm(m,rows,cols)+exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator+=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_sub_range_matrix& operator-= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == rows.size() && exp.nc() == cols.size(), + "\tassignable_matrix_expression set_subm(matrix& m, const matrix_exp rows, const matrix_exp cols)" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\trows.size() (target matrix): " << rows.size() + << "\n\tcols.size() (target matrix): " << cols.size() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, subm(m,rows,cols)-exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator-=(tmp(exp)); + } + + return *this; + } + + assignable_sub_range_matrix& operator= ( + const T& value + ) + { + for (long r = 0; r < rows.size(); ++r) + { + for (long c = 0; c < cols.size(); ++c) + { + m(rows(r),cols(c)) = value; + } + } + + return *this; + } + + assignable_sub_range_matrix& operator+= ( + const T& value + ) + { + for (long r = 0; r < rows.size(); ++r) + { + for (long c = 0; c < cols.size(); ++c) + { + m(rows(r),cols(c)) += value; + } + } + + return *this; + } + + assignable_sub_range_matrix& operator-= ( + const T& value + ) + { + for (long r = 0; r < rows.size(); ++r) + { + for (long c = 0; c < cols.size(); ++c) + { + m(rows(r),cols(c)) -= value; + } + } + + return *this; + } + + private: + + matrix<T,NR,NC,mm,l>& m; + const EXPr rows; + const EXPc cols; + }; + + template <typename T, long NR, long NC, typename mm, typename l, typename EXPr, typename EXPc> + assignable_sub_range_matrix<T,NR,NC,mm,l,EXPr,EXPc > set_subm ( + matrix<T,NR,NC,mm,l>& m, + const matrix_exp<EXPr>& rows, + const matrix_exp<EXPc>& cols + ) + { + DLIB_ASSERT(0 <= min(rows) && max(rows) < m.nr() && 0 <= min(cols) && max(cols) < m.nc() && + (rows.nr() == 1 || rows.nc() == 1) && (cols.nr() == 1 || cols.nc() == 1), + "\tassignable_matrix_expression set_subm(matrix& m, const matrix_exp& rows, const matrix_exp& cols)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tmin(rows): " << min(rows) + << "\n\tmax(rows): " << max(rows) + << "\n\tmin(cols): " << min(cols) + << "\n\tmax(cols): " << max(cols) + << "\n\trows.nr(): " << rows.nr() + << "\n\trows.nc(): " << rows.nc() + << "\n\tcols.nr(): " << cols.nr() + << "\n\tcols.nc(): " << cols.nc() + ); + + return assignable_sub_range_matrix<T,NR,NC,mm,l,EXPr,EXPc >(m,rows.ref(),cols.ref()); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T, long NR, long NC, typename mm, typename l, typename EXPr> + assignable_sub_range_matrix<T,NR,NC,mm,l,EXPr,matrix_range_exp<long> > set_rowm ( + matrix<T,NR,NC,mm,l>& m, + const matrix_exp<EXPr>& rows + ) + { + DLIB_ASSERT(0 <= min(rows) && max(rows) < m.nr() && (rows.nr() == 1 || rows.nc() == 1), + "\tassignable_matrix_expression set_rowm(matrix& m, const matrix_exp& rows)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tmin(rows): " << min(rows) + << "\n\tmax(rows): " << max(rows) + << "\n\trows.nr(): " << rows.nr() + << "\n\trows.nc(): " << rows.nc() + ); + + return assignable_sub_range_matrix<T,NR,NC,mm,l,EXPr,matrix_range_exp<long> >(m,rows.ref(),range(0,m.nc()-1)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T, long NR, long NC, typename mm, typename l, typename EXPc> + assignable_sub_range_matrix<T,NR,NC,mm,l,matrix_range_exp<long>,EXPc > set_colm ( + matrix<T,NR,NC,mm,l>& m, + const matrix_exp<EXPc>& cols + ) + { + DLIB_ASSERT(0 <= min(cols) && max(cols) < m.nc() && (cols.nr() == 1 || cols.nc() == 1), + "\tassignable_matrix_expression set_colm(matrix& m, const matrix_exp& cols)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tmin(cols): " << min(cols) + << "\n\tmax(cols): " << max(cols) + << "\n\tcols.nr(): " << cols.nr() + << "\n\tcols.nc(): " << cols.nc() + ); + + return assignable_sub_range_matrix<T,NR,NC,mm,l,matrix_range_exp<long>,EXPc >(m,range(0,m.nr()-1),cols.ref()); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T, long NR, long NC, typename mm, typename l> + class assignable_col_matrix + { + public: + typedef T type; + typedef l layout_type; + typedef matrix<T,NR,NC,mm,l> matrix_type; + + assignable_col_matrix( + matrix<T,NR,NC,mm,l>& m_, + const long col_ + ) : m(m_), col(col_) {} + + T& operator() ( + long r, + long + ) + { + return m(r,col); + } + + const T& operator() ( + long r, + long + ) const + { + return m(r,col); + } + + long nr() const { return m.nr(); } + long nc() const { return 1; } + + template <typename EXP> + assignable_col_matrix& operator= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nc() == 1 && exp.nr() == m.nr(), + "\tassignable_matrix_expression set_colm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\tm.nr() (target matrix): " << m.nr() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_col_matrix& operator+= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nc() == 1 && exp.nr() == m.nr(), + "\tassignable_matrix_expression set_colm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\tm.nr() (target matrix): " << m.nr() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, colm(m,col)+exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator+=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_col_matrix& operator-= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nc() == 1 && exp.nr() == m.nr(), + "\tassignable_matrix_expression set_colm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\tm.nr() (target matrix): " << m.nr() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, colm(m,col)-exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator-=(tmp(exp)); + } + + return *this; + } + + assignable_col_matrix& operator= ( + const T& value + ) + { + for (long i = 0; i < m.nr(); ++i) + { + m(i,col) = value; + } + + return *this; + } + + assignable_col_matrix& operator+= ( + const T& value + ) + { + for (long i = 0; i < m.nr(); ++i) + { + m(i,col) += value; + } + + return *this; + } + + assignable_col_matrix& operator-= ( + const T& value + ) + { + for (long i = 0; i < m.nr(); ++i) + { + m(i,col) -= value; + } + + return *this; + } + + + matrix<T,NR,NC,mm,l>& m; + const long col; + }; + + + template <typename T, long NR, long NC, typename mm, typename l> + assignable_col_matrix<T,NR,NC,mm,l> set_colm ( + matrix<T,NR,NC,mm,l>& m, + const long col + ) + { + DLIB_ASSERT(col >= 0 && col < m.nc(), + "\tassignable_matrix_expression set_colm(matrix& m, col)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tcol: " << col + ); + + + return assignable_col_matrix<T,NR,NC,mm,l>(m,col); + } + +// ---------------------------------------------------------------------------------------- + + + template <typename T, long NR, long NC, typename mm, typename l> + class assignable_row_matrix + { + public: + typedef T type; + typedef l layout_type; + typedef matrix<T,NR,NC,mm,l> matrix_type; + + assignable_row_matrix( + matrix<T,NR,NC,mm,l>& m_, + const long row_ + ) : m(m_), row(row_) {} + + + T& operator() ( + long , + long c + ) + { + return m(row,c); + } + + const T& operator() ( + long , + long c + ) const + { + return m(row,c); + } + + long nr() const { return 1; } + long nc() const { return m.nc(); } + + + template <typename EXP> + assignable_row_matrix& operator= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == 1 && exp.nc() == m.nc(), + "\tassignable_matrix_expression set_rowm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\tm.nc() (target matrix): " << m.nc() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_row_matrix& operator+= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == 1 && exp.nc() == m.nc(), + "\tassignable_matrix_expression set_rowm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\tm.nc() (target matrix): " << m.nc() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, rowm(m,row)+exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator+=(tmp(exp)); + } + + return *this; + } + + template <typename EXP> + assignable_row_matrix& operator-= ( + const matrix_exp<EXP>& exp + ) + { + DLIB_ASSERT( exp.nr() == 1 && exp.nc() == m.nc(), + "\tassignable_matrix_expression set_rowm()" + << "\n\tYou have tried to assign to this object using a matrix that isn't the right size" + << "\n\texp.nr() (source matrix): " << exp.nr() + << "\n\texp.nc() (source matrix): " << exp.nc() + << "\n\tm.nc() (target matrix): " << m.nc() + ); + + if (exp.destructively_aliases(m) == false) + { + matrix_assign(*this, rowm(m,row)-exp); + } + else + { + // make a temporary copy of the matrix we are going to assign to m to + // avoid aliasing issues during the copy + this->operator-=(tmp(exp)); + } + + return *this; + } + + assignable_row_matrix& operator= ( + const T& value + ) + { + for (long i = 0; i < m.nc(); ++i) + { + m(row,i) = value; + } + + return *this; + } + + assignable_row_matrix& operator+= ( + const T& value + ) + { + for (long i = 0; i < m.nc(); ++i) + { + m(row,i) += value; + } + + return *this; + } + + assignable_row_matrix& operator-= ( + const T& value + ) + { + for (long i = 0; i < m.nc(); ++i) + { + m(row,i) -= value; + } + + return *this; + } + + + matrix<T,NR,NC,mm,l>& m; + const long row; + }; + + + template <typename T, long NR, long NC, typename mm, typename l> + assignable_row_matrix<T,NR,NC,mm,l> set_rowm ( + matrix<T,NR,NC,mm,l>& m, + const long row + ) + { + DLIB_ASSERT(row >= 0 && row < m.nr(), + "\tassignable_matrix_expression set_rowm(matrix& m, row)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\trow: " << row + ); + + + return assignable_row_matrix<T,NR,NC,mm,l>(m,row); + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_SUBEXP_ + diff --git a/ml/dlib/dlib/matrix/matrix_subexp_abstract.h b/ml/dlib/dlib/matrix/matrix_subexp_abstract.h new file mode 100644 index 000000000..2665d1b99 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_subexp_abstract.h @@ -0,0 +1,570 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_SUBEXP_ABSTRACT_ +#ifdef DLIB_MATRIx_SUBEXP_ABSTRACT_ + +#include "matrix_abstract.h" +#include "../geometry/rectangle.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + template <long start, long inc, long end> + const matrix_exp range ( + ); + /*! + requires + - inc > 0 + ensures + - returns a matrix R such that: + - R::type == long + - R.nr() == 1 + - R.nc() == abs(end - start)/inc + 1 + - if (start <= end) then + - R(i) == start + i*inc + - else + - R(i) == start - i*inc + !*/ + + template <long start, long end> + const matrix_exp range ( + ) { return range<start,1,end>(); } + + const matrix_exp range ( + long start, + long inc, + long end + ); + /*! + requires + - inc > 0 + ensures + - returns a matrix R such that: + - R::type == long + - R.nr() == 1 + - R.nc() == abs(end - start)/inc + 1 + - if (start <= end) then + - R(i) == start + i*inc + - else + - R(i) == start - i*inc + !*/ + + const matrix_exp range ( + long start, + long end + ) { return range(start,1,end); } + +// ---------------------------------------------------------------------------------------- + + const matrix_exp subm ( + const matrix_exp& m, + const matrix_exp& rows, + const matrix_exp& cols, + ); + /*! + requires + - rows and cols contain integral elements (e.g. int, long) + - 0 <= min(rows) && max(rows) < m.nr() + - 0 <= min(cols) && max(cols) < m.nc() + - rows.nr() == 1 || rows.nc() == 1 + - cols.nr() == 1 || cols.nc() == 1 + (i.e. rows and cols must be vectors) + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R.nr() == rows.size() + - R.nc() == cols.size() + - for all valid r and c: + R(r,c) == m(rows(r),cols(c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp subm ( + const matrix_exp& m, + long row, + long col, + long nr, + long nc + ); + /*! + requires + - row >= 0 + - col >= 0 + - nr >= 0 + - nc >= 0 + - row + nr <= m.nr() + - col + nc <= m.nc() + ensures + - returns a matrix R such that: + - R.nr() == nr + - R.nc() == nc + - for all valid r and c: + R(r, c) == m(r+row,c+col) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp subm ( + const matrix_exp& m, + const rectangle& rect + ); + /*! + requires + - get_rect(m).contains(rect) == true + (i.e. rect is a region inside the matrix m) + ensures + - returns a matrix R such that: + - R.nr() == rect.height() + - R.nc() == rect.width() + - for all valid r and c: + R(r, c) == m(r+rect.top(), c+rect.left()) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp subm_clipped ( + const matrix_exp& m, + long row, + long col, + long nr, + long nc + ); + /*! + ensures + - This function is just like subm() except that it will automatically clip the + indicated sub matrix window so that it does not extend outside m. + In particular: + - Let box = rectangle(col,row,col+nc-1,row+nr-1) + (i.e. the box that contains the indicated sub matrix) + - Let box_clipped = box.intersect(get_rect(m)) + - Then this function returns a matrix R such that: + - R.nr() == box_clipped.height() + - R.nc() == box_clipped.width() + - for all valid r and c: + R(r, c) == m(r+box_clipped.top(),c+box_clipped.left()) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp subm_clipped ( + const matrix_exp& m, + const rectangle& rect + ); + /*! + ensures + - Let box_clipped == rect.intersect(get_rect(m)) + - returns a matrix R such that: + - R.nr() == box_clipped.height() + - R.nc() == box_clipped.width() + - for all valid r and c: + R(r, c) == m(r+box_clipped.top(), c+box_clipped.left()) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp rowm ( + const matrix_exp& m, + long row + ); + /*! + requires + - 0 <= row < m.nr() + ensures + - returns a matrix R such that: + - R.nr() == 1 + - R.nc() == m.nc() + - for all valid i: + R(i) == m(row,i) + !*/ + + template <typename EXP> + struct rowm_exp + { + /*! + WHAT THIS OBJECT REPRESENTS + This struct allows you to determine the type of matrix expression + object returned from the rowm(m,row) function. An example makes its + use clear: + + template <typename EXP> + void do_something( const matrix_exp<EXP>& mat) + { + // r is a matrix expression that aliases mat. + typename rowm_exp<EXP>::type r = rowm(mat,0); + + // Print the first row of mat. So we see that by using + // rowm_exp we can save the object returned by rowm() in + // a local variable. + cout << r << endl; + + // Note that you can only save the return value of rowm() to + // a local variable if the argument to rowm() has a lifetime + // beyond the rowm() expression. The example shown above is + // OK but the following would result in undefined behavior: + typename rowm_exp<EXP>::type bad = rowm(mat + mat,0); + } + !*/ + typedef type_of_expression_returned_by_rowm type; + }; + +// ---------------------------------------------------------------------------------------- + + const matrix_exp rowm ( + const matrix_exp& m, + long row, + long length + ); + /*! + requires + - 0 <= row < m.nr() + - 0 <= length <= m.nc() + ensures + - returns a matrix R such that: + - R.nr() == 1 + - R.nc() == length + - for all valid i: + R(i) == m(row,i) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp rowm ( + const matrix_exp& m, + const matrix_exp& rows + ); + /*! + requires + - rows contains integral elements (e.g. int, long) + - 0 <= min(rows) && max(rows) < m.nr() + - rows.nr() == 1 || rows.nc() == 1 + (i.e. rows must be a vector) + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R.nr() == rows.size() + - R.nc() == m.nc() + - for all valid r and c: + R(r,c) == m(rows(r),c) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp colm ( + const matrix_exp& m, + long col + ); + /*! + requires + - 0 <= col < m.nc() + ensures + - returns a matrix R such that: + - R.nr() == m.nr() + - R.nc() == 1 + - for all valid i: + R(i) == m(i,col) + !*/ + + template <typename EXP> + struct colm_exp + { + /*! + WHAT THIS OBJECT REPRESENTS + This struct allows you to determine the type of matrix expression + object returned from the colm(m,col) function. An example makes its + use clear: + + template <typename EXP> + void do_something( const matrix_exp<EXP>& mat) + { + // c is a matrix expression that aliases mat. + typename colm_exp<EXP>::type c = colm(mat,0); + + // Print the first column of mat. So we see that by using + // colm_exp we can save the object returned by colm() in + // a local variable. + cout << c << endl; + + // Note that you can only save the return value of colm() to + // a local variable if the argument to colm() has a lifetime + // beyond the colm() expression. The example shown above is + // OK but the following would result in undefined behavior: + typename colm_exp<EXP>::type bad = colm(mat + mat,0); + } + !*/ + typedef type_of_expression_returned_by_colm type; + }; + +// ---------------------------------------------------------------------------------------- + + const matrix_exp colm ( + const matrix_exp& m, + long col, + long length + ); + /*! + requires + - 0 <= col < m.nc() + - 0 <= length <= m.nr() + ensures + - returns a matrix R such that: + - R.nr() == length + - R.nc() == 1 + - for all valid i: + R(i) == m(i,col) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp colm ( + const matrix_exp& m, + const matrix_exp& cols + ); + /*! + requires + - cols contains integral elements (e.g. int, long) + - 0 <= min(cols) && max(cols) < m.nc() + - cols.nr() == 1 || cols.nc() == 1 + (i.e. cols must be a vector) + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R.nr() == m.nr() + - R.nc() == cols.size() + - for all valid r and c: + R(r,c) == m(r,cols(c)) + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename T> + assignable_matrix_expression set_ptrm ( + T* ptr, + long nr, + long nc = 1 + ); + /*! + requires + - ptr == a pointer to nr*nc elements of type T + - nr >= 0 + - nc >= 0 + ensures + - statements of the following form: + - set_ptrm(ptr,nr,nc) = some_matrix; + result in it being the case that: + - mat(ptr,nr,nc) == some_matrix. + + - statements of the following form: + - set_ptrm(ptr,nr,nc) = scalar_value; + result in it being the case that: + - mat(ptr,nr,nc) == uniform_matrix<matrix::type>(nr,nc,scalar_value). + + - In addition to the normal assignment statements using the = symbol, you may + also use the usual += and -= versions of the assignment operator. In these + cases, they have their usual effect. + !*/ + +// ---------------------------------------------------------------------------------------- + + assignable_matrix_expression set_subm ( + matrix& m, + long row, + long col, + long nr, + long nc + ); + /*! + requires + - row >= 0 + - col >= 0 + - nr >= 0 + - nc >= 0 + - row + nr <= m.nr() + - col + nc <= m.nc() + ensures + - statements of the following form: + - set_subm(m,row,col,nr,nc) = some_matrix; + result in it being the case that: + - subm(m,row,col,nr,nc) == some_matrix. + + - statements of the following form: + - set_subm(m,row,col,nr,nc) = scalar_value; + result in it being the case that: + - subm(m,row,col,nr,nc) == uniform_matrix<matrix::type>(nr,nc,scalar_value). + + - In addition to the normal assignment statements using the = symbol, you may + also use the usual += and -= versions of the assignment operator. In these + cases, they have their usual effect. + !*/ + +// ---------------------------------------------------------------------------------------- + + assignable_matrix_expression set_subm ( + matrix& m, + const rectangle& rect + ); + /*! + requires + - get_rect(m).contains(rect) == true + (i.e. rect is a region inside the matrix m) + ensures + - statements of the following form: + - set_subm(m,rect) = some_matrix; + result in it being the case that: + - subm(m,rect) == some_matrix. + + - statements of the following form: + - set_subm(m,rect) = scalar_value; + result in it being the case that: + - subm(m,rect) == uniform_matrix<matrix::type>(nr,nc,scalar_value). + + - In addition to the normal assignment statements using the = symbol, you may + also use the usual += and -= versions of the assignment operator. In these + cases, they have their usual effect. + !*/ + +// ---------------------------------------------------------------------------------------- + + assignable_matrix_expression set_subm ( + matrix& m, + const matrix_exp& rows, + const matrix_exp& cols + ); + /*! + requires + - rows and cols contain integral elements (e.g. int, long) + - 0 <= min(rows) && max(rows) < m.nr() + - 0 <= min(cols) && max(cols) < m.nc() + - rows.nr() == 1 || rows.nc() == 1 + - cols.nr() == 1 || cols.nc() == 1 + (i.e. rows and cols must be vectors) + ensures + - statements of the following form: + - set_subm(m,rows,cols) = some_matrix; + result in it being the case that: + - subm(m,rows,cols) == some_matrix. + + - statements of the following form: + - set_subm(m,rows,cols) = scalar_value; + result in it being the case that: + - subm(m,rows,cols) == uniform_matrix<matrix::type>(nr,nc,scalar_value). + + - In addition to the normal assignment statements using the = symbol, you may + also use the usual += and -= versions of the assignment operator. In these + cases, they have their usual effect. + !*/ + +// ---------------------------------------------------------------------------------------- + + assignable_matrix_expression set_rowm ( + matrix& m, + long row + ); + /*! + requires + - 0 <= row < m.nr() + ensures + - statements of the following form: + - set_rowm(m,row) = some_matrix; + result in it being the case that: + - rowm(m,row) == some_matrix. + + - statements of the following form: + - set_rowm(m,row) = scalar_value; + result in it being the case that: + - rowm(m,row) == uniform_matrix<matrix::type>(1,nc,scalar_value). + + - In addition to the normal assignment statements using the = symbol, you may + also use the usual += and -= versions of the assignment operator. In these + cases, they have their usual effect. + !*/ + +// ---------------------------------------------------------------------------------------- + + assignable_matrix_expression set_rowm ( + matrix& m, + const matrix_exp& rows + ); + /*! + requires + - rows contains integral elements (e.g. int, long) + - 0 <= min(rows) && max(rows) < m.nr() + - rows.nr() == 1 || rows.nc() == 1 + (i.e. rows must be a vector) + ensures + - statements of the following form: + - set_rowm(m,rows) = some_matrix; + result in it being the case that: + - rowm(m,rows) == some_matrix. + + - statements of the following form: + - set_rowm(m,rows) = scalar_value; + result in it being the case that: + - rowm(m,rows) == uniform_matrix<matrix::type>(nr,nc,scalar_value). + + - In addition to the normal assignment statements using the = symbol, you may + also use the usual += and -= versions of the assignment operator. In these + cases, they have their usual effect. + !*/ + +// ---------------------------------------------------------------------------------------- + + assignable_matrix_expression set_colm ( + matrix& m, + long col + ); + /*! + requires + - 0 <= col < m.nr() + ensures + - statements of the following form: + - set_colm(m,col) = some_matrix; + result in it being the case that: + - colm(m,col) == some_matrix. + + - statements of the following form: + - set_colm(m,col) = scalar_value; + result in it being the case that: + - colm(m,col) == uniform_matrix<matrix::type>(nr,1,scalar_value). + + - In addition to the normal assignment statements using the = symbol, you may + also use the usual += and -= versions of the assignment operator. In these + cases, they have their usual effect. + !*/ + +// ---------------------------------------------------------------------------------------- + + assignable_matrix_expression set_colm ( + matrix& m, + const matrix_exp& cols + ); + /*! + requires + - cols contains integral elements (e.g. int, long) + - 0 <= min(cols) && max(cols) < m.nc() + - cols.nr() == 1 || cols.nc() == 1 + (i.e. cols must be a vector) + ensures + - statements of the following form: + - set_colm(m,cols) = some_matrix; + result in it being the case that: + - colm(m,cols) == some_matrix. + + - statements of the following form: + - set_colm(m,cols) = scalar_value; + result in it being the case that: + - colm(m,cols) == uniform_matrix<matrix::type>(nr,nc,scalar_value). + + - In addition to the normal assignment statements using the = symbol, you may + also use the usual += and -= versions of the assignment operator. In these + cases, they have their usual effect. + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_SUBEXP_ABSTRACT_ + diff --git a/ml/dlib/dlib/matrix/matrix_trsm.h b/ml/dlib/dlib/matrix/matrix_trsm.h new file mode 100644 index 000000000..ef5ec5ed9 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_trsm.h @@ -0,0 +1,654 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRiX_TRSM_Hh_ +#define DLIB_MATRiX_TRSM_Hh_ +#include "lapack/fortran_id.h" +#include "cblas_constants.h" + +namespace dlib +{ + namespace blas_bindings + { +#ifdef DLIB_USE_BLAS +#ifndef CBLAS_H + extern "C" + { + void cblas_strsm(const CBLAS_ORDER Order, const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, const int M, const int N, + const float alpha, const float *A, const int lda, + float *B, const int ldb); + + void cblas_dtrsm(const CBLAS_ORDER Order, const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, const int M, const int N, + const double alpha, const double *A, const int lda, + double *B, const int ldb); + } +#endif // if not CBLAS_H +#endif // if DLIB_USE_BLAS + + // ------------------------------------------------------------------------------------ + +/* Purpose */ +/* ======= */ + +/* DTRSM solves one of the matrix equations */ + +/* op( A )*X = alpha*B, or X*op( A ) = alpha*B, */ + +/* where alpha is a scalar, X and B are m by n matrices, A is a unit, or */ +/* non-unit, upper or lower triangular matrix and op( A ) is one of */ + +/* op( A ) = A or op( A ) = A'. */ + +/* The matrix X is overwritten on B. */ + +/* Arguments */ +/* ========== */ + +/* SIDE - CHARACTER*1. */ +/* On entry, SIDE specifies whether op( A ) appears on the left */ +/* or right of X as follows: */ + +/* SIDE = 'L' or 'l' op( A )*X = alpha*B. */ + +/* SIDE = 'R' or 'r' X*op( A ) = alpha*B. */ + +/* Unchanged on exit. */ + +/* UPLO - CHARACTER*1. */ +/* On entry, UPLO specifies whether the matrix A is an upper or */ +/* lower triangular matrix as follows: */ + +/* UPLO = 'U' or 'u' A is an upper triangular matrix. */ + +/* UPLO = 'L' or 'l' A is a lower triangular matrix. */ + +/* Unchanged on exit. */ + +/* TRANSA - CHARACTER*1. */ +/* On entry, TRANSA specifies the form of op( A ) to be used in */ +/* the matrix multiplication as follows: */ + +/* TRANSA = 'N' or 'n' op( A ) = A. */ + +/* TRANSA = 'T' or 't' op( A ) = A'. */ + +/* TRANSA = 'C' or 'c' op( A ) = A'. */ + +/* Unchanged on exit. */ + +/* DIAG - CHARACTER*1. */ +/* On entry, DIAG specifies whether or not A is unit triangular */ +/* as follows: */ + +/* DIAG = 'U' or 'u' A is assumed to be unit triangular. */ + +/* DIAG = 'N' or 'n' A is not assumed to be unit */ +/* triangular. */ + +/* Unchanged on exit. */ + +/* M - INTEGER. */ +/* On entry, M specifies the number of rows of B. M must be at */ +/* least zero. */ +/* Unchanged on exit. */ + +/* N - INTEGER. */ +/* On entry, N specifies the number of columns of B. N must be */ +/* at least zero. */ +/* Unchanged on exit. */ + +/* ALPHA - DOUBLE PRECISION. */ +/* On entry, ALPHA specifies the scalar alpha. When alpha is */ +/* zero then A is not referenced and B need not be set before */ +/* entry. */ +/* Unchanged on exit. */ + +/* A - DOUBLE PRECISION array of DIMENSION ( LDA, k ), where k is m */ +/* when SIDE = 'L' or 'l' and is n when SIDE = 'R' or 'r'. */ +/* Before entry with UPLO = 'U' or 'u', the leading k by k */ +/* upper triangular part of the array A must contain the upper */ +/* triangular matrix and the strictly lower triangular part of */ +/* A is not referenced. */ +/* Before entry with UPLO = 'L' or 'l', the leading k by k */ +/* lower triangular part of the array A must contain the lower */ +/* triangular matrix and the strictly upper triangular part of */ +/* A is not referenced. */ +/* Note that when DIAG = 'U' or 'u', the diagonal elements of */ +/* A are not referenced either, but are assumed to be unity. */ +/* Unchanged on exit. */ + +/* LDA - INTEGER. */ +/* On entry, LDA specifies the first dimension of A as declared */ +/* in the calling (sub) program. When SIDE = 'L' or 'l' then */ +/* LDA must be at least max( 1, m ), when SIDE = 'R' or 'r' */ +/* then LDA must be at least max( 1, n ). */ +/* Unchanged on exit. */ + +/* B - DOUBLE PRECISION array of DIMENSION ( LDB, n ). */ +/* Before entry, the leading m by n part of the array B must */ +/* contain the right-hand side matrix B, and on exit is */ +/* overwritten by the solution matrix X. */ + +/* LDB - INTEGER. */ +/* On entry, LDB specifies the first dimension of B as declared */ +/* in the calling (sub) program. LDB must be at least */ +/* max( 1, m ). */ +/* Unchanged on exit. */ + + +/* Level 3 Blas routine. */ + + +/* -- Written on 8-February-1989. */ +/* Jack Dongarra, Argonne National Laboratory. */ +/* Iain Duff, AERE Harwell. */ +/* Jeremy Du Croz, Numerical Algorithms Group Ltd. */ +/* Sven Hammarling, Numerical Algorithms Group Ltd. */ + + template <typename T> + void local_trsm( + const CBLAS_ORDER Order, + CBLAS_SIDE Side, + CBLAS_UPLO Uplo, + const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, + long m, + long n, + T alpha, + const T *a, + long lda, + T *b, + long ldb + ) + /*! + This is a copy of the dtrsm routine from the netlib.org BLAS which was run though + f2c and converted into this form for use when a BLAS library is not available. + !*/ + { + if (Order == CblasRowMajor) + { + // since row major ordering looks like transposition to FORTRAN we need to flip a + // few things. + if (Side == CblasLeft) + Side = CblasRight; + else + Side = CblasLeft; + + if (Uplo == CblasUpper) + Uplo = CblasLower; + else + Uplo = CblasUpper; + + std::swap(m,n); + } + + /* System generated locals */ + long a_dim1, a_offset, b_dim1, b_offset, i__1, i__2, i__3; + + /* Local variables */ + long i__, j, k, info; + T temp; + bool lside; + long nrowa; + bool upper; + bool nounit; + + /* Parameter adjustments */ + a_dim1 = lda; + a_offset = 1 + a_dim1; + a -= a_offset; + b_dim1 = ldb; + b_offset = 1 + b_dim1; + b -= b_offset; + + /* Function Body */ + lside = (Side == CblasLeft); + if (lside) + { + nrowa = m; + } else + { + nrowa = n; + } + nounit = (Diag == CblasNonUnit); + upper = (Uplo == CblasUpper); + + info = 0; + if (! lside && ! (Side == CblasRight)) { + info = 1; + } else if (! upper && !(Uplo == CblasLower) ) { + info = 2; + } else if (!(TransA == CblasNoTrans) && + !(TransA == CblasTrans) && + !(TransA == CblasConjTrans)) { + info = 3; + } else if (!(Diag == CblasUnit) && + !(Diag == CblasNonUnit) ) { + info = 4; + } else if (m < 0) { + info = 5; + } else if (n < 0) { + info = 6; + } else if (lda < std::max<long>(1,nrowa)) { + info = 9; + } else if (ldb < std::max<long>(1,m)) { + info = 11; + } + DLIB_CASSERT( info == 0, "Invalid inputs given to local_trsm"); + + /* Quick return if possible. */ + + if (m == 0 || n == 0) { + return; + } + + /* And when alpha.eq.zero. */ + + if (alpha == 0.) { + i__1 = n; + for (j = 1; j <= i__1; ++j) { + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + j * b_dim1] = 0.; + /* L10: */ + } + /* L20: */ + } + return; + } + + /* Start the operations. */ + + if (lside) { + if (TransA == CblasNoTrans) { + + /* Form B := alpha*inv( A )*B. */ + + if (upper) { + i__1 = n; + for (j = 1; j <= i__1; ++j) { + if (alpha != 1.) { + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + j * b_dim1] = alpha * b[i__ + j * b_dim1] + ; + /* L30: */ + } + } + for (k = m; k >= 1; --k) { + if (b[k + j * b_dim1] != 0.) { + if (nounit) { + b[k + j * b_dim1] /= a[k + k * a_dim1]; + } + i__2 = k - 1; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + j * b_dim1] -= b[k + j * b_dim1] * a[ + i__ + k * a_dim1]; + /* L40: */ + } + } + /* L50: */ + } + /* L60: */ + } + } else { + i__1 = n; + for (j = 1; j <= i__1; ++j) { + if (alpha != 1.) { + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + j * b_dim1] = alpha * b[i__ + j * b_dim1] + ; + /* L70: */ + } + } + i__2 = m; + for (k = 1; k <= i__2; ++k) { + if (b[k + j * b_dim1] != 0.) { + if (nounit) { + b[k + j * b_dim1] /= a[k + k * a_dim1]; + } + i__3 = m; + for (i__ = k + 1; i__ <= i__3; ++i__) { + b[i__ + j * b_dim1] -= b[k + j * b_dim1] * a[ + i__ + k * a_dim1]; + /* L80: */ + } + } + /* L90: */ + } + /* L100: */ + } + } + } else { + + /* Form B := alpha*inv( A' )*B. */ + + if (upper) { + i__1 = n; + for (j = 1; j <= i__1; ++j) { + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + temp = alpha * b[i__ + j * b_dim1]; + i__3 = i__ - 1; + for (k = 1; k <= i__3; ++k) { + temp -= a[k + i__ * a_dim1] * b[k + j * b_dim1]; + /* L110: */ + } + if (nounit) { + temp /= a[i__ + i__ * a_dim1]; + } + b[i__ + j * b_dim1] = temp; + /* L120: */ + } + /* L130: */ + } + } else { + i__1 = n; + for (j = 1; j <= i__1; ++j) { + for (i__ = m; i__ >= 1; --i__) { + temp = alpha * b[i__ + j * b_dim1]; + i__2 = m; + for (k = i__ + 1; k <= i__2; ++k) { + temp -= a[k + i__ * a_dim1] * b[k + j * b_dim1]; + /* L140: */ + } + if (nounit) { + temp /= a[i__ + i__ * a_dim1]; + } + b[i__ + j * b_dim1] = temp; + /* L150: */ + } + /* L160: */ + } + } + } + } else { + if (TransA == CblasNoTrans) { + + /* Form B := alpha*B*inv( A ). */ + + if (upper) { + i__1 = n; + for (j = 1; j <= i__1; ++j) { + if (alpha != 1.) { + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + j * b_dim1] = alpha * b[i__ + j * b_dim1] + ; + /* L170: */ + } + } + i__2 = j - 1; + for (k = 1; k <= i__2; ++k) { + if (a[k + j * a_dim1] != 0.) { + i__3 = m; + for (i__ = 1; i__ <= i__3; ++i__) { + b[i__ + j * b_dim1] -= a[k + j * a_dim1] * b[ + i__ + k * b_dim1]; + /* L180: */ + } + } + /* L190: */ + } + if (nounit) { + temp = 1. / a[j + j * a_dim1]; + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + j * b_dim1] = temp * b[i__ + j * b_dim1]; + /* L200: */ + } + } + /* L210: */ + } + } else { + for (j = n; j >= 1; --j) { + if (alpha != 1.) { + i__1 = m; + for (i__ = 1; i__ <= i__1; ++i__) { + b[i__ + j * b_dim1] = alpha * b[i__ + j * b_dim1] + ; + /* L220: */ + } + } + i__1 = n; + for (k = j + 1; k <= i__1; ++k) { + if (a[k + j * a_dim1] != 0.) { + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + j * b_dim1] -= a[k + j * a_dim1] * b[ + i__ + k * b_dim1]; + /* L230: */ + } + } + /* L240: */ + } + if (nounit) { + temp = 1. / a[j + j * a_dim1]; + i__1 = m; + for (i__ = 1; i__ <= i__1; ++i__) { + b[i__ + j * b_dim1] = temp * b[i__ + j * b_dim1]; + /* L250: */ + } + } + /* L260: */ + } + } + } else { + + /* Form B := alpha*B*inv( A' ). */ + + if (upper) { + for (k = n; k >= 1; --k) { + if (nounit) { + temp = 1. / a[k + k * a_dim1]; + i__1 = m; + for (i__ = 1; i__ <= i__1; ++i__) { + b[i__ + k * b_dim1] = temp * b[i__ + k * b_dim1]; + /* L270: */ + } + } + i__1 = k - 1; + for (j = 1; j <= i__1; ++j) { + if (a[j + k * a_dim1] != 0.) { + temp = a[j + k * a_dim1]; + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + j * b_dim1] -= temp * b[i__ + k * + b_dim1]; + /* L280: */ + } + } + /* L290: */ + } + if (alpha != 1.) { + i__1 = m; + for (i__ = 1; i__ <= i__1; ++i__) { + b[i__ + k * b_dim1] = alpha * b[i__ + k * b_dim1] + ; + /* L300: */ + } + } + /* L310: */ + } + } else { + i__1 = n; + for (k = 1; k <= i__1; ++k) { + if (nounit) { + temp = 1. / a[k + k * a_dim1]; + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + k * b_dim1] = temp * b[i__ + k * b_dim1]; + /* L320: */ + } + } + i__2 = n; + for (j = k + 1; j <= i__2; ++j) { + if (a[j + k * a_dim1] != 0.) { + temp = a[j + k * a_dim1]; + i__3 = m; + for (i__ = 1; i__ <= i__3; ++i__) { + b[i__ + j * b_dim1] -= temp * b[i__ + k * + b_dim1]; + /* L330: */ + } + } + /* L340: */ + } + if (alpha != 1.) { + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + b[i__ + k * b_dim1] = alpha * b[i__ + k * b_dim1] + ; + /* L350: */ + } + } + /* L360: */ + } + } + } + } + } + + // ------------------------------------------------------------------------------------ + + inline void cblas_trsm(const CBLAS_ORDER Order, const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, const int M, const int N, + const float alpha, const float *A, const int lda, + float *B, const int ldb) + { +#ifdef DLIB_USE_BLAS + if (M > 4) + { + cblas_strsm(Order, Side, Uplo, TransA, Diag, M, N, alpha, A, lda, B, ldb); + return; + } +#endif + local_trsm(Order, Side, Uplo, TransA, Diag, M, N, alpha, A, lda, B, ldb); + } + + inline void cblas_trsm(const CBLAS_ORDER Order, const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, const int M, const int N, + const double alpha, const double *A, const int lda, + double *B, const int ldb) + { +#ifdef DLIB_USE_BLAS + if (M > 4) + { + cblas_dtrsm(Order, Side, Uplo, TransA, Diag, M, N, alpha, A, lda, B, ldb); + return; + } +#endif + local_trsm(Order, Side, Uplo, TransA, Diag, M, N, alpha, A, lda, B, ldb); + } + + inline void cblas_trsm(const CBLAS_ORDER Order, const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, const int M, const int N, + const long double alpha, const long double *A, const int lda, + long double *B, const int ldb) + { + local_trsm(Order, Side, Uplo, TransA, Diag, M, N, alpha, A, lda, B, ldb); + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, + long NC1, long NC2, + typename MM + > + inline void triangular_solver ( + const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, + const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, + const matrix<T,NR1,NC1,MM,row_major_layout>& A, + const T alpha, + matrix<T,NR2,NC2,MM,row_major_layout>& B + ) + { + cblas_trsm(CblasRowMajor, Side, Uplo, TransA, Diag, B.nr(), B.nc(), + alpha, &A(0,0), A.nc(), &B(0,0), B.nc()); + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, + long NC1, long NC2, + typename MM + > + inline void triangular_solver ( + const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, + const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, + const matrix<T,NR1,NC1,MM,column_major_layout>& A, + const T alpha, + matrix<T,NR2,NC2,MM,column_major_layout>& B + ) + { + cblas_trsm(CblasColMajor, Side, Uplo, TransA, Diag, B.nr(), B.nc(), + alpha, &A(0,0), A.nr(), &B(0,0), B.nr()); + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, + long NC1, long NC2, + typename MM + > + inline void triangular_solver ( + const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, + const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, + const matrix<T,NR1,NC1,MM,column_major_layout>& A, + matrix<T,NR2,NC2,MM,column_major_layout>& B, + long rows_of_B + ) + { + const T alpha = 1; + cblas_trsm(CblasColMajor, Side, Uplo, TransA, Diag, rows_of_B, B.nc(), + alpha, &A(0,0), A.nr(), &B(0,0), B.nr()); + } + + // ------------------------------------------------------------------------------------ + + template < + typename T, + long NR1, long NR2, + long NC1, long NC2, + typename MM, + typename layout + > + inline void triangular_solver ( + const CBLAS_SIDE Side, + const CBLAS_UPLO Uplo, + const CBLAS_TRANSPOSE TransA, + const CBLAS_DIAG Diag, + const matrix<T,NR1,NC1,MM,layout>& A, + matrix<T,NR2,NC2,MM,layout>& B + ) + { + const T alpha = 1; + triangular_solver(Side, Uplo, TransA, Diag, A, alpha, B); + } + + // ------------------------------------------------------------------------------------ + + } +} + +#endif // DLIB_MATRiX_TRSM_Hh_ + diff --git a/ml/dlib/dlib/matrix/matrix_utilities.h b/ml/dlib/dlib/matrix/matrix_utilities.h new file mode 100644 index 000000000..0c5091a4b --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_utilities.h @@ -0,0 +1,4544 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_MATRIx_UTILITIES_ +#define DLIB_MATRIx_UTILITIES_ + +#include "matrix_utilities_abstract.h" +#include "matrix.h" +#include <cmath> +#include <complex> +#include <limits> +#include "../pixel.h" +#include "../stl_checked.h" +#include <vector> +#include <algorithm> +#include "../std_allocator.h" +#include "matrix_expressions.h" +#include "matrix_math_functions.h" +#include "matrix_op.h" +#include "../general_hash/random_hashing.h" +#include "matrix_mat.h" + + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + /*!A is_complex + This is a template that can be used to determine if a type is a specialization + of the std::complex template class. + + For example: + is_complex<float>::value == false + is_complex<std::complex<float> >::value == true + !*/ + + template <typename T> + struct is_complex { static const bool value = false; }; + + template <typename T> + struct is_complex<std::complex<T> > { static const bool value = true; }; + template <typename T> + struct is_complex<std::complex<T>& > { static const bool value = true; }; + template <typename T> + struct is_complex<const std::complex<T>& > { static const bool value = true; }; + template <typename T> + struct is_complex<const std::complex<T> > { static const bool value = true; }; + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + inline bool is_row_vector ( + const matrix_exp<EXP>& m + ) { return m.nr() == 1; } + + template <typename EXP> + inline bool is_col_vector ( + const matrix_exp<EXP>& m + ) { return m.nc() == 1; } + + template <typename EXP> + inline bool is_vector ( + const matrix_exp<EXP>& m + ) { return is_row_vector(m) || is_col_vector(m); } + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + inline bool is_finite ( + const matrix_exp<EXP>& m + ) + { + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + if (!is_finite(m(r,c))) + return false; + } + } + return true; + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename T> + const T& magnitude (const T& item) { return item; } + template <typename T> + T magnitude (const std::complex<T>& item) { return std::norm(item); } + } + + template < + typename EXP + > + void find_min_and_max ( + const matrix_exp<EXP>& m, + typename EXP::type& min_val, + typename EXP::type& max_val + ) + { + DLIB_ASSERT(m.size() > 0, + "\ttype find_min_and_max(const matrix_exp& m, min_val, max_val)" + << "\n\tYou can't ask for the min and max of an empty matrix" + << "\n\tm.size(): " << m.size() + ); + typedef typename matrix_exp<EXP>::type type; + + min_val = m(0,0); + max_val = min_val; + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + type temp = m(r,c); + if (dlib::impl::magnitude(temp) > dlib::impl::magnitude(max_val)) + max_val = temp; + if (dlib::impl::magnitude(temp) < dlib::impl::magnitude(min_val)) + min_val = temp; + } + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + point max_point ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0, + "\tpoint max_point(const matrix_exp& m)" + << "\n\tm can't be empty" + << "\n\tm.size(): " << m.size() + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + typedef typename matrix_exp<EXP>::type type; + + point best_point(0,0); + type val = m(0,0); + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + type temp = m(r,c); + if (dlib::impl::magnitude(temp) > dlib::impl::magnitude(val)) + { + val = temp; + best_point = point(c,r); + } + } + } + return best_point; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + point min_point ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0, + "\tpoint min_point(const matrix_exp& m)" + << "\n\tm can't be empty" + << "\n\tm.size(): " << m.size() + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + typedef typename matrix_exp<EXP>::type type; + + point best_point(0,0); + type val = m(0,0); + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + type temp = m(r,c); + if (dlib::impl::magnitude(temp) < dlib::impl::magnitude(val)) + { + val = temp; + best_point = point(c,r); + } + } + } + return best_point; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + long index_of_max ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0 && is_vector(m) == true, + "\tlong index_of_max(const matrix_exp& m)" + << "\n\tm must be a row or column matrix" + << "\n\tm.size(): " << m.size() + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + typedef typename matrix_exp<EXP>::type type; + + type val = m(0); + long best_idx = 0; + for (long i = 1; i < m.size(); ++i) + { + type temp = m(i); + if (dlib::impl::magnitude(temp) > dlib::impl::magnitude(val)) + { + val = temp; + best_idx = i; + } + } + return best_idx; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + long index_of_min ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0 && is_vector(m), + "\tlong index_of_min(const matrix_exp& m)" + << "\n\tm must be a row or column matrix" + << "\n\tm.size(): " << m.size() + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + typedef typename matrix_exp<EXP>::type type; + + type val = m(0); + long best_idx = 0; + for (long i = 1; i < m.size(); ++i) + { + type temp = m(i); + if (dlib::impl::magnitude(temp) < dlib::impl::magnitude(val)) + { + val = temp; + best_idx = i; + } + } + return best_idx; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const typename matrix_exp<EXP>::type max ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0, + "\ttype max(const matrix_exp& m)" + << "\n\tYou can't ask for the max() of an empty matrix" + << "\n\tm.size(): " << m.size() + ); + typedef typename matrix_exp<EXP>::type type; + + type val = m(0,0); + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + type temp = m(r,c); + if (dlib::impl::magnitude(temp) > dlib::impl::magnitude(val)) + val = temp; + } + } + return val; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const typename matrix_exp<EXP>::type min ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0, + "\ttype min(const matrix_exp& m)" + << "\n\tYou can't ask for the min() of an empty matrix" + << "\n\tm.size(): " << m.size() + ); + typedef typename matrix_exp<EXP>::type type; + + type val = m(0,0); + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + type temp = m(r,c); + if (dlib::impl::magnitude(temp) < dlib::impl::magnitude(val)) + val = temp; + } + } + return val; + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_binary_min : basic_op_mm<M1,M2> + { + op_binary_min( const M1& m1_, const M2& m2_) : basic_op_mm<M1,M2>(m1_,m2_){} + + typedef typename M1::type type; + typedef const type const_ret_type; + const static long cost = M1::cost + M2::cost + 1; + + const_ret_type apply ( long r, long c) const + { return std::min(this->m1(r,c),this->m2(r,c)); } + }; + + template < + typename EXP1, + typename EXP2 + > + inline const matrix_op<op_binary_min<EXP1,EXP2> > min_pointwise ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NC == 0 || EXP2::NC == 0); + DLIB_ASSERT(a.nr() == b.nr() && + a.nc() == b.nc(), + "\t const matrix_exp min_pointwise(const matrix_exp& a, const matrix_exp& b)" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nr(): " << b.nr() + << "\n\tb.nc(): " << b.nc() + ); + typedef op_binary_min<EXP1,EXP2> op; + return matrix_op<op>(op(a.ref(),b.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, typename M3> + struct op_min_pointwise3 : basic_op_mmm<M1,M2,M3> + { + op_min_pointwise3( const M1& m1_, const M2& m2_, const M3& m3_) : + basic_op_mmm<M1,M2,M3>(m1_,m2_,m3_){} + + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + const static long cost = M1::cost + M2::cost + M3::cost + 2; + + const_ret_type apply (long r, long c) const + { return std::min(this->m1(r,c),std::min(this->m2(r,c),this->m3(r,c))); } + }; + + template < + typename EXP1, + typename EXP2, + typename EXP3 + > + inline const matrix_op<op_min_pointwise3<EXP1,EXP2,EXP3> > + min_pointwise ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b, + const matrix_exp<EXP3>& c + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT((is_same_type<typename EXP2::type,typename EXP3::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NR == 0 || EXP2::NC == 0); + COMPILE_TIME_ASSERT(EXP2::NR == EXP3::NR || EXP2::NR == 0 || EXP3::NR == 0); + COMPILE_TIME_ASSERT(EXP2::NC == EXP3::NC || EXP2::NC == 0 || EXP3::NC == 0); + DLIB_ASSERT(a.nr() == b.nr() && + a.nc() == b.nc() && + b.nr() == c.nr() && + b.nc() == c.nc(), + "\tconst matrix_exp min_pointwise(a,b,c)" + << "\n\tYou can only make a do a pointwise min between equally sized matrices" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nr(): " << b.nr() + << "\n\tb.nc(): " << b.nc() + << "\n\tc.nr(): " << c.nr() + << "\n\tc.nc(): " << c.nc() + ); + + typedef op_min_pointwise3<EXP1,EXP2,EXP3> op; + return matrix_op<op>(op(a.ref(),b.ref(),c.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_binary_max : basic_op_mm<M1,M2> + { + op_binary_max( const M1& m1_, const M2& m2_) : basic_op_mm<M1,M2>(m1_,m2_){} + + typedef typename M1::type type; + typedef const type const_ret_type; + const static long cost = M1::cost + M2::cost + 1; + + const_ret_type apply ( long r, long c) const + { return std::max(this->m1(r,c),this->m2(r,c)); } + }; + + template < + typename EXP1, + typename EXP2 + > + inline const matrix_op<op_binary_max<EXP1,EXP2> > max_pointwise ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NC == 0 || EXP2::NC == 0); + DLIB_ASSERT(a.nr() == b.nr() && + a.nc() == b.nc(), + "\t const matrix_exp max_pointwise(const matrix_exp& a, const matrix_exp& b)" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nr(): " << b.nr() + << "\n\tb.nc(): " << b.nc() + ); + typedef op_binary_max<EXP1,EXP2> op; + return matrix_op<op>(op(a.ref(),b.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, typename M3> + struct op_max_pointwise3 : basic_op_mmm<M1,M2,M3> + { + op_max_pointwise3( const M1& m1_, const M2& m2_, const M3& m3_) : + basic_op_mmm<M1,M2,M3>(m1_,m2_,m3_){} + + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + const static long cost = M1::cost + M2::cost + M3::cost + 2; + + const_ret_type apply (long r, long c) const + { return std::max(this->m1(r,c),std::max(this->m2(r,c),this->m3(r,c))); } + }; + + template < + typename EXP1, + typename EXP2, + typename EXP3 + > + inline const matrix_op<op_max_pointwise3<EXP1,EXP2,EXP3> > + max_pointwise ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b, + const matrix_exp<EXP3>& c + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT((is_same_type<typename EXP2::type,typename EXP3::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NR == 0 || EXP2::NC == 0); + COMPILE_TIME_ASSERT(EXP2::NR == EXP3::NR || EXP2::NR == 0 || EXP3::NR == 0); + COMPILE_TIME_ASSERT(EXP2::NC == EXP3::NC || EXP2::NC == 0 || EXP3::NC == 0); + DLIB_ASSERT(a.nr() == b.nr() && + a.nc() == b.nc() && + b.nr() == c.nr() && + b.nc() == c.nc(), + "\tconst matrix_exp max_pointwise(a,b,c)" + << "\n\tYou can only make a do a pointwise max between equally sized matrices" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nr(): " << b.nr() + << "\n\tb.nc(): " << b.nc() + << "\n\tc.nr(): " << c.nr() + << "\n\tc.nc(): " << c.nc() + ); + + typedef op_max_pointwise3<EXP1,EXP2,EXP3> op; + return matrix_op<op>(op(a.ref(),b.ref(),c.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + typename enable_if_c<std::numeric_limits<typename EXP::type>::is_integer, double>::type length ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(is_vector(m) == true, + "\ttype length(const matrix_exp& m)" + << "\n\tm must be a row or column vector" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + + return std::sqrt(static_cast<double>(sum(squared(m)))); + } + + template < + typename EXP + > + typename disable_if_c<std::numeric_limits<typename EXP::type>::is_integer, const typename EXP::type>::type length ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(is_vector(m) == true, + "\ttype length(const matrix_exp& m)" + << "\n\tm must be a row or column vector" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + return std::sqrt(sum(squared(m))); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const typename matrix_exp<EXP>::type length_squared ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(is_vector(m) == true, + "\ttype length_squared(const matrix_exp& m)" + << "\n\tm must be a row or column vector" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + return sum(squared(m)); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_trans + { + op_trans( const M& m_) : m(m_){} + + const M& m; + + const static long cost = M::cost; + const static long NR = M::NC; + const static long NC = M::NR; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + const_ret_type apply (long r, long c) const { return m(c,r); } + + long nr () const { return m.nc(); } + long nc () const { return m.nr(); } + + 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.aliases(item); } + + }; + + template < + typename M + > + const matrix_op<op_trans<M> > trans ( + const matrix_exp<M>& m + ) + { + typedef op_trans<M> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + +// don't to anything at all for diagonal matrices + template < + typename M + > + const matrix_diag_exp<M>& trans ( + const matrix_diag_exp<M>& m + ) + { + return m; + } + +// ---------------------------------------------------------------------------------------- + +// I introduced this struct because it avoids an inane compiler warning from gcc + template <typename EXP> + struct is_not_ct_vector{ static const bool value = (EXP::NR != 1 && EXP::NC != 1); }; + + template < + typename EXP1, + typename EXP2 + > + typename enable_if_c<(is_not_ct_vector<EXP1>::value) || (is_not_ct_vector<EXP2>::value), + typename EXP1::type>::type + dot ( + const matrix_exp<EXP1>& m1, + const matrix_exp<EXP2>& m2 + ) + { + // You are getting an error on this line because you are trying to + // compute the dot product between two matrices that aren't both vectors (i.e. + // they aren't column or row matrices). + COMPILE_TIME_ASSERT(EXP1::NR*EXP1::NC == 0 || + EXP2::NR*EXP2::NC == 0); + + DLIB_ASSERT(is_vector(m1) && is_vector(m2) && m1.size() == m2.size() && + m1.size() > 0, + "\t type dot(const matrix_exp& m1, const matrix_exp& m2)" + << "\n\t You can only compute the dot product between non-empty vectors of equal length." + << "\n\t is_vector(m1): " << is_vector(m1) + << "\n\t is_vector(m2): " << is_vector(m2) + << "\n\t m1.size(): " << m1.size() + << "\n\t m2.size(): " << m2.size() + ); + + if (is_col_vector(m1) && is_col_vector(m2)) return (trans(m1)*m2)(0); + if (is_col_vector(m1) && is_row_vector(m2)) return (m2*m1)(0); + if (is_row_vector(m1) && is_col_vector(m2)) return (m1*m2)(0); + + //if (is_row_vector(m1) && is_row_vector(m2)) + return (m1*trans(m2))(0); + } + + template < typename EXP1, typename EXP2 > + typename enable_if_c<EXP1::NR == 1 && EXP2::NR == 1 && EXP1::NC != 1 && EXP2::NC != 1, typename EXP1::type>::type + dot ( const matrix_exp<EXP1>& m1, const matrix_exp<EXP2>& m2) + { + DLIB_ASSERT(m1.size() == m2.size(), + "\t type dot(const matrix_exp& m1, const matrix_exp& m2)" + << "\n\t You can only compute the dot product between vectors of equal length" + << "\n\t m1.size(): " << m1.size() + << "\n\t m2.size(): " << m2.size() + ); + + return m1*trans(m2); + } + + template < typename EXP1, typename EXP2 > + typename enable_if_c<EXP1::NR == 1 && EXP2::NC == 1 && EXP1::NC != 1 && EXP2::NR != 1, typename EXP1::type>::type + dot ( const matrix_exp<EXP1>& m1, const matrix_exp<EXP2>& m2) + { + DLIB_ASSERT(m1.size() == m2.size(), + "\t type dot(const matrix_exp& m1, const matrix_exp& m2)" + << "\n\t You can only compute the dot product between vectors of equal length" + << "\n\t m1.size(): " << m1.size() + << "\n\t m2.size(): " << m2.size() + ); + + return m1*m2; + } + + template < typename EXP1, typename EXP2 > + typename enable_if_c<EXP1::NC == 1 && EXP2::NR == 1 && EXP1::NR != 1 && EXP2::NC != 1, typename EXP1::type>::type + dot ( const matrix_exp<EXP1>& m1, const matrix_exp<EXP2>& m2) + { + DLIB_ASSERT(m1.size() == m2.size(), + "\t type dot(const matrix_exp& m1, const matrix_exp& m2)" + << "\n\t You can only compute the dot product between vectors of equal length" + << "\n\t m1.size(): " << m1.size() + << "\n\t m2.size(): " << m2.size() + ); + + return m2*m1; + } + + template < typename EXP1, typename EXP2 > + typename enable_if_c<EXP1::NC == 1 && EXP2::NC == 1 && EXP1::NR != 1 && EXP2::NR != 1, typename EXP1::type>::type + dot ( const matrix_exp<EXP1>& m1, const matrix_exp<EXP2>& m2) + { + DLIB_ASSERT(m1.size() == m2.size(), + "\t type dot(const matrix_exp& m1, const matrix_exp& m2)" + << "\n\t You can only compute the dot product between vectors of equal length" + << "\n\t m1.size(): " << m1.size() + << "\n\t m2.size(): " << m2.size() + ); + + return trans(m1)*m2; + } + + template < typename EXP1, typename EXP2 > + typename enable_if_c<(EXP1::NC*EXP1::NR == 1) || (EXP2::NC*EXP2::NR == 1), typename EXP1::type>::type + dot ( const matrix_exp<EXP1>& m1, const matrix_exp<EXP2>& m2) + { + DLIB_ASSERT(m1.size() == m2.size(), + "\t type dot(const matrix_exp& m1, const matrix_exp& m2)" + << "\n\t You can only compute the dot product between vectors of equal length" + << "\n\t m1.size(): " << m1.size() + << "\n\t m2.size(): " << m2.size() + ); + + return m1(0)*m2(0); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, long R, long C> + struct op_removerc + { + op_removerc( const M& m_) : m(m_){} + + const M& m; + + const static long cost = M::cost+2; + const static long NR = (M::NR==0) ? 0 : (M::NR - 1); + const static long NC = (M::NC==0) ? 0 : (M::NC - 1); + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply (long r, long c) const + { + if (r < R) + { + if (c < C) + return m(r,c); + else + return m(r,c+1); + } + else + { + if (c < C) + return m(r+1,c); + else + return m(r+1,c+1); + } + } + + long nr () const { return m.nr() - 1; } + long nc () const { return m.nc() - 1; } + + 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.aliases(item); } + }; + + template <typename M> + struct op_removerc2 + { + op_removerc2( const M& m_, const long R_, const long C_) : m(m_), R(R_), C(C_){} + const M& m; + const long R; + const long C; + + const static long cost = M::cost+2; + const static long NR = (M::NR==0) ? 0 : (M::NR - 1); + const static long NC = (M::NC==0) ? 0 : (M::NC - 1); + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply (long r, long c) const + { + if (r < R) + { + if (c < C) + return m(r,c); + else + return m(r,c+1); + } + else + { + if (c < C) + return m(r+1,c); + else + return m(r+1,c+1); + } + } + + long nr () const { return m.nr() - 1; } + long nc () const { return m.nc() - 1; } + + 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.aliases(item); } + }; + + template < + long R, + long C, + typename EXP + > + const matrix_op<op_removerc<EXP,R,C> > removerc ( + const matrix_exp<EXP>& m + ) + { + // you can't remove a row from a matrix with only one row + COMPILE_TIME_ASSERT((EXP::NR > R && R >= 0) || EXP::NR == 0); + // you can't remove a column from a matrix with only one column + COMPILE_TIME_ASSERT((EXP::NC > C && C >= 0) || EXP::NR == 0); + DLIB_ASSERT(m.nr() > R && R >= 0 && m.nc() > C && C >= 0, + "\tconst matrix_exp removerc<R,C>(const matrix_exp& m)" + << "\n\tYou can't remove a row/column from a matrix if it doesn't have that row/column" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tR: " << R + << "\n\tC: " << C + ); + typedef op_removerc<EXP,R,C> op; + return matrix_op<op>(op(m.ref())); + } + + template < + typename EXP + > + const matrix_op<op_removerc2<EXP> > removerc ( + const matrix_exp<EXP>& m, + long R, + long C + ) + { + DLIB_ASSERT(m.nr() > R && R >= 0 && m.nc() > C && C >= 0, + "\tconst matrix_exp removerc(const matrix_exp& m,R,C)" + << "\n\tYou can't remove a row/column from a matrix if it doesn't have that row/column" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tR: " << R + << "\n\tC: " << C + ); + typedef op_removerc2<EXP> op; + return matrix_op<op>(op(m.ref(),R,C)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, long C> + struct op_remove_col + { + op_remove_col( const M& m_) : m(m_){} + const M& m; + + const static long cost = M::cost+2; + const static long NR = M::NR; + const static long NC = (M::NC==0) ? 0 : (M::NC - 1); + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long c) const + { + if (c < C) + { + return m(r,c); + } + else + { + return m(r,c+1); + } + } + + long nr () const { return m.nr(); } + long nc () const { return m.nc() - 1; } + + 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.aliases(item); } + }; + + template <typename M> + struct op_remove_col2 + { + op_remove_col2( const M& m_, const long C_) : m(m_), C(C_){} + const M& m; + const long C; + + const static long cost = M::cost+2; + const static long NR = M::NR; + const static long NC = (M::NC==0) ? 0 : (M::NC - 1); + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long c) const + { + if (c < C) + { + return m(r,c); + } + else + { + return m(r,c+1); + } + } + + long nr () const { return m.nr(); } + long nc () const { return m.nc() - 1; } + + 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.aliases(item); } + }; + + template < + long C, + typename EXP + > + const matrix_op<op_remove_col<EXP, C> > remove_col ( + const matrix_exp<EXP>& m + ) + { + // You can't remove the given column from the matrix because the matrix doesn't + // have a column with that index. + COMPILE_TIME_ASSERT((EXP::NC > C && C >= 0) || EXP::NC == 0); + DLIB_ASSERT(m.nc() > C && C >= 0 , + "\tconst matrix_exp remove_col<C>(const matrix_exp& m)" + << "\n\tYou can't remove a col from a matrix if it doesn't have it" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tC: " << C + ); + typedef op_remove_col<EXP,C> op; + return matrix_op<op>(op(m.ref())); + } + + template < + typename EXP + > + const matrix_op<op_remove_col2<EXP> > remove_col ( + const matrix_exp<EXP>& m, + long C + ) + { + DLIB_ASSERT(m.nc() > C && C >= 0 , + "\tconst matrix_exp remove_col(const matrix_exp& m,C)" + << "\n\tYou can't remove a col from a matrix if it doesn't have it" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tC: " << C + ); + typedef op_remove_col2<EXP> op; + return matrix_op<op>(op(m.ref(),C)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, long R> + struct op_remove_row + { + op_remove_row( const M& m_) : m(m_){} + const M& m; + + const static long cost = M::cost+2; + const static long NR = (M::NR==0) ? 0 : (M::NR - 1); + const static long NC = M::NC; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long c) const + { + if (r < R) + { + return m(r,c); + } + else + { + return m(r+1,c); + } + } + + long nr () const { return m.nr() - 1; } + long nc () const { return m.nc(); } + + 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.aliases(item); } + }; + + template <typename M> + struct op_remove_row2 + { + op_remove_row2( const M& m_, const long R_) : m(m_), R(R_){} + const M& m; + const long R; + + const static long cost = M::cost+2; + const static long NR = (M::NR==0) ? 0 : (M::NR - 1); + const static long NC = M::NC; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long c) const + { + if (r < R) + { + return m(r,c); + } + else + { + return m(r+1,c); + } + } + + long nr () const { return m.nr() - 1; } + long nc () const { return m.nc(); } + + 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.aliases(item); } + }; + + template < + long R, + typename EXP + > + const matrix_op<op_remove_row<EXP,R> > remove_row ( + const matrix_exp<EXP>& m + ) + { + // You can't remove the given row from the matrix because the matrix doesn't + // have a row with that index. + COMPILE_TIME_ASSERT((EXP::NR > R && R >= 0) || EXP::NR == 0); + DLIB_ASSERT(m.nr() > R && R >= 0, + "\tconst matrix_exp remove_row<R>(const matrix_exp& m)" + << "\n\tYou can't remove a row from a matrix if it doesn't have it" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tR: " << R + ); + typedef op_remove_row<EXP,R> op; + return matrix_op<op>(op(m.ref())); + } + + template < + typename EXP + > + const matrix_op<op_remove_row2<EXP> > remove_row ( + const matrix_exp<EXP>& m, + long R + ) + { + DLIB_ASSERT(m.nr() > R && R >= 0, + "\tconst matrix_exp remove_row(const matrix_exp& m, long R)" + << "\n\tYou can't remove a row from a matrix if it doesn't have it" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tR: " << R + ); + typedef op_remove_row2<EXP> op; + return matrix_op<op>(op(m.ref(),R)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_diagm + { + op_diagm( const M& m_) : m(m_){} + const M& m; + + const static long cost = M::cost+2; + const static long N = M::NC*M::NR; + const static long NR = N; + const static long NC = N; + typedef typename M::type type; + typedef const typename M::type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long c) const + { + if (r==c) + return m(r); + else + return 0; + } + + long nr () const { return (m.nr()>m.nc())? m.nr():m.nc(); } + long nc () const { return (m.nr()>m.nc())? m.nr():m.nc(); } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_diag_op<op_diagm<EXP> > diagm ( + const matrix_exp<EXP>& m + ) + { + // You can only make a diagonal matrix out of a row or column vector + COMPILE_TIME_ASSERT(EXP::NR == 0 || EXP::NR == 1 || EXP::NC == 1 || EXP::NC == 0); + DLIB_ASSERT(is_vector(m), + "\tconst matrix_exp diagm(const matrix_exp& m)" + << "\n\tYou can only apply diagm() to a row or column matrix" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + typedef op_diagm<EXP> op; + return matrix_diag_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_diagm_mult : basic_op_mm<M1,M2> + { + op_diagm_mult( const M1& m1_, const M2& m2_) : basic_op_mm<M1,M2>(m1_,m2_){} + + typedef typename M1::type type; + typedef const type const_ret_type; + const static long cost = M1::cost + M2::cost + 1; + + const_ret_type apply ( long r, long c) const + { + if (r == c) + return this->m1(r,c)*this->m2(r,c); + else + return 0; + } + }; + + template < + typename EXP1, + typename EXP2 + > + inline const matrix_diag_op<op_diagm_mult<EXP1,EXP2> > operator* ( + const matrix_diag_exp<EXP1>& a, + const matrix_diag_exp<EXP2>& b + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type, typename EXP2::type>::value)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NC == 0 || EXP2::NC == 0); + DLIB_ASSERT(a.nr() == b.nr() && + a.nc() == b.nc(), + "\tconst matrix_exp operator(const matrix_diag_exp& a, const matrix_diag_exp& b)" + << "\n\tYou can only multiply diagonal matrices together if they are the same size" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nr(): " << b.nr() + << "\n\tb.nc(): " << b.nc() + ); + typedef op_diagm_mult<EXP1,EXP2> op; + return matrix_diag_op<op>(op(a.ref(),b.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_diag + { + op_diag( const M& m_) : m(m_){} + const M& m; + + const static long cost = M::cost; + const static long NR = tmin<M::NR,M::NC>::value; + const static long NC = 1; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long ) const { return m(r,r); } + + long nr () const { return std::min(m.nc(),m.nr()); } + long nc () const { return 1; } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_diag<EXP> > diag ( + const matrix_exp<EXP>& m + ) + { + typedef op_diag<EXP> op; + return matrix_op<op>(op(m.ref())); + } + + template <typename EXP> + struct diag_exp + { + typedef matrix_op<op_diag<EXP> > type; + }; + +// ---------------------------------------------------------------------------------------- + + template <typename M, typename target_type> + struct op_cast + { + op_cast( const M& m_) : m(m_){} + const M& m; + + const static long cost = M::cost+2; + const static long NR = M::NR; + const static long NC = M::NC; + typedef target_type type; + typedef const target_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long c) const { return static_cast<target_type>(m(r,c)); } + + long nr () const { return m.nr(); } + long nc () const { return m.nc(); } + + 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); } + }; + + template < + typename target_type, + typename EXP + > + const matrix_op<op_cast<EXP, target_type> > matrix_cast ( + const matrix_exp<EXP>& m + ) + { + typedef op_cast<EXP, target_type> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename type, typename S> + inline type lessthan(const type& val, const S& s) + { + if (val < s) + return 1; + else + return 0; + } + + } + DLIB_DEFINE_OP_MS(op_lessthan, impl::lessthan, 1); + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_lessthan<EXP,S> > >::type operator< ( + const matrix_exp<EXP>& m, + const S& s + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_lessthan<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_lessthan<EXP,S> > >::type operator> ( + const S& s, + const matrix_exp<EXP>& m + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_lessthan<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename type, typename S> + inline type lessthan_eq(const type& val, const S& s) + { + if (val <= s) + return 1; + else + return 0; + } + + } + DLIB_DEFINE_OP_MS(op_lessthan_eq, impl::lessthan_eq, 1); + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_lessthan_eq<EXP,S> > >::type operator<= ( + const matrix_exp<EXP>& m, + const S& s + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_lessthan_eq<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_lessthan_eq<EXP,S> > >::type operator>= ( + const S& s, + const matrix_exp<EXP>& m + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_lessthan_eq<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename type, typename S> + inline type greaterthan(const type& val, const S& s) + { + if (val > s) + return 1; + else + return 0; + } + + } + DLIB_DEFINE_OP_MS(op_greaterthan, impl::greaterthan, 1); + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_greaterthan<EXP,S> > >::type operator> ( + const matrix_exp<EXP>& m, + const S& s + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_greaterthan<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_greaterthan<EXP,S> > >::type operator< ( + const S& s, + const matrix_exp<EXP>& m + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_greaterthan<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename type, typename S> + inline type greaterthan_eq(const type& val, const S& s) + { + if (val >= s) + return 1; + else + return 0; + } + + } + DLIB_DEFINE_OP_MS(op_greaterthan_eq, impl::greaterthan_eq, 1); + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_greaterthan_eq<EXP,S> > >::type operator>= ( + const matrix_exp<EXP>& m, + const S& s + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_greaterthan_eq<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_greaterthan_eq<EXP,S> > >::type operator<= ( + const S& s, + const matrix_exp<EXP>& m + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_greaterthan_eq<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename type, typename S> + inline type equal_to(const type& val, const S& s) + { + if (val == s) + return 1; + else + return 0; + } + + } + DLIB_DEFINE_OP_MS(op_equal_to, impl::equal_to, 1); + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_equal_to<EXP,S> > >::type operator== ( + const matrix_exp<EXP>& m, + const S& s + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT( is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_equal_to<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_equal_to<EXP,S> > >::type operator== ( + const S& s, + const matrix_exp<EXP>& m + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT( is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_equal_to<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + template <typename type, typename S> + inline type not_equal_to(const type& val, const S& s) + { + if (val != s) + return 1; + else + return 0; + } + + } + DLIB_DEFINE_OP_MS(op_not_equal_to, impl::not_equal_to, 1); + + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_not_equal_to<EXP,S> > >::type operator!= ( + const matrix_exp<EXP>& m, + const S& s + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_not_equal_to<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + + template < + typename EXP, + typename S + > + const typename enable_if<is_built_in_scalar_type<S>, matrix_op<op_not_equal_to<EXP,S> > >::type operator!= ( + const S& s, + const matrix_exp<EXP>& m + ) + { + // you can only use this relational operator with the built in scalar types like + // long, float, etc. + COMPILE_TIME_ASSERT(is_built_in_scalar_type<typename EXP::type>::value); + + typedef op_not_equal_to<EXP,S> op; + return matrix_op<op>(op(m.ref(),s)); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename MM, + typename U, + typename L + > + typename disable_if<is_matrix<U>,void>::type set_all_elements ( + matrix<T,NR,NC,MM,L>& m, + const U& value + ) + { + // The value you are trying to assign to each element of the m matrix + // doesn't have the appropriate type. + COMPILE_TIME_ASSERT(is_matrix<T>::value == is_matrix<U>::value); + + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + m(r,c) = static_cast<T>(value); + } + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename MM, + typename U, + typename L + > + typename enable_if<is_matrix<U>,void>::type set_all_elements ( + matrix<T,NR,NC,MM,L>& m, + const U& value + ) + { + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + m(r,c) = value; + } + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + inline const typename matrix_exp<EXP>::matrix_type tmp ( + const matrix_exp<EXP>& m + ) + { + return typename matrix_exp<EXP>::matrix_type (m); + } + +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + constexpr bool is_row_major ( + const matrix_exp<EXP>& + ) + { + return is_same_type<typename EXP::layout_type,row_major_layout>::value; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const typename lazy_disable_if<is_matrix<typename EXP::type>, EXP>::type sum ( + const matrix_exp<EXP>& m + ) + { + typedef typename matrix_exp<EXP>::type type; + + type val = 0; + if (is_row_major(m)) + { + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + val += m(r,c); + } + } + } + else + { + for (long c = 0; c < m.nc(); ++c) + { + for (long r = 0; r < m.nr(); ++r) + { + val += m(r,c); + } + } + } + return val; + } + + template < + typename EXP + > + const typename lazy_enable_if<is_matrix<typename EXP::type>, EXP>::type sum ( + const matrix_exp<EXP>& m + ) + { + typedef typename matrix_exp<EXP>::type type; + + type val; + if (m.size() > 0) + val.set_size(m(0,0).nr(),m(0,0).nc()); + set_all_elements(val,0); + + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + val += m(r,c); + } + } + return val; + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_sumr + { + op_sumr(const M& m_) : m(m_) {} + const M& m; + + const static long cost = M::cost+10; + const static long NR = 1; + const static long NC = M::NC; + typedef typename M::type type; + typedef const typename M::type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long , long c) const + { + type temp = m(0,c); + for (long r = 1; r < m.nr(); ++r) + temp += m(r,c); + return temp; + } + + long nr () const { return 1; } + long nc () const { return m.nc(); } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_sumr<EXP> > sum_rows ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0 , + "\tconst matrix_exp sum_rows(m)" + << "\n\t The matrix can't be empty" + << "\n\t m.size(): " << m.size() + ); + typedef op_sumr<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_sumc + { + op_sumc(const M& m_) : m(m_) {} + const M& m; + + const static long cost = M::cost + 10; + const static long NR = M::NR; + const static long NC = 1; + typedef typename M::type type; + typedef const typename M::type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long ) const + { + type temp = m(r,0); + for (long c = 1; c < m.nc(); ++c) + temp += m(r,c); + return temp; + } + + long nr () const { return m.nr(); } + long nc () const { return 1; } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_sumc<EXP> > sum_cols ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.size() > 0 , + "\tconst matrix_exp sum_cols(m)" + << "\n\t The matrix can't be empty" + << "\n\t m.size(): " << m.size() + ); + typedef op_sumc<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + inline const typename disable_if<is_complex<typename EXP::type>, typename matrix_exp<EXP>::type>::type mean ( + const matrix_exp<EXP>& m + ) + { + return sum(m)/(m.nr()*m.nc()); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + inline const typename enable_if<is_complex<typename EXP::type>, typename matrix_exp<EXP>::type>::type mean ( + const matrix_exp<EXP>& m + ) + { + typedef typename EXP::type::value_type type; + return sum(m)/(type)(m.nr()*m.nc()); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const typename matrix_exp<EXP>::type variance ( + const matrix_exp<EXP>& m + ) + { + using std::pow; + using dlib::pow; + const typename matrix_exp<EXP>::type avg = mean(m); + + typedef typename matrix_exp<EXP>::type type; + + type val; + val = 0; + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + val += pow(m(r,c) - avg,2); + } + } + + if (m.nr() * m.nc() <= 1) + { + return val; + } + else + { + // Note, for some reason, in gcc 4.1 performing this division using a + // double instead of a long value avoids a segmentation fault. That is, + // using 1.0 instead of 1 does the trick. + return val/(m.nr()*m.nc() - 1.0); + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const typename matrix_exp<EXP>::type stddev ( + const matrix_exp<EXP>& m + ) + { + using std::sqrt; + using dlib::sqrt; + return sqrt(variance(m)); + } + +// ---------------------------------------------------------------------------------------- + +// this is a workaround for a bug in visual studio 7.1 + template <typename EXP> + struct visual_studio_sucks_cov_helper + { + typedef typename EXP::type inner_type; + typedef matrix<typename inner_type::type, inner_type::NR, inner_type::NR, typename EXP::mem_manager_type> type; + }; + + template < + typename EXP + > + const typename visual_studio_sucks_cov_helper<EXP>::type covariance ( + const matrix_exp<EXP>& m + ) + { + // perform static checks to make sure m is a column vector + COMPILE_TIME_ASSERT(EXP::NR == 0 || EXP::NR > 1); + COMPILE_TIME_ASSERT(EXP::NC == 1 || EXP::NC == 0); + + // perform static checks to make sure the matrices contained in m are column vectors + COMPILE_TIME_ASSERT(EXP::type::NC == 1 || EXP::type::NC == 0 ); + + DLIB_ASSERT(m.size() > 1 && is_col_vector(m), + "\tconst matrix covariance(const matrix_exp& m)" + << "\n\tYou can only apply covariance() to a column matrix" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); +#ifdef ENABLE_ASSERTS + for (long i = 0; i < m.nr(); ++i) + { + DLIB_ASSERT(m(0).size() == m(i).size() && m(i).size() > 0 && is_col_vector(m(i)), + "\tconst matrix covariance(const matrix_exp& m)" + << "\n\tYou can only apply covariance() to a column matrix of column matrices" + << "\n\tm(0).size(): " << m(0).size() + << "\n\tm(i).size(): " << m(i).size() + << "\n\tis_col_vector(m(i)): " << (is_col_vector(m(i)) ? "true" : "false") + << "\n\ti: " << i + ); + } +#endif + + // now perform the actual calculation of the covariance matrix. + typename visual_studio_sucks_cov_helper<EXP>::type cov(m(0).nr(),m(0).nr()); + set_all_elements(cov,0); + + const typename EXP::type avg = mean(m); + + for (long r = 0; r < m.nr(); ++r) + { + cov += (m(r) - avg)*trans(m(r) - avg); + } + + cov *= 1.0 / (m.nr() - 1.0); + return cov; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const typename matrix_exp<EXP>::type prod ( + const matrix_exp<EXP>& m + ) + { + typedef typename matrix_exp<EXP>::type type; + + type val = 1; + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + val *= m(r,c); + } + } + return val; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + struct op_uniform_matrix_3 : does_not_alias + { + op_uniform_matrix_3(const long& rows_, const long& cols_, const T& val_ ) : + rows(rows_), cols(cols_), val(val_) {} + + const long rows; + const long cols; + const T val; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 0; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + typedef T type; + typedef const T& const_ret_type; + const_ret_type apply (long, long ) const { return val; } + + long nr() const { return rows; } + long nc() const { return cols; } + }; + + template < + typename T + > + const matrix_op<op_uniform_matrix_3<T> > uniform_matrix ( + long nr, + long nc, + const T& val + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0, + "\tconst matrix_exp uniform_matrix<T>(nr, nc, val)" + << "\n\tnr and nc have to be bigger than 0" + << "\n\tnr: " << nr + << "\n\tnc: " << nc + ); + typedef op_uniform_matrix_3<T> op; + return matrix_op<op>(op(nr, nc, val)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_op<op_uniform_matrix_3<T> > zeros_matrix ( + long nr, + long nc + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0, + "\tconst matrix_exp zeros_matrix<T>(nr, nc)" + << "\n\tnr and nc have to be >= 0" + << "\n\tnr: " << nr + << "\n\tnc: " << nc + ); + typedef op_uniform_matrix_3<T> op; + return matrix_op<op>(op(nr, nc, 0)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix_op<op_uniform_matrix_3<typename EXP::type> > zeros_matrix ( + const matrix_exp<EXP>& mat + ) + { + DLIB_ASSERT(mat.nr() >= 0 && mat.nc() >= 0, + "\tconst matrix_exp zeros_matrix(mat)" + << "\n\t nr and nc have to be >= 0" + << "\n\t mat.nr(): " << mat.nr() + << "\n\t mat.nc(): " << mat.nc() + ); + typedef typename EXP::type T; + typedef op_uniform_matrix_3<T> op; + return matrix_op<op>(op(mat.nr(), mat.nc(), 0)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_op<op_uniform_matrix_3<T> > ones_matrix ( + long nr, + long nc + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0, + "\tconst matrix_exp ones_matrix<T>(nr, nc)" + << "\n\tnr and nc have to be >= 0" + << "\n\tnr: " << nr + << "\n\tnc: " << nc + ); + typedef op_uniform_matrix_3<T> op; + return matrix_op<op>(op(nr, nc, 1)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + const matrix_op<op_uniform_matrix_3<typename EXP::type> > ones_matrix ( + const matrix_exp<EXP>& mat + ) + { + DLIB_ASSERT(mat.nr() >= 0 && mat.nc() >= 0, + "\tconst matrix_exp ones_matrix(mat)" + << "\n\t nr and nc have to be >= 0" + << "\n\t mat.nr(): " << mat.nr() + << "\n\t mat.nc(): " << mat.nc() + ); + typedef typename EXP::type T; + typedef op_uniform_matrix_3<T> op; + return matrix_op<op>(op(mat.nr(), mat.nc(), 1)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR_, + long NC_ + > + struct op_uniform_matrix_2 : does_not_alias + { + op_uniform_matrix_2( const T& val_ ) : val(val_) {} + const T val; + + const static long cost = 1; + const static long NR = NR_; + const static long NC = NC_; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + typedef T type; + typedef const T& const_ret_type; + + const_ret_type apply (long , long ) const { return val; } + + long nr() const { return NR; } + long nc() const { return NC; } + }; + + template < + typename T, + long NR, + long NC + > + const matrix_op<op_uniform_matrix_2<T,NR,NC> > uniform_matrix ( + const T& val + ) + { + COMPILE_TIME_ASSERT(NR > 0 && NC > 0); + + typedef op_uniform_matrix_2<T,NR,NC> op; + return matrix_op<op>(op(val)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR_, + long NC_, + T val + > + struct op_uniform_matrix : does_not_alias + { + const static long cost = 1; + const static long NR = NR_; + const static long NC = NC_; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + typedef T type; + typedef const T const_ret_type; + const_ret_type apply ( long , long ) const { return val; } + + long nr() const { return NR; } + long nc() const { return NC; } + }; + + template < + typename T, + long NR, + long NC, + T val + > + const matrix_op<op_uniform_matrix<T,NR,NC,val> > uniform_matrix ( + ) + { + COMPILE_TIME_ASSERT(NR > 0 && NC > 0); + typedef op_uniform_matrix<T,NR,NC,val> op; + return matrix_op<op>(op()); + } + +// ---------------------------------------------------------------------------------------- + + struct op_gaussian_randm : does_not_alias + { + op_gaussian_randm ( + long nr_, + long nc_, + unsigned long seed_ + ) :_nr(nr_), _nc(nc_), seed(seed_){} + + const long _nr; + const long _nc; + const unsigned long seed; + + const static long cost = 100; + const static long NR = 0; + const static long NC = 0; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + typedef double type; + typedef double const_ret_type; + const_ret_type apply ( long r, long c) const { return gaussian_random_hash(r,c,seed); } + + long nr() const { return _nr; } + long nc() const { return _nc; } + }; + + inline const matrix_op<op_gaussian_randm> gaussian_randm ( + long nr, + long nc, + unsigned long seed = 0 + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0, + "\tmatrix_exp gaussian_randm(nr, nc, seed)" + << "\n\tInvalid inputs to this function" + << "\n\tnr: " << nr + << "\n\tnc: " << nc + ); + + typedef op_gaussian_randm op; + return matrix_op<op>(op(nr,nc,seed)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_add_diag + { + op_add_diag( const M& m_, const typename M::type& value_) : m(m_), value(value_){} + const M& m; + const typename M::type value; + + const static long cost = M::cost+1; + const static long NR = M::NR; + const static long NC = M::NC; + typedef typename M::type type; + typedef const typename M::type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long c) const + { + if (r==c) + return m(r,c)+value; + else + return m(r,c); + } + + long nr () const { return m.nr(); } + long nc () const { return m.nc(); } + + 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); } + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + struct op_identity_matrix_2 : does_not_alias + { + op_identity_matrix_2(const long& size_) : size(size_) {} + + const long size; + + const static long cost = 1; + const static long NR = 0; + const static long NC = 0; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + typedef T type; + typedef const T const_ret_type; + const_ret_type apply (long r, long c) const { return static_cast<type>(r == c); } + + long nr() const { return size; } + long nc() const { return size; } + }; + + template < + typename T, + typename U + > + const matrix_diag_op<op_identity_matrix_2<T> > identity_matrix ( + const U& size + ) + { + // the size argument must be some scalar value, not a matrix! + COMPILE_TIME_ASSERT(is_matrix<U>::value == false); + + DLIB_ASSERT(size > 0, + "\tconst matrix_exp identity_matrix<T>(size)" + << "\n\tsize must be bigger than 0" + << "\n\tsize: " << size + ); + typedef op_identity_matrix_2<T> op; + return matrix_diag_op<op>(op(size)); + } + + template < + typename EXP + > + const matrix_diag_op<op_identity_matrix_2<typename EXP::type> > identity_matrix ( + const matrix_exp<EXP>& mat + ) + { + DLIB_ASSERT(mat.nr() == mat.nc(), + "\tconst matrix_exp identity_matrix(mat)" + << "\n\t mat must be a square matrix." + << "\n\t mat.nr(): " << mat.nr() + << "\n\t mat.nc(): " << mat.nc() + ); + typedef typename EXP::type T; + typedef op_identity_matrix_2<T> op; + return matrix_diag_op<op>(op(mat.nr())); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + typename T + > + const matrix_op<op_add_diag<EXP> > operator+ ( + const matrix_exp<EXP>& lhs, + const matrix_exp<matrix_diag_op<op_identity_matrix_2<T> > >& DLIB_IF_ASSERT(rhs) + ) + { + // both matrices must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<T,typename EXP::type>::value == true)); + + // You can only add matrices together if they both have the same number of rows and columns. + 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 + ); + + + typedef op_add_diag<EXP> op; + return matrix_op<op>(op(lhs.ref(),1)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + typename T + > + const matrix_op<op_add_diag<EXP> > operator+ ( + const matrix_exp<matrix_diag_op<op_identity_matrix_2<T> > >& DLIB_IF_ASSERT(lhs), + const matrix_exp<EXP>& rhs + ) + { + // both matrices must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<T,typename EXP::type>::value == true)); + + // You can only add matrices together if they both have the same number of rows and columns. + 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 + ); + + + typedef op_add_diag<EXP> op; + return matrix_op<op>(op(rhs.ref(),1)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long N + > + struct op_const_diag_matrix : does_not_alias + { + op_const_diag_matrix(const long& size_, const T& value_) : size(size_),value(value_) {} + + const long size; + const T value; + + const static long cost = 1; + const static long NR = N; + const static long NC = N; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + typedef T type; + typedef const T const_ret_type; + const_ret_type apply (long r, long c) const + { + if (r == c) + return value; + else + return 0; + } + + long nr() const { return size; } + long nc() const { return size; } + }; + + template < + typename T, + typename U + > + const typename disable_if<is_matrix<U>, matrix_diag_op<op_const_diag_matrix<T,0> > >::type operator* ( + const matrix_exp<matrix_diag_op<op_identity_matrix_2<T> > >& m, + const U& value + ) + { + typedef op_const_diag_matrix<T,0> op; + return matrix_diag_op<op>(op(m.nr(), value)); + } + + template < + typename T, + typename U + > + const typename disable_if<is_matrix<U>, matrix_diag_op<op_const_diag_matrix<T,0> > >::type operator* ( + const U& value, + const matrix_exp<matrix_diag_op<op_identity_matrix_2<T> > >& m + ) + { + typedef op_const_diag_matrix<T,0> op; + return matrix_diag_op<op>(op(m.nr(), value)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + typename T, + long N + > + const matrix_op<op_add_diag<EXP> > operator+ ( + const matrix_exp<EXP>& lhs, + const matrix_exp<matrix_diag_op<op_const_diag_matrix<T,N> > >& rhs + ) + { + // both matrices must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<T,typename EXP::type>::value == true)); + + // You can only add matrices together if they both have the same number of rows and columns. + 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 + ); + + + typedef op_add_diag<EXP> op; + return matrix_op<op>(op(lhs.ref(),rhs.ref().op.value)); + } + + template < + typename EXP, + typename T, + long N + > + const matrix_op<op_add_diag<EXP> > operator+ ( + const matrix_exp<matrix_diag_op<op_const_diag_matrix<T,N> > >& lhs, + const matrix_exp<EXP>& rhs + ) + { + // both matrices must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<T,typename EXP::type>::value == true)); + + // You can only add matrices together if they both have the same number of rows and columns. + 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 + ); + + + typedef op_add_diag<EXP> op; + return matrix_op<op>(op(rhs.ref(),lhs.ref().op.value)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long N + > + struct op_identity_matrix : does_not_alias + { + const static long cost = 1; + const static long NR = N; + const static long NC = N; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + typedef T type; + typedef const T const_ret_type; + const_ret_type apply ( long r, long c) const { return static_cast<type>(r == c); } + + long nr () const { return NR; } + long nc () const { return NC; } + }; + + template < + typename T, + long N + > + const matrix_diag_op<op_identity_matrix<T,N> > identity_matrix ( + ) + { + COMPILE_TIME_ASSERT(N > 0); + + typedef op_identity_matrix<T,N> op; + return matrix_diag_op<op>(op()); + } + + template < + typename T, + typename U, + long N + > + const typename disable_if<is_matrix<U>, matrix_diag_op<op_const_diag_matrix<T,N> > >::type operator* ( + const matrix_exp<matrix_diag_op<op_identity_matrix<T,N> > >& m, + const U& value + ) + { + typedef op_const_diag_matrix<T,N> op; + return matrix_diag_op<op>(op(m.nr(), value)); + } + + template < + typename T, + typename U, + long N + > + const typename disable_if<is_matrix<U>, matrix_diag_op<op_const_diag_matrix<T,N> > >::type operator* ( + const U& value, + const matrix_exp<matrix_diag_op<op_identity_matrix<T,N> > >& m + ) + { + typedef op_const_diag_matrix<T,N> op; + return matrix_diag_op<op>(op(m.nr(), value)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + typename T, + long N + > + const matrix_op<op_add_diag<EXP> > operator+ ( + const matrix_exp<matrix_diag_op<op_identity_matrix<T,N> > >& DLIB_IF_ASSERT(lhs), + const matrix_exp<EXP>& rhs + ) + { + // both matrices must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<T,typename EXP::type>::value == true)); + + // You can only add matrices together if they both have the same number of rows and columns. + 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 + ); + + + typedef op_add_diag<EXP> op; + return matrix_op<op>(op(rhs.ref(),1)); + } + + template < + typename EXP, + typename T, + long N + > + const matrix_op<op_add_diag<EXP> > operator+ ( + const matrix_exp<EXP>& lhs, + const matrix_exp<matrix_diag_op<op_identity_matrix<T,N> > >& DLIB_IF_ASSERT(rhs) + ) + { + // both matrices must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<T,typename EXP::type>::value == true)); + + // You can only add matrices together if they both have the same number of rows and columns. + 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 + ); + + + typedef op_add_diag<EXP> op; + return matrix_op<op>(op(lhs.ref(),1)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, long R, long C> + struct op_rotate + { + op_rotate(const M& m_) : m(m_) {} + const M& m; + + const static long cost = M::cost + 2; + const static long NR = M::NR; + const static long NC = M::NC; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + const_ret_type apply ( long r, long c) const { return m((r+R)%m.nr(),(c+C)%m.nc()); } + + long nr () const { return m.nr(); } + long nc () const { return m.nc(); } + + 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.aliases(item); } + }; + + template < + long R, + long C, + typename EXP + > + const matrix_op<op_rotate<EXP,R,C> > rotate ( + const matrix_exp<EXP>& m + ) + { + typedef op_rotate<EXP,R,C> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + // A template to tell me if two types can be multiplied together in a sensible way. Here + // I'm saying it is ok if they are both the same type or one is the complex version of the other. + template <typename T, typename U> struct compatible { static const bool value = false; typedef T type; }; + template <typename T> struct compatible<T,T> { static const bool value = true; typedef T type; }; + template <typename T> struct compatible<std::complex<T>,T> { static const bool value = true; typedef std::complex<T> type; }; + template <typename T> struct compatible<T,std::complex<T> > { static const bool value = true; typedef std::complex<T> type; }; + } + + + template <typename M1, typename M2> + struct op_pointwise_multiply : basic_op_mm<M1,M2> + { + op_pointwise_multiply( const M1& m1_, const M2& m2_) : basic_op_mm<M1,M2>(m1_,m2_){} + + typedef typename impl::compatible<typename M1::type, typename M2::type>::type type; + typedef const type const_ret_type; + const static long cost = M1::cost + M2::cost + 1; + + const_ret_type apply ( long r, long c) const + { return this->m1(r,c)*this->m2(r,c); } + }; + + template < + typename EXP1, + typename EXP2 + > + inline const matrix_op<op_pointwise_multiply<EXP1,EXP2> > pointwise_multiply ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b + ) + { + COMPILE_TIME_ASSERT((impl::compatible<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NC == 0 || EXP2::NC == 0); + DLIB_ASSERT(a.nr() == b.nr() && + a.nc() == b.nc(), + "\tconst matrix_exp pointwise_multiply(const matrix_exp& a, const matrix_exp& b)" + << "\n\tYou can only make a do a pointwise multiply with two equally sized matrices" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nr(): " << b.nr() + << "\n\tb.nc(): " << b.nc() + ); + typedef op_pointwise_multiply<EXP1,EXP2> op; + return matrix_op<op>(op(a.ref(),b.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, typename M3> + struct op_pointwise_multiply3 : basic_op_mmm<M1,M2,M3> + { + op_pointwise_multiply3( const M1& m1_, const M2& m2_, const M3& m3_) : + basic_op_mmm<M1,M2,M3>(m1_,m2_,m3_){} + + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + const static long cost = M1::cost + M2::cost + M3::cost + 2; + + const_ret_type apply (long r, long c) const + { return this->m1(r,c)*this->m2(r,c)*this->m3(r,c); } + }; + + template < + typename EXP1, + typename EXP2, + typename EXP3 + > + inline const matrix_op<op_pointwise_multiply3<EXP1,EXP2,EXP3> > + pointwise_multiply ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b, + const matrix_exp<EXP3>& c + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT((is_same_type<typename EXP2::type,typename EXP3::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NR == 0 || EXP2::NC == 0); + COMPILE_TIME_ASSERT(EXP2::NR == EXP3::NR || EXP2::NR == 0 || EXP3::NR == 0); + COMPILE_TIME_ASSERT(EXP2::NC == EXP3::NC || EXP2::NC == 0 || EXP3::NC == 0); + DLIB_ASSERT(a.nr() == b.nr() && + a.nc() == b.nc() && + b.nr() == c.nr() && + b.nc() == c.nc(), + "\tconst matrix_exp pointwise_multiply(a,b,c)" + << "\n\tYou can only make a do a pointwise multiply between equally sized matrices" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nr(): " << b.nr() + << "\n\tb.nc(): " << b.nc() + << "\n\tc.nr(): " << c.nr() + << "\n\tc.nc(): " << c.nc() + ); + + typedef op_pointwise_multiply3<EXP1,EXP2,EXP3> op; + return matrix_op<op>(op(a.ref(),b.ref(),c.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, typename M3, typename M4> + struct op_pointwise_multiply4 : basic_op_mmmm<M1,M2,M3,M4> + { + op_pointwise_multiply4( const M1& m1_, const M2& m2_, const M3& m3_, const M4& m4_) : + basic_op_mmmm<M1,M2,M3,M4>(m1_,m2_,m3_,m4_){} + + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + const static long cost = M1::cost + M2::cost + M3::cost + M4::cost + 3; + + const_ret_type apply (long r, long c) const + { return this->m1(r,c)*this->m2(r,c)*this->m3(r,c)*this->m4(r,c); } + }; + + + template < + typename EXP1, + typename EXP2, + typename EXP3, + typename EXP4 + > + inline const matrix_op<op_pointwise_multiply4<EXP1,EXP2,EXP3,EXP4> > pointwise_multiply ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b, + const matrix_exp<EXP3>& c, + const matrix_exp<EXP4>& d + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT((is_same_type<typename EXP2::type,typename EXP3::type>::value == true)); + COMPILE_TIME_ASSERT((is_same_type<typename EXP3::type,typename EXP4::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NC == 0 || EXP2::NC == 0 ); + COMPILE_TIME_ASSERT(EXP2::NR == EXP3::NR || EXP2::NR == 0 || EXP3::NR == 0); + COMPILE_TIME_ASSERT(EXP2::NC == EXP3::NC || EXP2::NC == 0 || EXP3::NC == 0); + COMPILE_TIME_ASSERT(EXP3::NR == EXP4::NR || EXP3::NR == 0 || EXP4::NR == 0); + COMPILE_TIME_ASSERT(EXP3::NC == EXP4::NC || EXP3::NC == 0 || EXP4::NC == 0); + DLIB_ASSERT(a.nr() == b.nr() && + a.nc() == b.nc() && + b.nr() == c.nr() && + b.nc() == c.nc() && + c.nr() == d.nr() && + c.nc() == d.nc(), + "\tconst matrix_exp pointwise_multiply(a,b,c,d)" + << "\n\tYou can only make a do a pointwise multiply between equally sized matrices" + << "\n\ta.nr(): " << a.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nr(): " << b.nr() + << "\n\tb.nc(): " << b.nc() + << "\n\tc.nr(): " << c.nr() + << "\n\tc.nc(): " << c.nc() + << "\n\td.nr(): " << d.nr() + << "\n\td.nc(): " << d.nc() + ); + + typedef op_pointwise_multiply4<EXP1,EXP2,EXP3,EXP4> op; + return matrix_op<op>(op(a.ref(),b.ref(),c.ref(),d.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename P, + int type = static_switch< + pixel_traits<P>::grayscale, + pixel_traits<P>::rgb, + pixel_traits<P>::hsi, + pixel_traits<P>::rgb_alpha, + pixel_traits<P>::lab + >::value + > + struct pixel_to_vector_helper; + + template <typename P> + struct pixel_to_vector_helper<P,1> + { + template <typename M> + static void assign ( + M& m, + const P& pixel + ) + { + m(0) = static_cast<typename M::type>(pixel); + } + }; + + template <typename P> + struct pixel_to_vector_helper<P,2> + { + template <typename M> + static void assign ( + M& m, + const P& pixel + ) + { + m(0) = static_cast<typename M::type>(pixel.red); + m(1) = static_cast<typename M::type>(pixel.green); + m(2) = static_cast<typename M::type>(pixel.blue); + } + }; + + template <typename P> + struct pixel_to_vector_helper<P,3> + { + template <typename M> + static void assign ( + M& m, + const P& pixel + ) + { + m(0) = static_cast<typename M::type>(pixel.h); + m(1) = static_cast<typename M::type>(pixel.s); + m(2) = static_cast<typename M::type>(pixel.i); + } + }; + + template <typename P> + struct pixel_to_vector_helper<P,4> + { + template <typename M> + static void assign ( + M& m, + const P& pixel + ) + { + m(0) = static_cast<typename M::type>(pixel.red); + m(1) = static_cast<typename M::type>(pixel.green); + m(2) = static_cast<typename M::type>(pixel.blue); + m(3) = static_cast<typename M::type>(pixel.alpha); + } + }; + + template <typename P> + struct pixel_to_vector_helper<P,5> + { + template <typename M> + static void assign ( + M& m, + const P& pixel + ) + { + m(0) = static_cast<typename M::type>(pixel.l); + m(1) = static_cast<typename M::type>(pixel.a); + m(2) = static_cast<typename M::type>(pixel.b); + } + }; + + + template < + typename T, + typename P + > + inline const matrix<T,pixel_traits<P>::num,1> pixel_to_vector ( + const P& pixel + ) + { + COMPILE_TIME_ASSERT(pixel_traits<P>::num > 0); + matrix<T,pixel_traits<P>::num,1> m; + pixel_to_vector_helper<P>::assign(m,pixel); + return m; + } + +// ---------------------------------------------------------------------------------------- + + template < + typename P, + int type = static_switch< + pixel_traits<P>::grayscale, + pixel_traits<P>::rgb, + pixel_traits<P>::hsi, + pixel_traits<P>::rgb_alpha, + pixel_traits<P>::lab + >::value + > + struct vector_to_pixel_helper; + + template <typename P> + struct vector_to_pixel_helper<P,1> + { + template <typename M> + static void assign ( + P& pixel, + const M& m + ) + { + pixel = static_cast<unsigned char>(m(0)); + } + }; + + template <typename P> + struct vector_to_pixel_helper<P,2> + { + template <typename M> + static void assign ( + P& pixel, + const M& m + ) + { + pixel.red = static_cast<unsigned char>(m(0)); + pixel.green = static_cast<unsigned char>(m(1)); + pixel.blue = static_cast<unsigned char>(m(2)); + } + }; + + template <typename P> + struct vector_to_pixel_helper<P,3> + { + template <typename M> + static void assign ( + P& pixel, + const M& m + ) + { + pixel.h = static_cast<unsigned char>(m(0)); + pixel.s = static_cast<unsigned char>(m(1)); + pixel.i = static_cast<unsigned char>(m(2)); + } + }; + + template <typename P> + struct vector_to_pixel_helper<P,4> + { + template <typename M> + static void assign ( + P& pixel, + const M& m + ) + { + pixel.red = static_cast<unsigned char>(m(0)); + pixel.green = static_cast<unsigned char>(m(1)); + pixel.blue = static_cast<unsigned char>(m(2)); + pixel.alpha = static_cast<unsigned char>(m(3)); + } + }; + + template <typename P> + struct vector_to_pixel_helper<P,5> + { + template <typename M> + static void assign ( + P& pixel, + const M& m + ) + { + pixel.l = static_cast<unsigned char>(m(0)); + pixel.a = static_cast<unsigned char>(m(1)); + pixel.b = static_cast<unsigned char>(m(2)); + } + }; + + template < + typename P, + typename EXP + > + inline void vector_to_pixel ( + P& pixel, + const matrix_exp<EXP>& vector + ) + { + COMPILE_TIME_ASSERT(pixel_traits<P>::num == matrix_exp<EXP>::NR); + COMPILE_TIME_ASSERT(matrix_exp<EXP>::NC == 1); + vector_to_pixel_helper<P>::assign(pixel,vector); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, long lower, long upper> + struct op_clamp : basic_op_m<M> + { + op_clamp( const M& m_) : basic_op_m<M>(m_){} + + typedef typename M::type type; + typedef const typename M::type const_ret_type; + const static long cost = M::cost + 2; + + const_ret_type apply ( long r, long c) const + { + const type temp = this->m(r,c); + if (temp > static_cast<type>(upper)) + return static_cast<type>(upper); + else if (temp < static_cast<type>(lower)) + return static_cast<type>(lower); + else + return temp; + } + }; + + template < + long l, + long u, + typename EXP + > + const matrix_op<op_clamp<EXP,l,u> > clamp ( + const matrix_exp<EXP>& m + ) + { + typedef op_clamp<EXP,l,u> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_clamp2 : basic_op_m<M> + { + typedef typename M::type type; + + op_clamp2( const M& m_, const type& l, const type& u) : + basic_op_m<M>(m_), lower(l), upper(u){} + + const type& lower; + const type& upper; + + typedef const typename M::type const_ret_type; + const static long cost = M::cost + 2; + + const_ret_type apply ( long r, long c) const + { + const type temp = this->m(r,c); + if (temp > upper) + return upper; + else if (temp < lower) + return lower; + else + return temp; + } + }; + + template < + typename EXP + > + const matrix_op<op_clamp2<EXP> > clamp ( + const matrix_exp<EXP>& m, + const typename EXP::type& lower, + const typename EXP::type& upper + ) + { + typedef op_clamp2<EXP> op; + return matrix_op<op>(op(m.ref(),lower, upper)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2, typename M3> + struct op_clamp_m : basic_op_mmm<M1,M2,M3> + { + op_clamp_m( const M1& m1_, const M2& m2_, const M3& m3_) : + basic_op_mmm<M1,M2,M3>(m1_,m2_,m3_){} + + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + const static long cost = M1::cost + M2::cost + M3::cost + 2; + + const_ret_type apply (long r, long c) const + { + const type val = this->m1(r,c); + const type lower = this->m2(r,c); + const type upper = this->m3(r,c); + if (val <= upper) + { + if (lower <= val) + return val; + else + return lower; + } + else + { + return upper; + } + } + }; + + template < + typename EXP1, + typename EXP2, + typename EXP3 + > + const matrix_op<op_clamp_m<EXP1,EXP2,EXP3> > + clamp ( + const matrix_exp<EXP1>& m, + const matrix_exp<EXP2>& lower, + const matrix_exp<EXP3>& upper + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + COMPILE_TIME_ASSERT((is_same_type<typename EXP2::type,typename EXP3::type>::value == true)); + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || EXP1::NR == 0 || EXP2::NR == 0); + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || EXP1::NR == 0 || EXP2::NC == 0); + COMPILE_TIME_ASSERT(EXP2::NR == EXP3::NR || EXP2::NR == 0 || EXP3::NR == 0); + COMPILE_TIME_ASSERT(EXP2::NC == EXP3::NC || EXP2::NC == 0 || EXP3::NC == 0); + DLIB_ASSERT(m.nr() == lower.nr() && + m.nc() == lower.nc() && + m.nr() == upper.nr() && + m.nc() == upper.nc(), + "\tconst matrix_exp clamp(m,lower,upper)" + << "\n\t Invalid inputs were given to this function." + << "\n\t m.nr(): " << m.nr() + << "\n\t m.nc(): " << m.nc() + << "\n\t lower.nr(): " << lower.nr() + << "\n\t lower.nc(): " << lower.nc() + << "\n\t upper.nr(): " << upper.nr() + << "\n\t upper.nc(): " << upper.nc() + ); + + typedef op_clamp_m<EXP1,EXP2,EXP3> op; + return matrix_op<op>(op(m.ref(),lower.ref(),upper.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_lowerbound : basic_op_m<M> + { + typedef typename M::type type; + + op_lowerbound( const M& m_, const type& thresh_) : + basic_op_m<M>(m_), thresh(thresh_){} + + const type& thresh; + + typedef const typename M::type const_ret_type; + const static long cost = M::cost + 2; + + const_ret_type apply ( long r, long c) const + { + const type temp = this->m(r,c); + if (temp >= thresh) + return temp; + else + return thresh; + } + }; + + template < + typename EXP + > + const matrix_op<op_lowerbound<EXP> > lowerbound ( + const matrix_exp<EXP>& m, + const typename EXP::type& thresh + ) + { + typedef op_lowerbound<EXP> op; + return matrix_op<op>(op(m.ref(), thresh)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_upperbound : basic_op_m<M> + { + typedef typename M::type type; + + op_upperbound( const M& m_, const type& thresh_) : + basic_op_m<M>(m_), thresh(thresh_){} + + const type& thresh; + + typedef const typename M::type const_ret_type; + const static long cost = M::cost + 2; + + const_ret_type apply ( long r, long c) const + { + const type temp = this->m(r,c); + if (temp <= thresh) + return temp; + else + return thresh; + } + }; + + template < + typename EXP + > + const matrix_op<op_upperbound<EXP> > upperbound ( + const matrix_exp<EXP>& m, + const typename EXP::type& thresh + ) + { + typedef op_upperbound<EXP> op; + return matrix_op<op>(op(m.ref(), thresh)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_reshape + { + op_reshape(const M& m_, const long& rows_, const long& cols_) : m(m_),rows(rows_),cols(cols_) {} + const M& m; + const long rows; + const long cols; + + const static long cost = M::cost+2; + const static long NR = 0; + const static long NC = 0; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + const_ret_type apply ( long r, long c) const + { + const long idx = r*cols + c; + return m(idx/m.nc(), idx%m.nc()); + } + + long nr () const { return rows; } + long nc () const { return cols; } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_reshape<EXP> > reshape ( + const matrix_exp<EXP>& m, + const long& rows, + const long& cols + ) + { + DLIB_ASSERT(m.size() == rows*cols && rows > 0 && cols > 0, + "\tconst matrix_exp reshape(m, rows, cols)" + << "\n\t The size of m must match the dimensions you want to reshape it into." + << "\n\t m.size(): " << m.size() + << "\n\t rows*cols: " << rows*cols + << "\n\t rows: " << rows + << "\n\t cols: " << cols + ); + + typedef op_reshape<EXP> op; + return matrix_op<op>(op(m.ref(), rows, cols)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP1, + typename EXP2 + > + typename disable_if<is_complex<typename EXP1::type>,bool>::type equal ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b, + const typename EXP1::type eps = 100*std::numeric_limits<typename EXP1::type>::epsilon() + ) + { + // check if the dimensions don't match + if (a.nr() != b.nr() || a.nc() != b.nc()) + return false; + + for (long r = 0; r < a.nr(); ++r) + { + for (long c = 0; c < a.nc(); ++c) + { + if (std::abs(a(r,c)-b(r,c)) > eps) + return false; + } + } + + // no non-equal points found so we return true + return true; + } + + template < + typename EXP1, + typename EXP2 + > + typename enable_if<is_complex<typename EXP1::type>,bool>::type equal ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b, + const typename EXP1::type::value_type eps = 100*std::numeric_limits<typename EXP1::type::value_type>::epsilon() + ) + { + // check if the dimensions don't match + if (a.nr() != b.nr() || a.nc() != b.nc()) + return false; + + for (long r = 0; r < a.nr(); ++r) + { + for (long c = 0; c < a.nc(); ++c) + { + if (std::abs(real(a(r,c)-b(r,c))) > eps || + std::abs(imag(a(r,c)-b(r,c))) > eps) + return false; + } + } + + // no non-equal points found so we return true + return true; + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_scale_columns + { + op_scale_columns(const M1& m1_, const M2& m2_) : m1(m1_), m2(m2_) {} + const M1& m1; + const M2& m2; + + const static long cost = M1::cost + M2::cost + 1; + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + const static long NR = M1::NR; + const static long NC = M1::NC; + + const_ret_type apply ( long r, long c) const { return m1(r,c)*m2(c); } + + long nr () const { return m1.nr(); } + long nc () const { return m1.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item) ; } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.destructively_aliases(item) || m2.aliases(item); } + }; + + template < + typename EXP1, + typename EXP2 + > + const matrix_op<op_scale_columns<EXP1,EXP2> > scale_columns ( + const matrix_exp<EXP1>& m, + const matrix_exp<EXP2>& v + ) + { + // Both arguments to this function must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + // The v argument must be a row or column vector. + COMPILE_TIME_ASSERT((EXP2::NC == 1 || EXP2::NC == 0) || (EXP2::NR == 1 || EXP2::NR == 0)); + + // figure out the compile time known length of v + const long v_len = ((EXP2::NR)*(EXP2::NC) == 0)? 0 : (tmax<EXP2::NR,EXP2::NC>::value); + + // the length of v must match the number of columns in m + COMPILE_TIME_ASSERT(EXP1::NC == v_len || EXP1::NC == 0 || v_len == 0); + + DLIB_ASSERT(is_vector(v) == true && v.size() == m.nc(), + "\tconst matrix_exp scale_columns(m, v)" + << "\n\tv must be a row or column vector and its length must match the number of columns in m" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tv.nr(): " << v.nr() + << "\n\tv.nc(): " << v.nc() + ); + typedef op_scale_columns<EXP1,EXP2> op; + return matrix_op<op>(op(m.ref(),v.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_scale_columns_diag + { + op_scale_columns_diag(const M1& m1_, const M2& m2_) : m1(m1_), m2(m2_) {} + const M1& m1; + const M2& m2; + + const static long cost = M1::cost + M2::cost + 1; + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + const static long NR = M1::NR; + const static long NC = M1::NC; + + const_ret_type apply ( long r, long c) const { return m1(r,c)*m2(c,c); } + + long nr () const { return m1.nr(); } + long nc () const { return m1.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item) ; } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.destructively_aliases(item) || m2.aliases(item); } + }; + +// turn expressions of the form mat*diagonal_matrix into scale_columns(mat, d) + template < + typename EXP1, + typename EXP2 + > + const matrix_op<op_scale_columns_diag<EXP1,EXP2> > operator* ( + const matrix_exp<EXP1>& m, + const matrix_diag_exp<EXP2>& d + ) + { + // Both arguments to this function must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + + // figure out the compile time known length of d + const long v_len = ((EXP2::NR)*(EXP2::NC) == 0)? 0 : (tmax<EXP2::NR,EXP2::NC>::value); + + // the length of d must match the number of columns in m + COMPILE_TIME_ASSERT(EXP1::NC == v_len || EXP1::NC == 0 || v_len == 0); + + DLIB_ASSERT(m.nc() == d.nr(), + "\tconst matrix_exp operator*(m, d)" + << "\n\tmatrix dimensions don't match" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\td.nr(): " << d.nr() + << "\n\td.nc(): " << d.nc() + ); + typedef op_scale_columns_diag<EXP1,EXP2> op; + return matrix_op<op>(op(m.ref(),d.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_scale_rows + { + op_scale_rows(const M1& m1_, const M2& m2_) : m1(m1_), m2(m2_) {} + const M1& m1; + const M2& m2; + + const static long cost = M1::cost + M2::cost + 1; + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + const static long NR = M1::NR; + const static long NC = M1::NC; + + const_ret_type apply ( long r, long c) const { return m1(r,c)*m2(r); } + + long nr () const { return m1.nr(); } + long nc () const { return m1.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item) ; } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.destructively_aliases(item) || m2.aliases(item); } + }; + + template < + typename EXP1, + typename EXP2 + > + const matrix_op<op_scale_rows<EXP1,EXP2> > scale_rows ( + const matrix_exp<EXP1>& m, + const matrix_exp<EXP2>& v + ) + { + // Both arguments to this function must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + // The v argument must be a row or column vector. + COMPILE_TIME_ASSERT((EXP2::NC == 1 || EXP2::NC == 0) || (EXP2::NR == 1 || EXP2::NR == 0)); + + // figure out the compile time known length of v + const long v_len = ((EXP2::NR)*(EXP2::NC) == 0)? 0 : (tmax<EXP2::NR,EXP2::NC>::value); + + // the length of v must match the number of rows in m + COMPILE_TIME_ASSERT(EXP1::NR == v_len || EXP1::NR == 0 || v_len == 0); + + DLIB_ASSERT(is_vector(v) == true && v.size() == m.nr(), + "\tconst matrix_exp scale_rows(m, v)" + << "\n\tv must be a row or column vector and its length must match the number of rows in m" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tv.nr(): " << v.nr() + << "\n\tv.nc(): " << v.nc() + ); + typedef op_scale_rows<EXP1,EXP2> op; + return matrix_op<op>(op(m.ref(),v.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_scale_rows_diag + { + op_scale_rows_diag(const M1& m1_, const M2& m2_) : m1(m1_), m2(m2_) {} + const M1& m1; + const M2& m2; + + const static long cost = M1::cost + M2::cost + 1; + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + const static long NR = M1::NR; + const static long NC = M1::NC; + + const_ret_type apply ( long r, long c) const { return m1(r,c)*m2(r,r); } + + long nr () const { return m1.nr(); } + long nc () const { return m1.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item) ; } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.destructively_aliases(item) || m2.aliases(item); } + }; + +// turn expressions of the form diagonal_matrix*mat into scale_rows(mat, d) + template < + typename EXP1, + typename EXP2 + > + const matrix_op<op_scale_rows_diag<EXP1,EXP2> > operator* ( + const matrix_diag_exp<EXP2>& d, + const matrix_exp<EXP1>& m + ) + { + // Both arguments to this function must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + + // figure out the compile time known length of d + const long v_len = ((EXP2::NR)*(EXP2::NC) == 0)? 0 : (tmax<EXP2::NR,EXP2::NC>::value); + + // the length of d must match the number of rows in m + COMPILE_TIME_ASSERT(EXP1::NR == v_len || EXP1::NR == 0 || v_len == 0); + + DLIB_ASSERT(d.nc() == m.nr(), + "\tconst matrix_exp operator*(d, m)" + << "\n\tThe dimensions of the d and m matrices don't match." + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\td.nr(): " << d.nr() + << "\n\td.nc(): " << d.nc() + ); + typedef op_scale_rows_diag<EXP1,EXP2> op; + return matrix_op<op>(op(m.ref(),d.ref())); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + /* + The idea here is to catch expressions of the form d*M*d where d is diagonal and M + is some square matrix and turn them into something equivalent to + pointwise_multiply(diag(d)*trans(diag(d)), M). + + The reason for this is that doing it this way is more numerically stable. In particular, + doing 2 matrix multiplies as suggested by d*M*d could result in an asymmetric matrix even + if M is symmetric to begin with. + */ + + template <typename M1, typename M2, typename M3> + struct op_diag_m_diag + { + // This operator represents M1*M2*M3 where M1 and M3 are diagonal + + op_diag_m_diag(const M1& m1_, const M2& m2_, const M3& m3_) : m1(m1_), m2(m2_), m3(m3_) {} + const M1& m1; + const M2& m2; + const M3& m3; + + const static long cost = M1::cost + M2::cost + M3::cost + 1; + typedef typename M2::type type; + typedef const typename M2::type const_ret_type; + typedef typename M2::mem_manager_type mem_manager_type; + typedef typename M2::layout_type layout_type; + const static long NR = M2::NR; + const static long NC = M2::NC; + + const_ret_type apply ( long r, long c) const { return (m1(r,r)*m3(c,c))*m2(r,c); } + + long nr () const { return m2.nr(); } + long nc () const { return m2.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item) || m3.aliases(item) ; } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m2.destructively_aliases(item) || m1.aliases(item) || m3.aliases(item) ; } + }; + + // catch d*(M*d) = EXP1*EXP2*EXP3 + template < + typename EXP1, + typename EXP2, + typename EXP3 + > + const matrix_op<op_diag_m_diag<EXP1,EXP2,EXP3> > operator* ( + const matrix_diag_exp<EXP1>& d, + const matrix_exp<matrix_op<op_scale_columns_diag<EXP2,EXP3> > >& m + ) + { + // Both arguments to this function must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + + // figure out the compile time known length of d + const long v_len = ((EXP1::NR)*(EXP1::NC) == 0)? 0 : (tmax<EXP1::NR,EXP1::NC>::value); + + // the length of d must match the number of rows in m + COMPILE_TIME_ASSERT(EXP2::NR == v_len || EXP2::NR == 0 || v_len == 0); + + DLIB_ASSERT(d.nc() == m.nr(), + "\tconst matrix_exp operator*(d, m)" + << "\n\tmatrix dimensions don't match" + << "\n\td.nr(): " << d.nr() + << "\n\td.nc(): " << d.nc() + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + ); + typedef op_diag_m_diag<EXP1,EXP2,EXP3> op; + return matrix_op<op>(op(d.ref(), m.ref().op.m1, m.ref().op.m2)); + } + + // catch (d*M)*d = EXP1*EXP2*EXP3 + template < + typename EXP1, + typename EXP2, + typename EXP3 + > + const matrix_op<op_diag_m_diag<EXP1,EXP2,EXP3> > operator* ( + const matrix_exp<matrix_op<op_scale_rows_diag<EXP2,EXP1> > >& m, + const matrix_diag_exp<EXP3>& d + ) + { + // Both arguments to this function must contain the same type of element + COMPILE_TIME_ASSERT((is_same_type<typename EXP3::type,typename EXP2::type>::value == true)); + + // figure out the compile time known length of d + const long v_len = ((EXP3::NR)*(EXP3::NC) == 0)? 0 : (tmax<EXP3::NR,EXP3::NC>::value); + + // the length of d must match the number of columns in m + COMPILE_TIME_ASSERT(EXP2::NC == v_len || EXP2::NC == 0 || v_len == 0); + + DLIB_ASSERT(m.nc() == d.nr(), + "\tconst matrix_exp operator*(m, d)" + << "\n\tmatrix dimensions don't match" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\td.nr(): " << d.nr() + << "\n\td.nc(): " << d.nc() + ); + typedef op_diag_m_diag<EXP1,EXP2,EXP3> op; + return matrix_op<op>(op(m.ref().op.m2, m.ref().op.m1, d.ref())); + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + struct sort_columns_sort_helper + { + template <typename T> + bool operator() ( + const T& item1, + const T& item2 + ) const + { + return item1.first < item2.first; + } + }; + + template < + typename T, long NR, long NC, typename mm, typename l1, + long NR2, long NC2, typename mm2, typename l2 + > + void sort_columns ( + matrix<T,NR,NC,mm,l1>& m, + matrix<T,NR2,NC2,mm2,l2>& v + ) + { + COMPILE_TIME_ASSERT(NC2 == 1 || NC2 == 0); + COMPILE_TIME_ASSERT(NC == NR2 || NC == 0 || NR2 == 0); + + DLIB_ASSERT(is_col_vector(v) == true && v.size() == m.nc(), + "\tconst matrix_exp sort_columns(m, v)" + << "\n\tv must be a column vector and its length must match the number of columns in m" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tv.nr(): " << v.nr() + << "\n\tv.nc(): " << v.nc() + ); + + + + // Now we have to sort the given vectors in the m matrix according + // to how big their corresponding v(column index) values are. + typedef std::pair<T, matrix<T,0,1,mm> > col_pair; + typedef std_allocator<col_pair, mm> alloc; + std::vector<col_pair,alloc> colvalues; + col_pair p; + for (long r = 0; r < v.nr(); ++r) + { + p.first = v(r); + p.second = colm(m,r); + colvalues.push_back(p); + } + std::sort(colvalues.begin(), colvalues.end(), sort_columns_sort_helper()); + + for (long i = 0; i < v.nr(); ++i) + { + v(i) = colvalues[i].first; + set_colm(m,i) = colvalues[i].second; + } + + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, long NR, long NC, typename mm, typename l1, + long NR2, long NC2, typename mm2, typename l2 + > + void rsort_columns ( + matrix<T,NR,NC,mm,l1>& m, + matrix<T,NR2,NC2,mm2,l2>& v + ) + { + COMPILE_TIME_ASSERT(NC2 == 1 || NC2 == 0); + COMPILE_TIME_ASSERT(NC == NR2 || NC == 0 || NR2 == 0); + + DLIB_ASSERT(is_col_vector(v) == true && v.size() == m.nc(), + "\tconst matrix_exp rsort_columns(m, v)" + << "\n\tv must be a column vector and its length must match the number of columns in m" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tv.nr(): " << v.nr() + << "\n\tv.nc(): " << v.nc() + ); + + + + // Now we have to sort the given vectors in the m matrix according + // to how big their corresponding v(column index) values are. + typedef std::pair<T, matrix<T,0,1,mm> > col_pair; + typedef std_allocator<col_pair, mm> alloc; + std::vector<col_pair,alloc> colvalues; + col_pair p; + for (long r = 0; r < v.nr(); ++r) + { + p.first = v(r); + p.second = colm(m,r); + colvalues.push_back(p); + } + std::sort(colvalues.rbegin(), colvalues.rend(), sort_columns_sort_helper()); + + for (long i = 0; i < v.nr(); ++i) + { + v(i) = colvalues[i].first; + set_colm(m,i) = colvalues[i].second; + } + + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_tensor_product + { + op_tensor_product(const M1& m1_, const M2& m2_) : m1(m1_),m2(m2_) {} + const M1& m1; + const M2& m2; + + const static long cost = M1::cost + M2::cost + 1; + const static long NR = M1::NR*M2::NR; + const static long NC = M1::NC*M2::NC; + typedef typename M1::type type; + typedef const typename M1::type const_ret_type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + const_ret_type apply ( long r, long c) const + { + return m1(r/m2.nr(),c/m2.nc())*m2(r%m2.nr(),c%m2.nc()); + } + + long nr () const { return m1.nr()*m2.nr(); } + long nc () const { return m1.nc()*m2.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + }; + + template < + typename EXP1, + typename EXP2 + > + inline const matrix_op<op_tensor_product<EXP1,EXP2> > tensor_product ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + typedef op_tensor_product<EXP1,EXP2> op; + return matrix_op<op>(op(a.ref(),b.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_make_symmetric : basic_op_m<M> + { + op_make_symmetric ( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost+1; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + const_ret_type apply ( long r, long c) const + { + if (r >= c) + return this->m(r,c); + else + return this->m(c,r); + } + }; + + template < + typename EXP + > + const matrix_op<op_make_symmetric<EXP> > make_symmetric ( + const matrix_exp<EXP>& m + ) + { + DLIB_ASSERT(m.nr() == m.nc(), + "\tconst matrix make_symmetric(m)" + << "\n\t m must be a square matrix" + << "\n\t m.nr(): " << m.nr() + << "\n\t m.nc(): " << m.nc() + ); + + typedef op_make_symmetric<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_lowerm : basic_op_m<M> + { + op_lowerm( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost+2; + typedef typename M::type type; + typedef const typename M::type const_ret_type; + const_ret_type apply ( long r, long c) const + { + if (r >= c) + return this->m(r,c); + else + return 0; + } + }; + + template <typename M> + struct op_lowerm_s : basic_op_m<M> + { + typedef typename M::type type; + op_lowerm_s( const M& m_, const type& s_) : basic_op_m<M>(m_), s(s_){} + + const type s; + + const static long cost = M::cost+2; + typedef const typename M::type const_ret_type; + const_ret_type apply ( long r, long c) const + { + if (r > c) + return this->m(r,c); + else if (r==c) + return s; + else + return 0; + } + }; + + template < + typename EXP + > + const matrix_op<op_lowerm<EXP> > lowerm ( + const matrix_exp<EXP>& m + ) + { + typedef op_lowerm<EXP> op; + return matrix_op<op>(op(m.ref())); + } + + template < + typename EXP + > + const matrix_op<op_lowerm_s<EXP> > lowerm ( + const matrix_exp<EXP>& m, + typename EXP::type s + ) + { + typedef op_lowerm_s<EXP> op; + return matrix_op<op>(op(m.ref(),s)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_upperm : basic_op_m<M> + { + op_upperm( const M& m_) : basic_op_m<M>(m_){} + + const static long cost = M::cost+2; + typedef typename M::type type; + typedef const typename M::type const_ret_type; + const_ret_type apply ( long r, long c) const + { + if (r <= c) + return this->m(r,c); + else + return 0; + } + }; + + template <typename M> + struct op_upperm_s : basic_op_m<M> + { + typedef typename M::type type; + op_upperm_s( const M& m_, const type& s_) : basic_op_m<M>(m_), s(s_){} + + const type s; + + const static long cost = M::cost+2; + typedef const typename M::type const_ret_type; + const_ret_type apply ( long r, long c) const + { + if (r < c) + return this->m(r,c); + else if (r==c) + return s; + else + return 0; + } + }; + + template < + typename EXP + > + const matrix_op<op_upperm<EXP> > upperm ( + const matrix_exp<EXP>& m + ) + { + typedef op_upperm<EXP> op; + return matrix_op<op>(op(m.ref())); + } + + template < + typename EXP + > + const matrix_op<op_upperm_s<EXP> > upperm ( + const matrix_exp<EXP>& m, + typename EXP::type s + ) + { + typedef op_upperm_s<EXP> op; + return matrix_op<op>(op(m.ref(),s)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename rand_gen> + inline const matrix<double> randm( + long nr, + long nc, + rand_gen& rnd + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0, + "\tconst matrix randm(nr, nc, rnd)" + << "\n\tInvalid inputs to this function" + << "\n\tnr: " << nr + << "\n\tnc: " << nc + ); + + matrix<double> m(nr,nc); + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + m(r,c) = rnd.get_random_double(); + } + } + + return m; + } + +// ---------------------------------------------------------------------------------------- + + inline const matrix<double> randm( + long nr, + long nc + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0, + "\tconst matrix randm(nr, nc)" + << "\n\tInvalid inputs to this function" + << "\n\tnr: " << nr + << "\n\tnc: " << nc + ); + + matrix<double> m(nr,nc); + // make a double that contains RAND_MAX + the smallest number that still + // makes the resulting double slightly bigger than static_cast<double>(RAND_MAX) + double max_val = RAND_MAX; + max_val += std::numeric_limits<double>::epsilon()*RAND_MAX; + + for (long r = 0; r < m.nr(); ++r) + { + for (long c = 0; c < m.nc(); ++c) + { + m(r,c) = std::rand()/max_val; + } + } + + return m; + } + +// ---------------------------------------------------------------------------------------- + + inline const matrix_range_exp<double> linspace ( + double start, + double end, + long num + ) + { + DLIB_ASSERT(num >= 0, + "\tconst matrix_exp linspace(start, end, num)" + << "\n\tInvalid inputs to this function" + << "\n\tstart: " << start + << "\n\tend: " << end + << "\n\tnum: " << num + ); + + return matrix_range_exp<double>(start,end,num,false); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_linpiece + { + op_linpiece(const double val_, const M& joints_) : joints(joints_), val(val_){} + + const M& joints; + const double val; + + const static long cost = 10; + + const static long NR = (M::NR*M::NC==0) ? (0) : (M::NR*M::NC-1); + const static long NC = 1; + typedef typename M::type type; + typedef default_memory_manager mem_manager_type; + typedef row_major_layout layout_type; + + typedef type const_ret_type; + const_ret_type apply (long i, long ) const + { + if (joints(i) < val) + return std::min<type>(val,joints(i+1)) - joints(i); + else + return 0; + } + + long nr () const { return joints.size()-1; } + long nc () const { return 1; } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const { return joints.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const { return joints.aliases(item); } + }; + + template < typename EXP > + const matrix_op<op_linpiece<EXP> > linpiece ( + const double val, + const matrix_exp<EXP>& joints + ) + { + // make sure requires clause is not broken + DLIB_ASSERT(is_vector(joints) && joints.size() >= 2, + "\t matrix_exp linpiece()" + << "\n\t Invalid inputs were given to this function " + << "\n\t is_vector(joints): " << is_vector(joints) + << "\n\t joints.size(): " << joints.size() + ); +#ifdef ENABLE_ASSERTS + for (long i = 1; i < joints.size(); ++i) + { + DLIB_ASSERT(joints(i-1) < joints(i), + "\t matrix_exp linpiece()" + << "\n\t Invalid inputs were given to this function " + << "\n\t joints("<<i-1<<"): " << joints(i-1) + << "\n\t joints("<<i<<"): " << joints(i) + ); + } +#endif + + typedef op_linpiece<EXP> op; + return matrix_op<op>(op(val,joints.ref())); + } + +// ---------------------------------------------------------------------------------------- + + inline const matrix_log_range_exp<double> logspace ( + double start, + double end, + long num + ) + { + DLIB_ASSERT(num >= 0, + "\tconst matrix_exp logspace(start, end, num)" + << "\n\tInvalid inputs to this function" + << "\n\tstart: " << start + << "\n\tend: " << end + << "\n\tnum: " << num + ); + + return matrix_log_range_exp<double>(start,end,num); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_cart_prod + { + op_cart_prod(const M1& m1_, const M2& m2_) : m1(m1_),m2(m2_) {} + const M1& m1; + const M2& m2; + + const static long cost = M1::cost+M2::cost+1; + typedef typename M1::type type; + typedef const typename M1::const_ret_type const_ret_type; + + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + const static long NR = M1::NR+M2::NR; + const static long NC = M1::NC*M2::NC; + + const_ret_type apply ( long r, long c) const + { + if (r < m1.nr()) + return m1(r, c/m2.nc()); + else + return m2(r-m1.nr(), c%m2.nc()); + } + + long nr () const { return m1.nr() + m2.nr(); } + long nc () const { return m1.nc() * m2.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + }; + + template < + typename EXP1, + typename EXP2 + > + const matrix_op<op_cart_prod<EXP1,EXP2> > cartesian_product ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + + typedef op_cart_prod<EXP1,EXP2> op; + return matrix_op<op>(op(a.ref(),b.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_mat_to_vect + { + op_mat_to_vect(const M& m_) : m(m_) {} + const M& m; + + const static long cost = M::cost+2; + const static long NR = M::NC*M::NR; + const static long NC = 1; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + const_ret_type apply ( long r, long ) const { return m(r/m.nc(), r%m.nc()); } + + long nr () const { return m.size(); } + long nc () const { return 1; } + + 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.aliases(item); } + }; + + template < + typename EXP + > + const matrix_op<op_mat_to_vect<EXP> > reshape_to_column_vector ( + const matrix_exp<EXP>& m + ) + { + typedef op_mat_to_vect<EXP> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR_, + long NC_, + typename MM + > + struct op_mat_to_vect2 + { + typedef matrix<T,NR_,NC_,MM,row_major_layout> M; + op_mat_to_vect2(const M& m_) : m(m_) {} + const M& m; + + const static long cost = M::cost+2; + const static long NR = M::NC*M::NR; + const static long NC = 1; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + const_ret_type apply ( long r, long ) const { return (&m(0,0))[r]; } + + long nr () const { return m.size(); } + long nc () const { return 1; } + + 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.aliases(item); } + }; + + template < + typename T, + long NR, + long NC, + typename MM + > + const matrix_op<op_mat_to_vect2<T,NR,NC,MM> > reshape_to_column_vector ( + const matrix<T,NR,NC,MM,row_major_layout>& m + ) + { + typedef op_mat_to_vect2<T,NR,NC,MM> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_join_rows + { + op_join_rows(const M1& m1_, const M2& m2_) : m1(m1_),m2(m2_),_nr(std::max(m1.nr(),m2.nr())) {} + const M1& m1; + const M2& m2; + const long _nr; + + template <typename T, typename U, bool selection> + struct type_selector; + template <typename T, typename U> + struct type_selector<T,U,true> { typedef T type; }; + template <typename T, typename U> + struct type_selector<T,U,false> { typedef U type; }; + + // If both const_ret_types are references then we should use them as the const_ret_type type + // but otherwise we should use the normal type. + typedef typename M1::const_ret_type T1; + typedef typename M1::type T2; + typedef typename M2::const_ret_type T3; + typedef typename type_selector<T1, T2, is_reference_type<T1>::value && is_reference_type<T3>::value>::type const_ret_type; + + const static long cost = M1::cost + M2::cost + 1; + const static long NR = tmax<M1::NR, M2::NR>::value; + const static long NC = (M1::NC*M2::NC != 0)? (M1::NC+M2::NC) : (0); + typedef typename M1::type type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + const_ret_type apply (long r, long c) const + { + if (c < m1.nc()) + return m1(r,c); + else + return m2(r,c-m1.nc()); + } + + long nr () const { return _nr; } + long nc () const { return m1.nc()+m2.nc(); } + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + }; + + template < + typename EXP1, + typename EXP2 + > + inline const matrix_op<op_join_rows<EXP1,EXP2> > join_rows ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + // You are getting an error on this line because you are trying to join two matrices that + // don't have the same number of rows + COMPILE_TIME_ASSERT(EXP1::NR == EXP2::NR || (EXP1::NR*EXP2::NR == 0)); + + DLIB_ASSERT(a.nr() == b.nr() || a.size() == 0 || b.size() == 0, + "\tconst matrix_exp join_rows(const matrix_exp& a, const matrix_exp& b)" + << "\n\tYou can only use join_rows() if both matrices have the same number of rows" + << "\n\ta.nr(): " << a.nr() + << "\n\tb.nr(): " << b.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nc(): " << b.nc() + ); + + typedef op_join_rows<EXP1,EXP2> op; + return matrix_op<op>(op(a.ref(),b.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M1, typename M2> + struct op_join_cols + { + op_join_cols(const M1& m1_, const M2& m2_) : m1(m1_),m2(m2_),_nc(std::max(m1.nc(),m2.nc())) {} + const M1& m1; + const M2& m2; + const long _nc; + + template <typename T, typename U, bool selection> + struct type_selector; + template <typename T, typename U> + struct type_selector<T,U,true> { typedef T type; }; + template <typename T, typename U> + struct type_selector<T,U,false> { typedef U type; }; + + // If both const_ret_types are references then we should use them as the const_ret_type type + // but otherwise we should use the normal type. + typedef typename M1::const_ret_type T1; + typedef typename M1::type T2; + typedef typename M2::const_ret_type T3; + typedef typename type_selector<T1, T2, is_reference_type<T1>::value && is_reference_type<T3>::value>::type const_ret_type; + + + + const static long cost = M1::cost + M2::cost + 1; + const static long NC = tmax<M1::NC, M2::NC>::value; + const static long NR = (M1::NR*M2::NR != 0)? (M1::NR+M2::NR) : (0); + typedef typename M1::type type; + typedef typename M1::mem_manager_type mem_manager_type; + typedef typename M1::layout_type layout_type; + + const_ret_type apply ( long r, long c) const + { + if (r < m1.nr()) + return m1(r,c); + else + return m2(r-m1.nr(),c); + } + + long nr () const { return m1.nr()+m2.nr(); } + long nc () const { return _nc; } + + + template <typename U> bool aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + template <typename U> bool destructively_aliases ( const matrix_exp<U>& item) const + { return m1.aliases(item) || m2.aliases(item); } + }; + + template < + typename EXP1, + typename EXP2 + > + inline const matrix_op<op_join_cols<EXP1,EXP2> > join_cols ( + const matrix_exp<EXP1>& a, + const matrix_exp<EXP2>& b + ) + { + COMPILE_TIME_ASSERT((is_same_type<typename EXP1::type,typename EXP2::type>::value == true)); + // You are getting an error on this line because you are trying to join two matrices that + // don't have the same number of columns + COMPILE_TIME_ASSERT(EXP1::NC == EXP2::NC || (EXP1::NC*EXP2::NC == 0)); + + DLIB_ASSERT(a.nc() == b.nc() || a.size() == 0 || b.size() == 0, + "\tconst matrix_exp join_cols(const matrix_exp& a, const matrix_exp& b)" + << "\n\tYou can only use join_cols() if both matrices have the same number of columns" + << "\n\ta.nr(): " << a.nr() + << "\n\tb.nr(): " << b.nr() + << "\n\ta.nc(): " << a.nc() + << "\n\tb.nc(): " << b.nc() + ); + + typedef op_join_cols<EXP1,EXP2> op; + return matrix_op<op>(op(a.ref(),b.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_fliplr + { + op_fliplr( const M& m_) : m(m_){} + + const M& m; + + const static long cost = M::cost; + const static long NR = M::NR; + const static long NC = M::NC; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + const_ret_type apply (long r, long c) const { return m(r,m.nc()-c-1); } + + long nr () const { return m.nr(); } + long nc () const { return m.nc(); } + + 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.aliases(item); } + + }; + + template < + typename M + > + const matrix_op<op_fliplr<M> > fliplr ( + const matrix_exp<M>& m + ) + { + typedef op_fliplr<M> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_flipud + { + op_flipud( const M& m_) : m(m_){} + + const M& m; + + const static long cost = M::cost; + const static long NR = M::NR; + const static long NC = M::NC; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + const_ret_type apply (long r, long c) const { return m(m.nr()-r-1,c); } + + long nr () const { return m.nr(); } + long nc () const { return m.nc(); } + + 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.aliases(item); } + + }; + + template < + typename M + > + const matrix_op<op_flipud<M> > flipud ( + const matrix_exp<M>& m + ) + { + typedef op_flipud<M> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_flip + { + op_flip( const M& m_) : m(m_){} + + const M& m; + + const static long cost = M::cost; + const static long NR = M::NR; + const static long NC = M::NC; + typedef typename M::type type; + typedef typename M::const_ret_type const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + + const_ret_type apply (long r, long c) const { return m(m.nr()-r-1, m.nc()-c-1); } + + long nr () const { return m.nr(); } + long nc () const { return m.nc(); } + + 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.aliases(item); } + + }; + + template < + typename M + > + const matrix_op<op_flip<M> > flip ( + const matrix_exp<M>& m + ) + { + typedef op_flip<M> op; + return matrix_op<op>(op(m.ref())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename T, long NR, long NC, typename MM, typename L> + uint32 hash ( + const matrix<T,NR,NC,MM,L>& item, + uint32 seed = 0 + ) + { + DLIB_ASSERT_HAS_STANDARD_LAYOUT(T); + + if (item.size() == 0) + return 0; + else + return murmur_hash3(&item(0,0), sizeof(T)*item.size(), seed); + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_UTILITIES_ + diff --git a/ml/dlib/dlib/matrix/matrix_utilities_abstract.h b/ml/dlib/dlib/matrix/matrix_utilities_abstract.h new file mode 100644 index 000000000..ad4c91167 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix_utilities_abstract.h @@ -0,0 +1,1874 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_MATRIx_UTILITIES_ABSTRACT_ +#ifdef DLIB_MATRIx_UTILITIES_ABSTRACT_ + +#include "matrix_abstract.h" +#include <complex> +#include "../pixel.h" +#include "../geometry/rectangle.h" +#inclue <vector> + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Simple matrix utilities +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename EXP> + constexpr bool is_row_major ( + const matrix_exp<EXP>& + ); + /*! + ensures + - returns true if and only if the given matrix expression uses the row_major_layout. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp diag ( + const matrix_exp& m + ); + /*! + ensures + - returns a column vector R that contains the elements from the diagonal + of m in the order R(0)==m(0,0), R(1)==m(1,1), R(2)==m(2,2) and so on. + !*/ + + template <typename EXP> + struct diag_exp + { + /*! + WHAT THIS OBJECT REPRESENTS + This struct allows you to determine the type of matrix expression + object returned from the diag() function. An example makes its + use clear: + + template <typename EXP> + void do_something( const matrix_exp<EXP>& mat) + { + // d is a matrix expression that aliases mat. + typename diag_exp<EXP>::type d = diag(mat); + + // Print the diagonal of mat. So we see that by using + // diag_exp we can save the object returned by diag() in + // a local variable. + cout << d << endl; + + // Note that you can only save the return value of diag() to + // a local variable if the argument to diag() has a lifetime + // beyond the diag() expression. The example shown above is + // OK but the following would result in undefined behavior: + typename diag_exp<EXP>::type bad = diag(mat + mat); + } + !*/ + typedef type_of_expression_returned_by_diag type; + }; + +// ---------------------------------------------------------------------------------------- + + const matrix_exp diagm ( + const matrix_exp& m + ); + /*! + requires + - is_vector(m) == true + (i.e. m is a row or column matrix) + ensures + - returns a square matrix M such that: + - diag(M) == m + - non diagonal elements of M are 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp trans ( + const matrix_exp& m + ); + /*! + ensures + - returns the transpose of the matrix m + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_type::type dot ( + const matrix_exp& m1, + const matrix_exp& m2 + ); + /*! + requires + - is_vector(m1) == true + - is_vector(m2) == true + - m1.size() == m2.size() + - m1.size() > 0 + ensures + - returns the dot product between m1 and m2. That is, this function + computes and returns the sum, for all i, of m1(i)*m2(i). + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp lowerm ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix M such that: + - M::type == the same type that was in m + - M has the same dimensions as m + - M is the lower triangular part of m. That is: + - if (r >= c) then + - M(r,c) == m(r,c) + - else + - M(r,c) == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp lowerm ( + const matrix_exp& m, + const matrix_exp::type scalar_value + ); + /*! + ensures + - returns a matrix M such that: + - M::type == the same type that was in m + - M has the same dimensions as m + - M is the lower triangular part of m except that the diagonal has + been set to scalar_value. That is: + - if (r > c) then + - M(r,c) == m(r,c) + - else if (r == c) then + - M(r,c) == scalar_value + - else + - M(r,c) == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp upperm ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix M such that: + - M::type == the same type that was in m + - M has the same dimensions as m + - M is the upper triangular part of m. That is: + - if (r <= c) then + - M(r,c) == m(r,c) + - else + - M(r,c) == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp upperm ( + const matrix_exp& m, + const matrix_exp::type scalar_value + ); + /*! + ensures + - returns a matrix M such that: + - M::type == the same type that was in m + - M has the same dimensions as m + - M is the upper triangular part of m except that the diagonal has + been set to scalar_value. That is: + - if (r < c) then + - M(r,c) == m(r,c) + - else if (r == c) then + - M(r,c) == scalar_value + - else + - M(r,c) == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp make_symmetric ( + const matrix_exp& m + ); + /*! + requires + - m.nr() == m.nc() + (i.e. m must be a square matrix) + ensures + - returns a matrix M such that: + - M::type == the same type that was in m + - M has the same dimensions as m + - M is a symmetric matrix, that is, M == trans(M) and + it is constructed from the lower triangular part of m. Specifically, + we have: + - lowerm(M) == lowerm(m) + - upperm(M) == trans(lowerm(m)) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + T val + > + const matrix_exp uniform_matrix ( + ); + /*! + requires + - NR > 0 && NC > 0 + ensures + - returns an NR by NC matrix with elements of type T and all set to val. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC + > + const matrix_exp uniform_matrix ( + const T& val + ); + /*! + requires + - NR > 0 && NC > 0 + ensures + - returns an NR by NC matrix with elements of type T and all set to val. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp uniform_matrix ( + long nr, + long nc, + const T& val + ); + /*! + requires + - nr >= 0 && nc >= 0 + ensures + - returns an nr by nc matrix with elements of type T and all set to val. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp ones_matrix ( + const matrix_exp& mat + ); + /*! + requires + - mat.nr() >= 0 && mat.nc() >= 0 + ensures + - Let T denote the type of element in mat. Then this function + returns uniform_matrix<T>(mat.nr(), mat.nc(), 1) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp ones_matrix ( + long nr, + long nc + ); + /*! + requires + - nr >= 0 && nc >= 0 + ensures + - returns uniform_matrix<T>(nr, nc, 1) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp zeros_matrix ( + const matrix_exp& mat + ); + /*! + requires + - mat.nr() >= 0 && mat.nc() >= 0 + ensures + - Let T denote the type of element in mat. Then this function + returns uniform_matrix<T>(mat.nr(), mat.nc(), 0) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp zeros_matrix ( + long nr, + long nc + ); + /*! + requires + - nr >= 0 && nc >= 0 + ensures + - returns uniform_matrix<T>(nr, nc, 0) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp identity_matrix ( + const matrix_exp& mat + ); + /*! + requires + - mat.nr() == mat.nc() + ensures + - returns an identity matrix with the same dimensions as mat and + containing the same type of elements as mat. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp identity_matrix ( + long N + ); + /*! + requires + - N > 0 + ensures + - returns an N by N identity matrix with elements of type T. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long N + > + const matrix_exp identity_matrix ( + ); + /*! + requires + - N > 0 + ensures + - returns an N by N identity matrix with elements of type T. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp linspace ( + double start, + double end, + long num + ); + /*! + requires + - num >= 0 + ensures + - returns a matrix M such that: + - M::type == double + - is_row_vector(M) == true + - M.size() == num + - M == a row vector with num linearly spaced values beginning with start + and stopping with end. + - M(num-1) == end + - if (num > 1) then + - M(0) == start + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp logspace ( + double start, + double end, + long num + ); + /*! + requires + - num >= 0 + ensures + - returns a matrix M such that: + - M::type == double + - is_row_vector(M) == true + - M.size() == num + - M == a row vector with num logarithmically spaced values beginning with + 10^start and stopping with 10^end. + (i.e. M == pow(10, linspace(start, end, num))) + - M(num-1) == 10^end + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp linpiece ( + const double val, + const matrix_exp& joints + ); + /*! + requires + - is_vector(joints) == true + - joints.size() >= 2 + - for all valid i < j: + - joints(i) < joints(j) + ensures + - linpiece() is useful for creating piecewise linear functions of val. For + example, if w is a parameter vector then you can represent a piecewise linear + function of val as: f(val) = dot(w, linpiece(val, linspace(0,100,5))). In + this case, f(val) is piecewise linear on the intervals [0,25], [25,50], + [50,75], [75,100]. Moreover, w(i) defines the derivative of f(val) in the + i-th interval. Finally, outside the interval [0,100] f(val) has a derivative + of zero and f(0) == 0. + - To be precise, this function returns a column vector L such that: + - L.size() == joints.size()-1 + - is_col_vector(L) == true + - L contains the same type of elements as joints. + - for all valid i: + - if (joints(i) < val) + - L(i) == min(val,joints(i+1)) - joints(i) + - else + - L(i) == 0 + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + long R, + long C + > + const matrix_exp rotate ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + R( (r+R)%m.nr() , (c+C)%m.nc() ) == m(r,c) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp fliplr ( + const matrix_exp& m + ); + /*! + ensures + - flips the matrix m from left to right and returns the result. + I.e. reverses the order of the columns. + - returns a matrix M such that: + - M::type == the same type that was in m + - M has the same dimensions as m + - for all valid r and c: + M(r,c) == m(r, m.nc()-c-1) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp flipud ( + const matrix_exp& m + ); + /*! + ensures + - flips the matrix m from up to down and returns the result. + I.e. reverses the order of the rows. + - returns a matrix M such that: + - M::type == the same type that was in m + - M has the same dimensions as m + - for all valid r and c: + M(r,c) == m(m.nr()-r-1, c) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp flip ( + const matrix_exp& m + ); + /*! + ensures + - flips the matrix m from up to down and left to right and returns the + result. I.e. returns flipud(fliplr(m)). + - returns a matrix M such that: + - M::type == the same type that was in m + - M has the same dimensions as m + - for all valid r and c: + M(r,c) == m(m.nr()-r-1, m.nc()-c-1) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp reshape ( + const matrix_exp& m, + long rows, + long cols + ); + /*! + requires + - m.size() == rows*cols + - rows > 0 + - cols > 0 + ensures + - returns a matrix M such that: + - M.nr() == rows + - M.nc() == cols + - M.size() == m.size() + - for all valid r and c: + - let IDX = r*cols + c + - M(r,c) == m(IDX/m.nc(), IDX%m.nc()) + + - i.e. The matrix m is reshaped into a new matrix of rows by cols + dimension. Additionally, the elements of m are laid into M in row major + order. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp reshape_to_column_vector ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix M such that: + - is_col_vector(M) == true + - M.size() == m.size() + - for all valid r and c: + - m(r,c) == M(r*m.nc() + c) + + - i.e. The matrix m is reshaped into a column vector. Note that + the elements are pulled out in row major order. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + long R, + long C + > + const matrix_exp removerc ( + const matrix_exp& m + ); + /*! + requires + - m.nr() > R >= 0 + - m.nc() > C >= 0 + ensures + - returns a matrix M such that: + - M.nr() == m.nr() - 1 + - M.nc() == m.nc() - 1 + - M == m with its R row and C column removed + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp removerc ( + const matrix_exp& m, + long R, + long C + ); + /*! + requires + - m.nr() > R >= 0 + - m.nc() > C >= 0 + ensures + - returns a matrix M such that: + - M.nr() == m.nr() - 1 + - M.nc() == m.nc() - 1 + - M == m with its R row and C column removed + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + long R + > + const matrix_exp remove_row ( + const matrix_exp& m + ); + /*! + requires + - m.nr() > R >= 0 + ensures + - returns a matrix M such that: + - M.nr() == m.nr() - 1 + - M.nc() == m.nc() + - M == m with its R row removed + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp remove_row ( + const matrix_exp& m, + long R + ); + /*! + requires + - m.nr() > R >= 0 + ensures + - returns a matrix M such that: + - M.nr() == m.nr() - 1 + - M.nc() == m.nc() + - M == m with its R row removed + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + long C + > + const matrix_exp remove_col ( + const matrix_exp& m + ); + /*! + requires + - m.nc() > C >= 0 + ensures + - returns a matrix M such that: + - M.nr() == m.nr() + - M.nc() == m.nc() - 1 + - M == m with its C column removed + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp remove_col ( + const matrix_exp& m, + long C + ); + /*! + requires + - m.nc() > C >= 0 + ensures + - returns a matrix M such that: + - M.nr() == m.nr() + - M.nc() == m.nc() - 1 + - M == m with its C column removed + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename target_type + > + const matrix_exp matrix_cast ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R where for all valid r and c: + R(r,c) == static_cast<target_type>(m(r,c)) + also, R has the same dimensions as m. + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename MM, + typename U, + typename L + > + void set_all_elements ( + matrix<T,NR,NC,MM,L>& m, + U value + ); + /*! + ensures + - for all valid r and c: + m(r,c) == value + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::matrix_type tmp ( + const matrix_exp& m + ); + /*! + ensures + - returns a temporary matrix object that is a copy of m. + (This allows you to easily force a matrix_exp to fully evaluate) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename MM, + typename L + > + uint32 hash ( + const matrix<T,NR,NC,MM,L>& item, + uint32 seed = 0 + ); + /*! + requires + - T is a standard layout type (e.g. a POD type like int, float, + or a simple struct). + ensures + - returns a 32bit hash of the data stored in item. + - Each value of seed results in a different hash function being used. + (e.g. hash(item,0) should generally not be equal to hash(item,1)) + - uses the murmur_hash3() routine to compute the actual hash. + - Note that if the memory layout of the elements in item change between + hardware platforms then hash() will give different outputs. If you want + hash() to always give the same output for the same input then you must + ensure that elements of item always have the same layout in memory. + Typically this means using fixed width types and performing byte swapping + to account for endianness before passing item to hash(). + !*/ + +// ---------------------------------------------------------------------------------------- + + // if matrix_exp contains non-complex types (e.g. float, double) + bool equal ( + const matrix_exp& a, + const matrix_exp& b, + const matrix_exp::type epsilon = 100*std::numeric_limits<matrix_exp::type>::epsilon() + ); + /*! + ensures + - if (a and b don't have the same dimensions) then + - returns false + - else if (there exists an r and c such that abs(a(r,c)-b(r,c)) > epsilon) then + - returns false + - else + - returns true + !*/ + +// ---------------------------------------------------------------------------------------- + + // if matrix_exp contains std::complex types + bool equal ( + const matrix_exp& a, + const matrix_exp& b, + const matrix_exp::type::value_type epsilon = 100*std::numeric_limits<matrix_exp::type::value_type>::epsilon() + ); + /*! + ensures + - if (a and b don't have the same dimensions) then + - returns false + - else if (there exists an r and c such that abs(real(a(r,c)-b(r,c))) > epsilon + or abs(imag(a(r,c)-b(r,c))) > epsilon) then + - returns false + - else + - returns true + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp pointwise_multiply ( + const matrix_exp& a, + const matrix_exp& b + ); + /*! + requires + - a.nr() == b.nr() + - a.nc() == b.nc() + - a and b both contain the same type of element (one or both + can also be of type std::complex so long as the underlying type + in them is the same) + ensures + - returns a matrix R such that: + - R::type == the same type that was in a and b. + - R has the same dimensions as a and b. + - for all valid r and c: + R(r,c) == a(r,c) * b(r,c) + !*/ + + const matrix_exp pointwise_multiply ( + const matrix_exp& a, + const matrix_exp& b, + const matrix_exp& c + ); + /*! + performs pointwise_multiply(a,pointwise_multiply(b,c)); + !*/ + + const matrix_exp pointwise_multiply ( + const matrix_exp& a, + const matrix_exp& b, + const matrix_exp& c, + const matrix_exp& d + ); + /*! + performs pointwise_multiply(pointwise_multiply(a,b),pointwise_multiply(c,d)); + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp join_rows ( + const matrix_exp& a, + const matrix_exp& b + ); + /*! + requires + - a.nr() == b.nr() || a.size() == 0 || b.size() == 0 + - a and b both contain the same type of element + ensures + - This function joins two matrices together by concatenating their rows. + - returns a matrix R such that: + - R::type == the same type that was in a and b. + - R.nr() == a.nr() == b.nr() + - R.nc() == a.nc() + b.nc() + - for all valid r and c: + - if (c < a.nc()) then + - R(r,c) == a(r,c) + - else + - R(r,c) == b(r, c-a.nc()) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp join_cols ( + const matrix_exp& a, + const matrix_exp& b + ); + /*! + requires + - a.nc() == b.nc() || a.size() == 0 || b.size() == 0 + - a and b both contain the same type of element + ensures + - This function joins two matrices together by concatenating their columns. + - returns a matrix R such that: + - R::type == the same type that was in a and b. + - R.nr() == a.nr() + b.nr() + - R.nc() == a.nc() == b.nc() + - for all valid r and c: + - if (r < a.nr()) then + - R(r,c) == a(r,c) + - else + - R(r,c) == b(r-a.nr(), c) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp tensor_product ( + const matrix_exp& a, + const matrix_exp& b + ); + /*! + requires + - a and b both contain the same type of element + ensures + - returns a matrix R such that: + - R::type == the same type that was in a and b. + - R.nr() == a.nr() * b.nr() + - R.nc() == a.nc() * b.nc() + - for all valid r and c: + R(r,c) == a(r/b.nr(), c/b.nc()) * b(r%b.nr(), c%b.nc()) + - I.e. R is the tensor product of matrix a with matrix b + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp cartesian_product ( + const matrix_exp& A, + const matrix_exp& B + ); + /*! + requires + - A and B both contain the same type of element + ensures + - Think of A and B as sets of column vectors. Then this function + returns a matrix that contains a set of column vectors that is + the Cartesian product of the sets A and B. That is, the resulting + matrix contains every possible combination of vectors from both A and + B. + - returns a matrix R such that: + - R::type == the same type that was in A and B. + - R.nr() == A.nr() + B.nr() + - R.nc() == A.nc() * B.nc() + - Each column of R is the concatenation of a column vector + from A with a column vector from B. + - for all valid r and c: + - if (r < A.nr()) then + - R(r,c) == A(r, c/B.nc()) + - else + - R(r,c) == B(r-A.nr(), c%B.nc()) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp scale_columns ( + const matrix_exp& m, + const matrix_exp& v + ); + /*! + requires + - is_vector(v) == true + - v.size() == m.nc() + - m and v both contain the same type of element + ensures + - returns a matrix R such that: + - R::type == the same type that was in m and v. + - R has the same dimensions as m. + - for all valid r and c: + R(r,c) == m(r,c) * v(c) + - i.e. R is the result of multiplying each of m's columns by + the corresponding scalar in v. + + - Note that this function is identical to the expression m*diagm(v). + That is, the * operator is overloaded for this case and will invoke + scale_columns() automatically as appropriate. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp scale_rows ( + const matrix_exp& m, + const matrix_exp& v + ); + /*! + requires + - is_vector(v) == true + - v.size() == m.nr() + - m and v both contain the same type of element + ensures + - returns a matrix R such that: + - R::type == the same type that was in m and v. + - R has the same dimensions as m. + - for all valid r and c: + R(r,c) == m(r,c) * v(r) + - i.e. R is the result of multiplying each of m's rows by + the corresponding scalar in v. + + - Note that this function is identical to the expression diagm(v)*m. + That is, the * operator is overloaded for this case and will invoke + scale_rows() automatically as appropriate. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename T> + void sort_columns ( + matrix<T>& m, + matrix<T>& v + ); + /*! + requires + - is_col_vector(v) == true + - v.size() == m.nc() + - m and v both contain the same type of element + ensures + - the dimensions for m and v are not changed + - sorts the columns of m according to the values in v. + i.e. + - #v == the contents of v but in sorted order according to + operator<. So smaller elements come first. + - Let #v(new(i)) == v(i) (i.e. new(i) is the index element i moved to) + - colm(#m,new(i)) == colm(m,i) + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename T> + void rsort_columns ( + matrix<T>& m, + matrix<T>& v + ); + /*! + requires + - is_col_vector(v) == true + - v.size() == m.nc() + - m and v both contain the same type of element + ensures + - the dimensions for m and v are not changed + - sorts the columns of m according to the values in v. + i.e. + - #v == the contents of v but in sorted order according to + operator>. So larger elements come first. + - Let #v(new(i)) == v(i) (i.e. new(i) is the index element i moved to) + - colm(#m,new(i)) == colm(m,i) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type length_squared ( + const matrix_exp& m + ); + /*! + requires + - is_vector(m) == true + ensures + - returns sum(squared(m)) + (i.e. returns the square of the length of the vector m) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type length ( + const matrix_exp& m + ); + /*! + requires + - is_vector(m) == true + ensures + - returns sqrt(sum(squared(m))) + (i.e. returns the length of the vector m) + - if (m contains integer valued elements) then + - The return type is a double that represents the length. Therefore, the + return value of length() is always represented using a floating point + type. + !*/ + +// ---------------------------------------------------------------------------------------- + + bool is_row_vector ( + const matrix_exp& m + ); + /*! + ensures + - if (m.nr() == 1) then + - return true + - else + - returns false + !*/ + + bool is_col_vector ( + const matrix_exp& m + ); + /*! + ensures + - if (m.nc() == 1) then + - return true + - else + - returns false + !*/ + + bool is_vector ( + const matrix_exp& m + ); + /*! + ensures + - if (is_row_vector(m) || is_col_vector(m)) then + - return true + - else + - returns false + !*/ + +// ---------------------------------------------------------------------------------------- + + bool is_finite ( + const matrix_exp& m + ); + /*! + ensures + - returns true if all the values in m are finite values and also not any kind + of NaN value. + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Thresholding relational operators +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator< ( + const matrix_exp& m, + const S& s + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (m(r,c) < s) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator< ( + const S& s, + const matrix_exp& m + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (s < m(r,c)) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator<= ( + const matrix_exp& m, + const S& s + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (m(r,c) <= s) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator<= ( + const S& s, + const matrix_exp& m + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (s <= m(r,c)) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator> ( + const matrix_exp& m, + const S& s + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (m(r,c) > s) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator> ( + const S& s, + const matrix_exp& m + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (s > m(r,c)) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator>= ( + const matrix_exp& m, + const S& s + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (m(r,c) >= s) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator>= ( + const S& s, + const matrix_exp& m + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (s >= m(r,c)) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator== ( + const matrix_exp& m, + const S& s + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (m(r,c) == s) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator== ( + const S& s, + const matrix_exp& m + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (s == m(r,c)) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator!= ( + const matrix_exp& m, + const S& s + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (m(r,c) != s) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename S> + const matrix_exp operator!= ( + const S& s, + const matrix_exp& m + ); + /*! + requires + - is_built_in_scalar_type<S>::value == true + - is_built_in_scalar_type<matrix_exp::type>::value == true + ensures + - returns a matrix R such that: + - R::type == the same type that was in m. + - R has the same dimensions as m. + - for all valid r and c: + - if (s != m(r,c)) then + - R(r,c) == 1 + - else + - R(r,c) == 0 + - i.e. R is a binary matrix of all 1s or 0s. + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Statistics +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type min ( + const matrix_exp& m + ); + /*! + requires + - m.size() > 0 + ensures + - returns the value of the smallest element of m. If m contains complex + elements then the element returned is the one with the smallest norm + according to std::norm(). + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp min_pointwise ( + const matrix_exp& a, + const matrix_exp& b + ); + /*! + requires + - a.nr() == b.nr() + - a.nc() == b.nc() + - a and b both contain the same type of element + ensures + - returns a matrix R such that: + - R::type == the same type that was in a and b. + - R has the same dimensions as a and b. + - for all valid r and c: + R(r,c) == std::min(a(r,c), b(r,c)) + !*/ + + const matrix_exp min_pointwise ( + const matrix_exp& a, + const matrix_exp& b, + const matrix_exp& c + ); + /*! + performs min_pointwise(a,min_pointwise(b,c)); + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type max ( + const matrix_exp& m + ); + /*! + requires + - m.size() > 0 + ensures + - returns the value of the biggest element of m. If m contains complex + elements then the element returned is the one with the largest norm + according to std::norm(). + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp max_pointwise ( + const matrix_exp& a, + const matrix_exp& b + ); + /*! + requires + - a.nr() == b.nr() + - a.nc() == b.nc() + - a and b both contain the same type of element + ensures + - returns a matrix R such that: + - R::type == the same type that was in a and b. + - R has the same dimensions as a and b. + - for all valid r and c: + R(r,c) == std::max(a(r,c), b(r,c)) + !*/ + + const matrix_exp max_pointwise ( + const matrix_exp& a, + const matrix_exp& b, + const matrix_exp& c + ); + /*! + performs max_pointwise(a,max_pointwise(b,c)); + !*/ + +// ---------------------------------------------------------------------------------------- + + void find_min_and_max ( + const matrix_exp& m, + matrix_exp::type& min_val, + matrix_exp::type& max_val + ); + /*! + requires + - m.size() > 0 + ensures + - #min_val == min(m) + - #max_val == max(m) + - This function computes both the min and max in just one pass + over the elements of the matrix m. + !*/ + +// ---------------------------------------------------------------------------------------- + + long index_of_max ( + const matrix_exp& m + ); + /*! + requires + - is_vector(m) == true + - m.size() > 0 + ensures + - returns the index of the largest element in m. + (i.e. m(index_of_max(m)) == max(m)) + !*/ + +// ---------------------------------------------------------------------------------------- + + long index_of_min ( + const matrix_exp& m + ); + /*! + requires + - is_vector(m) == true + - m.size() > 0 + ensures + - returns the index of the smallest element in m. + (i.e. m(index_of_min(m)) == min(m)) + !*/ + +// ---------------------------------------------------------------------------------------- + + point max_point ( + const matrix_exp& m + ); + /*! + requires + - m.size() > 0 + ensures + - returns the location of the maximum element of the array, that is, if the + returned point is P then it will be the case that: m(P.y(),P.x()) == max(m). + !*/ + +// ---------------------------------------------------------------------------------------- + + dlib::vector<double,2> max_point_interpolated ( + const matrix_exp& m + ); + /*! + requires + - m.size() > 0 + ensures + - Like max_point(), this function finds the location in m with the largest + value. However, we additionally use some quadratic interpolation to find the + location of the maximum point with sub-pixel accuracy. Therefore, the + returned point is equal to max_point(m) + some small sub-pixel delta. + !*/ + +// ---------------------------------------------------------------------------------------- + + point min_point ( + const matrix_exp& m + ); + /*! + requires + - m.size() > 0 + ensures + - returns the location of the minimum element of the array, that is, if the + returned point is P then it will be the case that: m(P.y(),P.x()) == min(m). + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type sum ( + const matrix_exp& m + ); + /*! + ensures + - returns the sum of all elements in m + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp sum_rows ( + const matrix_exp& m + ); + /*! + requires + - m.size() > 0 + ensures + - returns a row matrix that contains the sum of all the rows in m. + - returns a matrix M such that + - M::type == the same type that was in m + - M.nr() == 1 + - M.nc() == m.nc() + - for all valid i: + - M(i) == sum(colm(m,i)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp sum_cols ( + const matrix_exp& m + ); + /*! + requires + - m.size() > 0 + ensures + - returns a column matrix that contains the sum of all the columns in m. + - returns a matrix M such that + - M::type == the same type that was in m + - M.nr() == m.nr() + - M.nc() == 1 + - for all valid i: + - M(i) == sum(rowm(m,i)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type prod ( + const matrix_exp& m + ); + /*! + ensures + - returns the results of multiplying all elements of m together. + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type mean ( + const matrix_exp& m + ); + /*! + ensures + - returns the mean of all elements in m. + (i.e. returns sum(m)/(m.nr()*m.nc())) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type variance ( + const matrix_exp& m + ); + /*! + ensures + - returns the unbiased sample variance of all elements in m + (i.e. 1.0/(m.nr()*m.nc() - 1)*(sum of all pow(m(i,j) - mean(m),2))) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp::type stddev ( + const matrix_exp& m + ); + /*! + ensures + - returns sqrt(variance(m)) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix covariance ( + const matrix_exp& m + ); + /*! + requires + - matrix_exp::type == a dlib::matrix object + - is_col_vector(m) == true + - m.size() > 1 + - for all valid i, j: + - is_col_vector(m(i)) == true + - m(i).size() > 0 + - m(i).size() == m(j).size() + - i.e. m contains only column vectors and all the column vectors + have the same non-zero length + ensures + - returns the unbiased sample covariance matrix for the set of samples + in m. + (i.e. 1.0/(m.nr()-1)*(sum of all (m(i) - mean(m))*trans(m(i) - mean(m)))) + - the returned matrix will contain elements of type matrix_exp::type::type. + - the returned matrix will have m(0).nr() rows and columns. + !*/ + +// ---------------------------------------------------------------------------------------- + + template <typename rand_gen> + const matrix<double> randm( + long nr, + long nc, + rand_gen& rnd + ); + /*! + requires + - nr >= 0 + - nc >= 0 + - rand_gen == an object that implements the rand/rand_float_abstract.h interface + ensures + - generates a random matrix using the given rnd random number generator + - returns a matrix M such that + - M::type == double + - M.nr() == nr + - M.nc() == nc + - for all valid i, j: + - M(i,j) == a random number such that 0 <= M(i,j) < 1 + !*/ + +// ---------------------------------------------------------------------------------------- + + inline const matrix<double> randm( + long nr, + long nc + ); + /*! + requires + - nr >= 0 + - nc >= 0 + ensures + - generates a random matrix using std::rand() + - returns a matrix M such that + - M::type == double + - M.nr() == nr + - M.nc() == nc + - for all valid i, j: + - M(i,j) == a random number such that 0 <= M(i,j) < 1 + !*/ + +// ---------------------------------------------------------------------------------------- + + inline const matrix_exp gaussian_randm ( + long nr, + long nc, + unsigned long seed = 0 + ); + /*! + requires + - nr >= 0 + - nc >= 0 + ensures + - returns a matrix with its values filled with 0 mean unit variance Gaussian + random numbers. + - Each setting of the seed results in a different random matrix. + - The returned matrix is lazily evaluated using the expression templates + technique. This means that the returned matrix doesn't take up any memory + and is only an expression template. The values themselves are computed on + demand using the gaussian_random_hash() routine. + - returns a matrix M such that + - M::type == double + - M.nr() == nr + - M.nc() == nc + - for all valid i, j: + - M(i,j) == gaussian_random_hash(i,j,seed) + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// Pixel and Image Utilities +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template < + typename T, + typename P + > + const matrix<T,pixel_traits<P>::num,1> pixel_to_vector ( + const P& pixel + ); + /*! + requires + - pixel_traits<P> must be defined + ensures + - returns a matrix M such that: + - M::type == T + - M::NC == 1 + - M::NR == pixel_traits<P>::num + - if (pixel_traits<P>::grayscale) then + - M(0) == pixel + - if (pixel_traits<P>::rgb) then + - M(0) == pixel.red + - M(1) == pixel.green + - M(2) == pixel.blue + - if (pixel_traits<P>::hsi) then + - M(0) == pixel.h + - M(1) == pixel.s + - M(2) == pixel.i + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + typename P + > + void vector_to_pixel ( + P& pixel, + const matrix_exp& vector + ); + /*! + requires + - vector::NR == pixel_traits<P>::num + - vector::NC == 1 + (i.e. you have to use a statically dimensioned vector) + ensures + - if (pixel_traits<P>::grayscale) then + - pixel == M(0) + - if (pixel_traits<P>::rgb) then + - pixel.red == M(0) + - pixel.green == M(1) + - pixel.blue == M(2) + - if (pixel_traits<P>::hsi) then + - pixel.h == M(0) + - pixel.s == M(1) + - pixel.i == M(2) + !*/ + +// ---------------------------------------------------------------------------------------- + + template < + long lower, + long upper + > + const matrix_exp clamp ( + const matrix_exp& m + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (m(r,c) > upper) then + - R(r,c) == upper + - else if (m(r,c) < lower) then + - R(r,c) == lower + - else + - R(r,c) == m(r,c) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp clamp ( + const matrix_exp& m, + const matrix_exp::type& lower, + const matrix_exp::type& upper + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (m(r,c) > upper) then + - R(r,c) == upper + - else if (m(r,c) < lower) then + - R(r,c) == lower + - else + - R(r,c) == m(r,c) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp clamp ( + const matrix_exp& m, + const matrix_exp& lower, + const matrix_exp& upper + ); + /*! + requires + - m.nr() == lower.nr() + - m.nc() == lower.nc() + - m.nr() == upper.nr() + - m.nc() == upper.nc() + - m, lower, and upper all contain the same type of elements. + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (m(r,c) > upper(r,c)) then + - R(r,c) == upper(r,c) + - else if (m(r,c) < lower(r,c)) then + - R(r,c) == lower(r,c) + - else + - R(r,c) == m(r,c) + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp lowerbound ( + const matrix_exp& m, + const matrix_exp::type& thresh + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (m(r,c) >= thresh) then + - R(r,c) == m(r,c) + - else + - R(r,c) == thresh + !*/ + +// ---------------------------------------------------------------------------------------- + + const matrix_exp upperbound ( + const matrix_exp& m, + const matrix_exp::type& thresh + ); + /*! + ensures + - returns a matrix R such that: + - R::type == the same type that was in m + - R has the same dimensions as m + - for all valid r and c: + - if (m(r,c) <= thresh) then + - R(r,c) == m(r,c) + - else + - R(r,c) == thresh + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_MATRIx_UTILITIES_ABSTRACT_ + diff --git a/ml/dlib/dlib/matrix/symmetric_matrix_cache.h b/ml/dlib/dlib/matrix/symmetric_matrix_cache.h new file mode 100644 index 000000000..bff268aef --- /dev/null +++ b/ml/dlib/dlib/matrix/symmetric_matrix_cache.h @@ -0,0 +1,464 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_SYMMETRIC_MATRIX_CAcHE_Hh_ +#define DLIB_SYMMETRIC_MATRIX_CAcHE_Hh_ + +#include "symmetric_matrix_cache_abstract.h" +#include <vector> +#include "../matrix.h" +#include "../algs.h" +#include "../array.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + template <typename M, typename cache_element_type> + struct op_symm_cache : basic_op_m<M> + { + inline op_symm_cache( + const M& m_, + long max_size_megabytes_ + ) : + basic_op_m<M>(m_), + max_size_megabytes(max_size_megabytes_), + is_initialized(false) + { + lookup.assign(this->m.nr(), -1); + + diag_cache = matrix_cast<cache_element_type>(dlib::diag(m_)); + } + + op_symm_cache ( + const op_symm_cache& item + ) : + basic_op_m<M>(item.m), + diag_cache(item.diag_cache), + max_size_megabytes(item.max_size_megabytes), + is_initialized(false) + { + lookup.assign(this->m.nr(), -1); + } + + typedef cache_element_type type; + typedef const cache_element_type& const_ret_type; + const static long cost = M::cost + 3; + + inline const_ret_type apply ( long r, long c) const + { + if (lookup[c] != -1) + { + return cache[lookup[c]](r); + } + else if (r == c) + { + return diag_cache(r); + } + else if (lookup[r] != -1) + { + // the matrix is symmetric so this is legit + return cache[lookup[r]](c); + } + else + { + add_col_to_cache(c); + return cache[lookup[c]](r); + } + } + + inline std::pair<const type*,long*> col(long i) const + /*! + requires + - 0 <= i < nc() + ensures + - returns a pair P such that: + - P.first == a pointer to the first element of the ith column + - P.second == a pointer to the integer used to count the number of + outstanding references to the ith column. + !*/ + { + if (is_cached(i) == false) + add_col_to_cache(i); + + // find where this column is in the cache + long idx = lookup[i]; + if (idx == next) + { + // if this column was the next to be replaced + // then make sure that doesn't happen + next = (next + 1)%cache.size(); + } + + return std::make_pair(&cache[idx](0), &references[idx]); + } + + const type* diag() const { init(); return &diag_cache(0); } + + long* diag_ref_count() const + { + return &diag_reference_count; + } + + private: + inline bool is_cached ( + long r + ) const + { + return (lookup[r] != -1); + } + + inline void init() const + { + if (is_initialized == false) + { + // figure out how many columns of the matrix we can have + // with the given amount of memory. + long max_size = (max_size_megabytes*1024*1024)/(this->m.nr()*sizeof(type)); + // don't let it be 0 or 1 + if (max_size <= 1) + max_size = 2; + + const long size = std::min(max_size,this->m.nr()); + + diag_reference_count = 0; + + references.set_max_size(this->m.nr()); + references.set_size(size); + for (unsigned long i = 0; i < references.size(); ++i) + references[i] = 0; + + cache.set_max_size(this->m.nr()); + cache.set_size(size); + + rlookup.assign(size,-1); + next = 0; + + is_initialized = true; + } + } + + void make_sure_next_is_unreferenced ( + ) const + { + if (references[next] != 0) + { + // find an unreferenced element of the cache + unsigned long i; + for (i = 1; i < references.size(); ++i) + { + const unsigned long idx = (next+i)%references.size(); + if (references[idx] == 0) + { + next = idx; + break; + } + } + + // if all elements of the cache are referenced then make the cache bigger + // and use the new element. + if (references[next] != 0) + { + cache.resize(cache.size()+1); + + next = references.size(); + references.resize(references.size()+1); + references[next] = 0; + + rlookup.push_back(-1); + } + } + } + + inline void add_col_to_cache( + long c + ) const + { + init(); + make_sure_next_is_unreferenced(); + + // if the lookup table is pointing to cache[next] then clear lookup[next] + if (rlookup[next] != -1) + lookup[rlookup[next]] = -1; + + // make the lookup table so that it says c is now cached at the spot indicated by next + lookup[c] = next; + rlookup[next] = c; + + // compute this column in the matrix and store it in the cache + cache[next] = matrix_cast<cache_element_type>(colm(this->m,c)); + + next = (next + 1)%cache.size(); + } + + /*! + INITIAL VALUE + - for all valid x: + - lookup(x) == -1 + + - diag_cache == the diagonal of the original matrix + - is_initialized == false + - max_size_megabytes == the max_size_megabytes from symmetric_matrix_cache() + + CONVENTION + - diag_cache == the diagonal of the original matrix + - lookup.size() == diag_cache.size() + + - if (is_initialized) then + - if (lookup[c] != -1) then + - cache[lookup[c]] == the cached column c of the matrix + - rlookup[lookup[c]] == c + + - if (rlookup[x] != -1) then + - lookup[rlookup[x]] == x + - cache[x] == the cached column rlookup[x] of the matrix + + - next == the next element in the cache table to use to cache something + - references[i] == the number of outstanding references to cache element cache[i] + + - diag_reference_count == the number of outstanding references to diag_cache. + (this isn't really needed. It's just here so that we can reuse the matrix + expression from colm() to implement diag()) + !*/ + + + mutable array<matrix<type,0,1,typename M::mem_manager_type> > cache; + mutable array<long> references; + matrix<type,0,1,typename M::mem_manager_type> diag_cache; + mutable std::vector<long> lookup; + mutable std::vector<long> rlookup; + mutable long next; + + const long max_size_megabytes; + mutable bool is_initialized; + mutable long diag_reference_count; + + }; + + template < + typename cache_element_type, + typename EXP + > + const matrix_op<op_symm_cache<EXP,cache_element_type> > symmetric_matrix_cache ( + const matrix_exp<EXP>& m, + long max_size_megabytes + ) + { + // Don't check that m is symmetric since doing so would be extremely onerous for the + // kinds of matrices intended for use with the symmetric_matrix_cache. Check everything + // else though. + DLIB_ASSERT(m.size() > 0 && m.nr() == m.nc() && max_size_megabytes >= 0, + "\tconst matrix_exp symmetric_matrix_cache(const matrix_exp& m, max_size_megabytes)" + << "\n\t You have given invalid arguments to this function" + << "\n\t m.nr(): " << m.nr() + << "\n\t m.nc(): " << m.nc() + << "\n\t m.size(): " << m.size() + << "\n\t max_size_megabytes: " << max_size_megabytes + ); + + typedef op_symm_cache<EXP,cache_element_type> op; + return matrix_op<op>(op(m.ref(), max_size_megabytes)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, typename cache_element_type> + struct op_colm_symm_cache + { + typedef cache_element_type type; + + op_colm_symm_cache( + const M& m_, + const type* data_, + long* ref_count_ + ) : + m(m_), + data(data_), + ref_count(ref_count_) + { + *ref_count += 1; + } + + op_colm_symm_cache ( + const op_colm_symm_cache& item + ) : + m(item.m), + data(item.data), + ref_count(item.ref_count) + { + *ref_count += 1; + } + + ~op_colm_symm_cache( + ) + { + *ref_count -= 1; + } + + const M& m; + + const type* const data; + long* const ref_count; + + const static long cost = M::cost; + const static long NR = M::NR; + const static long NC = 1; + typedef const type& const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + inline const_ret_type apply ( long r, long) const { return data[r]; } + + long nr () const { return m.nr(); } + long nc () const { return 1; } + + 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.aliases(item); } + }; + + template < + typename EXP, + typename cache_element_type + > + inline const matrix_op<op_colm_symm_cache<EXP,cache_element_type> > colm ( + const matrix_exp<matrix_op<op_symm_cache<EXP,cache_element_type> > >& m, + long col + ) + { + DLIB_ASSERT(col >= 0 && col < m.nc(), + "\tconst matrix_exp colm(const matrix_exp& m, row)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\tcol: " << col + ); + + std::pair<const cache_element_type*,long*> p = m.ref().op.col(col); + + typedef op_colm_symm_cache<EXP,cache_element_type> op; + return matrix_op<op>(op(m.ref().op.m, + p.first, + p.second)); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP, + typename cache_element_type + > + inline const matrix_op<op_colm_symm_cache<EXP,cache_element_type> > diag ( + const matrix_exp<matrix_op<op_symm_cache<EXP,cache_element_type> > >& m + ) + { + typedef op_colm_symm_cache<EXP,cache_element_type> op; + return matrix_op<op>(op(m.ref().op.m, + m.ref().op.diag(), + m.ref().op.diag_ref_count())); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, typename cache_element_type> + struct op_rowm_symm_cache + { + typedef cache_element_type type; + + op_rowm_symm_cache( + const M& m_, + const type* data_, + long* ref_count_ + ) : + m(m_), + data(data_), + ref_count(ref_count_) + { + *ref_count += 1; + } + + op_rowm_symm_cache ( + const op_rowm_symm_cache& item + ) : + m(item.m), + data(item.data), + ref_count(item.ref_count) + { + *ref_count += 1; + } + + ~op_rowm_symm_cache( + ) + { + *ref_count -= 1; + } + + const M& m; + + const type* const data; + long* const ref_count; + + const static long cost = M::cost; + const static long NR = 1; + const static long NC = M::NC; + typedef const type& const_ret_type; + typedef typename M::mem_manager_type mem_manager_type; + typedef typename M::layout_type layout_type; + inline const_ret_type apply ( long , long c) const { return data[c]; } + + long nr () const { return 1; } + long nc () const { return m.nc(); } + + 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.aliases(item); } + }; + + template < + typename EXP, + typename cache_element_type + > + inline const matrix_op<op_rowm_symm_cache<EXP,cache_element_type> > rowm ( + const matrix_exp<matrix_op<op_symm_cache<EXP,cache_element_type> > >& m, + long row + ) + { + DLIB_ASSERT(row >= 0 && row < m.nr(), + "\tconst matrix_exp rowm(const matrix_exp& m, row)" + << "\n\tYou have specified invalid sub matrix dimensions" + << "\n\tm.nr(): " << m.nr() + << "\n\tm.nc(): " << m.nc() + << "\n\trow: " << row + ); + + std::pair<const cache_element_type*,long*> p = m.ref().op.col(row); + + typedef op_rowm_symm_cache<EXP,cache_element_type> op; + return matrix_op<op>(op(m.ref().op.m, + p.first, + p.second)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename EXP, typename cache_element_type> + struct colm_exp<matrix_op<op_symm_cache<EXP, cache_element_type> > > + { + typedef matrix_op<op_colm_symm_cache<EXP, cache_element_type> > type; + }; + + template <typename EXP, typename cache_element_type> + struct rowm_exp<matrix_op<op_symm_cache<EXP, cache_element_type> > > + { + typedef matrix_op<op_rowm_symm_cache<EXP, cache_element_type> > type; + }; + + template <typename EXP, typename cache_element_type> + struct diag_exp<matrix_op<op_symm_cache<EXP, cache_element_type> > > + { + typedef matrix_op<op_colm_symm_cache<EXP, cache_element_type> > type; + }; + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_SYMMETRIC_MATRIX_CAcHE_Hh_ + diff --git a/ml/dlib/dlib/matrix/symmetric_matrix_cache_abstract.h b/ml/dlib/dlib/matrix/symmetric_matrix_cache_abstract.h new file mode 100644 index 000000000..6a41ad282 --- /dev/null +++ b/ml/dlib/dlib/matrix/symmetric_matrix_cache_abstract.h @@ -0,0 +1,63 @@ +// Copyright (C) 2010 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#define DLIB_SYMMETRIC_MATRIX_CAcHE_ABSTRACT_Hh_ +#ifndef DLIB_SYMMETRIC_MATRIX_CAcHE_ABSTRACT_Hh_ + +#include "matrix_abstract.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + template < + typename cache_element_type + > + const matrix_exp symmetric_matrix_cache ( + const matrix_exp& m, + long max_size_megabytes + ); + /*! + requires + - m.size() > 0 + - m.nr() == m.nc() + - max_size_megabytes >= 0 + ensures + - This function assumes that m is symmetric. If m is not symmetric then it won't + crash but you will get incorrect results. + - This method creates a matrix expression which internally caches the elements + of m so that they can be accessed quickly. It is useful if m is some kind of + complex matrix expression which is both very large and expensive to evaluate. + An example would be a kernel_matrix() expression with an expensive kernel and + a large number of samples. Such an expression would result in a huge matrix, + potentially too big to store in memory. The symmetric_matrix_cache() then makes + it easy to store just the parts of a matrix expression which are accessed most + often in memory. The specific details are defined below. + - returns a matrix M such that + - M == m + (i.e. M represents the same matrix as m) + - M will cache elements of m and hold them internally so they can be quickly + accessed. In particular, M will attempt to allocate no more than + max_size_megabytes megabytes of memory for the purposes of caching + elements of m. When an element of the matrix is accessed it is either + retrieved from the cache, or if this is not possible, then an entire + column of m is loaded into a part of the cache which hasn't been used + recently and the needed element returned. + - diag(m) is always loaded into the cache and is stored separately from + the cached columns. That means accesses to the diagonal elements of m + are always fast. + - M will store the cached elements of m as cache_element_type objects. + Typically, cache_element_type will be float or double. + - To avoid repeated cache lookups, the following operations are optimized for + use with the symmetric_matrix_cache(): + - diag(M), rowm(M,row_idx), colm(M,col_idx) + These methods will perform only one cache lookup operation for an + entire row/column/diagonal worth of data. + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_SYMMETRIC_MATRIX_CAcHE_ABSTRACT_Hh_ + |