|
Intrepid
|
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
1.7.6.1