mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
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:
parent
6309e821f0
commit
b1e2c6ca37
@ -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"
|
||||
|
223
dlib/matrix/matrix_cholesky.h
Normal file
223
dlib/matrix/matrix_cholesky.h
Normal 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
|
||||
|
||||
|
||||
|
||||
|
1279
dlib/matrix/matrix_eigenvalue.h
Normal file
1279
dlib/matrix/matrix_eigenvalue.h
Normal file
File diff suppressed because it is too large
Load Diff
20
dlib/matrix/matrix_la.h
Normal file
20
dlib/matrix/matrix_la.h
Normal 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_
|
||||
|
||||
|
580
dlib/matrix/matrix_la_abstract.h
Normal file
580
dlib/matrix/matrix_la_abstract.h
Normal 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
377
dlib/matrix/matrix_lu.h
Normal 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
|
||||
|
||||
|
@ -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
416
dlib/matrix/matrix_qr.h
Normal 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
|
||||
|
||||
|
||||
|
@ -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()
|
||||
);
|
||||
|
@ -725,7 +725,7 @@ namespace dlib
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
const matrix_exp::matrix_type cholesky_decomposition (
|
||||
const matrix_exp::matrix_type chol (
|
||||
const matrix_exp& A
|
||||
);
|
||||
/*!
|
||||
|
@ -44,6 +44,7 @@ set (tests
|
||||
matrix.cpp
|
||||
matrix2.cpp
|
||||
matrix3.cpp
|
||||
matrix_la.cpp
|
||||
md5.cpp
|
||||
member_function_pointer.cpp
|
||||
metaprogramming.cpp
|
||||
|
@ -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
480
dlib/test/matrix_la.cpp
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user