summaryrefslogtreecommitdiffstats
path: root/ml/dlib/dlib/matrix
diff options
context:
space:
mode:
Diffstat (limited to 'ml/dlib/dlib/matrix')
-rw-r--r--ml/dlib/dlib/matrix/cblas_constants.h22
-rw-r--r--ml/dlib/dlib/matrix/lapack/fortran_id.h62
-rw-r--r--ml/dlib/dlib/matrix/lapack/gees.h264
-rw-r--r--ml/dlib/dlib/matrix/lapack/geev.h234
-rw-r--r--ml/dlib/dlib/matrix/lapack/geqrf.h168
-rw-r--r--ml/dlib/dlib/matrix/lapack/gesdd.h364
-rw-r--r--ml/dlib/dlib/matrix/lapack/gesvd.h323
-rw-r--r--ml/dlib/dlib/matrix/lapack/getrf.h132
-rw-r--r--ml/dlib/dlib/matrix/lapack/ormqr.h224
-rw-r--r--ml/dlib/dlib/matrix/lapack/pbtrf.h178
-rw-r--r--ml/dlib/dlib/matrix/lapack/potrf.h174
-rw-r--r--ml/dlib/dlib/matrix/lapack/syev.h218
-rw-r--r--ml/dlib/dlib/matrix/lapack/syevr.h445
-rw-r--r--ml/dlib/dlib/matrix/matrix.h2162
-rw-r--r--ml/dlib/dlib/matrix/matrix_abstract.h857
-rw-r--r--ml/dlib/dlib/matrix/matrix_assign.h978
-rw-r--r--ml/dlib/dlib/matrix/matrix_assign_fwd.h413
-rw-r--r--ml/dlib/dlib/matrix/matrix_blas_bindings.h1637
-rw-r--r--ml/dlib/dlib/matrix/matrix_cholesky.h231
-rw-r--r--ml/dlib/dlib/matrix/matrix_conj_trans.h71
-rw-r--r--ml/dlib/dlib/matrix/matrix_conv.h358
-rw-r--r--ml/dlib/dlib/matrix/matrix_conv_abstract.h158
-rw-r--r--ml/dlib/dlib/matrix/matrix_data_layout.h1271
-rw-r--r--ml/dlib/dlib/matrix/matrix_data_layout_abstract.h40
-rw-r--r--ml/dlib/dlib/matrix/matrix_default_mul.h134
-rw-r--r--ml/dlib/dlib/matrix/matrix_eigenvalue.h1379
-rw-r--r--ml/dlib/dlib/matrix/matrix_exp.h271
-rw-r--r--ml/dlib/dlib/matrix/matrix_exp_abstract.h210
-rw-r--r--ml/dlib/dlib/matrix/matrix_expressions.h280
-rw-r--r--ml/dlib/dlib/matrix/matrix_fft.h846
-rw-r--r--ml/dlib/dlib/matrix/matrix_fft_abstract.h118
-rw-r--r--ml/dlib/dlib/matrix/matrix_fwd.h31
-rw-r--r--ml/dlib/dlib/matrix/matrix_generic_image.h110
-rw-r--r--ml/dlib/dlib/matrix/matrix_la.h1807
-rw-r--r--ml/dlib/dlib/matrix/matrix_la_abstract.h1005
-rw-r--r--ml/dlib/dlib/matrix/matrix_lu.h361
-rw-r--r--ml/dlib/dlib/matrix/matrix_mat.h733
-rw-r--r--ml/dlib/dlib/matrix/matrix_mat_abstract.h243
-rw-r--r--ml/dlib/dlib/matrix/matrix_math_functions.h448
-rw-r--r--ml/dlib/dlib/matrix/matrix_math_functions_abstract.h595
-rw-r--r--ml/dlib/dlib/matrix/matrix_op.h479
-rw-r--r--ml/dlib/dlib/matrix/matrix_qr.h466
-rw-r--r--ml/dlib/dlib/matrix/matrix_read_from_istream.h108
-rw-r--r--ml/dlib/dlib/matrix/matrix_subexp.h1566
-rw-r--r--ml/dlib/dlib/matrix/matrix_subexp_abstract.h570
-rw-r--r--ml/dlib/dlib/matrix/matrix_trsm.h654
-rw-r--r--ml/dlib/dlib/matrix/matrix_utilities.h4544
-rw-r--r--ml/dlib/dlib/matrix/matrix_utilities_abstract.h1874
-rw-r--r--ml/dlib/dlib/matrix/symmetric_matrix_cache.h464
-rw-r--r--ml/dlib/dlib/matrix/symmetric_matrix_cache_abstract.h63
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_
+