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 CompositeOFTest.cpp
29 * \brief Unit tests for Composite Objective Functions
30 * \author Jason Kraftcheck
31 */
32
33
34
35 #include "Mesquite.hpp"
36 #include "CompositeOFAdd.hpp"
37 #include "CompositeOFMultiply.hpp"
38 #include "CompositeOFScalarAdd.hpp"
39 #include "CompositeOFScalarMultiply.hpp"
40 #include "MsqHessian.hpp"
41 #include "IdealWeightInverseMeanRatio.hpp"
42 #include "LPtoPTemplate.hpp"
43
44 #include "ObjectiveFunctionTests.hpp"
45 #include "PatchDataInstances.hpp"
46 #include "cppunit/extensions/HelperMacros.h"
47 #include "UnitUtil.hpp"
48 #include "Matrix3D.hpp"
49 #include <iterator>
50
51 using namespace MBMesquite;
52 using std::cout;
53 using std::endl;
54 using std::cerr;
55
56 /** Fake ObjectiveFunction to pass to Composite OFs */
57 class FauxObjectiveFunction : public ObjectiveFunction
58 {
59 public:
FauxObjectiveFunction(double value,bool invalid=false,bool error=false)60 FauxObjectiveFunction( double value, bool invalid = false, bool error = false)
61 : mValue(value), mInvalid(invalid), mError(error)
62 { ++instanceCount; }
~FauxObjectiveFunction()63 ~FauxObjectiveFunction() { --instanceCount; }
initialize_block_coordinate_descent(MeshDomainAssoc *,const Settings *,PatchSet *,MsqError &)64 bool initialize_block_coordinate_descent( MeshDomainAssoc*, const Settings*, PatchSet*, MsqError& )
65 { CPPUNIT_ASSERT_MESSAGE("This shouldn't ever get called", false ); return false; }
evaluate(EvalType,PatchData &,double & value_out,bool,MsqError & err)66 bool evaluate( EvalType, PatchData&, double& value_out, bool, MsqError& err )
67 {
68 if (mError)
69 MSQ_SETERR(err)(MsqError::INTERNAL_ERROR);
70 value_out = mValue;
71 return !mInvalid;
72 }
clone() const73 ObjectiveFunction* clone() const
74 {
75 ++instanceCount;
76 return new FauxObjectiveFunction(*this);
77 }
clear()78 void clear() {}
min_patch_layers() const79 int min_patch_layers() const { return 0; }
80
initialize_queue(MeshDomainAssoc *,const Settings *,MsqError &)81 void initialize_queue( MeshDomainAssoc* ,
82 const Settings* ,
83 MsqError& ) {}
84
get_value() const85 double get_value() const { return mValue; }
get_instance_count()86 static int get_instance_count() { return instanceCount; }
87 private:
88 double mValue;
89 bool mInvalid, mError;
90 static int instanceCount;
91 };
92 int FauxObjectiveFunction::instanceCount = 0;
93
94 class CompositeOFTest : public CppUnit::TestFixture, public ObjectiveFunctionTests
95 {
96 private:
97 CPPUNIT_TEST_SUITE(CompositeOFTest);
98
99 CPPUNIT_TEST (test_add_value);
100 CPPUNIT_TEST (test_multiply_value);
101 CPPUNIT_TEST (test_scalar_add_value);
102 CPPUNIT_TEST (test_scalar_multiply_value);
103
104 CPPUNIT_TEST (test_add_gradient);
105 CPPUNIT_TEST (test_multiply_gradient);
106 CPPUNIT_TEST (test_scalar_add_gradient);
107 CPPUNIT_TEST (test_scalar_multiply_gradient);
108
109 CPPUNIT_TEST (test_add_hess_diagonal);
110 CPPUNIT_TEST (test_multiply_hess_diagonal);
111 CPPUNIT_TEST (test_scalar_add_hess_diagonal);
112 CPPUNIT_TEST (test_scalar_multiply_hess_diagonal);
113
114 CPPUNIT_TEST (test_add_hessian);
115 CPPUNIT_TEST (test_multiply_hessian);
116 CPPUNIT_TEST (test_scalar_add_hessian);
117 CPPUNIT_TEST (test_scalar_multiply_hessian);
118
119 CPPUNIT_TEST (test_clone_add);
120 CPPUNIT_TEST (test_clone_multiply);
121 CPPUNIT_TEST (test_clone_scalar_add);
122 CPPUNIT_TEST (test_clone_scalar_multiply);
123
124 CPPUNIT_TEST (test_add_invalid);
125 CPPUNIT_TEST (test_multiply_invalid);
126 CPPUNIT_TEST (test_scalar_add_invalid);
127 CPPUNIT_TEST (test_scalar_multiply_invalid);
128
129 CPPUNIT_TEST (test_add_error);
130 CPPUNIT_TEST (test_multiply_error);
131 CPPUNIT_TEST (test_scalar_add_error);
132 CPPUNIT_TEST (test_scalar_multiply_error);
133
134 CPPUNIT_TEST_SUITE_END();
135
136 FauxObjectiveFunction OF1, OF2, OF3, OF4, invalidOF, errorOF;
137 IdealWeightInverseMeanRatio metric;
138 LPtoPTemplate LP1, LP2;
139
140 public:
141
CompositeOFTest()142 CompositeOFTest()
143 : OF1(1.0), OF2(3.0), OF3(-7.0), OF4(M_PI),
144 invalidOF(1.0,true,false), errorOF(1.0,false,true),
145 LP1( 1, &metric ), LP2( 2, &metric )
146 {}
147
148 void test_add_value();
149 void test_multiply_value();
150 void test_scalar_add_value();
151 void test_scalar_multiply_value();
152
153 void test_add_gradient();
154 void test_multiply_gradient();
155 void test_scalar_add_gradient();
156 void test_scalar_multiply_gradient();
157
158 void test_add_hess_diagonal();
159 void test_multiply_hess_diagonal();
160 void test_scalar_add_hess_diagonal();
161 void test_scalar_multiply_hess_diagonal();
162
163 void test_add_hessian();
164 void test_multiply_hessian();
165 void test_scalar_add_hessian();
166 void test_scalar_multiply_hessian();
167
168 void test_clone_add();
169 void test_clone_multiply();
170 void test_clone_scalar_add();
171 void test_clone_scalar_multiply();
172
173 void test_add_invalid();
174 void test_multiply_invalid();
175 void test_scalar_add_invalid();
176 void test_scalar_multiply_invalid();
177
178 void test_add_error();
179 void test_multiply_error();
180 void test_scalar_add_error();
181 void test_scalar_multiply_error();
182
183 void test_evaluate( double expected_value, ObjectiveFunction& of );
184 void get_hessians( MsqHessian& LP1_hess, MsqHessian& LP2_hess,
185 ObjectiveFunction& OF, MsqHessian& OF_hess );
186 void test_composite_clone( ObjectiveFunction& of );
187 void test_invalid_eval( ObjectiveFunction& of );
188 void test_eval_fails( ObjectiveFunction& of );
189 };
190
191 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CompositeOFTest, "CompositeOFTest");
192 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CompositeOFTest, "Unit");
193
test_evaluate(double expected,ObjectiveFunction & OF)194 void CompositeOFTest::test_evaluate( double expected, ObjectiveFunction& OF )
195 {
196 MsqPrintError err(cout);
197 double value;
198 bool rval = OF.evaluate( ObjectiveFunction::CALCULATE, patch(), value, false, err );
199 ASSERT_NO_ERROR(err);
200 CPPUNIT_ASSERT(rval);
201 CPPUNIT_ASSERT_DOUBLES_EQUAL( expected, value, 1e-6 );
202 }
203
test_add_value()204 void CompositeOFTest::test_add_value()
205 {
206 CompositeOFAdd add1( &OF1, &OF2 );
207 test_evaluate( OF1.get_value() + OF2.get_value(), add1 );
208
209 CompositeOFAdd add2( &OF3, &OF4 );
210 test_evaluate( OF3.get_value() + OF4.get_value(), add2 );
211 }
212
test_multiply_value()213 void CompositeOFTest::test_multiply_value()
214 {
215 CompositeOFMultiply mult1( &OF1, &OF2 );
216 test_evaluate( OF1.get_value() * OF2.get_value(), mult1 );
217
218 CompositeOFMultiply mult2( &OF3, &OF4 );
219 test_evaluate( OF3.get_value() * OF4.get_value(), mult2 );
220 }
221
test_scalar_add_value()222 void CompositeOFTest::test_scalar_add_value()
223 {
224 CompositeOFScalarAdd add1( sqrt(2.0), &OF1 );
225 test_evaluate( OF1.get_value() + sqrt(2.0), add1 );
226
227 CompositeOFScalarAdd add2( -1.0, &OF4 );
228 test_evaluate( OF4.get_value() - 1, add2 );
229 }
230
test_scalar_multiply_value()231 void CompositeOFTest::test_scalar_multiply_value()
232 {
233 CompositeOFScalarMultiply mult1( sqrt(2.0), &OF1 );
234 test_evaluate( OF1.get_value() * sqrt(2.0), mult1 );
235
236 CompositeOFScalarMultiply mult2( -1.0, &OF4 );
237 test_evaluate( -OF4.get_value(), mult2 );
238 }
239
test_add_gradient()240 void CompositeOFTest::test_add_gradient()
241 { CompositeOFAdd OF( &LP1, &LP2 ); compare_numerical_gradient( &OF ); }
test_multiply_gradient()242 void CompositeOFTest::test_multiply_gradient()
243 { CompositeOFMultiply OF( &LP1, &LP2 ); compare_numerical_gradient( &OF ); }
test_scalar_add_gradient()244 void CompositeOFTest::test_scalar_add_gradient()
245 { CompositeOFScalarAdd OF( M_PI, &LP1 ); compare_numerical_gradient( &OF ); }
test_scalar_multiply_gradient()246 void CompositeOFTest::test_scalar_multiply_gradient()
247 { CompositeOFScalarMultiply OF( M_PI, &LP1 ); compare_numerical_gradient( &OF ); }
248
get_hessians(MsqHessian & LP1_hess,MsqHessian & LP2_hess,ObjectiveFunction & OF,MsqHessian & OF_hess)249 void CompositeOFTest::get_hessians( MsqHessian& LP1_hess,
250 MsqHessian& LP2_hess,
251 ObjectiveFunction& OF,
252 MsqHessian& OF_hess )
253 {
254 MsqPrintError err(cout);
255 PatchData pd;
256 create_twelve_hex_patch( pd, err );
257 ASSERT_NO_ERROR( err );
258
259 LP1_hess.initialize( pd, err ); ASSERT_NO_ERROR(err);
260 LP2_hess.initialize( pd, err ); ASSERT_NO_ERROR(err);
261 OF_hess .initialize( pd, err ); ASSERT_NO_ERROR(err);
262
263 std::vector<Vector3D> grad;
264 bool rval;
265 double value;
266 rval = LP1.evaluate_with_Hessian( ObjectiveFunction::CALCULATE, pd, value, grad, LP1_hess, err );
267 ASSERT_NO_ERROR(err);
268 CPPUNIT_ASSERT(rval);
269 rval = LP2.evaluate_with_Hessian( ObjectiveFunction::CALCULATE, pd, value, grad, LP2_hess, err );
270 ASSERT_NO_ERROR(err);
271 CPPUNIT_ASSERT(rval);
272 rval = OF .evaluate_with_Hessian( ObjectiveFunction::CALCULATE, pd, value, grad, OF_hess , err );
273 ASSERT_NO_ERROR(err);
274 CPPUNIT_ASSERT(rval);
275 }
276
test_add_hess_diagonal()277 void CompositeOFTest::test_add_hess_diagonal()
278 {
279 CompositeOFAdd OF( &LP1, &LP2 );
280 compare_hessian_diagonal( &OF );
281 }
282
test_multiply_hess_diagonal()283 void CompositeOFTest::test_multiply_hess_diagonal()
284 {
285 CompositeOFMultiply OF( &LP1, &LP2 );
286 std::vector<SymMatrix3D> hess1, hess2, hess;
287
288 MsqPrintError err(cout);
289 PatchData pd;
290 create_twelve_hex_patch( pd, err );
291 ASSERT_NO_ERROR( err );
292
293 std::vector<Vector3D> grad1, grad2, grad;
294 bool rval;
295 double value1, value2, value;
296 rval = LP1.evaluate_with_Hessian_diagonal( ObjectiveFunction::CALCULATE, pd, value1, grad1, hess1, err );
297 ASSERT_NO_ERROR(err);
298 CPPUNIT_ASSERT(rval);
299 rval = LP2.evaluate_with_Hessian_diagonal( ObjectiveFunction::CALCULATE, pd, value2, grad2, hess2, err );
300 ASSERT_NO_ERROR(err);
301 CPPUNIT_ASSERT(rval);
302 rval = OF .evaluate_with_Hessian_diagonal( ObjectiveFunction::CALCULATE, pd, value, grad, hess , err );
303 ASSERT_NO_ERROR(err);
304 CPPUNIT_ASSERT(rval);
305
306 CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), grad1.size() );
307 CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), grad2.size() );
308 CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), grad .size() );
309
310 CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), hess1.size() );
311 CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), hess2.size() );
312 CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), hess .size() );
313
314 CPPUNIT_ASSERT_DOUBLES_EQUAL( value1 * value2, value, 1e-6 );
315
316 for (size_t i = 0; i < pd.num_free_vertices(); ++i) {
317 const Vector3D expected_grad = value2 * grad1[i] + value1 * grad2[i];
318 CPPUNIT_ASSERT_VECTORS_EQUAL( expected_grad, grad[i], 1e-6 );
319
320 Matrix3D o;
321 o.outer_product( grad1[i], grad2[i] );
322 Matrix3D expect = o + transpose(o);
323 expect += value2 * hess1[i];
324 expect += value1 * hess2[i];
325 CPPUNIT_ASSERT_MATRICES_EQUAL( expect, Matrix3D(hess[i]), 1e-6 );
326 }
327 }
328
test_scalar_add_hess_diagonal()329 void CompositeOFTest::test_scalar_add_hess_diagonal()
330 {
331 CompositeOFScalarAdd OF( 1111.1, &LP1 );
332 compare_hessian_diagonal( &OF );
333 }
334
test_scalar_multiply_hess_diagonal()335 void CompositeOFTest::test_scalar_multiply_hess_diagonal()
336 {
337 const double scale = 2.5;
338 CompositeOFScalarMultiply OF( scale, &LP1 );
339 compare_hessian_diagonal( &OF );
340 }
341
test_add_hessian()342 void CompositeOFTest::test_add_hessian()
343 {
344 // test value and gradient
345 CompositeOFAdd OF( &LP1, &LP2 );
346 compare_hessian_gradient( &OF );
347
348 // test actual hessian values
349 MsqHessian hess1, hess2, hess;
350 get_hessians( hess1, hess2, OF, hess );
351 Matrix3D *b1, *b2, *b;
352 for (unsigned r = 0; r < hess.size(); ++r) {
353 for (unsigned c = r; c < hess.size(); ++c) {
354 b1 = hess1.get_block(r,c);
355 b2 = hess2.get_block(r,c);
356 b = hess .get_block(r,c);
357 if (b) {
358 CPPUNIT_ASSERT_MATRICES_EQUAL( *b1 + *b2, *b, 1e-6 );
359 }
360 }
361 }
362 }
363
test_multiply_hessian()364 void CompositeOFTest::test_multiply_hessian()
365 {
366 MsqError err;
367 PatchData pd;
368 create_twelve_hex_patch( pd, err );
369 ASSERT_NO_ERROR( err );
370
371 // this should always fail because the Hessian is not sparse
372 CompositeOFMultiply OF( &LP1, &LP2 );
373 double value;
374 MsqHessian hess;
375 hess.initialize( pd, err );
376 ASSERT_NO_ERROR(err);
377 std::vector<Vector3D> grad;
378 OF.evaluate_with_Hessian( ObjectiveFunction::CALCULATE, pd, value, grad, hess, err );
379 CPPUNIT_ASSERT(err);
380 }
381
test_scalar_add_hessian()382 void CompositeOFTest::test_scalar_add_hessian()
383 {
384 // test value and gradient
385 CompositeOFScalarAdd OF( 1111.1, &LP1 );
386 compare_hessian_gradient( &OF );
387
388 // test actual hessian values
389 MsqHessian hess1, hess2, hess;
390 get_hessians( hess1, hess2, OF, hess );
391 Matrix3D *b1, *b;
392 for (unsigned r = 0; r < hess.size(); ++r) {
393 for (unsigned c = r; c < hess.size(); ++c) {
394 b1 = hess1.get_block(r,c);
395 b = hess .get_block(r,c);
396 if (b) {
397 CPPUNIT_ASSERT_MATRICES_EQUAL( *b1, *b, 1e-6 );
398 }
399 }
400 }
401 }
402
test_scalar_multiply_hessian()403 void CompositeOFTest::test_scalar_multiply_hessian()
404 {
405 // test value and gradient
406 const double scale = 2.5;
407 CompositeOFScalarMultiply OF( scale, &LP1 );
408 compare_hessian_gradient( &OF );
409
410 // test actual hessian values
411 MsqHessian hess1, hess2, hess;
412 get_hessians( hess1, hess2, OF, hess );
413 Matrix3D *b1, *b;
414 for (unsigned r = 0; r < hess.size(); ++r) {
415 for (unsigned c = r; c < hess.size(); ++c) {
416 b1 = hess1.get_block(r,c);
417 b = hess .get_block(r,c);
418 if (b) {
419 CPPUNIT_ASSERT_MATRICES_EQUAL( scale * *b1, *b, 1e-6 );
420 }
421 }
422 }
423 }
424
425
test_composite_clone(ObjectiveFunction & OF)426 void CompositeOFTest::test_composite_clone( ObjectiveFunction& OF )
427 {
428 // save current count of instances of underlying OFs for later
429 const int init_count = FauxObjectiveFunction::get_instance_count();
430
431 // clone the objective function
432 ObjectiveFunction* clone = OF.clone();
433
434 // check that the underlying OFs were also cloned
435 CPPUNIT_ASSERT( init_count < FauxObjectiveFunction::get_instance_count() );
436
437 // check that the value is the same
438 MsqPrintError err(cout);
439 double orig_val, clone_val;
440 bool rval;
441 rval = OF.evaluate( ObjectiveFunction::CALCULATE, patch(), orig_val, false, err );
442 ASSERT_NO_ERROR(err);
443 CPPUNIT_ASSERT(rval);
444 rval = clone->evaluate( ObjectiveFunction::CALCULATE, patch(), clone_val, false, err );
445 ASSERT_NO_ERROR(err);
446 CPPUNIT_ASSERT(rval);
447 CPPUNIT_ASSERT_DOUBLES_EQUAL( orig_val, clone_val, 1e-6 );
448
449 // check that cloned instances of underlying OFs are deleted
450 delete clone;
451 CPPUNIT_ASSERT_EQUAL( init_count, FauxObjectiveFunction::get_instance_count() );
452 }
453
454
test_clone_add()455 void CompositeOFTest::test_clone_add()
456 { CompositeOFAdd OF( &OF1, &OF2 ); test_composite_clone( OF ); }
test_clone_multiply()457 void CompositeOFTest::test_clone_multiply()
458 { CompositeOFMultiply OF( &OF1, &OF2 ); test_composite_clone( OF ); }
test_clone_scalar_add()459 void CompositeOFTest::test_clone_scalar_add()
460 { CompositeOFScalarAdd OF( 2.1, &OF2 ); test_composite_clone( OF ); }
test_clone_scalar_multiply()461 void CompositeOFTest::test_clone_scalar_multiply()
462 { CompositeOFScalarMultiply OF( 0.333, &OF2 ); test_composite_clone( OF ); }
463
test_invalid_eval(ObjectiveFunction & OF)464 void CompositeOFTest::test_invalid_eval( ObjectiveFunction& OF )
465 {
466 MsqPrintError err(cout);
467 bool rval;
468 double value;
469 rval = OF.evaluate( ObjectiveFunction::CALCULATE, patch(), value, false, err );
470 ASSERT_NO_ERROR(err);
471 CPPUNIT_ASSERT(rval == false);
472 }
473
test_eval_fails(ObjectiveFunction & OF)474 void CompositeOFTest::test_eval_fails( ObjectiveFunction& OF )
475 {
476 MsqError err;
477 double value;
478 OF.evaluate( ObjectiveFunction::CALCULATE, patch(), value, false, err );
479 CPPUNIT_ASSERT_EQUAL(MsqError::INTERNAL_ERROR, err.error_code());
480 }
481
test_add_invalid()482 void CompositeOFTest::test_add_invalid()
483 {
484 CompositeOFAdd add1( &OF1, &invalidOF );
485 test_invalid_eval( add1 );
486
487 CompositeOFAdd add2( &invalidOF, &OF3 );
488 test_invalid_eval( add2 );
489 }
490
test_multiply_invalid()491 void CompositeOFTest::test_multiply_invalid()
492 {
493 CompositeOFMultiply mult1( &OF1, &invalidOF );
494 test_invalid_eval( mult1 );
495
496 CompositeOFMultiply mult2( &invalidOF, &OF3 );
497 test_invalid_eval( mult2 );
498 }
499
test_scalar_add_invalid()500 void CompositeOFTest::test_scalar_add_invalid()
501 {
502 CompositeOFScalarAdd OF( 2.0, &invalidOF );
503 test_invalid_eval( OF );
504 }
505
test_scalar_multiply_invalid()506 void CompositeOFTest::test_scalar_multiply_invalid()
507 {
508 CompositeOFScalarMultiply OF( 2.0, &invalidOF );
509 test_invalid_eval( OF );
510 }
511
test_add_error()512 void CompositeOFTest::test_add_error()
513 {
514 CompositeOFAdd add1( &OF1, &errorOF );
515 test_eval_fails( add1 );
516
517 CompositeOFAdd add2( &errorOF, &OF3 );
518 test_eval_fails( add2 );
519 }
520
test_multiply_error()521 void CompositeOFTest::test_multiply_error()
522 {
523 CompositeOFMultiply mult1( &OF1, &errorOF );
524 test_eval_fails( mult1 );
525
526 CompositeOFMultiply mult2( &errorOF, &OF3 );
527 test_eval_fails( mult2 );
528 }
529
test_scalar_add_error()530 void CompositeOFTest::test_scalar_add_error()
531 {
532 CompositeOFScalarAdd OF( 2.0, &errorOF );
533 test_eval_fails( OF );
534 }
535
test_scalar_multiply_error()536 void CompositeOFTest::test_scalar_multiply_error()
537 {
538 CompositeOFScalarMultiply OF( 2.0, &errorOF );
539 test_eval_fails( OF );
540 }
541
542