Пример #1
0
void TagVertexMesh::initialize( Mesh* mesh, std::string name, MsqError& err )
{
  MeshDecorator::set_mesh( mesh );
  tagName = name;

  tagHandle = get_mesh()->tag_get( tagName, err );
    // If tag isn't defined yet, we're done for now.
  if (err.error_code() == MsqError::TAG_NOT_FOUND) {
    err.clear();
    return;
  } 
  else if (MSQ_CHKERR(err))
    return;
  
    // If tag is already defined, make sure it is the correct type.
  std::string t_name;
  Mesh::TagType type;
  unsigned length;
  tag_properties( tagHandle, t_name, type, length, err ); 
  MSQ_ERRRTN(err);
  if (!(type == Mesh::DOUBLE && length == 3) &&
      !(type == Mesh::BYTE && length == 3*sizeof(double)))
    MSQ_SETERR(err)(MsqError::TAG_ALREADY_EXISTS,
                    "Tag \"%s\" has invalid type or size.",
                    tagName.c_str());
 
    // If tag is already defined and init was true, reset tag
    // values.
  haveTagHandle = true;
}
Пример #2
0
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());
}
double 
NonGradient::evaluate( double *point,  PatchData &pd, MsqError &err )
{
  if( pd.num_free_vertices() > 1 )
  {
    MSQ_SETERR(err)("Only one free vertex per patch implemented", MsqError::NOT_IMPLEMENTED);
  }
  const size_t vertexIndex = 0; 
  Vector3D originalVec = pd.vertex_by_index(vertexIndex);
  Vector3D pointVec;
  for( int index = 0; index<3; index++)
  {
    pointVec[index] = point[index];
  }
  pd.set_vertex_coordinates( pointVec, vertexIndex, err ); 
  pd.snap_vertex_to_domain( vertexIndex, err );  

  OFEvaluator& obj_func = get_objective_function_evaluator();
  TerminationCriterion* term_crit=get_inner_termination_criterion();

  double value;
  bool feasible = obj_func.evaluate( pd, value, err ); // MSQ_ERRZERO(err);
  term_crit->accumulate_inner( pd, value, 0, err );  //MSQ_CHKERR(err);
  if (err.error_code() == err.BARRIER_VIOLATED)
    err.clear();   // barrier violation not an error here
  MSQ_ERRZERO(err);
  pd.set_vertex_coordinates( originalVec, vertexIndex, err ); 
  if( !feasible && mUseExactPenaltyFunction )  
  { // "value" undefined btw
    double ensureFiniteRtol= .25;
    value = DBL_MAX * ensureFiniteRtol;
    //std::cout << " Infeasible patch: " << value << std::endl; printPatch( pd, err );
  }
  return value;
}
Пример #4
0
TagHandle TargetWriter::get_tag_handle( const std::string& base_name,
                                        unsigned num_dbl, Mesh* mesh, MsqError& err )
{
  std::ostringstream sstr;
  sstr << base_name << num_dbl;
  
  TagHandle handle = mesh->tag_get( sstr.str().c_str(), err );
  if (!MSQ_CHKERR(err))
  {
    std::string temp_name;
    Mesh::TagType temp_type;
    unsigned temp_length;
    mesh->tag_properties( handle, temp_name, temp_type, temp_length, err );
    MSQ_ERRZERO(err);
    
    if (temp_type != Mesh::DOUBLE || temp_length != num_dbl)
    {
      MSQ_SETERR(err)( MsqError::TAG_ALREADY_EXISTS,
                      "Mismatched type or length for existing tag \"%s\"",
                       sstr.str().c_str() );
    }
  }
  else if (err.error_code() == MsqError::TAG_NOT_FOUND)
  {
    err.clear();
    handle = mesh->tag_create( sstr.str().c_str(), Mesh::DOUBLE, num_dbl, 0, err );
    MSQ_ERRZERO(err);
  }
  return handle;
}
void LinearMappingFunctionTest::test_deriv_fail( MappingFunction3D& mf, unsigned subdim )
{
    // make sure it fails if called
  MsqError err;
  MsqVector<3> coeff[100];
  size_t verts[100], num_coeff;
  mf.derivatives( Sample(subdim, 0), NodeSet(), verts, coeff, num_coeff, err );
  CPPUNIT_ASSERT(err);
  err.clear();
}
void LinearMappingFunctionTest::test_coeff_fail( MappingFunction& mf, unsigned subdim )
{
    // make sure it fails if called
  MsqError err;
  double coeff[100];
  size_t num_coeff, indices[100];
  mf.coefficients( Sample(subdim, 0), NodeSet(), coeff, indices, num_coeff, err );
  CPPUNIT_ASSERT(err);
  err.clear();
}  
void LinearMappingFunctionTest::do_coeff_test( MappingFunction& mf, 
                                               unsigned subdim,
                                               map_func mf2,
                                               unsigned count,
                                               double* xi )
{
    // make sure it fails if passed a nonlinear element
  MsqError err;
  double coeff[100];
  size_t indices[100];
  size_t num_coeff = 100;
  NodeSet tmp_set;
  tmp_set.set_mid_edge_node(1);
  mf.coefficients( Sample(0, 1), tmp_set, coeff, indices, num_coeff, err );
  CPPUNIT_ASSERT(err);
  err.clear();
  
    // get number of vertices in element
  const unsigned n = TopologyInfo::corners( mf.element_topology() );
  const unsigned d = TopologyInfo::dimension( mf.element_topology() );
  
    // compare coefficients at each location
  vector<double> comp(n);
  for (unsigned i = 0; i < count; ++i)
  {
    num_coeff = 101;
    mf.coefficients( Sample(subdim, i), NodeSet(), coeff, indices, num_coeff, err );
    CPPUNIT_ASSERT(!err);
    
    mf2( xi, &comp[0] );
    string xi_str;
    for (unsigned j = 0; j < d; ++j) {
      xi_str += !j ? "(" : ", ";
      xi_str += dtostr(xi[j]);
    }
    xi_str += ")";
    xi += d;
    
    for (unsigned j = 0; j < n; ++j)
    {
      double mf_val = 0.0;
      size_t idx = std::find( indices, indices+num_coeff, j ) - indices;
      if (idx < num_coeff)
	mf_val = coeff[idx];

      CppUnit::Message message( "Coefficients do not match." );
      message.addDetail( string("Entity:             ") + itostr( i ) );
      message.addDetail( string("Coefficient number: ") + itostr( j ) );
      message.addDetail( string("Xi:             ") + xi_str );
      message.addDetail( string("Expected value: ") + dtostr( comp[j] ) );
      message.addDetail( string("Actual value:   ") + dtostr( mf_val ) );
      ASSERT_MESSAGE( message, fabs(comp[j]-mf_val) < DBL_EPSILON );
    }
  }
}
Пример #8
0
size_t MeshImplTags::create( const TagDescription& desc,
                             const void* defval,
                             MsqError& err )
{
  size_t h = handle( desc.name.c_str(), err );
  if (h)
  {
    MSQ_SETERR(err)(desc.name.c_str(), MsqError::TAG_ALREADY_EXISTS);
    return 0;
  }
  
  err.clear();
  if (desc.size == 0 || (desc.size % size_from_tag_type(desc.type)) != 0)
  {
    MSQ_SETERR(err)(MsqError::INVALID_ARG);
    return 0;
  }
  
  TagData* tag = new TagData( desc );
  h = tagList.size();
  tagList.push_back(tag);
  
  if (defval)
  {
    tag->defaultValue = malloc( tag->desc.size );
    memcpy( tag->defaultValue, defval, tag->desc.size );
  }
  
  return h+1;
}
Пример #9
0
void SlaveBoundaryVerticesTest::test_fail_if_slaves_not_calculated()
{
  MsqError err;

  Settings settings;
  settings.set_slaved_ho_node_mode( Settings::SLAVE_ALL );
  SlaveBoundaryVertices tool( 1 );
  
  MeshImpl mesh;
  DomainClassifier domain;
  make_mesh( mesh, domain, 2 );
  
  tool.loop_over_mesh( &mesh, &domain, &settings, err );
  CPPUNIT_ASSERT(err);
  err.clear();
}
Пример #10
0
/*! 
  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;
}
Пример #11
0
void ObjectiveFunctionTests::test_clone( ObjectiveFunctionTemplate* of )
{
  const double some_vals[] = { 1, 2, 3, 4, 5, 6, 0.5 };
  const unsigned num_vals = sizeof(some_vals)/sizeof(some_vals[0]);
  OFTestQM metric( some_vals, num_vals );
  of->set_quality_metric(&metric);
  
    // test that we get the same value from both
  auto_ptr<ObjectiveFunction> of2( of->clone() );
  double exp_val, val;
  exp_val = evaluate_internal( ObjectiveFunction::CALCULATE, EVAL, of );
  val = evaluate_internal( ObjectiveFunction::CALCULATE, EVAL, of2.get() );
  CPPUNIT_ASSERT_DOUBLES_EQUAL( exp_val, val, 1e-12 );
  
    // check if OF supports BCD -- if not then done
  MsqError err;
  of->evaluate( ObjectiveFunction::UPDATE, patch(), val, false, err );
  if (err) {
    err.clear();
    return;
  }
  
    // build up some saved state in the objective function
  of->clear();
  const double vals1[] = { 1.0, 3.0, 4.0, 8.0 };
  const size_t vals1_len = sizeof(vals1)/sizeof(vals1[0]);
  const double vals2[] = { 21.5, 11.1, 30.0, 0.5 };
  const size_t vals2_len = sizeof(vals2)/sizeof(vals2[0]);
  metric.set_values( vals1, vals1_len );
  evaluate_internal( ObjectiveFunction::SAVE, EVAL, of );
  metric.append_values( vals2, vals2_len );
  evaluate_internal( ObjectiveFunction::ACCUMULATE, EVAL, of );
  
    // check that clone has same accumulated data
  of2 = auto_ptr<ObjectiveFunction>(of->clone());
  metric.set_values( some_vals, num_vals );
  exp_val = evaluate_internal( ObjectiveFunction::UPDATE, EVAL, of );
  val = evaluate_internal( ObjectiveFunction::UPDATE, EVAL, of2.get() );
  CPPUNIT_ASSERT_DOUBLES_EQUAL( exp_val, val, 1e-12 );
}
/*!Reset function using using a PatchData object.  This function is
  called for the inner-stopping criterion directly from the
  loop over mesh function in VertexMover.  For outer criterion,
  it is called from the reset function which takes a MeshSet object.
  This function prepares the object to be used by setting the initial
  values of some of the data members.  As examples, if needed, it resets
  the cpu timer to zero, the iteration counter to zero, and the
  initial and previous objective function values to the current
  objective function value for this patch.
  The return value for this function is similar to that of terminate().
  The function returns false if the checked criteria have not been
  satisfied, and true if they have been.  reset() only checks the
  GRADIENT_INF_NORM_ABSOLUTE, GRADIENT_L2_NORM_ABSOLUTE, and the
  QUALITY_IMPROVEMENT_ABSOLUTE criteria.  Checking these criteria
  allows the QualityImprover to skip the entire optimization if
  the initial mesh satisfies the appropriate conditions.
 */
