bool PMeanPTemplate::evaluate_with_Hessian( EvalType type, PatchData& pd, double& value_out, std::vector<Vector3D>& grad_out, MsqHessian& Hessian_out, MsqError& err ) { QualityMetric* qm = get_quality_metric(); qm->get_evaluations( pd, qmHandles, OF_FREE_EVALS_ONLY, err ); MSQ_ERRFALSE(err); // zero gradient and hessian grad_out.clear(); grad_out.resize( pd.num_free_vertices(), 0.0 ); Hessian_out.zero_out(); // calculate OF value and gradient for just the patch std::vector<size_t>::const_iterator i; size_t j, k, n; double value, working_sum = 0.0; const double f1 = qm->get_negate_flag() * mPower.value(); const double f2 = f1 * (mPower.value() - 1); Matrix3D m; for (i = qmHandles.begin(); i != qmHandles.end(); ++i) { bool result = qm->evaluate_with_Hessian( pd, *i, value, mIndices, mGradient, mHessian, err ); if (MSQ_CHKERR(err) || !result) return false; if (fabs(value) < DBL_EPSILON) continue; const size_t nfree = mIndices.size(); n = 0; if (mPower.value() == 1.0) { working_sum += mPower.raise( value ); for (j = 0; j < nfree; ++j) { mGradient[j] *= f1; grad_out[mIndices[j]] += mGradient[j]; for (k = j; k < nfree; ++k) { mHessian[n] *= f1; Hessian_out.add( mIndices[j], mIndices[k], mHessian[n], err ); MSQ_ERRFALSE(err); ++n; } } } else { const double r2 = mPowerMinus2.raise( value ); const double r1 = r2 * value; working_sum += r1 * value; const double hf = f2 * r2; const double gf = f1 * r1; for (j = 0; j < nfree; ++j) { for (k = j; k < nfree; ++k) { m.outer_product( mGradient[j], mGradient[k] ); m *= hf; mHessian[n] *= gf; m += mHessian[n]; Hessian_out.add( mIndices[j], mIndices[k], m, err ); MSQ_ERRFALSE(err); ++n; } } for (j = 0; j < nfree; ++j) { mGradient[j] *= gf; grad_out[mIndices[j]] += mGradient[j]; } } } // get overall OF value, update member data, etc. size_t global_count; value_out = qm->get_negate_flag() * get_value( working_sum, qmHandles.size(), type, global_count ); const double inv_n = 1.0 / global_count; std::vector<Vector3D>::iterator g; for (g = grad_out.begin(); g != grad_out.end(); ++g) *g *= inv_n; Hessian_out.scale( inv_n ); return true; }
/*\ For each element, each entry to be accumulated in the Hessian for this objective function (\f$ \sum_{e \in E} Q(e)^p \f$ where \f$ E \f$ is the set of all elements in the patch) has the form: \f$ pQ(e)^{p-1} \nabla^2 Q(e) + p(p-1)Q(e)^{p-2} \nabla Q(e) [\nabla Q(e)]^T \f$. For \f$ p=2 \f$, this simplifies to \f$ 2Q(e) \nabla^2 Q(e) + 2 \nabla Q(e) [\nabla Q(e)]^T \f$. For \f$ p=1 \f$, this simplifies to \f$ \nabla^2 Q(e) \f$. The \f$ p=1 \f$ simplified version is implemented directly to speed up computation. This function does not support vertex-based metrics. \param pd The PatchData object for which the objective function hessian is computed. \param hessian this object must have been previously initialized. */ bool LPtoPTemplate::evaluate_with_Hessian( EvalType type, PatchData& pd, double& OF_val, std::vector<Vector3D>& grad, MsqHessian& hessian, MsqError& err ) { QualityMetric* qm = get_quality_metric(); qm->get_evaluations( pd, qmHandles, OF_FREE_EVALS_ONLY, err ); MSQ_ERRFALSE(err); double negate_flag = qm->get_negate_flag(); // zero gradient and hessian grad.clear(); grad.resize( pd.num_free_vertices(), 0.0 ); hessian.zero_out(); double QM_val, QM_pow = 1.0; double fac1, fac2; Matrix3D elem_outer_product; bool qm_bool; size_t i, j, n; short p; // Loops over all elements in the patch. OF_val = 0.0; std::vector<size_t>::const_iterator k; for (k = qmHandles.begin(); k != qmHandles.end(); ++k) { // Computes \nabla^2 Q(e). Only the free vertices will have non-zero entries. qm_bool = qm->evaluate_with_Hessian( pd, *k, QM_val, mIndices, mGradient, mHessian, err ); if (MSQ_CHKERR(err) || !qm_bool) return false; QM_val = fabs(QM_val); // **** Computes Hessian **** const size_t nve = mIndices.size(); if (pVal == 1) { QM_pow = 1.0; n=0; for (i=0; i<nve; ++i) { for (j=i; j<nve; ++j) { //negate if necessary mHessian[n] *= negate_flag; hessian.add( mIndices[i], mIndices[j], mHessian[n], err ); MSQ_ERRFALSE(err); ++n; } } fac1 = 1; } else if (pVal >= 2) { // Computes the coefficients: QM_pow = 1.0; for (p=0; p<pVal-2; ++p) QM_pow *= QM_val; // 1 - computes p(p-1)Q(e)^{p-2} fac2 = pVal* (pVal-1) * QM_pow; // 2 - computes pQ(e)^{p-1} QM_pow *= QM_val; fac1 = pVal * QM_pow; //fac1 *= qm->get_negate_flag(); //fac2 *= qm->get_negate_flag(); n=0; for (i=0; i<nve; ++i) { for (j=i; j<nve; ++j) { elem_outer_product.outer_product(mGradient[i], mGradient[j]); elem_outer_product *= fac2; mHessian[n] *= fac1; mHessian[n] += elem_outer_product; mHessian[n] *= negate_flag; hessian.add( mIndices[i], mIndices[j], mHessian[n], err ); MSQ_ERRFALSE(err); ++n; } } } else { MSQ_SETERR(err)(" invalid P value.", MsqError::INVALID_STATE); return false; } // **** Computes Gradient **** // For each vertex in the element ... for (i=0; i<nve; ++i) { // ... computes p*q^{p-1}*grad(q) ... mGradient[i] *= fac1*negate_flag; // ... and accumulates it in the objective function gradient. //also scale the gradient by the scaling factor assert (mIndices[i] < pd.num_free_vertices()); grad[mIndices[i]] += mGradient[i]; } // **** computes Objective Function value \sum_{i=1}^{N_e} |q_i|^P **** OF_val += QM_pow * QM_val; } size_t global_count; OF_val = negate_flag * get_value( OF_val, qmHandles.size(), type, global_count, err ); // if (!global_count) // return false; // invalid mesh if (dividingByN && global_count) { const double inv_n = 1.0 / global_count; std::vector<Vector3D>::iterator g; for (g = grad.begin(); g != grad.end(); ++g) *g *= inv_n; hessian.scale( inv_n ); } return true; }