SDDSlib
Loading...
Searching...
No Matches
simplex.c
Go to the documentation of this file.
1/** @file simplex.c
2 * @brief Provides routines for performing multivariate function optimization using the simplex method.
3 *
4 * @copyright
5 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
6 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
7 *
8 * @license
9 * This file is distributed under the terms of the Software License Agreement
10 * found in the file LICENSE included with this distribution.
11 *
12 * @author M. Borland, C. Saunders, R. Soliday, H. Shang, J. Calvey
13 */
14
15#include "mdb.h"
16#include <time.h>
17
18#define DEFAULT_MAXEVALS 100
19#define DEFAULT_MAXPASSES 5
20
21#define SIMPLEX_ABORT 0x0001UL
22static unsigned long simplexFlags = 0;
23
24/**
25 * @brief Abort or query the status of the simplex optimization.
26 *
27 * If a nonzero value is passed, an abort is requested. If zero, this function queries whether
28 * an abort was previously requested.
29 *
30 * @param abort Nonzero to request abort, zero to query.
31 *
32 * @return 1 if abort was requested, 0 otherwise.
33 */
34long simplexMinAbort(unsigned long abort) {
35 if (abort) {
36 /* if zero, then operation is a query */
37 simplexFlags |= SIMPLEX_ABORT;
38 if (abort & SIMPLEX_ABORT_ANNOUNCE_STDOUT) {
39 printf("simplexMin abort requested\n");
40 fflush(stdout);
41 }
42 if (abort & SIMPLEX_ABORT_ANNOUNCE_STDERR)
43 fprintf(stderr, "simplexMin abort requested\n");
44 }
45 return simplexFlags & SIMPLEX_ABORT ? 1 : 0;
46}
47
48long checkVariableLimits(double *x, double *xlo, double *xhi, long n) {
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}
62
63void computeSimplexCenter(double *center, double **vector, long dimensions, long activeDimensions) {
64 long point, direction;
65 for (direction = 0; direction < dimensions; direction++) {
66 /* outer loop over dimension */
67 for (point = center[direction] = 0; point <= activeDimensions; point++)
68 /* inner loop over vectors */
69 center[direction] += vector[point][direction];
70 center[direction] /= activeDimensions; /* sic--not activeDimensions+1, as one term will get
71 * subtracted out later
72 */
73 }
74}
75
76double trialSimplex(
77 double **simplexVector,
78 double *funcValue,
79 double *simplexCenter,
80 double *coordLowerLimit,
81 double *coordUpperLimit,
82 short *disable,
83 long dimensions,
84 long activeDimensions,
85 double (*func)(double *x, long *inval),
86 long worstPoint,
87 long *evaluations,
88 double factor,
89 short *usedLast, short *newPoint) {
90 double *trialVector;
91 long direction, isInvalid;
92 double trialValue, center;
93
94 *newPoint = *usedLast = 0;
95 trialVector = tmalloc(sizeof(*trialVector) * dimensions);
96
97#if DEBUG
98 fprintf(stdout, "Creating new trial simplex\n");
99 fflush(stdout);
100#endif
101
102 for (direction = 0; direction < dimensions; direction++) {
103 /* compute the center of the simplex excluding the worst point */
104 center = simplexCenter[direction] - simplexVector[worstPoint][direction] / activeDimensions;
105 /* Move relative to that center by factor times the distance from it to the worst point.
106 * (In some cases, the "worst point" is actually just the new (improved) point put in the
107 * slot of the previous worst point.)
108 */
109 if (!disable || !disable[direction])
110 trialVector[direction] =
111 center + factor * (simplexVector[worstPoint][direction] - center);
112 else
113 trialVector[direction] = simplexVector[worstPoint][direction];
114 }
115
116 /* check limits on the values of each coordinate of the trial vector */
117 if (!checkVariableLimits(trialVector, coordLowerLimit, coordUpperLimit, dimensions)) {
118 /* return 1e9*funcValue[worstPoint]; this is wrong
119 in the casue of funcValue[worstPoint]<0 */
120#if DEBUG
121 fprintf(stdout, "Variables out of limits\n");
122 fflush(stdout);
123#endif
124 free(trialVector);
125 return DBL_MAX;
126 /*return a positive value so that simplex will do contraction in case of exceeding limits*/
127 } else {
128 /* check to see if this is the same as the last point evaluated here */
129 *usedLast = 0;
130
131#if DEBUG
132 fprintf(stdout, "Evaluating point\n");
133 fflush(stdout);
134#endif
135 trialValue = (*func)(trialVector, &isInvalid);
136 ++(*evaluations);
137 if (isInvalid) {
138#if DEBUG
139 fprintf(stdout, "Invalid point\n");
140 fflush(stdout);
141#endif
142 free(trialVector);
143 return DBL_MAX;
144 }
145 }
146
147 if (trialValue < funcValue[worstPoint]) {
148 /* this is better than the previous worst value, so replace the worst value */
149 *newPoint = 1;
150 funcValue[worstPoint] = trialValue;
151 for (direction = 0; direction < dimensions; direction++) {
152 /* adjust the "center" values for the simplex and copy the new vector coordinates */
153 simplexCenter[direction] += (trialVector[direction] - simplexVector[worstPoint][direction]) / activeDimensions;
154 simplexVector[worstPoint][direction] = trialVector[direction];
155 }
156 }
157#if DEBUG
158 fprintf(stdout, "Returning improved trial point\n");
159 fflush(stdout);
160#endif
161
162 free(trialVector);
163 return (trialValue);
164}
165
166void simplexFindBestWorst(double *fValue, long points,
167 long *bestPointPtr, long *worstPointPtr,
168 long *nextWorstPointPtr) {
169 long bestPoint, worstPoint, nextWorstPoint, point;
170 double fBest, fNextWorst, fWorst;
171
172 if (fValue[0] > fValue[1]) {
173 bestPoint = nextWorstPoint = 1;
174 worstPoint = 0;
175 } else {
176 bestPoint = nextWorstPoint = 0;
177 worstPoint = 1;
178 }
179 fBest = fNextWorst = fValue[bestPoint];
180 fWorst = fValue[worstPoint];
181 for (point = 1; point < points; point++) {
182 if (fBest > fValue[point]) {
183 bestPoint = point;
184 fBest = fValue[point];
185 }
186 if (fWorst < fValue[point]) {
187 worstPoint = point;
188 fWorst = fValue[point];
189 }
190 }
191 for (point = 0; point < points; point++)
192 if (fNextWorst < fValue[point] && point != worstPoint) {
193 fNextWorst = fValue[point];
194 nextWorstPoint = point;
195 }
196 *bestPointPtr = bestPoint;
197 *worstPointPtr = worstPoint;
198 *nextWorstPointPtr = nextWorstPoint;
199}
200
201/**
202 * @brief Perform a simplex-based minimization of a given function.
203 *
204 * This function uses the simplex method to minimize the given function, updating the
205 * simplex until the desired tolerance is reached or maximum evaluations are met.
206 *
207 * @param simplexVector 2D array defining the current simplex vertices.
208 * @param fValue Array of function values at each simplex vertex.
209 * @param coordLowerLimit Array of lower limits for each variable.
210 * @param coordUpperLimit Array of upper limits for each variable.
211 * @param disable Array indicating which variables are fixed (not optimized).
212 * @param dimensions Total number of variables.
213 * @param activeDimensions Number of variables currently being optimized.
214 * @param target Target function value; stop if reached.
215 * @param tolerance Tolerance for stopping criteria.
216 * @param tolerance_mode Defines whether tolerance is absolute (1) or fractional (0).
217 * @param function Pointer to the function to be minimized.
218 * @param maxEvaluations Maximum number of function evaluations allowed.
219 * @param evaluations Pointer to store the number of evaluations performed.
220 * @param flags Bitwise flags modifying the behavior of the minimization.
221 *
222 * @return Nonzero if a solution is found or zero if the iteration limit is reached.
223 */
225 double **simplexVector, /* vectors defining the simplex */
226 double *fValue, /* values of the function at the vertices of the simplex */
227 double *coordLowerLimit, /* lower limits allowed for independent variables */
228 double *coordUpperLimit, /* upper limits allowed for independent variables */
229 short *disable, /* indicates coordinate not involved in optimization */
230 long dimensions, /* number of variables in function */
231 long activeDimensions, /* number of variables changed in optimization */
232 double target, /* will return with any value <= this */
233 double tolerance, /* desired tolerance of minimum value */
234 long tolerance_mode, /* 0==fractional, 1==absolute */
235 double (*function)(double *x, long *invalid),
236 long maxEvaluations,
237 long *evaluations, /* number of function evaluations done during minimization */
238 unsigned long flags) {
239 long point, points, invalids, degenerates, isDegenerate, isInvalid;
240 long direction, bestPoint, worstPoint, nextWorstPoint;
241 double fTrial, fProblem, fWorst, fBest, merit, denominator;
242 double *simplexCenter = NULL, *tmpVector;
243 short usedLast, usedLastCount = 0, newPoint;
244 long reflectionWorked = 0, extensionWorked = 0, contractionWorked = 0, shrinkingDone = 0;
245 long progressMade;
246
247 simplexCenter = tmalloc(sizeof(*simplexCenter) * (dimensions));
248 tmpVector = tmalloc(sizeof(*tmpVector) * (dimensions));
249
250 *evaluations = 0;
251 if (maxEvaluations <= 0)
252 maxEvaluations = DEFAULT_MAXEVALS;
253
254 computeSimplexCenter(simplexCenter, simplexVector, dimensions, activeDimensions);
255
256 points = activeDimensions + 1;
257 while (*evaluations < maxEvaluations && !(simplexFlags & SIMPLEX_ABORT)) {
258 /* find indices of lowest, highest, and next-to-highest y values .
259 These starting values are to guarantee that worstPoint!=bestPoint even if
260 all function values are the same.
261 */
262 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
263 fprintf(stdout, "simplexMinimization: finding best and worst points\n");
264 fflush(stdout);
265 }
266 simplexFindBestWorst(fValue, points, &bestPoint, &worstPoint, &nextWorstPoint);
267 fBest = fValue[bestPoint];
268 fWorst = fValue[worstPoint];
269
270 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
271 fprintf(stdout, "simplexMinimization: evaluating present results\n");
272 fflush(stdout);
273 }
274 /* evaluate the merit of the present vectors */
275 if (tolerance_mode == 0) {
276 /* fractional tolerance */
277 if ((denominator = (fabs(fWorst) + fabs(fBest)) / 2))
278 merit = fabs(fWorst - fBest) / denominator;
279 else {
280 fputs("error: divide-by-zero in fractional tolerance evaluation (simplexMinimization)\n", stderr);
281 free(simplexCenter);
282 free(tmpVector);
283 return 0;
284 }
285 } else
286 /* absolute tolerance */
287 merit = fabs(fWorst - fBest);
288 if (merit < tolerance || fBest <= target) {
289 /* tolerance exceeded, or value small enough */
290 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
291 fprintf(stdout, "simplexMinimization: tolerance exceed or value small enough\n");
292 fflush(stdout);
293 }
294 break;
295 }
296
297 progressMade = 0;
298 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
299 fprintf(stdout, "simplexMinimization: Reflecting simplex\n");
300 fflush(stdout);
301 }
302 /* Reflect the simplex through the high point */
303 fTrial = trialSimplex(simplexVector, fValue, simplexCenter, coordLowerLimit,
304 coordUpperLimit, disable, dimensions, activeDimensions, function,
305 worstPoint, evaluations, -1.0, &usedLast, &newPoint);
306 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
307 fprintf(stdout, "simplexMinization: reflection returns (newPoint=%d)\n", newPoint);
308 fflush(stdout);
309 }
310 reflectionWorked += newPoint ? 1 : 0;
311 progressMade += newPoint;
312 if (usedLast)
313 usedLastCount++;
314 else
315 usedLastCount = 0;
316 if (usedLastCount > 2) {
317 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
318 fprintf(stdout, "simplexMinization: simplex is looping--ending iterations\n");
319 fflush(stdout);
320 }
321 /* stuck in some kind of loop */
322 break;
323 }
324 if (fTrial < fValue[bestPoint]) {
325 /* since this worked, extend the simplex by the same amount in that direction.
326 * relies on the fact that the new point of the simplex is in the old "worstPoint"
327 * slot
328 */
329 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
330 fprintf(stdout, "simplexMinization: extending simplex\n");
331 fflush(stdout);
332 }
333 fTrial = trialSimplex(simplexVector, fValue, simplexCenter, coordLowerLimit,
334 coordUpperLimit, disable, dimensions, activeDimensions, function,
335 worstPoint, evaluations, 2.0, &usedLast, &newPoint);
336 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
337 fprintf(stdout, "simplexMinization: extension returns (newPoint=%d)\n", newPoint);
338 fflush(stdout);
339 }
340 extensionWorked += newPoint ? 1 : 0;
341 progressMade += newPoint;
342 } else if (fTrial > fValue[nextWorstPoint]) {
343 /* reflection through the simplex didn't help, so try contracting away from worst point without
344 * going through the face opposite the worst point */
345 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
346 fprintf(stdout, "simplexMinization: contracting simplex\n");
347 fflush(stdout);
348 }
349 fProblem = fTrial;
350 fTrial = trialSimplex(simplexVector, fValue, simplexCenter, coordLowerLimit,
351 coordUpperLimit, disable, dimensions, activeDimensions, function,
352 worstPoint, evaluations, 0.5, &usedLast, &newPoint);
353 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
354 fprintf(stdout, "simplexMinization: contraction returns (newPoint=%d)\n", newPoint);
355 fflush(stdout);
356 }
357 contractionWorked += newPoint ? 1 : 0;
358 progressMade += newPoint;
359 if (fTrial > fProblem) {
360 /* the new point is worse than the old trial point, so try moving the entire simplex in on the
361 best point by averaging each vector with the vector to the best point. Don't allow invalid points,
362 however, and keep track of the number of degenerate points (those with the same vector or the
363 same function value).
364 */
365 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
366 fprintf(stdout, "simplexMinimization: contracting on best point\n");
367 fflush(stdout);
368 }
369 invalids = degenerates = 0;
370 for (point = 0; point < points; point++) {
371 if (point == bestPoint)
372 continue;
373 for (direction = 0; direction < dimensions; direction++)
374 tmpVector[direction] = 0.5 * (simplexVector[point][direction] + simplexVector[bestPoint][direction]);
375 for (direction = 0; direction < dimensions; direction++)
376 if (tmpVector[direction] != simplexVector[point][direction])
377 break;
378 isInvalid = 0;
379 if (!(isDegenerate = direction != dimensions)) {
380 fTrial = (*function)(tmpVector, &isInvalid);
381 if (!isInvalid) {
382 if (fTrial == fValue[point])
383 isDegenerate = 1;
384 for (direction = 0; direction < dimensions; direction++)
385 simplexVector[point][direction] = tmpVector[direction];
386 fValue[point] = fTrial;
387 }
388 }
389 if (isInvalid)
390 invalids++;
391 if (isDegenerate)
392 degenerates++;
393 }
394 shrinkingDone++;
395 if (invalids + degenerates >= points - 1) {
396 SWAP_PTR(simplexVector[0], simplexVector[bestPoint]);
397 SWAP_DOUBLE(fValue[0], fValue[bestPoint]);
398 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
399 fprintf(stdout, "simplexMinimization exiting: reflection: %ld extension: %ld contraction: %ld shrinking: %ld\n",
400 reflectionWorked, extensionWorked, contractionWorked, shrinkingDone);
401 fflush(stdout);
402 }
403 free(simplexCenter);
404 free(tmpVector);
405 return 0;
406 }
407 *evaluations += points;
408 /* since the simplex was changed without using trialSimplex, the "center" must be recomputed
409 */
410 progressMade += 1;
411 computeSimplexCenter(simplexCenter, simplexVector, dimensions, activeDimensions);
412 }
413 }
414 if (!progressMade) {
415 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
416 fprintf(stdout, "simplexMinimization: Breaking out of loop--no progress.\n");
417 fflush(stdout);
418 }
419 break;
420 }
421 }
422 simplexFindBestWorst(fValue, points, &bestPoint, &worstPoint, &nextWorstPoint);
423 if (*evaluations >= maxEvaluations) {
424 SWAP_PTR(simplexVector[0], simplexVector[bestPoint]);
425 SWAP_DOUBLE(fValue[0], fValue[bestPoint]);
426 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
427 fprintf(stdout, "simplexMinimization: too many iterations\n");
428 fflush(stdout);
429 }
430 free(simplexCenter);
431 free(tmpVector);
432 return 0;
433 }
434 SWAP_PTR(simplexVector[0], simplexVector[bestPoint]);
435 SWAP_DOUBLE(fValue[0], fValue[bestPoint]);
436 if (flags & SIMPLEX_VERBOSE_LEVEL2) {
437 fprintf(stdout, "simplexMinimization exit report: reflection: %ld extension: %ld contraction: %ld shrinking: %ld\n",
438 reflectionWorked, extensionWorked, contractionWorked, shrinkingDone);
439 fflush(stdout);
440 }
441 free(simplexCenter);
442 free(tmpVector);
443 return (1);
444}
445
446/**
447 * @brief Top-level convenience function for simplex-based minimization.
448 *
449 * This function sets up and runs a simplex optimization on the provided function,
450 * attempting to find a minimum within given constraints and stopping criteria.
451 *
452 * @param yReturn Pointer to store the best found function value.
453 * @param xGuess Initial guess for the variables.
454 * @param dxGuess Initial step sizes for each variable (may be adjusted automatically).
455 * @param xLowerLimit Lower variable limits.
456 * @param xUpperLimit Upper variable limits.
457 * @param disable Array indicating which variables are fixed.
458 * @param dimensions Total number of variables.
459 * @param target Target function value to stop if reached.
460 * @param tolerance Tolerance for stopping criteria. Negative means fractional.
461 * @param func Pointer to the objective function.
462 * @param report Optional reporting function called at each pass.
463 * @param maxEvaluations Maximum function evaluations.
464 * @param maxPasses Maximum passes over the simplex.
465 * @param maxDivisions Maximum allowed divisions in initial simplex setup.
466 * @param divisorFactor Factor to adjust step size.
467 * @param passRangeFactor Factor to adjust range after each pass.
468 * @param flags Bitwise flags for controlling verbosity and behavior.
469 *
470 * @return Number of evaluations if successful, negative on error, or if aborted.
471 */
473 double *yReturn,
474 double *xGuess,
475 double *dxGuess,
476 double *xLowerLimit,
477 double *xUpperLimit,
478 short *disable,
479 long dimensions,
480 double target, /* will return if any value is <= this */
481 double tolerance, /* <0 means fractional, >0 means absolute */
482 double (*func)(double *x, long *invalid),
483 void (*report)(double ymin, double *xmin, long pass, long evals, long dims),
484 long maxEvaluations,
485 long maxPasses,
486 long maxDivisions,
487 double divisorFactor, /* for old default behavior, set to 3 */
488 double passRangeFactor, /* for old default behavior, set to 1 */
489 unsigned long flags) {
490 double **simplexVector = NULL, *y = NULL, *trialVector = NULL, *dxLocal = NULL;
491 long *dimIndex = NULL;
492 double yLast, dVector = 1, divisor, denominator, merit;
493 long direction, point, evaluations, totalEvaluations = 0, isInvalid, pass = 0, step, divisions;
494 long activeDimensions, dimension, i;
495
496 if (divisorFactor <= 1.0)
497 divisorFactor = 3; /* old default value */
498 simplexFlags = 0;
499 if (dimensions <= 0)
500 return (-3);
501 if (disable) {
502 activeDimensions = 0;
503 for (direction = 0; direction < dimensions; direction++)
504 if (!disable[direction])
505 activeDimensions++;
506 } else
507 activeDimensions = dimensions;
508 if (activeDimensions <= 0)
509 return -3;
510
511 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
512 fprintf(stdout, "simplexMin: Active dimensions: %ld\n", activeDimensions);
513 fflush(stdout);
514 }
515 simplexVector = (double **)zarray_2d(sizeof(**simplexVector), activeDimensions + 1, dimensions);
516 y = tmalloc(sizeof(*y) * (activeDimensions + 1));
517 trialVector = tmalloc(sizeof(*trialVector) * activeDimensions);
518 dxLocal = tmalloc(sizeof(*dxLocal) * activeDimensions);
519 dimIndex = tmalloc(sizeof(*dimIndex) * activeDimensions);
520
521 for (direction = i = 0; direction < dimensions; direction++) {
522 if (!disable || !disable[direction])
523 dimIndex[i++] = direction;
524 }
525 if (i != activeDimensions) {
526 fprintf(stderr, "Fatal error (simplexMin): active dimensions not properly counted\n");
527 exit(1);
528 }
529
530 if (!dxGuess) {
531 dxGuess = dxLocal;
532 for (direction = 0; direction < dimensions; direction++)
533 dxGuess[direction] = 0;
534 }
535 if (flags & SIMPLEX_RANDOM_SIGNS) {
536 time_t intTime;
537 time(&intTime);
538 srand(intTime);
539 }
540 for (direction = 0; direction < dimensions; direction++) {
541 if (dxGuess[direction] == 0) {
542 if (xLowerLimit && xUpperLimit)
543 dxGuess[direction] = (xUpperLimit[direction] - xLowerLimit[direction]) / 4;
544 else if ((dxGuess[direction] = xGuess[direction] / 4) == 0)
545 dxGuess[direction] = 1;
546 }
547 if (flags & SIMPLEX_RANDOM_SIGNS) {
548 if (rand() > RAND_MAX / 2.0)
549 dxGuess[direction] *= -1;
550 }
551 if (xLowerLimit && xUpperLimit) {
552 if ((dVector = fabs(xUpperLimit[direction] - xLowerLimit[direction]) / 4) < fabs(dxGuess[direction]))
553 dxGuess[direction] = dVector;
554 }
555 if (disable && disable[direction])
556 dxGuess[direction] = 0;
557 }
558
559 if (xLowerLimit) {
560 /* if start is at lower limit, make sure initial step is positive */
561 for (direction = 0; direction < dimensions; direction++)
562 if (xLowerLimit[direction] >= xGuess[direction])
563 dxGuess[direction] = fabs(dxGuess[direction]);
564 }
565 if (xUpperLimit) {
566 /* if start is at upper limit, make sure initial step is negative */
567 for (direction = 0; direction < dimensions; direction++)
568 if (xUpperLimit[direction] <= xGuess[direction])
569 dxGuess[direction] = -fabs(dxGuess[direction]);
570 }
571 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
572 fprintf(stdout, "simplexMin: starting conditions:\n");
573 for (direction = 0; direction < dimensions; direction++)
574 fprintf(stdout, "direction %ld: guess=%le delta=%le disable=%hd\n",
575 direction, xGuess[direction], dxGuess[direction],
576 disable ? disable[direction] : (short)0);
577 fflush(stdout);
578 }
579
580 if (maxPasses <= 0)
581 maxPasses = DEFAULT_MAXPASSES;
582
583 /* need this to prevent problems when abort occurs before the
584 * initial simplex is formed
585 */
586 for (point = 0; point < activeDimensions + 1; point++)
587 y[point] = DBL_MAX;
588
589 while (pass < maxPasses && !(simplexFlags & SIMPLEX_ABORT)) {
590 /* Set up the initial simplex */
591 /* The first vertex is just the starting point */
592 for (direction = 0; direction < dimensions; direction++)
593 simplexVector[0][direction] = xGuess[direction];
594 *yReturn = y[0] = (*func)(simplexVector[0], &isInvalid);
595 totalEvaluations++;
596 pass++;
597 if (isInvalid) {
598 fprintf(stderr, "error: initial guess is invalid in simplexMin()\n");
599 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
600 free(y);
601 free(trialVector);
602 free(dxLocal);
603 free(dimIndex);
604 return (-3);
605 }
606 if (y[0] <= target) {
607 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
608 fprintf(stdout, "simplexMin: target value achieved in initial simplex setup.\n");
609 fflush(stdout);
610 }
611 if (report)
612 (*report)(y[0], simplexVector[0], pass, totalEvaluations, dimensions);
613 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
614 free(y);
615 free(trialVector);
616 free(dxLocal);
617 free(dimIndex);
618 return (totalEvaluations);
619 }
620
621 divisor = 1;
622 divisions = 0;
623 for (point = 1; !(simplexFlags & SIMPLEX_ABORT) && point < activeDimensions + 1; point++) {
624 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
625 fprintf(stdout, "simplexMin: Setting initial simplex for direction %ld\n", point - 1);
626 fflush(stdout);
627 }
628 dimension = dimIndex[point - 1];
629 if (!(flags & SIMPLEX_NO_1D_SCANS)) {
630 /* Set up the rest of the simplex. Each vertex is found by doing a 1-D scan
631 * starting with the first or the last vertex.
632 */
633 for (direction = 0; direction < dimensions; direction++)
634 simplexVector[point][direction] = simplexVector[(flags & SIMPLEX_START_FROM_VERTEX1) ? 0 : point - 1][direction];
635
636 /* Scan direction point-1 until a direction of improvement is found. */
637 divisions = 0;
638 divisor = 1;
639 yLast = y[point - 1];
640 while (divisions < maxDivisions && !(simplexFlags & SIMPLEX_ABORT)) {
641 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
642 fprintf(stdout, "simplexMin: working on division %ld (divisor=%e) for direction %ld\n",
643 divisions, divisor, point - 1);
644 fflush(stdout);
645 }
646 simplexVector[point][dimension] = simplexVector[point - 1][dimension] + dxGuess[dimension] / divisor;
647 if ((xLowerLimit || xUpperLimit) &&
648 !checkVariableLimits(simplexVector[point], xLowerLimit, xUpperLimit, dimensions)) {
649#if DEBUG
650 long idum;
651 fprintf(stdout, " Point outside of bounds:\n");
652 fflush(stdout);
653 for (idum = 0; idum < dimensions; idum++)
654 fprintf(stdout, " %le %le, %le\n", simplexVector[point][idum],
655 xLowerLimit[idum], xUpperLimit[idum]);
656 fflush(stdout);
657#endif
658 /* y[point] = fabs(y[0])*1e9;*/
659 y[point] = DBL_MAX;
660 } else {
661 y[point] = (*func)(simplexVector[point], &isInvalid);
662 totalEvaluations++;
663 if (isInvalid) {
664#if DEBUG
665 fprintf(stdout, " Point is invalid\n");
666 fflush(stdout);
667#endif
668 /* y[point] = fabs(y[0])*1e9; */
669 y[point] = DBL_MAX;
670 }
671 if (y[point] <= target) {
672 for (direction = 0; direction < dimensions; direction++)
673 xGuess[direction] = simplexVector[point][direction];
674 *yReturn = y[point];
675 if (report)
676 (*report)(*yReturn, xGuess, pass, totalEvaluations, dimensions);
677 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
678 fprintf(stdout, "simplexMin: invalid function status. Returning.\n");
679 fflush(stdout);
680 }
681 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
682 free(y);
683 free(trialVector);
684 free(dxLocal);
685 free(dimIndex);
686 return (totalEvaluations);
687 }
688 }
689 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
690 fprintf(stdout, "simplexMin: New value: %le Last value: %le\n", y[point], yLast);
691 fflush(stdout);
692 }
693 if (y[point] < yLast)
694 /* decrease found */
695 break;
696 divisions++;
697 if (divisions % 2)
698 /* reverse directions */
699 divisor *= -1;
700 else
701 /* decrease the step size */
702 divisor *= divisorFactor;
703 }
704 }
705 if ((flags & SIMPLEX_NO_1D_SCANS) || divisions == maxDivisions) {
706 for (direction = 0; direction < dimensions; direction++)
707 simplexVector[point][direction] = simplexVector[0][direction];
708
709 /* Try +/-step-size/divisor until a valid point is found */
710 divisions = 0;
711 divisor = 1;
712 yLast = y[point - 1];
713 while (divisions < maxDivisions && !(simplexFlags & SIMPLEX_ABORT)) {
714#if DEBUG
715 fprintf(stdout, "Trying divisor %ld\n", divisions);
716 fflush(stdout);
717#endif
718 simplexVector[point][dimension] = simplexVector[0][dimension] +
719 dxGuess[dimension] / divisor;
720 if ((xLowerLimit || xUpperLimit) &&
721 !checkVariableLimits(simplexVector[point], xLowerLimit, xUpperLimit, dimensions)) {
722 divisions++;
723 } else {
724 y[point] = (*func)(simplexVector[point], &isInvalid);
725 totalEvaluations++;
726 if (isInvalid) {
727#if DEBUG
728 fprintf(stdout, " Point is invalid\n");
729 fflush(stdout);
730#endif
731 /* y[point] = fabs(y[0])*1e9; */
732 y[point] = DBL_MAX;
733 divisions++;
734 } else
735 break;
736 }
737 if (divisions % 2)
738 /* reverse directions */
739 divisor *= -1;
740 else
741 /* decrease the step size */
742 divisor *= 10;
743 }
744 if (divisions == maxDivisions) {
745 fprintf(stderr, "error: can't find valid initial simplex in simplexMin()\n");
746 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
747 free(y);
748 free(trialVector);
749 free(dxLocal);
750 free(dimIndex);
751 return (-4);
752 }
753
754 } else {
755 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
756 fprintf(stdout, "simplexMin: decrease found---trying more steps\n");
757 fflush(stdout);
758 }
759 /* decrease found---try a few more steps in this direction */
760 for (step = 0; !(simplexFlags & SIMPLEX_ABORT) && step < 3; step++) {
761 divisor /= divisorFactor; /* increase step size */
762 simplexVector[point][dimension] += dxGuess[dimension] / divisor;
763 if ((xLowerLimit || xUpperLimit) &&
764 !checkVariableLimits(simplexVector[point], xLowerLimit, xUpperLimit, dimensions)) {
765 simplexVector[point][dimension] -= dxGuess[dimension] / divisor;
766 break;
767 }
768 yLast = y[point];
769 y[point] = (*func)(simplexVector[point], &isInvalid);
770 totalEvaluations++;
771 if (isInvalid || y[point] > yLast) {
772 simplexVector[point][dimension] -= dxGuess[dimension] / divisor;
773 y[point] = yLast;
774 break;
775 }
776 if (y[point] <= target) {
777 for (direction = 0; direction < dimensions; direction++)
778 xGuess[direction] = simplexVector[point][direction];
779 *yReturn = y[point];
780 if (report)
781 (*report)(*yReturn, xGuess, pass, totalEvaluations, dimensions);
782 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
783 fprintf(stdout, "simplexMin: value below target during 1D scan---returning\n");
784 fflush(stdout);
785 }
786 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
787 free(y);
788 free(trialVector);
789 free(dxLocal);
790 free(dimIndex);
791 return totalEvaluations;
792 }
793 }
794 }
795 }
796
797 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
798 fprintf(stdout, "simplexMin: Starting simplex: \n");
799 for (point = 0; point < activeDimensions + 1; point++) {
800 fprintf(stdout, "V%2ld %.5g: ", point, y[point]);
801 for (direction = 0; direction < dimensions; direction++)
802 fprintf(stdout, "%.5g ", simplexVector[point][direction]);
803 fprintf(stdout, "\n");
804 }
805 fflush(stdout);
806 }
807
808 if (simplexFlags & SIMPLEX_ABORT) {
809 long best = 0;
810 for (point = 1; point < activeDimensions + 1; point++)
811 if (y[point] < y[best])
812 best = point;
813 for (direction = 0; direction < dimensions; direction++)
814 xGuess[direction] = simplexVector[best][direction];
815 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
816 fprintf(stdout, "simplexMin: abort received before simplex began---returning\n");
817 fflush(stdout);
818 }
819 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
820 free(y);
821 free(trialVector);
822 free(dxLocal);
823 free(dimIndex);
824 return totalEvaluations;
825 }
826
827 evaluations = 0;
828 simplexMinimization(simplexVector, y, xLowerLimit, xUpperLimit, disable,
829 dimensions, activeDimensions, target,
830 fabs(tolerance), (tolerance < 0 ? 0 : 1), func, maxEvaluations, &evaluations,
831 flags);
832 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
833 fprintf(stdout, "simplexMin: returned from simplexMinimization after %ld evaluations\n",
834 evaluations);
835 fflush(stdout);
836 }
837 totalEvaluations += evaluations;
838 for (point = 1; point < activeDimensions + 1; point++) {
839 if (y[0] > y[point]) {
840 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
841 free(y);
842 free(trialVector);
843 free(dxLocal);
844 free(dimIndex);
845 bomb("problem with ordering of data from simplexMinimization", NULL);
846 }
847 }
848
849 /* Copy the new best result into the guess vector (for return or re-use) */
850 for (direction = 0; direction < dimensions; direction++)
851 xGuess[direction] = simplexVector[0][direction];
852
853 if (report)
854 (*report)(y[0], simplexVector[0], pass, totalEvaluations, dimensions);
855
856 if (y[0] <= target || (simplexFlags & SIMPLEX_ABORT)) {
857 *yReturn = y[0];
858 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
859 fprintf(stdout, "simplexMin: target value achieved---returning\n");
860 fflush(stdout);
861 }
862 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
863 free(y);
864 free(trialVector);
865 free(dxLocal);
866 free(dimIndex);
867 return (totalEvaluations);
868 }
869
870 if (tolerance <= 0) {
871 denominator = (y[0] + (*yReturn)) / 2;
872 if (denominator)
873 merit = fabs(y[0] - (*yReturn)) / denominator;
874 else {
875 fputs("error: divide-by-zero in fractional tolerance evaluation (simplexMin)\n", stderr);
876 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
877 free(y);
878 free(trialVector);
879 free(dxLocal);
880 free(dimIndex);
881 return -1;
882 }
883 } else
884 merit = fabs(y[0] - (*yReturn));
885 if (merit <= fabs(tolerance) || y[0] <= target)
886 break;
887
888 /* Set up step sizes for finding the new simplex */
889 for (direction = 0; direction < dimensions; direction++) {
890 double min, max;
891 min = max = simplexVector[0][direction];
892 for (point = 1; point < activeDimensions + 1; point++) {
893 if (simplexVector[point][direction] > max)
894 max = simplexVector[point][direction];
895 if (simplexVector[point][direction] < min)
896 min = simplexVector[point][direction];
897 }
898 if (max > min)
899 dxGuess[direction] = passRangeFactor * (max - min);
900 }
901 }
902
903 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
904 fprintf(stdout, "simplexMin: iterations exhausted---returning\n");
905 fflush(stdout);
906 }
907 *yReturn = y[0];
908
909 free_zarray_2d((void **)simplexVector, activeDimensions + 1, dimensions);
910 free(y);
911 free(trialVector);
912 free(dxLocal);
913 free(dimIndex);
914
915 if (pass > maxPasses)
916 return (-2);
917 return (totalEvaluations);
918}
919
920/**
921 * @brief Enforce variable limits on a given vector of variables.
922 *
923 * @note
924 * - No longer used---checkVariableLimits is used instead.
925 * @param x Array of variable values to be checked and corrected.
926 * @param xlo Array of lower limits for each variable.
927 * @param xhi Array of upper limits for each variable.
928 * @param n Number of variables.
929 */
930void enforceVariableLimits(double *x, double *xlo, double *xhi, long n) {
931 long i;
932
933 if (xlo)
934 for (i = 0; i < n; i++)
935 if ((!xhi || xlo[i] != xhi[i]) && x[i] < xlo[i])
936 x[i] = xlo[i];
937
938 if (xhi)
939 for (i = 0; i < n; i++)
940 if ((!xlo || xlo[i] != xhi[i]) && x[i] > xhi[i])
941 x[i] = xhi[i];
942}
943
void ** zarray_2d(uint64_t size, uint64_t n1, uint64_t n2)
Allocates a 2D array with specified dimensions.
Definition array.c:93
int free_zarray_2d(void **array, uint64_t n1, uint64_t n2)
Frees a 2D array and its associated memory.
Definition array.c:155
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
Definition array.c:59
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
Definition bomb.c:26
long simplexMinimization(double **simplexVector, double *fValue, double *coordLowerLimit, double *coordUpperLimit, short *disable, long dimensions, long activeDimensions, double target, double tolerance, long tolerance_mode, double(*function)(double *x, long *invalid), long maxEvaluations, long *evaluations, unsigned long flags)
Perform a simplex-based minimization of a given function.
Definition simplex.c:224
long simplexMinAbort(unsigned long abort)
Abort or query the status of the simplex optimization.
Definition simplex.c:34
void enforceVariableLimits(double *x, double *xlo, double *xhi, long n)
Enforce variable limits on a given vector of variables.
Definition simplex.c:930
long simplexMin(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 maxEvaluations, long maxPasses, long maxDivisions, double divisorFactor, double passRangeFactor, unsigned long flags)
Top-level convenience function for simplex-based minimization.
Definition simplex.c:472