SDDSlib
Loading...
Searching...
No Matches
powellMin.c File Reference

Implementation of Powell's optimization algorithm for multidimensional minimization. More...

#include "mdb.h"

Go to the source code of this file.

Macros

#define DEFAULT_MAXEVALS   100
 
#define DEFAULT_MAXPASSES   5
 
#define DEBUG   0
 

Functions

long powellMoveToMin (double *yReturn, double *x, double *xWork, double *dx, double *xLower, double *xUpper, long dims, long linMinIterations, long maxGoodSteps, double(*func)(double *x, long *invalid))
 
long powellMinStep (double *yReturn, double *xReturn, double **dirVector, double **P, double *xLower, double *xUpper, long dims, double target, long linMinIterations, long limitGoodSteps, double(*func)(double *x, long *invalid))
 
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.
 

Detailed Description

Implementation of Powell's optimization algorithm for multidimensional minimization.

License
This file is distributed under the terms of the Software License Agreement found in the file LICENSE included with this distribution.
Author
M. Borland, C. Saunders, R. Soliday

Definition in file powellMin.c.

Macro Definition Documentation

◆ DEBUG

#define DEBUG   0

Definition at line 21 of file powellMin.c.

◆ DEFAULT_MAXEVALS

#define DEFAULT_MAXEVALS   100

Definition at line 18 of file powellMin.c.

◆ DEFAULT_MAXPASSES

#define DEFAULT_MAXPASSES   5

Definition at line 19 of file powellMin.c.

Function Documentation

◆ powellMin()

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.

This is the main function implementing Powell's optimization algorithm. It iteratively searches for the minimum of the provided objective function within the specified bounds and tolerance levels.

Parameters
yReturnPointer to store the final function value at the minimum.
xGuessInitial guess for the position in the parameter space.
dxGuessInitial guess for the step sizes in each dimension.
xLowerLimitArray of lower bounds for each dimension.
xUpperLimitArray of upper bounds for each dimension.
dimsNumber of dimensions in the parameter space.
targetTarget function value; the algorithm will terminate if any value is less than or equal to this target.
toleranceTolerance for convergence. If negative, interpreted as a fractional tolerance; if positive, as an absolute tolerance.
funcPointer to the objective function to be minimized. It should accept the current position and a pointer to a long indicating if the evaluation is invalid.
reportPointer to a reporting function that is called after each pass. It receives the current minimum value, position, pass number, total evaluations, and number of dimensions.
maxPassesMaximum number of passes (iterations) to perform.
maxEvaluationsMaximum number of function evaluations allowed.
linMinIterationsNumber of linear minimization iterations to perform.
Returns
The total number of function evaluations performed during the minimization.

Definition at line 220 of file powellMin.c.

