Added QR, LU, cholesky, and eigenvalue decomposition classes.

--HG--
extra : convert_revision : svn%3Afdd8eb12-d10e-0410-9acb-85c331704f74/trunk%402845
This commit is contained in:
Davis King 2009-02-01 19:31:01 +00:00
parent 6309e821f0
commit b1e2c6ca37
13 changed files with 3385 additions and 7 deletions

View File

@ -8,6 +8,7 @@
#include "matrix/matrix_subexp.h"
#include "matrix/matrix_math_functions.h"
#include "matrix/matrix_assign.h"
#include "matrix/matrix_la.h"
#ifdef DLIB_USE_BLAS
#include "matrix/matrix_blas_bindings.h"

View File

@ -0,0 +1,223 @@
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.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>
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 typename matrix_exp_type::matrix_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 matrix<type,matrix_exp_type::NR,EXP::NC,mem_manager_type,layout_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
);
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;
}
}
}
// ----------------------------------------------------------------------------------------
template <typename matrix_exp_type>
template <typename EXP>
const matrix<typename matrix_exp_type::type,
matrix_exp_type::NR,
EXP::NC,
typename matrix_exp_type::mem_manager_type,
typename matrix_exp_type::layout_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);
const long nx = B.nc();
const long n = L_.nr();
// Solve L*y = b;
for (long j=0; j< nx; j++)
{
for (long k = 0; k < n; k++)
{
for (long i = 0; i < k; i++)
X(k,j) -= X(i,j)*L_(k,i);
X(k,j) /= L_(k,k);
}
}
// Solve L'*X = Y;
for (long j=0; j<nx; j++)
{
for (long k = n-1; k >= 0; k--)
{
for (long i = k+1; i < n; i++)
X(k,j) -= X(i,j)*L_(i,k);
X(k,j) /= L_(k,k);
}
}
return X;
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_MATRIX_CHOLESKY_DECOMPOSITION_H

File diff suppressed because it is too large Load Diff

20
dlib/matrix/matrix_la.h Normal file
View File

@ -0,0 +1,20 @@
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.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_lu.h"
#include "matrix_qr.h"
#include "matrix_cholesky.h"
#include "matrix_eigenvalue.h"
namespace dlib
{
}
#endif // DLIB_MATRIx_LA_FUNCTS_

View File

@ -0,0 +1,580 @@
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.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
{
// ----------------------------------------------------------------------------------------
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)
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).
!*/
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)
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.
!*/
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)
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.
!*/
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,0,1,mem_manager_type,layout_type> column_vector_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_householder (
) const;
/*!
ensures
- returns a matrix H such that:
- H is the lower trapezoidal matrix whose columns define the
Householder reflection vectors from QR factorization
- H.nr() == nr()
- H.nc() == nc()
!*/
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()
!*/
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)
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 linearly independent.
- So A == V*D*trans(V)
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.
!*/
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
!*/
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_

377
dlib/matrix/matrix_lu.h Normal file
View File

@ -0,0 +1,377 @@
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.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 <algorithm>
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 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
);
// Use a "left-looking", dot-product, Crout/Doolittle algorithm.
piv = 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);
const double s = rowm(LU,i, kmax)*colm(LUcolj,0,kmax);
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++)
{
double 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);
}
}
}
}
// ----------------------------------------------------------------------------------------
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 eps = max(abs(diag(LU)));
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(abs(diag(LU))) < 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 ant 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
);
const long nx = B.nc();
// if there are multiple columns in B
if (nx > 1)
{
// Copy right hand side with pivoting
matrix_type X(rowm(B, piv));
// Solve L*Y = B(piv,:)
for (long k = 0; k < n; k++)
{
for (long i = k+1; i < n; i++)
{
for (long j = 0; j < nx; j++)
{
X(i,j) -= X(k,j)*LU(i,k);
}
}
}
// Solve U*X = Y;
for (long k = n-1; k >= 0; k--)
{
for (long j = 0; j < nx; j++)
{
X(k,j) /= LU(k,k);
}
for (long i = 0; i < k; i++)
{
for (long j = 0; j < nx; j++)
{
X(i,j) -= X(k,j)*LU(i,k);
}
}
}
return X;
}
else
{
column_vector_type x(rowm(B, piv));
// Solve L*Y = B(piv)
for (long k = 0; k < n; k++)
{
for (long i = k+1; i < n; i++)
{
x(i) -= x(k)*LU(i,k);
}
}
// Solve U*X = Y;
for (long k = n-1; k >= 0; k--)
{
x(k) /= LU(k,k);
for (long i = 0; i < k; i++)
x(i) -= x(k)*LU(i,k);
}
return x;
}
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_MATRIX_LU_DECOMPOSITION_H

