iDynoOptimizer.MOEAFramework26.src.org.moeaframework.analysis.tools.Solve.java Source code

Java tutorial

Introduction

Here is the source code for iDynoOptimizer.MOEAFramework26.src.org.moeaframework.analysis.tools.Solve.java

Source

/* Copyright 2009-2015 David Hadka
 *
 * This file is part of the MOEA Framework.
 *
 * The MOEA Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * The MOEA Framework is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with the MOEA Framework.  If not, see <http://www.gnu.org/licenses/>.
 */
package iDynoOptimizer.MOEAFramework26.src.org.moeaframework.analysis.tools;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.algorithm.PeriodicAction;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.analysis.sensitivity.ResultEntry;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.analysis.sensitivity.ResultFileWriter;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.Algorithm;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.FrameworkException;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.NondominatedPopulation;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.PRNG;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.Problem;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.Solution;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.Variable;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.operator.RandomInitialization;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.spi.AlgorithmFactory;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.spi.ProblemFactory;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.variable.EncodingUtils;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.problem.ExternalProblem;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.util.CommandLineUtility;
import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.util.io.FileUtils;

/**
 * Command line utility for solving an optimization problem using any of the
 * supported optimization algorithms.  This utility supports solving problems
 * defined within the MOEA Framework as well as compatible external problems.
 * See {@link ExternalProblem} for details on developing an external problem.
 */
public class Solve extends CommandLineUtility {

    /**
     * Constructs the command line utility for solving an optimization problem.
     */
    public Solve() {
        super();
    }

    @SuppressWarnings("static-access")
    @Override
    public Options getOptions() {
        Options options = super.getOptions();

        options.addOption(
                OptionBuilder.withLongOpt("output").hasArg().withArgName("file").isRequired().create('f'));
        options.addOption(OptionBuilder.withLongOpt("problem").hasArg().withArgName("name").create('b'));
        options.addOption(
                OptionBuilder.withLongOpt("algorithm").hasArg().withArgName("name").isRequired().create('a'));
        options.addOption(OptionBuilder.withLongOpt("properties").hasArgs().withArgName("p1=v1;p2=v2;...")
                .withValueSeparator(';').create('x'));
        options.addOption(OptionBuilder.withLongOpt("seed").hasArg().withArgName("value").create('s'));
        options.addOption(OptionBuilder.withLongOpt("epsilon").hasArg().withArgName("e1,e2,...").create('e'));
        options.addOption(OptionBuilder.withLongOpt("numberOfEvaluations").hasArg().withArgName("value")
                .isRequired().create('n'));
        options.addOption(OptionBuilder.withLongOpt("runtimeFrequency").hasArg().withArgName("value").create('F'));
        options.addOption(OptionBuilder.withLongOpt("variables").hasArg().withArgName("v1,v2,...").create('v'));
        options.addOption(OptionBuilder.withLongOpt("objectives").hasArg().withArgName("value").create('o'));
        options.addOption(OptionBuilder.withLongOpt("constraints").hasArg().withArgName("value").create('c'));
        options.addOption(OptionBuilder.withLongOpt("lowerBounds").hasArg().withArgName("v1,v2,...").create("l"));
        options.addOption(OptionBuilder.withLongOpt("upperBounds").hasArg().withArgName("v1,v2,...").create('u'));

        options.addOption(OptionBuilder.withLongOpt("useSocket").create('S'));
        options.addOption(OptionBuilder.withLongOpt("hostname").hasArg().withArgName("value").create('H'));
        options.addOption(OptionBuilder.withLongOpt("port").hasArg().withArgName("value").create('P'));
        options.addOption(OptionBuilder.withLongOpt("startupDelay").hasArg().withArgName("value").create('D'));

        options.addOption(OptionBuilder.withLongOpt("test").hasOptionalArg().withArgName("trials").create('t'));

        return options;
    }

