/** Initialise the simplex and evaluate it. Returns null on failure. */ private List <double[]> makeSimplex(double[] initialParams, double[] initialParamVariations, Random random) { var simp = Java.NewDoubleArray2(numVertices, numParams + 1 + numExtraArrayElements); /* simpTable.put(Thread.currentThread(), simp); */ if (initialParams != null) { for (int i = 0; i < numParams; i++) { if (double.IsNaN(initialParams[i])) { Array.Copy(initialParams, 0, simp[0], 0, Math.Min(initialParams.Length, numParams)); } } } evaluate(simp[0]); if (double.IsNaN(value(simp[0]))) { findValidInitalParams(simp[0], initialParamVariations, random); } if (double.IsNaN(value(simp[0]))) { return(null); } if (initializeSimplex(simp, initialParamVariations, random)) { return(simp); } else { return(null); } }
/** Get the full simplex of the current thread. This may be useful if the target function * wants to modify the simplex */ /* public List<double[]> getSimplex() { * return simpTable.get(Thread.currentThread()); * } */ /** One minimization run (including reinitializations of the simplex until the result is stable) */ private void minimizeOnce(double[] initialParams, double[] initialParamVariations, int seed) { Random random = new Random(seed); var simp = makeSimplex(initialParams, initialParamVariations, random); if (simp == null) { status = wasInitialized ? REINITIALIZATION_FAILURE : INITIALIZATION_FAILURE; return; } wasInitialized = true; if (startTime == 0) { startTime = Java.SystemCurrentTimeMillis(); } //if (io.binroot.regression.IJ.debugMode) showSimplex(simp, seed+" Initialized:"); int bestVertexNumber = minimize(simp); // first minimization double bestValueSoFar = value(simp[bestVertexNumber]); //reinitialize until converged or error/aborted (don't care about reinitialization failure in other thread) bool reinitialisationFailure = false; while (status == SUCCESS || status == REINITIALIZATION_FAILURE) { double[] paramVariations = makeNewParamVariations(simp, bestVertexNumber, initialParams, initialParamVariations); if (!reInitializeSimplex(simp, bestVertexNumber, paramVariations, random)) { reinitialisationFailure = true; break; } //if (io.binroot.regression.IJ.debugMode) showSimplex(simp, seed+" Reinitialized:"); bestVertexNumber = minimize(simp); // minimize with reinitialized simplex if (belowErrorLimit(value(simp[bestVertexNumber]), bestValueSoFar, 2.0)) { break; } bestValueSoFar = value(simp[bestVertexNumber]); } if (reinitialisationFailure) { status = REINITIALIZATION_FAILURE; } else if (status == SUCCESS || status == REINITIALIZATION_FAILURE) //i.e. not aborted, not max iterations exceeded { numCompletedMinimizations++; // it was a complete minimization } //if (io.binroot.regression.IJ.debugMode) showSimplex(simp, seed+" Final:"); if (resultsVector != null) { lock (resultsVector) { resultsVector.Add(simp[bestVertexNumber]); } } else { result = simp[bestVertexNumber]; } }
/** Get the value of the minimum, i.e. the value associated with the resulting parameters * as obtained by getParams(). May return NaN in case the minimize call has returned * an INITIALIZATION_FAILURE status or that abort() has been called at the very * beginning of the minimization. * Do not call this method before minimization. */ public double getFunctionValue() { if (result == null) { result = new double[numParams + 1]; Java.DoubleArrayFill(result, double.NaN); } return(value(result)); }
/** Get the result, i.e., the set of parameter values (i.e., variable values) * from the best corner of the simplex. Note that the array returned may have more * elements than numParams; ignore the rest. * May return an array with only NaN values in case the minimize call has returned * an INITIALIZATION_FAILURE status or that abort() has been called at the very * beginning of the minimization. * Do not call this method before minimization. */ public double[] getParams() { if (result == null) { result = new double[numParams + 1 + numExtraArrayElements]; Java.DoubleArrayFill(result, double.NaN); } return(result); }
/** Get center of all vertices except one to exclude * (may be -1 to exclude none) * Does not care about function values (i.e., last array elements) */ private void getCenter(List <double[]> simp, int excludeVertex, double[] center) { Java.DoubleArrayFill(center, 0.0); int nV = 0; for (int v = 0; v < numVertices; v++) { if (v != excludeVertex) { for (int i = 0; i < numParams; i++) { center[i] += simp[v][i]; } nV++; } } double norm = 1.0 / nV; for (int i = 0; i < numParams; i++) { center[i] *= norm; } }
/** Minimizes the target function by variation of the simplex. * Note that one call to this function never does more than 0.4*maxIter iterations. * @return index of the best value in simp */ private int minimize(List <double[]> simp) { int[] worstNextBestArray = new int[3]; // used to pass these indices from 'order' function double[] center = new double[numParams + 1 + numExtraArrayElements]; // center of all vertices except worst double[] reflected = new double[numParams + 1 + numExtraArrayElements]; // the 1st new vertex to try double[] secondTry = new double[numParams + 1 + numExtraArrayElements]; // expanded or contracted vertex order(simp, worstNextBestArray); int worst = worstNextBestArray[WORST]; int nextWorst = worstNextBestArray[NEXT_WORST]; int best = worstNextBestArray[BEST]; //showSimplex(simp, "before minimization, value="+value(simp[best])); //String operation="ini"; int thisNumIter = 0; while (true) { totalNumIter++; // global count over all threads thisNumIter++; // local count for this minimize call // THE MINIMIZAION ALGORITHM IS HERE { getCenter(simp, worst, center); // centroid of vertices except worst // Reflect worst vertex through centriod of not-worst getVertexAndEvaluate(center, simp[worst], -C_REFLECTION, reflected); if (value(reflected) <= value(simp[best])) // if it's better than the best... // try expanding it { getVertexAndEvaluate(center, simp[worst], -C_EXPANSION, secondTry); if (value(secondTry) <= value(reflected)) { copyVertex(secondTry, simp[worst]); // if expanded is better than reflected, keep it //operation="expa"; goto iteration; } } if (value(reflected) < value(simp[nextWorst])) { copyVertex(reflected, simp[worst]); // keep reflected if better than 2nd worst //operation="refl"; goto iteration; } else if (value(reflected) < value(simp[worst])) { // try outer contraction getVertexAndEvaluate(center, simp[worst], -C_CONTRACTION, secondTry); if (value(secondTry) <= value(reflected)) { copyVertex(secondTry, simp[worst]); // keep outer contraction //operation="outC"; goto iteration; } } else if (value(reflected) > value(simp[worst]) || double.IsNaN(value(reflected))) { // else inner contraction getVertexAndEvaluate(center, simp[worst], C_CONTRACTION, secondTry); if (value(secondTry) < value(simp[worst])) { copyVertex(secondTry, simp[worst]); // keep contracted if better than 2nd worst //operation="innC"; goto iteration; } } // if everything else has failed, contract simplex in on best shrinkSimplexAndEvaluate(simp, best); //operation="shri"; goto iteration; } // iteration: iteration: bool checkParamResolution = // if new 'worst' is not close to 'best', don't check any further paramResolutions != null && belowResolutionLimit(simp[worst], simp[best]); order(simp, worstNextBestArray); worst = worstNextBestArray[WORST]; nextWorst = worstNextBestArray[NEXT_WORST]; best = worstNextBestArray[BEST]; if (checkParamResolution) { if (belowResolutionLimit(simp, best)) // check whether all parameters are within the resolution limit { break; } } if (belowErrorLimit(value(simp[best]), value(simp[worst]), 4.0)) // make sure we are at the minimum: { getCenter(simp, -1, secondTry); // check center of the simplex evaluate(secondTry); if (value(secondTry) < value(simp[best])) { copyVertex(secondTry, simp[best]); // better than best: keep } } if (belowErrorLimit(value(simp[best]), value(simp[worst]), 4.0)) // no large spread of values { break; // looks like we are at the minimum } if (totalNumIter > maxIter || thisNumIter > 4 * (maxIter / 10)) { status = MAX_ITERATIONS_EXCEEDED; } if (status != SUCCESS) { break; } if ((ijStatusString != null || checkEscape) && totalNumIter > nextIterationForStatus) { long time = Java.SystemCurrentTimeMillis(); nextIterationForStatus = totalNumIter + (int)(totalNumIter * 500L / (time - startTime + 1)); //next display 0.5 sec later } } //showSimplex(simp, "after "+totalNumIter+" iterations: value="+value(simp[best])); return(best); }