18#define DEFAULT_MAXEVALS 100
19#define DEFAULT_MAXPASSES 5
23long powellMoveToMin(
double *yReturn,
double *x,
double *xWork,
double *dx,
24 double *xLower,
double *xUpper,
long dims,
long linMinIterations,
long maxGoodSteps,
25 double (*func)(
double *x,
long *invalid)) {
27 long i, j, invalid, goodSteps, goodSteps0;
28 long divisor, division, divisions, success, evals, limitHit;
37 memcpy(xWork, x,
sizeof(*x) * dims);
38 while (division++ < divisions) {
40 for (i = 0; i < dims; i++) {
41 x[i] += dx[i] / divisor;
42 if (xLower && xLower[i] > x[i])
44 if (xUpper && xUpper[i] < x[i])
47 y1 = (*func)(x, &invalid);
49 fprintf(stderr,
">> values are: ");
50 for (i = 0; i < dims; i++)
51 fprintf(stderr,
"%g, ", x[i]);
52 fprintf(stderr,
"\n");
53 fprintf(stderr,
"result is %g, invalid=%ld\n",
58 y1 = 1e9 * fabs(y0) + 1;
61 fprintf(stderr,
">> not improved\n");
63 memcpy(x, xWork,
sizeof(*x) * dims);
70 fprintf(stderr,
">> improved\n");
78 fprintf(stderr,
">> optimization failed\n");
83 fprintf(stderr,
">> optimization succeeded\n");
88 for (j = 0; !limitHit && j < linMinIterations; j++) {
90 fprintf(stderr,
">> trying to step again\n");
93 memcpy(xWork, x,
sizeof(*x) * dims);
94 goodSteps = goodSteps0 = 0;
97 for (i = 0; i < dims; i++) {
98 x[i] += dx[i] / divisor;
99 if (xLower && xLower[i] > x[i]) {
103 if (xUpper && xUpper[i] < x[i]) {
108 y1 = (*func)(x, &invalid);
111 y1 = 1e9 * fabs(y0) + 1;
113 memcpy(x, xWork,
sizeof(*x) * dims);
117 memcpy(xWork, x,
sizeof(*x) * dims);
120 if (maxGoodSteps > 0 && goodSteps > maxGoodSteps) {
124 if ((goodSteps - goodSteps0) > 5) {
126 goodSteps0 = goodSteps;
141long powellMinStep(
double *yReturn,
double *xReturn,
double **dirVector,
double **P,
142 double *xLower,
double *xUpper,
long dims,
double target,
143 long linMinIterations,
long limitGoodSteps,
144 double (*func)(
double *x,
long *invalid)) {
145 long i, invalid, evals;
146 double *yValue, yExtrap, *PExtrap = NULL, dyMax, dy;
148 if (!(yValue = malloc(
sizeof(*yValue) * (dims + 1))) ||
149 !(PExtrap = malloc(
sizeof(*PExtrap) * dims)))
150 bomb(
"memory allocation failure (powellMinStep)", NULL);
152 memcpy(P[0], xReturn,
sizeof(*xReturn) * dims);
153 yValue[0] = *yReturn;
156 for (i = 1; i <= dims; i++) {
158 fprintf(stderr,
">> Scanning %ld\n", i - 1);
160 memcpy(P[i], P[i - 1],
sizeof(**P) * dims);
161 yValue[i] = yValue[i - 1];
162 evals += powellMoveToMin(yValue + i, P[i], PExtrap, dirVector[i - 1], xLower, xUpper, dims,
163 linMinIterations, limitGoodSteps ? 3 : 0, func);
164 if ((dy = yValue[i] - yValue[i - 1]) > dyMax)
167 if (dyMax == -DBL_MAX)
170 for (i = 0; i < dims; i++)
171 PExtrap[i] = 2 * P[dims][i] - P[0][i];
172 yExtrap = (*func)(PExtrap, &invalid);
174 yExtrap = 1e9 * yValue[0];
175 if (!(yExtrap >= yValue[0] ||
176 2 * (yValue[0] - 2 * yValue[dims] + yExtrap) *
177 sqr(yValue[0] - yValue[dims] - dyMax) >=
178 sqr(yValue[0] - yExtrap) * dyMax)) {
180 for (i = 0; i < dims - 1; i++)
181 memcpy(dirVector[i], dirVector[i + 1],
sizeof(**dirVector) * dims);
182 for (i = 0; i < dims; i++)
183 dirVector[dims - 1][i] = P[dims][i] - P[0][i];
185 memcpy(xReturn, P[dims],
sizeof(*xReturn) * dims);
186 *yReturn = yValue[dims];
229 double (*func)(
double *x,
long *invalid),
230 void (*report)(
double ymin,
double *xmin,
long pass,
long evals,
long dims),
233 long linMinIterations) {
234 double *xTrial, *dxLocal = NULL, **dirVector = NULL, **P = NULL;
235 double y0, dVector, denominator, merit;
236 long dir, totalEvaluations = 0, isInvalid, pass = 0;
238 if (!yReturn || !xGuess || !dxGuess)
243 if (!(xTrial = malloc(
sizeof(*xTrial) * dims)) ||
244 !(dxLocal = malloc(
sizeof(*dxLocal) * dims)) ||
245 !(P = (
double **)
zarray_2d(
sizeof(**P), dims + 1, dims)) ||
246 !(dirVector = (
double **)
zarray_2d(
sizeof(**dirVector), dims, dims)))
247 bomb(
"memory allocation failure (powellMin)", NULL);
248 memcpy(dxLocal, dxGuess,
sizeof(*dxLocal) * dims);
250 for (dir = 0; dir < dims; dir++) {
251 if (dxLocal[dir] == 0) {
252 if (xLowerLimit && xUpperLimit)
253 dxLocal[dir] = (xUpperLimit[dir] - xLowerLimit[dir]) / 4;
254 else if ((dxLocal[dir] = xGuess[dir] / 4) == 0)
257 if ((xLowerLimit && xUpperLimit) &&
258 (dVector = fabs(xUpperLimit[dir] - xLowerLimit[dir]) / 4) < fabs(dxLocal[dir]))
259 dxLocal[dir] = dVector;
262 if (xLowerLimit || xUpperLimit) {
264 for (dir = 0; dir < dims; dir++) {
265 if (xLowerLimit && xUpperLimit && xLowerLimit[dir] >= xUpperLimit[dir])
267 if (xLowerLimit && xLowerLimit[dir] >= xGuess[dir]) {
268 dxLocal[dir] = fabs(dxLocal[dir]);
269 xGuess[dir] = xLowerLimit[dir];
271 if (xUpperLimit && xUpperLimit[dir] <= xGuess[dir]) {
272 dxLocal[dir] = -fabs(dxLocal[dir]);
273 xGuess[dir] = xUpperLimit[dir];
278 for (dir = 0; dir < dims; dir++) {
279 P[0][dir] = xGuess[dir];
280 dirVector[dir][dir] = dxLocal[dir];
284 maxPasses = DEFAULT_MAXPASSES;
286 y0 = (*func)(xGuess, &isInvalid);
289 fprintf(stderr,
"Fatal error (powellMin): initial guess is invalid\n");
294 (*report)(y0, xGuess, pass, totalEvaluations, dims);
295 return (totalEvaluations);
298 while (pass++ < maxPasses) {
301 fprintf(stderr,
">> Performing powell step to min %ld\n", pass);
303 totalEvaluations += powellMinStep(yReturn, xGuess, dirVector, P,
304 xLowerLimit, xUpperLimit, dims, target, linMinIterations,
307 fprintf(stderr,
">> %ld evaluations total, value is %le\n",
308 totalEvaluations, *yReturn);
310 if (tolerance <= 0) {
311 denominator = (y0 + (*yReturn)) / 2;
313 merit = fabs(y0 - (*yReturn)) / denominator;
315 fputs(
"error: divide-by-zero in fractional tolerance evaluation (powellMin)\n", stderr);
319 merit = fabs(y0 - (*yReturn));
320 if (merit <= fabs(tolerance) || (*yReturn) <= target || totalEvaluations > maxEvaluations) {
322 (*report)(*yReturn, xGuess, pass, totalEvaluations, dims);
323 return (totalEvaluations);
326 (*report)(*yReturn, xGuess, pass, totalEvaluations, dims);
330 fprintf(stderr,
">> returning from powell optimization\n");
332 return totalEvaluations;
void ** zarray_2d(uint64_t size, uint64_t n1, uint64_t n2)
Allocates a 2D array with specified dimensions.
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
long powellMin(double *yReturn, double *xGuess, double *dxGuess, double *xLowerLimit, double *xUpperLimit, long dims, double target, double tolerance, double(*func)(double *x, long *invalid), void(*report)(double ymin, double *xmin, long pass, long evals, long dims), long maxPasses, long maxEvaluations, long linMinIterations)
Minimizes an objective function using Powell's method.