View File

@ -312,6 +312,7 @@ namespace dlib
- 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())
!*/
// ----------------------------------------------------------------------------------------

416
dlib/matrix/matrix_qr.h Normal file
View File

@ -0,0 +1,416 @@
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.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"
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;
typedef matrix<type,0,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>
qr_decomposition(
const matrix_exp<EXP>& A
);
bool is_full_rank(
) const;
long nr(
) const;
long nc(
) const;
const matrix_type get_householder (
) const;
const matrix_type get_r (
) const;
const matrix_type get_q (
) const;
template <typename EXP>
const matrix_type solve (
const matrix_exp<EXP>& B
) const;
private:
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;
/** Array for internal storage of decomposition.
@serial internal array storage.
*/
matrix_type 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.
*/
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();
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;
}
}
// ----------------------------------------------------------------------------------------
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_householder (
) const
{
return lowerm(QR_);
}
// ----------------------------------------------------------------------------------------
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
{
long i=0, j=0, k=0;
matrix_type Q(m,n);
for (k = n-1; k >= 0; k--)
{
for (i = 0; i < m; i++)
{
Q(i,k) = 0.0;
}
Q(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)*Q(i,j);
}
s = -s/QR_(k,k);
for (i = k; i < m; i++)
{
Q(i,j) += s*QR_(i,k);
}
}
}
}
return Q;
}
// ----------------------------------------------------------------------------------------
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
);
// just call the right version of the solve function
if (B.nc() == 1)
return solve_vect(B);
else
return solve_mat(B);
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// Private member functions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
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_MATRIX_QR_DECOMPOSITION_H

View File