    /**
     * Parses a single variable specification from the command line.  This
     * method is case sensitive.
     * 
     * @param token the variable specification from the command line
     * @return the generated variable object
     * @throws ParseException if an error occurred while parsing the variable
     *         specification
     */
    private Variable parseVariableSpecification(String token) throws ParseException {
        if (!token.endsWith(")")) {
            throw new ParseException("invalid variable specification '" + token + "', not properly formatted");
        }

        if (token.startsWith("R(")) {
            // real-valued decision variable
            String content = token.substring(2, token.length() - 1);
            int index = content.indexOf(';');

            if (index >= 0) {
                double lowerBound = Double.parseDouble(content.substring(0, index));
                double upperBound = Double.parseDouble(content.substring(index + 1, content.length()));
                return EncodingUtils.newReal(lowerBound, upperBound);
            } else {
                throw new ParseException("invalid real specification '" + token + "', expected R(<lb>,<ub>)");
            }
        } else if (token.startsWith("B(")) {
            String content = token.substring(2, token.length() - 1);

            try {
                int length = Integer.parseInt(content.trim());
                return EncodingUtils.newBinary(length);
            } catch (NumberFormatException e) {
                throw new ParseException("invalid binary specification '" + token + "', expected B(<length>)");
            }
        } else if (token.startsWith("P(")) {
            String content = token.substring(2, token.length() - 1);

            try {
                int length = Integer.parseInt(content.trim());
                return EncodingUtils.newPermutation(length);
            } catch (NumberFormatException e) {
                throw new ParseException("invalid permutation specification '" + token + "', expected P(<length>)");
            }
        } else {
            throw new ParseException("invalid variable specification '" + token + "', unknown type");
        }
    }

    /**
     * Parses the decision variable specification either from the
     * {@code --lowerBounds} and {@code --upperBounds} options or the
     * {@code --variables} option.
     * 
     * @param commandLine the command line arguments
     * @return the parsed variable specifications
     * @throws ParseException if an error occurred while parsing the variable
     *         specifications
     */
    private List<Variable> parseVariables(CommandLine commandLine) throws ParseException {
        List<Variable> variables = new ArrayList<Variable>();

        if (commandLine.hasOption("lowerBounds") && commandLine.hasOption("upperBounds")) {
            String[] lowerBoundTokens = commandLine.getOptionValue("lowerBounds").split(",");
            String[] upperBoundTokens = commandLine.getOptionValue("upperBounds").split(",");

            if (lowerBoundTokens.length != upperBoundTokens.length) {
                throw new ParseException("lower bound and upper bounds not " + "the same length");
            }

            for (int i = 0; i < lowerBoundTokens.length; i++) {
                double lowerBound = Double.parseDouble(lowerBoundTokens[i]);
                double upperBound = Double.parseDouble(upperBoundTokens[i]);
                variables.add(EncodingUtils.newReal(lowerBound, upperBound));
            }
        } else if (commandLine.hasOption("variables")) {
            String[] tokens = commandLine.getOptionValue("variables").split(",");

            for (String token : tokens) {
                variables.add(parseVariableSpecification(token.trim().toUpperCase()));
            }
        } else {
            throw new ParseException(
                    "must specify either the problem, the " + "variables, or the lower and upper bounds arguments");
        }

        return variables;
    }

