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