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.
489 {
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;
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
561 for (direction = 0; direction < dimensions; direction++)
562 if (xLowerLimit[direction] >= xGuess[direction])
563 dxGuess[direction] = fabs(dxGuess[direction]);
564 }
565 if (xUpperLimit) {
566
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
584
585
586 for (point = 0; point < activeDimensions + 1; point++)
587 y[point] = DBL_MAX;
588
589 while (pass < maxPasses && !(simplexFlags & SIMPLEX_ABORT)) {
590
591
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
631
632
633 for (direction = 0; direction < dimensions; direction++)
634 simplexVector[point][direction] = simplexVector[(flags & SIMPLEX_START_FROM_VERTEX1) ? 0 : point - 1][direction];
635
636
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
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
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
695 break;
696 divisions++;
697 if (divisions % 2)
698
699 divisor *= -1;
700 else
701
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
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
732 y[point] = DBL_MAX;
733 divisions++;
734 } else
735 break;
736 }
737 if (divisions % 2)
738
739 divisor *= -1;
740 else
741
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
760 for (step = 0; !(simplexFlags & SIMPLEX_ABORT) && step < 3; step++) {
761 divisor /= divisorFactor;
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;
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
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
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}
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.