1 /* -*- C++ -*- ------------------------------------------------------------
3 Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/
5 The Configurable Math Library (CML) is distributed under the terms of the
6 Boost Software License, v1.0 (see cml/LICENSE for details).
8 *-----------------------------------------------------------------------*/
10 * @brief Implements LU decomposition for square matrix expressions.
12 * @todo The LU implementation does not check for a zero diagonal entry
13 * (implying that the input has no LU factorization).
15 * @todo Should also have a pivoting implementation.
17 * @todo need to throw a numeric error if the determinant of the matrix
18 * given to lu(), lu_solve(), or inverse() is 0.
20 * @internal The implementation is the same for fixed- and dynamic-size
21 * matrices. It can be sped up for small matrices later.
27 #include <cml/et/size_checking.h>
28 #include <cml/matrix/matrix_expr.h>
29 #include <cml/matvec/matvec_promotions.h>
31 /* This is used below to create a more meaningful compile-time error when
32 * lu is not provided with a matrix or MatrixExpr argument:
34 struct lu_expects_a_matrix_arg_error
;
36 /* This is used below to create a more meaningful compile-time error when
37 * lu_inplace is not provided with an assignable matrix argument:
39 struct lu_inplace_expects_an_assignable_matrix_arg_error
;
44 /* Compute the LU decomposition in-place: */
45 template<class MatT
> inline
46 void lu_inplace(MatT
& A
)
49 typedef et::ExprTraits
<MatT
> arg_traits
;
50 typedef typename
arg_traits::result_tag arg_result
;
51 typedef typename
arg_traits::assignable_tag arg_assignment
;
52 typedef typename
arg_traits::size_tag size_tag
;
53 typedef typename
arg_traits::value_type value_type
;
55 /* lu_inplace() requires an assignable matrix expression: */
57 (same_type
<arg_result
, et::matrix_result_tag
>::is_true
58 && same_type
<arg_assignment
, et::assignable_tag
>::is_true
),
59 lu_inplace_expects_an_assignable_matrix_arg_error
);
60 /* Note: parens are required here so that the preprocessor ignores the
64 /* Verify that the matrix is square, and get the size: */
65 ssize_t N
= (ssize_t
) cml::et::CheckedSquare(A
, size_tag());
68 for(ssize_t k
= 0; k
< N
-1; ++k
) {
69 /* XXX Should check if A(k,k) = 0! */
70 for(ssize_t i
= k
+1; i
< N
; ++i
) {
71 value_type n
= (A(i
,k
) /= A(k
,k
));
72 for(ssize_t j
= k
+1; j
< N
; ++ j
) {
79 /* Compute the LU decomposition, and return a copy of the result: */
81 inline typename
MatT::temporary_type
82 lu_copy(const MatT
& M
)
85 typedef et::ExprTraits
<MatT
> arg_traits
;
86 typedef typename
arg_traits::result_tag arg_result
;
88 /* lu_with_copy() requires a matrix expression: */
90 (same_type
<arg_result
, et::matrix_result_tag
>::is_true
),
91 lu_expects_a_matrix_arg_error
);
92 /* Note: parens are required here so that the preprocessor ignores the
96 /* Use the in-place LU function, and return the result: */
97 typename
MatT::temporary_type A
;
98 cml::et::detail::Resize(A
,M
.rows(),M
.cols());
104 } // namespace detail
106 /** LU factorization for a matrix. */
107 template<typename E
, class AT
, typename BO
, class L
>
108 inline typename matrix
<E
,AT
,BO
,L
>::temporary_type
109 lu(const matrix
<E
,AT
,BO
,L
>& m
)
111 return detail::lu_copy(m
);
114 /** LU factorization for a matrix expression. */
115 template<typename XprT
>
116 inline typename
et::MatrixXpr
<XprT
>::temporary_type
117 lu(const et::MatrixXpr
<XprT
>& e
)
119 return detail::lu_copy(e
);
122 /** Solve y = LUx for x.
124 * This solves Lb = y for b by forward substitution, then Ux = b for x by
125 * backward substitution.
127 template<typename MatT
, typename VecT
> inline
128 typename
et::MatVecPromote
<MatT
,VecT
>::temporary_type
129 lu_solve(const MatT
& LU
, const VecT
& b
)
132 typedef et::ExprTraits
<MatT
> lu_traits
;
133 typedef typename
et::MatVecPromote
<MatT
,VecT
>::temporary_type vector_type
;
134 typedef typename
vector_type::value_type value_type
;
136 /* Verify that the matrix is square, and get the size: */
137 ssize_t N
= (ssize_t
) cml::et::CheckedSquare(
138 LU
, typename
lu_traits::size_tag());
140 /* Verify that the matrix and vector have compatible sizes: */
141 et::CheckedSize(LU
, b
, typename
vector_type::size_tag());
143 /* Solve Ly = b for y by forward substitution. The entries below the
144 * diagonal of LU correspond to L, understood to be below a diagonal of
147 vector_type y
; cml::et::detail::Resize(y
,N
);
148 for(ssize_t i
= 0; i
< N
; ++i
) {
150 for(ssize_t j
= 0; j
< i
; ++j
) {
151 y
[i
] -= LU(i
,j
)*y
[j
];
155 /* Solve Ux = y for x by backward substitution. The entries at and above
156 * the diagonal of LU correspond to U:
158 vector_type x
; cml::et::detail::Resize(x
,N
);
159 for(ssize_t i
= N
-1; i
>= 0; --i
) {
161 for(ssize_t j
= i
+1; j
< N
; ++j
) {
162 x
[i
] -= LU(i
,j
)*x
[j
];
175 // -------------------------------------------------------------------------