233 {
234 double *xTrial, *dxLocal = NULL, **dirVector = NULL, **P = NULL;
235 double y0, dVector, denominator, merit;
236 long dir, totalEvaluations = 0, isInvalid, pass = 0;
237
238 if (!yReturn || !xGuess || !dxGuess)
239 return -1;
240 if (dims <= 0)
241 return (-3);
242
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);
249
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)
255 dxLocal[dir] = 1;
256 }
257 if ((xLowerLimit && xUpperLimit) &&
258 (dVector = fabs(xUpperLimit[dir] - xLowerLimit[dir]) / 4) < fabs(dxLocal[dir]))
259 dxLocal[dir] = dVector;
260 }
261
262 if (xLowerLimit || xUpperLimit) {
263 /* if start is at lower/upper limit, make sure initial step is positive/negative */
264 for (dir = 0; dir < dims; dir++) {
265 if (xLowerLimit && xUpperLimit && xLowerLimit[dir] >= xUpperLimit[dir])
266 continue;
267 if (xLowerLimit && xLowerLimit[dir] >= xGuess[dir]) {
268 dxLocal[dir] = fabs(dxLocal[dir]);
269 xGuess[dir] = xLowerLimit[dir];
270 }
271 if (xUpperLimit && xUpperLimit[dir] <= xGuess[dir]) {
272 dxLocal[dir] = -fabs(dxLocal[dir]);
273 xGuess[dir] = xUpperLimit[dir];
274 }
275 }
276 }
277
278 for (dir = 0; dir < dims; dir++) {
279 P[0][dir] = xGuess[dir];
280 dirVector[dir][dir] = dxLocal[dir];
281 }
282
283 if (maxPasses <= 0)
284 maxPasses = DEFAULT_MAXPASSES;
285
286 y0 = (*func)(xGuess, &isInvalid);
287 totalEvaluations++;
288 if (isInvalid) {
289 fprintf(stderr, "Fatal error (powellMin): initial guess is invalid\n");
290 exit(1);
291 }
292 if (y0 <= target) {
293 if (report)
294 (*report)(y0, xGuess, pass, totalEvaluations, dims);
295 return (totalEvaluations);
296 }
297
298 while (pass++ < maxPasses) {
299 *yReturn = y0;
300#if DEBUG
301 fprintf(stderr, ">> Performing powell step to min %ld\n", pass);
302#endif
303 totalEvaluations += powellMinStep(yReturn, xGuess, dirVector, P,
304 xLowerLimit, xUpperLimit, dims, target, linMinIterations,
305 !pass, func);
306#if DEBUG
307 fprintf(stderr, ">> %ld evaluations total, value is %le\n",
308 totalEvaluations, *yReturn);
309#endif
310 if (tolerance <= 0) {
311 denominator = (y0 + (*yReturn)) / 2;
312 if (denominator)
313 merit = fabs(y0 - (*yReturn)) / denominator;
314 else {
315 fputs("error: divide-by-zero in fractional tolerance evaluation (powellMin)\n", stderr);
316 return -1;
317 }
318 } else
319 merit = fabs(y0 - (*yReturn));
320 if (merit <= fabs(tolerance) || (*yReturn) <= target || totalEvaluations > maxEvaluations) {
321 if (report)
322 (*report)(*yReturn, xGuess, pass, totalEvaluations, dims);
323 return (totalEvaluations);
324 }
325 if (report)
326 (*report)(*yReturn, xGuess, pass, totalEvaluations, dims);
327 y0 = *yReturn;
328 }
329#if DEBUG
330 fprintf(stderr, ">> returning from powell optimization\n");
331#endif
332 return totalEvaluations;
333}
void ** zarray_2d(uint64_t size, uint64_t n1, uint64_t n2)
Allocates a 2D array with specified dimensions.
Definition array.c:93
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
Definition bomb.c:26

◆ powellMinStep()

long powellMinStep ( double * yReturn,
double * xReturn,
double ** dirVector,
double ** P,
double * xLower,
double * xUpper,
long dims,
double target,
long linMinIterations,
long limitGoodSteps,
double(* func )(double *x, long *invalid) )

Definition at line 141 of file powellMin.c.

144 {
145 long i, invalid, evals;
146 double *yValue, yExtrap, *PExtrap = NULL, dyMax, dy;
147
148 if (!(yValue = malloc(sizeof(*yValue) * (dims + 1))) ||
149 !(PExtrap = malloc(sizeof(*PExtrap) * dims)))
150 bomb("memory allocation failure (powellMinStep)", NULL);
151
152 memcpy(P[0], xReturn, sizeof(*xReturn) * dims);
153 yValue[0] = *yReturn;
154 dyMax = -DBL_MAX;
155 evals = 0;
156 for (i = 1; i <= dims; i++) {
157#if DEBUG
158 fprintf(stderr, ">> Scanning %ld\n", i - 1);
159#endif
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)
165 dyMax = dy;
166 }
167 if (dyMax == -DBL_MAX)
168 return evals;
169
170 for (i = 0; i < dims; i++)
171 PExtrap[i] = 2 * P[dims][i] - P[0][i];
172 yExtrap = (*func)(PExtrap, &invalid);
173 if (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)) {
179 /* exchange dir vectors */
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];
184 }
185 memcpy(xReturn, P[dims], sizeof(*xReturn) * dims);
186 *yReturn = yValue[dims];
187 free(yValue);
188 free(PExtrap);
189 return evals;
190}