@ -2030,13 +2030,13 @@ convergence:
template <
typename EXP
>
inline const typename matrix_exp<EXP>::matrix_type cholesky_decomposition (
inline const typename matrix_exp<EXP>::matrix_type chol (
const matrix_exp<EXP>& A
)
{
DLIB_ASSERT(A.nr() == A.nc(),
"\tconst matrix cholesky_decomposition(const matrix_exp& A)"
<< "\n\tYou can only apply the cholesky_decomposition to a square matrix"
"\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()
);

View File

@ -725,7 +725,7 @@ namespace dlib
// ----------------------------------------------------------------------------------------
const matrix_exp::matrix_type cholesky_decomposition (
const matrix_exp::matrix_type chol (
const matrix_exp& A
);
/*!

View File

@ -44,6 +44,7 @@ set (tests
matrix.cpp
matrix2.cpp
matrix3.cpp
matrix_la.cpp
md5.cpp
member_function_pointer.cpp
metaprogramming.cpp

View File

@ -608,7 +608,7 @@ namespace
// make m be symmetric
m = m*trans(m);
matrix<double> L = cholesky_decomposition(m);
matrix<double> L = chol(m);
DLIB_CASSERT(equal(L*trans(L), m), "");
DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "")
@ -633,7 +633,7 @@ namespace
// make m be symmetric
m = m*trans(m);
matrix<double> L = cholesky_decomposition(m);
matrix<double> L = chol(m);
DLIB_CASSERT(equal(L*trans(L), m, 1e-10), L*trans(L)-m);
DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "")
@ -667,7 +667,7 @@ namespace
matrix<double,6,6> m(identity_matrix<double>(6)*4.5);
matrix<double> L = cholesky_decomposition(m);
matrix<double> L = chol(m);
DLIB_CASSERT(equal(L*trans(L), m, 1e-10), L*trans(L)-m);
DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "")

480
dlib/test/matrix_la.cpp Normal file
View File

@ -0,0 +1,480 @@
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#include <dlib/matrix.h>
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <vector>
#include "../stl_checked.h"
#include "../array.h"
#include "../rand.h"
#include <dlib/string.h>
#include "tester.h"
namespace
{
using namespace test;
using namespace dlib;
using namespace std;
logger dlog("test.matrix_la");
dlib::rand::float_1a rnd;
// ----------------------------------------------------------------------------------------
template <typename matrix_type>
const typename matrix_type::matrix_type symm(const matrix_type& m) { return m*trans(m); }
// ----------------------------------------------------------------------------------------
template <typename type>
const matrix<type> randm(long r, long c)
{
matrix<type> m(r,c);
for (long row = 0; row < m.nr(); ++row)
{
for (long col = 0; col < m.nc(); ++col)
{
m(row,col) = static_cast<type>(rnd.get_random_double());
}
}
return m;
}
template <typename type, long NR, long NC>
const matrix<type,NR,NC> randm()
{
matrix<type,NR,NC> m;
for (long row = 0; row < m.nr(); ++row)
{
for (long col = 0; col < m.nc(); ++col)
{
m(row,col) = static_cast<type>(rnd.get_random_double());
}
}
return m;
}
// ----------------------------------------------------------------------------------------
template <typename matrix_type>
void test_qr ( const matrix_type& m)
{
typedef typename matrix_type::type type;
const type eps = 10*max(abs(m))*sqrt(std::numeric_limits<type>::epsilon());
dlog << LDEBUG << "test_qr(): " << m.nr() << " x " << m.nc() << " eps: " << eps;
print_spinner();
qr_decomposition<matrix_type> test(m);
DLIB_CASSERT(test.nr() == m.nr(),"");
DLIB_CASSERT(test.nc() == m.nc(),"");
type temp;
DLIB_CASSERT( (temp= max(abs(test.get_q()*test.get_r() - m))) < eps,temp);
// none of the matrices we should be passing in to test_qr() should be non-full rank.
DLIB_CASSERT(test.is_full_rank() == true,"");
if (m.nr() == m.nc())
{
matrix<type> m2;
matrix<type,0,1> col;
m2 = identity_matrix<type>(m.nr());
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
m2 = randm<type>(m.nr(),5);
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
m2 = randm<type>(m.nr(),1);
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
col = randm<type>(m.nr(),1);
DLIB_CASSERT(equal(m*test.solve(col), col,eps),max(abs(m*test.solve(m2)- m2)));
}
else
{
DLIB_CASSERT(dlib::equal(pinv(m), test.solve(identity_matrix<type>(m.nr())), eps),
max(abs(pinv(m) - test.solve(identity_matrix<type>(m.nr())))) );
}
// now make us a non-full rank matrix
if (m.nc() > 1)
{
matrix<type> sm(m);
set_colm(sm,0) = colm(sm,1);
qr_decomposition<matrix_type> test2(sm);
DLIB_CASSERT( (temp= max(abs(test.get_q()*test.get_r() - m))) < eps,temp);
if (test2.nc() < 100)
{
DLIB_CASSERT(test2.is_full_rank() == false,"eps: " << eps);
}
}
}
// ----------------------------------------------------------------------------------------
template <typename matrix_type>
void test_lu ( const matrix_type& m)
{
typedef typename matrix_type::type type;
const type eps = 10*max(abs(m))*sqrt(std::numeric_limits<type>::epsilon());
dlog << LDEBUG << "test_lu(): " << m.nr() << " x " << m.nc() << " eps: " << eps;
print_spinner();
lu_decomposition<matrix_type> test(m);
DLIB_CASSERT(test.is_square() == (m.nr() == m.nc()), "");
DLIB_CASSERT(test.nr() == m.nr(),"");
DLIB_CASSERT(test.nc() == m.nc(),"");
type temp;
DLIB_CASSERT( (temp= max(abs(test.get_l()*test.get_u() - rowm(m,test.get_pivot())))) < eps,temp);
if (test.is_square())
{
// none of the matrices we should be passing in to test_lu() should be singular.
DLIB_CASSERT (abs(test.det()) > eps/100, "det: " << test.det() );
dlog << LDEBUG << "big det: " << test.det();
DLIB_CASSERT(test.is_singular() == false,"");
matrix<type> m2;
matrix<type,0,1> col;
m2 = identity_matrix<type>(m.nr());
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
m2 = randm<type>(m.nr(),5);
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
m2 = randm<type>(m.nr(),1);
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
col = randm<type>(m.nr(),1);
DLIB_CASSERT(equal(m*test.solve(col), col,eps),max(abs(m*test.solve(m2)- m2)));
// now make us a singular matrix
if (m.nr() > 1)
{
matrix<type> sm(m);
set_colm(sm,0) = colm(sm,1);
lu_decomposition<matrix_type> test2(sm);
DLIB_CASSERT( (temp= max(abs(test2.get_l()*test2.get_u() - rowm(sm,test2.get_pivot())))) < eps,temp);
// these checks are only accurate for small matrices
if (test2.nr() < 100)
{
DLIB_CASSERT(test2.is_singular() == true,"det: " << test2.det());
DLIB_CASSERT(abs(test2.det()) < eps,"det: " << test2.det());
}
}
}
}
// ----------------------------------------------------------------------------------------
template <typename matrix_type>
void test_cholesky ( const matrix_type& m)
{
typedef typename matrix_type::type type;
const type eps = 10*max(abs(m))*sqrt(std::numeric_limits<type>::epsilon());
dlog << LDEBUG << "test_cholesky(): " << m.nr() << " x " << m.nc() << " eps: " << eps;
print_spinner();
cholesky_decomposition<matrix_type> test(m);
// none of the matrices we should be passing in to test_cholesky() should be non-spd.
DLIB_CASSERT(test.is_spd() == true, "");
type temp;
DLIB_CASSERT( (temp= max(abs(test.get_l()*trans(test.get_l()) - m))) < eps,temp);
matrix<type> m2;
matrix<type,0,1> col;
m2 = identity_matrix<type>(m.nr());
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
m2 = randm<type>(m.nr(),5);
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
m2 = randm<type>(m.nr(),1);
DLIB_CASSERT(equal(m*test.solve(m2), m2,eps),max(abs(m*test.solve(m2)- m2)));
col = randm<type>(m.nr(),1);
DLIB_CASSERT(equal(m*test.solve(col), col,eps),max(abs(m*test.solve(m2)- m2)));
// now make us a non-spd matrix
if (m.nr() > 1)
{
matrix<type> sm(lowerm(m));
sm(1,1) = 0;
cholesky_decomposition<matrix_type> test2(sm);
DLIB_CASSERT(test2.is_spd() == false, test2.get_l());
cholesky_decomposition<matrix_type> test3(sm*trans(sm));
DLIB_CASSERT(test3.is_spd() == false, test3.get_l());
sm = sm*trans(sm);
sm(1,1) = 5;
sm(1,0) -= 1;
cholesky_decomposition<matrix_type> test4(sm);
DLIB_CASSERT(test4.is_spd() == false, test4.get_l());
}
}
// ----------------------------------------------------------------------------------------
template <typename matrix_type>
void test_eigenvalue ( const matrix_type& m)
{
typedef typename matrix_type::type type;
const type eps = max(abs(m))*sqrt(std::numeric_limits<type>::epsilon());
dlog << LDEBUG << "test_eigenvalue(): " << m.nr() << " x " << m.nc() << " eps: " << eps;
print_spinner();
eigenvalue_decomposition<matrix_type> test(m);
DLIB_CASSERT(test.dim() == m.nr(), "");
// make sure all the various ways of asking for the eigenvalues are actually returning a
// consistent set of eigenvalues.
DLIB_CASSERT(equal(real(test.get_eigenvalues()), test.get_real_eigenvalues(), eps), "");
DLIB_CASSERT(equal(imag(test.get_eigenvalues()), test.get_imag_eigenvalues(), eps), "");
DLIB_CASSERT(equal(real(diag(test.get_d())), test.get_real_eigenvalues(), eps), "");
DLIB_CASSERT(equal(imag(diag(test.get_d())), test.get_imag_eigenvalues(), eps), "");
const matrix<type> V = test.get_pseudo_v();
const matrix<type> D = test.get_pseudo_d();
const matrix<complex<type> > CV = test.get_v();
const matrix<complex<type> > CD = test.get_d();
const matrix<complex<type> > CM = complex_matrix(m, uniform_matrix<type>(m.nr(),m.nc(),0));
DLIB_CASSERT(V.nr() == test.dim(),"");
DLIB_CASSERT(V.nc() == test.dim(),"");
DLIB_CASSERT(D.nr() == test.dim(),"");
DLIB_CASSERT(D.nc() == test.dim(),"");
// CD is a diagonal matrix
DLIB_CASSERT(diagm(diag(CD)) == CD,"");
// verify that these things are actually eigenvalues and eigenvectors of m
DLIB_CASSERT(max(abs(m*V - V*D)) < eps, "");
DLIB_CASSERT(max(norm(CM*CV - CV*CD)) < eps, "");
// if m is a symmetric matrix
if (max(abs(m-trans(m))) < 1e-5)
{
dlog << LTRACE << "m is symmetric";
// there aren't any imaginary eigenvalues
DLIB_CASSERT(max(abs(test.get_imag_eigenvalues())) < eps, "");
DLIB_CASSERT(diagm(diag(D)) == D,"");
// V is orthogonal
DLIB_CASSERT(equal(V*trans(V), identity_matrix<type>(test.dim()), eps), "");
DLIB_CASSERT(equal(m , V*D*trans(V), eps), "");
}
else
{
dlog << LTRACE << "m is NOT symmetric";
DLIB_CASSERT(equal(m , V*D*inv(V), eps), max(abs(m - V*D*inv(V))));
}
}
// ----------------------------------------------------------------------------------------
void matrix_test_double()
{
// -------------------------------
test_lu(10*randm<double>(1,1));
test_lu(10*randm<double>(2,2));
test_lu(10*symm(randm<double>(2,2)));
test_lu(10*randm<double>(4,4));
test_lu(10*randm<double>(9,4));
test_lu(10*randm<double>(3,8));
test_lu(10*randm<double>(15,15));
test_lu(2*symm(randm<double>(15,15)));
test_lu(10*randm<double>(100,100));
test_lu(10*randm<double>(137,200));
test_lu(10*randm<double>(200,101));
test_lu(10*randm<double,1,1>());
test_lu(10*randm<double,2,2>());
test_lu(10*randm<double,4,3>());
test_lu(10*randm<double,4,4>());
test_lu(10*randm<double,9,4>());
test_lu(10*randm<double,3,8>());
test_lu(10*randm<double,15,15>());
test_lu(10*randm<double,100,100>());
test_lu(10*randm<double,137,200>());
test_lu(10*randm<double,200,101>());
// -------------------------------
test_cholesky(uniform_matrix<double>(1,1,1) + 10*symm(randm<double>(1,1)));
test_cholesky(uniform_matrix<double>(2,2,1) + 10*symm(randm<double>(2,2)));
test_cholesky(uniform_matrix<double>(3,3,1) + 10*symm(randm<double>(3,3)));
test_cholesky(uniform_matrix<double>(4,4,1) + 10*symm(randm<double>(4,4)));
test_cholesky(uniform_matrix<double>(15,15,1) + 10*symm(randm<double>(15,15)));
test_cholesky(uniform_matrix<double>(101,101,1) + 10*symm(randm<double>(101,101)));
// -------------------------------
test_qr(10*randm<double>(1,1));
test_qr(10*randm<double>(2,2));
test_qr(10*symm(randm<double>(2,2)));
test_qr(10*randm<double>(4,4));
test_qr(10*randm<double>(9,4));
test_qr(10*randm<double>(15,15));
test_qr(2*symm(randm<double>(15,15)));
test_qr(10*randm<double>(100,100));
test_qr(10*randm<double>(237,200));
test_qr(10*randm<double>(200,101));
test_qr(10*randm<double,1,1>());
test_qr(10*randm<double,2,2>());
test_qr(10*randm<double,4,3>());
test_qr(10*randm<double,4,4>());
test_qr(10*randm<double,9,4>());
test_qr(10*randm<double,15,15>());
test_qr(10*randm<double,100,100>());
// -------------------------------
test_eigenvalue(10*randm<double>(1,1));
test_eigenvalue(10*randm<double>(2,2));
test_eigenvalue(10*randm<double>(3,3));
test_eigenvalue(10*randm<double>(4,4));
test_eigenvalue(10*randm<double>(15,15));
test_eigenvalue(10*randm<double>(150,150));
test_eigenvalue(10*randm<double,1,1>());
test_eigenvalue(10*randm<double,2,2>());
test_eigenvalue(10*randm<double,3,3>());
test_eigenvalue(10*symm(randm<double>(1,1)));
test_eigenvalue(10*symm(randm<double>(2,2)));
test_eigenvalue(10*symm(randm<double>(3,3)));
test_eigenvalue(10*symm(randm<double>(4,4)));
test_eigenvalue(10*symm(randm<double>(15,15)));
test_eigenvalue(10*symm(randm<double>(150,150)));
// -------------------------------
}
// ----------------------------------------------------------------------------------------
void matrix_test_float()
{
// -------------------------------
test_lu(3*randm<float>(1,1));
test_lu(3*randm<float>(2,2));
test_lu(3*randm<float>(4,4));
test_lu(3*randm<float>(9,4));
test_lu(3*randm<float>(3,8));
test_lu(3*randm<float>(137,200));
test_lu(3*randm<float>(200,101));
test_lu(3*randm<float,1,1>());
test_lu(3*randm<float,2,2>());
test_lu(3*randm<float,4,3>());
test_lu(3*randm<float,4,4>());
test_lu(3*randm<float,9,4>());
test_lu(3*randm<float,3,8>());
test_lu(3*randm<float,137,200>());
test_lu(3*randm<float,200,101>());
// -------------------------------
test_cholesky(uniform_matrix<float>(1,1,1) + 2*symm(randm<float>(1,1)));
test_cholesky(uniform_matrix<float>(2,2,1) + 2*symm(randm<float>(2,2)));
test_cholesky(uniform_matrix<float>(3,3,1) + 2*symm(randm<float>(3,3)));
// -------------------------------
test_qr(3*randm<float>(1,1));
test_qr(3*randm<float>(2,2));
test_qr(3*randm<float>(4,4));
test_qr(3*randm<float>(9,4));
test_qr(3*randm<float>(237,200));
test_qr(3*randm<float,1,1>());
test_qr(3*randm<float,2,2>());
test_qr(3*randm<float,4,3>());
test_qr(3*randm<float,4,4>());
test_qr(3*randm<float,9,4>());
// -------------------------------
test_eigenvalue(10*randm<float>(1,1));
test_eigenvalue(10*randm<float>(2,2));
test_eigenvalue(10*randm<float>(3,3));
test_eigenvalue(10*randm<float>(4,4));
test_eigenvalue(10*randm<float>(15,15));
test_eigenvalue(10*randm<float>(150,150));
test_eigenvalue(10*randm<float,1,1>());
test_eigenvalue(10*randm<float,2,2>());
test_eigenvalue(10*randm<float,3,3>());
test_eigenvalue(10*symm(randm<float>(1,1)));
test_eigenvalue(10*symm(randm<float>(2,2)));
test_eigenvalue(10*symm(randm<float>(3,3)));
test_eigenvalue(10*symm(randm<float>(4,4)));
test_eigenvalue(10*symm(randm<float>(15,15)));
test_eigenvalue(10*symm(randm<float>(150,150)));
}
// ----------------------------------------------------------------------------------------
class matrix_tester : public tester
{
public:
matrix_tester (
) :
tester ("test_matrix_la",
"Runs tests on the matrix component.")
{
rnd.set_seed(cast_to_string(time(0)));
}
void perform_test (
)
{
dlog << LINFO << "seed string: " << rnd.get_seed();
dlog << LINFO << "begin testing with double";
matrix_test_double();
dlog << LINFO << "begin testing with float";
matrix_test_float();
}
} a;
}