/* * Class Minimisation * * Contains methods for finding the values of the * function parameters that minimise that function * using the Nelder and Mead Simplex method. * * The function needed by the minimisation method * is supplied by though the interface, MinimisationFunction or Minimization * * WRITTEN BY: Dr Michael Thomas Flanagan * * DATE: April 2003 * MODIFIED: 29 December 2005, 18 February 2006, 28 December 2007, 10/12 May 2008, * July 2008, 12 January 2010, 23 September 2010 * * DOCUMENTATION: * See Michael Thomas Flanagan's Java library on-line web page: * http://www.ee.ucl.ac.uk/~mflanaga/java/Minimisation.html * http://www.ee.ucl.ac.uk/~mflanaga/java/ * * Copyright (c) 2003 - 2008 * * PERMISSION TO COPY: * Permission to use, copy and modify this software and its documentation for * NON-COMMERCIAL purposes is granted, without fee, provided that an acknowledgement * to the author, Michael Thomas Flanagan at www.ee.ucl.ac.uk/~mflanaga, appears in all copies. * * Dr Michael Thomas Flanagan makes no representations about the suitability * or fitness of the software for any or for a particular purpose. * Michael Thomas Flanagan shall not be liable for any damages suffered * as a result of using, modifying or distributing this software or its derivatives. * ***************************************************************************************/ package flanagan.math; import java.util.*; import flanagan.math.Fmath; import flanagan.io.FileOutput; // Minimisation class public class Minimisation{ protected boolean iseOption = true; // = true if minimis... spelling used // = false if minimiz... spelling used protected int nParam=0; // number of unknown parameters to be estimated protected double[]paramValue = null; // function parameter values (returned at function minimum) protected String[] paraName = null; // names of parameters, eg, c[0], c[1], c[2] . . . protected double functValue = 0.0D; // current value of the function to be minimised protected double lastFunctValNoCnstrnt=0.0D;// Last function value with no constraint penalty protected double minimum = 0.0D; // value of the function to be minimised at the minimum protected int prec = 4; // number of places to which double variables are truncated on output to text files protected int field = 13; // field width on output to text files protected boolean convStatus = false; // Status of minimisation on exiting minimisation method // = true - convergence criterion was met // = false - convergence criterion not met - current estimates returned protected boolean suppressNoConvergenceMessage = false; // if true - suppress the print out of a message saying that convergence was not achieved. protected int scaleOpt=0; // if = 0; no scaling of initial estimates // if = 1; initial simplex estimates scaled to unity // if = 2; initial estimates scaled by user provided values in scale[] // (default = 0) protected double[] scale = null; // values to scale initial estimate (see scaleOpt above) protected boolean penalty = false; // true if single parameter penalty function is included protected boolean sumPenalty = false; // true if multiple parameter penalty function is included protected int nConstraints = 0; // number of single parameter constraints protected int nSumConstraints = 0; // number of multiple parameter constraints protected int maxConstraintIndex = -1; // maximum index of constrained parameter/s protected ArrayList penalties = new ArrayList(); // method index, // number of single parameter constraints, // then repeated for each constraint: // penalty parameter index, // below or above constraint flag, // constraint boundary value protected ArrayList sumPenalties = new ArrayList(); // constraint method index, // number of multiple parameter constraints, // then repeated for each constraint: // number of parameters in summation // penalty parameter indices, // summation signs // below or above constraint flag, // constraint boundary value protected int[] penaltyCheck = null; // = -1 values below the single constraint boundary not allowed // = +1 values above the single constraint boundary not allowed protected int[] sumPenaltyCheck = null; // = -1 values below the multiple constraint boundary not allowed // = +1 values above the multiple constraint boundary not allowed protected double penaltyWeight = 1.0e30; // weight for the penalty functions protected int[] penaltyParam = null; // indices of paramaters subject to single parameter constraint protected int[][] sumPenaltyParam = null; // indices of paramaters subject to multiple parameter constraint protected double[][] sumPlusOrMinus = null; // value before each parameter in multiple parameter summation protected int[] sumPenaltyNumber = null; // number of paramaters in each multiple parameter constraint protected double[] constraints = null; // single parameter constraint values protected double constraintTolerance = 1e-4; // tolerance in constraining parameter/s to a fixed value protected double[] sumConstraints = null; // multiple parameter constraint values protected int constraintMethod = 0; // constraint method number // =0: cliff to the power two (only method at present) protected int nMax = 3000; // Nelder and Mead simplex maximum number of iterations protected int nIter = 0; // Nelder and Mead simplex number of iterations performed protected int konvge = 3; // Nelder and Mead simplex number of restarts allowed protected int kRestart = 0; // Nelder and Mead simplex number of restarts taken protected double fTol = 1e-13; // Nelder and Mead simplex convergence tolerance protected double rCoeff = 1.0D; // Nelder and Mead simplex reflection coefficient protected double eCoeff = 2.0D; // Nelder and Mead simplex extension coefficient protected double cCoeff = 0.5D; // Nelder and Mead simplex contraction coefficient protected double[] startH = null; // Nelder and Mead simplex initial estimates protected double[] step = null; // Nelder and Mead simplex step values protected double dStep = 0.5D; // Nelder and Mead simplex default step value protected int minTest = 0; // Nelder and Mead minimum test // = 0; tests simplex sd < fTol // allows options for further tests to be added later protected double simplexSd = 0.0D; // simplex standard deviation //Constructor public Minimisation(){ this.iseOption = true; } // Suppress the print out of a message saying that convergence was not achieved. public void suppressNoConvergenceMessage(){ this.suppressNoConvergenceMessage = true; } // Suppress the print out of a message saying that convergence was not achieved. public void supressNoConvergenceMessage(){ this.suppressNoConvergenceMessage = true; } // Nelder and Mead Simplex minimisation public void nelderMead(MinimisationFunction gg, double[] start, double[] step, double fTol, int nMax){ Object g = (Object)gg; this.nelderMead(g, start, step, fTol, nMax); } // Nelder and Mead Simplex minimisation public void nelderMead(MinimizationFunction gg, double[] start, double[] step, double fTol, int nMax){ Object g = (Object)gg; this.nelderMead(g, start, step, fTol, nMax); } // Nelder and Mead Simplex minimisation public void nelderMead(Object g, double[] start, double[] step, double fTol, int nMax){ boolean testContract=false; // test whether a simplex contraction has been performed int np = start.length; // number of unknown parameters; if(this.maxConstraintIndex>=np)throw new IllegalArgumentException("You have entered more constrained parameters ("+this.maxConstraintIndex+") than minimisation parameters (" + np + ")"); this.nParam = np; this.convStatus = true; int nnp = np+1; // Number of simplex apices this.lastFunctValNoCnstrnt=0.0D; if(this.scaleOpt<2)this.scale = new double[np]; if(scaleOpt==2 && scale.length!=start.length)throw new IllegalArgumentException("scale array and initial estimate array are of different lengths"); if(step.length!=start.length)throw new IllegalArgumentException("step array length " + step.length + " and initial estimate array length " + start.length + " are of different"); // check for zero step sizes for(int i=0; i0){ boolean testzero=false; for(int i=0; iynewlo){ ynewlo=yy[i]; ihi=i; } } // Calculate pbar for (int i=0; iyi, i!=h ln=0; for (int i=0; i yy[i]) ++ln; if (ln==np ){ // y*>= all yi; Check if y*>yh if(ystar<=yy[ihi]){ // Replace ph by p* for (int i=0; iyh if(y2star>yy[ihi]){ //Replace all pi by (pi+pl)/2 for (int j=0; jyy[i]){ ynewlo=yy[i]; ilo=i; } } sumnm /= (double)(nnp); summnm=0.0; for (int i=0; i0){ test=true; for (int j=0; jthis.nMax){ if(!this.suppressNoConvergenceMessage){ System.out.println("Maximum iteration number reached, in Minimisation.simplex(...)"); System.out.println("without the convergence criterion being satisfied"); System.out.println("Current parameter estimates and function value returned"); } this.convStatus = false; // store current estimates for (int i=0; iconstraints[i]*(1.0+this.constraintTolerance)){ this.functValue = tempFunctVal + this.penaltyWeight*Fmath.square(param[k]-constraints[i]*(1.0+this.constraintTolerance)); test=false; } break; case 1: if(param[k]>constraints[i]){ this.functValue = tempFunctVal + this.penaltyWeight*Fmath.square(param[k]-constraints[i]); test=false; } break; } } } // multiple parameter penalty functions if(this.sumPenalty){ int kk = 0; double pSign = 0; for(int i=0; isumConstraints[i]*(1.0+this.constraintTolerance)){ this.functValue = tempFunctVal + this.penaltyWeight*Fmath.square(sumPenaltySum-sumConstraints[i]*(1.0+this.constraintTolerance)); test=false; } break; case 1: if(sumPenaltySum>sumConstraints[i]){ this.functValue = tempFunctVal + this.penaltyWeight*Fmath.square(sumPenaltySum-sumConstraints[i]); test=false; } break; } } } return test; } // add a single parameter constraint boundary for the minimisation public void addConstraint(int paramIndex, int conDir, double constraint){ this.penalty=true; // First element reserved for method number if other methods than 'cliff' are added later if(this.penalties.isEmpty())this.penalties.add(new Integer(this.constraintMethod)); // add constraint if(penalties.size()==1){ this.penalties.add(new Integer(1)); } else{ int nPC = ((Integer)this.penalties.get(1)).intValue(); nPC++; this.penalties.set(1, new Integer(nPC)); } this.penalties.add(new Integer(paramIndex)); this.penalties.add(new Integer(conDir)); this.penalties.add(new Double(constraint)); if(paramIndex>this.maxConstraintIndex)this.maxConstraintIndex = paramIndex; } // add a multiple parameter constraint boundary for the non-linear regression public void addConstraint(int[] paramIndices, int[] plusOrMinus, int conDir, double constraint){ ArrayMaths am = new ArrayMaths(plusOrMinus); double[] dpom = am.getArray_as_double(); addConstraint(paramIndices, dpom, conDir, constraint); } // add a multiple parameter constraint boundary for the minimisation public void addConstraint(int[] paramIndices, double[] plusOrMinus, int conDir, double constraint){ int nCon = paramIndices.length; int nPorM = plusOrMinus.length; if(nCon!=nPorM)throw new IllegalArgumentException("num of parameters, " + nCon + ", does not equal number of parameter signs, " + nPorM); this.sumPenalty=true; // First element reserved for method number if other methods than 'cliff' are added later if(this.sumPenalties.isEmpty())this.sumPenalties.add(new Integer(this.constraintMethod)); // add constraint if(sumPenalties.size()==1){ this.sumPenalties.add(new Integer(1)); } else{ int nPC = ((Integer)this.sumPenalties.get(1)).intValue(); nPC++; this.sumPenalties.set(1, new Integer(nPC)); } this.sumPenalties.add(new Integer(nCon)); this.sumPenalties.add(paramIndices); this.sumPenalties.add(plusOrMinus); this.sumPenalties.add(new Integer(conDir)); this.sumPenalties.add(new Double(constraint)); ArrayMaths am = new ArrayMaths(paramIndices); int maxI = am.getMaximum_as_int(); if(maxI>this.maxConstraintIndex)this.maxConstraintIndex = maxI; } // Set constraint method public void setConstraintMethod(int conMeth){ this.constraintMethod = conMeth; if(!this.penalties.isEmpty())this.penalties.set(0, new Integer(this.constraintMethod)); } // remove all constraint boundaries for the minimisation public void removeConstraints(){ // check if single parameter constraints already set if(!this.penalties.isEmpty()){ int m=this.penalties.size(); // remove single parameter constraints for(int i=m-1; i>=0; i--){ this.penalties.remove(i); } } this.penalty = false; this.nConstraints = 0; // check if mutiple parameter constraints already set if(!this.sumPenalties.isEmpty()){ int m=this.sumPenalties.size(); // remove multiple parameter constraints for(int i=m-1; i>=0; i--){ this.sumPenalties.remove(i); } } this.sumPenalty = false; this.nSumConstraints = 0; this.maxConstraintIndex = -1; } // Reset the tolerance used in a fixed value constraint public void setConstraintTolerance(double tolerance){ this.constraintTolerance = tolerance; } // Print the results of the minimisation // File name provided // prec = truncation precision public void print(String filename, int prec){ this.prec = prec; this.print(filename); } // Print the results of the minimisation // No file name provided // prec = truncation precision public void print(int prec){ this.prec = prec; String filename="MinimisationOutput.txt"; this.print(filename); } // Print the results of the minimisation // File name provided // prec = truncation precision public void print(String filename){ if(filename.indexOf('.')==-1)filename = filename+".txt"; FileOutput fout = new FileOutput(filename, 'n'); fout.dateAndTimeln(filename); fout.println(" "); fout.println("Simplex minimisation, using the method of Nelder and Mead,"); fout.println("of the function y = f(c[0], c[1], c[2] . . .)"); this.paraName = new String[this.nParam]; for(int i=0;i the tolerance"); } break; } fout.println(); fout.println("End of file"); fout.close(); } // Print the results of the minimisation // No file name provided public void print(){ String filename="MinimisationOutput.txt"; this.print(filename); } // Get the minimisation status // true if convergence was achieved // false if convergence not achieved before maximum number of iterations // current values then returned public boolean getConvStatus(){ return this.convStatus; } // Reset scaling factors (scaleOpt 0 and 1, see below for scaleOpt 2) public void setScale(int n){ if(n<0 || n>1)throw new IllegalArgumentException("The argument must be 0 (no scaling) 1(initial estimates all scaled to unity) or the array of scaling factors"); this.scaleOpt=n; } // Reset scaling factors (scaleOpt 2, see above for scaleOpt 0 and 1) public void setScale(double[] sc){ this.scale=sc; this.scaleOpt=2; } // Get scaling factors public double[] getScale(){ return this.scale; } // Reset the minimisation convergence test option public void setMinTest(int n){ if(n<0 || n>1)throw new IllegalArgumentException("minTest must be 0 or 1"); this.minTest=n; } // Get the minimisation convergence test option public int getMinTest(){ return this.minTest; } // Get the simplex sd at the minimum public double getSimplexSd(){ return this.simplexSd; } // Get the values of the parameters at the minimum public double[] getParamValues(){ return this.paramValue; } // Get the function value at minimum public double getMinimum(){ return this.minimum; } // Get the number of iterations in Nelder and Mead public int getNiter(){ return this.nIter; } // Set the maximum number of iterations allowed in Nelder and Mead public void setNmax(int nmax){ this.nMax = nmax; } // Get the maximum number of iterations allowed in Nelder and Mead public int getNmax(){ return this.nMax; } // Get the number of restarts in Nelder and Mead public int getNrestarts(){ return this.kRestart; } // Set the maximum number of restarts allowed in Nelder and Mead public void setNrestartsMax(int nrs){ this.konvge = nrs; } // Get the maximum number of restarts allowed in Nelder amd Mead public int getNrestartsMax(){ return this.konvge; } // Reset the Nelder and Mead reflection coefficient [alpha] public void setNMreflect(double refl){ this.rCoeff = refl; } // Get the Nelder and Mead reflection coefficient [alpha] public double getNMreflect(){ return this.rCoeff; } // Reset the Nelder and Mead extension coefficient [beta] public void setNMextend(double ext){ this.eCoeff = ext; } // Get the Nelder and Mead extension coefficient [beta] public double getNMextend(){ return this.eCoeff; } // Reset the Nelder and Mead contraction coefficient [gamma] public void setNMcontract(double con){ this.cCoeff = con; } // Get the Nelder and Mead contraction coefficient [gamma] public double getNMcontract(){ return cCoeff; } // Set the minimisation tolerance public void setTolerance(double tol){ this.fTol = tol; } // Get the minimisation tolerance public double getTolerance(){ return this.fTol; } }