/* 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 org.moeaframework.analysis.sensitivity;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.moeaframework.core.Algorithm;
import org.moeaframework.core.FrameworkException;
import org.moeaframework.core.NondominatedPopulation;
import org.moeaframework.core.PRNG;
import org.moeaframework.core.PopulationIO;
import org.moeaframework.core.Problem;
import org.moeaframework.core.indicator.QualityIndicator;
import org.moeaframework.core.spi.AlgorithmFactory;
import org.moeaframework.core.spi.ProblemFactory;
import org.moeaframework.problem.TimingProblem;
import org.moeaframework.util.CommandLineUtility;
import org.moeaframework.util.TypedProperties;
/**
* Command line utility for evaluating an algorithm using many
* parameterizations.
*/
public class Evaluator extends CommandLineUtility {
/**
* The problem being evaluated.
*/
protected Problem problem;
/**
* The output writer where end-of-run results are stored.
*/
protected OutputWriter output;
/**
* The sample reader from which input parameters are read.
*/
protected SampleReader input;
/**
* Constructs the command line utility for evaluating an algorithm using
* many parameterizations.
*/
public Evaluator() {
super();
}
@SuppressWarnings("static-access")
@Override
public Options getOptions() {
Options options = super.getOptions();
options.addOption(OptionBuilder
.withLongOpt("parameterFile")
.hasArg()
.withArgName("file")
.isRequired()
.create('p'));
options.addOption(OptionBuilder
.withLongOpt("input")
.hasArg()
.withArgName("file")
.isRequired()
.create('i'));
options.addOption(OptionBuilder
.withLongOpt("output")
.hasArg()
.withArgName("file")
.isRequired()
.create('o'));
options.addOption(OptionBuilder
.withLongOpt("problem")
.hasArg()
.withArgName("name")
.isRequired()
.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("metrics")
.create('m'));
options.addOption(OptionBuilder
.withLongOpt("reference")
.hasArg()
.withArgName("file")
.create('r'));
options.addOption(OptionBuilder
.withLongOpt("novariables")
.create('n'));
options.addOption(OptionBuilder
.withLongOpt("force")
.create('f'));
return options;
}
@Override
public void run(CommandLine commandLine) throws IOException {
File outputFile = new File(commandLine.getOptionValue("output"));
File inputFile = new File(commandLine.getOptionValue("input"));
ParameterFile parameterFile = new ParameterFile(new File(commandLine
.getOptionValue("parameterFile")));
// sanity check to ensure input hasn't been modified after the output
if (!commandLine.hasOption("force") &&
(outputFile.lastModified() > 0L) &&
(inputFile.lastModified() > outputFile.lastModified())) {
throw new FrameworkException(
"input appears to be newer than output");
}
// open the resources and begin processing
try {
problem = ProblemFactory.getInstance().getProblem(commandLine
.getOptionValue("problem"));
try {
input = new SampleReader(new FileReader(inputFile),
parameterFile);
try {
if (commandLine.hasOption("metrics")) {
NondominatedPopulation referenceSet = null;
// load reference set and create the quality indicator
if (commandLine.hasOption("reference")) {
referenceSet = new NondominatedPopulation(
PopulationIO.readObjectives(new File(
commandLine.getOptionValue(
"reference"))));
} else {
referenceSet = ProblemFactory.getInstance()
.getReferenceSet(commandLine.getOptionValue(
"problem"));
}
if (referenceSet == null) {
throw new FrameworkException(
"no reference set available");
}
QualityIndicator indicator = new QualityIndicator(
problem, referenceSet);
output = new MetricFileWriter(indicator, outputFile);
} else {
output = new ResultFileWriter(problem, outputFile,
!commandLine.hasOption("novariables"));
}
// resume at the last good output
for (int i = 0; i < output.getNumberOfEntries(); i++) {
if (input.hasNext()) {
input.next();
} else {
throw new FrameworkException(
"output has more entries than input");
}
}
// setup any default parameters
Properties defaultProperties = new Properties();
if (commandLine.hasOption("properties")) {
for (String property : commandLine
.getOptionValues("properties")) {
String[] tokens = property.split("=");
if (tokens.length == 2) {
defaultProperties.setProperty(tokens[0],
tokens[1]);
} else {
throw new FrameworkException(
"malformed property argument");
}
}
}
if (commandLine.hasOption("epsilon")) {
defaultProperties.setProperty("epsilon", commandLine
.getOptionValue("epsilon"));
}
// seed the pseudo-random number generator
if (commandLine.hasOption("seed")) {
PRNG.setSeed(Long.parseLong(commandLine
.getOptionValue("seed")));
}
// process the remaining runs
while (input.hasNext()) {
Properties properties = input.next();
properties.putAll(defaultProperties);
process(commandLine.getOptionValue("algorithm"),
properties);
}
} finally {
if (output != null) {
output.close();
}
}
} finally {
if (input != null) {
input.close();
}
}
} finally {
if (problem != null) {
problem.close();
}
}
}
/**
* Performs a single run of the specified algorithm using the parameters.
*
* @param algorithmName the algorithm name
* @param properties the parameters stored in a properties object
* @throws IOException if an I/O error occurred
*/
protected void process(String algorithmName, Properties properties)
throws IOException {
// instrument the problem to record timing information
TimingProblem timingProblem = new TimingProblem(problem);
Algorithm algorithm = AlgorithmFactory.getInstance().getAlgorithm(
algorithmName, properties, timingProblem);
// find the maximum NFE to run
if (!properties.containsKey("maxEvaluations")) {
throw new FrameworkException("maxEvaluations not defined");
}
int maxEvaluations = (int) Double.parseDouble(properties
.getProperty("maxEvaluations"));
// run the algorithm
long startTime = System.nanoTime();
while (!algorithm.isTerminated()
&& (algorithm.getNumberOfEvaluations() < maxEvaluations)) {
algorithm.step();
}
long endTime = System.nanoTime();
// extract the result and free any resources
NondominatedPopulation result = algorithm.getResult();
algorithm.terminate();
// apply epsilon-dominance if required
if (properties.containsKey("epsilon")) {
TypedProperties typedProperties = new TypedProperties(properties);
double[] epsilon = typedProperties.getDoubleArray("epsilon", null);
result = EpsilonHelper.convert(result, epsilon);
}
// record instrumented data
Properties timingData = new Properties();
timingData.setProperty("EvaluationTime",
Double.toString(timingProblem.getTime()));
timingData.setProperty("TotalTime",
Double.toString((endTime - startTime) / 1e9));
// write result to output
output.append(new ResultEntry(result, timingData));
}
/**
* Starts the command line utility for evaluating an algorithm using many
* parameterizations.
*
* @param args the command line arguments
* @throws Exception if an error occurred
*/
public static void main(String[] args) throws Exception {
new Evaluator().start(args);
}
}