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

Implementation of one-dimensional optimization functions. More...

#include "mdb.h"

Go to the source code of this file.

Macros

#define DEFAULT_MAXEVALS   100
 
#define DEFAULT_MAXPASSES   5
 

Functions

long checkVariableLimits (double *x, double *xlo, double *xhi, long n)
 
long OneDScanOptimize (double *yReturn, double *xGuess, double *dxGuess, double *xLowerLimit, double *xUpperLimit, short *disable, long dimensions, double target, double tolerance, double(*func)(double *x, long *invalid), void(*report)(double ymin, double *xmin, long pass, long evals, long dims), long maxSteps, long maxDivisions, long maxRepeats, unsigned long flags)
 Performs one-dimensional scan optimization on a multi-dimensional function.
 
long OneDParabolicOptimization (double *yReturn, double *xGuess, double dx, double xLower, double xUpper, double(*func)(double x, long *invalid), long maxCycles, double dxLimit, double tolerance, long maximize)
 Optimizes a single-variable function using parabolic interpolation.
 

Detailed Description

Implementation of one-dimensional optimization functions.

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, R. Soliday

Definition in file onedoptimize.c.

Macro Definition Documentation

◆ DEFAULT_MAXEVALS

#define DEFAULT_MAXEVALS   100

Definition at line 17 of file onedoptimize.c.

◆ DEFAULT_MAXPASSES

#define DEFAULT_MAXPASSES   5

Definition at line 18 of file onedoptimize.c.

Function Documentation

◆ checkVariableLimits()

long checkVariableLimits ( double * x,
double * xlo,
double * xhi,
long n )

Definition at line 48 of file simplex.c.

48 {
49 long i;
50
51 if (xlo)
52 for (i = 0; i < n; i++)
53 if (xlo[i] != xhi[i] && x[i] < xlo[i])
54 return 0;
55
56 if (xhi)
57 for (i = 0; i < n; i++)
58 if (xlo[i] != xhi[i] && x[i] > xhi[i])
59 return 0;
60 return 1;
61}

◆ OneDParabolicOptimization()

long OneDParabolicOptimization ( double * yReturn,
double * xGuess,
double dx,
double xLower,
double xUpper,
double(* func )(double x, long *invalid),
long maxCycles,
double dxLimit,
double tolerance,
long maximize )

Optimizes a single-variable function using parabolic interpolation.

This function performs optimization (minimization or maximization) of a single-variable function using parabolic interpolation. It iteratively adjusts the variable to find the function's minimum or maximum by fitting a parabola to three points and locating its vertex. The optimization continues until convergence criteria based on step size or function value tolerance are met, or until the maximum number of cycles is reached.

Parameters
yReturnPointer to store the optimized function value.
xGuessInitial guess for the variable.
dxInitial step size for searching.
xLowerThe lower bound for the variable.
xUpperThe upper bound for the variable.
funcPointer to the function to be optimized. It takes the variable and a pointer to an invalid flag.
maxCyclesThe maximum number of optimization cycles to perform.
dxLimitThe minimum step size below which the optimization will stop.
toleranceThe tolerance for convergence based on the difference in function values.
maximizeIf non-zero, the function will perform maximization instead of minimization.
Returns
Returns 1 on successful optimization, or a negative error code on failure.

Definition at line 299 of file onedoptimize.c.