◆ powellMoveToMin()

long powellMoveToMin ( double * yReturn,
double * x,
double * xWork,
double * dx,
double * xLower,
double * xUpper,
long dims,
long linMinIterations,
long maxGoodSteps,
double(* func )(double *x, long *invalid) )

Definition at line 23 of file powellMin.c.

25 {
26 double y0, y1 = 0;
27 long i, j, invalid, goodSteps, goodSteps0;
28 long divisor, division, divisions, success, evals, limitHit;
29
30 y0 = *yReturn;
31
32 /* first, try to find a direction and step-size that leads to a decrease in the function value */
33 divisor = 10;
34 success = evals = 0;
35 divisions = 20;
36 division = 0;
37 memcpy(xWork, x, sizeof(*x) * dims);
38 while (division++ < divisions) {
39 /* step in direction dx, looking for reduction */
40 for (i = 0; i < dims; i++) {
41 x[i] += dx[i] / divisor;
42 if (xLower && xLower[i] > x[i])
43 x[i] = xLower[i];
44 if (xUpper && xUpper[i] < x[i])
45 x[i] = xUpper[i];
46 }
47 y1 = (*func)(x, &invalid);
48#if DEBUG
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",
54 y1, invalid);
55#endif
56 evals++;
57 if (invalid)
58 y1 = 1e9 * fabs(y0) + 1;
59 if (y1 >= y0) {
60#if DEBUG
61 fprintf(stderr, ">> not improved\n");
62#endif
63 memcpy(x, xWork, sizeof(*x) * dims);
64 if (division % 2)
65 divisor *= -1;
66 else
67 divisor *= -10;
68 } else {
69#if DEBUG
70 fprintf(stderr, ">> improved\n");
71#endif
72 success = 1;
73 break;
74 }
75 }
76 if (!success) {
77#if DEBUG
78 fprintf(stderr, ">> optimization failed\n");
79#endif
80 return evals;
81 }
82#if DEBUG
83 fprintf(stderr, ">> optimization succeeded\n");
84#endif
85
86 /* continue in this direction, if possible */
87 limitHit = 0;
88 for (j = 0; !limitHit && j < linMinIterations; j++) {
89#if DEBUG
90 fprintf(stderr, ">> trying to step again\n");
91#endif
92 success = 0;
93 memcpy(xWork, x, sizeof(*x) * dims);
94 goodSteps = goodSteps0 = 0;
95 while (1) {
96 y0 = y1;
97 for (i = 0; i < dims; i++) {
98 x[i] += dx[i] / divisor;
99 if (xLower && xLower[i] > x[i]) {
100 limitHit = 1;
101 x[i] = xLower[i];
102 }
103 if (xUpper && xUpper[i] < x[i]) {
104 limitHit = 1;
105 x[i] = xUpper[i];
106 }
107 }
108 y1 = (*func)(x, &invalid);
109 evals++;
110 if (invalid)
111 y1 = 1e9 * fabs(y0) + 1;
112 if (y1 >= y0) {
113 memcpy(x, xWork, sizeof(*x) * dims);
114 y1 = y0;
115 break;
116 } else {
117 memcpy(xWork, x, sizeof(*x) * dims);
118 success = 1;
119 goodSteps++;
120 if (maxGoodSteps > 0 && goodSteps > maxGoodSteps) {
121 *yReturn = y0;
122 return evals;
123 }
124 if ((goodSteps - goodSteps0) > 5) {
125 divisor /= 2;
126 goodSteps0 = goodSteps;
127 }
128 }
129 }
130 if (success)
131 /* turn around and repeat */
132 divisor *= -PI;
133 else
134 break;
135 }
136
137 *yReturn = y0;
138 return evals;
139}