void QuasiNewton::optimize_vertex_positions( PatchData& pd, MsqError& err ) { TerminationCriterion& term = *get_inner_termination_criterion(); OFEvaluator& func = get_objective_function_evaluator(); const double sigma = 1e-4; const double beta0 = 0.25; const double beta1 = 0.80; const double tol1 = 1e-8; const double epsilon = 1e-10; double norm_r; //, norm_g; double alpha, beta; double obj, objn; size_t i; // Initialize stuff const size_t nn = pd.num_free_vertices(); double a[QNVEC], b[QNVEC], r[QNVEC]; for (i = 0; i < QNVEC; ++i) r[i] = 0; for (i = 0; i <= QNVEC; ++i) { v[i].clear(); v[i].resize( nn, Vector3D(0.0) ); w[i].clear(); w[i].resize( nn, Vector3D(0.0) ); } d.resize( nn ); mHess.resize( nn ); //hMesh(mesh); bool valid = func.update( pd, obj, v[QNVEC], mHess, err ); MSQ_ERRRTN(err); if (!valid) { MSQ_SETERR(err)("Initial objective function is not valid", MsqError::INVALID_MESH); return; } while (!term.terminate()) { pd.recreate_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); pd.get_free_vertex_coordinates( w[QNVEC] ); x = v[QNVEC]; for (i = QNVEC; i--; ) { a[i] = r[i] * inner( &(w[i][0]), arrptr(x), nn ); plus_eq_scaled( arrptr(x), -a[i], &v[i][0], nn ); } solve( arrptr(d), arrptr(x) ); for (i = QNVEC; i--; ) { b[i] = r[i] * inner( &(v[i][0]), arrptr(d), nn ); plus_eq_scaled( arrptr(d), a[i]-b[i], &(w[i][0]), nn ); } alpha = -inner( &(v[QNVEC][0]), arrptr(d), nn ); /* direction is negated */ if (alpha > 0.0) { MSQ_SETERR(err)("No descent.", MsqError::INVALID_MESH); return; } alpha *= sigma; beta = 1.0; pd.move_free_vertices_constrained( arrptr(d), nn, -beta, err ); MSQ_ERRRTN(err); valid = func.evaluate( pd, objn, v[QNVEC], err ); if (err.error_code() == err.BARRIER_VIOLATED) err.clear(); // barrier violated does not represent an actual error here MSQ_ERRRTN(err); if (!valid || (obj - objn < -alpha*beta - epsilon && length( &(v[QNVEC][0]), nn ) >= tol1)) { if (!valid) // function not defined at trial point beta *= beta0; else // unacceptable iterate beta *= beta1; for (;;) { if (beta < tol1) { pd.set_to_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); MSQ_SETERR(err)("Newton step not good", MsqError::INTERNAL_ERROR); return; } pd.set_free_vertices_constrained( mMemento, arrptr(d), nn, -beta, err ); MSQ_ERRRTN(err); valid = func.evaluate( pd, objn, err ); if (err.error_code() == err.BARRIER_VIOLATED) err.clear(); // barrier violated does not represent an actual error here MSQ_ERRRTN(err); if (!valid) // function undefined at trial point beta *= beta0; else if (obj - objn < -alpha*beta - epsilon) // unacceptlable iterate beta *= beta1; else break; } } for (i = 0; i < QNVEC-1; ++i) { r[i] = r[i+1]; w[i].swap( w[i+1] ); v[i].swap( v[i+1] ); } w[QNVEC-1].swap( w[0] ); v[QNVEC-1].swap( v[0] ); func.update( pd, obj, v[QNVEC], mHess, err ); MSQ_ERRRTN(err); norm_r = length_squared( &(v[QNVEC][0]), nn ); //norm_g = sqrt(norm_r); // checks stopping criterion term.accumulate_patch( pd, err ); MSQ_ERRRTN(err); term.accumulate_inner( pd, objn, &v[QNVEC][0], err ); MSQ_ERRRTN(err); } }
void TrustRegion::optimize_vertex_positions( PatchData& pd, MsqError& err ) { TerminationCriterion& term = *get_inner_termination_criterion(); OFEvaluator& func = get_objective_function_evaluator(); const double cg_tol = 1e-2; const double eta_1 = 0.01; const double eta_2 = 0.90; const double tr_incr = 10; const double tr_decr_def = 0.25; const double tr_decr_undef = 0.25; const double tr_num_tol = 1e-6; const int max_cg_iter = 10000; double radius = 1000; /* delta*delta */ const int nn = pd.num_free_vertices(); wVect.resize(nn); Vector3D* w = arrptr(wVect); zVect.resize(nn); Vector3D* z = arrptr(zVect); dVect.resize(nn); Vector3D* d = arrptr(dVect); pVect.resize(nn); Vector3D* p = arrptr(pVect); rVect.resize(nn); Vector3D* r = arrptr(rVect); double norm_r, norm_g; double alpha, beta, kappa; double rz, rzm1; double dMp, norm_d, norm_dp1, norm_p; double obj, objn; int cg_iter; bool valid; mHess.initialize( pd, err ); //hMesh(mesh); valid = func.update( pd, obj, mGrad, mHess, err ); MSQ_ERRRTN(err); if (!valid) { MSQ_SETERR(err)("Initial objective function is not valid", MsqError::INVALID_MESH); return; } compute_preconditioner( err ); MSQ_ERRRTN(err); pd.recreate_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); while (!term.terminate() && (radius > 1e-20)) { norm_r = length_squared(arrptr(mGrad), nn); norm_g = sqrt(norm_r); memset(d, 0, 3*sizeof(double)*nn); memcpy(r, arrptr(mGrad), nn*sizeof(Vector3D)); //memcpy(r, mesh->g, 3*sizeof(double)*nn); norm_g *= cg_tol; apply_preconditioner( z, r, err); MSQ_ERRRTN(err); //prec->apply(z, r, prec, mesh); negate(p, z, nn); rz = inner(r, z, nn); dMp = 0; norm_p = rz; norm_d = 0; cg_iter = 0; while ((sqrt(norm_r) > norm_g) && #ifdef DO_STEEP_DESC (norm_d > tr_num_tol) && #endif (cg_iter < max_cg_iter)) { ++cg_iter; memset(w, 0, 3*sizeof(double)*nn); //matmul(w, mHess, p); //matmul(w, mesh, p); mHess.product( w, p ); kappa = inner(p, w, nn); if (kappa <= 0.0) { alpha = (sqrt(dMp*dMp+norm_p*(radius-norm_d))-dMp)/norm_p; plus_eq_scaled( d, alpha, p, nn ); break; } alpha = rz / kappa; norm_dp1 = norm_d + 2.0*alpha*dMp + alpha*alpha*norm_p; if (norm_dp1 >= radius) { alpha = (sqrt(dMp*dMp+norm_p*(radius-norm_d))-dMp)/norm_p; plus_eq_scaled( d, alpha, p, nn ); break; } plus_eq_scaled( d, alpha, p, nn ); plus_eq_scaled( r, alpha, w, nn ); norm_r = length_squared(r, nn); apply_preconditioner( z, r, err); MSQ_ERRRTN(err); //prec->apply(z, r, prec, mesh); rzm1 = rz; rz = inner(r, z, nn); beta = rz / rzm1; times_eq_minus( p, beta, z, nn ); dMp = beta*(dMp + alpha*norm_p); norm_p = rz + beta*beta*norm_p; norm_d = norm_dp1; } #ifdef DO_STEEP_DESC if (norm_d <= tr_num_tol) { norm_g = length(arrptr(mGrad), nn); double ll = 1.0; if (norm_g < tr_num_tol) break; if (norm_g > radius) ll = radius / nurm_g; for (int i = 0; i < nn; ++i) d[i] = ll * mGrad[i]; } #endif alpha = inner( arrptr(mGrad), d, nn ); // inner(mesh->g, d, nn); memset(p, 0, 3*sizeof(double)*nn); //matmul(p, mHess, d); //matmul(p, mesh, d); mHess.product( p, d ); beta = 0.5*inner(p, d, nn); kappa = alpha + beta; /* Put the new point into the locations */ pd.move_free_vertices_constrained( d, nn, 1.0, err ); MSQ_ERRRTN(err); valid = func.evaluate( pd, objn, err ); MSQ_ERRRTN(err); if (!valid) { /* Function not defined at trial point */ radius *= tr_decr_undef; pd.set_to_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); continue; } if ((fabs(kappa) <= tr_num_tol) && (fabs(objn - obj) <= tr_num_tol)) { kappa = 1; } else { kappa = (objn - obj) / kappa; } if (kappa < eta_1) { /* Iterate is unacceptable */ radius *= tr_decr_def; pd.set_to_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); continue; } /* Iterate is acceptable */ if (kappa >= eta_2) { /* Iterate is a very good step, increase radius */ radius *= tr_incr; if (radius > 1e20) { radius = 1e20; } } func.update( pd, obj, mGrad, mHess, err ); compute_preconditioner( err ); MSQ_ERRRTN(err); pd.recreate_vertices_memento( mMemento, err ); MSQ_ERRRTN(err); // checks stopping criterion term.accumulate_patch( pd, err ); MSQ_ERRRTN(err); term.accumulate_inner( pd, objn, arrptr(mGrad), err ); MSQ_ERRRTN(err); } }