303 {
304 double maxFactor, fBest, xBest;
305 long invalid, cycle;
306 double x0, x1 = 0, x2, f0, f1 = 0, f2;
307
308 maxFactor = maximize ? -1 : 1;
309 invalid = 0;
310
311 x0 = *xGuess;
312 f0 = maxFactor * (*func)(x0, &invalid);
313 xBest = x0;
314 fBest = f0;
315
316 *yReturn = maxFactor * f0;
317 if (invalid)
318 return -1;
319
320 /* find direction in which function decreases */
321 for (cycle = 0; cycle < 2 * maxCycles; cycle++) {
322 x1 = x0 + dx;
323 if (x1 == x0)
324 break;
325 if (x1 > xUpper || x1 < xLower)
326 return -2;
327 f1 = maxFactor * (*func)(x1, &invalid);
328 if (invalid)
329 return -1;
330#ifdef DEBUG
331 fprintf(stderr, "cycle = %ld, maxCycles=%ld, f1 = %21.15e, fBest = %21.15e\n",
332 cycle, 2 * maxCycles, f1, fBest);
333#endif
334 if (f1 < fBest) {
335#ifdef DEBUG
336 fprintf(stderr, "f1<fBest\n");
337#endif
338 fBest = f1;
339 xBest = x1;
340 }
341 if (f1 < f0) {
342#ifdef DEBUG
343 fprintf(stderr, "f1<f0, breaking\n");
344#endif
345 break;
346 }
347 dx = dx * (cycle % 2 == 0 ? -1 : -0.5);
348 }
349 if (x1 == x0)
350 return 1;
351 if (cycle == 2 * maxCycles) {
352 if (fabs(dx) < dxLimit)
353 return 1;
354 return -3;
355 }
356#ifdef DEBUG
357 fprintf(stderr, "Function decreases with dx=%e, f0=%21.15e, f1=%21.15e, cycle=%ld\n", dx, f0, f1, cycle);
358#endif
359
360 /* take steps until passing through minimum */
361 while (1) {
362 x2 = x1 + dx;
363 if (x2 > xUpper || x2 < xLower)
364 return -4;
365 f2 = maxFactor * (*func)(x2, &invalid);
366 if (invalid)
367 return -1;
368 if (f2 < fBest) {
369 fBest = f2;
370 xBest = x2;
371 }
372#ifdef DEBUG
373 fprintf(stderr, "fBest = %21.15e, f1 = %21.15e, f2 = %21.15e\n",
374 fBest, f1, f2);
375#endif
376 if (f2 > f1)
377 break;
378 if (x1 == x2)
379 break;
380 x0 = x1;
381 f0 = f1;
382 x1 = x2;
383 f1 = f2;
384 }
385 if (x0 > x2) {
386 /* arrange in increasing order */
387 SWAP_DOUBLE(x0, x2);
388 SWAP_DOUBLE(f0, f2);
389 }
390
391 /* now f0 > f1 and f2 > f1 */
392 for (cycle = 0; cycle < maxCycles; cycle++) {
393 double numer, denom, x3, f3, scale;
394 long failed;
395#ifdef DEBUG
396 fprintf(stderr, "Cycle %ld: f(%e)=%e, f(%e)=%e, f(%e)=%e\n",
397 cycle, x0, f0, x1, f1, x2, f2);
398#endif
399
400 if (x2 == x0 || (x2 - x0) < dxLimit || (MAX(f2, f0) - f1) < tolerance)
401 break;
402 /* try parabolic interpolation */
403 numer = sqr(x1 - x0) * (f1 - f2) - sqr(x1 - x2) * (f1 - f0);
404 denom = (x1 - x0) * (f1 - f2) - (x1 - x2) * (f1 - f0);
405 x3 = x1 - numer / denom / 2.0;
406 failed = 1;
407 scale = x2 - x0;
408#ifdef DEBUG
409 fprintf(stderr, "parabolic parameters: x3 = %e, f3 = %e, scale=%e, x0=%e, x2=%e\n",
410 x3,
411 isinf(x3) ? DBL_MAX : maxFactor * (*func)(x3, &invalid), scale, x0, x2);
412#endif
413 if (!isinf(x3) && x0 < x3 && x3 < x2 &&
414 fabs(x3 - x0) > 1e-6 * scale && fabs(x3 - x1) > 1e-6 * scale &&
415 fabs(x3 - x2) > 1e-6 * scale) {
416 /* evaluate at parabolic interpolation point */
417 failed = 0;
418 f3 = maxFactor * (*func)(x3, &invalid);
419 if (invalid)
420 failed = 1;
421 else {
422 if (f3 < fBest) {
423 fBest = f3;
424 xBest = x3;
425 }
426 if (f3 < f1) {
427 /* replace point 1 */
428 f1 = f3;
429 x1 = x3;
430 } else if (f2 > f0 && f3 < f2) {
431 /* replace point 2 */
432 f2 = f3;
433 x2 = x3;
434 if (x2 < x1) {
435 SWAP_DOUBLE(x1, x2);
436 SWAP_DOUBLE(f1, f2);
437 }
438 } else if (f2 < f0 && f3 < f0) {
439 /* replace point 0 */
440 f0 = f3;
441 x0 = x3;
442 if (x0 > x1) {
443 SWAP_DOUBLE(x0, x1);
444 SWAP_DOUBLE(f0, f1);
445 }
446 } else
447 failed = 1;
448 }
449 }
450#ifdef DEBUG
451 if (!failed)
452 fprintf(stderr, "Parabolic interpolation succeeded\n");
453#endif
454 if (failed) {
455 long right, other;
456 for (other = 0; other < 2; other++) {
457 /* try dividing one of the intervals */
458 if (fabs(x0 - x1) < fabs(x1 - x2)) {
459 if (!other) {
460 x3 = (x1 + x2) / 2;
461 right = 1;
462 } else {
463 x3 = (x0 + x1) / 2;
464 right = 0;
465 }
466 } else {
467 if (!other) {
468 x3 = (x0 + x1) / 2;
469 right = 0;
470 } else {
471 x3 = (x1 + x2) / 2;
472 right = 1;
473 }
474 }
475 f3 = maxFactor * (*func)(x3, &invalid);
476 if (invalid)
477 return -1;
478 if (f3 < fBest) {
479 fBest = f3;
480 xBest = x3;
481 }
482#ifdef DEBUG
483 fprintf(stderr, "f3 = %e at x3=%e\n", f3, x3);
484#endif
485 if (f3 < f1) {
486#ifdef DEBUG
487 fprintf(stderr, "Replacing point 1\n");
488#endif
489 f1 = f3;
490 x1 = x3;
491 break;
492 }
493 if (right && f3 < f2) {
494 /* replace point 2 */
495#ifdef DEBUG
496 fprintf(stderr, "Replacing point 2\n");
497#endif
498 f2 = f3;
499 x2 = x3;
500 if (x2 < x1) {
501 SWAP_DOUBLE(x1, x2);
502 SWAP_DOUBLE(f1, f2);
503 }
504 break;
505 } else if (!right && f3 < f0) {
506 /* replace point 0 */
507#ifdef DEBUG
508 fprintf(stderr, "Replacing point 0\n");
509#endif
510 f0 = f3;
511 x0 = x3;
512 if (x0 > x1) {
513 SWAP_DOUBLE(x0, x1);
514 SWAP_DOUBLE(f0, f1);
515 }
516 break;
517 }
518 }
519#ifdef DEBUG
520 fprintf(stderr, "Sectioning succeeded\n");
521#endif
522 }
523 }
524#ifdef DEBUG
525 fprintf(stderr, "Returning: x=%21.15e, y=%21.15e\n",
526 xBest, maxFactor * fBest);
527#endif
528 *yReturn = maxFactor * fBest;
529 *xGuess = xBest;
530 return 1;
531}

