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