Top-level convenience function for simplex-based minimization.
This function sets up and runs a simplex optimization on the provided function, attempting to find a minimum within given constraints and stopping criteria.
519 {
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;
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
596 for (direction = 0; direction < dimensions; direction++)
597 if (xLowerLimit[direction] >= xGuess[direction])
598 dxGuess[direction] = fabs(dxGuess[direction]);
599 }
600 if (xUpperLimit) {
601
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
621
622
623 for (point = 0; point < activeDimensions + 1; point++)
624 y[point] = DBL_MAX;
625
626 while (pass < maxPasses && !simplexAbortRequested()) {
627
628
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
668
669
670 for (direction = 0; direction < dimensions; direction++)
671 simplexVector[point][direction] = simplexVector[(flags & SIMPLEX_START_FROM_VERTEX1) ? 0 : point - 1][direction];
672
673
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
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
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
736 break;
737 divisions++;
738 if (divisions % 2)
739
740 divisor *= -1;
741 else
742
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
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
773 y[point] = DBL_MAX;
774 divisions++;
775 } else
776 break;
777 }
778 if (divisions % 2)
779
780 divisor *= -1;
781 else
782
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
801 for (step = 0; !simplexAbortRequested() && step < 3; step++) {
802 divisor /= divisorFactor;
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;
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
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
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}
void ** zarray_2d(uint64_t size, uint64_t n1, uint64_t n2)
Allocates a 2D array with specified dimensions.
int free_zarray_2d(void **array, uint64_t n1, uint64_t n2)
Frees a 2D array and its associated memory.
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
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.