◆ OneDScanOptimize()

long OneDScanOptimize ( double * yReturn,
double * xGuess,
double * dxGuess,
double * xLowerLimit,
double * xUpperLimit,
short * disable,
long dimensions,
double target,
double tolerance,
double(* func )(double *x, long *invalid),
void(* report )(double ymin, double *xmin, long pass, long evals, long dims),
long maxSteps,
long maxDivisions,
long maxRepeats,
unsigned long flags )

Performs one-dimensional scan optimization on a multi-dimensional function.

This function optimizes a given multi-dimensional function by performing a one-dimensional scan along each active dimension. It iteratively adjusts each variable within its specified limits to minimize the target function value. The optimization process continues until the target is achieved, the tolerance is met, or the maximum number of repeats is reached.

Parameters
yReturnPointer to store the optimized function value.
xGuessInitial guess for the variables.
dxGuessInitial step sizes for each variable.
xLowerLimitPointer to the array of lower limits for each variable.
xUpperLimitPointer to the array of upper limits for each variable.
disablePointer to an array indicating which dimensions to disable (1 to disable, 0 to enable).
dimensionsThe number of dimensions (variables) in the optimization.
targetThe target function value to achieve. Optimization stops if any value is <= this.
toleranceThe tolerance for convergence. If <0, it is treated as fractional; if >0, as absolute.
funcPointer to the function to be optimized. It takes the variables array and a pointer to an invalid flag.
reportPointer to a reporting function that is called with the current minimum, variables, pass number, evaluations, and dimensions.
maxStepsThe maximum number of steps to try in a good direction for each scan.
maxDivisionsThe maximum number of divisions (direction changes) allowed during scanning.
maxRepeatsThe maximum number of optimization passes to perform.
flagsOptimization flags that modify the behavior of the optimization process.
Returns
Returns the total number of function evaluations performed if successful, or a negative error code on failure.

Definition at line 48 of file onedoptimize.c.

