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