    /**
     * Creates an external problem using the information provided on the
     * command line.
     * 
     * @param commandLine the command line arguments
     * @return the external problem
     * @throws ParseException if an error occurred parsing any of the command
     *         line options
     * @throws IOException if an error occurred starting the external program
     */
    private Problem createExternalProblem(final CommandLine commandLine) throws ParseException, IOException {
        final int numberOfObjectives = Integer.parseInt(commandLine.getOptionValue("objectives"));

        final int numberOfConstraints = commandLine.hasOption("constraints")
                ? Integer.parseInt(commandLine.getOptionValue("constraints"))
                : 0;

        final List<Variable> variables = parseVariables(commandLine);

        if (commandLine.hasOption("useSocket")) {
            String hostname = null; // default to localhost
            int port = 16801;
            int delay = 1;

            if (commandLine.hasOption("hostname")) {
                hostname = commandLine.getOptionValue("hostname");
            }

            if (commandLine.hasOption("port")) {
                port = Integer.parseInt(commandLine.getOptionValue("port"));
            }

            if (commandLine.hasOption("startupDelay")) {
                delay = Integer.parseInt(commandLine.getOptionValue("startupDelay"));
            }

            if (commandLine.getArgs().length > 0) {
                // the command to run is specified on the command line
                System.out.print("Running ");
                System.out.println(StringUtils.join(commandLine.getArgs()));
                new ProcessBuilder(commandLine.getArgs()).start();
            }

            try {
                System.out.print("Sleeping for ");
                System.out.print(delay);
                System.out.println(" seconds");
                Thread.sleep(delay * 1000);
            } catch (InterruptedException e) {
                // do nothing
            }

            System.out.println("Starting optimization");
            return new ExternalProblem(hostname, port) {

                @Override
                public String getName() {
                    return StringUtils.join(commandLine.getArgs());
                }

                @Override
                public int getNumberOfVariables() {
                    return variables.size();
                }

                @Override
                public int getNumberOfObjectives() {
                    return numberOfObjectives;
                }

                @Override
                public int getNumberOfConstraints() {
                    return numberOfConstraints;
                }

                @Override
                public Solution newSolution() {
                    Solution solution = new Solution(variables.size(), numberOfObjectives, numberOfConstraints);

                    for (int i = 0; i < variables.size(); i++) {
                        solution.setVariable(i, variables.get(i).copy());
                    }

                    return solution;
                }

            };
        } else {
            return new ExternalProblem(commandLine.getArgs()) {

                @Override
                public String getName() {
                    return StringUtils.join(commandLine.getArgs());
                }

                @Override
                public int getNumberOfVariables() {
                    return variables.size();
                }

                @Override
                public int getNumberOfObjectives() {
                    return numberOfObjectives;
                }

                @Override
                public int getNumberOfConstraints() {
                    return numberOfConstraints;
                }

                @Override
                public Solution newSolution() {
                    Solution solution = new Solution(variables.size(), numberOfObjectives, numberOfConstraints);

                    for (int i = 0; i < variables.size(); i++) {
                        solution.setVariable(i, variables.get(i).copy());
                    }

                    return solution;
                }

            };
        }
    }

    /**
     * Runs a number of trials as a way to quickly test if the connection
     * between this solver and the problem is functional.
     * 
     * @param problem the problem
     * @param commandLine the command line arguments
     */
    private void runTests(Problem problem, CommandLine commandLine) {
        int trials = 5;

        if (commandLine.getOptionValue("test") != null) {
            trials = Integer.parseInt(commandLine.getOptionValue("test"));
        }

        try {
            int count = 0;
            RandomInitialization initialization = new RandomInitialization(problem, trials);

            Solution[] solutions = initialization.initialize();

            for (Solution solution : solutions) {
                System.out.println("Running test " + (++count) + ":");

                for (int j = 0; j < solution.getNumberOfVariables(); j++) {
                    System.out.print("  Variable ");
                    System.out.print(j + 1);
                    System.out.print(" = ");
                    System.out.println(solution.getVariable(j));
                }

                System.out.println("  * Evaluating solution *");
                problem.evaluate(solution);
                System.out.println("  * Evaluation complete *");

                for (int j = 0; j < solution.getNumberOfObjectives(); j++) {
                    System.out.print("  Objective ");
                    System.out.print(j + 1);
                    System.out.print(" = ");
                    System.out.println(solution.getObjective(j));
                }

                for (int j = 0; j < solution.getNumberOfConstraints(); j++) {
                    System.out.print("  Constraint ");
                    System.out.print(j + 1);
                    System.out.print(" = ");
                    System.out.println(solution.getConstraint(j));
                }

                if ((solution.getNumberOfConstraints() > 0) && solution.violatesConstraints()) {
                    System.out.println("  Solution is infeasible (non-zero " + "constraint value)!");
                }
            }

            System.out.println("Test succeeded!");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Test failed!  Please see the error message " + "above for details.");
        }
    }

