00001 #ifndef OPENTISSUE_CORE_MATH_BIG_BIG_GMRES_H
00002 #define OPENTISSUE_CORE_MATH_BIG_BIG_GMRES_H
00003
00004
00005
00006
00007
00008
00009
00010 #include <OpenTissue/configuration.h>
00011
00012 #include <OpenTissue/core/math/big/big_identity_preconditioner.h>
00013 #include <OpenTissue/core/math/big/big_prod.h>
00014 #include <OpenTissue/core/math/big/big_residual.h>
00015 #include <OpenTissue/core/math/big/big_backsolve.h>
00016
00017 #include <OpenTissue/core/math/math_precision.h>
00018 #include <OpenTissue/core/math/math_value_traits.h>
00019 #include <OpenTissue/core/math/math_is_number.h>
00020
00021 #include <boost/cast.hpp>
00022
00023 #include <vector>
00024 #include <stdexcept>
00025
00026 namespace OpenTissue
00027 {
00028 namespace math
00029 {
00030 namespace big
00031 {
00032 namespace detail
00033 {
00034
00045 template<typename value_type>
00046 inline void get_rotation (
00047 value_type const & a
00048 , value_type const & b
00049 , value_type & c
00050 , value_type & s
00051 )
00052 {
00053 using std::sqrt;
00054 using std::fabs;
00055
00056 typedef OpenTissue::math::ValueTraits<value_type> value_traits;
00057
00058 assert( is_number(a) || !"get_rotation(): a was not a number");
00059 assert( is_number(b) || !"get_rotation(): b was not a number");
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 if ( b == value_traits::zero() )
00134 {
00135 c = (a>0) ? value_traits::one() : -value_traits::one();
00136 s = value_traits::zero();
00137 }
00138 else if ( a == value_traits::zero() )
00139 {
00140 c = value_traits::zero();
00141 s = (b>0) ? value_traits::one() : - value_traits::one();
00142 }
00143 else if ( fabs( b ) > fabs( a ) )
00144 {
00145 value_type t = a / b;
00146 value_type u = sqrt ( value_traits::one() + t*t );
00147 u = (b > value_traits::zero()) ? u : -u;
00148 s = value_traits::one() / u;
00149 c = t * s;
00150 }
00151 else
00152 {
00153 value_type t = b / a;
00154 value_type u = sqrt ( value_traits::one() + t*t );
00155 u = (a > value_traits::zero()) ? u : -u;
00156 c = value_traits::one() / u;
00157 s = t * c;
00158 }
00159
00160 assert( is_number(c) || !"get_rotation(): c was not a number");
00161 assert( is_number(s) || !"get_rotation(): s was not a number");
00162 assert( c <= value_traits::one() || !"get_rotation(): illegal value of c");
00163 assert( c >= -value_traits::one() || !"get_rotation(): illegal value of c");
00164 assert( s <= value_traits::one() || !"get_rotation(): illegal value of s");
00165 assert( s >= -value_traits::one() || !"get_rotation(): illegal value of s");
00166 assert( fabs( (c*c+s*s) - value_traits::one() ) < math::working_precision<value_type>() || !"get_rotation(): Not a valid rotation");
00167 }
00168
00177 template<typename value_type>
00178 inline void set_rotation (
00179 value_type & a
00180 , value_type & b
00181 , value_type const & c
00182 , value_type const & s
00183 )
00184 {
00185 using std::fabs;
00186
00187 typedef OpenTissue::math::ValueTraits<value_type> value_traits;
00188
00189 assert( is_number(a) || !"set_rotation(): a was not a number");
00190 assert( is_number(b) || !"set_rotation(): b was not a number");
00191 assert( is_number(c) || !"set_rotation(): c was not a number");
00192 assert( is_number(s) || !"set_rotation(): s was not a number");
00193 assert( c <= value_traits::one() || !"set_rotation(): illegal value of c");
00194 assert( c >= -value_traits::one() || !"set_rotation(): illegal value of c");
00195 assert( s <= value_traits::one() || !"set_rotation(): illegal value of s");
00196 assert( s >= -value_traits::one() || !"set_rotation(): illegal value of s");
00197 assert( fabs( (c*c+s*s) - value_traits::one() ) < math::working_precision<value_type>() || !"set_rotation(): Not a valid rotation");
00198
00199
00200
00201
00202
00203
00204
00205
00206 value_type temp = c * a + s * b;
00207 b = c * b - s * a;
00208 a = temp;
00209 }
00210
00279 template<typename size_type, typename matrix_type, typename vector_type>
00280 inline void hessenberg_matrix_transform(
00281 size_type j
00282 , matrix_type & H
00283 , vector_type & g
00284 , vector_type & c
00285 , vector_type & s
00286 )
00287 {
00288 using std::fabs;
00289
00290 typedef typename vector_type::value_type value_type;
00291 typedef OpenTissue::math::ValueTraits<value_type> value_traits;
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303 for (size_type k = 0; k < j; ++k )
00304 {
00305 value_type a = H( k, j );
00306 value_type b = H( k+1, j );
00307 set_rotation ( a, b, c[ k ], s[ k ] );
00308 H( k, j ) = a;
00309 H( k+1, j ) = b;
00310 }
00311
00312
00313
00314 value_type a = H(j,j);
00315 value_type b = H(j+1,j);
00316 get_rotation ( a, b, c[j], s[j] );
00317
00318
00319
00320 a = H( j, j );
00321 b = H( j+1, j );
00322 set_rotation ( a, b, c[j], s[j] );
00323
00324 assert( a > value_traits::zero() || !"hessenberg_matrix_transform(): invalid rotation, diagonal should be positive?");
00325 assert( fabs(b) < math::working_precision<value_type>() || !"hessenberg_matrix_transform(): invalid rotation, lower diagonal is nonzero?");
00326
00327
00328
00329
00330 if( fabs(a) < math::working_precision<value_type>())
00331 throw std::logic_error("A matrix is singular");
00332
00333 H( j ,j ) = a;
00334 H( j+1,j ) = b;
00335
00336
00337 set_rotation ( g[ j ], g[ j+1 ], c[ j ], s[ j ] );
00338 }
00339
00349 template<typename size_type, typename vector_type>
00350 inline bool is_orthonormal (
00351 size_type m
00352 , std::vector<vector_type> const & v
00353 )
00354 {
00355 using std::fabs;
00356
00357 typedef typename vector_type::value_type value_type;
00358 typedef OpenTissue::math::ValueTraits<value_type> value_traits;
00359
00360 assert( m>0 || !"is_orthonormal(): m was out of range");
00361 assert( m<=v.size() || !"is_orthonormal(): m was out of range");
00362
00363 value_type const precision = ::boost::numeric_cast<value_type>(10e-6);
00364
00365 for ( size_type i = 0; i < m; ++i )
00366 {
00367 value_type tmp = inner_prod( v[i], v[i] );
00368 if( fabs(tmp-value_traits::one()) > precision )
00369 return false;
00370 }
00371 for ( size_type i = 0; i < m; ++i )
00372 for ( size_type j = i+1; j < m; ++j )
00373 {
00374 value_type tmp = inner_prod( v[i], v[j] );
00375 if( fabs(tmp) > precision )
00376 return false;
00377
00378 }
00379 return true;
00380 }
00381
00391 template<typename size_type, typename matrix_type>
00392 inline bool is_upper_triangular (
00393 size_type m
00394 , matrix_type const & H
00395 )
00396 {
00397 typedef typename matrix_type::value_type value_type;
00398 typedef OpenTissue::math::ValueTraits<value_type> value_traits;
00399
00400 assert( m>0 || !"is_upper_triangular(): m was out of range");
00401 assert( H.size1() >= m || !"is_upper_triangular(): size of H was incompatible");
00402 assert( H.size2() >= m-1 || !"is_upper_triangular(): size of H was incompatible");
00403
00404 for ( size_type i = 0; i < m; ++i )
00405 for ( size_type j = i+1; j < m; ++j )
00406 {
00407 value_type h_ij = H(i,j);
00408 if(i>j && h_ij > value_traits::zero())
00409 return false;
00410 }
00411 return true;
00412 }
00413
00439 template<typename size_type, typename vector_type, typename matrix_type>
00440 inline void update (
00441 vector_type & x
00442 , size_type m
00443 , matrix_type & H
00444 , vector_type const & g
00445 , std::vector<vector_type> const & v
00446 )
00447 {
00448 assert( m>0 || !"update(): m was out of range");
00449 assert( m<=g.size() || !"update(): m was out of range");
00450 assert( is_orthonormal( m, v ) || !"update(): basis where not orthonormal?");
00451 assert( is_upper_triangular( m, H ) || !"update(): H where not upper triangular?");
00452
00453 vector_type y;
00454 OpenTissue::math::big::backsolve( m, H, y, g);
00455
00456 for ( size_type j = 0; j < m; ++j )
00457 x += y[j]*v[j];
00458 }
00459
00460 }
00461
00511 template<typename matrix_type, typename vector_type, typename preconditioner_type>
00512 inline void gmres(
00513 matrix_type const & A
00514 , vector_type & x
00515 , vector_type const & b
00516 , typename vector_type::size_type const & max_iterations
00517 , typename vector_type::size_type const & max_restart_iterations
00518 , typename vector_type::value_type const & tolerance
00519 , typename vector_type::value_type & relative_residual_error
00520 , typename vector_type::size_type & used_inner_iterations
00521 , typename vector_type::size_type & used_outer_iterations
00522 , typename vector_type::size_type & status
00523 , preconditioner_type const & P
00524 )
00525 {
00526 using std::fabs;
00527 using std::min;
00528 using std::ceil;
00529
00530 typedef typename matrix_type::value_type value_type;
00531 typedef typename vector_type::size_type size_type;
00532 typedef OpenTissue::math::ValueTraits<value_type> value_traits;
00533
00534 static size_type const ten = ::boost::numeric_cast<size_type>(10);
00535
00536 size_type const & N = b.size();
00537 size_type const & R = max_restart_iterations;
00538 size_type const & M = max_iterations;
00539 value_type const & eps = tolerance>0 ? tolerance : boost::numeric_cast<value_type>( 1e-6 );
00540 bool const restarted = R!=N && R>0;
00541 size_type maxit = M;
00542
00543 if(maxit==0)
00544 {
00545 if (restarted)
00546 {
00547 size_type const fraction = ::boost::numeric_cast<size_type>( ceil( value_traits::one()*N / R ) );
00548
00549 maxit = min( fraction, ten);
00550 }
00551 else
00552 {
00553 maxit = min( N, ten);
00554 }
00555 }
00556
00557 size_type const outer = restarted ? maxit : 1;
00558 size_type const inner = restarted ? R : maxit;
00559
00560 used_inner_iterations = 0;
00561 used_outer_iterations = 0;
00562 relative_residual_error = value_traits::infinity();
00563
00564 value_type norm_b = ublas::norm_2 ( b );
00565
00566
00567 if(norm_b < eps )
00568 {
00569
00570
00571
00572
00573 status = 0;
00574 x.clear();
00575 relative_residual_error = value_traits::zero();
00576 return;
00577 }
00578
00579 vector_type r( N );
00580
00581 value_type const rel_eps = eps*norm_b;
00582
00583
00584 residual(A, x, b, r);
00585 value_type norm_r = ublas::norm_2 ( r );
00586
00587
00588 if ( norm_r <= rel_eps )
00589 {
00590 status = 0;
00591 relative_residual_error = norm_r / norm_b;
00592 return;
00593 }
00594
00595 value_type norm_r_min = norm_r;
00596 vector_type x_min = x;
00597 size_type k_min = 0;
00598 size_type j_min = 0;
00599
00600 status = 1;
00601
00602
00603 std::vector<vector_type> v;
00604 v.resize(inner + 1);
00605
00606
00607 vector_type tmp( N );
00608 matrix_type H( inner+1, inner );
00609 vector_type g( inner+1 );
00610 vector_type c( inner+1 );
00611 vector_type s( inner+1 );
00612 vector_type w( N );
00613
00614
00615 for(size_type k=1; k<=outer; ++k )
00616 {
00617
00618 P( A, tmp, r );
00619 r = tmp;
00620
00621
00622
00623
00624 value_type beta = ublas::norm_2 ( r );
00625 v[0].resize ( N );
00626 v[0] = r / beta;
00627
00628 g.clear();
00629 g[0] = beta;
00630
00631 size_type used_inner = 0;
00632
00633 for (size_type j = 0; j < inner; ++j )
00634 {
00635 ++used_inner;
00636
00637
00638 prod( A, v[j], tmp);
00639 P( A, w, tmp);
00640
00641
00642
00643
00644
00645 for (size_type i = 0; i <= j; ++i )
00646 {
00647 value_type h_ij = ublas::inner_prod ( w, v[i] );
00648 w -= ( h_ij * v[i] );
00649 H( i, j ) = h_ij;
00650 }
00651 value_type h_jp1j = ublas::norm_2( w );
00652 v[j+1].resize(N);
00653 v[j+1] = w/h_jp1j;
00654 H(j+1,j) = h_jp1j;
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681 detail::hessenberg_matrix_transform( j, H, g, c, s);
00682
00683
00684
00685
00686
00687
00688
00689 norm_r = fabs( g[j+1] );
00690
00691 if(norm_r < norm_r_min)
00692 {
00693 norm_r_min = norm_r;
00694 k_min = k;
00695 j_min = used_inner;
00696
00697 }
00698 if( norm_r < rel_eps )
00699 {
00700 status = 0;
00701 used_inner_iterations = used_inner;
00702 used_outer_iterations = k ;
00703 break;
00704 }
00705 }
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717 detail::update ( x, used_inner, H, g, v );
00718
00719
00720 residual(A, x, b, r);
00721 norm_r = ublas::norm_2 ( r );
00722
00723 if(norm_r < norm_r_min)
00724 {
00725 norm_r_min = norm_r;
00726 k_min = k;
00727 j_min = used_inner;
00728 x_min = x;
00729 }
00730 if ( norm_r < rel_eps )
00731 {
00732 status = 0;
00733 used_inner_iterations = used_inner;
00734 used_outer_iterations = k;
00735 break;
00736 }
00737 }
00738 if ( status == 0 )
00739 {
00740
00741 relative_residual_error = norm_r / norm_b;
00742 }
00743 else
00744 {
00745
00746 x = x_min;
00747 used_inner_iterations = j_min;
00748 used_outer_iterations = k_min;
00749 relative_residual_error = norm_r_min / norm_b;
00750 }
00751 }
00752
00753 template<typename matrix_type, typename vector_type>
00754 inline void gmres(
00755 matrix_type const & A
00756 , vector_type & x
00757 , vector_type const & b
00758 , typename vector_type::size_type const & max_iterations
00759 , typename vector_type::size_type const & max_restart_iterations
00760 , typename vector_type::value_type const & tolerance
00761 , typename vector_type::value_type & relative_residual_error
00762 , typename vector_type::size_type & used_inner_iterations
00763 , typename vector_type::size_type & used_outer_iterations
00764 , typename vector_type::size_type & status
00765 )
00766 {
00767 IdentityPreconditioner preconditioner;
00768
00769 gmres( A,x,b,max_iterations,max_restart_iterations,tolerance, relative_residual_error, used_inner_iterations, used_outer_iterations, status, preconditioner);
00770 }
00771
00772 template<typename matrix_type, typename vector_type>
00773 inline void gmres(
00774 matrix_type const & A
00775 , vector_type & x
00776 , vector_type const & b
00777 , typename vector_type::size_type const & max_iterations
00778 , typename vector_type::size_type const & max_restart_iterations
00779 , typename vector_type::value_type const & tolerance
00780 )
00781 {
00782 typename vector_type::value_type relative_residual_error;
00783 typename vector_type::size_type used_inner_iterations;
00784 typename vector_type::size_type used_outer_iterations;
00785 typename vector_type::size_type status;
00786
00787 IdentityPreconditioner preconditioner;
00788
00789 gmres( A,x,b,max_iterations,max_restart_iterations,tolerance, relative_residual_error, used_inner_iterations, used_outer_iterations, status, preconditioner);
00790 }
00791
00792
00793 template<typename matrix_type, typename vector_type>
00794 inline void gmres(
00795 matrix_type const & A
00796 , vector_type & x
00797 , vector_type const & b
00798 )
00799 {
00800 typedef OpenTissue::math::ValueTraits< typename vector_type::value_type> value_traits;
00801
00802 typename vector_type::size_type max_iterations = 0;
00803 typename vector_type::size_type max_restart_iterations = 0;
00804 typename vector_type::value_type tolerance = value_traits::zero();
00805 typename vector_type::value_type relative_residual_error = value_traits::zero();
00806 typename vector_type::size_type used_inner_iterations = 0;
00807 typename vector_type::size_type used_outer_iterations = 0;
00808 typename vector_type::size_type status = 0;
00809
00810 IdentityPreconditioner preconditioner;
00811
00812 gmres( A,x,b,max_iterations,max_restart_iterations,tolerance, relative_residual_error, used_inner_iterations, used_outer_iterations, status, preconditioner);
00813 }
00814
00815
00824 class GMRESFunctor
00825 {
00826 public:
00827
00828 template<typename matrix_type, typename vector_type, typename preconditioner_type>
00829 void operator()(
00830 matrix_type const & A
00831 , vector_type & x
00832 , vector_type const & b
00833 , typename vector_type::size_type const & max_iterations
00834 , typename vector_type::size_type const & max_restart_iterations
00835 , typename vector_type::value_type const & tolerance
00836 , typename vector_type::value_type & relative_residual_error
00837 , typename vector_type::size_type & used_inner_iterations
00838 , typename vector_type::size_type & used_outer_iterations
00839 , typename vector_type::size_type & status
00840 , preconditioner_type const & P
00841 ) const
00842 {
00843 gmres(A,x,b,max_iterations,max_restart_iterations,tolerance,relative_residual_error,used_inner_iterations,used_outer_iterations,status,P);
00844 }
00845
00846 template<typename matrix_type, typename vector_type>
00847 void operator()(
00848 matrix_type const & A
00849 , vector_type & x
00850 , vector_type const & b
00851 , typename vector_type::size_type const & max_iterations
00852 , typename vector_type::size_type const & max_restart_iterations
00853 , typename vector_type::value_type const & tolerance
00854 , typename vector_type::value_type & relative_residual_error
00855 , typename vector_type::size_type & used_inner_iterations
00856 , typename vector_type::size_type & used_outer_iterations
00857 , typename vector_type::size_type & status
00858 ) const
00859 {
00860 gmres(A,x,b,max_iterations,max_restart_iterations,tolerance,relative_residual_error,used_inner_iterations,used_outer_iterations,status);
00861 }
00862
00863 template<typename matrix_type, typename vector_type>
00864 void operator()(
00865 matrix_type const & A
00866 , vector_type & x
00867 , vector_type const & b
00868 , typename vector_type::size_type const & max_iterations
00869 , typename vector_type::size_type const & max_restart_iterations
00870 , typename vector_type::value_type const & tolerance
00871 ) const
00872 {
00873 gmres(A,x,b,max_iterations,max_restart_iterations,tolerance);
00874 }
00875
00876 template<typename matrix_type, typename vector_type>
00877 void operator()(
00878 matrix_type const & A
00879 , vector_type & x
00880 , vector_type const & b
00881 ) const
00882 {
00883 gmres(A,x,b);
00884 }
00885 };
00886
00887 }
00888 }
00889 }
00890
00891
00892 #endif