Commit b1e2c6ca authored by Davis King's avatar Davis King
Browse files

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

--HG--
extra : convert_revision : svn%3Afdd8eb12-d10e-0410-9acb-85c331704f74/trunk%402845
parent 6309e821
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "matrix/matrix_subexp.h" #include "matrix/matrix_subexp.h"
#include "matrix/matrix_math_functions.h" #include "matrix/matrix_math_functions.h"
#include "matrix/matrix_assign.h" #include "matrix/matrix_assign.h"
#include "matrix/matrix_la.h"
#ifdef DLIB_USE_BLAS #ifdef DLIB_USE_BLAS
#include "matrix/matrix_blas_bindings.h" #include "matrix/matrix_blas_bindings.h"
......
// 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
This diff is collapsed.
// 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_
This diff is collapsed.
// 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 ...@@ -312,6 +312,7 @@ namespace dlib
- R has the same dimensions as m - R has the same dimensions as m
- for all valid r and c: - for all valid r and c:
R(r,c) == std::norm(m(r,c)) R(r,c) == std::norm(m(r,c))
(note that std::norm(val) == val.real()*val.real() + val.imag()*val.imag())
!*/ !*/
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
......
// 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: ...@@ -2030,13 +2030,13 @@ convergence:
template < template <
typename EXP 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 const matrix_exp<EXP>& A
) )
{ {
DLIB_ASSERT(A.nr() == A.nc(), DLIB_ASSERT(A.nr() == A.nc(),
"\tconst matrix cholesky_decomposition(const matrix_exp& A)" "\tconst matrix chol(const matrix_exp& A)"
<< "\n\tYou can only apply the cholesky_decomposition to a square matrix" << "\n\tYou can only apply the chol to a square matrix"
<< "\n\tA.nr(): " << A.nr() << "\n\tA.nr(): " << A.nr()
<< "\n\tA.nc(): " << A.nc() << "\n\tA.nc(): " << A.nc()
); );
......
...@@ -725,7 +725,7 @@ namespace dlib ...@@ -725,7 +725,7 @@ namespace dlib
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
const matrix_exp::matrix_type cholesky_decomposition ( const matrix_exp::matrix_type chol (
const matrix_exp& A const matrix_exp& A
); );
/*! /*!
......
...@@ -44,6 +44,7 @@ set (tests ...@@ -44,6 +44,7 @@ set (tests
matrix.cpp matrix.cpp
matrix2.cpp matrix2.cpp
matrix3.cpp matrix3.cpp
matrix_la.cpp
md5.cpp md5.cpp
member_function_pointer.cpp member_function_pointer.cpp
metaprogramming.cpp metaprogramming.cpp
......
...@@ -608,7 +608,7 @@ namespace ...@@ -608,7 +608,7 @@ namespace
// make m be symmetric // make m be symmetric
m = m*trans(m); 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(L*trans(L), m), "");
DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "") DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "")
...@@ -633,7 +633,7 @@ namespace ...@@ -633,7 +633,7 @@ namespace
// make m be symmetric // make m be symmetric
m = m*trans(m); 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(L*trans(L), m, 1e-10), L*trans(L)-m);
DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "") DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "")
...@@ -667,7 +667,7 @@ namespace ...@@ -667,7 +667,7 @@ namespace
matrix<double,6,6> m(identity_matrix<double>(6)*4.5); 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(L*trans(L), m, 1e-10), L*trans(L)-m);
DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "") DLIB_CASSERT(equal(inv(m), inv_upper_triangular(trans(L))*inv_lower_triangular((L))), "")
......
// 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;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment