1 /* *****************************************************************
2 MESQUITE -- The Mesh Quality Improvement Toolkit
3
4 Copyright 2004 Sandia Corporation and Argonne National
5 Laboratory. Under the terms of Contract DE-AC04-94AL85000
6 with Sandia Corporation, the U.S. Government retains certain
7 rights in 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 diachin2@llnl.gov, djmelan@sandia.gov, mbrewer@sandia.gov,
24 pknupp@sandia.gov, tleurent@mcs.anl.gov, tmunson@mcs.anl.gov
25
26 ***************************************************************** */
27 /*!
28 \file LPtoPTemplate.cpp
29 \brief
30
31 This Objective Function is evaluated using an L P norm to the pth power.
32 total=(sum (x_i)^pVal)
33 \author Michael Brewer
34 \author Thomas Leurent
35 \date 2002-01-23
36 */
37 #include <math.h>
38 #include "LPtoPTemplate.hpp"
39 #include "MsqFreeVertexIndexIterator.hpp"
40 #include "MsqTimer.hpp"
41 #include "MsqHessian.hpp"
42 #include "MsqDebug.hpp"
43 #include "QualityMetric.hpp"
44
45 using namespace MBMesquite;
46
LPtoPTemplate(QualityMetric * qualitymetric,short Pinput,MsqError & err)47 LPtoPTemplate::LPtoPTemplate(QualityMetric *qualitymetric, short Pinput, MsqError &err)
48 : ObjectiveFunctionTemplate(qualitymetric)
49 {
50 pVal=Pinput;
51 if(pVal<1){
52 MSQ_SETERR(err)("P_VALUE must be greater than 0.", MsqError::INVALID_ARG);
53 return;
54 }
55
56 dividingByN=false;
57
58 clear();
59 }
60
LPtoPTemplate(short P,QualityMetric * qm)61 LPtoPTemplate::LPtoPTemplate( short P, QualityMetric* qm )
62 : ObjectiveFunctionTemplate(qm), pVal(P), dividingByN(false)
63 { clear(); }
64
clear()65 void LPtoPTemplate::clear()
66 {
67 mCount = 0;
68 mPowSum = 0;
69 saveCount = 0;
70 savePowSum = 0;
71 }
72
73 //Michael: need to clean up here
~LPtoPTemplate()74 LPtoPTemplate::~LPtoPTemplate(){
75
76 }
77
clone() const78 ObjectiveFunction* LPtoPTemplate::clone() const
79 { return new LPtoPTemplate(*this); }
80
get_value(double power_sum,size_t count,EvalType type,size_t & global_count,MsqError &)81 double LPtoPTemplate::get_value( double power_sum, size_t count, EvalType type,
82 size_t& global_count, MsqError& /*err*/ )
83 {
84 double result = 0;
85 switch (type)
86 {
87 default:
88 case CALCULATE:
89 result = power_sum;
90 global_count = count;
91 break;
92
93 case ACCUMULATE:
94 mPowSum += power_sum;
95 mCount += count;
96 result = mPowSum;
97 global_count = mCount;
98 break;
99
100 case SAVE:
101 savePowSum = power_sum;
102 saveCount = count;
103 result = mPowSum;
104 global_count = mCount;
105 break;
106
107 case UPDATE:
108 mPowSum -= savePowSum;
109 mCount -= saveCount;
110 savePowSum = power_sum;
111 saveCount = count;
112 mPowSum += savePowSum;
113 mCount += saveCount;
114 result = mPowSum;
115 global_count = mCount;
116 break;
117
118 case TEMPORARY:
119 result = mPowSum - savePowSum + power_sum;
120 global_count = mCount + count - saveCount;
121 break;
122 }
123
124 // if (!global_count)
125 // {
126 // MSQ_SETERR(err)(" global_count is zero, possibly due to an invalid mesh.", MsqError::INVALID_MESH);
127 // return -1; // result is invalid
128 // }
129 if (dividingByN && global_count)
130 result /= global_count;
131 return result;
132 }
133
evaluate(EvalType type,PatchData & pd,double & value_out,bool free,MsqError & err)134 bool LPtoPTemplate::evaluate( EvalType type,
135 PatchData& pd,
136 double& value_out,
137 bool free,
138 MsqError& err )
139 {
140 QualityMetric* qm = get_quality_metric();
141 if (type == ObjectiveFunction::ACCUMULATE)
142 qm->get_single_pass( pd, qmHandles, free, err );
143 else
144 qm->get_evaluations( pd, qmHandles, free, err );
145 MSQ_ERRFALSE(err);
146
147 // calculate OF value for just the patch
148 std::vector<size_t>::const_iterator i;
149 double value, working_sum = 0.0;
150 for (i = qmHandles.begin(); i != qmHandles.end(); ++i)
151 {
152 bool result = qm->evaluate( pd, *i, value, err );
153 if (MSQ_CHKERR(err) || !result)
154 return false;
155
156 double tmp_val = value;
157 for (short j = 1; j < pVal; ++j)
158 tmp_val *= value;
159 working_sum += fabs(tmp_val);
160 }
161
162 // get overall OF value, update member data, etc.
163 size_t global_count;
164 value_out = qm->get_negate_flag() * get_value( working_sum, qmHandles.size(), type, global_count, err );
165 // if (!global_count)
166 // return false; // invalid mesh
167 // else
168 return true;
169 }
170
evaluate_with_gradient(EvalType type,PatchData & pd,double & OF_val,std::vector<Vector3D> & grad_out,MsqError & err)171 bool LPtoPTemplate::evaluate_with_gradient( EvalType type,
172 PatchData& pd,
173 double& OF_val,
174 std::vector<Vector3D>& grad_out,
175 MsqError& err )
176 {
177 QualityMetric* qm = get_quality_metric();
178 qm->get_evaluations( pd, qmHandles, OF_FREE_EVALS_ONLY, err ); MSQ_ERRFALSE(err);
179
180 // zero gradient
181 grad_out.clear();
182 grad_out.resize( pd.num_free_vertices(), Vector3D(0.0,0.0,0.0) );
183 bool qm_bool=true;
184 double QM_val;
185 OF_val = 0.;
186 int p1;
187
188 // calculate OF value and gradient for just the patch
189 std::vector<size_t>::const_iterator i;
190 for (i = qmHandles.begin(); i != qmHandles.end(); ++i)
191 {
192 qm_bool = qm->evaluate_with_gradient( pd, *i, QM_val, mIndices, mGradient, err );
193 if (MSQ_CHKERR(err) || !qm_bool)
194 return false;
195
196 QM_val = fabs(QM_val);
197 double QM_pow = 1.0;
198 double factor = qm->get_negate_flag();
199 if (pVal == 1)
200 QM_pow = 1.0;
201 else {
202 QM_pow = QM_val;
203 for (p1 = 2; p1 < pVal; ++p1)
204 QM_pow *= QM_val;
205 factor *= QM_pow * pVal;
206 }
207
208 OF_val += QM_pow * QM_val;
209 for (size_t j = 0; j < mIndices.size(); ++j) {
210 mGradient[j] *= factor;
211 grad_out[mIndices[j]] += mGradient[j];
212 }
213 }
214
215 // get overall OF value, update member data, etc.
216 size_t global_count;
217 OF_val = qm->get_negate_flag() * get_value( OF_val, qmHandles.size(), type, global_count, err );
218 // if (!global_count)
219 // return false; // invalid mesh
220
221 if (dividingByN && global_count) {
222 const double inv_n = 1.0/global_count;
223 std::vector<Vector3D>::iterator g;
224 for (g = grad_out.begin(); g != grad_out.end(); ++g)
225 *g *= inv_n;
226 }
227
228 return true;
229 }
230
evaluate_with_Hessian_diagonal(EvalType type,PatchData & pd,double & OF_val,std::vector<Vector3D> & grad,std::vector<SymMatrix3D> & hess_diag,MsqError & err)231 bool LPtoPTemplate::evaluate_with_Hessian_diagonal( EvalType type,
232 PatchData& pd,
233 double& OF_val,
234 std::vector<Vector3D>& grad,
235 std::vector<SymMatrix3D>& hess_diag,
236 MsqError& err )
237 {
238 QualityMetric* qm = get_quality_metric();
239 qm->get_evaluations( pd, qmHandles, OF_FREE_EVALS_ONLY, err ); MSQ_ERRFALSE(err);
240
241 // zero gradient and hessian
242 grad.clear();
243 grad.resize( pd.num_free_vertices(), 0.0 );
244 hess_diag.clear();
245 hess_diag.resize( pd.num_free_vertices(), 0.0 );
246
247 double QM_val, QM_pow = 1.0;
248 double fac1, fac2;
249 const double negate_flag = qm->get_negate_flag();
250 bool qm_bool;
251 size_t i;
252 short p;
253
254 // Loops over all elements in the patch.
255 OF_val = 0.0;
256 std::vector<size_t>::const_iterator k;
257 for (k = qmHandles.begin(); k != qmHandles.end(); ++k)
258 {
259 // Computes \nabla^2 Q(e). Only the free vertices will have non-zero entries.
260 qm_bool = qm->evaluate_with_Hessian_diagonal( pd, *k, QM_val, mIndices, mGradient, mDiag, err );
261 if (MSQ_CHKERR(err) || !qm_bool) return false;
262 QM_val = fabs(QM_val);
263
264 // **** Computes Hessian ****
265 const size_t nve = mIndices.size();
266 if (pVal == 1) {
267 QM_pow = 1.0;
268 for (i=0; i<nve; ++i) {
269 mDiag[i] *= negate_flag;
270 hess_diag[mIndices[i]] += mDiag[i];
271 }
272 fac1 = 1;
273 }
274 else if (pVal >= 2) {
275 // Computes the coefficients:
276 QM_pow = 1.0;
277 for (p=0; p<pVal-2; ++p)
278 QM_pow *= QM_val;
279 // 1 - computes p(p-1)Q(e)^{p-2}
280 fac2 = pVal* (pVal-1) * QM_pow;
281 // 2 - computes pQ(e)^{p-1}
282 QM_pow *= QM_val;
283 fac1 = pVal * QM_pow;
284
285 //fac1 *= qm->get_negate_flag();
286 //fac2 *= qm->get_negate_flag();
287
288 for (i=0; i<nve; ++i) {
289 SymMatrix3D op(mGradient[i]);
290 op *= fac2;
291 mDiag[i] *= fac1;
292 op += mDiag[i];
293 op *= negate_flag;
294 hess_diag[mIndices[i]] += op;
295 }
296 } else {
297 MSQ_SETERR(err)(" invalid P value.", MsqError::INVALID_STATE);
298 return false;
299 }
300
301
302 // **** Computes Gradient ****
303
304 // For each vertex in the element ...
305 for (i=0; i<nve; ++i) {
306 // ... computes p*q^{p-1}*grad(q) ...
307 mGradient[i] *= fac1*negate_flag;
308 // ... and accumulates it in the objective function gradient.
309 //also scale the gradient by the scaling factor
310 assert (mIndices[i] < pd.num_free_vertices());
311 grad[mIndices[i]] += mGradient[i];
312 }
313
314 // **** computes Objective Function value \sum_{i=1}^{N_e} |q_i|^P ****
315 OF_val += QM_pow * QM_val;
316 }
317
318 size_t global_count;
319 OF_val = negate_flag
320 * get_value( OF_val, qmHandles.size(), type, global_count, err );
321 // if (!global_count)
322 // return false; // invalid mesh
323
324 if (dividingByN && global_count) {
325 const double inv_n = 1.0 / global_count;
326 for (i = 0; i < pd.num_free_vertices(); ++i) {
327 grad[i] *= inv_n;
328 hess_diag[i] *= inv_n;
329 }
330 }
331
332 return true;
333 }
334
335 /*\ For each element, each entry to be accumulated in the Hessian for
336 this objective function (\f$ \sum_{e \in E} Q(e)^p \f$ where \f$ E \f$
337 is the set of all elements in the patch) has the form:
338 \f$ pQ(e)^{p-1} \nabla^2 Q(e) + p(p-1)Q(e)^{p-2} \nabla Q(e) [\nabla Q(e)]^T \f$.
339
340 For \f$ p=2 \f$, this simplifies to
341 \f$ 2Q(e) \nabla^2 Q(e) + 2 \nabla Q(e) [\nabla Q(e)]^T \f$.
342
343 For \f$ p=1 \f$, this simplifies to \f$ \nabla^2 Q(e) \f$.
344
345 The \f$ p=1 \f$ simplified version is implemented directly
346 to speed up computation.
347
348 This function does not support vertex-based metrics.
349
350 \param pd The PatchData object for which the objective function
351 hessian is computed.
352 \param hessian this object must have been previously initialized.
353 */
evaluate_with_Hessian(EvalType type,PatchData & pd,double & OF_val,std::vector<Vector3D> & grad,MsqHessian & hessian,MsqError & err)354 bool LPtoPTemplate::evaluate_with_Hessian( EvalType type,
355 PatchData& pd,
356 double& OF_val,
357 std::vector<Vector3D>& grad,
358 MsqHessian& hessian,
359 MsqError& err )
360 {
361 QualityMetric* qm = get_quality_metric();
362 qm->get_evaluations( pd, qmHandles, OF_FREE_EVALS_ONLY, err ); MSQ_ERRFALSE(err);
363 double negate_flag = qm->get_negate_flag();
364
365 // zero gradient and hessian
366 grad.clear();
367 grad.resize( pd.num_free_vertices(), 0.0 );
368 hessian.zero_out();
369
370 double QM_val, QM_pow = 1.0;
371 double fac1, fac2;
372 Matrix3D elem_outer_product;
373 bool qm_bool;
374 size_t i, j, n;
375 short p;
376
377 // Loops over all elements in the patch.
378 OF_val = 0.0;
379 std::vector<size_t>::const_iterator k;
380 for (k = qmHandles.begin(); k != qmHandles.end(); ++k)
381 {
382 // Computes \nabla^2 Q(e). Only the free vertices will have non-zero entries.
383 qm_bool = qm->evaluate_with_Hessian( pd, *k, QM_val, mIndices, mGradient, mHessian, err );
384 if (MSQ_CHKERR(err) || !qm_bool)
385 return false;
386 QM_val = fabs(QM_val);
387
388 // **** Computes Hessian ****
389 const size_t nve = mIndices.size();
390 if (pVal == 1) {
391 QM_pow = 1.0;
392 n=0;
393 for (i=0; i<nve; ++i) {
394 for (j=i; j<nve; ++j) {
395 //negate if necessary
396 mHessian[n] *= negate_flag;
397 hessian.add( mIndices[i], mIndices[j], mHessian[n], err ); MSQ_ERRFALSE(err);
398 ++n;
399 }
400 }
401 fac1 = 1;
402 }
403 else if (pVal >= 2) {
404 // Computes the coefficients:
405 QM_pow = 1.0;
406 for (p=0; p<pVal-2; ++p)
407 QM_pow *= QM_val;
408 // 1 - computes p(p-1)Q(e)^{p-2}
409 fac2 = pVal* (pVal-1) * QM_pow;
410 // 2 - computes pQ(e)^{p-1}
411 QM_pow *= QM_val;
412 fac1 = pVal * QM_pow;
413
414 //fac1 *= qm->get_negate_flag();
415 //fac2 *= qm->get_negate_flag();
416
417 n=0;
418 for (i=0; i<nve; ++i) {
419 for (j=i; j<nve; ++j) {
420 elem_outer_product.outer_product(mGradient[i], mGradient[j]);
421 elem_outer_product *= fac2;
422 mHessian[n] *= fac1;
423 mHessian[n] += elem_outer_product;
424 mHessian[n] *= negate_flag;
425 hessian.add( mIndices[i], mIndices[j], mHessian[n], err ); MSQ_ERRFALSE(err);
426 ++n;
427 }
428 }
429 } else {
430 MSQ_SETERR(err)(" invalid P value.", MsqError::INVALID_STATE);
431 return false;
432 }
433
434
435 // **** Computes Gradient ****
436
437 // For each vertex in the element ...
438 for (i=0; i<nve; ++i) {
439 // ... computes p*q^{p-1}*grad(q) ...
440 mGradient[i] *= fac1*negate_flag;
441 // ... and accumulates it in the objective function gradient.
442 //also scale the gradient by the scaling factor
443 assert (mIndices[i] < pd.num_free_vertices());
444 grad[mIndices[i]] += mGradient[i];
445 }
446
447 // **** computes Objective Function value \sum_{i=1}^{N_e} |q_i|^P ****
448 OF_val += QM_pow * QM_val;
449 }
450
451 size_t global_count;
452 OF_val = negate_flag
453 * get_value( OF_val, qmHandles.size(), type, global_count, err );
454 // if (!global_count)
455 // return false; // invalid mesh
456
457 if (dividingByN && global_count) {
458 const double inv_n = 1.0 / global_count;
459 std::vector<Vector3D>::iterator g;
460 for (g = grad.begin(); g != grad.end(); ++g)
461 *g *= inv_n;
462 hessian.scale( inv_n );
463 }
464
465 return true;
466 }
467