void CompositeOFTest::test_composite_clone( ObjectiveFunction& OF ) { // save current count of instances of underlying OFs for later const int init_count = FauxObjectiveFunction::get_instance_count(); // clone the objective function ObjectiveFunction* clone = OF.clone(); // check that the underlying OFs were also cloned CPPUNIT_ASSERT( init_count < FauxObjectiveFunction::get_instance_count() ); // check that the value is the same MsqPrintError err(cout); double orig_val, clone_val; bool rval; rval = OF.evaluate( ObjectiveFunction::CALCULATE, patch(), orig_val, false, err ); ASSERT_NO_ERROR(err); CPPUNIT_ASSERT(rval); rval = clone->evaluate( ObjectiveFunction::CALCULATE, patch(), clone_val, false, err ); ASSERT_NO_ERROR(err); CPPUNIT_ASSERT(rval); CPPUNIT_ASSERT_DOUBLES_EQUAL( orig_val, clone_val, 1e-6 ); // check that cloned instances of underlying OFs are deleted delete clone; CPPUNIT_ASSERT_EQUAL( init_count, FauxObjectiveFunction::get_instance_count() ); }
// Gradient computations double DenseCRF::gradient( int n_iterations, const ObjectiveFunction & objective, VectorXf * unary_grad, VectorXf * lbl_cmp_grad, VectorXf * kernel_grad) const { // Run inference std::vector< MatrixXf > Q(n_iterations+1); MatrixXf tmp1, unary( M_, N_ ), tmp2; unary.fill(0); if( unary_ ) unary = unary_->get(); expAndNormalize( Q[0], -unary ); for( int it=0; it<n_iterations; it++ ) { tmp1 = -unary; for( unsigned int k=0; k<pairwise_.size(); k++ ) { pairwise_[k]->apply( tmp2, Q[it] ); tmp1 -= tmp2; } expAndNormalize( Q[it+1], tmp1 ); } // Compute the objective value MatrixXf b( M_, N_ ); double r = objective.evaluate( b, Q[n_iterations] ); sumAndNormalize( b, b, Q[n_iterations] ); // Compute the gradient if(unary_grad && unary_) *unary_grad = unary_->gradient( b ); if( lbl_cmp_grad ) *lbl_cmp_grad = 0*labelCompatibilityParameters(); if( kernel_grad ) *kernel_grad = 0*kernelParameters(); for( int it=n_iterations-1; it>=0; it-- ) { // Do the inverse message passing tmp1.fill(0); int ip = 0, ik = 0; // Add up all pairwise potentials for( unsigned int k=0; k<pairwise_.size(); k++ ) { // Compute the pairwise gradient expression if( lbl_cmp_grad ) { VectorXf pg = pairwise_[k]->gradient( b, Q[it] ); lbl_cmp_grad->segment( ip, pg.rows() ) += pg; ip += pg.rows(); } // Compute the kernel gradient expression if( kernel_grad ) { VectorXf pg = pairwise_[k]->kernelGradient( b, Q[it] ); kernel_grad->segment( ik, pg.rows() ) += pg; ik += pg.rows(); } // Compute the new b pairwise_[k]->applyTranspose( tmp2, b ); tmp1 += tmp2; } sumAndNormalize( b, tmp1.array()*Q[it].array(), Q[it] ); // Add the gradient if(unary_grad && unary_) *unary_grad += unary_->gradient( b ); } return r; }
void CompositeOFTest::test_eval_fails( ObjectiveFunction& OF ) { MsqError err; double value; OF.evaluate( ObjectiveFunction::CALCULATE, patch(), value, false, err ); CPPUNIT_ASSERT_EQUAL(MsqError::INTERNAL_ERROR, err.error_code()); }
void UnOptimizer::optimize_vertex_positions( PatchData &pd, MsqError &err) { assert( pd.num_free_vertices() == 1 && pd.vertex_by_index(0).is_free_vertex() ); std::vector<Vector3D> grad(1); double val, junk, coeff; bool state; state = objectiveFunction->evaluate_with_gradient( ObjectiveFunction::CALCULATE, pd, val, grad, err ); MSQ_ERRRTN(err); if (!state) { MSQ_SETERR(err)(MsqError::INVALID_MESH); return; } grad[0] /= grad[0].length(); PatchDataVerticesMemento* memento = pd.create_vertices_memento( err ); MSQ_ERRRTN(err); std::auto_ptr<PatchDataVerticesMemento> deleter( memento ); pd.get_minmax_edge_length( junk, coeff ); for (int i = 0; i < 100; ++i) { pd.set_free_vertices_constrained( memento, &grad[0], 1, coeff, err ); MSQ_ERRRTN(err); state = objectiveFunction->evaluate( ObjectiveFunction::CALCULATE, pd, val, true, err ); MSQ_ERRRTN(err); if (state) break; coeff *= 0.5; } if (!state) { pd.set_to_vertices_memento( memento, err ); } }
/*! Numerically Calculates the gradient of the ObjectiveFunction for the free vertices in the patch. Returns 'false' if the patch is outside of a required feasible region, returns 'ture' otherwise. The behavior of the function depends on the value of the boolean useLocalGradient. If useLocalGradient is set to 'true', compute_numerical_gradient creates a sub-patch around a free vertex, and then perturbs that vertex in one of the coordinate directions. Only the ObjectiveFunction value on the local sub-patch is used in the computation of the gradient. Therefore, useLocalGradient should only be set to 'true' for ObjectiveFunctions which can use this method. Unless the concrete ObjectiveFunction sets useLocalGradient to 'true' in its constructor, the value will be 'false'. In this case, the objective function value for the entire patch is used in the calculation of the gradient. This is computationally expensive, but it is numerically correct for all (C_1) functions. \param pd PatchData on which the gradient is taken. \param grad Array of Vector3D of length the number of vertices used to store gradient. \param OF_val will be set to the objective function value. */ bool ObjectiveFunction::evaluate_with_gradient( EvalType eval_type, PatchData &pd, double& OF_val, std::vector<Vector3D>& grad, MsqError &err ) { bool b; grad.resize( pd.num_free_vertices() ); // Fast path for single-free-vertex patch if (pd.num_free_vertices() == 1) { const EvalType sub_type = (eval_type == CALCULATE) ? CALCULATE : TEMPORARY; b = compute_subpatch_numerical_gradient( eval_type, sub_type, pd, OF_val, grad[0], err ); return !MSQ_CHKERR(err) && b; } ObjectiveFunction* of = this; std::auto_ptr<ObjectiveFunction> deleter; if (eval_type == CALCULATE) { of->clear(); b = of->evaluate( ACCUMULATE, pd, OF_val, OF_FREE_EVALS_ONLY, err ); if (err) { // OF doesn't support BCD type evals, try slow method err.clear(); of->clear(); b = compute_patch_numerical_gradient( CALCULATE, CALCULATE, pd, OF_val, grad, err ); return !MSQ_CHKERR(err) && b; } else if (!b) return b; } else { b = this->evaluate( eval_type, pd, OF_val, OF_FREE_EVALS_ONLY, err ); if (MSQ_CHKERR(err) || !b) return false; of = this->clone(); deleter = std::auto_ptr<ObjectiveFunction>(of); } // Determine number of layers of adjacent elements based on metric type. unsigned layers = min_patch_layers(); // Create a subpatch for each free vertex and use it to evaluate the // gradient for that vertex. double flocal; PatchData subpatch; for (size_t i = 0; i < pd.num_free_vertices(); ++i) { pd.get_subpatch( i, layers, subpatch, err ); MSQ_ERRZERO(err); b = of->compute_subpatch_numerical_gradient( SAVE, TEMPORARY, subpatch, flocal, grad[i], err ); if (MSQ_CHKERR(err) || !b) { of->clear(); return false; } } of->clear(); return true; }
void CompositeOFTest::test_invalid_eval( ObjectiveFunction& OF ) { MsqPrintError err(cout); bool rval; double value; rval = OF.evaluate( ObjectiveFunction::CALCULATE, patch(), value, false, err ); ASSERT_NO_ERROR(err); CPPUNIT_ASSERT(rval == false); }
void CompositeOFTest::test_evaluate( double expected, ObjectiveFunction& OF ) { MsqPrintError err(cout); double value; bool rval = OF.evaluate( ObjectiveFunction::CALCULATE, patch(), value, false, err ); ASSERT_NO_ERROR(err); CPPUNIT_ASSERT(rval); CPPUNIT_ASSERT_DOUBLES_EQUAL( expected, value, 1e-6 ); }