From 21fb2cb373ecd4976dbc4c60490b8aa2a11ed923 Mon Sep 17 00:00:00 2001 From: Forrest Stonedahl Date: Fri, 28 Jul 2017 15:51:09 -0500 Subject: [PATCH 1/6] early work on MOEA integration --- src/bsearch/MOEAlink/BSProblem.java | 74 +++ src/bsearch/MOEAlink/RunBSProblem.java | 112 +++++ src/bsearch/algorithms/SearchMethod.java | 2 +- src/bsearch/app/BehaviorSearch.java | 14 +- .../evaluation/DerivativeFitnessFunction.java | 6 +- src/bsearch/evaluation/SearchManager.java | 2 +- src/bsearch/fx/BSearchMain.fxml | 2 +- src/bsearch/fx/ProgressController.java | 6 +- src/bsearch/fx/RunOptionDialog.fxml | 2 +- src/bsearch/fx/RunOptionDialogController.java | 15 +- src/bsearch/space/CategoricalSpec.java | 13 + src/bsearch/space/ConstantSpec.java | 13 + src/bsearch/space/DoubleContinuousSpec.java | 12 + src/bsearch/space/DoubleDiscreteSpec.java | 12 + src/bsearch/space/ParameterSpec.java | 14 +- src/bsearch/space/SearchSpace.java | 20 +- src/bsearch/test/SearchSpaceTest.java | 81 +++ test/TesterMOEA.bsearch | 34 ++ test/TesterMOEA.nlogo | 476 ++++++++++++++++++ 19 files changed, 878 insertions(+), 32 deletions(-) create mode 100644 src/bsearch/MOEAlink/BSProblem.java create mode 100644 src/bsearch/MOEAlink/RunBSProblem.java create mode 100644 src/bsearch/test/SearchSpaceTest.java create mode 100644 test/TesterMOEA.bsearch create mode 100644 test/TesterMOEA.nlogo diff --git a/src/bsearch/MOEAlink/BSProblem.java b/src/bsearch/MOEAlink/BSProblem.java new file mode 100644 index 0000000..204f181 --- /dev/null +++ b/src/bsearch/MOEAlink/BSProblem.java @@ -0,0 +1,74 @@ +package bsearch.MOEAlink; + +import java.util.LinkedHashMap; +import java.util.List; + +import org.moeaframework.core.Solution; +import org.moeaframework.core.Variable; +import org.moeaframework.core.variable.EncodingUtils; +import org.moeaframework.problem.AbstractProblem; +import org.nlogo.util.MersenneTwisterFast; + +import bsearch.app.BehaviorSearchException; +import bsearch.app.SearchProtocol; +import bsearch.evaluation.SearchManager; +import bsearch.nlogolink.ModelRunner.ModelRunnerException; +import bsearch.representations.Chromosome; +import bsearch.representations.ChromosomeFactory; +import bsearch.representations.ChromosomeTypeLoader; +import bsearch.space.ParameterSpec; +import bsearch.space.SearchSpace; + +public class BSProblem extends AbstractProblem { + private SearchProtocol protocol; + private SearchSpace space; + private SearchManager searchManager; + private ChromosomeFactory cFactory; // TODO: remove this later... + MersenneTwisterFast rng =new MersenneTwisterFast(); + + public BSProblem(SearchProtocol protocol, SearchManager searchManager) { + super(protocol.paramSpecStrings.size(), 1); + this.protocol = protocol; + this.searchManager = searchManager; + this.space = new SearchSpace(protocol.paramSpecStrings); + try { + cFactory = ChromosomeTypeLoader.createFromName(protocol.chromosomeType); + } catch (BehaviorSearchException e) { + e.printStackTrace(); + } + } + + @Override + public void evaluate(Solution solution) { + List specs = space.getParamSpecs(); + + LinkedHashMap paramSettings = new LinkedHashMap<>(); + for (int i = 0; i < solution.getNumberOfVariables(); i++) { + ParameterSpec spec = specs.get(i); + Object val = spec.getValueFromMOEAVariable(solution.getVariable(i)); + paramSettings.put(spec.getParameterName(), val); + } + Chromosome point = cFactory.createChromosome(this.space, paramSettings); + double fitness = 0.0; + try { + fitness = searchManager.computeFitnessSingle(point, protocol.fitnessSamplingReplications, rng); + } catch (ModelRunnerException | BehaviorSearchException | InterruptedException e) { + e.printStackTrace(); + } + if (!protocol.fitnessMinimized) { + fitness *= -1; // invert fitness for maximization problems... + } + + solution.setObjective(0, fitness); + } + + @Override + public Solution newSolution() { + List vars = space.getMOEAVariables(); + Solution solution = new Solution(vars.size(), 1); + for (int i = 0; i < vars.size(); i++) { + solution.setVariable(i, vars.get(i)); + } + return solution; + } +} \ No newline at end of file diff --git a/src/bsearch/MOEAlink/RunBSProblem.java b/src/bsearch/MOEAlink/RunBSProblem.java new file mode 100644 index 0000000..b2e6842 --- /dev/null +++ b/src/bsearch/MOEAlink/RunBSProblem.java @@ -0,0 +1,112 @@ +package bsearch.MOEAlink; + +import java.io.IOException; + +import org.moeaframework.Executor; +import org.moeaframework.Instrumenter; +import org.moeaframework.analysis.collector.Accumulator; +import org.moeaframework.analysis.plot.Plot; +import org.moeaframework.core.NondominatedPopulation; +import org.moeaframework.core.Solution; +import org.moeaframework.core.variable.EncodingUtils; +import org.moeaframework.util.progress.ProgressEvent; +import org.moeaframework.util.progress.ProgressListener; +import org.xml.sax.SAXException; + +import bsearch.app.SearchProtocol; +import bsearch.evaluation.SearchManager; +import bsearch.evaluation.StandardFitnessFunction; +import bsearch.nlogolink.BatchRunner; +import bsearch.nlogolink.ModelRunner; +import bsearch.nlogolink.Utils; +import bsearch.util.GeneralUtils; + +public class RunBSProblem { + + public static void main(String[] args) throws IOException, SAXException { +// Instrumenter instrumenter = new Instrumenter() +// .withProblemClass(SchafferProblem.class) +// .withFrequency(1000) +// .attachElapsedTimeCollector(); + + String FILENAME = GeneralUtils.attemptResolvePathFromBSearchRoot("test/TesterMOEA.bsearch") ; + + GeneralUtils.updateProtocolFolder(FILENAME); + + SearchProtocol protocol = SearchProtocol.loadFile( FILENAME ) ; + + boolean measureEveryTick = !protocol.fitnessCollecting.equals(SearchProtocol.FITNESS_COLLECTING.AT_FINAL_STEP); + ModelRunner.Factory mrunnerFactory = new ModelRunner.Factory( + GeneralUtils.attemptResolvePathFromProtocolFolder(protocol.modelFile), measureEveryTick, + protocol.modelStepLimit, protocol.modelSetupCommands, protocol.modelStepCommands, + protocol.modelStopCondition, protocol.modelMetricReporter, protocol.modelMeasureIf); + int numEvaluationThreads = 1; + BatchRunner batchRunner = new BatchRunner(numEvaluationThreads, mrunnerFactory); + + SearchManager manager = new SearchManager(0, batchRunner, protocol, new StandardFitnessFunction(protocol), + false, 0); + + Plot plot = new Plot(); + + ProgressListener progListener = new ProgressListener() { + @Override + public void progressUpdate(ProgressEvent event) { +// if (event.getCurrentNFE() % 10 != 0) { +// return; +// } + System.out.println("nfe: " + event.getCurrentNFE()); + NondominatedPopulation tempResult = event.getCurrentAlgorithm().getResult(); + System.out.println(" size: " + tempResult.size()); + Solution oneOfBest = tempResult.get(0); + System.out.println(" obj: " + oneOfBest.getObjective(0)); + + plot.add("NFE"+event.getCurrentNFE(), tempResult); + // Solution solution = event.getCurrentAlgorithm().getResult().get(0); +// System.out.printf("%.5f => %.5f, %.5f\n", +// EncodingUtils.getReal(solution.getVariable(0)), +// solution.getObjective(0), solution.getObjective(1)); +// System.out.println("result= " + sol.toString()); + +// Accumulator acc = instrumenter.getLastAccumulator(); +// for (String key : acc.keySet()) { +// System.out.println(" " + key + ": " + acc.get(key, acc.size(key) - 1)); +// } + + } + }; + long startTime = System.currentTimeMillis(); + NondominatedPopulation result = new Executor().withAlgorithm("GA") + .withProblem(new BSProblem(protocol,manager)) + .withProperty("populationSize", 20) +// .withInstrumenter(instrumenter) + .withMaxEvaluations(protocol.evaluationLimit) + .distributeOn(1) + //.withProgressListener(progListener) + .run(); + long endTime = System.currentTimeMillis(); + System.out.println(endTime-startTime); + //plot.show(); + + + try { + batchRunner.dispose(); + Utils.fullyShutDownNetLogoLink(); + } catch (InterruptedException e) { + e.printStackTrace(); + } +// for (Solution solution : result) { +// System.out.printf("%.5f => %.5f, %.5f\n", +// EncodingUtils.getReal(solution.getVariable(0)), +// solution.getObjective(0), solution.getObjective(1)); +// } + +// new Plot() +// .add("NSGAII", result) +// .show(); +// +// new Plot() +// .add(instrumenter.getLastAccumulator()) +// .show(); + } + +} diff --git a/src/bsearch/algorithms/SearchMethod.java b/src/bsearch/algorithms/SearchMethod.java index 81c7e7e..04e2cb1 100644 --- a/src/bsearch/algorithms/SearchMethod.java +++ b/src/bsearch/algorithms/SearchMethod.java @@ -49,7 +49,7 @@ public strictfp interface SearchMethod //TODO: return SearchResults object? Or just use the SearchManager for this purpose? public abstract void search( SearchSpace space , ChromosomeFactory cFactory, SearchProtocol protocol, - SearchManager archive, MersenneTwisterFast rng ) + SearchManager manager, MersenneTwisterFast rng ) throws BehaviorSearchException, NetLogoLinkException, InterruptedException; public abstract boolean supportsAdaptiveSampling(); diff --git a/src/bsearch/app/BehaviorSearch.java b/src/bsearch/app/BehaviorSearch.java index 7b0e287..c0347af 100644 --- a/src/bsearch/app/BehaviorSearch.java +++ b/src/bsearch/app/BehaviorSearch.java @@ -61,6 +61,8 @@ public static void runMultipleSearches(SearchProtocol protocol, int numSearches, public static SearchManager runProtocol(SearchProtocol protocol, SearchSpace space, int searchIDNumber, int numEvaluationThreads, MersenneTwisterFast rng, List listeners) throws SearchParameterException, BehaviorSearchException, InterruptedException { boolean measureEveryTick = !protocol.fitnessCollecting.equals(SearchProtocol.FITNESS_COLLECTING.AT_FINAL_STEP); + System.out.println("***" + GeneralUtils.attemptResolvePathFromProtocolFolder(protocol.modelFile)); + ModelRunner.Factory mrunnerFactory = new ModelRunner.Factory( GeneralUtils.attemptResolvePathFromProtocolFolder(protocol.modelFile), measureEveryTick, protocol.modelStepLimit, protocol.modelSetupCommands, protocol.modelStepCommands, @@ -96,22 +98,22 @@ public static SearchManager runProtocol(SearchProtocol protocol, SearchSpace spa } } - SearchManager archive = new SearchManager(searchIDNumber, batchRunner, protocol, ffun, false, 0.0); + SearchManager manager = new SearchManager(searchIDNumber, batchRunner, protocol, ffun, false, 0.0); for (ResultListener listener: listeners) { - archive.addResultsListener(listener); + manager.addResultsListener(listener); } ChromosomeFactory cFactory = ChromosomeTypeLoader.createFromName(protocol.chromosomeType); try { for (ResultListener listener : listeners) { - listener.searchStarting(archive); + listener.searchStarting(manager); } - searcher.search( space , cFactory, protocol, archive, rng ); + searcher.search( space , cFactory, protocol, manager, rng ); for (ResultListener listener : listeners) { - listener.searchFinished(archive); + listener.searchFinished(manager); } } catch (NetLogoLinkException ex) @@ -127,7 +129,7 @@ public static SearchManager runProtocol(SearchProtocol protocol, SearchSpace spa } } - return archive; + return manager; } diff --git a/src/bsearch/evaluation/DerivativeFitnessFunction.java b/src/bsearch/evaluation/DerivativeFitnessFunction.java index 5a127c4..514d9e9 100644 --- a/src/bsearch/evaluation/DerivativeFitnessFunction.java +++ b/src/bsearch/evaluation/DerivativeFitnessFunction.java @@ -15,7 +15,9 @@ /** - * This class is a work-in-progress. Currently unused... + * This class is used to compute fitness based on an approximation of the derivative + * of the objective function -- i.e. how much change there is in the objective + * function as one of the parameters is varied... */ public strictfp class DerivativeFitnessFunction implements FitnessFunction { @@ -44,7 +46,7 @@ private Chromosome getPointDeltaNearby(Chromosome point) throws BehaviorSearchEx //Special case: if we set paramName to "@MUTATE@" then it chooses a neighboring point // in the search space by using mutation (with the mutation rate specified by deltaDistance) // from the current point. - if (paramName.equals("@MUTATE@")) + if (paramName.equals("@MUTATE@")) // TODO: Consider, is this really useful, or should we remove this feature? { double mutRate = deltaDistance; int failedMutationCounter = 0; diff --git a/src/bsearch/evaluation/SearchManager.java b/src/bsearch/evaluation/SearchManager.java index 32cd362..7649e32 100644 --- a/src/bsearch/evaluation/SearchManager.java +++ b/src/bsearch/evaluation/SearchManager.java @@ -68,7 +68,7 @@ public void addResultsListener(ResultListener listener) } /** - * Note that this method *will* effect the state of the RNG, so you may wish to pass in a cloned copy of your RNG, + * Note that this method *will* affect the state of the RNG, so you may wish to pass in a cloned copy of your RNG, * or a brand new RNG, rather than an RNG that you are using for other purposes... * @param pointOfInterest * @param numReplicationsDesired diff --git a/src/bsearch/fx/BSearchMain.fxml b/src/bsearch/fx/BSearchMain.fxml index 1e5efb3..cdfde41 100644 --- a/src/bsearch/fx/BSearchMain.fxml +++ b/src/bsearch/fx/BSearchMain.fxml @@ -310,7 +310,7 @@ - + diff --git a/src/bsearch/fx/ProgressController.java b/src/bsearch/fx/ProgressController.java index 2678e50..9ad3437 100644 --- a/src/bsearch/fx/ProgressController.java +++ b/src/bsearch/fx/ProgressController.java @@ -64,7 +64,7 @@ public void startSearchTask(SearchProtocol protocol, BehaviorSearch.RunOptions r labelMessage.setText("Search 0 of " + runOptions.numSearches); taskStartTime = System.currentTimeMillis(); - TaskWorker insideTask = new TaskWorker(protocol, runOptions); + BSearchTaskWorker insideTask = new BSearchTaskWorker(protocol, runOptions); task = new FutureTask(insideTask, null); new Thread(new Runnable() { @@ -142,14 +142,14 @@ public void doCancel(ActionEvent event) { } - class TaskWorker implements Runnable, ResultListener { + class BSearchTaskWorker implements Runnable, ResultListener { private SearchProtocol protocol; private BehaviorSearch.RunOptions runOptions; protected Throwable fatalException = null; private double evaluationLimit; - public TaskWorker(SearchProtocol protocol, BehaviorSearch.RunOptions runOptions) { + public BSearchTaskWorker(SearchProtocol protocol, BehaviorSearch.RunOptions runOptions) { this.protocol = protocol; this.runOptions = runOptions; this.evaluationLimit = (double) protocol.evaluationLimit; diff --git a/src/bsearch/fx/RunOptionDialog.fxml b/src/bsearch/fx/RunOptionDialog.fxml index 08bce25..5fd66da 100644 --- a/src/bsearch/fx/RunOptionDialog.fxml +++ b/src/bsearch/fx/RunOptionDialog.fxml @@ -19,7 +19,7 @@