예제 #1
0
void simplex_optimization
(
  vfp nmodel,             /* pointer to noise model */
  vfp smodel,             /* pointer to signal model */
  int r,                  /* number of parameters in the noise model */
  int p,                  /* number of parameters in the signal model */
  float * min_nconstr,    /* minimum parameter constraints for noise model */
  float * max_nconstr,    /* maximum parameter constraints for noise model */
  float * min_sconstr,    /* minimum parameter constraints for signal model */
  float * max_sconstr,    /* maximum parameter constraints for signal model */
  int nabs,               /* use absolute constraints for noise parameters */
  int ts_length,          /* length of time series array */
  float ** x_array,       /* independent variable matrix */
  float * ts_array,       /* observed time series */
  float * par_rdcd,       /* estimated parameters for the reduced model */
  float * parameters,     /* estimated parameters */
  float * sse             /* error sum of squares */
)

{
  const int MAX_ITERATIONS = 50;         /* maximum number of iterations */
  const int MAX_RESTARTS = 5;            /* maximum number of restarts */
  const float EXPANSION_COEF = 2.0;      /* expansion coefficient */
  const float REFLECTION_COEF = 1.0;     /* reflection coefficient */
  const float CONTRACTION_COEF = 0.5;    /* contraction coefficient */
  const float TOLERANCE = 1.0e-4;        /* solution convergence tolerance */

  float ** simplex   = NULL;    /* the simplex itself */
  float * centroid   = NULL;    /* center of mass of the simplex */
  float * response   = NULL;    /* error sum of squares at each vertex */
  float * step_size  = NULL;    /* controls random placement of new vertex */
  float * test1      = NULL;    /* test vertex */
  float * test2      = NULL;    /* test vertex */
  float resp1, resp2;           /* error sum of squares for test vertex */
  int i;                        /* vertex index */
  int worst;                    /* index of worst vertex in simplex */
  int best;                     /* index of best vertex in simplex */
  int next;                     /* index of next-to-worst vertex in simplex */
  int num_iter;                 /* number of simplex algorithm iterations */
  int num_restarts;             /* number of restarts of simplex algorithm */
  int done;                     /* boolean for search finished */
  float fit;                    /* array of fitted time series values */
  int dimension;                /* dimension of parameter space */


  /*----- dimension of parameter space -----*/
  dimension = r + p;

  /*----- allocate memory -----*/
  allocate_arrays (dimension, &simplex, &centroid, &response, &step_size,
		   &test1, &test2);

  /*----- initialization for simplex algorithm -----*/
  initialize_simplex (dimension, nmodel, smodel, r, p, nabs,
		      min_nconstr, max_nconstr, min_sconstr, max_sconstr,
		      par_rdcd, parameters, simplex, response, step_size,
		      ts_length, x_array, ts_array);

  /* start loop to do simplex optimization */
  num_iter = 0;
  num_restarts = 0;
  done = 0;

  while (!done)
    {
      /*----- find the worst vertex and compute centroid of remaining simplex,
	discarding the worst vertex -----*/
      eval_vertices (dimension, response, &worst, &next, &best);
      calc_centroid (dimension, simplex, worst, centroid);

      /*----- reflect the worst point through the centroid -----*/
      calc_reflection (dimension, simplex, centroid, worst,
		       REFLECTION_COEF, test1);
      resp1 = calc_sse (nmodel, smodel, r, p, nabs, min_nconstr, max_nconstr,
			min_sconstr, max_sconstr, par_rdcd, test1,
			ts_length, x_array, ts_array);


      /*----- test the reflection against the best vertex and expand it if the
	reflection is better.  if not, keep the reflection -----*/
      if (resp1 < response[best])
	{
	  /*----- try expanding -----*/
	  calc_reflection (dimension, simplex, centroid, worst,
			   EXPANSION_COEF, test2);

	  resp2 = calc_sse (nmodel, smodel, r, p, nabs,
			    min_nconstr, max_nconstr,
			    min_sconstr, max_sconstr, par_rdcd, test2,
			    ts_length, x_array, ts_array);

	  if (resp2 <= resp1)    /* keep expansion */
	    replace (dimension, simplex, response, worst, test2, resp2);
	  else                   /* keep reflection */
	    replace (dimension, simplex, response, worst, test1, resp1);
	}

      else if (resp1 < response[next])
	{
	  /*----- new response is between the best and next worst
	    so keep reflection -----*/
	  replace (dimension, simplex, response, worst, test1, resp1);
	}
          else
	{
	  /*----- try contraction -----*/
	  if (resp1 >= response[worst])
	    calc_reflection (dimension, simplex, centroid, worst,
			     -CONTRACTION_COEF, test2);
	  else
	    calc_reflection (dimension, simplex, centroid, worst,
			     CONTRACTION_COEF, test2);

	  resp2 = calc_sse (nmodel, smodel, r, p, nabs,
			    min_nconstr, max_nconstr,
			    min_sconstr, max_sconstr, par_rdcd, test2,
			    ts_length, x_array, ts_array);
	
	  /*---- test the contracted response against the worst response ----*/
	  if (resp2 > response[worst])
	    {
	      /*----- new contracted response is worse, so decrease step size
		and restart -----*/
	      num_iter = 0;
	      num_restarts += 1;
	      restart (dimension, nmodel, smodel, r, p, nabs,
		       min_nconstr, max_nconstr, min_sconstr, max_sconstr,
		       par_rdcd, simplex, response, step_size,
		       ts_length, x_array, ts_array);
	    }
	  else       /*----- keep contraction -----*/
	    replace (dimension, simplex, response, worst, test2, resp2);
	}

      /*----- test to determine when to stop.
	first, check the number of iterations -----*/
      num_iter += 1;    /*----- increment iteration counter -----*/
      if (num_iter >= MAX_ITERATIONS)
	{
	  /*----- restart with smaller steps -----*/
	  num_iter = 0;
	  num_restarts += 1;
	  restart (dimension, nmodel, smodel, r, p, nabs,
		   min_nconstr, max_nconstr, min_sconstr, max_sconstr,
		   par_rdcd, simplex, response, step_size,
		   ts_length, x_array, ts_array);
	}

      /*----- limit the number of restarts -----*/
      if (num_restarts == MAX_RESTARTS)  done = 1;

      /*----- compare relative standard deviation of vertex responses
	against a defined tolerance limit -----*/
      fit = calc_good_fit (dimension, response);
      if (fit <= TOLERANCE)  done = 1;

      /*----- if done, copy the best solution to the output array -----*/
      if (done)
	{
	  eval_vertices (dimension, response, &worst, &next, &best);
	  for (i = 0;  i < dimension;  i++)
	    parameters[i] = simplex[best][i];
	  *sse = response[best];
	}

    }  /*----- while (!done) -----*/

  deallocate_arrays (dimension, &simplex, &centroid, &response, &step_size,
		     &test1, &test2);

}
예제 #2
0
void NelderMeadSolver::solve(const Function& function,
                             SolverResults* results) const
{
	double global_start_time = wall_time();

	// Dimension of problem.
	size_t n = function.get_number_of_scalars();

	if (n == 0) {
		results->exit_condition = SolverResults::FUNCTION_TOLERANCE;
		return;
	}

	// The Nelder-Mead simplex.
	std::vector<SimplexPoint> simplex(n + 1);

	// Copy the user state to the current point.
	Eigen::VectorXd x;
	function.copy_user_to_global(&x);

	initialize_simplex(function, x, &simplex);

	SimplexPoint mean_point;
	SimplexPoint reflection_point;
	SimplexPoint expansion_point;
	mean_point.x.resize(n);
	reflection_point.x.resize(n);
	expansion_point.x.resize(n);

	double fmin  = std::numeric_limits<double>::quiet_NaN();
	double fmax  = std::numeric_limits<double>::quiet_NaN();
	double fval  = std::numeric_limits<double>::quiet_NaN();
	double area  = std::numeric_limits<double>::quiet_NaN();
	double area0 = std::numeric_limits<double>::quiet_NaN();
	double length  = std::numeric_limits<double>::quiet_NaN();
	double length0 = std::numeric_limits<double>::quiet_NaN();

	Eigen::MatrixXd area_mat(n, n);

	//
	// START MAIN ITERATION
	//
	results->startup_time   += wall_time() - global_start_time;
	results->exit_condition = SolverResults::INTERNAL_ERROR;
	int iter = 0;
	int n_shrink_in_a_row = 0;
	while (true) {

		//
		// In each iteration, the worst point in the simplex
		// is replaced with a new one.
		//
		double start_time = wall_time();

		mean_point.x.setZero();
		fval = 0;
		// Compute the mean of the best n points.
		for (size_t i = 0; i < n; ++i) {
			mean_point.x += simplex[i].x;
			fval         += simplex[i].value;
		}
		fval         /= double(n);
		mean_point.x /= double(n);
		fmin = simplex[0].value;
		fmax = simplex[n].value;

		const char* iteration_type = "n/a";

		// Compute the reflexion point and evaluate it.
		reflection_point.x = 2.0 * mean_point.x - simplex[n].x;
		reflection_point.value = function.evaluate(reflection_point.x);

		bool is_shrink = false;
		if (simplex[0].value <= reflection_point.value &&
			reflection_point.value < simplex[n - 1].value) {
			// Reflected point is neither better nor worst in the
			// new simplex.
			std::swap(reflection_point, simplex[n]);
			iteration_type = "Reflect 1";
		}
		else if (reflection_point.value < simplex[0].value) {
			// Reflected point is better than the current best; try
			// to go farther along this direction.

			// Compute expansion point.
			expansion_point.x = 3.0 * mean_point.x - 2.0 * simplex[n].x;
			expansion_point.value = function.evaluate(expansion_point.x);

			if (expansion_point.value < reflection_point.value) {
				std::swap(expansion_point, simplex[n]);
				iteration_type = "Expansion";
			}
			else {
				std::swap(reflection_point, simplex[n]);
				iteration_type = "Reflect 2";
			}
		}
		else {
			// Reflected point is still worse than x[n]; contract.
			bool success = false;

			if (simplex[n - 1].value <= reflection_point.value &&
			    reflection_point.value < simplex[n].value) {
				// Try to perform "outside" contraction.
				expansion_point.x = 1.5 * mean_point.x - 0.5 * simplex[n].x;
				expansion_point.value = function.evaluate(expansion_point.x);

				if (expansion_point.value <= reflection_point.value) {
					std::swap(expansion_point, simplex[n]);
					success = true;
					iteration_type = "Outside contraction";
				}
			}
			else {
				// Try to perform "inside" contraction.
				expansion_point.x = 0.5 * mean_point.x + 0.5 * simplex[n].x;
				expansion_point.value = function.evaluate(expansion_point.x);

				if (expansion_point.value < simplex[n].value) {
					std::swap(expansion_point, simplex[n]);
					success = true;
					iteration_type = "Inside contraction";
				}
			}

			if (! success) {
				// Neither outside nor inside contraction was acceptable;
				// shrink the simplex toward the best point.
				for (size_t i = 1; i < n + 1; ++i) {
					simplex[i].x = 0.5 * (simplex[0].x + simplex[i].x);
					simplex[i].value = function.evaluate(simplex[i].x);
					iteration_type = "Shrink";
					is_shrink = true;
				}
			}
		}

		std::sort(simplex.begin(), simplex.end());

		results->function_evaluation_time += wall_time() - start_time;

		//
		// Test stopping criteriea
		//
		start_time = wall_time();
		
		// Compute the area of the simplex.
		length = 0;
		for (size_t i = 0; i < n; ++i) {
			area_mat.col(i) = simplex[i].x - simplex[n].x;
			length = std::max(length, area_mat.col(i).norm());
		}
		area = std::abs(area_mat.determinant());
		if (iter == 0) {
			area0 = area;
			length0 = length;
		}

		if (area / area0 < this->area_tolerance) {
			results->exit_condition = SolverResults::GRADIENT_TOLERANCE;
			break;
		}

		if (area == 0) {
			results->exit_condition = SolverResults::GRADIENT_TOLERANCE;
			break;
		}

		if (length / length0 < this->length_tolerance) {
			results->exit_condition = SolverResults::GRADIENT_TOLERANCE;
			break;
		}

		if (is_shrink) {
			n_shrink_in_a_row++;
		}
		else {
			n_shrink_in_a_row = 0;
		}
		if (n_shrink_in_a_row > 50) {
			results->exit_condition = SolverResults::GRADIENT_TOLERANCE;
			break;
		}

		if (iter >= this->maximum_iterations) {
			results->exit_condition = SolverResults::NO_CONVERGENCE;
			break;
		}

		if (this->callback_function) {
			CallbackInformation information;
			information.objective_value = simplex[0].value;
			information.x = &simplex[0].x;

			if (!callback_function(information)) {
				results->exit_condition = SolverResults::USER_ABORT;
				break;
			}
		}
		results->stopping_criteria_time += wall_time() - start_time;

		//
		// Restarting
		//
		//if (area / area1 < 1e-10) {
		//	x = simplex[0].x;
		//	initialize_simplex(function, x, &simplex);
		//	area1 = area;
		//	if (this->log_function) {
		//		this->log_function("Restarted.");
		//	}
		//}

		//
		// Log the results of this iteration.
		//
		start_time = wall_time();

		int log_interval = 1;
		if (iter > 30) {
			log_interval = 10;
		}
		if (iter > 200) {
			log_interval = 100;
		}
		if (iter > 2000) {
			log_interval = 1000;
		}
		if (this->log_function && iter % log_interval == 0) {
			char str[1024];
				if (iter == 0) {
					this->log_function("Itr     min(f)     avg(f)     max(f)    area    length   type");
				}
				std::sprintf(str, "%6d %+.3e %+.3e %+.3e %.3e %.3e %s",
					iter, fmin, fval, fmax, area, length, iteration_type);
			this->log_function(str);
		}
		results->log_time += wall_time() - start_time;

		iter++;
	}

	// Return the best point as solution.
	function.copy_global_to_user(simplex[0].x);
	results->total_time += wall_time() - global_start_time;

	if (this->log_function) {
		char str[1024];
		std::sprintf(str, " end   %+.3e                       %.3e %.3e", fval, area, length);
		this->log_function(str);
	}
}
예제 #3
0
/***** Main Driver Function ****************************/
int main (void)
{
  struct simplex *simpx;
  int best;
  int nfunc; 
  double answer[5] = {-3.0, 0.0};

  int ndims = 2;
  int npts  = 3;
  int i, success = 0;

  /** Check 2D Polynomial ***************************************/
  double *pts = malloc ( sizeof(double) * ndims*npts ); /* Freed in destroy_simplex */
  /* Set initial Points */
  pts[0] = -3.9; pts[1] =  0.1; 
  pts[2] =  3.5; pts[3] =  1.0;
  pts[4] =  5.0; pts[5] = -9.0;
  
  /* Set Soft Boundaries */
  double *bounds = malloc ( sizeof(double) * 3*ndims ); /* Freed in destroy_simplex */
  bounds[0] =   3; bounds[1] =   3;
  bounds[2] = -10; bounds[3] = -10;
  bounds[4] =  10; bounds[5] =  10;

  nfunc = 0;
  simpx = initialize_simplex(2, 3, 2, pts, bounds, poly_2d);
  assert( simpx );
  best = amoeba_omp (simpx, 1.0e-6, poly_2d, &nfunc, 0);
  printf("\n\n\n");
  printf("Poly_2d: Best Point: ");
  for ( i = 0; i < simpx->ndims; i++)
    {
      printf("%.3f  ", simpx->points[best*simpx->ndims + i]);
      if( fabs( answer[i] - simpx->points[best*simpx->ndims + i] ) < .01 )
        {
          success++;
        }
    }
  printf("\n\tMinimum at Best Point: %.3f ", simpx->vals[best]);
  printf("\n\tFunction Calls: %d\n\t", nfunc);
  success == simpx->ndims ? printf("SUCCEEDED\n\n") : printf("FAILED\n\n");
  destroy_simplex( simpx );
  /********************************************************************/


  /** Check 2D Polynomial with minimum of 0.   ***************************/ 
  /** Verifies no divide by 0 error.   ***********************************/
  nfunc = 0;
  double *bound = malloc ( sizeof(double) * 3*ndims ); /* Freed in destroy_simplex */
  bound[0] =   3; bound[1] =   3;
  bound[2] = -10; bound[3] = -10;
  bound[4] =  10; bound[5] =  10;
  simpx = initialize_simplex(2, 3, 1, NULL, bound, poly_2d_0);
  best = amoeba_omp (simpx, 1.0e-6, poly_2d_0, &nfunc, 0);
  success = 0;



  printf("Poly_2d_0: Best Point: ");
  for ( i = 0; i < simpx->ndims; i++)
    {
      printf("%.3f  ", simpx->points[best*simpx->ndims + i]);
      if( fabs( answer[i] - simpx->points[best*simpx->ndims + i] ) < .01 )
        {
          success++;
        }
    }
  printf("\n\tMinimum at Best Point: %.3f ", simpx->vals[best]);
  printf("\n\tFunction Calls: %d\n\t", nfunc);
  success == simpx->ndims ? printf("SUCCEEDED\n\n") : printf("FAILED\n\n");
  destroy_simplex( simpx );
  /********************************************************************/

  return 0;
}