    001    package calhoun.analysis.crf.solver;
    002
    003    import java.util.Arrays;
    004    import java.util.List;
    005
    006    import org.apache.commons.logging.Log;
    007    import org.apache.commons.logging.LogFactory;
    008
    009    import calhoun.analysis.crf.CRFObjectiveFunctionGradient;
    010    import calhoun.analysis.crf.CRFTraining;
    011    import calhoun.analysis.crf.ModelManager;
    012    import calhoun.analysis.crf.io.TrainingSequence;
    013    import calhoun.util.ErrorException;
    014    import cern.colt.matrix.impl.DenseDoubleMatrix1D;
    015    import flanagan.math.Minimisation;
    016    import flanagan.math.MinimisationFunction;
    017
    018    /** uses a nelder-mead algorithm (the simplex method) to do a general function optimization objective function.
    019     * This optimization method does not use the gradient but requires many iterations and so is mainly useful in
    020     * debugging.  Use the {@link StandardOptimizer} for most problems.
    021     * <p>
    022     * This optimizer has several configuration properties that allow control over the optimization process:
    023     * <ul>
    024     * <li> <b><code>maxIters</code></b> - The maximum number of iterations (objective function evaluations) to attempt
    025     * <li> <b><code>requireConverge</code></b> - if true, throws an error if convergence is not reached in the maximum number
    026     * of iterations.  Otherwise, the current feature weights are returned when <code>maxIters</code> is reached.
    027     * <li> <b><code>stepSize</code></b> - the size of the initial changes made to the weights when exploring the objective function.
    028     * <li> <b><code>starts</code></b> - an initial set of guesses at feature weights.  Defaults to 1.0
    029     * </ul>
    030     * */
    031    public class SimplexOptimizer implements CRFTraining {
    032            @SuppressWarnings("unused")
    033            private static final Log log = LogFactory.getLog(SimplexOptimizer.class);
    034
    035            // Configuration
    036            CRFObjectiveFunctionGradient gradFunc;
    037            int maxIters = 500;
    038            double stepSize = 0.5;
    039            boolean requireConvergence = true;
    040            double[] starts = null;
    041
    042            public double[] optimize(ModelManager fm, List<? extends TrainingSequence<?>> data) {
    043                    gradFunc.setTrainingData(fm, data);
    044                    final int nFeatures = fm.getNumFeatures();
    045                    MinimisationFunction mFunc = new MinimisationFunction() {
    046                            double[] grad = new double[nFeatures];
    047
    048                            public double function(double[] d) {
    049                                    return -gradFunc.apply(d, grad);
    050                            }
    051                    };
    052
    053                    Minimisation m = new Minimisation();
    054                    DenseDoubleMatrix1D steps = new DenseDoubleMatrix1D(nFeatures);
    055                    m.setNmax(maxIters);
    056                    if(starts == null) {
    057                            starts = new double[nFeatures];
    058                            Arrays.fill(starts, 1.0);
    059                    }
    060                    steps.assign(stepSize);
    061                    m.nelderMead(mFunc, starts, steps.toArray());
    062                    if(requireConvergence && !m.getConvStatus()) {
    063                            throw new ErrorException("Convergence not reached.");
    064                    }
    065                    //m.print("test/working/nelder.txt");
    066                    return m.getParamValues();
    067            }
    068
    069            /** returns the configured objective function gradient which will be
    070             * used by the optimizer during the training process.
    071             * @return the configured objective function gradient
    072             */
    073            public CRFObjectiveFunctionGradient getObjectiveFunction() {
    074                    return gradFunc;
    075            }
    076
    077            /** sets the objective function gradient.  Called automatically during configuration. */
    078            public void setObjectiveFunction(CRFObjectiveFunctionGradient objectiveFunction) {
    079                    this.gradFunc = objectiveFunction;
    080            }
    081
    082            public int getMaxIters() {
    083                    return maxIters;
    084            }
    085
    086            public void setMaxIters(int maxIters) {
    087                    this.maxIters = maxIters;
    088            }
    089
    090            public boolean isRequireConvergence() {
    091                    return requireConvergence;
    092            }
    093
    094            public void setRequireConvergence(boolean requireConvergence) {
    095                    this.requireConvergence = requireConvergence;
    096            }
    097
    098            public double[] getStarts() {
    099                    return starts;
    100            }
    101
    102            public void setStarts(double[] starts) {
    103                    this.starts = starts;
    104            }
    105
    106            public double getStepSize() {
    107                    return stepSize;
    108            }
    109
    110            public void setStepSize(double stepSize) {
    111                    this.stepSize = stepSize;
    112            }
    113    }