void TerminationCriterion::reset_inner(PatchData &pd, OFEvaluator& obj_eval,
                                    MsqError &err)
{
  const unsigned long totalFlag = terminationCriterionFlag | cullingMethodFlag;
  
    // clear flag for BOUNDED_VERTEX_MOVEMENT
  vertexMovementExceedsBound = 0;
  
    // Use -1 to denote that this isn't initialized yet.
    // As all valid values must be >= 0.0, a negative
    // value indicates that it is uninitialized and is
    // always less than any valid value.
  maxSquaredMovement = -1;
  
    // Clear the iteration count.
  iterationCounter = 0;
  
    //reset the inner timer if needed
  if(totalFlag & CPU_TIME){
    mTimer.reset();
  }
   
    //GRADIENT
  currentGradInfNorm = initialGradInfNorm = 0.0;
  currentGradL2NormSquared = initialGradL2NormSquared = 0.0;
  if(totalFlag & GRAD_FLAGS)
  {
    if (!obj_eval.have_objective_function()) {
      MSQ_SETERR(err)("Error termination criteria set which uses objective "
                      "functions, but no objective function is available.",
                      MsqError::INVALID_STATE);   
      return;
    } 
    int num_vertices=pd.num_free_vertices();
    mGrad.resize( num_vertices );

      //get gradient and make sure it is valid
    bool b = obj_eval.evaluate(pd, currentOFValue, mGrad, err); MSQ_ERRRTN(err);
    if (!b) {
      MSQ_SETERR(err)("Initial patch is invalid for gradient computation.", 
                      MsqError::INVALID_STATE);
      return;
    } 

      //get the gradient norms
    if (totalFlag & (GRADIENT_INF_NORM_ABSOLUTE|GRADIENT_INF_NORM_RELATIVE))
    {
      currentGradInfNorm = initialGradInfNorm = Linf(mGrad);
      MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Initial gradient Inf norm: " << " "
                                     << RPM(initialGradInfNorm) << std::endl;
    }  
      
    if (totalFlag & (GRADIENT_L2_NORM_ABSOLUTE|GRADIENT_L2_NORM_RELATIVE))
    {
      currentGradL2NormSquared = initialGradL2NormSquared = length_squared(mGrad);
      MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Initial gradient L2 norm: " << " "
                                     << RPM(std::sqrt(initialGradL2NormSquared)) << std::endl;
    }  

      //the OFvalue comes for free, so save it
    previousOFValue=currentOFValue;
    initialOFValue=currentOFValue;
  }
  //find the initial objective function value if needed and not already
  //computed.  If we needed the gradient, we have the OF value for free.
  // Also, if possible, get initial OF value if writing plot file.  Solvers
  // often supply the OF value for subsequent iterations so by calculating
  // the initial value we can generate OF value plots.
  else if ((totalFlag & OF_FLAGS) || 
           (plotFile.is_open() && pd.num_free_vertices() && obj_eval.have_objective_function()))
  {
      //ensure the obj_ptr is not null
    if(!obj_eval.have_objective_function()){
      MSQ_SETERR(err)("Error termination criteria set which uses objective "
                      "functions, but no objective function is available.",
                      MsqError::INVALID_STATE);
      return;
    }
    
    bool b = obj_eval.evaluate(pd, currentOFValue, err); MSQ_ERRRTN(err);
    if (!b){
      MSQ_SETERR(err)("Initial patch is invalid for evaluation.",MsqError::INVALID_STATE);
      return;
    }
      //std::cout<<"\nReseting initial of value = "<<initialOFValue;
    previousOFValue=currentOFValue;
    initialOFValue=currentOFValue;
  }
  
  if (totalFlag & (GRAD_FLAGS|OF_FLAGS))
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Initial OF value: " << " " << RPM(initialOFValue) << std::endl;
  
    // Store current vertex locations now, because we'll
    // need them later to compare the current movement with.
  if (totalFlag & VERTEX_MOVEMENT_RELATIVE)
  {
    if (initialVerticesMemento)
    {
      pd.recreate_vertices_memento( initialVerticesMemento, err );
    }
    else
    {
      initialVerticesMemento = pd.create_vertices_memento( err );
    }
    MSQ_ERRRTN(err);
    maxSquaredInitialMovement = DBL_MAX;
  }
  else {
    maxSquaredInitialMovement = 0;
  }
  
  if (terminationCriterionFlag & UNTANGLED_MESH) {
    globalInvertedCount = count_inverted( pd, err );
    //if (innerOuterType==TYPE_OUTER) MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Num Inverted: " << " " << globalInvertedCount << std::endl;
    patchInvertedCount = 0;
    MSQ_ERRRTN(err);
  }

  if (timeStepFileType) {
      // If didn't already calculate gradient abive, calculate it now.
    if (!(totalFlag & GRAD_FLAGS)) {
      mGrad.resize( pd.num_free_vertices() );
      obj_eval.evaluate(pd, currentOFValue, mGrad, err);
      err.clear();
    }
    write_timestep( pd, mGrad.empty() ? 0 : arrptr(mGrad), err);
  }
    
  if (plotFile.is_open()) {
      // two newlines so GNU plot knows that we are starting a new data set
    plotFile << std::endl << std::endl;
      // write column headings as comment in data file
    plotFile << "#Iter\tCPU\tObjFunc\tGradL2\tGradInf\tMovement\tInverted" << std::endl;
      // write initial values
    plotFile << 0 
     << '\t' << mTimer.since_birth() 
     << '\t' << initialOFValue 
     << '\t' << std::sqrt( currentGradL2NormSquared ) 
     << '\t' << currentGradInfNorm 
     << '\t' << 0.0
     << '\t' << globalInvertedCount
     << std::endl;
  }
}
void SteepestDescent::optimize_vertex_positions(PatchData &pd, 
                                                MsqError &err)
{
  MSQ_FUNCTION_TIMER( "SteepestDescent::optimize_vertex_positions" );

  const int SEARCH_MAX = 100;
  const double c1 = 1e-4;
  //std::vector<Vector3D> unprojected(pd.num_free_vertices()); 
  std::vector<Vector3D> gradient(pd.num_free_vertices()); 
  bool feasible=true;//bool for OF values
  double min_edge_len, max_edge_len;
  double step_size=0, original_value=0, new_value=0;
  double norm_squared=0;
  PatchDataVerticesMemento* pd_previous_coords;
  TerminationCriterion* term_crit=get_inner_termination_criterion();
  OFEvaluator& obj_func = get_objective_function_evaluator();
  
    // get vertex memento so we can restore vertex coordinates for bad steps.
  pd_previous_coords = pd.create_vertices_memento( err ); MSQ_ERRRTN(err);
    // use auto_ptr to automatically delete memento when we exit this function
  std::auto_ptr<PatchDataVerticesMemento> memento_deleter( pd_previous_coords );

    // Evaluate objective function.
    //
    // Always use 'update' version when beginning optimization so that
    // if doing block coordinate descent the OF code knows the set of
    // vertices we are modifying during the optimziation (the subset
    // of the mesh contained in the current patch.)  This has to be
    // done up-front because typically an OF will just store the portion
    // of the OF value (e.g. the numeric contribution to the sum for an
    // averaging OF) for the initial patch.
  feasible = obj_func.update( pd, original_value, gradient, err ); MSQ_ERRRTN(err);
    // calculate gradient dotted with itself
  norm_squared = length_squared( gradient );
  
    //set an error if initial patch is invalid.
  if(!feasible){
    MSQ_SETERR(err)("SteepestDescent passed invalid initial patch.",
                    MsqError::INVALID_ARG);
    return;
  }

    // use edge length as an initial guess for for step size
  pd.get_minmax_edge_length( min_edge_len, max_edge_len );
  //step_size = max_edge_len / std::sqrt(norm_squared);
  //if (!finite(step_size))  // zero-length gradient
  //  return;
//  if (norm_squared < DBL_EPSILON)
//    return;
  if (norm_squared >= DBL_EPSILON)
    step_size = max_edge_len / std::sqrt(norm_squared) * pd.num_free_vertices();

    // The steepest descent loop...
    // We loop until the user-specified termination criteria are met.
  while (!term_crit->terminate()) {
    MSQ_DBGOUT(3) << "Iteration " << term_crit->get_iteration_count() << std::endl;
    MSQ_DBGOUT(3) << "  o  original_value: " << original_value << std::endl;
    MSQ_DBGOUT(3) << "  o  grad norm suqared: " << norm_squared << std::endl;

      // Save current vertex coords so that they can be restored if
      // the step was bad.
    pd.recreate_vertices_memento( pd_previous_coords, err ); MSQ_ERRRTN(err);

      // Reduce step size until it satisfies Armijo condition
    int counter = 0;
    for (;;) {
      if (++counter > SEARCH_MAX || step_size < DBL_EPSILON) {
        MSQ_DBGOUT(3) << "    o  No valid step found.  Giving Up." << std::endl;
        return;
      }
      
      // Move vertices to new positions.
      // Note: step direction is -gradient so we pass +gradient and 
      //       -step_size to achieve the same thing.
      pd.move_free_vertices_constrained( arrptr(gradient), gradient.size(), -step_size, err ); MSQ_ERRRTN(err);
      // Evaluate objective function for new vertices.  We call the
      // 'evaluate' form here because we aren't sure yet if we want to
      // keep these vertices.  Until we call 'update', we have the option
      // of reverting a block coordinate decent objective function's state
      // to that of the initial vertex coordinates.  However, for block
      // coordinate decent to work correctly, we will need to call an
      // 'update' form if we decide to keep the new vertex coordinates.
      feasible = obj_func.evaluate( pd, new_value, err ); 
      if (err.error_code() == err.BARRIER_VIOLATED) 
        err.clear();  // barrier violated does not represent an actual error here
      MSQ_ERRRTN(err);
      MSQ_DBGOUT(3) << "    o  step_size: " << step_size << std::endl;
      MSQ_DBGOUT(3) << "    o  new_value: " << new_value << std::endl;

      if (!feasible) {
        // OF value is invalid, decrease step_size a lot
        step_size *= 0.2;
      }
      else if (new_value > original_value - c1 * step_size * norm_squared) {
        // Armijo condition not met.
        step_size *= 0.5;
      }
      else {
        // Armijo condition met, stop
        break;
      }
      
      // undo previous step : restore vertex coordinates
      pd.set_to_vertices_memento( pd_previous_coords, err );  MSQ_ERRRTN(err);
    }
   
      // Re-evaluate objective function to get gradient.
      // Calling the 'update' form here incorporates the new vertex 
      // positions into the 'accumulated' value if we are doing a 
      // block coordinate descent optimization.
    obj_func.update(pd, original_value, gradient, err ); MSQ_ERRRTN(err);
    if (projectGradient) {
      //if (cosineStep) {
      //  unprojected = gradient;
      //  pd.project_gradient( gradient, err ); MSQ_ERRRTN(err);
      //  double dot = inner_product( arrptr(gradient), arrptr(unprojected), gradient.size() );
      //  double lensqr1 = length_squared( gradient );
      //  double lensqr2 = length_squared( unprojected );
      //  double cossqr = dot * dot / lensqr1 / lensqr2;
      //  step_size *= sqrt(cossqr);
      //}
      //else {
        pd.project_gradient( gradient, err ); MSQ_ERRRTN(err);
      //}      
    }
      
      // Update terination criterion for next iteration.
      // This is necessary for efficiency.  Some values can be adjusted
      // for each iteration so we don't need to re-caculate the value
      // over the entire mesh.
    term_crit->accumulate_patch( pd, err );  MSQ_ERRRTN(err);
    term_crit->accumulate_inner( pd, original_value, arrptr(gradient), err ); MSQ_ERRRTN(err); 
      
      // Calculate initial step size for next iteration using step size 
      // from this iteration
    step_size *= norm_squared;
    norm_squared = length_squared( gradient );
//    if (norm_squared < DBL_EPSILON)
//      break;
  if (norm_squared >= DBL_EPSILON)
    step_size /= norm_squared;
  }
}
Пример #14
0
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 );
        if (err.error_code() == err.BARRIER_VIOLATED)
            err.clear();  // barrier violated does not represent an actual error here
        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);
    }
}
Пример #15
0
void LinearMappingFunctionTest::do_deriv_test( MappingFunction2D& mf, 
                                               unsigned subdim,
                                               map_func mf2,
                                               unsigned count,
                                               double* xi )
{
    // make sure it fails if passed a nonlinear element
  MsqError err;
  MsqVector<2> derivs[100];
  size_t verts[100], num_vtx = 37;
  NodeSet tmp_set;
  tmp_set.set_mid_edge_node(1);
  mf.derivatives( Sample(subdim, 0), tmp_set, verts, derivs, num_vtx, err );
  CPPUNIT_ASSERT(err);
  err.clear();
  
    // get number of vertices in element
  const unsigned n = TopologyInfo::corners( mf.element_topology() );
  
    // compare coefficients at each location
  vector<double> comp(2*n);
  for (unsigned i = 0; i < count; ++i)
  {
    num_vtx = 33;
    mf.derivatives( Sample(subdim, i), NodeSet(), verts, derivs, num_vtx, err );
    CPPUNIT_ASSERT(!err);
    CPPUNIT_ASSERT( num_vtx > 0 );
    CPPUNIT_ASSERT( num_vtx <= n );
    
    mf2( xi, &comp[0] );
    string xi_str;
    for (unsigned j = 0; j < 2; ++j) {
      xi_str += !j ? "(" : ", ";
      xi_str += dtostr(xi[j]);
    }
    xi_str += ")";
    xi += 2;
    
    for (unsigned j = 0; j < num_vtx; ++j)
    {
      bool all_zero = true;
      for (unsigned k = 0; k < 2; ++k)
      {
        CppUnit::Message message( "Coefficient derivatives do not match." );
        message.addDetail( string("Entity:             ") + itostr( i ) );
        message.addDetail( string("Coefficient number: ") + itostr( j ) );
        message.addDetail( string("Xi:             ") + xi_str );
        message.addDetail( string("Axis:           ") + itostr( k ) );
        message.addDetail( string("Expected value: ") + dtostr( comp[2*verts[j]+k] ) );
        message.addDetail( string("Actual value:   ") + dtostr( derivs[j][k] ) );
        ASSERT_MESSAGE( message, fabs(comp[2*verts[j]+k]-derivs[j][k]) < DBL_EPSILON );
        if (fabs(derivs[j][k]) > DBL_EPSILON)
          all_zero = false;
      }

        // if vertex has all zero values, it shouldn't have been in the
        // vertex list at all, as the Jacobian will not depend on that vertex.
      CPPUNIT_ASSERT( !all_zero );
    }
    
      // If any vertex is not in the list, then its values must be zero.
    sort( verts, verts + num_vtx );
    for (unsigned j = 0; j < num_vtx; ++j) {
      if (!binary_search( verts, verts+num_vtx, j )) {
        for (unsigned k = 0; k < 2; ++k)
        {
          CppUnit::Message message( "Missing coefficient derivatives." );
          message.addDetail( string("Entity:              ") + itostr( i ) );
          message.addDetail( string("Coefficient number:  ") + itostr( j ) );
          message.addDetail( string("Axis:                ") + itostr( k ) );
          message.addDetail( string("Expected derivative: ") + dtostr( comp[2*j+k] ) );
          ASSERT_MESSAGE( message, fabs(comp[2*j+k]) < DBL_EPSILON );
        }
      }
    }
  }
}
Пример #16
0
int uwt( bool skip,
         UntangleWrapper::UntangleMetric metric,
         const char* input_file_base,
         int expected,
         bool flip_domain )
{
  if (skip)
    return 0;

  if (!brief_output)
    std::cout << std::endl << "**********************************************" << std::endl;
  std::cout << "Running \"" << input_file_base << "\" for " << tostr(metric) << std::endl;
  if (!brief_output)
    std::cout << "**********************************************" << std::endl << std::endl;

    // get mesh
  MsqError err;
  MeshImpl mesh;
  std::string input_file( VTK_2D_DIR );
  input_file += input_file_base;
  mesh.read_vtk( input_file.c_str(), err );
  if (err) {
    std::cerr << err << std::endl;
    std::cerr << "ERROR: " << input_file << " : failed to read file" << std::endl;
    return 1;
  }
    // get domain
  std::vector<Mesh::VertexHandle> verts;
  mesh.get_all_vertices( verts, err );
  if (err || verts.empty()) abort();
  MsqVertex coords;
  mesh.vertices_get_coordinates( arrptr(verts), &coords, 1, err );
  if (err) abort();
  Vector3D norm(0,0,flip_domain ? -1 : 1);
  PlanarDomain domain( norm, coords );
    // run wrapper
  UntangleWrapper wrapper( metric );
  wrapper.set_vertex_movement_limit_factor( 0.005 );
  double constant = (metric == UntangleWrapper::BETA) ? beta : mu_sigma;
  if (constant > 0)
    wrapper.set_metric_constant( constant );
  if (brief_output)
    wrapper.quality_assessor().disable_printing_results();
  MeshDomainAssoc mesh_and_domain = MeshDomainAssoc(&mesh, &domain);
  wrapper.run_instructions( &mesh_and_domain, err );
  if (err) {
    std::cerr << err << std::endl;
    std::cerr << "ERROR: optimization failed" << std::endl;
    return 1;
  }
    // write output file
  if (write_output) {
    std::string result_file(tostr(metric));
    result_file += "-";
    result_file += input_file_base;
    mesh.write_vtk( result_file.c_str(), err );
    if (err) {
      std::cerr << err << std::endl;
      std::cerr << "ERROR: " << result_file << " : failed to write file" << std::endl;
      err.clear();
    }
    else {
      std::cerr << "Wrote file: " << result_file << std::endl;
    }
  }
  
    // test number of inverted elements
  int count, junk;
  wrapper.quality_assessor().get_inverted_element_count( count, junk, err );
  if (err) abort();
  if (count < expected) {
    std::cout << "WARNING: expected " << expected 
              << " inverted elements but finished with only " 
              << count << std::endl
              << "Test needs to be updated?" << std::endl << std::endl;
    return 0;
  }
  else if (count == expected) {
    std::cout << "Completed with " << count << " inverted elements remaining" 
              << std::endl << std::endl;
    return 0;
  }
  else {
    std::cerr << "ERROR: expected " << expected 
              << " inverted elements but finished with " 
              << count << std::endl << std::endl;
    return 1;
  }
}
void Mesquite::PlanarDomain::fit_vertices( Mesh* mesh, MsqError& err, double epsilon )
{
    // Our goal here is to consider only the boundary (fixed) vertices
    // when calculating the plane.  If for some reason the user wants
    // to snap a not-quite-planar mesh to a plane during optimization, 
    // if possible we want to start with the plane containing the fixed
    // vertices, as those won't get snapped.  If no vertices are fixed,
    // then treat them all as fixed for the purpose calculating the plane
    // (consider them all.)

  std::vector<Mesh::VertexHandle> verts, fixed;
  mesh->get_all_vertices( verts, err ); MSQ_ERRRTN(err);
  DomainUtil::get_fixed_vertices( mesh, arrptr(verts), verts.size(), fixed, err ); MSQ_ERRRTN(err);
  
  bool do_all_verts = true;
  if (fixed.size() > 2) {
    do_all_verts = false;
    fit_vertices( mesh, arrptr(fixed), fixed.size(), err, epsilon );
    
      // if we failed with only the fixed vertices, try again with all of the 
      // vertices
    if (err) {
      err.clear();
      do_all_verts = true;
    }
  }
  
  if (do_all_verts) {
    fit_vertices( mesh, arrptr(verts), verts.size(), err, epsilon );
    MSQ_ERRRTN(err);
  }
  
    // now count inverted elements
  size_t inverted_count = 0, total_count = 0;
  std::vector<Mesh::ElementHandle> elems;
  std::vector<size_t> junk;
  std::vector<MsqVertex> coords;
  mesh->get_all_elements( elems, err ); MSQ_ERRRTN(err);
  for (size_t i = 0; i < elems.size(); ++i) {

    EntityTopology type;
    mesh->elements_get_topologies( &elems[i], &type, 1, err );
    if (TopologyInfo::dimension(type) != 2)
      continue;
    
    verts.clear();
    mesh->elements_get_attached_vertices( arrptr(elems), 1, verts, junk, err ); MSQ_ERRRTN(err);
    if (verts.size() < 3)
      continue;
    
    coords.resize( verts.size() );
    mesh->vertices_get_coordinates( arrptr(verts), arrptr(coords), 3, err ); MSQ_ERRRTN(err);
    Vector3D n = (coords[1] - coords[0]) * (coords[2] - coords[0]);
    ++total_count;
    if (n % mNormal < 0.0)
      ++inverted_count;
  }
  
    // if most elements are inverted, flip normal
  if (2*inverted_count > total_count)
    this->flip();
}
void FeasibleNewton::optimize_vertex_positions(PatchData &pd, 
                                               MsqError &err)
{
  MSQ_FUNCTION_TIMER( "FeasibleNewton::optimize_vertex_positions" );
  MSQ_DBGOUT(2) << "\no  Performing Feasible Newton optimization.\n";
  
  //
  // the only valid 2D meshes that FeasibleNewton works for are truly planar which 
  // lie in the X-Y coordinate plane.
  //

  XYPlanarDomain *xyPlanarDomainPtr = dynamic_cast<XYPlanarDomain*>(pd.get_domain());
    // only optimize if input mesh is a volume or an XYPlanarDomain
  if (!pd.domain_set() || xyPlanarDomainPtr != NULL)  
  {
    const double sigma   = 1e-4;
    const double beta0   = 0.25;
    const double beta1   = 0.80;
    const double tol1    = 1e-8;
    const double tol2    = 1e-12;
    const double epsilon = 1e-10;
    double original_value, new_value;
    double beta;
  
    int nv = pd.num_free_vertices();
    std::vector<Vector3D> grad(nv), d(nv);
    bool fn_bool=true;// bool used for determining validity of patch
  
    OFEvaluator& objFunc = get_objective_function_evaluator();

    int i;

    // TODD -- Don't blame the code for bad things happening when using a
    //         bad termination test or requesting more accuracy than is
    //	     possible.
    //
    //         Also, 

    // 1.  Allocate a hessian and calculate the sparsity pattern.
    mHessian.initialize(pd, err); MSQ_ERRRTN(err);
  
    // does the Feasible Newton iteration until stopping is required.
    // Terminate when inner termination criterion signals.

    /* Computes the value of the stopping criterion*/
    TerminationCriterion* term_crit=get_inner_termination_criterion();
    while ( !term_crit->terminate() ) {
      fn_bool = objFunc.update( pd, original_value, grad, mHessian,  err );
      MSQ_ERRRTN(err);
      if (!fn_bool) {
        MSQ_SETERR(err)("invalid patch for hessian calculation", MsqError::INTERNAL_ERROR);
        return; 
      }

      if (MSQ_DBG(3)) { // avoid expensive norm calculations if debug flag is off
        MSQ_DBGOUT(3) << "  o  objective function: " << original_value << std::endl;
        MSQ_DBGOUT(3) << "  o  gradient norm: " << length(grad) << std::endl;
        MSQ_DBGOUT(3) << "  o  Hessian norm: " << mHessian.norm() << std::endl;
      }
    
      // Prints out free vertices coordinates. 
      //
      // Comment out the following because it is way to verbose for larger
      // problems.  Consider doing:
      //  inner_term_crit->write_mesh_steps( "filename.vtk" );
      // instead.
      // - j.kraftcheck 2010-11-17
//      if (MSQ_DBG(3)) {
//        MSQ_DBGOUT(3) << "\n  o Free vertices ("<< pd.num_free_vertices()
//                  <<")original coordinates:\n ";
//        MSQ_ERRRTN(err);
//        const MsqVertex* toto1 = pd.get_vertex_array(err); MSQ_ERRRTN(err);
//        MsqFreeVertexIndexIterator ind1(pd, err); MSQ_ERRRTN(err);
//        ind1.reset();
//        while (ind1.next()) {
//          MSQ_DBGOUT(3) << "\t\t\t" << toto1[ind1.value()];
//        }
//      }
      
      // 4. Calculate a direction using preconditionned conjugate gradients
      //    to find a zero of the Newton system of equations (H*d = -g)
      //    (a) stop if conjugate iteration limit reached
      //    (b) stop if relative residual is small
      //    (c) stop if direction of negative curvature is obtained

      mHessian.cg_solver(arrptr(d), arrptr(grad), err); MSQ_ERRRTN(err);

      // 5. Check for descent direction (inner produce of gradient and
      //    direction is negative.
      double alpha = inner( grad, d );
      // TODD -- Add back in if you encounter problems -- do a gradient
      //         step if the direction from the conjugate gradient solver
      //         is not a descent direction for the objective function.  We
      //         SHOULD always get a descent direction from the conjugate
      //         method though, unless the preconditioner is not positive
      //         definite.
      // If direction is positive, does a gradient (steepest descent) step.

      if (alpha > -epsilon) {
    
        MSQ_DBGOUT(3) << "  o  alpha = " << alpha << " (rejected)" << std::endl;
    
        if (!havePrintedDirectionMessage) {
          MSQ_PRINT(1)("Newton direction not guaranteed descent.  Ensure preconditioner is positive definite.\n");
          havePrintedDirectionMessage = true;
        }

        // TODD: removed performing gradient step here since we will use
        // gradient if step does not produce descent.  Instead we set
        // alpha to a small negative value.

        alpha = -epsilon;

        // alpha = inner(grad, grad, nv); // compute norm squared of gradient
        // if (alpha < 1) alpha = 1;	// take max with constant
        // for (i = 0; i < nv; ++i) {
        //   d[i] = -grad[i] / alpha; 	// compute scaled gradient
        // }
        // alpha = inner(grad, d, nv);  	// recompute alpha
        // 				// equal to one for large gradient
      }
      else {
        MSQ_DBGOUT(3) << "  o  alpha = " << alpha << std::endl;
      }
    
      alpha *= sigma;
      beta = 1.0;
      pd.recreate_vertices_memento(coordsMem, err); MSQ_ERRRTN(err);
    
      // TODD: Unrolling the linesearch loop.  We do a function and
      // gradient evaluation when beta = 1.  Otherwise, we end up
      // in the linesearch regime.  We expect that several
      // evaluations will be made, so we only do a function evaluation
      // and finish with a gradient evaluation.  When beta = 1, we also
      // check the gradient for stability.

      // TODD -- the Armijo linesearch is based on the objective function,
      //         so theoretically we only need to evaluate the objective
      //         function.  However, near a very accurate solution, say with
      //         the two norm of the gradient of the objective function less
      //         than 1e-5, the numerical error in the objective function
      //         calculation is enough that the Armijo linesearch will
      //         fail.  To correct this situation, the iterate is accepted
      //         when the norm of the gradient is also small.  If you need
      //         high accuracy and have a large mesh, talk with Todd about
      //         the numerical issues so that we can fix it.

      // TODD -- the Armijo linesearch here is only good for differentiable
      //         functions.  When projections are involved, you should change
      //	       to a form of the linesearch meant for nondifferentiable
      //         functions.

      pd.move_free_vertices_constrained(arrptr(d), nv, beta, err); MSQ_ERRRTN(err);
      fn_bool = objFunc.evaluate(pd, new_value, grad, err); 
      if (err.error_code() == err.BARRIER_VIOLATED)
        err.clear();  // barrier violated does not represent an actual error here
      MSQ_ERRRTN(err);

      if ((fn_bool && (original_value - new_value >= -alpha*beta - epsilon)) ||
          (fn_bool && (length(arrptr(grad), nv) < 100*convTol))) {
        // Armijo linesearch rules passed.
        MSQ_DBGOUT(3) << "  o  beta = " << beta << " (accepted without line search)" << std::endl;
      }
      else {
        if (!fn_bool) {
  	// Function undefined.  Use the higher decrease rate.
          beta *= beta0;
          MSQ_DBGOUT(3) << "  o  beta = " << beta << " (invalid step)" << std::endl;
        }
        else {
          // Function defined, but not sufficient decrease
          // Use the lower decrease rate.
          beta *= beta1;
          MSQ_DBGOUT(3) << "  o  beta = " << beta << " (insufficient decrease)" << std::endl;
        }
        pd.set_to_vertices_memento(coordsMem, err); MSQ_ERRRTN(err);
      
        // Standard Armijo linesearch rules
 
        MSQ_DBGOUT(3) << "  o  Doing line search" << std::endl;
        while (beta >= tol1) {
          // 6. Search along the direction
          //    (a) trial = x + beta*d
          pd.move_free_vertices_constrained(arrptr(d), nv, beta, err); MSQ_ERRRTN(err);
          //    (b) function evaluation
          fn_bool = objFunc.evaluate(pd, new_value, err); 
          if (err.error_code() == err.BARRIER_VIOLATED)
            err.clear();  // barrier violated does not represent an actual error here
          MSQ_ERRRTN(err);

          //    (c) check for sufficient decrease and stop
          if (!fn_bool) { 
	    // function not defined at trial point
            beta *= beta0;
          }
          else if (original_value - new_value >= -alpha*beta - epsilon ) {
            // iterate is acceptable.
            break; 
          }
          else {
            // iterate is not acceptable -- shrink beta
            beta *= beta1;
          }
          pd.set_to_vertices_memento(coordsMem, err); MSQ_ERRRTN(err);
        } 

        if (beta < tol1) {
          // assert(pd.set_to_vertices_memento called last)

          // TODD -- Lower limit on steplength reached.  Direction does not 
	         //         appear to make sufficient progress decreasing the 
          //         objective function.  This can happen when you are 
          //         very near a solution due to numerical errors in 
	         //         computing the objective function.  It can also happen 
          //         when the direction is not a descent direction and when
	         //         you are projecting the iterates onto a surface.
          //
          //         The latter cases require the use of a linesearch on
          //         a gradient step.  If this linesearch terminate with
          //         insufficient decrease, then you are at a critical 
          //         point and should stop!
          //
          //         The numerical errors with the objective function cannot
          //         be overcome.  Eventually, the gradient step will
          //         fail to compute a new direction and you will stop.

          MSQ_PRINT(1)("Sufficient decrease not obtained in linesearch; switching to gradient.\n");

          alpha = inner(arrptr(grad), arrptr(grad), nv); 	// compute norm squared of gradient
          if (alpha < 1) alpha = 1;	// take max with constant
	  for (i = 0; i < nv; ++i) {
	    d[i] = -grad[i] / alpha; 	// compute scaled gradient
	  }
          alpha = inner(arrptr(grad), arrptr(d), nv);  	// recompute alpha
	  alpha *= sigma;                 // equal to one for large gradient
	  beta = 1.0;

	  // Standard Armijo linesearch rules
          while (beta >= tol2) {
	    // 6. Search along the direction
	    //    (a) trial = x + beta*d
	    pd.move_free_vertices_constrained(arrptr(d), nv, beta, err); MSQ_ERRRTN(err);
	    //    (b) function evaluation
	    fn_bool = objFunc.evaluate(pd, new_value, err);
            if (err.error_code() == err.BARRIER_VIOLATED)
              err.clear();  // barrier violated does not represent an actual error here
            MSQ_ERRRTN(err);

	    //    (c) check for sufficient decrease and stop
	    if (!fn_bool) { 
	      // function not defined at trial point
	      beta *= beta0;
	    }
	    else if (original_value - new_value >= -alpha*beta - epsilon ) {
	      // iterate is acceptable.
	      break; 
	   }
	    else {
	      // iterate is not acceptable -- shrink beta
	     beta *= beta1;
	   }
	    pd.set_to_vertices_memento(coordsMem, err); MSQ_ERRRTN(err);
	  } 

	  if (beta < tol2) {
	    // assert(pd.set_to_vertices_memento called last)
	  
	    // TODD -- Lower limit on steplength reached.  Gradient does not 
	    //         appear to make sufficient progress decreasing the 
	    //         objective function.  This can happen when you are 
	    //         very near a solution due to numerical errors in 
	    //         computing the objective function.  Most likely you
	    //         are at a critical point for the problem.

	    MSQ_PRINT(1)("Sufficient decrease not obtained with gradient; critical point likely found.\n");
	    break;
	  }
        }

        // Compute the gradient at the new point -- needed by termination check
        fn_bool = objFunc.update(pd, new_value, grad, err); MSQ_ERRRTN(err);
      }

      // Prints out free vertices coordinates. 
//    if (MSQ_DBG(3)) {
//      MSQ_DBGOUT(3) << "  o Free vertices new coordinates: \n";
//      const MsqVertex* toto1 = pd.get_vertex_array(err); MSQ_ERRRTN(err);
//      MsqFreeVertexIndexIterator ind(pd, err); MSQ_ERRRTN(err);
//      ind.reset();
//      while (ind.next()) {
//        MSQ_DBGOUT(3) << "\t\t\t" << toto1[ind.value()];
//      }
//    }

      // checks stopping criterion 
      term_crit->accumulate_patch( pd, err ); MSQ_ERRRTN(err);
      term_crit->accumulate_inner( pd, new_value, arrptr(grad), err ); MSQ_ERRRTN(err);
    }
    MSQ_PRINT(2)("FINISHED\n");
  }
  else
  {
    std::cout << "WARNING: Feasible Newton optimization only supported for volume meshes"
        << std::endl <<  "   and XYPlanarDomain surface meshes." << std::endl
        << std::endl << "Try a different solver such as Steepest Descent." << std::endl;
  }
}
Пример #19
0
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);
  }
}
Пример #20
0
double ConjugateGradient::get_step(PatchData &pd,double f0,int &j,
                                   MsqError &err)
{
  // get OF evaluator
  OFEvaluator& objFunc = get_objective_function_evaluator();

  size_t num_vertices=pd.num_free_vertices();
    //initial guess for alp
  double alp=1.0;
  int jmax=100;
  double rho=0.5;
    //feasible=false implies the mesh is not in the feasible region
  bool feasible=false;
  int found=0;
    //f and fnew hold the objective function value
  double f=0;
  double fnew=0;
    //Counter to avoid infinitly scaling alp
  j=0;
  //save memento
  pd.recreate_vertices_memento(pMemento, err);
    //if we must check feasiblility
    //while step takes mesh into infeasible region and ...
  while (j<jmax && !feasible && alp>MSQ_MIN) {
    ++j;
    pd.set_free_vertices_constrained(pMemento,arrptr(pGrad),num_vertices,alp,err);
    feasible=objFunc.evaluate(pd,f,err);
    if(err.error_code() == err.BARRIER_VIOLATED)
      err.clear();  // barrier violation does not represent an actual error here
    MSQ_ERRZERO(err);
      //if not feasible, try a smaller alp (take smaller step)
    if(!feasible){
      alp*=rho;
    }
  }//end while ...
  
    //if above while ended due to j>=jmax, no valid step was found.
  if(j>=jmax){
    MSQ_PRINT(2)("\nFeasible Point Not Found");
    return 0.0;
  }
    //Message::print_info("\nOriginal f %f, first new f = %f, alp = %f",f0,f,alp);
    //if new f is larger than original, our step was too large
  if(f>=f0){
    j=0;
    while (j<jmax && found == 0){
      ++j;
      alp *= rho;
      pd.set_free_vertices_constrained(pMemento,arrptr(pGrad),num_vertices,alp,err);
        //Get new obj value
        //if patch is now invalid, then the feasible region is  convex or
        //we have an error.  For now, we assume an error.
      if(! objFunc.evaluate(pd,f,err) ){
        MSQ_SETERR(err)("Non-convex feasiblility region found.",MsqError::INVALID_MESH);
      }
      pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err);
        //if our step has now improved the objective function value
      if(f<f0){
        found=1;
      }
    }//   end while j less than jmax
      //Message::print_info("\nj = %d found = %d f = %20.18f f0 = %20.18f\n",j,found,f,f0);
      //if above ended because of j>=jmax, take no step
    if(found==0){
        //Message::print_info("alp = %10.8f, but returning zero\n",alp);
      alp=0.0; 
      return alp;
    }

    j=0;
      //while shrinking the step improves the objFunc value further,
      //scale alp down.  Return alp, when scaling once more would
      //no longer improve the objFunc value.  
    while(j<jmax){
      ++j;
      alp*=rho;
      //step alp in search direction from original positions
      pd.set_free_vertices_constrained(pMemento,arrptr(pGrad),num_vertices,alp,err);MSQ_ERRZERO(err);

        //get new objective function value
      if (! objFunc.evaluate(pd,fnew,err))
        MSQ_SETERR(err)("Non-convex feasiblility region found while "
                        "computing new f.",MsqError::INVALID_MESH);
      if(fnew<f){
        f=fnew;
      }
      else{
	//Reset the vertices to original position
	pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err);
	alp/=rho;
	return alp;
      }
    }
    //Reset the vertices to original position and return alp
    pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err);
    return alp;
  }
    //else our new f was already smaller than our original
  else{
    j=0;
      //check to see how large of step we can take
    while (j<jmax && found == 0) {
      ++j;
        //scale alp up (rho must be less than 1)
      alp /= rho;
      //step alp in search direction from original positions
      pd.set_free_vertices_constrained(pMemento,arrptr(pGrad),num_vertices,alp,err);MSQ_ERRZERO(err);

      feasible = objFunc.evaluate(pd,fnew, err);
      if(err.error_code() == err.BARRIER_VIOLATED)
        err.clear();  // evaluate() error does not represent an actual problem here
      MSQ_ERRZERO(err);
      if ( ! feasible ){
        alp *= rho;
          //Reset the vertices to original position and return alp
        pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err);
        return alp;
      }
      if (fnew<f) { 
          f = fnew; 
       }
       else {
         found=1;
         alp *= rho;
       }
    }

    //Reset the vertices to original position and return alp
    pd.set_to_vertices_memento(pMemento,err);MSQ_ERRZERO(err);
    return alp;
  }
}