static double pmean_of_triangle_corner_hessians( double inner_power, double outer_power, const double* v, const Vector3D* cg, const Matrix3D* ch, Vector3D* tg, Matrix3D* th, bool scale ) { Matrix3D op; double gf[3], hf[3]; double nm, m = 0; double den = scale ? 3.0: 1.0; for (unsigned i = 0; i < 3; ++i) { nm = pow(v[i], inner_power); m += nm; gf[i] = inner_power*nm / v[i] / den; hf[i] = (inner_power-1)*gf[i] / v[i]; } nm = m / den; tg[0] = gf[0] * cg[0] + gf[1] * cg[5] + gf[2] * cg[7]; tg[1] = gf[0] * cg[1] + gf[1] * cg[3] + gf[2] * cg[8]; tg[2] = gf[0] * cg[2] + gf[1] * cg[4] + gf[2] * cg[6]; th[0] = hf[0]*op.outer_product( cg[0], cg[0] ) + gf[0]*ch[ 0] + hf[1]*op.outer_product( cg[5], cg[5] ) + gf[1]*ch[11] + hf[2]*op.outer_product( cg[7], cg[7] ) + gf[2]*ch[15]; th[3] = hf[0]*op.outer_product( cg[1], cg[1] ) + gf[0]*ch[ 3] + hf[1]*op.outer_product( cg[3], cg[3] ) + gf[1]*ch[ 6] + hf[2]*op.outer_product( cg[8], cg[8] ) + gf[2]*ch[17]; th[5] = hf[0]*op.outer_product( cg[2], cg[2] ) + gf[0]*ch[ 5] + hf[1]*op.outer_product( cg[4], cg[4] ) + gf[1]*ch[ 9] + hf[2]*op.outer_product( cg[6], cg[6] ) + gf[2]*ch[12]; th[1] = hf[0]*op.outer_product( cg[0], cg[1] ) + gf[0]*ch[ 1] + hf[1]*op.outer_product( cg[5], cg[3] ) + gf[1]*transpose(ch[ 8]) + hf[2]*op.outer_product( cg[7], cg[8] ) + gf[2]*ch[16]; th[2] = hf[0]*op.outer_product( cg[0], cg[2] ) + gf[0]*ch[ 2] + hf[1]*op.outer_product( cg[5], cg[4] ) + gf[1]*transpose(ch[10]) + hf[2]*op.outer_product( cg[7], cg[6] ) + gf[2]*transpose(ch[13]); th[4] = hf[0]*op.outer_product( cg[1], cg[2] ) + gf[0]*ch[ 4] + hf[1]*op.outer_product( cg[3], cg[4] ) + gf[1]*ch[ 7] + hf[2]*op.outer_product( cg[8], cg[6] ) + gf[2]*transpose(ch[14]); m = pow(nm, outer_power); double g = m * outer_power / nm; double h = (outer_power - 1.0) * g / nm; for (unsigned r = 0; r < 3; ++r) { for (unsigned c = r; c < 3; ++c) { *th = g * *th + h * op.outer_product( tg[r], tg[c] ); ++th; } tg[r] *= g; } return m; }
bool PowerQualityMetric::evaluate_with_Hessian( PatchData& pd, size_t handle, double& value, std::vector<size_t>& indices, std::vector<Vector3D>& gradient, std::vector<Matrix3D>& Hessian, MsqError& err ) { indices.clear(); bool rval = mMetric.evaluate_with_Hessian( pd, handle, value, indices, gradient, Hessian, err ); const double v = mPower.raise(value); const double g = fabs(value) > DBL_EPSILON ? mPower.value() * v / value : 0.0; const double h = fabs(value) > DBL_EPSILON ? g * (mPower.value() - 1) / value : 0.0; value = v; Matrix3D op; unsigned idx = 0; unsigned n = indices.size(); for (unsigned r = 0; r < n; ++r) { for (unsigned c = r; c < n; ++c) { Hessian[idx] *= g; op.outer_product( gradient[r], gradient[c] ); op *= h; Hessian[idx] += op; ++idx; } gradient[r] *= g; } return !MSQ_CHKERR(err) && rval; }
void test_outer_product() { Matrix3D mat; Vector3D vec1(2, 7, 3); Vector3D vec2(5, 8, 9); mat.outer_product(vec1, vec2); Matrix3D correct(" 10 16 18 " " 35 56 63 " " 15 24 27 "); CPPUNIT_ASSERT( mat == correct ); }
void CompositeOFTest::test_multiply_hess_diagonal() { CompositeOFMultiply OF( &LP1, &LP2 ); std::vector<SymMatrix3D> hess1, hess2, hess; MsqPrintError err(cout); PatchData pd; create_twelve_hex_patch( pd, err ); ASSERT_NO_ERROR( err ); std::vector<Vector3D> grad1, grad2, grad; bool rval; double value1, value2, value; rval = LP1.evaluate_with_Hessian_diagonal( ObjectiveFunction::CALCULATE, pd, value1, grad1, hess1, err ); ASSERT_NO_ERROR(err); CPPUNIT_ASSERT(rval); rval = LP2.evaluate_with_Hessian_diagonal( ObjectiveFunction::CALCULATE, pd, value2, grad2, hess2, err ); ASSERT_NO_ERROR(err); CPPUNIT_ASSERT(rval); rval = OF .evaluate_with_Hessian_diagonal( ObjectiveFunction::CALCULATE, pd, value, grad, hess , err ); ASSERT_NO_ERROR(err); CPPUNIT_ASSERT(rval); CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), grad1.size() ); CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), grad2.size() ); CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), grad .size() ); CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), hess1.size() ); CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), hess2.size() ); CPPUNIT_ASSERT_EQUAL( pd.num_free_vertices(), hess .size() ); CPPUNIT_ASSERT_DOUBLES_EQUAL( value1 * value2, value, 1e-6 ); for (size_t i = 0; i < pd.num_free_vertices(); ++i) { const Vector3D expected_grad = value2 * grad1[i] + value1 * grad2[i]; CPPUNIT_ASSERT_VECTORS_EQUAL( expected_grad, grad[i], 1e-6 ); Matrix3D o; o.outer_product( grad1[i], grad2[i] ); Matrix3D expect = o + transpose(o); expect += value2 * hess1[i]; expect += value1 * hess2[i]; CPPUNIT_ASSERT_MATRICES_EQUAL( expect, Matrix3D(hess[i]), 1e-6 ); } }
void PMeanPMetricTest::test_hessian() { MsqError err; FauxMetric m; ElementPMeanP m1( 1.0, &m ); ElementPMeanP m2( 2.0, &m ); // get vertices for later std::vector<size_t> vertices; pd.element_by_index(0).get_vertex_indices( vertices ); // evaluate gradient double v1, v2, v3, v4; std::vector<size_t> indices1, indices2, indices3, indices4, tmpi; std::vector<Vector3D> grad1, grad2, grad3, grad4; std::vector<Matrix3D> hess3, hess4; m1.evaluate_with_gradient( pd, 0, v1, indices1, grad1, err ); CPPUNIT_ASSERT(!err); m2.evaluate_with_gradient( pd, 0, v2, indices2, grad2, err ); CPPUNIT_ASSERT(!err); // evaluate with Hessian m1.evaluate_with_Hessian( pd, 0, v3, indices3, grad3, hess3, err ); CPPUNIT_ASSERT(!err); m2.evaluate_with_Hessian( pd, 0, v4, indices4, grad4, hess4, err ); CPPUNIT_ASSERT(!err); // compare value and indices to eval w/out gradient CPPUNIT_ASSERT_DOUBLES_EQUAL( v1, v3, 1e-6 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( v2, v4, 1e-6 ); // It isn't a requirement that the index order remain the same // for both eval_with_grad and eval_with_Hess, but assuming it // does simplifies a lot of stuff in this test. Check that the // assumption remains valid. CPPUNIT_ASSERT( indices3 == indices1 ); CPPUNIT_ASSERT( indices4 == indices2 ); // It isn't a requirement that the index order remain the same // for any value of P, but assuming it does simplifies a lot // of stuff in this test, so check that the assumption is valid. CPPUNIT_ASSERT( indices1 == indices2 ); // check that gradient values match for (size_t i = 0; i < indices1.size(); ++i) { CPPUNIT_ASSERT_VECTORS_EQUAL( grad1[i], grad3[i], 1e-5 ); CPPUNIT_ASSERT_VECTORS_EQUAL( grad2[i], grad4[i], 1e-5 ); } // setup evaluation of underying metric std::vector<size_t> handles; m.get_element_evaluations( pd, 0, handles, err ); CPPUNIT_ASSERT(!err); // calculate expected Hessians std::vector<Vector3D> g; std::vector<Matrix3D> expected1, expected2, h; std::vector<Matrix3D>::iterator h_iter; const unsigned N = vertices.size(); expected1.resize( N*(N+1)/2, Matrix3D(0,0,0,0,0,0,0,0,0) ); expected2 = expected1; Matrix3D outer; for (unsigned i = 0; i < handles.size(); ++i) { double v; m.evaluate_with_Hessian( pd, handles[i], v, tmpi, g, h, err ); CPPUNIT_ASSERT(!err); h_iter = h.begin(); for (unsigned r = 0; r < tmpi.size(); ++r) { unsigned R = index_of( vertices, tmpi[r] ); CPPUNIT_ASSERT( R < N ); for (unsigned c = r; c < tmpi.size(); ++c ,++h_iter) { unsigned C = index_of( vertices, tmpi[c] ); CPPUNIT_ASSERT( C < N ); if (R <= C) { unsigned idx = N*R - R*(R+1)/2 + C; expected1[idx] += 1.0 / handles.size() * *h_iter; expected2[idx] += 2.0 * v / handles.size() * *h_iter; outer.outer_product( g[r], g[c] ); expected2[idx] += 2.0 / handles.size() * outer; } else { unsigned idx = N*C - C*(C+1)/2 + R; expected1[idx] += 1.0 / handles.size() * transpose(*h_iter); expected2[idx] += 2.0 * v / handles.size() * transpose(*h_iter); outer.outer_product( g[c], g[r] ); expected2[idx] += 2.0 / handles.size() * outer; } } } } // compare Hessians unsigned H_idx = 0; for (unsigned R = 0; R < vertices.size(); ++R) { if (vertices[R] >= pd.num_free_vertices()) continue; unsigned r = index_of( indices3, vertices[R] ); CPPUNIT_ASSERT(r < indices3.size() ); for (unsigned C = R; C < vertices.size(); ++C, ++H_idx) { if (vertices[C] >= pd.num_free_vertices()) continue; unsigned c = index_of( indices3, vertices[C] ); CPPUNIT_ASSERT( c < indices3.size() ); if (r <= c) { unsigned idx = indices3.size()*r - r*(r+1)/2 + c; CPPUNIT_ASSERT_MATRICES_EQUAL( expected1[H_idx], hess3[idx], 1e-5 ); CPPUNIT_ASSERT_MATRICES_EQUAL( expected2[H_idx], hess4[idx], 1e-5 ); } else { unsigned idx = indices3.size()*c - c*(c+1)/2 + r; CPPUNIT_ASSERT_MATRICES_EQUAL( transpose(expected1[H_idx]), hess3[idx], 1e-5 ); CPPUNIT_ASSERT_MATRICES_EQUAL( transpose(expected2[H_idx]), hess4[idx], 1e-5 ); } } } }
bool MultiplyQualityMetric::evaluate_with_Hessian( PatchData& pd, size_t handle, double& value, std::vector<size_t>& indices, std::vector<Vector3D>& gradient, std::vector<Matrix3D>& Hessian, MsqError& err ) { std::vector<size_t>::iterator i; size_t j, r, c, n, h; double val1, val2; bool rval1, rval2; rval1 = metric1.evaluate_with_Hessian( pd, handle, val1, indices1, grad1, Hess1, err ); MSQ_ERRZERO(err); rval2 = metric2.evaluate_with_Hessian( pd, handle, val2, indices2, grad2, Hess2, err ); MSQ_ERRZERO(err); // merge index lists indices.resize( indices1.size() + indices2.size() ); i = std::copy( indices1.begin(), indices1.end(), indices.begin() ); std::copy( indices2.begin(), indices2.end(), i ); std::sort( indices.begin(), indices.end() ); indices.erase( std::unique( indices.begin(), indices.end() ), indices.end() ); // calculate grads and convert index lists to indices into output list gradient.clear(); gradient.resize( indices.size(), Vector3D(0.0) ); for (j = 0; j < indices1.size(); ++j) { i = std::lower_bound( indices.begin(), indices.end(), indices1[j] ); indices1[j] = i - indices.begin(); gradient[indices1[j]] += val2 * grad1[j]; } for (j = 0; j < indices2.size(); ++j) { i = std::lower_bound( indices.begin(), indices.end(), indices2[j] ); indices2[j] = i - indices.begin(); gradient[indices2[j]] += val1 * grad2[j]; } // allocate space for hessians, and zero it const size_t N = indices.size(); Hessian.clear(); Hessian.resize( N * (N+1) / 2, Matrix3D(0.0) ); // add hessian terms from first metric n = indices1.size(); h = 0; for (r = 0; r < n; ++r) { const size_t nr = indices1[r]; for (c = r; c < n; ++c) { const size_t nc = indices1[c]; Hess1[h] *= val2; if (nr <= nc) Hessian[N*nr - nr*(nr+1)/2 + nc] += Hess1[h]; else Hessian[N*nc - nc*(nc+1)/2 + nr].plus_transpose_equal( Hess1[h] ); ++h; } } // add hessian terms from second metric n = indices2.size(); h = 0; for (r = 0; r < n; ++r) { const size_t nr = indices2[r]; for (c = r; c < n; ++c) { const size_t nc = indices2[c]; Hess2[h] *= val1; if (nr <= nc) Hessian[N*nr - nr*(nr+1)/2 + nc] += Hess2[h]; else Hessian[N*nc - nc*(nc+1)/2 + nr].plus_transpose_equal( Hess2[h] ); ++h; } } // add gradient outer products n = indices1.size(); size_t m = indices2.size(); Matrix3D outer; for (r = 0; r < n; ++r) { const size_t nr = indices1[r]; for (c = 0; c < m; ++c) { const size_t nc = indices2[c]; outer.outer_product( grad1[r], grad2[c] ); if (nr == nc) Hessian[N*nr - nr*(nr+1)/2 + nc] += outer.plus_transpose_equal(outer); else if (nr < nc) Hessian[N*nr - nr*(nr+1)/2 + nc] += outer; else Hessian[N*nc - nc*(nc+1)/2 + nr].plus_transpose_equal(outer); } } value = val1 * val2; return rval1 && rval2; }
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; }