1 /* *****************************************************************
2     MESQUITE -- The Mesh Quality Improvement Toolkit
3 
4     Copyright 2006 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     (2006) kraftche@cae.wisc.edu
24 
25   ***************************************************************** */
26 
27 
28 /** \file ObjectiveFunctionTests.hpp
29  *  \brief
30  *  \author Jason Kraftcheck
31  */
32 
33 #ifndef MSQ_OBJECTIVE_FUNCTION_TESTS_HPP
34 #define MSQ_OBJECTIVE_FUNCTION_TESTS_HPP
35 
36 #include "Mesquite.hpp"
37 #include "PatchDataInstances.hpp"
38 #include "IdealWeightInverseMeanRatio.hpp"
39 #include "ObjectiveFunctionTemplate.hpp"
40 #include "UnitUtil.hpp"
41 #include "MsqHessian.hpp"
42 
43 #include <memory>
44 
45 using namespace MBMesquite;
46 using namespace std;
47 
48 class ObjectiveFunctionTests {
49 
50 public:
51 
52 /** Which function to call */
53 enum OFTestMode { EVAL, GRAD, DIAG, HESS };
54 
55 /** Test eval type support for OF templates that provide
56  *  block coordinate descent functionality.
57  *
58  *  Note: If OF does not support BCD, this test will fail,
59  *        even for the ObjectiveFunction::CALCULATE eval type.
60  */
61 static void test_eval_type( ObjectiveFunction::EvalType,
62                             OFTestMode test_mode,
63                             ObjectiveFunctionTemplate* of );
64 
65 /** Verify that if QualityMetric returns invalid but not error
66  *  from evaluate, that the ObjectiveFunction does the same */
67 static void test_handles_invalid_qm( OFTestMode test_mode, ObjectiveFunctionTemplate* of );
68 
69 /** Verify that OF correctly handles error in QM::evaluate() */
70 static void test_handles_qm_error( OFTestMode test_mode, ObjectiveFunctionTemplate* of );
71 
72 /** Test the behavior of the clone() method */
73 static void test_clone( ObjectiveFunctionTemplate* of );
74 
75 /** Test correct handling of QM negate flag */
76 static void test_negate_flag( OFTestMode test_mode, ObjectiveFunctionTemplate* of );
77 
78 /** Test OF value */
79 static void test_value( const double* input_values,
80                         unsigned num_input_values,
81                         double expected_value,
82                         OFTestMode test_mode,
83                         ObjectiveFunctionTemplate* of );
84 
85 /** Compare numerical and analytical gradient values */
86 static inline void compare_numerical_gradient( ObjectiveFunctionTemplate* of );
87 static void compare_numerical_gradient( ObjectiveFunction* of );
88 
89 /** Compare gradients from evaluate_with_gradient with gradient
90  *  from evaluate_with_Hessian
91  */
92 static inline void compare_hessian_gradient( ObjectiveFunctionTemplate* of );
93 static void compare_hessian_gradient( ObjectiveFunction* of );
94 
95 /** Compare gradients from evaluate_with_gradient with gradient
96  *  from evaluate_with_Hessian_diagonal
97  */
98 static inline void compare_diagonal_gradient( ObjectiveFunctionTemplate* of );
99 static void compare_diagonal_gradient( ObjectiveFunction* of );
100 
101 /** Compare gradient and diagonal terms from evaluate_with_Hessian
102  *  and evaluate_with_Hessian_diagonal
103  */
104 static inline void compare_hessian_diagonal( ObjectiveFunctionTemplate* of );
105 static void compare_hessian_diagonal( ObjectiveFunction* of );
106 
107 static void compare_numerical_hessian( ObjectiveFunctionTemplate* of );
108 static void compare_numerical_hessian_diagonal( ObjectiveFunctionTemplate* of );
109 static void compare_numerical_hessian( ObjectiveFunction* of, bool diagonal_only );
110 
111 static PatchData& patch();
112 
113 private:
114 
115 static double evaluate_internal( ObjectiveFunction::EvalType type,
116                                  OFTestMode test_mode,
117                                  ObjectiveFunction* of );
118 };
119 
120 /** The QualityMetric to use for testing purposes
121  *
122  *  Just pass a specified list of values to the OF
123  */
124 class OFTestQM : public QualityMetric {
125   public:
126 
OFTestQM()127     OFTestQM( ) : negateFlag(1) {}
128 
OFTestQM(const double * values,unsigned num_values)129     OFTestQM( const double* values, unsigned num_values )
130       : mValues(num_values), negateFlag(1)
131       { copy( values, values+num_values, mValues.begin() ); }
132 
set_values(const double * values,unsigned num_values)133     void set_values( const double* values, unsigned num_values )
134       {
135         mValues.resize(num_values);
136         copy( values, values+num_values, mValues.begin() );
137       }
138 
append_values(const double * values,unsigned num_values)139     void append_values( const double* values, unsigned num_values )
140       { copy( values, values+num_values, back_inserter(mValues) ); }
141 
get_metric_type() const142     virtual MetricType get_metric_type() const  { return ELEMENT_BASED; }
143 
get_name() const144     virtual string get_name() const { return "ObjectiveFunctionTests"; }
145 
get_negate_flag() const146     virtual int get_negate_flag() const { return negateFlag; }
147 
set_negate_flag(int value)148     void set_negate_flag( int value ) { negateFlag = value; }
149 
get_evaluations(PatchData &,vector<size_t> & h,bool,MsqError &)150     virtual void get_evaluations( PatchData&, vector<size_t>& h, bool, MsqError& )
151       {
152         h.resize( mValues.size() );
153         for (unsigned i = 0; i < mValues.size(); ++i)
154           h[i] = i;
155       }
156 
evaluate(PatchData &,size_t h,double & v,MsqError & err)157     virtual bool evaluate( PatchData&, size_t h, double& v, MsqError& err )
158       {
159         if (h >= mValues.size()) {
160           MSQ_SETERR(err)("handle out of range", MsqError::INVALID_ARG );
161           return false;
162         }
163         v = mValues[h];
164         return true;
165       }
166 
evaluate_with_indices(PatchData & pd,size_t h,double & v,vector<size_t> & i,MsqError & err)167     virtual bool evaluate_with_indices( PatchData& pd, size_t h, double& v, vector<size_t>& i, MsqError& err )
168       {
169         i.clear();
170         for (unsigned j = 0; j < pd.num_free_vertices(); ++j)
171           i.push_back(j);
172         return evaluate( pd, h, v, err );
173       }
174 
evaluate_with_gradient(PatchData & pd,size_t h,double & v,vector<size_t> & i,vector<Vector3D> & g,MsqError & err)175     virtual bool evaluate_with_gradient( PatchData& pd, size_t h, double& v, vector<size_t>& i, vector<Vector3D>& g, MsqError& err )
176       {
177         g.clear();
178         bool rval = evaluate_with_indices( pd, h, v, i, err );
179         // grad values are just used to test negate flag, so just
180         // pass back an arbitrary value for each free vertex
181         for (unsigned j = 0; j < i.size(); ++j)
182           g.push_back( Vector3D(1,0,2) );
183         return rval;
184       }
185 
evaluate_with_Hessian(PatchData & pd,size_t h,double & v,vector<size_t> & i,vector<Vector3D> & g,vector<Matrix3D> & H,MsqError & err)186     virtual bool evaluate_with_Hessian( PatchData& pd, size_t h, double& v, vector<size_t>& i, vector<Vector3D>& g, vector<Matrix3D>& H, MsqError& err )
187       {
188         H.clear();
189         bool rval = evaluate_with_gradient( pd, h, v, i, g, err );
190         // Hessian values are just used to test negate flag, so
191         // pass back arbirary values.
192         for (unsigned r = 0; r < i.size(); ++r)
193           for (unsigned c = r; c < i.size(); ++c)
194             H.push_back( Matrix3D(1.0) );
195         return rval;
196       }
197 
198   private:
199 
200     vector<double> mValues;
201     int negateFlag;
202 };
203 
compare_numerical_gradient(ObjectiveFunctionTemplate * of)204 inline void ObjectiveFunctionTests::compare_numerical_gradient( ObjectiveFunctionTemplate* of )
205 {
206   MsqPrintError err(std::cout);
207   IdealWeightInverseMeanRatio metric(err);
208   ASSERT_NO_ERROR( err );
209   of->set_quality_metric( &metric );
210   compare_numerical_gradient( (ObjectiveFunction*)of );
211 }
212 
compare_hessian_gradient(ObjectiveFunctionTemplate * of)213 inline void ObjectiveFunctionTests::compare_hessian_gradient( ObjectiveFunctionTemplate* of )
214 {
215   MsqPrintError err(std::cout);
216   IdealWeightInverseMeanRatio metric(err);
217   ASSERT_NO_ERROR( err );
218   of->set_quality_metric( &metric );
219   compare_hessian_gradient( (ObjectiveFunction*)of );
220 }
221 
compare_diagonal_gradient(ObjectiveFunctionTemplate * of)222 inline void ObjectiveFunctionTests::compare_diagonal_gradient( ObjectiveFunctionTemplate* of )
223 {
224   MsqPrintError err(std::cout);
225   IdealWeightInverseMeanRatio metric(err);
226   ASSERT_NO_ERROR( err );
227   of->set_quality_metric( &metric );
228   compare_diagonal_gradient( (ObjectiveFunction*)of );
229 }
230 
compare_hessian_diagonal(ObjectiveFunctionTemplate * of)231 inline void ObjectiveFunctionTests::compare_hessian_diagonal( ObjectiveFunctionTemplate* of )
232 {
233   MsqPrintError err(std::cout);
234   IdealWeightInverseMeanRatio metric(err);
235   ASSERT_NO_ERROR( err );
236   of->set_quality_metric( &metric );
237   compare_hessian_diagonal( (ObjectiveFunction*)of );
238 }
239 
compare_numerical_hessian(ObjectiveFunctionTemplate * of)240 inline void ObjectiveFunctionTests::compare_numerical_hessian( ObjectiveFunctionTemplate* of )
241 {
242   MsqPrintError err(std::cout);
243   IdealWeightInverseMeanRatio metric(err);
244   ASSERT_NO_ERROR( err );
245   of->set_quality_metric( &metric );
246   compare_numerical_hessian( (ObjectiveFunction*)of, false );
247 }
248 
compare_numerical_hessian_diagonal(ObjectiveFunctionTemplate * of)249 inline void ObjectiveFunctionTests::compare_numerical_hessian_diagonal( ObjectiveFunctionTemplate* of )
250 {
251   MsqPrintError err(std::cout);
252   IdealWeightInverseMeanRatio metric(err);
253   ASSERT_NO_ERROR( err );
254   of->set_quality_metric( &metric );
255   compare_numerical_hessian( (ObjectiveFunction*)of, true );
256 }
257 
258 #endif
259