    @Override
    public void run(CommandLine commandLine) throws IOException {
        // parse the algorithm parameters
        Properties properties = new Properties();

        if (commandLine.hasOption("properties")) {
            for (String property : commandLine.getOptionValues("properties")) {
                String[] tokens = property.split("=");

                if (tokens.length == 2) {
                    properties.setProperty(tokens[0], tokens[1]);
                } else {
                    throw new FrameworkException("malformed property argument");
                }
            }
        }

        if (commandLine.hasOption("epsilon")) {
            properties.setProperty("epsilon", commandLine.getOptionValue("epsilon"));
        }

        int maxEvaluations = Integer.parseInt(commandLine.getOptionValue("numberOfEvaluations"));

        // seed the pseudo-random number generator
        if (commandLine.hasOption("seed")) {
            PRNG.setSeed(Long.parseLong(commandLine.getOptionValue("seed")));
        }

        // parse the runtime frequency
        int runtimeFrequency = 100;

        if (commandLine.hasOption("runtimeFrequency")) {
            runtimeFrequency = Integer.parseInt(commandLine.getOptionValue("runtimeFrequency"));
        }

        // open the resources and begin processing
        Problem problem = null;
        Algorithm algorithm = null;
        ResultFileWriter writer = null;
        File file = new File(commandLine.getOptionValue("output"));

        try {
            if (commandLine.hasOption("problem")) {
                problem = ProblemFactory.getInstance().getProblem(commandLine.getOptionValue("problem"));
            } else {
                problem = createExternalProblem(commandLine);
            }

            if (commandLine.hasOption("test")) {
                runTests(problem, commandLine);
                return;
            }

            try {
                algorithm = AlgorithmFactory.getInstance().getAlgorithm(commandLine.getOptionValue("algorithm"),
                        properties, problem);

                // if the output file exists, delete first to avoid appending
                FileUtils.delete(file);

                try {
                    writer = new ResultFileWriter(problem, file);

                    algorithm = new RuntimeCollector(algorithm, runtimeFrequency, writer);

                    while (!algorithm.isTerminated() && (algorithm.getNumberOfEvaluations() < maxEvaluations)) {
                        algorithm.step();
                    }
                } finally {
                    if (writer != null) {
                        writer.close();
                    }
                }
            } finally {
                if (algorithm != null) {
                    algorithm.terminate();
                }
            }
        } catch (ParseException e) {
            throw new IOException(e);
        } finally {
            if (problem != null) {
                problem.close();
            }
        }
    }

    /**
     * Wraps an algorithm to write the approximation set and periodic intervals.
     */
    private static class RuntimeCollector extends PeriodicAction {

        /**
         * The result file writer where the runtime information is stored.
         */
        private final ResultFileWriter writer;

        /**
         * The time, in nanoseconds, this collector was created.  This roughly
         * corresponds to the time the algorithm starts, assuming that the
         * algorithm is run immediately following its setup.
         */
        private final long startTime;

        /**
         * Constructs a new wrapper to collect runtime dynamics.
         * 
         * @param algorithm the wrapped algorithm
         * @param frequency the frequency at which the runtime snapshots are
         *        recorded
         * @param writer the result file writer where the runtime information
         *        is stored
         */
        public RuntimeCollector(Algorithm algorithm, int frequency, ResultFileWriter writer) {
            super(algorithm, frequency, FrequencyType.EVALUATIONS);
            this.writer = writer;

            startTime = System.nanoTime();
        }

        @Override
        public void doAction() {
            double elapsedTime = (System.nanoTime() - startTime) * 1e-9;
            NondominatedPopulation result = algorithm.getResult();

            Properties properties = new Properties();
            properties.setProperty("NFE", Integer.toString(algorithm.getNumberOfEvaluations()));
            properties.setProperty("ElapsedTime", Double.toString(elapsedTime));

            try {
                writer.append(new ResultEntry(result, properties));
            } catch (IOException e) {
                throw new FrameworkException(e);
            }
        }

    }

    /**
     * Starts the command line utility for solving an optimization problem.
     * 
     * @param args the command line arguments
     * @throws Exception if an error occurred
     */
    public static void main(String[] args) throws Exception {
        new Solve().start(args);
    }

}