/*************************************************************************\ * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ /* $Log: simplex.c,v $ Revision 1.29 2004/04/01 14:39:45 borland Added SIMPLEX_VERBOSE_LEVEL1 and SIMPLEX_VERBOSE_LEVEL2 flags for simplexMin() and simplexMinimization(). Revision 1.28 2003/01/21 18:55:05 borland simplexMin() has another argument: a factor by which to multiply the parameter ranges at the end of a pass to get the initial step sizes for the next pass. A value of 1.0 reproduces the old behavior. Revision 1.27 2003/01/15 22:59:17 borland Added SIMPLEX_START_FROM_VERTEX1 flag for simplexMin(). Added simplexDivisor argument for simplexMin(); default value is 3 to reproduce old behavior. Revision 1.26 2002/08/14 16:19:00 soliday Added Open License Revision 1.25 2002/07/13 21:05:30 borland Added optional randomization of signs for the initial steps. Revision 1.24 2002/05/08 17:16:00 shang replaced the return value (or function value) by DBL_MAX in the case of out of limits and other invalid calculations. Revision 1.23 2002/03/30 16:31:43 borland Fixed logic around a debugging statement in simplexMinAbort(). Revision 1.22 2002/02/22 22:41:00 borland Fixed potential uninitialized variable problem. Revision 1.21 2002/02/18 17:42:48 borland Added external abort feature and fixed a bug that sometimes resulted in an infinite loop. Revision 1.20 2002/01/07 22:18:42 borland Removed some spurious characters after #endif's. Revision 1.19 2002/01/02 22:08:33 borland Fixed problem with UMR related to usedLast variable. Revision 1.18 2001/07/23 16:36:08 borland Added maxDivisions argument and changed the divisor for 1d prescan from 10 to 3. Revision 1.16 2001/05/15 16:39:13 borland Added flags argument to simplexMin(). At present, this permits disabling the 1D scans that are normally done prior to the actual simplex routine. Revision 1.15 2000/03/27 20:30:31 borland Error message now goes to stderr rather than stdout. Revision 1.14 1999/08/11 19:56:41 borland Added more checks for results below the target. Should check more frequently in simplexMinimization, also. Revision 1.13 1999/06/12 19:12:54 borland Turned off debugging. Revision 1.12 1999/06/12 19:12:41 borland Inserted a test to break out of the simplex loop if no progress is made. Prevents an infinite loop when one of the variables hits a limit. Revision 1.11 1999/05/28 14:57:49 soliday Removed compiler warnings under Linux. Revision 1.10 1999/02/11 20:45:02 borland Now calls report function when terminating due to reaching the target. Revision 1.9 1999/01/21 15:03:02 borland Now chooses initial step sizes more carefully to avoid getting into unproductive loops. Problem occurred when user gave step sizes that were >1/2 the distance from lower to upper limit. Revision 1.8 1999/01/07 21:52:59 borland Turned off debugging statements. Revision 1.7 1999/01/07 21:45:20 borland Added capability of removing some variables from the optimization, without the need to change the function being optimized. Revision 1.6 1999/01/05 19:38:52 borland Fixed bug in checks of boundaries of variables. Revision 1.5 1998/12/23 19:50:08 borland Improved method of finding initial simplex---now uses a series of 1-d scans to find a direction of improvement in each dimension; the initialization is done again at the start of each pass. Method of implementing limits on variables was changed; limits are no longer perfectly hard-edged. Fixed a bug in simplexMinimization that resulted in the wrong vector being returned as the optimum. Revision 1.4 1998/11/16 22:16:16 borland Fixed bug that resulted in infinite looping inside simplexMinimization. Revision 1.3 1998/11/12 17:44:54 borland Commented-out a warning message about too many iterations. * Revision 1.2 1995/09/05 21:20:52 saunders * First test release of the SDDS1.5 package. * */ /* file: simplex.c * contents: * simplexMin convenient top-level routine * simplexMinimization primarily used by simplexMin * computeSimplexCenter used by simplexMinimization * trialSimplex used by simplexMinimization * enforceVariableLimits used by simplexMin and simplexMinimization * * M.Borland, 1991, 1993, 1995. * Based partly on routines in Numerical Recipes in C. */ #include "mdb.h" #include void enforceVariableLimits(double *x, double *xlo, double *xhi, long n); 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); void computeSimplexCenter(double *center, double **vector, long dimensions, long activeDimensions); double trialSimplex(double **simplexVector, double *funcValue, double *simplexCenter, double *coordLowerLimit, double *coordUpperLimit, short *disable, long dimensions, long activeDimensions, double (*func)(double *x, long *inval), long iWorst, long *evaluations, double factor, short *usedLast, short *newPoint); long checkVariableLimits(double *x, double *xlo, double *xhi, long n); void simplexFindBestWorst(double *fValue, long points, long *bestPointPtr, long *worstPointPtr, long *nextWorstPointPtr); #define DEFAULT_MAXEVALS 100 #define DEFAULT_MAXPASSES 5 #define DEBUG 0 #define SIMPLEX_ABORT 0x0001UL static unsigned long simplexFlags = 0; long simplexMinAbort(long abort) { if (abort) { /* if zero, then operation is a query */ simplexFlags |= SIMPLEX_ABORT; #ifdef DEBUG fprintf(stderr, "simplexMin abort requested\n"); #endif } return simplexFlags&SIMPLEX_ABORT ? 1 : 0; } long simplexMin( double *yReturn, double *xGuess, double *dxGuess, double *xLowerLimit, double *xUpperLimit, short *disable, long dimensions, double target, /* will return if any value is <= this */ double tolerance, /* <0 means fractional, >0 means absolute */ 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, /* for old default behavior, set to 3 */ double passRangeFactor, /* for old default behavior, set to 1 */ unsigned long flags ) { static double **simplexVector = NULL, *y = NULL, *trialVector = NULL, *dxLocal = NULL; static long lastDimensions = 0; static long *dimIndex = NULL; double yLast, dVector = 1, divisor, denominator, merit; long direction, point, evaluations, totalEvaluations = 0, isInvalid, pass=0, step, divisions; long activeDimensions, dimension, i; if (divisorFactor<=1.0) divisorFactor = 3; /* old default value */ simplexFlags = 0; if (dimensions<=0) return(-3); if (disable) { activeDimensions = 0; for (direction=0; directionlastDimensions) { if (simplexVector) free_zarray_2d((void**)simplexVector, lastDimensions+1, lastDimensions); simplexVector = (double**)zarray_2d(sizeof(**simplexVector), activeDimensions+1, dimensions); if (y) free(y); y = tmalloc(sizeof(*y)*(activeDimensions+1)); if (trialVector) free(trialVector); trialVector = tmalloc(sizeof(*trialVector)*activeDimensions); if (dxLocal) free(dxLocal); dxLocal = tmalloc(sizeof(*dxLocal)*activeDimensions); if (dimIndex) free(dimIndex); dimIndex = tmalloc(sizeof(*dimIndex)*activeDimensions); lastDimensions = activeDimensions; } for (direction=i=0; directionRAND_MAX/2.0) dxGuess[direction] *= -1; } if (xLowerLimit && xUpperLimit) { if ((dVector = fabs(xUpperLimit[direction]-xLowerLimit[direction])/4)=xGuess[direction]) dxGuess[direction] = fabs(dxGuess[direction]); } if (xUpperLimit) { /* if start is at upper limit, make sure initial step is negative */ for (direction=0; directionyLast) { simplexVector[point][dimension] -= dxGuess[dimension]/divisor; y[point] = yLast; break; } if (y[point]<=target) { for (direction=0; directiony[point]) { bomb("problem with ordering of data from simplexMinimization", NULL); } } /* Copy the new best result into the guess vector (for return or re-use) */ for (direction=0; directionmax) max = simplexVector[point][direction]; if (simplexVector[point][direction]min) dxGuess[direction] = passRangeFactor*(max-min); } } if (flags&SIMPLEX_VERBOSE_LEVEL1) fprintf(stdout, "simplexMin: iterations exhausted---returning\n"); *yReturn = y[0]; if (pass>maxPasses) return(-2); return(totalEvaluations); } long simplexMinimization( double **simplexVector, /* vectors defining the simplex */ double *fValue, /* values of the function at the vertices of the simplex */ double *coordLowerLimit, /* lower limits allowed for independent variables */ double *coordUpperLimit, /* upper limits allowed for independent variables */ short *disable, /* indicates coordinate not involved in optimization */ long dimensions, /* number of variables in function */ long activeDimensions, /* number of variables changed in optimization */ double target, /* will return with any value <= this */ double tolerance, /* desired tolerance of minimum value */ long tolerance_mode, /* 0==fractional, 1==absolute */ double (*function)(double *x, long *invalid), long maxEvaluations, long *evaluations, /* number of function evaluations done during minimization */ unsigned long flags ) { long point, points, invalids, degenerates, isDegenerate, isInvalid; long direction, bestPoint, worstPoint, nextWorstPoint; double fTrial, fProblem, fWorst, fBest, merit, denominator; static long maxDimensions = 0; static double *simplexCenter = NULL, *tmpVector; short usedLast, usedLastCount=0, newPoint; long reflectionWorked=0, extensionWorked=0, contractionWorked=0, shrinkingDone=0; long progressMade; if (dimensions>maxDimensions) { if (simplexCenter) free(simplexCenter); simplexCenter = tmalloc(sizeof(*simplexCenter)*(maxDimensions=dimensions)); if (tmpVector) free(tmpVector); tmpVector = tmalloc(sizeof(*tmpVector)*(maxDimensions=dimensions)); } *evaluations = 0; if (maxEvaluations<=0) maxEvaluations = DEFAULT_MAXEVALS; computeSimplexCenter(simplexCenter, simplexVector, dimensions, activeDimensions); points = activeDimensions+1; while (*evaluations2) { if (flags&SIMPLEX_VERBOSE_LEVEL2) fprintf(stdout, "simplexMinization: simplex is loooing--ending iterations\n"); /* stuck in some kind of loop */ break; } if (fTrialfValue[nextWorstPoint]) { /* reflection through the simplex didn't help, so try contracting away from worst point without * going through the face opposite the worst point */ if (flags&SIMPLEX_VERBOSE_LEVEL2) fprintf(stdout, "simplexMinization: contracting simplex\n"); fProblem = fTrial; fTrial = trialSimplex(simplexVector, fValue, simplexCenter, coordLowerLimit, coordUpperLimit, disable, dimensions, activeDimensions, function, worstPoint, evaluations, 0.5, &usedLast, &newPoint); contractionWorked += newPoint?1:0; progressMade += newPoint; if (fTrial>fProblem) { /* the new point is worse than the old trial point, so try moving the entire simplex in on the best point by averaging each vector with the vector to the best point. Don't allow invalid points, however, and keep track of the number of degenerate points (those with the same vector or the same function value). */ if (flags&SIMPLEX_VERBOSE_LEVEL2) fprintf(stdout, "simplexMinimization: contracting on best point\n"); invalids = degenerates = 0; for (point=0; point=points-1) { SWAP_PTR(simplexVector[0], simplexVector[bestPoint]); SWAP_DOUBLE(fValue[0], fValue[bestPoint]); if (flags&SIMPLEX_VERBOSE_LEVEL2) fprintf(stdout, "simplexMinimization exiting: reflection: %ld extension: %ld contraction: %ld shrinking: %ld\n", reflectionWorked, extensionWorked, contractionWorked, shrinkingDone); return 0; } *evaluations += points; /* since the simplex was changed without using trialSimplex, the "center" must be recomputed */ progressMade += 1; computeSimplexCenter(simplexCenter, simplexVector, dimensions, activeDimensions); } } if (!progressMade) { if (flags&SIMPLEX_VERBOSE_LEVEL2) fprintf(stdout, "simplexMinimization: Breaking out of loop--no progress.\n"); break; } } simplexFindBestWorst(fValue, points, &bestPoint, &worstPoint, &nextWorstPoint); if (*evaluations >= maxEvaluations) { SWAP_PTR(simplexVector[0], simplexVector[bestPoint]); SWAP_DOUBLE(fValue[0], fValue[bestPoint]); if (flags&SIMPLEX_VERBOSE_LEVEL2) fprintf(stdout, "simplexMinimization: too many iterations\n"); return 0; } SWAP_PTR(simplexVector[0], simplexVector[bestPoint]); SWAP_DOUBLE(fValue[0], fValue[bestPoint]); if (flags&SIMPLEX_VERBOSE_LEVEL2) fprintf(stdout, "simplexMinimization exit report: reflection: %ld extension: %ld contraction: %ld shrinking: %ld\n", reflectionWorked, extensionWorked, contractionWorked, shrinkingDone); return(1); } void computeSimplexCenter(double *center, double **vector, long dimensions, long activeDimensions) { long point, direction; for (direction=0; directionxhi[i]) return 0; return 1; } /* No longer used---checkVariableLimits is used instead. */ void enforceVariableLimits(double *x, double *xlo, double *xhi, long n) { long i; if (xlo) for (i=0; ixhi[i]) x[i] = xhi[i]; } void simplexFindBestWorst(double *fValue, long points, long *bestPointPtr, long *worstPointPtr, long *nextWorstPointPtr) { long bestPoint, worstPoint, nextWorstPoint, point; double fBest, fNextWorst, fWorst; if (fValue[0]>fValue[1]) { bestPoint = nextWorstPoint = 1; worstPoint = 0; } else { bestPoint = nextWorstPoint = 0; worstPoint = 1; } fBest = fNextWorst = fValue[bestPoint]; fWorst = fValue[worstPoint]; for (point=1; pointfValue[point]) { bestPoint = point; fBest = fValue[point]; } if (fWorst