LCOV - code coverage report
Current view: top level - src/mesquite/QualityImprover/OptSolvers - QuasiNewton.cpp (source / functions) Hit Total Coverage
Test: coverage_sk.info Lines: 6 143 4.2 %
Date: 2020-07-18 00:09:26 Functions: 4 15 26.7 %
Branches: 20 500 4.0 %

           Branch data     Line data    Source code
       1                 :            : /* *****************************************************************
       2                 :            :     MESQUITE -- The Mesh Quality Improvement Toolkit
       3                 :            : 
       4                 :            :     Copyright 2007 Sandia National Laboratories.  Developed at the
       5                 :            :     University of Wisconsin--Madison under SNL contract number
       6                 :            :     624796.  The U.S. Government and the University of Wisconsin
       7                 :            :     retain certain rights to this software.
       8                 :            : 
       9                 :            :     This library is free software; you can redistribute it and/or
      10                 :            :     modify it under the terms of the GNU Lesser General Public
      11                 :            :     License as published by the Free Software Foundation; either
      12                 :            :     version 2.1 of the License, or (at your option) any later version.
      13                 :            : 
      14                 :            :     This library is distributed in the hope that it will be useful,
      15                 :            :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      16                 :            :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17                 :            :     Lesser General Public License for more details.
      18                 :            : 
      19                 :            :     You should have received a copy of the GNU Lesser General Public License
      20                 :            :     (lgpl.txt) along with this library; if not, write to the Free Software
      21                 :            :     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      22                 :            : 
      23                 :            :     (2008) [email protected]
      24                 :            : 
      25                 :            :   ***************************************************************** */
      26                 :            : 
      27                 :            : /** \file QuasiNewton.cpp
      28                 :            :  *  \brief Port Todd Munson's quasi-Newton solver to Mesquite
      29                 :            :  *  \author Jason Kraftcheck (Mesquite Port)
      30                 :            :  */
      31                 :            : 
      32                 :            : #include "Mesquite.hpp"
      33                 :            : #include "QuasiNewton.hpp"
      34                 :            : #include "MsqDebug.hpp"
      35                 :            : #include "MsqError.hpp"
      36                 :            : #include "PatchData.hpp"
      37                 :            : 
      38                 :            : namespace MBMesquite
      39                 :            : {
      40                 :            : 
      41                 :            : // Force std::vector to release allocated memory
      42                 :            : template < typename T >
      43                 :          0 : static inline void free_vector( std::vector< T >& v )
      44                 :            : {
      45 [ #  # ][ #  # ]:          0 :     std::vector< T > temp;
      46                 :          0 :     temp.swap( v );
      47                 :          0 : }
      48                 :            : 
      49                 :          0 : std::string QuasiNewton::get_name() const
      50                 :            : {
      51         [ #  # ]:          0 :     return "QuasiNewton";
      52                 :            : }
      53                 :            : 
      54                 :          0 : PatchSet* QuasiNewton::get_patch_set()
      55                 :            : {
      56                 :          0 :     return PatchSetUser::get_patch_set();
      57                 :            : }
      58                 :            : 
      59 [ +  - ][ +  - ]:         13 : QuasiNewton::QuasiNewton( ObjectiveFunction* of ) : VertexMover( of ), PatchSetUser( true ), mMemento( 0 ) {}
         [ +  - ][ +  + ]
         [ +  - ][ +  + ]
         [ +  - ][ +  - ]
           [ #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
      60                 :            : 
      61 [ +  - ][ +  + ]:         14 : QuasiNewton::~QuasiNewton()
         [ +  - ][ +  + ]
      62                 :            : {
      63         [ -  + ]:          1 :     delete mMemento;
      64                 :          1 :     mMemento = 0;
      65         [ -  + ]:          1 : }
      66                 :            : 
      67                 :          0 : void QuasiNewton::initialize( PatchData& pd, MsqError& err )
      68                 :            : {
      69         [ #  # ]:          0 :     if( !mMemento )
      70                 :            :     {
      71 [ #  # ][ #  # ]:          0 :         mMemento = pd.create_vertices_memento( err );MSQ_CHKERR( err );
      72                 :            :     }
      73                 :          0 : }
      74                 :            : 
      75                 :          0 : void QuasiNewton::initialize_mesh_iteration( PatchData& /*pd*/, MsqError& /*err*/ ) {}
      76                 :            : 
      77                 :          0 : void QuasiNewton::terminate_mesh_iteration( PatchData& /*pd*/, MsqError& /*err*/ ) {}
      78                 :            : 
      79                 :          0 : void QuasiNewton::cleanup()
      80                 :            : {
      81                 :            :     // release memento
      82         [ #  # ]:          0 :     delete mMemento;
      83                 :          0 :     mMemento = 0;
      84                 :            : 
      85                 :            :     // release coordinates
      86         [ #  # ]:          0 :     for( size_t i = 0; i < ( sizeof( w ) / sizeof( w[0] ) ); ++i )
      87                 :          0 :         free_vector( w[i] );
      88                 :            :     // release gradients
      89         [ #  # ]:          0 :     for( size_t i = 0; i < ( sizeof( v ) / sizeof( v[0] ) ); ++i )
      90                 :          0 :         free_vector( v[i] );
      91                 :            : 
      92                 :            :     // release Hessian memory
      93                 :          0 :     free_vector( mHess );
      94                 :            : 
      95                 :            :     // release temporary array memory
      96                 :          0 :     free_vector( x );
      97                 :          0 :     free_vector( d );
      98                 :          0 : }
      99                 :            : 
     100                 :            : // Do v += s * x, where v and x are arrays of length n
     101                 :          0 : static inline void plus_eq_scaled( Vector3D* v, double s, const Vector3D* x, size_t n )
     102                 :            : {
     103                 :          0 :     Vector3D* end = v + n;
     104         [ #  # ]:          0 :     for( ; v != end; ++v, ++x )
     105         [ #  # ]:          0 :         *v += s * *x;
     106                 :          0 : }
     107                 :            : 
     108                 :          0 : void QuasiNewton::solve( Vector3D* z_arr, const Vector3D* v_arr ) const
     109                 :            : {
     110         [ #  # ]:          0 :     SymMatrix3D pd;
     111                 :            : 
     112                 :          0 :     const double small = DBL_EPSILON;
     113                 :          0 :     const size_t nn    = mHess.size();
     114         [ #  # ]:          0 :     for( size_t i = 0; i < nn; ++i )
     115                 :            :     {
     116                 :            : 
     117                 :            :         // ensure positive definite: perturb a bit if
     118                 :            :         // diagonal values are zero.
     119         [ #  # ]:          0 :         SymMatrix3D dd = mHess[i];
     120 [ #  # ][ #  # ]:          0 :         while( fabs( dd[0] ) < small || fabs( dd[3] ) < small || fabs( dd[5] ) < small )
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     121 [ #  # ][ #  # ]:          0 :             dd += small;
     122                 :            : 
     123                 :            :         // factor
     124 [ #  # ][ #  # ]:          0 :         pd[0] = 1.0 / dd[0];
     125 [ #  # ][ #  # ]:          0 :         pd[1] = dd[1] * pd[0];
                 [ #  # ]
     126 [ #  # ][ #  # ]:          0 :         pd[2] = dd[2] * pd[0];
                 [ #  # ]
     127                 :            : 
     128 [ #  # ][ #  # ]:          0 :         pd[3] = 1.0 / ( dd[3] - dd[1] * pd[1] );
         [ #  # ][ #  # ]
     129 [ #  # ][ #  # ]:          0 :         pd[5] = dd[4] - dd[2] * pd[1];
         [ #  # ][ #  # ]
     130 [ #  # ][ #  # ]:          0 :         pd[4] = pd[3] * pd[5];
                 [ #  # ]
     131 [ #  # ][ #  # ]:          0 :         pd[5] = 1.0 / ( dd[5] - dd[2] * pd[2] - pd[4] * pd[5] );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     132                 :            : 
     133 [ #  # ][ #  # ]:          0 :         if( pd[0] <= 0.0 || pd[3] <= 0.0 || pd[5] <= 0.0 )
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     134                 :            :         {
     135 [ #  # ][ #  # ]:          0 :             if( dd[0] + dd[3] + dd[5] <= 0 )
         [ #  # ][ #  # ]
     136                 :            :             {
     137                 :            :                 // switch to diagonal
     138 [ #  # ][ #  # ]:          0 :                 pd[0] = 1.0 / fabs( dd[0] );
     139         [ #  # ]:          0 :                 pd[1] = 0.0;
     140         [ #  # ]:          0 :                 pd[2] = 0.0;
     141 [ #  # ][ #  # ]:          0 :                 pd[3] = 1.0 / fabs( dd[3] );
     142         [ #  # ]:          0 :                 pd[4] = 0.0;
     143 [ #  # ][ #  # ]:          0 :                 pd[5] = 1.0 / fabs( dd[5] );
     144                 :            :             }
     145                 :            :             else
     146                 :            :             {
     147                 :            :                 // diagonal preconditioner
     148 [ #  # ][ #  # ]:          0 :                 pd[0] = pd[3] = pd[5] = 1.0 / ( dd[0] + dd[3] + dd[5] );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     149 [ #  # ][ #  # ]:          0 :                 pd[1] = pd[2] = pd[4] = 0.0;
                 [ #  # ]
     150                 :            :             }
     151                 :            :         }
     152                 :            : 
     153                 :            :         // solve
     154                 :          0 :         const Vector3D& vv = v_arr[i];
     155                 :          0 :         Vector3D& z        = z_arr[i];
     156 [ #  # ][ #  # ]:          0 :         z[0]               = vv[0];
     157 [ #  # ][ #  # ]:          0 :         z[1]               = vv[1] - pd[1] * z[0];
         [ #  # ][ #  # ]
     158 [ #  # ][ #  # ]:          0 :         z[2]               = vv[2] - pd[2] * z[0] - pd[4] * z[1];
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     159                 :            : 
     160 [ #  # ][ #  # ]:          0 :         z[0] *= pd[0];
     161 [ #  # ][ #  # ]:          0 :         z[1] *= pd[3];
     162 [ #  # ][ #  # ]:          0 :         z[2] *= pd[5];
     163                 :            : 
     164 [ #  # ][ #  # ]:          0 :         z[1] -= pd[4] * z[2];
                 [ #  # ]
     165 [ #  # ][ #  # ]:          0 :         z[0] -= pd[1] * z[1] + pd[2] * z[2];
         [ #  # ][ #  # ]
                 [ #  # ]
     166                 :            :     }
     167                 :          0 : }
     168                 :            : 
     169                 :          0 : void QuasiNewton::optimize_vertex_positions( PatchData& pd, MsqError& err )
     170                 :            : {
     171         [ #  # ]:          0 :     TerminationCriterion& term = *get_inner_termination_criterion();
     172         [ #  # ]:          0 :     OFEvaluator& func          = get_objective_function_evaluator();
     173                 :            : 
     174                 :          0 :     const double sigma   = 1e-4;
     175                 :          0 :     const double beta0   = 0.25;
     176                 :          0 :     const double beta1   = 0.80;
     177                 :          0 :     const double tol1    = 1e-8;
     178                 :          0 :     const double epsilon = 1e-10;
     179                 :            : 
     180                 :            :     // double norm_r; //, norm_g;
     181                 :            :     double alpha, beta;
     182                 :            :     double obj, objn;
     183                 :            : 
     184                 :            :     size_t i;
     185                 :            : 
     186                 :            :     // Initialize stuff
     187         [ #  # ]:          0 :     const size_t nn = pd.num_free_vertices();
     188                 :            :     double a[QNVEC], b[QNVEC], r[QNVEC];
     189         [ #  # ]:          0 :     for( i = 0; i < QNVEC; ++i )
     190                 :          0 :         r[i] = 0;
     191         [ #  # ]:          0 :     for( i = 0; i <= QNVEC; ++i )
     192                 :            :     {
     193                 :          0 :         v[i].clear();
     194 [ #  # ][ #  # ]:          0 :         v[i].resize( nn, Vector3D( 0.0 ) );
     195                 :          0 :         w[i].clear();
     196 [ #  # ][ #  # ]:          0 :         w[i].resize( nn, Vector3D( 0.0 ) );
     197                 :            :     }
     198         [ #  # ]:          0 :     d.resize( nn );
     199         [ #  # ]:          0 :     mHess.resize( nn );  // hMesh(mesh);
     200                 :            : 
     201 [ #  # ][ #  # ]:          0 :     bool valid = func.update( pd, obj, v[QNVEC], mHess, err );MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     202         [ #  # ]:          0 :     if( !valid )
     203                 :            :     {
     204 [ #  # ][ #  # ]:          0 :         MSQ_SETERR( err )( "Initial objective function is not valid", MsqError::INVALID_MESH );
     205                 :          0 :         return;
     206                 :            :     }
     207                 :            : 
     208 [ #  # ][ #  # ]:          0 :     while( !term.terminate() )
     209                 :            :     {
     210 [ #  # ][ #  # ]:          0 :         pd.recreate_vertices_memento( mMemento, err );MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     211         [ #  # ]:          0 :         pd.get_free_vertex_coordinates( w[QNVEC] );
     212                 :            : 
     213         [ #  # ]:          0 :         x = v[QNVEC];
     214         [ #  # ]:          0 :         for( i = QNVEC; i--; )
     215                 :            :         {
     216 [ #  # ][ #  # ]:          0 :             a[i] = r[i] * inner( &( w[i][0] ), arrptr( x ), nn );
                 [ #  # ]
     217 [ #  # ][ #  # ]:          0 :             plus_eq_scaled( arrptr( x ), -a[i], &v[i][0], nn );
                 [ #  # ]
     218                 :            :         }
     219                 :            : 
     220 [ #  # ][ #  # ]:          0 :         solve( arrptr( d ), arrptr( x ) );
                 [ #  # ]
     221                 :            : 
     222         [ #  # ]:          0 :         for( i = QNVEC; i--; )
     223                 :            :         {
     224 [ #  # ][ #  # ]:          0 :             b[i] = r[i] * inner( &( v[i][0] ), arrptr( d ), nn );
                 [ #  # ]
     225 [ #  # ][ #  # ]:          0 :             plus_eq_scaled( arrptr( d ), a[i] - b[i], &( w[i][0] ), nn );
                 [ #  # ]
     226                 :            :         }
     227                 :            : 
     228 [ #  # ][ #  # ]:          0 :         alpha = -inner( &( v[QNVEC][0] ), arrptr( d ), nn ); /* direction is negated */
                 [ #  # ]
     229         [ #  # ]:          0 :         if( alpha > 0.0 )
     230                 :            :         {
     231 [ #  # ][ #  # ]:          0 :             MSQ_SETERR( err )( "No descent.", MsqError::INVALID_MESH );
     232                 :          0 :             return;
     233                 :            :         }
     234                 :            : 
     235                 :          0 :         alpha *= sigma;
     236                 :          0 :         beta = 1.0;
     237                 :            : 
     238 [ #  # ][ #  # ]:          0 :         pd.move_free_vertices_constrained( arrptr( d ), nn, -beta, err );MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     239         [ #  # ]:          0 :         valid = func.evaluate( pd, objn, v[QNVEC], err );
     240 [ #  # ][ #  # ]:          0 :         if( err.error_code() == err.BARRIER_VIOLATED )
     241         [ #  # ]:          0 :             err.clear();  // barrier violated does not represent an actual error here
     242 [ #  # ][ #  # ]:          0 :         MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
                 [ #  # ]
     243 [ #  # ][ #  # ]:          0 :         if( !valid || ( obj - objn < -alpha * beta - epsilon && length( &( v[QNVEC][0] ), nn ) >= tol1 ) )
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     244                 :            :         {
     245                 :            : 
     246         [ #  # ]:          0 :             if( !valid )  // function not defined at trial point
     247                 :          0 :                 beta *= beta0;
     248                 :            :             else  // unacceptable iterate
     249                 :          0 :                 beta *= beta1;
     250                 :            : 
     251                 :            :             for( ;; )
     252                 :            :             {
     253         [ #  # ]:          0 :                 if( beta < tol1 )
     254                 :            :                 {
     255 [ #  # ][ #  # ]:          0 :                     pd.set_to_vertices_memento( mMemento, err );MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     256 [ #  # ][ #  # ]:          0 :                     MSQ_SETERR( err )( "Newton step not good", MsqError::INTERNAL_ERROR );
     257                 :          0 :                     return;
     258                 :            :                 }
     259                 :            : 
     260 [ #  # ][ #  # ]:          0 :                 pd.set_free_vertices_constrained( mMemento, arrptr( d ), nn, -beta, err );MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     261         [ #  # ]:          0 :                 valid = func.evaluate( pd, objn, err );
     262 [ #  # ][ #  # ]:          0 :                 if( err.error_code() == err.BARRIER_VIOLATED )
     263         [ #  # ]:          0 :                     err.clear();  // barrier violated does not represent an actual error here
     264 [ #  # ][ #  # ]:          0 :                 MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
                 [ #  # ]
     265         [ #  # ]:          0 :                 if( !valid )  // function undefined at trial point
     266                 :          0 :                     beta *= beta0;
     267         [ #  # ]:          0 :                 else if( obj - objn < -alpha * beta - epsilon )  // unacceptlable iterate
     268                 :          0 :                     beta *= beta1;
     269                 :            :                 else
     270                 :          0 :                     break;
     271                 :            :             }
     272                 :            :         }
     273                 :            : 
     274         [ #  # ]:          0 :         for( i = 0; i < QNVEC - 1; ++i )
     275                 :            :         {
     276                 :          0 :             r[i] = r[i + 1];
     277                 :          0 :             w[i].swap( w[i + 1] );
     278                 :          0 :             v[i].swap( v[i + 1] );
     279                 :            :         }
     280                 :          0 :         w[QNVEC - 1].swap( w[0] );
     281                 :          0 :         v[QNVEC - 1].swap( v[0] );
     282                 :            : 
     283 [ #  # ][ #  # ]:          0 :         func.update( pd, obj, v[QNVEC], mHess, err );MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     284                 :            :         // norm_r = length_squared( &(v[QNVEC][0]), nn );
     285                 :            :         // norm_g = sqrt(norm_r);
     286                 :            : 
     287                 :            :         // checks stopping criterion
     288 [ #  # ][ #  # ]:          0 :         term.accumulate_patch( pd, err );MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     289 [ #  # ][ #  # ]:          0 :         term.accumulate_inner( pd, objn, &v[QNVEC][0], err );MSQ_ERRRTN( err );
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     290                 :            :     }
     291                 :            : }
     292                 :            : 
     293 [ +  - ][ +  - ]:          4 : }  // namespace MBMesquite

Generated by: LCOV version 1.11