63 {
64 static double *dxLocal = NULL;
65 static long lastDimensions = 0;
66 double yLast, dVector = 1, yNew, xLocal;
67 long point, totalEvaluations = 0, isInvalid, repeats = 0, divisions;
68 long activeDimensions, direction, found = 0, stepsTaken, decreaseSeen;
69 double *divisor, *minimum, min;
70
71 min = 1e9;
72 if (dimensions <= 0)
73 return (-3);
74 if (disable) {
75 activeDimensions = 0;
76 for (direction = 0; direction < dimensions; direction++)
77 if (!disable[direction])
78 activeDimensions++;
79 } else
80 activeDimensions = dimensions;
81 if (activeDimensions <= 0)
82 return -3;
83
84 if (activeDimensions > lastDimensions) {
85 if (dxLocal)
86 free(dxLocal);
87 dxLocal = tmalloc(sizeof(*dxLocal) * activeDimensions);
88 lastDimensions = activeDimensions;
89 }
90 divisor = (double *)malloc(sizeof(*divisor) * activeDimensions);
91 minimum = (double *)malloc(sizeof(*minimum) * activeDimensions);
92 for (point = 0; point < activeDimensions; point++)
93 minimum[point] = target; /*important!!!!*/
94 if (!dxGuess) {
95 dxGuess = dxLocal;
96 for (direction = 0; direction < dimensions; direction++)
97 dxGuess[direction] = 0;
98 } else {
99 for (direction = 0; direction < dimensions; direction++)
100 dxLocal[direction] = dxGuess[direction];
101 }
102
103 for (direction = 0; direction < dimensions; direction++) {
104 if (dxGuess[direction] == 0) {
105 if (xLowerLimit && xUpperLimit)
106 dxGuess[direction] = (xUpperLimit[direction] - xLowerLimit[direction]) / 4;
107 else if ((dxGuess[direction] = xGuess[direction] / 4) == 0)
108 dxGuess[direction] = 1;
109 }
110 if (xLowerLimit && xUpperLimit) {
111 if ((dVector = fabs(xUpperLimit[direction] - xLowerLimit[direction]) / 4) < fabs(dxGuess[direction]))
112 dxGuess[direction] = dVector;
113 }
114 if (disable && disable[direction])
115 dxGuess[direction] = 0;
116 }
117
118 if (xLowerLimit) {
119 /* if start is at lower limit, make sure initial step is positive */
120 for (direction = 0; direction < dimensions; direction++)
121 if (xLowerLimit[direction] >= xGuess[direction])
122 dxGuess[direction] = fabs(dxGuess[direction]);
123 }
124 if (xUpperLimit) {
125 /* if start is at upper limit, make sure initial step is negative */
126 for (direction = 0; direction < dimensions; direction++)
127 if (xUpperLimit[direction] <= xGuess[direction])
128 dxGuess[direction] = -fabs(dxGuess[direction]);
129 }
130#if DEBUG
131 for (direction = 0; direction < dimensions; direction++)
132 fprintf(stderr, "direction %ld: guess=%le delta=%le disable=%hd\n",
133 direction, xGuess[direction], dxGuess[direction],
134 disable ? disable[direction] : (short)0);
135#endif
136
137 if (maxRepeats <= 0)
138 maxRepeats = DEFAULT_MAXPASSES;
139 yLast = (*func)(xGuess, &isInvalid);
140 totalEvaluations++;
141 if (isInvalid) {
142 fprintf(stderr, "error: initial guess is invalid in simplexMin()\n");
143 return (-3);
144 }
145 if (yLast <= target) {
146 *yReturn = yLast;
147 if (report)
148 (*report)(*yReturn, xGuess, repeats, totalEvaluations, dimensions);
149 return (totalEvaluations);
150 }
151 for (point = 0; point < activeDimensions; point++)
152 divisor[point] = 1;
153 while (repeats < maxRepeats) {
154 repeats++;
155 for (point = 0; point < activeDimensions; point++) {
156 /* fprintf(stderr,"scan the %ldth variable\n",point+1); */
157 divisions = 0;
158 found = 0;
159 xLocal = xGuess[point];
160 while (divisions < maxDivisions) {
161 stepsTaken = 0;
162 decreaseSeen = 0;
163 while (stepsTaken < maxSteps) {
164 xGuess[point] = xGuess[point] + dxGuess[point] / divisor[point];
165 if ((xLowerLimit || xUpperLimit) &&
166 !checkVariableLimits(xGuess, xLowerLimit, xUpperLimit, dimensions)) {
167#if DEBUG
168 long idum;
169 fprintf(stderr, " Point outside of bounds:\n");
170 for (idum = 0; idum < dimensions; idum++)
171 fprintf(stderr, " %le %le, %le\n", xGuess[idum],
172 xLowerLimit[idum], xUpperLimit[idum]);
173#endif
174 yNew = yLast * 1e9;
175 /* breaks us out of this loop */
176 stepsTaken = maxSteps;
177 } else {
178 /* measure the function value */
179 yNew = (*func)(xGuess, &isInvalid);
180 totalEvaluations++;
181 if (isInvalid) {
182#if DEBUG
183 fprintf(stderr, " Point is invalid\n");
184#endif
185 yNew = yLast * 1e9;
186 }
187 if (yNew <= target) {
188 /* if the value reaches target, success */
189 if (yNew <= yLast)
190 *yReturn = yNew;
191 else {
192 *yReturn = yLast;
193 xGuess[point] = xLocal;
194 }
195 if (report)
196 (*report)(*yReturn, xGuess, repeats, totalEvaluations, dimensions);
197 return (totalEvaluations);
198 }
199 if (fabs(yNew - yLast) <= tolerance) {
200 /* if the difference reaches tolerance, reach the local minimum */
201 if (yNew <= yLast)
202 minimum[point] = yNew;
203 else {
204 minimum[point] = yLast;
205 xGuess[point] = xLocal;
206 }
207 found = 1;
208 /* fprintf(stderr,"min[%ld]=%e\n",point,minimum[point]); */
209 if (fabs(minimum[point] - min) <= tolerance) {
210 /* if the difference of two minimums reaches tolerance, success */
211 *yReturn = minimum[point];
212 if (report)
213 (*report)(*yReturn, xGuess, repeats, totalEvaluations, dimensions);
214 return (totalEvaluations);
215 } else
216 break;
217 }
218 if (yNew <= yLast) {
219 /*decrease found*/
220#ifdef DEBUG
221 fprintf(stderr, "decrease found, yNew=%le \n", yNew);
222#endif
223 yLast = yNew;
224 xLocal = xGuess[point];
225 decreaseSeen = 1;
226 } else {
227 /* new value is greater than the old, set the PVs back
228 * and break out of this loop. Will result (normally)
229 * in re-entering the loop with opposite sign on step size
230 */
231 xGuess[point] = xLocal;
232 stepsTaken = maxSteps;
233#ifdef DEBUG
234 fprintf(stderr, "Increase seen: setting back to %e\n", xLocal);
235#endif
236 if (flags & ONEDSCANOPTIMIZE_REFRESH) {
237 yLast = (*func)(xGuess, &isInvalid); /*read the measurements again */
238 totalEvaluations++;
239 }
240 if (isInvalid) {
241 fprintf(stderr, "error: initial guess is invalid in OneDScanOptimize()\n");
242 return (-3);
243 }
244 }
245 } /* end of branch for trial points that are within limits */
246 stepsTaken++;
247 } /* end of loop over allowed steps in one direction */
248 divisions++;
249 if ((divisions % 2))
250 /* reverse directions */
251 divisor[point] *= decreaseSeen ? -2 : -1;
252 else
253 /* decrease the step size */
254 divisor[point] *= 3;
255 } /* end of "while (divisions<maxDivisions)" */
256 dxGuess[point] /= 2; /*decrease the delta by 2 for next cycle */
257 } /*end of "for (point=0;point<activeDimensions;point++)" */
258 if (found) {
259 if (minimum[activeDimensions - 1] < min)
260 min = minimum[activeDimensions - 1];
261 /* fprintf(stderr,"min=%e\n",min); */
262 }
263 } /*end of "while (repeats<maxRepeats)" */
264 *yReturn = yLast;
265 if (*yReturn <= target) {
266 if (report)
267 (*report)(*yReturn, xGuess, repeats, totalEvaluations, dimensions);
268 return (totalEvaluations);
269 } else {
270 if (report)
271 (*report)(*yReturn, xGuess, repeats, totalEvaluations, dimensions);
272 /* fprintf(stderr,"The minimum (may not be the best) is %le after %ld cycles of 1dscan.\n",
273 *yReturn, repeats); */
274 return 0;
275 }
276}
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
Definition array.c:59