forked from g-truc/glm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implements QR and RQ matrix decomposition functions.
- Loading branch information
1 parent
2dc6196
commit d6abdb7
Showing
5 changed files
with
245 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,3 +52,5 @@ Makefile | |
# local build(s) | ||
build* | ||
|
||
/.vs | ||
/CMakeSettings.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/// @ref gtx_matrix_factorisation | ||
/// @file glm/gtx/matrix_factorisation.hpp | ||
/// | ||
/// @see core (dependence) | ||
/// | ||
/// @defgroup gtx_matrix_factorisation GLM_GTX_matrix_factorisation | ||
/// @ingroup gtx | ||
/// | ||
/// @brief Functions to factor matrices in various forms | ||
/// | ||
/// <glm/gtx/matrix_factorisation.hpp> need to be included to use these functionalities. | ||
|
||
#pragma once | ||
|
||
// Dependency: | ||
#include <algorithm> | ||
#include "../glm.hpp" | ||
|
||
#ifndef GLM_ENABLE_EXPERIMENTAL | ||
# error "GLM: GLM_GTX_matrix_factorisation is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." | ||
#endif | ||
|
||
#if GLM_MESSAGES == GLM_MESSAGES_ENABLED && !defined(GLM_EXT_INCLUDED) | ||
# pragma message("GLM: GLM_GTX_matrix_factorisation extension included") | ||
#endif | ||
|
||
/* | ||
Suggestions: | ||
- Move helper functions flipud and flip lr to another file: They may be helpful in more general circumstances. | ||
- When rq_decompose is fed a matrix that has more rows than columns, the resulting r matrix is NOT upper triangular. Is that a bug? | ||
- Implement other types of matrix factorisation, such as: QL and LQ, L(D)U, eigendecompositions, etc... | ||
*/ | ||
|
||
namespace glm{ | ||
/// @addtogroup gtx_matrix_factorisation | ||
/// @{ | ||
|
||
/// Flips the matrix rows up and down. | ||
/// From GLM_GTX_matrix_factorisation extension. | ||
template <length_t C, length_t R, typename T, precision P, template<length_t, length_t, typename, precision> class matType> | ||
GLM_FUNC_DECL matType<C, R, T, P> flipud(const matType<C, R, T, P>& in); | ||
|
||
/// Flips the matrix columns right and left. | ||
/// From GLM_GTX_matrix_factorisation extension. | ||
template <length_t C, length_t R, typename T, precision P, template<length_t, length_t, typename, precision> class matType> | ||
GLM_FUNC_DECL matType<C, R, T, P> fliplr(const matType<C, R, T, P>& in); | ||
|
||
/// Performs QR factorisation of a matrix. | ||
/// Returns 2 matrices, q and r, such that q columns are orthonormal, r is an upper triangular matrix, and q*r=in. | ||
/// r is a square matrix whose dimensions are the same than the width of the input matrix, and q has the same dimensions than the input matrix. | ||
/// From GLM_GTX_matrix_factorisation extension. | ||
template <length_t C, length_t R, typename T, precision P, template<length_t, length_t, typename, precision> class matType> | ||
GLM_FUNC_DECL void qr_decompose(matType<std::min(C, R), R, T, P>& q, matType<C, std::min(C, R), T, P>& r, const matType<C, R, T, P>& in); | ||
|
||
/// Performs RQ factorisation of a matrix. | ||
/// Returns 2 matrices, r and q, such that r is an upper triangular matrix, q rows are orthonormal, and r*q=in. | ||
/// q has the same dimensions than the input matrix, and r is a square matrix whose dimensions are the same than the height of the input matrix. | ||
/// From GLM_GTX_matrix_factorisation extension. | ||
template <length_t C, length_t R, typename T, precision P, template<length_t, length_t, typename, precision> class matType> | ||
GLM_FUNC_DECL void rq_decompose(matType<std::min(C, R), R, T, P>& r, matType<C, std::min(C, R), T, P>& q, const matType<C, R, T, P>& in); | ||
|
||
/// @} | ||
} | ||
|
||
#include "matrix_factorisation.inl" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/// @ref gtx_matrix_factorisation | ||
/// @file glm/gtx/matrix_factorisation.inl | ||
|
||
namespace glm { | ||
template <length_t C, length_t R, typename T, precision P, template<length_t, length_t, typename, precision> class matType> | ||
GLM_FUNC_QUALIFIER matType<C, R, T, P> flipud(const matType<C, R, T, P>& in) { | ||
matType<R, C, T, P> tin = transpose(in); | ||
tin = fliplr(tin); | ||
matType<C, R, T, P> out = transpose(tin); | ||
|
||
return out; | ||
} | ||
|
||
template <length_t C, length_t R, typename T, precision P, template<length_t, length_t, typename, precision> class matType> | ||
GLM_FUNC_QUALIFIER matType<C, R, T, P> fliplr(const matType<C, R, T, P>& in) { | ||
constexpr length_t num_cols = C; | ||
|
||
matType<C, R, T, P> out; | ||
for (length_t i = 0; i < num_cols; i++) { | ||
out[i] = in[(num_cols - i) - 1]; | ||
} | ||
|
||
return out; | ||
} | ||
|
||
template <length_t C, length_t R, typename T, precision P, template<length_t, length_t, typename, precision> class matType> | ||
GLM_FUNC_QUALIFIER void qr_decompose(matType<std::min(C, R), R, T, P>& q, matType<C, std::min(C, R), T, P>& r, const matType<C, R, T, P>& in) { | ||
// Uses modified Gram-Schmidt method | ||
// Source: https://en.wikipedia.org/wiki/Gram–Schmidt_process | ||
// And https://en.wikipedia.org/wiki/QR_decomposition | ||
|
||
for (length_t i = 0; i < std::min(R, C); i++) { | ||
q[i] = in[i]; | ||
|
||
for (length_t j = 0; j < i; j++) { | ||
q[i] -= dot(q[i], q[j])*q[j]; | ||
} | ||
|
||
q[i] = normalize(q[i]); | ||
} | ||
|
||
for (length_t i = 0; i < std::min(R, C); i++) { | ||
for (length_t j = 0; j < i; j++) { | ||
r[j][i] = 0; | ||
} | ||
|
||
for (length_t j = i; j < C; j++) { | ||
r[j][i] = dot(in[j], q[i]); | ||
} | ||
} | ||
} | ||
|
||
template <length_t C, length_t R, typename T, precision P, template<length_t, length_t, typename, precision> class matType> | ||
GLM_FUNC_QUALIFIER void rq_decompose(matType<std::min(C, R), R, T, P>& r, matType<C, std::min(C, R), T, P>& q, const matType<C, R, T, P>& in) { | ||
// From https://en.wikipedia.org/wiki/QR_decomposition: | ||
// The RQ decomposition transforms a matrix A into the product of an upper triangular matrix R (also known as right-triangular) and an orthogonal matrix Q. The only difference from QR decomposition is the order of these matrices. | ||
// QR decomposition is Gram–Schmidt orthogonalization of columns of A, started from the first column. | ||
// RQ decomposition is Gram–Schmidt orthogonalization of rows of A, started from the last row. | ||
|
||
matType<R, C, T, P> tin = transpose(in); | ||
tin = fliplr(tin); | ||
|
||
matType<R, std::min(C, R), T, P> tr; | ||
matType<std::min(C, R), C, T, P> tq; | ||
qr_decompose(tq, tr, tin); | ||
|
||
tr = fliplr(tr); | ||
r = transpose(tr); | ||
r = fliplr(r); | ||
|
||
tq = fliplr(tq); | ||
q = transpose(tq); | ||
} | ||
} //namespace glm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
#define GLM_ENABLE_EXPERIMENTAL | ||
#include <glm/gtx/matrix_factorisation.hpp> | ||
|
||
const double epsilon = 1e-10f; | ||
|
||
template <glm::length_t C, glm::length_t R, typename T, glm::precision P, template<glm::length_t, glm::length_t, typename, glm::precision> class matType> | ||
int test_qr(matType<C, R, T, P> m) { | ||
matType<std::min(C, R), R, T, P> q(-999); | ||
matType<C, std::min(C, R), T, P> r(-999); | ||
|
||
glm::qr_decompose(q, r, m); | ||
|
||
//Test if q*r really equals the input matrix | ||
matType<C, R, T, P> tm = q*r; | ||
matType<C, R, T, P> err = tm - m; | ||
|
||
for (glm::length_t i = 0; i < C; i++) { | ||
for (glm::length_t j = 0; j < R; j++) { | ||
if (abs(err[i][j]) > epsilon) return 1; | ||
} | ||
} | ||
|
||
//Test if the columns of q are orthonormal | ||
for (glm::length_t i = 0; i < std::min(C, R); i++) { | ||
if ((length(q[i]) - 1) > epsilon) return 1; | ||
|
||
for (glm::length_t j = 0; j<i; j++) { | ||
if (abs(dot(q[i], q[j])) > epsilon) return 1; | ||
} | ||
} | ||
|
||
//Test if the matrix r is upper triangular | ||
for (glm::length_t i = 0; i < C; i++) { | ||
for (glm::length_t j = i + 1; j < std::min(C, R); j++) { | ||
if (r[i][j] != 0) return 1; | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
template <glm::length_t C, glm::length_t R, typename T, glm::precision P, template<glm::length_t, glm::length_t, typename, glm::precision> class matType> | ||
int test_rq(matType<C, R, T, P> m) { | ||
matType<C, std::min(C, R), T, P> q(-999); | ||
matType<std::min(C, R), R, T, P> r(-999); | ||
|
||
glm::rq_decompose(r, q, m); | ||
|
||
//Test if q*r really equals the input matrix | ||
matType<C, R, T, P> tm = r*q; | ||
matType<C, R, T, P> err = tm - m; | ||
|
||
for (glm::length_t i = 0; i < C; i++) { | ||
for (glm::length_t j = 0; j < R; j++) { | ||
if (abs(err[i][j]) > epsilon) return 1; | ||
} | ||
} | ||
|
||
|
||
//Test if the rows of q are orthonormal | ||
matType<std::min(C, R), C, T, P> tq = transpose(q); | ||
|
||
for (glm::length_t i = 0; i < std::min(C, R); i++) { | ||
if ((length(tq[i]) - 1) > epsilon) return 1; | ||
|
||
for (glm::length_t j = 0; j<i; j++) { | ||
if (abs(dot(tq[i], tq[j])) > epsilon) return 1; | ||
} | ||
} | ||
|
||
//Test if the matrix r is upper triangular | ||
for (glm::length_t i = 0; i < std::min(C, R); i++) { | ||
for (glm::length_t j = i + 1; j < R; j++) { | ||
if (r[i][j] != 0) return 1; | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int main() | ||
{ | ||
|
||
//Test QR square | ||
if(test_qr(glm::dmat3(12, 6, -4, -51, 167, 24, 4, -68, -41))) return 1; | ||
|
||
//Test RQ square | ||
if (test_rq(glm::dmat3(12, 6, -4, -51, 167, 24, 4, -68, -41))) return 1; | ||
|
||
//Test QR triangular 1 | ||
if (test_qr(glm::dmat3x4(12, 6, -4, -51, 167, 24, 4, -68, -41, 7, 2, 15))) return 1; | ||
|
||
//Test QR triangular 2 | ||
if (test_qr(glm::dmat4x3(12, 6, -4, -51, 167, 24, 4, -68, -41, 7, 2, 15))) return 1; | ||
|
||
//Test RQ triangular 1 : Fails at the triangular test | ||
//if (test_rq(glm::dmat3x4(12, 6, -4, -51, 167, 24, 4, -68, -41, 7, 2, 15))) return 1; | ||
|
||
//Test QR triangular 2 | ||
if (test_rq(glm::dmat4x3(12, 6, -4, -51, 167, 24, 4, -68, -41, 7, 2, 15))) return 1; | ||
|
||
return 0; | ||
} |