inverse.h

Go to the documentation of this file.
00001 /* -*- C++ -*- ------------------------------------------------------------
00002  
00003 Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/
00004 
00005 The Configurable Math Library (CML) is distributed under the terms of the
00006 Boost Software License, v1.0 (see cml/LICENSE for details).
00007 
00008  *-----------------------------------------------------------------------*/
00013 #ifndef matrix_inverse_h
00014 #define matrix_inverse_h
00015 
00016 #include <cml/matrix/lu.h>
00017 
00018 namespace cml {
00019 namespace detail {
00020 
00021 /* Need to use a functional, since template functions cannot be
00022  * specialized. _tag is used to specialize based upon dimension:
00023  */
00024 template<typename MatT, int _tag> struct inverse_f;
00025 
00026 /* @todo: Reciprocal optimization for division by determinant.
00027  */
00028 
00029 /* 2x2 inverse.  Despite being marked for fixed_size matrices, this can
00030  * be used for dynamic-sized ones also:
00031  */
00032 template<typename MatT>
00033 struct inverse_f<MatT,2>
00034 {
00035     typename MatT::temporary_type operator()(const MatT& M) const
00036     {
00037         typedef typename MatT::temporary_type temporary_type;
00038         typedef typename temporary_type::value_type value_type;
00039 
00040         /* Matrix containing the inverse: */
00041         temporary_type Z;
00042         cml::et::detail::Resize(Z,2,2);
00043 
00044         /* Compute determinant and inverse: */
00045         value_type D = value_type(1) / (M(0,0)*M(1,1) - M(0,1)*M(1,0));
00046         Z(0,0) =   M(1,1)*D; Z(0,1) = - M(0,1)*D;
00047         Z(1,0) = - M(1,0)*D; Z(1,1) =   M(0,0)*D;
00048 
00049         return Z;
00050     }
00051 };
00052 
00053 /* 3x3 inverse.  Despite being marked for fixed_size matrices, this can
00054  * be used for dynamic-sized ones also:
00055  */
00056 template<typename MatT>
00057 struct inverse_f<MatT,3>
00058 {
00059     /*     [00 01 02]
00060      * M = [10 11 12]
00061      *     [20 21 22]
00062      */
00063     typename MatT::temporary_type operator()(const MatT& M) const
00064     {
00065         /* Shorthand. */
00066         typedef typename MatT::value_type value_type;
00067 
00068         /* Compute cofactors for each entry: */
00069         value_type m_00 = M(1,1)*M(2,2) - M(1,2)*M(2,1);
00070         value_type m_01 = M(1,2)*M(2,0) - M(1,0)*M(2,2);
00071         value_type m_02 = M(1,0)*M(2,1) - M(1,1)*M(2,0);
00072 
00073         value_type m_10 = M(0,2)*M(2,1) - M(0,1)*M(2,2);
00074         value_type m_11 = M(0,0)*M(2,2) - M(0,2)*M(2,0);
00075         value_type m_12 = M(0,1)*M(2,0) - M(0,0)*M(2,1);
00076 
00077         value_type m_20 = M(0,1)*M(1,2) - M(0,2)*M(1,1);
00078         value_type m_21 = M(0,2)*M(1,0) - M(0,0)*M(1,2);
00079         value_type m_22 = M(0,0)*M(1,1) - M(0,1)*M(1,0);
00080 
00081         /* Compute determinant from the minors: */
00082         value_type D =
00083             value_type(1) / (M(0,0)*m_00 + M(0,1)*m_01 + M(0,2)*m_02);
00084 
00085         /* Matrix containing the inverse: */
00086         typename MatT::temporary_type Z;
00087         cml::et::detail::Resize(Z,3,3);
00088 
00089         /* Assign the inverse as (1/D) * (cofactor matrix)^T: */
00090         Z(0,0) = m_00*D;  Z(0,1) = m_10*D;  Z(0,2) = m_20*D;
00091         Z(1,0) = m_01*D;  Z(1,1) = m_11*D;  Z(1,2) = m_21*D;
00092         Z(2,0) = m_02*D;  Z(2,1) = m_12*D;  Z(2,2) = m_22*D;
00093 
00094         return Z;
00095     }
00096 };
00097 
00098 /* 4x4 inverse.  Despite being marked for fixed_size matrices, this can
00099  * be used for dynamic-sized ones also:
00100  */
00101 template<typename MatT>
00102 struct inverse_f<MatT,4>
00103 {
00104     /*     [00 01 02 03]
00105      * M = [10 11 12 13]
00106      *     [20 21 22 23]
00107      *     [30 31 32 33]
00108      *
00109      *       |11 12 13|         |10 12 13|
00110      * C00 = |21 22 23|   C01 = |20 22 23|
00111      *       |31 32 33|         |30 32 33|
00112      *
00113      *       |10 11 13|         |10 11 12|
00114      * C02 = |20 21 23|   C03 = |20 21 22|
00115      *       |30 31 33|         |30 31 32|
00116      */
00117     typename MatT::temporary_type operator()(const MatT& M) const
00118     {
00119         /* Shorthand. */
00120         typedef typename MatT::value_type value_type;
00121 
00122         /* Common cofactors, rows 0,1: */
00123         value_type m_22_33_23_32 = M(2,2)*M(3,3) - M(2,3)*M(3,2);
00124         value_type m_23_30_20_33 = M(2,3)*M(3,0) - M(2,0)*M(3,3);
00125         value_type m_20_31_21_30 = M(2,0)*M(3,1) - M(2,1)*M(3,0);
00126         value_type m_21_32_22_31 = M(2,1)*M(3,2) - M(2,2)*M(3,1);
00127         value_type m_23_31_21_33 = M(2,3)*M(3,1) - M(2,1)*M(3,3);
00128         value_type m_20_32_22_30 = M(2,0)*M(3,2) - M(2,2)*M(3,0);
00129 
00130         /* Compute minors: */
00131         value_type d00
00132             = M(1,1)*m_22_33_23_32+M(1,2)*m_23_31_21_33+M(1,3)*m_21_32_22_31;
00133 
00134         value_type d01
00135             = M(1,0)*m_22_33_23_32+M(1,2)*m_23_30_20_33+M(1,3)*m_20_32_22_30;
00136 
00137         value_type d02
00138             = M(1,0)*-m_23_31_21_33+M(1,1)*m_23_30_20_33+M(1,3)*m_20_31_21_30;
00139 
00140         value_type d03
00141             = M(1,0)*m_21_32_22_31+M(1,1)*-m_20_32_22_30+M(1,2)*m_20_31_21_30;
00142 
00143         /* Compute minors: */
00144         value_type d10
00145             = M(0,1)*m_22_33_23_32+M(0,2)*m_23_31_21_33+M(0,3)*m_21_32_22_31;
00146 
00147         value_type d11
00148             = M(0,0)*m_22_33_23_32+M(0,2)*m_23_30_20_33+M(0,3)*m_20_32_22_30;
00149 
00150         value_type d12
00151             = M(0,0)*-m_23_31_21_33+M(0,1)*m_23_30_20_33+M(0,3)*m_20_31_21_30;
00152 
00153         value_type d13
00154             = M(0,0)*m_21_32_22_31+M(0,1)*-m_20_32_22_30+M(0,2)*m_20_31_21_30;
00155 
00156         /* Common cofactors, rows 2,3: */
00157         value_type m_02_13_03_12 = M(0,2)*M(1,3) - M(0,3)*M(1,2);
00158         value_type m_03_10_00_13 = M(0,3)*M(1,0) - M(0,0)*M(1,3);
00159         value_type m_00_11_01_10 = M(0,0)*M(1,1) - M(0,1)*M(1,0);
00160         value_type m_01_12_02_11 = M(0,1)*M(1,2) - M(0,2)*M(1,1);
00161         value_type m_03_11_01_13 = M(0,3)*M(1,1) - M(0,1)*M(1,3);
00162         value_type m_00_12_02_10 = M(0,0)*M(1,2) - M(0,2)*M(1,0);
00163 
00164         /* Compute minors (uses row 3 as the multipliers instead of row 0,
00165          * which uses the same signs as row 0):
00166          */
00167         value_type d20
00168             = M(3,1)*m_02_13_03_12+M(3,2)*m_03_11_01_13+M(3,3)*m_01_12_02_11;
00169 
00170         value_type d21
00171             = M(3,0)*m_02_13_03_12+M(3,2)*m_03_10_00_13+M(3,3)*m_00_12_02_10;
00172 
00173         value_type d22
00174             = M(3,0)*-m_03_11_01_13+M(3,1)*m_03_10_00_13+M(3,3)*m_00_11_01_10;
00175 
00176         value_type d23
00177             = M(3,0)*m_01_12_02_11+M(3,1)*-m_00_12_02_10+M(3,2)*m_00_11_01_10;
00178 
00179         /* Compute minors: */
00180         value_type d30
00181             = M(2,1)*m_02_13_03_12+M(2,2)*m_03_11_01_13+M(2,3)*m_01_12_02_11;
00182 
00183         value_type d31
00184             = M(2,0)*m_02_13_03_12+M(2,2)*m_03_10_00_13+M(2,3)*m_00_12_02_10;
00185 
00186         value_type d32
00187             = M(2,0)*-m_03_11_01_13+M(2,1)*m_03_10_00_13+M(2,3)*m_00_11_01_10;
00188 
00189         value_type d33
00190             = M(2,0)*m_01_12_02_11+M(2,1)*-m_00_12_02_10+M(2,2)*m_00_11_01_10;
00191 
00192         /* Finally, compute determinant from the minors, and assign the
00193          * inverse as (1/D) * (cofactor matrix)^T:
00194          */
00195         typename MatT::temporary_type Z;
00196         cml::et::detail::Resize(Z,4,4);
00197 
00198         value_type D = value_type(1) /
00199             (M(0,0)*d00 - M(0,1)*d01 + M(0,2)*d02 - M(0,3)*d03);
00200         Z(0,0) = +d00*D; Z(0,1) = -d10*D; Z(0,2) = +d20*D; Z(0,3) = -d30*D;
00201         Z(1,0) = -d01*D; Z(1,1) = +d11*D; Z(1,2) = -d21*D; Z(1,3) = +d31*D;
00202         Z(2,0) = +d02*D; Z(2,1) = -d12*D; Z(2,2) = +d22*D; Z(2,3) = -d32*D;
00203         Z(3,0) = -d03*D; Z(3,1) = +d13*D; Z(3,2) = -d23*D; Z(3,3) = +d33*D;
00204 
00205         return Z;
00206     }
00207 };
00208 
00209 /* If more extensive general linear algebra functionality is offered in
00210  * future versions it may be useful to make the elementary row and column
00211  * operations separate functions. For now they're simply performed in place,
00212  * but the commented-out lines of code show where the calls to these functions
00213  * should go if and when they become available.
00214  */
00215  
00216 /* @todo: In-place version, and address memory allocation for pivot vector.
00217  */
00218 
00219 /* General NxN inverse by Gauss-Jordan elimination with full pivoting:  */
00220 template<typename MatT, int _tag>
00221 struct inverse_f
00222 {
00223     typename MatT::temporary_type operator()(const MatT& M) const
00224     {
00225         /* Shorthand. */
00226         typedef typename MatT::value_type value_type;
00227         
00228         /* Size of matrix */
00229         size_t N = M.rows();
00230 
00231         /* Matrix containing the inverse: */
00232         typename MatT::temporary_type Z;
00233         cml::et::detail::Resize(Z,N,N);
00234         Z = M;
00235 
00236         /* For tracking pivots */
00237         std::vector<size_t> row_index(N);
00238         std::vector<size_t> col_index(N);
00239         std::vector<size_t> pivoted(N,0);
00240 
00241         /* For each column */
00242         for (size_t i = 0; i < N; ++i) {
00243         
00244             /* Find the pivot */
00245             size_t row = 0, col = 0;
00246             value_type max = value_type(0);
00247             for (size_t j = 0; j < N; ++j) {
00248                 if (!pivoted[j]) {
00249                     for (size_t k = 0; k < N; ++k) {
00250                         if (!pivoted[k]) {
00251                             value_type mag = std::fabs(Z(j,k));
00252                             if (mag > max) {
00253                                 max = mag;
00254                                 row = j;
00255                                 col = k;
00256                             }
00257                         }
00258                     }
00259                 }
00260             }
00261 
00262             /* TODO: Check max against epsilon here to catch singularity */
00263 
00264             row_index[i] = row;
00265             col_index[i] = col;
00266 
00267             /* Swap rows if necessary */
00268             if (row != col) {
00269                 /*Z.row_op_swap(row,col);*/
00270                 for (size_t j = 0; j < Z.cols(); ++j) {
00271                     std::swap(Z(row,j),Z(col,j));
00272                 }
00273             }
00274             
00275             /* Process pivot row */
00276             pivoted[col] = true;
00277             value_type pivot = Z(col,col);
00278             Z(col,col) = value_type(1);
00279             /*Z.row_op_mult(col,value_type(1)/pivot);*/
00280             value_type k = value_type(1)/pivot;
00281             for (size_t j = 0; j < Z.cols(); ++j) {
00282                 Z(col,j) *= k;
00283             }
00284 
00285             /* Process other rows */
00286             for (size_t j = 0; j < N; ++j) {
00287                 if (j != col) {
00288                     value_type mult = -Z(j,col);
00289                     Z(j,col) = value_type(0);
00290                     /*Z.row_op_add_mult(col,j,mult);*/
00291                     for (size_t k = 0; k < Z.cols(); ++k) {
00292                         Z(j,k) += mult * Z(col,k);
00293                     }
00294                 }
00295             }
00296         }
00297 
00298         /* Swap columns if necessary */
00299         for (int i = N-1; i >= 0; --i) {
00300             if (row_index[i] != col_index[i]) {
00301                 /*Z.col_op_swap(row_index[i],col_index[i]);*/
00302                 for (size_t j = 0; j < Z.rows(); ++j) {
00303                     std::swap(Z(j,row_index[i]),Z(j,col_index[i]));
00304                 }
00305             }
00306         }
00307 
00308         /* Return result */
00309         return Z;
00310     }
00311 };
00312 
00313 /* Inversion by LU factorization is turned off for now due to lack of
00314  * pivoting in the implementation, but we may switch back to it at some future
00315  * time.
00316  */
00317  
00318 #if 0
00319 
00320 /* General NxN inverse by LU factorization:  */
00321 template<typename MatT, int _tag>
00322 struct inverse_f
00323 {
00324     typename MatT::temporary_type operator()(const MatT& M) const
00325     {
00326         /* Shorthand. */
00327         typedef typename MatT::value_type value_type;
00328 
00329         /* Compute LU factorization: */
00330         size_t N = M.rows();
00331         typename MatT::temporary_type LU;
00332         cml::et::detail::Resize(LU,N,N);
00333         LU = lu(M);
00334 
00335         /* Matrix containing the inverse: */
00336         typename MatT::temporary_type Z;
00337         cml::et::detail::Resize(Z,N,N);
00338 
00339         typename MatT::col_vector_type v, x;
00340         cml::et::detail::Resize(v,N);
00341         cml::et::detail::Resize(x,N);
00342         for(size_t i = 0; i < N; ++i)
00343             v[i] = value_type(0);
00344         /* XXX Need a fill() function here. */
00345 
00346         /* Use lu_solve to solve M*x = v for x, where v = [0 ... 1 ... 0]^T: */
00347         for(size_t i = 0; i < N; ++i) {
00348             v[i] = 1.;
00349             x = lu_solve(LU,v);
00350 
00351             /* x is column i of the inverse of LU: */
00352             for(size_t k = 0; k < N; ++ k) {
00353                 Z(k,i) = x[k];
00354             }
00355             v[i] = 0.;
00356         }
00357 
00358         return Z;
00359     }
00360 
00361 };
00362 
00363 #endif
00364 
00365 /* Note: force_NxN is for checking general NxN inversion against the special-
00366  * case 2x2, 3x3 and 4x4 code. I'm leaving it in for now since we may need to
00367  * test the NxN code further if the implementation changes. At some future
00368  * time when the implementation is stable, everything related to force_NxN can
00369  * be taken out.
00370  */
00371 
00372 /* Note: Commenting the force_NxN stuff out, but leaving the code here in
00373  * case we need to do more testing in the future.
00374  */
00375 
00376 /* Generator for the inverse functional for fixed-size matrices: */
00377 template<typename MatT> typename MatT::temporary_type
00378 inverse(const MatT& M, fixed_size_tag/*, bool force_NxN*/)
00379 {
00380     /* Require a square matrix: */
00381     cml::et::CheckedSquare(M, fixed_size_tag());
00382     
00383     /*
00384     if (force_NxN) {
00385         return inverse_f<MatT,0>()(M);
00386     } else {
00387     */
00388     return inverse_f<MatT,MatT::array_rows>()(M);
00389     /*
00390     }
00391     */
00392 }
00393 
00394 /* Generator for the inverse functional for dynamic-size matrices: */
00395 template<typename MatT> typename MatT::temporary_type
00396 inverse(const MatT& M, dynamic_size_tag/*, bool force_NxN*/)
00397 {
00398     /* Require a square matrix: */
00399     cml::et::CheckedSquare(M, dynamic_size_tag());
00400     
00401     /*
00402     if (force_NxN) { 
00403         return inverse_f<MatT,0>()(M);
00404     } else {
00405     */
00406     /* Dispatch based upon the matrix dimension: */
00407     switch(M.rows()) {
00408         case 2:  return inverse_f<MatT,2>()(M);     //   2x2
00409         case 3:  return inverse_f<MatT,3>()(M);     //   3x3
00410         case 4:  return inverse_f<MatT,4>()(M);     //   4x4
00411         default: return inverse_f<MatT,0>()(M);     // > 4x4 (or 1x1)
00412     }
00413     /*
00414     }
00415     */
00416 }
00417 
00418 } // namespace detail
00419 
00421 template<typename E, class AT, typename BO, typename L> inline
00422 typename matrix<E,AT,BO,L>::temporary_type
00423 inverse(const matrix<E,AT,BO,L>& M/*, bool force_NxN = false*/)
00424 {
00425     typedef typename matrix<E,AT,BO,L>::size_tag size_tag;
00426     return detail::inverse(M,size_tag()/*,force_NxN*/);
00427 }
00428 
00430 template<typename XprT> inline
00431 typename et::MatrixXpr<XprT>::temporary_type
00432 inverse(const et::MatrixXpr<XprT>& e/*, bool force_NxN = false*/)
00433 {
00434     typedef typename et::MatrixXpr<XprT>::size_tag size_tag;
00435     return detail::inverse(e,size_tag()/*,force_NxN*/);
00436 }
00437 
00438 } // namespace cml
00439 
00440 #endif
00441 
00442 // -------------------------------------------------------------------------
00443 // vim:ft=cpp

Generated on Sat Jul 18 19:35:22 2009 for CML 1.0 by  doxygen 1.5.9