Intrepid
/usr/src/RPM/BUILD/trilinos-11.12.1/packages/intrepid/src/Shared/MiniTensor/Intrepid_MiniTensor_LinearAlgebra.t.h
00001 // @HEADER
00002 // ************************************************************************
00003 //
00004 //                           Intrepid Package
00005 //                 Copyright (2007) Sandia Corporation
00006 //
00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
00008 // license for use of this work by or on behalf of the U.S. Government.
00009 //
00010 // Redistribution and use in source and binary forms, with or without
00011 // modification, are permitted provided that the following conditions are
00012 // met:
00013 //
00014 // 1. Redistributions of source code must retain the above copyright
00015 // notice, this list of conditions and the following disclaimer.
00016 //
00017 // 2. Redistributions in binary form must reproduce the above copyright
00018 // notice, this list of conditions and the following disclaimer in the
00019 // documentation and/or other materials provided with the distribution.
00020 //
00021 // 3. Neither the name of the Corporation nor the names of the
00022 // contributors may be used to endorse or promote products derived from
00023 // this software without specific prior written permission.
00024 //
00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00036 //
00037 // Questions: Alejandro Mota (amota@sandia.gov)
00038 //
00039 // ************************************************************************
00040 // @HEADER
00041 
00042 #if !defined(Intrepid_MiniTensor_LinearAlgebra_t_h)
00043 #define Intrepid_MiniTensor_LinearAlgebra_t_h
00044 
00045 namespace Intrepid {
00046 
00047 //
00048 // R^N 2nd-order tensor inverse
00049 // Gauss-Jordan elimination. Warning: full pivoting
00050 // for small tensors. Use Teuchos LAPACK interface for
00051 // more efficient and robust techniques.
00052 // \param A nonsingular tensor
00053 // \return \f$ A^{-1} \f$
00054 //
00055 template<typename T, Index N>
00056 Tensor<T, N>
00057 inverse(Tensor<T, N> const & A)
00058 {
00059   Index const
00060   dimension = A.get_dimension();
00061 
00062   Index const
00063   maximum_dimension = static_cast<Index>(std::numeric_limits<Index>::digits);
00064 
00065   if (dimension > maximum_dimension) {
00066     std::cerr << "ERROR: " << __PRETTY_FUNCTION__;
00067     std::cerr << std::endl;
00068     std::cerr << "Requested dimension (" << dimension;
00069     std::cerr << ") exceeds maximum allowed for inverse: " << maximum_dimension;
00070     std::cerr << std::endl;
00071     exit(1);
00072   }
00073 
00074   switch (dimension) {
00075 
00076   case 3:
00077     {
00078       T const determinant = det(A);
00079       assert(determinant != 0.0);
00080       return Tensor<T, N>(
00081         -A(1,2)*A(2,1) + A(1,1)*A(2,2),
00082          A(0,2)*A(2,1) - A(0,1)*A(2,2),
00083         -A(0,2)*A(1,1) + A(0,1)*A(1,2),
00084          A(1,2)*A(2,0) - A(1,0)*A(2,2),
00085         -A(0,2)*A(2,0) + A(0,0)*A(2,2),
00086          A(0,2)*A(1,0) - A(0,0)*A(1,2),
00087         -A(1,1)*A(2,0) + A(1,0)*A(2,1),
00088          A(0,1)*A(2,0) - A(0,0)*A(2,1),
00089         -A(0,1)*A(1,0) + A(0,0)*A(1,1)
00090         ) / determinant;
00091     }
00092     break;
00093 
00094   case 2:
00095     {
00096       T const determinant = det(A);
00097       assert(determinant != 0.0);
00098       return Tensor<T, N>(A(1,1), -A(0,1), -A(1,0), A(0,0)) / determinant;
00099     }
00100     break;
00101 
00102   default:
00103     break;
00104   }
00105 
00106   Tensor<T, N>
00107   S = A;
00108 
00109   Tensor<T, N>
00110   B = identity<T, N>(dimension);
00111 
00112   // Set 1 ... dimension bits to one.
00113   Index
00114   intact_rows = static_cast<Index>((1UL << dimension) - 1);
00115 
00116   Index
00117   intact_cols = static_cast<Index>((1UL << dimension) - 1);
00118 
00119   // Gauss-Jordan elimination with full pivoting
00120   for (Index k = 0; k < dimension; ++k) {
00121 
00122     // Determine full pivot
00123     T
00124     pivot = 0.0;
00125 
00126     Index
00127     pivot_row = dimension;
00128 
00129     Index
00130     pivot_col = dimension;
00131 
00132     for (Index row = 0; row < dimension; ++row) {
00133 
00134       if (!(intact_rows & (1 << row))) continue;
00135 
00136       for (Index col = 0; col < dimension; ++col) {
00137 
00138         if (!(intact_cols & (1 << col))) continue;
00139 
00140         T
00141         s = std::abs(S(row, col));
00142 
00143         if (s > pivot) {
00144 
00145           pivot_row = row;
00146           pivot_col = col;
00147 
00148           pivot = s;
00149 
00150         }
00151 
00152       }
00153 
00154     }
00155 
00156     // Gauss-Jordan elimination
00157     T const
00158     t = S(pivot_row, pivot_col);
00159 
00160     assert(t != 0.0);
00161 
00162     for (Index j = 0; j < dimension; ++j) {
00163       S(pivot_row, j) /= t;
00164       B(pivot_row, j) /= t;
00165     }
00166 
00167     for (Index i = 0; i < dimension; ++i) {
00168       if (i == pivot_row) continue;
00169 
00170       T const
00171       c = S(i, pivot_col);
00172 
00173       for (Index j = 0; j < dimension; ++j) {
00174         S(i, j) -= c * S(pivot_row, j);
00175         B(i, j) -= c * B(pivot_row, j);
00176       }
00177     }
00178 
00179     // Eliminate current row and col from intact rows and cols
00180     intact_rows &= ~(1 << pivot_row);
00181     intact_cols &= ~(1 << pivot_col);
00182 
00183   }
00184 
00185   Tensor<T, N> const
00186   X = t_dot(S, B);
00187 
00188   return X;
00189 }
00190 
00191 //
00192 // R^N Subtensor
00193 // \param A tensor
00194 // \param i index
00195 // \param j index
00196 // \return Subtensor with i-row and j-col deleted.
00197 //
00198 template<typename T, Index N>
00199 Tensor<T, dimension_subtract<N, 1>::value >
00200 subtensor(Tensor<T, N> const & A, Index const i, Index const j)
00201 {
00202   Index const
00203   dimension = A.get_dimension();
00204 
00205   assert(i < dimension);
00206   assert(j < dimension);
00207 
00208   Tensor<T, dimension_subtract<N, 1>::value >
00209   B(dimension - 1);
00210 
00211   Index p = 0;
00212   Index q = 0;
00213   for (Index m = 0; m < dimension; ++m) {
00214     if (m == i) continue;
00215     for (Index n = 0; n < dimension; ++n) {
00216       if (n == j) continue;
00217       B(p, q) = A(m, n);
00218       ++q;
00219     }
00220     ++p;
00221   }
00222 
00223   return B;
00224 }
00225 
00226 //
00227 // Exponential map
00228 //
00229 template<typename T, Index N>
00230 Tensor<T, N>
00231 exp(Tensor<T, N> const & A)
00232 {
00233   return exp_pade(A);
00234 }
00235 
00236 //
00237 // R^N exponential map by Taylor series, radius of convergence is infinity
00238 // \param A tensor
00239 // \return \f$ \exp A \f$
00240 //
00241 template<typename T, Index N>
00242 Tensor<T, N>
00243 exp_taylor(Tensor<T, N> const & A)
00244 {
00245   Index const
00246   max_iter = 128;
00247 
00248   T const
00249   tol = machine_epsilon<T>();
00250 
00251   Index const
00252   dimension = A.get_dimension();
00253 
00254   Tensor<T, N>
00255   term = identity<T, N>(dimension);
00256 
00257   // Relative error taken wrt to the first term, which is I and norm = 1
00258   T
00259   relative_error = 1.0;
00260 
00261   Tensor<T, N>
00262   B = term;
00263 
00264   Index
00265   k = 0;
00266 
00267   while (relative_error > tol && k < max_iter) {
00268     term = static_cast<T>(1.0 / (k + 1.0)) * term * A;
00269     B = B + term;
00270     relative_error = norm_1(term);
00271     ++k;
00272   }
00273 
00274   return B;
00275 }
00276 
00277 namespace {
00278 
00279 //
00280 // Scaling parameter theta for scaling and squaring exponential.
00281 //
00282 template<typename T>
00283 T
00284 scaling_squaring_theta(Index const order)
00285 {
00286   assert(order > 0 && order < 22);
00287 
00288   T const theta[] =
00289   {
00290       0.0e-0, 3.7e-8, 5.3e-4, 1.5e-2, 8.5e-2, 2.5e-1, 5.4e-1, 9.5e-1,
00291       1.5e-0, 2.1e-0, 2.8e-0, 3.6e-0, 4.5e-0, 5.4e-0, 6.3e-0, 7.3e-0,
00292       8.4e-0, 9,4e-0, 1.1e+1, 1.2e+1, 1.3e+1, 1.4e+1
00293   };
00294 
00295   return theta[order];
00296 }
00297 
00298 //
00299 // Polynomial coefficients for Padé approximants.
00300 //
00301 template<typename T>
00302 T
00303 polynomial_coefficient(Index const order, Index const index)
00304 {
00305   assert(index <= order);
00306 
00307   T
00308   c = 0.0;
00309 
00310   switch (order) {
00311 
00312     default:
00313       std::cerr << "ERROR: " << __PRETTY_FUNCTION__;
00314       std::cerr << std::endl;
00315       std::cerr << "Wrong order in Pade' polynomial coefficient: ";
00316       std::cerr << order << std::endl;
00317       exit(1);
00318       break;
00319 
00320     case 3:
00321     {
00322       T const
00323       b[] = {120.0, 60.0, 12.0, 1.0};
00324 
00325       c = b[index];
00326     }
00327     break;
00328 
00329     case 5:
00330     {
00331       T const
00332       b[] = {30240.0, 15120.0, 3360.0, 420.0, 30.0, 1.0};
00333 
00334       c = b[index];
00335     }
00336     break;
00337 
00338     case 7:
00339     {
00340       T const
00341       b[] = {17297280.0, 8648640.0, 1995840.0, 277200.0, 25200.0, 1512.0,
00342           56.0, 1.0};
00343 
00344       c = b[index];
00345     }
00346     break;
00347 
00348     case 9:
00349     {
00350       T const
00351       b[] = {17643225600.0, 8821612800.0, 2075673600.0, 302702400.0,
00352           30270240.0, 2162160.0, 110880.0, 3960.0, 90.0, 1.0};
00353 
00354       c = b[index];
00355     }
00356     break;
00357 
00358     case 13:
00359     {
00360       T const
00361       b[] = {64764752532480000.0, 32382376266240000.0, 7771770303897600.0,
00362           1187353796428800.0, 129060195264000.0, 10559470521600.0,
00363           670442572800.0, 33522128640.0, 1323241920.0, 40840800.0,
00364           960960.0, 16380.0, 182.0, 1.0};
00365 
00366       c = b[index];
00367     }
00368     break;
00369 
00370   }
00371 
00372   return c;
00373 }
00374 
00375 //
00376 // Padé approximant polynomial odd and even terms.
00377 //
00378 template<typename T, Index N>
00379 std::pair<Tensor<T, N>, Tensor<T, N> >
00380 pade_polynomial_terms(Tensor<T, N> const & A, Index const order)
00381 {
00382   Index const
00383   dimension = A.get_dimension();
00384 
00385   Tensor<T, N>
00386   B = identity<T, N>(dimension);
00387 
00388   Tensor<T, N>
00389   U = polynomial_coefficient<Real>(order, 1) * B;
00390 
00391   Tensor<T, N>
00392   V = polynomial_coefficient<Real>(order, 0) * B;
00393 
00394   Tensor<T, N> const
00395   A2 = A * A;
00396 
00397   for (Index i = 3; i <= order; i += 2) {
00398 
00399     B = B * A2;
00400 
00401     Tensor<T, N> const
00402     O = polynomial_coefficient<Real>(order, i) * B;
00403 
00404     Tensor<T, N> const
00405     E = polynomial_coefficient<Real>(order, i - 1) * B;
00406 
00407     U += O;
00408 
00409     V += E;
00410 
00411   }
00412 
00413   U = A * U;
00414 
00415   return std::make_pair(U, V);
00416 }
00417 
00418 //
00419 // Compute a non-negative integer power of a tensor by binary manipulation.
00420 //
00421 template<typename T, Index N>
00422 Tensor<T, N>
00423 binary_powering(Tensor<T, N> const & A, Index const exponent)
00424 {
00425   if (exponent == 0) return eye<T, N>(A.get_dimension());
00426 
00427   Index const
00428   rightmost_bit = 1;
00429 
00430   Index const
00431   number_digits = std::numeric_limits<Index>::digits;
00432 
00433   Index const
00434   leftmost_bit = rightmost_bit << (number_digits - 1);
00435 
00436   Index
00437   t = 0;
00438 
00439   for (Index j = 0; j < number_digits; ++j) {
00440 
00441     if (((exponent << j) & leftmost_bit) != 0) {
00442 
00443       t = number_digits - j - 1;
00444       break;
00445 
00446     }
00447 
00448   }
00449 
00450   Tensor<T, N>
00451   P = A;
00452 
00453   Index
00454   i = 0;
00455 
00456   Index
00457   m = exponent;
00458 
00459   while ((m & rightmost_bit) == 0) {
00460     P = P * P;
00461     ++i;
00462     m = m >> 1;
00463   }
00464 
00465   Tensor<T, N>
00466   X = P;
00467 
00468   for (Index j = i + 1; j <= t; ++j) {
00469     P = P * P;
00470 
00471     if (((exponent >> j) & rightmost_bit) != 0) {
00472       X = X * P;
00473     }
00474   }
00475 
00476   return X;
00477 }
00478 
00479 } // anonymous namespace
00480 
00481 //
00482 // Exponential map by squaring and scaling and Padé approximants.
00483 // See algorithm 10.20 in Functions of Matrices, N.J. Higham, SIAM, 2008.
00484 // \param A tensor
00485 // \return \f$ \exp A \f$
00486 //
00487 template<typename T, Index N>
00488 Tensor<T, N>
00489 exp_pade(Tensor<T, N> const & A)
00490 {
00491   Index const
00492   dimension = A.get_dimension();
00493 
00494   Index const
00495   orders[] = {3, 5, 7, 9, 13};
00496 
00497   Index const
00498   number_orders = 5;
00499 
00500   Index const
00501   highest_order = orders[number_orders - 1];
00502 
00503   Tensor<T, N>
00504   B;
00505 
00506   Real const
00507   norm = Sacado::ScalarValue<T>::eval((norm_1(A)));
00508 
00509   for (Index i = 0; i < number_orders; ++i) {
00510 
00511     Index const
00512     order = orders[i];
00513 
00514     Real const
00515     theta = scaling_squaring_theta<Real>(order);
00516 
00517     if (order < highest_order && norm < theta) {
00518 
00519       Tensor<T, N>
00520       U;
00521 
00522       Tensor<T, N>
00523       V;
00524 
00525       boost::tie(U, V) = pade_polynomial_terms(A, order);
00526 
00527       B = inverse(V - U) * (U + V);
00528 
00529       break;
00530 
00531     } else if (order == highest_order) {
00532 
00533       Real const
00534       theta_highest = scaling_squaring_theta<Real>(order);
00535 
00536       int const
00537       signed_power = static_cast<int>(std::ceil(log2(norm / theta_highest)));
00538 
00539       Index const
00540       power_two = signed_power > 0 ? static_cast<Index>(signed_power) : 0;
00541 
00542       Real
00543       scale = 1.0;
00544 
00545       for (Index j = 0; j < power_two; ++j) {
00546         scale /= 2.0;
00547       }
00548 
00549       Tensor<T, N> const
00550       I = identity<T, N>(dimension);
00551 
00552       Tensor<T, N> const
00553       A1 = scale * A;
00554 
00555       Tensor<T, N> const
00556       A2 = A1 * A1;
00557 
00558       Tensor<T, N> const
00559       A4 = A2 * A2;
00560 
00561       Tensor<T, N> const
00562       A6 = A2 * A4;
00563 
00564       Real const b0  = polynomial_coefficient<Real>(order, 0);
00565       Real const b1  = polynomial_coefficient<Real>(order, 1);
00566       Real const b2  = polynomial_coefficient<Real>(order, 2);
00567       Real const b3  = polynomial_coefficient<Real>(order, 3);
00568       Real const b4  = polynomial_coefficient<Real>(order, 4);
00569       Real const b5  = polynomial_coefficient<Real>(order, 5);
00570       Real const b6  = polynomial_coefficient<Real>(order, 6);
00571       Real const b7  = polynomial_coefficient<Real>(order, 7);
00572       Real const b8  = polynomial_coefficient<Real>(order, 8);
00573       Real const b9  = polynomial_coefficient<Real>(order, 9);
00574       Real const b10 = polynomial_coefficient<Real>(order, 10);
00575       Real const b11 = polynomial_coefficient<Real>(order, 11);
00576       Real const b12 = polynomial_coefficient<Real>(order, 12);
00577       Real const b13 = polynomial_coefficient<Real>(order, 13);
00578 
00579       Tensor<T, N> const
00580       U = A1 * (
00581           (A6 * (b13 * A6 + b11 * A4 + b9 * A2) +
00582               b7 * A6 + b5 * A4 + b3 * A2 + b1 * I));
00583 
00584       Tensor<T, N> const
00585       V = A6 * (b12 * A6 + b10 * A4 + b8 * A2) +
00586       b6 * A6 + b4 * A4 + b2 * A2 + b0 * I;
00587 
00588       Tensor<T, N> const
00589       R = inverse(V - U) * (U + V);
00590 
00591       Index const
00592       exponent = (1U << power_two);
00593 
00594       B = binary_powering(R, exponent);
00595 
00596     }
00597 
00598   }
00599 
00600   return B;
00601 }
00602 
00603 //
00604 // Logarithmic map by Taylor series.
00605 //
00606 template<typename T, Index N>
00607 Tensor<T, N>
00608 log_taylor(Tensor<T, N> const & A)
00609 {
00610   Index const
00611   max_iter = 128;
00612 
00613   T const
00614   tol = machine_epsilon<T>();
00615 
00616   T const
00617   norm_tensor = norm_1(A);
00618 
00619   Index const
00620   dimension = A.get_dimension();
00621 
00622   Tensor<T, N> const
00623   A_minus_I = A - identity<T, N>(dimension);
00624 
00625   Tensor<T, N>
00626   term = A_minus_I;
00627 
00628   T
00629   norm_term = norm_1(term);
00630 
00631   T
00632   relative_error = norm_term / norm_tensor;
00633 
00634   Tensor<T, N>
00635   B = term;
00636 
00637   Index
00638   k = 1;
00639 
00640   while (relative_error > tol && k <= max_iter) {
00641     term = static_cast<T>(- (k / (k + 1.0))) * term * A_minus_I;
00642     B = B + term;
00643     norm_term = norm_1(term);
00644     relative_error = norm_term / norm_tensor;
00645     ++k;
00646   }
00647 
00648   return B;
00649 }
00650 
00651 //
00652 // Logarithmic map.
00653 //
00654 template<typename T, Index N>
00655 Tensor<T, N>
00656 log(Tensor<T, N> const & A)
00657 {
00658   return log_gregory(A);
00659 }
00660 
00661 //
00662 // Logarithmic map by Gregory series.
00663 //
00664 template<typename T, Index N>
00665 Tensor<T, N>
00666 log_gregory(Tensor<T, N> const & A)
00667 {
00668   Index const
00669   max_iter = 128;
00670 
00671   T const
00672   tol = machine_epsilon<T>();
00673 
00674   T const
00675   norm_tensor = norm_1(A);
00676 
00677   Index const
00678   dimension = A.get_dimension();
00679 
00680   Tensor<T, N> const
00681   I_minus_A = identity<T, N>(dimension) - A;
00682 
00683   Tensor<T, N> const
00684   I_plus_A = identity<T, N>(dimension) + A;
00685 
00686   Tensor<T, N>
00687   term = I_minus_A * inverse(I_plus_A);
00688 
00689   T
00690   norm_term = norm_1(term);
00691 
00692   T
00693   relative_error = norm_term / norm_tensor;
00694 
00695   Tensor<T, N> const
00696   C = term * term;
00697 
00698   Tensor<T, N>
00699   B = term;
00700 
00701   Index
00702   k = 1;
00703 
00704   while (relative_error > tol && k <= max_iter + 1) {
00705     term = static_cast<T>((2 * k - 1.0) / (2 * k + 1.0)) * term * C;
00706     B = B + term;
00707     norm_term = norm_1(term);
00708     relative_error = norm_term / norm_tensor;
00709     ++k;
00710   }
00711 
00712   B = - 2.0 * B;
00713 
00714   return B;
00715 }
00716 
00717 //
00718 // Logarithmic map for symmetric tensor.
00719 //
00720 template<typename T, Index N>
00721 Tensor<T, N>
00722 log_sym(Tensor<T, N> const & A)
00723 {
00724   return log_eig_sym(A);
00725 }
00726 
00727 //
00728 // Logarithmic map for symmetric tensor using eigenvalue decomposition.
00729 //
00730 template<typename T, Index N>
00731 Tensor<T, N>
00732 log_eig_sym(Tensor<T, N> const & A)
00733 {
00734   Index const
00735   dimension = A.get_dimension();
00736 
00737   Tensor<T, N>
00738   V(dimension);
00739 
00740   Tensor<T, N>
00741   D(dimension);
00742 
00743   boost::tie(V, D) = eig_sym(A);
00744 
00745   for (Index i = 0; i < dimension; ++i) {
00746     D(i, i) = std::log(D(i, i));
00747   }
00748 
00749   Tensor<T, N> const
00750   B = dot_t(dot(V, D), V);
00751 
00752   return B;
00753 }
00754 
00755 //
00756 // R^N logarithmic map of a rotation.
00757 // \param R with \f$ R \in SO(N) \f$
00758 // \return \f$ r = \log R \f$ with \f$ r \in so(N) \f$
00759 //
00760 template<typename T, Index N>
00761 Tensor<T, N>
00762 log_rotation(Tensor<T, N> const & R)
00763 {
00764   Index const
00765   dimension = R.get_dimension();
00766 
00767   //firewalls, make sure R \in SO(N)
00768   assert(norm(dot_t(R,R) - eye<T, N>(dimension)) <
00769       100.0 * machine_epsilon<T>());
00770   assert(std::abs(det(R) - 1.0) < 100.0 * machine_epsilon<T>());
00771 
00772   // acos requires input between -1 and +1
00773   T
00774   cosine = 0.5 * (trace(R) - 1.0);
00775 
00776   if (cosine < -1.0) {
00777     cosine = -1.0;
00778   } else if(cosine > 1.0) {
00779     cosine = 1.0;
00780   }
00781 
00782   T
00783   theta = std::acos(cosine);
00784 
00785   Tensor<T, N>
00786   r(dimension);
00787 
00788   switch (dimension) {
00789 
00790     default:
00791       std::cerr << "Logarithm of SO(N) N != 2,3 not implemented." << std::endl;
00792       exit(1);
00793       break;
00794 
00795     case 3:
00796       if (theta == 0.0) {
00797 
00798         r = zero<T, N>(3);
00799 
00800       } else if (std::abs(cosine + 1.0) < 10.0 * machine_epsilon<T>())  {
00801 
00802         r = log_rotation_pi(R);
00803 
00804       } else {
00805 
00806         r = theta / std::sin(theta) * skew(R);
00807 
00808       }
00809       break;
00810 
00811     case 2:
00812       r(0,0) = 0.0;
00813       r(0,1) = -theta;
00814       r(1,0) = theta;
00815       r(1,1) = 0.0;
00816       break;
00817 
00818   }
00819 
00820   return r;
00821 }
00822 
00823 // R^N Logarithmic map of a 180-degree rotation.
00824 // \param R with \f$ R \in SO(N) \f$
00825 // \return \f$ r = \log R \f$ with \f$ r \in so(N) \f$
00826 //
00827 template<typename T, Index N>
00828 Tensor<T, N>
00829 log_rotation_pi(Tensor<T, N> const & R)
00830 {
00831   Index const
00832   dimension = R.get_dimension();
00833 
00834   T
00835   cosine = 0.5*(trace(R) - 1.0);
00836   // set firewall to make sure the rotation is indeed 180 degrees
00837   assert(std::abs(cosine + 1.0) < 10.0 * machine_epsilon<T>());
00838 
00839   Tensor<T, N>
00840   r(dimension);
00841 
00842   switch (dimension) {
00843 
00844     default:
00845       std::cerr << "Logarithm of SO(N) N != 2,3 not implemented." << std::endl;
00846       exit(1);
00847       break;
00848 
00849     case 3:
00850     {
00851       // obtain U from R = LU
00852       r = gaussian_elimination((R - identity<T, N>(3)));
00853 
00854       // backward substitution (for rotation exp(R) only)
00855       T const tol = 10.0 * machine_epsilon<T>();
00856 
00857       Vector<T, N> normal(3);
00858 
00859       if (std::abs(r(2,2)) < tol){
00860         normal(2) = 1.0;
00861       } else {
00862         normal(2) = 0.0;
00863       }
00864 
00865       if (std::abs(r(1,1)) < tol){
00866         normal(1) = 1.0;
00867       } else {
00868         normal(1) = -normal(2)*r(1,2)/r(1,1);
00869       }
00870 
00871       if (std::abs(r(0,0)) < tol){
00872         normal(0) = 1.0;
00873       } else {
00874         normal(0) = -normal(1)*r(0,1) - normal(2)*r(0,2)/r(0,0);
00875       }
00876 
00877       normal = normal / norm(normal);
00878 
00879       r.fill(ZEROS);
00880       r(0,1) = -normal(2);
00881       r(0,2) =  normal(1);
00882       r(1,0) =  normal(2);
00883       r(1,2) = -normal(0);
00884       r(2,0) = -normal(1);
00885       r(2,1) =  normal(0);
00886 
00887       T const pi = std::acos(-1.0);
00888 
00889       r = pi * r;
00890     }
00891     break;
00892 
00893     case 2:
00894     {
00895       T theta = std::acos(-1.0);
00896 
00897       if (R(0,0) > 0.0) {
00898         theta = -theta;
00899       }
00900 
00901       r(0,0) = 0.0;
00902       r(0,1) = -theta;
00903       r(1,0) = theta;
00904       r(1,1) = 0.0;
00905     }
00906     break;
00907 
00908   }
00909 
00910   return r;
00911 }
00912 
00913 // Gaussian Elimination with partial pivot
00914 // \param matrix \f$ A \f$
00915 // \return \f$ U \f$ where \f$ A = LU \f$
00916 //
00917 template<typename T, Index N>
00918 Tensor<T, N>
00919 gaussian_elimination(Tensor<T, N> const & A)
00920 {
00921   Index const
00922   dimension = A.get_dimension();
00923 
00924   Tensor<T, N>
00925   U = A;
00926 
00927   T const
00928   tol = 10.0 * machine_epsilon<T>();
00929 
00930   Index i = 0;
00931   Index j = 0;
00932   Index i_max = 0;
00933 
00934   while ((i < dimension) && (j < dimension)) {
00935     // find pivot in column j, starting in row i
00936     i_max = i;
00937     for (Index k = i + 1; k < dimension; ++k) {
00938       if (std::abs(U(k,j)) > std::abs(U(i_max,j))) {
00939         i_max = k;
00940       }
00941     }
00942 
00943     // Check if A(i_max,j) equal to or very close to 0
00944     if (std::abs(U(i_max,j)) > tol){
00945       // swap rows i and i_max and divide each entry in row i
00946       // by U(i,j)
00947       for (Index k = 0; k < dimension; ++k) {
00948         std::swap(U(i,k), U(i_max,k));
00949       }
00950 
00951       for (Index k = 0; k < dimension; ++k) {
00952         U(i,k) = U(i,k) / U(i,j);
00953       }
00954 
00955       for (Index l = i + 1; l < dimension; ++l) {
00956         for (Index k = 0; k < dimension; ++k) {
00957           U(l,k) = U(l,k) - U(l,i) * U(i,k) / U(i,i);
00958         }
00959       }
00960       ++i;
00961     }
00962     ++j;
00963   }
00964 
00965   return U;
00966 }
00967 
00968 //
00969 // Apply Givens-Jacobi rotation on the left in place.
00970 //
00971 template<typename T, Index N>
00972 void
00973 givens_left(T const & c, T const & s, Index i, Index k, Tensor<T, N> & A)
00974 {
00975   Index const
00976   dimension = A.get_dimension();
00977 
00978   for (Index j = 0; j < dimension; ++j) {
00979     T const t1 = A(i,j);
00980     T const t2 = A(k,j);
00981     A(i,j) = c * t1 - s * t2;
00982     A(k,j) = s * t1 + c * t2;
00983   }
00984   return;
00985 }
00986 
00987 //
00988 // Apply Givens-Jacobi rotation on the right in place.
00989 //
00990 template<typename T, Index N>
00991 void
00992 givens_right(T const & c, T const & s, Index i, Index k, Tensor<T, N> & A)
00993 {
00994   Index const
00995   dimension = A.get_dimension();
00996 
00997   for (Index j = 0; j < dimension; ++j) {
00998     T const t1 = A(j,i);
00999     T const t2 = A(j,k);
01000     A(j,i) = c * t1 - s * t2;
01001     A(j,k) = s * t1 + c * t2;
01002   }
01003   return;
01004 }
01005 
01009 template<typename T, Index N>
01010 void
01011 rank_one_left(T const & beta, Vector<T, N> const & v, Tensor<T, N> & A)
01012 {
01013   A -= beta * dyad(v, dot(v, A));
01014   return;
01015 }
01016 
01020 template<typename T, Index N>
01021 void
01022 rank_one_right(T const & beta, Vector<T, N> const & v, Tensor<T, N> & A)
01023 {
01024   A -= beta * dyad(dot(A, v), v);
01025   return;
01026 }
01027 
01028 //
01029 // R^N exponential map of a skew-symmetric tensor.
01030 //
01031 template<typename T, Index N>
01032 Tensor<T, N>
01033 exp_skew_symmetric(Tensor<T, N> const & r)
01034 {
01035   // Check whether skew-symmetry holds
01036   assert(norm(sym(r)) < machine_epsilon<T>());
01037 
01038   Index const
01039   dimension = r.get_dimension();
01040 
01041   Tensor<T, N>
01042   R = identity<T, N>(dimension);
01043 
01044   T
01045   theta = 0.0;
01046 
01047   switch (dimension) {
01048 
01049     default:
01050       R = exp(r);
01051       break;
01052 
01053     case 3:
01054       theta = std::sqrt(r(2,1)*r(2,1)+r(0,2)*r(0,2)+r(1,0)*r(1,0));
01055 
01056       //Check whether norm == 0. If so, return identity.
01057       if (theta >= machine_epsilon<T>()) {
01058         R += std::sin(theta) / theta * r +
01059             (1.0 - std::cos(theta)) / (theta * theta) * r * r;
01060       }
01061       break;
01062 
01063     case 2:
01064       theta = r(1,0);
01065 
01066       T
01067       c = std::cos(theta);
01068 
01069       T
01070       s = std::sin(theta);
01071 
01072       R(0,0) = c;
01073       R(0,1) = -s;
01074       R(1,0) = s;
01075       R(1,1) = c;
01076 
01077       break;
01078 
01079   }
01080 
01081   return R;
01082 }
01083 
01084 //
01085 // R^N off-diagonal norm. Useful for SVD and other algorithms
01086 // that rely on Jacobi-type procedures.
01087 // \param A
01088 // \return \f$ \sqrt(\sum_i \sum_{j, j\neq i} a_{ij}^2) \f$
01089 //
01090 template<typename T, Index N>
01091 T
01092 norm_off_diagonal(Tensor<T, N> const & A)
01093 {
01094   Index const
01095   dimension = A.get_dimension();
01096 
01097   T
01098   s = 0.0;
01099 
01100   switch (dimension) {
01101 
01102     default:
01103       for (Index i = 0; i < dimension; ++i) {
01104         for (Index j = 0; j < dimension; ++j) {
01105           if (i != j) s += A(i,j)*A(i,j);
01106         }
01107       }
01108       break;
01109 
01110     case 3:
01111       s = A(0,1)*A(0,1) + A(0,2)*A(0,2) + A(1,2)*A(1,2) +
01112       A(1,0)*A(1,0) + A(2,0)*A(2,0) + A(2,1)*A(2,1);
01113       break;
01114 
01115     case 2:
01116       s = A(0,1)*A(0,1) + A(1,0)*A(1,0);
01117       break;
01118 
01119   }
01120 
01121   return std::sqrt(s);
01122 }
01123 
01124 //
01125 // R^N arg max abs. Useful for inverse and other algorithms
01126 // that rely on Jacobi-type procedures.
01127 // \param A
01128 // \return \f$ (p,q) = arg max_{i,j} |a_{ij}| \f$
01129 //
01130 template<typename T, Index N>
01131 std::pair<Index, Index>
01132 arg_max_abs(Tensor<T, N> const & A)
01133 {
01134   Index p = 0;
01135   Index q = 0;
01136 
01137   T
01138   s = std::abs(A(p,q));
01139 
01140   Index const
01141   dimension = A.get_dimension();
01142 
01143   for (Index i = 0; i < dimension; ++i) {
01144     for (Index j = 0; j < dimension; ++j) {
01145       if (std::abs(A(i,j)) > s) {
01146         p = i;
01147         q = j;
01148         s = std::abs(A(i,j));
01149       }
01150     }
01151   }
01152 
01153   return std::make_pair(p,q);
01154 }
01155 
01156 //
01157 // R^N arg max off-diagonal. Useful for SVD and other algorithms
01158 // that rely on Jacobi-type procedures.
01159 // \param A
01160 // \return \f$ (p,q) = arg max_{i \neq j} |a_{ij}| \f$
01161 //
01162 template<typename T, Index N>
01163 std::pair<Index, Index>
01164 arg_max_off_diagonal(Tensor<T, N> const & A)
01165 {
01166   Index p = 0;
01167   Index q = 1;
01168 
01169   T s = std::abs(A(p,q));
01170 
01171   Index const
01172   dimension = A.get_dimension();
01173 
01174   for (Index i = 0; i < dimension; ++i) {
01175     for (Index j = 0; j < dimension; ++j) {
01176       if (i != j && std::abs(A(i,j)) > s) {
01177         p = i;
01178         q = j;
01179         s = std::abs(A(i,j));
01180       }
01181     }
01182   }
01183 
01184   return std::make_pair(p,q);
01185 }
01186 
01187 namespace {
01188 
01189 //
01190 // Singular value decomposition (SVD) for 2x2
01191 // bidiagonal matrix. Used for general 2x2 SVD.
01192 // Adapted from LAPAPCK's DLASV2, Netlib's dlasv2.c
01193 // and LBNL computational crystalography toolbox
01194 // \param f, g, h where A = [f, g; 0, h]
01195 // \return \f$ A = USV^T\f$
01196 //
01197 template<typename T, Index N>
01198 boost::tuple<Tensor<T, N>, Tensor<T, N>, Tensor<T, N> >
01199 svd_bidiagonal(T f, T g, T h)
01200 {
01201   T fa = std::abs(f);
01202   T ga = std::abs(g);
01203   T ha = std::abs(h);
01204 
01205   T s0 = 0.0;
01206   T s1 = 0.0;
01207 
01208   T cu = 1.0;
01209   T su = 0.0;
01210   T cv = 1.0;
01211   T sv = 0.0;
01212 
01213   bool swap_diag = (ha > fa);
01214 
01215   if (swap_diag) {
01216     std::swap(fa, ha);
01217     std::swap(f, h);
01218   }
01219 
01220   if (ga == 0.0) {
01221     s1 = ha;
01222     s0 = fa;
01223   } else if (ga > fa && fa / ga < machine_epsilon<T>()) {
01224     // case of very large ga
01225     s0 = ga;
01226     s1 = ha > 1.0 ?
01227         T(fa / (ga / ha)) :
01228         T((fa / ga) * ha);
01229     cu = 1.0;
01230     su = h / g;
01231     cv = f / g;
01232     sv = 1.0;
01233   } else {
01234     // normal case
01235     T d = fa - ha;
01236     T l = d != fa ?
01237         T(d / fa) :
01238         T(1.0); // l \in [0,1]
01239     T m = g / f; // m \in (-1/macheps, 1/macheps)
01240     T t = 2.0 - l; // t \in [1,2]
01241     T mm = m * m;
01242     T tt = t * t;
01243     T s = std::sqrt(tt + mm); // s \in [1,1 + 1/macheps]
01244     T r = l != 0.0 ?
01245         T(std::sqrt(l * l + mm)) :
01246         T(std::abs(m)); // r \in [0,1 + 1/macheps]
01247     T a = 0.5 * (s + r); // a \in [1,1 + |m|]
01248     s1 = ha / a;
01249     s0 = fa * a;
01250 
01251     // Compute singular vectors
01252     T tau; // second assignment to T in DLASV2
01253     if (mm != 0.0) {
01254       tau = (m / (s + t) + m / (r + l)) * (1.0 + a);
01255     } else {
01256       // note that m is very tiny
01257       tau = l == 0.0 ?
01258           T(copysign(T(2.0), f) * copysign(T(1.0), g)) :
01259           T(g / copysign(d, f) + m / t);
01260     }
01261     T lv = std::sqrt(tau * tau + 4.0); // second assignment to L in DLASV2
01262     cv = 2.0 / lv;
01263     sv = tau / lv;
01264     cu = (cv + sv * m) / a;
01265     su = (h / f) * sv / a;
01266   }
01267 
01268   // Fix signs of singular values in accordance to sign of singular vectors
01269   s0 = copysign(s0, f);
01270   s1 = copysign(s1, h);
01271 
01272   if (swap_diag) {
01273     std::swap(cu, sv);
01274     std::swap(su, cv);
01275   }
01276 
01277   Tensor<T, N> U(cu, -su, su, cu);
01278 
01279   Tensor<T, N> S(s0, 0.0, 0.0, s1);
01280 
01281   Tensor<T, N> V(cv, -sv, sv, cv);
01282 
01283   return boost::make_tuple(U, S, V);
01284 }
01285 
01286 //
01287 // R^2 singular value decomposition (SVD)
01288 // \param A tensor
01289 // \return \f$ A = USV^T\f$
01290 //
01291 template<typename T, Index N>
01292 boost::tuple<Tensor<T, N>, Tensor<T, N>, Tensor<T, N> >
01293 svd_2x2(Tensor<T, N> const & A)
01294 {
01295   assert(A.get_dimension() == 2);
01296 
01297   // First compute a givens rotation to eliminate 1,0 entry in tensor
01298   T c = 1.0;
01299   T s = 0.0;
01300   boost::tie(c, s) = givens(A(0,0), A(1,0));
01301 
01302   Tensor<T, N>
01303   R(c, -s, s, c);
01304 
01305   Tensor<T, N>
01306   B = R * A;
01307 
01308   // B is bidiagonal. Use specialized algorithm to compute its SVD
01309   Tensor<T, N>
01310   X(2), S(2), V(2);
01311 
01312   boost::tie(X, S, V) = svd_bidiagonal<T, N>(B(0,0), B(0,1), B(1,1));
01313 
01314   // Complete general 2x2 SVD with givens rotation calculated above
01315   Tensor<T, N>
01316   U = transpose(R) * X;
01317 
01318   return boost::make_tuple(U, S, V);
01319 }
01320 
01321 //
01322 // R^N singular value decomposition (SVD)
01323 // \param A tensor
01324 // \return \f$ A = USV^T\f$
01325 //
01326 template<typename T, Index N>
01327 boost::tuple<Tensor<T, N>, Tensor<T, N>, Tensor<T, N> >
01328 svd_NxN(Tensor<T, N> const & A)
01329 {
01330   Tensor<T, N>
01331   S = A;
01332 
01333   Index const
01334   dimension = A.get_dimension();
01335 
01336   Tensor<T, N>
01337   U = identity<T, N>(dimension);
01338 
01339   Tensor<T, N>
01340   V = identity<T, N>(dimension);
01341 
01342   T
01343   off = norm_off_diagonal(S);
01344 
01345   T const
01346   tol = machine_epsilon<T>() * norm(A);
01347 
01348   Index const
01349   max_iter = 128;
01350 
01351   Index
01352   num_iter = 0;
01353 
01354   while (off > tol && num_iter < max_iter) {
01355 
01356     // Find largest off-diagonal entry
01357     Index
01358     p = 0;
01359 
01360     Index
01361     q = 0;
01362 
01363     boost::tie(p,q) = arg_max_off_diagonal(S);
01364 
01365     if (p > q) {
01366       std::swap(p, q);
01367     }
01368 
01369     // Obtain left and right Givens rotations by using 2x2 SVD
01370     Tensor <T, 2>
01371     Spq(S(p,p), S(p,q), S(q,p), S(q,q));
01372 
01373     Tensor <T, 2>
01374     L(2), D(2), R(2);
01375 
01376     boost::tie(L, D, R) = svd_2x2(Spq);
01377 
01378     T const &
01379     cl = L(0,0);
01380 
01381     T const &
01382     sl = L(0,1);
01383 
01384     T const &
01385     cr = R(0,0);
01386 
01387     T const &
01388     sr = (sgn(R(0,1)) == sgn(R(1,0))) ? T(-R(0,1)) : T(R(0,1));
01389 
01390     // Apply both Givens rotations to matrices
01391     // that are converging to singular values and singular vectors
01392     givens_left(cl, sl, p, q, S);
01393     givens_right(cr, sr, p, q, S);
01394 
01395     givens_right(cl, sl, p, q, U);
01396     givens_left(cr, sr, p, q, V);
01397 
01398     off = norm_off_diagonal(S);
01399     num_iter++;
01400   }
01401 
01402   if (num_iter == max_iter) {
01403     std::cerr << "WARNING: SVD iteration did not converge." << std::endl;
01404   }
01405 
01406   // Fix signs for entries in the diagonal matrix S
01407   // that are negative
01408   for (Index i = 0; i < dimension; ++i) {
01409     if (S(i,i) < 0.0) {
01410       S(i,i) = -S(i,i);
01411       for (Index j = 0; j < dimension; ++j) {
01412         U(j,i) = -U(j,i);
01413       }
01414     }
01415   }
01416 
01417   Vector<T, N> s(dimension);
01418   Tensor<T, N> P(dimension);
01419 
01420   boost::tie(s, P) = sort_permutation(diag(S));
01421   S = diag(s);
01422   U = U * P;
01423   V = V * P;
01424 
01425   return boost::make_tuple(U, diag(diag(S)), transpose(V));
01426 }
01427 
01428 } // anonymous namespace
01429 
01430 //
01431 // R^N singular value decomposition (SVD)
01432 // \param A tensor
01433 // \return \f$ A = USV^T\f$
01434 //
01435 template<typename T, Index N>
01436 boost::tuple<Tensor<T, N>, Tensor<T, N>, Tensor<T, N> >
01437 svd(Tensor<T, N> const & A)
01438 {
01439   Index const
01440   dimension = A.get_dimension();
01441 
01442   Tensor<T, N>
01443   U(dimension), S(dimension), V(dimension);
01444 
01445   switch (dimension) {
01446 
01447     default:
01448       boost::tie(U, S, V) = svd_NxN(A);
01449       break;
01450 
01451     case 2:
01452       boost::tie(U, S, V) = svd_2x2(A);
01453       break;
01454 
01455   }
01456 
01457   return boost::make_tuple(U, S, V);
01458 }
01459 
01460 //
01461 // Project to O(N) (Orthogonal Group) using a Newton-type algorithm.
01462 // See Higham's Functions of Matrices p210 [2008]
01463 // \param A tensor (often a deformation-gradient-like tensor)
01464 // \return \f$ R = \argmin_Q \|A - Q\|\f$
01465 // This algorithm projects a given tensor in GL(N) to O(N).
01466 // The rotation/reflection obtained through this projection is
01467 // the orthogonal component of the real polar decomposition
01468 //
01469 template<typename T, Index N>
01470 Tensor<T, N>
01471 polar_rotation(Tensor<T, N> const & A)
01472 {
01473   Index const
01474   dimension = A.get_dimension();
01475 
01476   bool
01477   scale = true;
01478 
01479   T const
01480   tol_scale = 0.01;
01481 
01482   T const
01483   tol_conv = std::sqrt(dimension) * machine_epsilon<T>();
01484 
01485   Tensor<T, N>
01486   X = A;
01487 
01488   T
01489   gamma = 2.0;
01490 
01491   Index const
01492   max_iter = 128;
01493 
01494   Index
01495   num_iter = 0;
01496  
01497   while (num_iter < max_iter) {
01498 
01499     Tensor<T, N>
01500     Y = inverse(X);
01501 
01502     T
01503     mu = 1.0;
01504 
01505     if (scale == true) {
01506       mu = (norm_1(Y) * norm_infinity(Y)) / (norm_1(X) * norm_infinity(X));
01507       mu = std::sqrt(std::sqrt(mu));
01508     }
01509 
01510     Tensor<T, N>
01511     Z = 0.5 * (mu * X + transpose(Y) / mu);
01512 
01513     Tensor<T, N>
01514     D = Z - X;
01515 
01516     T
01517     delta = norm(D) / norm(Z);
01518 
01519     if (scale == true && delta < tol_scale) {
01520       scale = false;
01521     }
01522 
01523     bool
01524     end_iter =
01525         norm(D) <= std::sqrt(tol_conv) ||
01526         (delta > 0.5 * gamma && scale == false);
01527 
01528     X = Z;
01529     gamma = delta;
01530 
01531     if (end_iter == true) {
01532       break;
01533     }
01534 
01535     num_iter++;
01536 
01537   }
01538 
01539   if (num_iter == max_iter) {
01540     std::cerr << "WARNING: Polar iteration did not converge." << std::endl;
01541   }
01542 
01543   return X;
01544 }
01545 
01546 //
01547 // R^N Left polar decomposition
01548 // \param A tensor (often a deformation-gradient-like tensor)
01549 // \return \f$ VR = A \f$ with \f$ R \in SO(N) \f$ and \f$ V \in SPD(N) \f$
01550 //
01551 template<typename T, Index N>
01552 std::pair<Tensor<T, N>, Tensor<T, N> >
01553 polar_left(Tensor<T, N> const & A)
01554 {
01555   Tensor<T, N>
01556   R = polar_rotation(A);
01557 
01558   Tensor<T, N>
01559   V = sym(A * transpose(R));
01560 
01561   return std::make_pair(V, R);
01562 }
01563 
01564 //
01565 // R^N Right polar decomposition
01566 // \param A tensor (often a deformation-gradient-like tensor)
01567 // \return \f$ RU = A \f$ with \f$ R \in SO(N) \f$ and \f$ U \in SPD(N) \f$
01568 //
01569 template<typename T, Index N>
01570 std::pair<Tensor<T, N>, Tensor<T, N> >
01571 polar_right(Tensor<T, N> const & A)
01572 {
01573   Tensor<T, N>
01574   R = polar_rotation(A);
01575 
01576   Tensor<T, N>
01577   U = sym(transpose(R) * A);
01578 
01579   return std::make_pair(R, U);
01580 }
01581 
01582 //
01583 // R^3 left polar decomposition with eigenvalue decomposition
01584 // \param F tensor (often a deformation-gradient-like tensor)
01585 // \return \f$ VR = F \f$ with \f$ R \in SO(3) \f$ and V SPD(3)
01586 //
01587 template<typename T, Index N>
01588 std::pair<Tensor<T, N>, Tensor<T, N> >
01589 polar_left_eig(Tensor<T, N> const & F)
01590 {
01591   assert(F.get_dimension() == 3);
01592 
01593   // set up return tensors
01594   Tensor<T, N>
01595   R(3);
01596 
01597   Tensor<T, N>
01598   V(3);
01599 
01600   // temporary tensor used to compute R
01601   Tensor<T, N>
01602   Vinv(3);
01603 
01604   // compute spd tensor
01605   Tensor<T, N>
01606   b = F * transpose(F);
01607 
01608   // get eigenvalues/eigenvectors
01609   Tensor<T, N>
01610   eVal(3);
01611 
01612   Tensor<T, N>
01613   eVec(3);
01614 
01615   boost::tie(eVec, eVal) = eig_spd(b);
01616 
01617   // compute sqrt() and inv(sqrt()) of eigenvalues
01618   Tensor<T, N>
01619   x = zero<T, N>(3);
01620 
01621   x(0,0) = std::sqrt(eVal(0,0));
01622   x(1,1) = std::sqrt(eVal(1,1));
01623   x(2,2) = std::sqrt(eVal(2,2));
01624 
01625   Tensor<T, N>
01626   xi = zero<T, N>(3);
01627 
01628   xi(0,0) = 1.0 / x(0,0);
01629   xi(1,1) = 1.0 / x(1,1);
01630   xi(2,2) = 1.0 / x(2,2);
01631 
01632   // compute V, Vinv, and R
01633   V    = eVec * x * transpose(eVec);
01634   Vinv = eVec * xi * transpose(eVec);
01635   R    = Vinv * F;
01636 
01637   return std::make_pair(V, R);
01638 }
01639 
01640 //
01641 // R^3 right polar decomposition with eigenvalue decomposition
01642 // \param F tensor (often a deformation-gradient-like tensor)
01643 // \return \f$ RU = F \f$ with \f$ R \in SO(3) \f$ and U SPD(3)
01644 //
01645 template<typename T, Index N>
01646 std::pair<Tensor<T, N>, Tensor<T, N> >
01647 polar_right_eig(Tensor<T, N> const & F)
01648 {
01649 
01650   Index const
01651   dimension = F.get_dimension();
01652 
01653   assert(dimension == 3);
01654 
01655   Tensor<T, N>
01656   R(dimension);
01657 
01658   Tensor<T, N>
01659   U(dimension);
01660 
01661   // temporary tensor used to compute R
01662   Tensor<T, N>
01663   Uinv(dimension);
01664 
01665   // compute spd tensor
01666   Tensor<T, N>
01667   C = transpose(F) * F;
01668 
01669   // get eigenvalues/eigenvectors
01670   Tensor<T, N>
01671   eVal(dimension);
01672 
01673   Tensor<T, N>
01674   eVec(dimension);
01675 
01676   boost::tie(eVec, eVal) = eig_spd(C);
01677 
01678   // compute sqrt() and inv(sqrt()) of eigenvalues
01679   Tensor<T, N>
01680   x = zero<T, N>(dimension);
01681 
01682   x(0,0) = std::sqrt(eVal(0,0));
01683   x(1,1) = std::sqrt(eVal(1,1));
01684   x(2,2) = std::sqrt(eVal(2,2));
01685 
01686   Tensor<T, N>
01687   xi = zero<T, N>(dimension);
01688 
01689   xi(0,0) = 1.0 / x(0,0);
01690   xi(1,1) = 1.0 / x(1,1);
01691   xi(2,2) = 1.0 / x(2,2);
01692 
01693   // compute U, Uinv, and R
01694   U    = eVec * x * transpose(eVec);
01695   Uinv = eVec * xi * transpose(eVec);
01696   R    = F * Uinv;
01697 
01698   return std::make_pair(R, U);
01699 }
01700 
01701 //
01702 // R^N left polar decomposition with matrix logarithm for V
01703 // \param F tensor (often a deformation-gradient-like tensor)
01704 // \return \f$ VR = F \f$ with \f$ R \in SO(N) \f$ and V SPD(N), and log V
01705 //
01706 template<typename T, Index N>
01707 boost::tuple<Tensor<T, N>, Tensor<T, N>, Tensor<T, N> >
01708 polar_left_logV(Tensor<T, N> const & F)
01709 {
01710   Index const
01711   dimension = F.get_dimension();
01712 
01713   Tensor<T, N>
01714   X(dimension), S(dimension), Y(dimension);
01715 
01716   boost::tie(X, S, Y) = svd(F);
01717 
01718   Tensor<T, N>
01719   R = X * transpose(Y);
01720 
01721   Tensor<T, N>
01722   V = X * S * transpose(X);
01723 
01724   Tensor<T, N>
01725   s = S;
01726 
01727   for (Index i = 0; i < dimension; ++i) {
01728     s(i,i) = std::log(s(i,i));
01729   }
01730 
01731   Tensor<T, N>
01732   v = X * s * transpose(X);
01733 
01734   return boost::make_tuple(V, R, v);
01735 }
01736 
01737 template<typename T, Index N>
01738 boost::tuple<Tensor<T, N>, Tensor<T, N>, Tensor<T, N> >
01739 polar_left_logV_eig(Tensor<T, N> const & F)
01740 {
01741   Index const
01742   dimension = F.get_dimension();
01743 
01744   Tensor<T, N> const
01745   b = dot_t(F, F);
01746 
01747   Tensor<T, N>
01748   V(dimension), D(dimension);
01749 
01750   boost::tie(V, D) = eig_sym(b);
01751 
01752   Tensor<T, N>
01753   DQ(dimension, ZEROS), DI(dimension, ZEROS), DL(dimension, ZEROS);
01754 
01755   for (Index i = 0; i < dimension; ++i) {
01756     DQ(i,i) = std::sqrt(D(i,i));
01757     DI(i,i) = 1.0 / DQ(i,i);
01758     DL(i,i) = std::log(DQ(i,i));
01759   }
01760 
01761   Tensor<T, N> const
01762   R = dot(V, DI) * t_dot(V, F);
01763 
01764   Tensor<T, N> const
01765   X = V * dot_t(DQ, V);
01766 
01767   Tensor<T, N> const
01768   x = V * dot_t(DL, V);
01769 
01770   return boost::make_tuple(X, R, x);
01771 }
01772 
01773 //
01774 // R^N left polar decomposition with matrix logarithm for V
01775 // \param F tensor (often a deformation-gradient-like tensor)
01776 // \return \f$ VR = F \f$ with \f$ R \in SO(N) \f$ and V SPD(N), and log V
01777 //
01778 template<typename T, Index N>
01779 boost::tuple<Tensor<T, N>, Tensor<T, N>, Tensor<T, N> >
01780 polar_left_logV_lame(Tensor<T, N> const & F)
01781 {
01782   Index const
01783   dimension = F.get_dimension();
01784 
01785   // set up return tensors
01786   Tensor<T, N> R(dimension), V(dimension), v(dimension), Vinv(dimension);
01787 
01788   // compute spd tensor
01789   Tensor<T, N> b = F*transpose(F);
01790 
01791   // get eigenvalues/eigenvectors
01792   Tensor<T, N> eVal(dimension);
01793   Tensor<T, N> eVec(dimension);
01794   boost::tie(eVec,eVal) = eig_spd_cos(b);
01795 
01796   // compute sqrt() and inv(sqrt()) of eigenvalues
01797   Tensor<T, N> x = zero<T, N>(3);
01798   x(0,0) = std::sqrt(eVal(0,0));
01799   x(1,1) = std::sqrt(eVal(1,1));
01800   x(2,2) = std::sqrt(eVal(2,2));
01801   Tensor<T, N> xi = zero<T, N>(3);
01802   xi(0,0) = 1.0/x(0,0);
01803   xi(1,1) = 1.0/x(1,1);
01804   xi(2,2) = 1.0/x(2,2);
01805   Tensor<T, N> lnx = zero<T, N>(3);
01806   lnx(0,0) = std::log(x(0,0));
01807   lnx(1,1) = std::log(x(1,1));
01808   lnx(2,2) = std::log(x(2,2));
01809 
01810   // compute V, Vinv, log(V)=v, and R
01811   V    = eVec*x*transpose(eVec);
01812   Vinv = eVec*xi*transpose(eVec);
01813   v    = eVec*lnx*transpose(eVec);
01814   R    = Vinv*F;
01815 
01816   return boost::make_tuple(V,R,v);
01817 }
01818 
01819 //
01820 // R^N logarithmic map using BCH expansion (4 terms)
01821 // \param x tensor
01822 // \param y tensor
01823 // \return Baker-Campbell-Hausdorff series up to 4 terms
01824 //
01825 template<typename T, Index N>
01826 Tensor<T, N>
01827 bch(Tensor<T, N> const & x, Tensor<T, N> const & y)
01828 {
01829   return
01830       // first order term
01831       x + y
01832       +
01833       // second order term
01834       0.5*(x*y - y*x)
01835       +
01836       // third order term
01837       1.0/12.0 *
01838       (x*x*y - 2.0*x*y*x + x*y*y + y*x*x - 2.0*y*x*y + y*y*x)
01839       +
01840       // fourth order term
01841       1.0/24.0 *
01842       (x*x*y*y - 2.0*x*y*x*y + 2.0*y*x*y*x - y*y*x*x);
01843 }
01844 
01845 //
01846 // Symmetric Schur algorithm for R^2.
01847 // \param \f$ A = [f, g; g, h] \in S(2) \f$
01848 // \return \f$ c, s \rightarrow [c, -s; s, c]\f diagonalizes A$
01849 //
01850 template<typename T>
01851 std::pair<T, T>
01852 schur_sym(T const f, T const g, T const h)
01853 {
01854   T c = 1.0;
01855   T s = 0.0;
01856 
01857   if (g != 0.0) {
01858     T t = (h - f) / (2.0 * g);
01859 
01860     if (t >= 0.0) {
01861       t = 1.0 / (std::sqrt(1.0 + t * t) + t);
01862     } else {
01863       t = -1.0 / (std::sqrt(1.0 + t * t) - t);
01864     }
01865     c = 1.0 / std::sqrt(1.0 + t * t);
01866     s = t * c;
01867   }
01868 
01869   return std::make_pair(c, s);
01870 }
01871 
01872 //
01873 // Givens rotation. [c, -s; s, c] [a; b] = [r; 0]
01874 // \param a, b
01875 // \return c, s
01876 //
01877 template<typename T>
01878 std::pair<T, T>
01879 givens(T const & a, T const & b)
01880 {
01881   T c = 1.0;
01882   T s = 0.0;
01883 
01884   if (b != 0.0) {
01885     if (std::abs(b) > std::abs(a)) {
01886       T const t = - a / b;
01887       s = 1.0 / std::sqrt(1.0 + t * t);
01888       c = t * s;
01889     } else {
01890       T const t = - b / a;
01891       c = 1.0 / std::sqrt(1.0 + t * t);
01892       s = t * c;
01893     }
01894   }
01895 
01896   return std::make_pair(c, s);
01897 }
01898 
01899 namespace {
01900 
01901 //
01902 // R^N eigenvalue decomposition for symmetric 2nd-order tensor
01903 // \param A tensor
01904 // \return V eigenvectors, D eigenvalues in diagonal Matlab-style
01905 // See algorithm 8.4.2 in Matrix Computations, Golub & Van Loan 1996
01906 //
01907 template<typename T, Index N>
01908 std::pair<Tensor<T, N>, Tensor<T, N> >
01909 eig_sym_NxN(Tensor<T, N> const & A)
01910 {
01911   Tensor<T, N>
01912   D = sym(A);
01913 
01914   Index const
01915   dimension = A.get_dimension();
01916 
01917   Tensor<T, N>
01918   V = identity<T, N>(dimension);
01919 
01920   T
01921   off = norm_off_diagonal(D);
01922 
01923   T
01924   tol = machine_epsilon<T>() * norm(A);
01925 
01926   // Estimate based on random generation and linear regression.
01927   // Golub & Van Loan p 429 expect ~ dimension * log(dimension)
01928   Index const
01929   max_iter = 5 * dimension * dimension / 2;
01930 
01931   Index
01932   num_iter = 0;
01933 
01934   while (off > tol && num_iter < max_iter) {
01935 
01936     // Find largest off-diagonal entry
01937     Index
01938     p = 0;
01939 
01940     Index
01941     q = 0;
01942 
01943     boost::tie(p,q) = arg_max_off_diagonal(D);
01944     if (p > q) {
01945       std::swap(p,q);
01946     }
01947 
01948     // Obtain Givens rotations by using 2x2 symmetric Schur algorithm
01949     T const &
01950     f = D(p,p);
01951 
01952     T const &
01953     g = D(p,q);
01954 
01955     T const &
01956     h = D(q,q);
01957 
01958     T
01959     c, s;
01960 
01961     boost::tie(c, s) = schur_sym(f, g, h);
01962 
01963     // Apply Givens rotation to matrices
01964     // that are converging to eigenvalues and eigenvectors
01965     givens_left(c, s, p, q, D);
01966     givens_right(c, s, p, q, D);
01967 
01968     givens_right(c, s, p, q, V);
01969 
01970     off = norm_off_diagonal(D);
01971     num_iter++;
01972   }
01973 
01974   Vector<T, N> d(dimension);
01975   Tensor<T, N> P(dimension);
01976 
01977   boost::tie(d, P) = sort_permutation(diag(D));
01978   D = diag(d);
01979   V = V * P;
01980 
01981   return std::make_pair(V, D);
01982 }
01983 
01984 //
01985 // R^2 eigenvalue decomposition for symmetric 2nd-order tensor
01986 // \param A tensor
01987 // \return V eigenvectors, D eigenvalues in diagonal Matlab-style
01988 //
01989 template<typename T, Index N>
01990 std::pair<Tensor<T, N>, Tensor<T, N> >
01991 eig_sym_2x2(Tensor<T, N> const & A)
01992 {
01993   assert(A.get_dimension() == 2);
01994 
01995   T const f = A(0,0);
01996   T const g = 0.5 * (A(0,1) + A(1,0));
01997   T const h = A(1,1);
01998 
01999   //
02000   // Eigenvalues, based on LAPACK's dlae2
02001   //
02002   T const sum = f + h;
02003   T const dif = std::abs(f - h);
02004   T const g2 = std::abs(g + g);
02005 
02006   T fhmax = f;
02007   T fhmin = h;
02008 
02009   const bool swap_diag = std::abs(h) > std::abs(f);
02010 
02011   if (swap_diag == true) {
02012     std::swap(fhmax, fhmin);
02013   }
02014 
02015   T r = 0.0;
02016   if (dif > g2) {
02017     T const t = g2 / dif;
02018     r = dif * std::sqrt(1.0 + t * t);
02019   } else if (dif < g2) {
02020     T const t = dif / g2;
02021     r = g2 * std::sqrt(1.0 + t * t);
02022   } else {
02023     // dif == g2, including zero
02024         r = g2 * std::sqrt(2.0);
02025   }
02026 
02027   T s0 = 0.0;
02028   T s1 = 0.0;
02029 
02030   if (sum != 0.0) {
02031     s0 = 0.5 * (sum + copysign(r, sum));
02032     // Order of execution important.
02033     // To get fully accurate smaller eigenvalue,
02034     // next line needs to be executed in higher precision.
02035     s1 = (fhmax / s0) * fhmin - (g / s0) * g;
02036   } else {
02037     // s0 == s1, including zero
02038     s0 = 0.5 * r;
02039     s1 = -0.5 * r;
02040   }
02041 
02042   Tensor<T, N>
02043   D(s0, 0.0, 0.0, s1);
02044 
02045   //
02046   // Eigenvectors
02047   //
02048   T
02049   c, s;
02050 
02051   boost::tie(c, s) = schur_sym(f, g, h);
02052 
02053   Tensor<T, N>
02054   V(c, -s, s, c);
02055 
02056   if (swap_diag == true) {
02057     // swap eigenvectors if eigenvalues were swapped
02058     std::swap(V(0,0), V(0,1));
02059     std::swap(V(1,0), V(1,1));
02060   }
02061 
02062   return std::make_pair(V, D);
02063 }
02064 
02065 } // anonymous namespace
02066 
02067 //
02068 // R^N eigenvalue decomposition for symmetric 2nd-order tensor
02069 // \param A tensor
02070 // \return V eigenvectors, D eigenvalues in diagonal Matlab-style
02071 //
02072 template<typename T, Index N>
02073 std::pair<Tensor<T, N>, Tensor<T, N> >
02074 eig_sym(Tensor<T, N> const & A)
02075 {
02076   Index const
02077   dimension = A.get_dimension();
02078 
02079   Tensor<T, N>
02080   V(dimension), D(dimension);
02081 
02082   switch (dimension) {
02083 
02084     default:
02085       boost::tie(V, D) = eig_sym_NxN(A);
02086       break;
02087 
02088     case 2:
02089       boost::tie(V, D) = eig_sym_2x2(A);
02090       break;
02091 
02092   }
02093 
02094   return std::make_pair(V, D);
02095 }
02096 
02097 //
02098 // R^N eigenvalue decomposition for SPD 2nd-order tensor
02099 // \param A tensor
02100 // \return V eigenvectors, D eigenvalues in diagonal Matlab-style
02101 //
02102 template<typename T, Index N>
02103 std::pair<Tensor<T, N>, Tensor<T, N> >
02104 eig_spd(Tensor<T, N> const & A)
02105 {
02106   return eig_sym(A);
02107 }
02108 
02109 //
02110 // R^3 eigenvalue decomposition for SPD 2nd-order tensor
02111 // \param A tensor
02112 // \return V eigenvectors, D eigenvalues in diagonal Matlab-style
02113 //
02114 template<typename T, Index N>
02115 std::pair<Tensor<T, N>, Tensor<T, N> >
02116 eig_spd_cos(Tensor<T, N> const & A)
02117 {
02118   Index const
02119   dimension = A.get_dimension();
02120 
02121   assert(dimension == 3);
02122 
02123   // This algorithm comes from the journal article
02124   // Scherzinger and Dohrmann, CMAME 197 (2008) 4007-4015
02125 
02126   // this algorithm will return the eigenvalues in D
02127   // and the eigenvectors in V
02128   Tensor<T, N>
02129   D = zero<T, N>(dimension);
02130 
02131   Tensor<T, N>
02132   V = zero<T, N>(dimension);
02133 
02134   // not sure if this is necessary...
02135   T
02136   pi = std::acos(-1);
02137 
02138   // convenience operators
02139   Tensor<T, N> const
02140   I = identity<T, N>(dimension);
02141 
02142   int
02143   ii[3][2] = { { 1, 2 }, { 2, 0 }, { 0, 1 } };
02144 
02145   Tensor<T, N>
02146   rm = zero<T, N>(dimension);
02147 
02148   // scale the matrix to reduce the characteristic equation
02149   T
02150   trA = (1.0/3.0) * I1(A);
02151 
02152   Tensor<T, N>
02153   Ap(A - trA*I);
02154 
02155   // compute other invariants
02156   T
02157   J2 = I2(Ap);
02158 
02159   T
02160   J3 = det(Ap);
02161 
02162   // deal with volumetric tensors
02163   if (-J2 <= 1.e-30)
02164   {
02165     D(0,0) = trA;
02166     D(1,1) = trA;
02167     D(2,2) = trA;
02168 
02169     V(0,0) = 1.0;
02170     V(1,0) = 0.0;
02171     V(2,0) = 0.0;
02172 
02173     V(0,1) = 0.0;
02174     V(1,1) = 1.0;
02175     V(2,1) = 0.0;
02176 
02177     V(0,2) = 0.0;
02178     V(1,2) = 0.0;
02179     V(2,2) = 1.0;
02180   }
02181   else
02182   {
02183     // first things first, find the most dominant e-value
02184     // Need to solve cos(3 theta)=rhs for theta
02185     T
02186     t1 = 3.0 / -J2;
02187 
02188     T
02189     rhs = (J3 / 2.0) * T(std::sqrt(t1 * t1 * t1));
02190 
02191     T
02192     theta = pi / 2.0 * (1.0 - (rhs < 0 ? -1.0 : 1.0));
02193 
02194     if (std::abs(rhs) <= 1.0) theta = std::acos(rhs);
02195 
02196     T
02197     thetad3 = theta / 3.0;
02198 
02199     if (thetad3 > pi / 6.0) thetad3 += 2.0 * pi / 3.0;
02200 
02201     // most dominant e-value
02202     D(2,2) = 2.0 * std::cos(thetad3) * std::sqrt(-J2 / 3.0);
02203 
02204     // now reduce the system
02205     Tensor<T, N>
02206     R = Ap - D(2,2) * I;
02207 
02208     // QR factorization with column pivoting
02209     Vector<T, N> a(dimension);
02210     a(0) = R(0,0)*R(0,0) + R(1,0)*R(1,0) + R(2,0)*R(2,0);
02211     a(1) = R(0,1)*R(0,1) + R(1,1)*R(1,1) + R(2,1)*R(2,1);
02212     a(2) = R(0,2)*R(0,2) + R(1,2)*R(1,2) + R(2,2)*R(2,2);
02213 
02214     // find the most dominant column
02215     int k = 0;
02216     T max = a(0);
02217     if (a(1) > max)
02218     {
02219       k = 1;
02220       max = a(1);
02221     }
02222     if (a(2) > max)
02223     {
02224       k = 2;
02225     }
02226 
02227     // normalize the most dominant column to get s1
02228     a(k) = std::sqrt(a(k));
02229     for (int i(0); i < dimension; ++i)
02230       R(i,k) /= a(k);
02231 
02232     // dot products of dominant column with other two columns
02233     T d0 = 0.0;
02234     T d1 = 0.0;
02235     for (int i(0); i < dimension; ++i)
02236     {
02237       d0 += R(i,k) * R(i,ii[k][0]);
02238       d1 += R(i,k) * R(i,ii[k][1]);
02239     }
02240 
02241     // projection
02242     for (int i(0); i < dimension; ++i)
02243     {
02244       R(i,ii[k][0]) -= d0 * R(i,k);
02245       R(i,ii[k][1]) -= d1 * R(i,k);
02246     }
02247 
02248     // now finding next most dominant column
02249     a.clear();
02250     for (int i(0); i < dimension; ++i)
02251     {
02252       a(0) += R(i,ii[k][0]) * R(i,ii[k][0]);
02253       a(1) += R(i,ii[k][1]) * R(i,ii[k][1]);
02254     }
02255 
02256     int p = 0;
02257     if (std::abs(a(1)) > std::abs(a(0))) p = 1;
02258 
02259     // normalize next most dominant column to get s2
02260     a(p) = std::sqrt(a(p));
02261     int k2 = ii[k][p];
02262 
02263     for (int i(0); i < dimension; ++i)
02264       R(i,k2) /= a(p);
02265 
02266     // set first eigenvector as cross product of s1 and s2
02267     V(0,2) = R(1,k) * R(2,k2) - R(2,k) * R(1,k2);
02268     V(1,2) = R(2,k) * R(0,k2) - R(0,k) * R(2,k2);
02269     V(2,2) = R(0,k) * R(1,k2) - R(1,k) * R(0,k2);
02270 
02271     // normalize
02272     T
02273     mag = std::sqrt(V(0,2) * V(0,2) + V(1,2) * V(1,2) + V(2,2) * V(2,2));
02274 
02275     V(0,2) /= mag;
02276     V(1,2) /= mag;
02277     V(2,2) /= mag;
02278 
02279     // now for the other two eigenvalues, extract vectors
02280     Vector<T, N>
02281     rk(R(0,k), R(1,k), R(2,k));
02282 
02283     Vector<T, N>
02284     rk2(R(0,k2), R(1,k2), R(2,k2));
02285 
02286     // compute projections
02287     Vector<T, N>
02288     ak = Ap * rk;
02289 
02290     Vector<T, N>
02291     ak2 = Ap * rk2;
02292 
02293     // set up reduced remainder matrix
02294     rm(0,0) = dot(rk,ak);
02295     rm(0,1) = dot(rk,ak2);
02296     rm(1,1) = dot(rk2,ak2);
02297 
02298     // compute eigenvalues 2 and 3
02299     T
02300     b = 0.5 * (rm(0,0) - rm(1,1));
02301 
02302     T
02303     fac = (b < 0 ? -1.0 : 1.0);
02304 
02305     T
02306     arg = b * b + rm(0,1) * rm(0,1);
02307 
02308     if (arg == 0)
02309       D(0,0) = rm(1,1) + b;
02310     else
02311       D(0,0) = rm(1,1) + b - fac * std::sqrt(b * b + rm(0,1) * rm(0,1));
02312 
02313     D(1,1) = rm(0,0) + rm(1,1) - D(0,0);
02314 
02315     // update reduced remainder matrix
02316     rm(0,0) -= D(0,0);
02317     rm(1,0) = rm(0,1);
02318     rm(1,1) -= D(0,0);
02319 
02320     // again, find most dominant column
02321     a.clear();
02322     a(0) = rm(0,0) * rm(0,0) + rm(0,1) * rm(0,1);
02323     a(1) = rm(0,1) * rm(0,1) + rm(1,1) * rm(1,1);
02324 
02325     int k3 = 0;
02326     if (a(1) > a(0)) k3 = 1;
02327     if (a(k3) == 0.0)
02328     {
02329       rm(0,k3) = 1.0;
02330       rm(1,k3) = 0.0;
02331     }
02332 
02333     // set 2nd eigenvector via cross product
02334     V(0,0) = rm(0,k3) * rk2(0) - rm(1,k3) * rk(0);
02335     V(1,0) = rm(0,k3) * rk2(1) - rm(1,k3) * rk(1);
02336     V(2,0) = rm(0,k3) * rk2(2) - rm(1,k3) * rk(2);
02337 
02338     // normalize
02339     mag = std::sqrt(V(0,0) * V(0,0) + V(1,0) * V(1,0) + V(2,0) * V(2,0));
02340     V(0,0) /= mag;
02341     V(1,0) /= mag;
02342     V(2,0) /= mag;
02343 
02344     // set last eigenvector as cross product of other two
02345     V(0,1) = V(1,0) * V(2,2) - V(2,0) * V(1,2);
02346     V(1,1) = V(2,0) * V(0,2) - V(0,0) * V(2,2);
02347     V(2,1) = V(0,0) * V(1,2) - V(1,0) * V(0,2);
02348 
02349     // normalize
02350     mag = std::sqrt(V(0,1) * V(0,1) + V(1,1) * V(1,1) + V(2,1) * V(2,1));
02351     V(0,1) /= mag;
02352     V(1,1) /= mag;
02353     V(2,1) /= mag;
02354 
02355     // add back in the offset
02356     for (int i(0); i < dimension; ++i)
02357       D(i,i) += trA;
02358   }
02359 
02360   return std::make_pair(V, D);
02361 }
02362 
02363 //
02364 // Cholesky decomposition, rank-1 update algorithm
02365 // (Matrix Computations 3rd ed., Golub & Van Loan, p145)
02366 // \param A assumed symmetric tensor
02367 // \return G Cholesky factor A = GG^T
02368 // \return completed (bool) algorithm ran to completion
02369 //
02370 template<typename T, Index N>
02371 std::pair<Tensor<T, N>, bool >
02372 cholesky(Tensor<T, N> const & A)
02373 {
02374   Tensor<T, N>
02375   G = sym(A);
02376 
02377   Index const
02378   dimension = A.get_dimension();
02379 
02380   for (Index k = 0; k < dimension; ++k) {
02381 
02382     // Zeros above the diagonal
02383     for (Index j = k + 1; j < dimension; ++j) {
02384       G(k,j) = 0.0;
02385     }
02386 
02387     T
02388     s = G(k,k);
02389 
02390     if (s <= 0.0) {
02391       return std::make_pair(G, false);
02392     }
02393 
02394     s = std::sqrt(s);
02395 
02396     for (Index j = k + 1; j < dimension; ++j) {
02397       G(j,k) /= s;
02398     }
02399 
02400     G(k,k) = s;
02401 
02402     for (Index j = k + 1; j < dimension; ++j) {
02403       for (Index i = j; i < dimension; ++i) {
02404         G(i,j) -= G(i,k) * G(j,k);
02405       }
02406     }
02407 
02408   }
02409 
02410   return std::make_pair(G, true);
02411 }
02412 
02413 } // namespace Intrepid
02414 
02415 #endif // Intrepid_MiniTensor_LinearAlgebra_t_h