TooN: doc/cramer.h Source File

TooN 2.0.0-beta8

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List

doc/cramer.h

    00001 /**
    00002 \page sCramerIsBad Speed, but not at the expense of accuracy.
    00003
    00004 TooN aims to be a fast library, and may choose between one of several algorithms
    00005 depending on the size of the arguments. However TooN will never substitute a
    00006 fast but numerically inferior algorithm.  For example LU decomposition, Gaussian
    00007 elimination and Gauss-Jordan reduction all have similar numerical properties for
    00008 computing a matrix inverse. Direct inversino using Cramer's rule is
    00009 significantly less stable, even for 3x3 matrices.
    00010
    00011 The following code computes a matrix inverse of the ill conditioned matrix:
    00012 \f[
    00013 \begin{bmatrix}
    00014 1  & 2 & 3 \\
    00015 1  & 2 + \epsilon & 3 \\
    00016 1  & 2  & 3 + \epsilon
    00017 \end{bmatrix},
    00018 \f]
    00019 using LU decomposition, Gauss-Jordan, pseudo-inverse with singular value
    00020 decomposition and with Cramer's rule. The error is computed as:
    00021 \f[
    00022     \|A^{-1} A - I\|_{\text{fro}}
    00023 \f]
    00024 The code is:
    00025
    00026 @code
    00027 #include <TooN/TooN.h>
    00028 #include <TooN/helpers.h>
    00029 #include <TooN/LU.h>
    00030 #include <TooN/GR_SVD.h>
    00031 #include <TooN/gauss_jordan.h>
    00032 #include <TooN/gaussian_elimination.h>
    00033 #include <iomanip>
    00034 using namespace TooN;
    00035 using namespace std;
    00036 Matrix<3> invert_cramer(const Matrix<3>& A)
    00037 {
    00038     Matrix<3> i;
    00039     double t0 = A[0][0]*A[1][1]*A[2][2]-A[0][0]*A[1][2]*A[2][1]-A[1][0]*A[0][1]*A[2][2]+A[1][0]*A[0][2]*A[2][1]+A[2][0]*A[0][1]*A[1][2]-A[2][0]*A[0][2]*A[1][1];
    00040     double idet = 1/t0;
    00041     t0 = A[1][1]*A[2][2]-A[1][2]*A[2][1];
    00042     i[0][0] = t0*idet;
    00043     t0 = -A[0][1]*A[2][2]+A[0][2]*A[2][1];
    00044     i[0][1] = t0*idet;
    00045     t0 = A[0][1]*A[1][2]-A[0][2]*A[1][1];
    00046     i[0][2] = t0*idet;
    00047     t0 = -A[1][0]*A[2][2]+A[1][2]*A[2][0];
    00048     i[1][0] = t0*idet;
    00049     t0 = A[0][0]*A[2][2]-A[0][2]*A[2][0];
    00050     i[1][1] = t0*idet;
    00051     t0 = -A[0][0]*A[1][2]+A[0][2]*A[1][0];
    00052     i[1][2] = t0*idet;
    00053     t0 = A[1][0]*A[2][1]-A[1][1]*A[2][0];
    00054     i[2][0] = t0*idet;
    00055     t0 = -A[0][0]*A[2][1]+A[0][1]*A[2][0];
    00056     i[2][1] = t0*idet;
    00057     t0 = A[0][0]*A[1][1]-A[0][1]*A[1][0];
    00058     i[2][2] = t0*idet;
    00059
    00060     return i;
    00061 }
    00062
    00063
    00064 int main()
    00065 {
    00066     Matrix<3> singular = Data(1, 2, 3,
    00067                               1, 2, 3,
    00068                               1, 2, 3);
    00069
    00070
    00071     for(double i=0; i < 1000; i++)
    00072     {
    00073         double delta = pow(0.9, i);
    00074
    00075         //Make a poorly conditioned matrix
    00076         Matrix<3> bad = singular;
    00077         bad[2][2] += delta;
    00078         bad[1][1] += delta;
    00079
    00080
    00081         //Compute the inverse with LU decomposition
    00082         Matrix<3> linv;
    00083         LU<3> blu(bad);
    00084         linv = blu.get_inverse();
    00085
    00086         //Compute the inverse with Gauss-Jordan reduction
    00087         Matrix<3, 6> gj;
    00088         Matrix<3> ginv;
    00089         gj.slice<0,0,3,3>() = bad;
    00090         gj.slice<0,3,3,3>() = Identity;
    00091         gauss_jordan(gj);
    00092         ginv = gj.slice<0,3,3,3>();
    00093
    00094
    00095         //Compute the pseudo-inverse with singular value decomposition
    00096         GR_SVD<3,3> bsvd(bad);
    00097         Matrix<3> sinv = bsvd.get_pinv();
    00098
    00099
    00100         Matrix<3> I = Identity;
    00101         Matrix<3> einv = gaussian_elimination(bad, I);
    00102
    00103         Matrix<3> cinv = invert_cramer(bad);
    00104
    00105
    00106         double lerr = norm_fro(linv * bad + -1 * Identity);
    00107         double gerr = norm_fro(ginv * bad + -1 * Identity);
    00108         double serr = norm_fro(sinv * bad + -1 * Identity);
    00109         double cerr = norm_fro(cinv * bad + -1 * Identity);
    00110         double eerr = norm_fro(einv * bad + -1 * Identity);
    00111
    00112         cout << setprecision(15) << scientific << delta << " " << lerr << " " << gerr << " " << serr << " " << eerr << " " << cerr << endl;
    00113
    00114
    00115
    00116     }
    00117
    00118
    00119 }
    00120 @endcode
    00121
    00122 The direct inverse with Cramer's rule is about three times faster than the
    00123 builtin routines. However, the numerical stability is considerably worse,
    00124 giving errors 1e6 times higher in extreme cases:
    00125
    00126 \image html cramer.png "Comparison of errors in matrix inverse"
    00127
    00128 */