diff --git a/src/eva2/OptimizerRunnable.java b/src/eva2/OptimizerRunnable.java index b52b2884..5dd8cb04 100644 --- a/src/eva2/OptimizerRunnable.java +++ b/src/eva2/OptimizerRunnable.java @@ -38,6 +38,8 @@ public class OptimizerRunnable implements Runnable { private boolean postProcessOnly = false; private InterfaceTextListener listener = null; private String ident="OptimizerRunnable"; + private static int cntID = 0; + private int rnblID = -1; /** * Construct an OptimizerRunnable with given parameters and a StatisticsStandalone instance without restart, @@ -83,6 +85,8 @@ public class OptimizerRunnable implements Runnable { * @param restart */ public OptimizerRunnable(GOParameters params, InterfaceStatistics stats, boolean restart) { + rnblID = cntID; + cntID++; proc = new Processor(stats, null, params); if (proc.getStatistics() instanceof AbstractStatistics) ((AbstractStatistics)proc.getStatistics()).setSaveParams(false); doRestart = restart; @@ -96,6 +100,14 @@ public class OptimizerRunnable implements Runnable { return ident; } + /** + * A unique ID for the runnable. + * @return + */ + public int getID() { + return rnblID; + } + public InterfaceGOParameters getGOParams() { return proc.getGOParams(); } @@ -156,6 +168,10 @@ public class OptimizerRunnable implements Runnable { return isFinished; } + public boolean wasAborted() { + return (proc!=null && (proc.wasAborted())); + } + public void restartOpt() { proc.restartOpt(); } @@ -243,12 +259,12 @@ public class OptimizerRunnable implements Runnable { } /** - * Set the additional info output. + * Indicate whether full stats should be printed as text (or only selected entries). * @see StatsParameter * @param addInfo */ - public void setOutputAdditionalInfo(boolean addInfo) { - ((AbstractStatistics)proc.getStatistics()).getStatisticsParameter().setOutputAdditionalInfo(addInfo); + public void setOutputFullStatsToText(boolean addInfo) { + ((AbstractStatistics)proc.getStatistics()).getStatisticsParameter().setOutputAllFieldsAsText(addInfo); } // public void configureStats(int verbosityLevel, int outputDirection, int multiRuns, boolean additionalInfo) { diff --git a/src/eva2/gui/EvATabbedFrameMaker.java b/src/eva2/gui/EvATabbedFrameMaker.java index d2560bc0..d5759e57 100644 --- a/src/eva2/gui/EvATabbedFrameMaker.java +++ b/src/eva2/gui/EvATabbedFrameMaker.java @@ -17,14 +17,20 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.io.Serializable; import java.util.ArrayList; +import java.util.List; import javax.swing.JPanel; import javax.swing.JTabbedPane; +import eva2.server.go.InterfaceNotifyOnInformers; +import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; + /** - * Produces the main EvA2 frame and a tool bar instance. + * Produces the main EvA2 frame and a tool bar instance. + * TODO This class should be removed alltogether. */ -public class EvATabbedFrameMaker implements Serializable, PanelMaker { +public class EvATabbedFrameMaker implements Serializable, PanelMaker, InterfaceNotifyOnInformers { + private static final long serialVersionUID = 2637376545826821423L; private ArrayList guiContainer; private JExtToolBar m_BarStandard; EvAModuleButtonPanelMaker butPanelMkr=null; @@ -42,7 +48,24 @@ public class EvATabbedFrameMaker implements Serializable, PanelMaker { gbconst.weighty = 1; gbconst.gridwidth = GridBagConstraints.REMAINDER; - JTabbedPane m_MainPanel = new JTabbedPane(); + final JTabbedPane m_MainPanel = new JTabbedPane(); +// m_MainPanel.addChangeListener(new ChangeListener() { +// /* +// * This listener was added to catch the switch to the statistics panel. In that event, +// * the stats selection string may have to be updated. +// */ +// public void stateChanged(ChangeEvent e) { +//// System.out.println("AAAA " + e.toString()); +// if (m_MainPanel.getSelectedIndex()==1) { +// // the statistics panel is being activated! +//// System.out.println(guiContainer); +// // the third object should be the statistics panel, refer to GenericModuleAdapter +// JParaPanel statsPan = (JParaPanel) guiContainer.get(2); +//// System.out.println(statsPan.m_LocalParameter); +//// statsPan.m_Editor.setValue(statsPan.m_Editor.getValue()); // really update the contents of the stats panel -- + // this is now done in a cleaner way using this class as a listener from AbstractGOParameters +// } +// }}); m_BarStandard = new JExtToolBar(); m_BarStandard.setFloatable(false); @@ -73,4 +96,20 @@ public class EvATabbedFrameMaker implements Serializable, PanelMaker { butPanelMkr.onUserStart(); } else System.err.println("Error: button panel was null (EvATabbedFrameMaker)"); } + + public void setInformers( + List informers) { + // if the informers have changed, update the GUI element which displays them + try { + JParaPanel statsPan = (JParaPanel) guiContainer.get(2); + if (statsPan.m_Editor!=null) { + statsPan.m_Editor.setValue(statsPan.m_Editor.getValue()); // really update the contents of the stats panel +// System.out.println("OOO setting informers to stats panel succeeded!"); + } + } catch(Exception e) { + System.err.println("Failed to update statistics panel from " + this.getClass()); + System.err.println(e.getMessage()); + e.printStackTrace(System.err); + } + } } diff --git a/src/eva2/server/go/GOStandaloneVersion.java b/src/eva2/server/go/GOStandaloneVersion.java index 0e50b8eb..8e0dd807 100644 --- a/src/eva2/server/go/GOStandaloneVersion.java +++ b/src/eva2/server/go/GOStandaloneVersion.java @@ -26,6 +26,7 @@ import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import eva2.client.EvAClient; +import eva2.gui.BeanInspector; import eva2.gui.JParaPanel; import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.individuals.ESIndividualDoubleData; @@ -410,7 +411,7 @@ public class GOStandaloneVersion implements InterfaceGOStandalone, InterfacePopu if (Thread.interrupted()) throw new InterruptedException(); // write header to file - this.writeToFile(" FitnessCalls\t Best\t Mean\t Worst \t" + this.m_GO.getProblem().getAdditionalFileStringHeader(this.m_GO.getOptimizer().getPopulation())); + this.writeToFile(" FitnessCalls\t Best\t Mean\t Worst \t" + BeanInspector.toString(this.m_GO.getProblem().getAdditionalFileStringHeader(this.m_GO.getOptimizer().getPopulation()), '\t', false)); if ((this.m_ContinueFlag) && (this.m_Backup != null)) { this.m_RecentFC += this.m_Backup.getFunctionCalls(); this.m_GO.getOptimizer().getProblem().initProblem(); diff --git a/src/eva2/server/go/InterfaceGOParameters.java b/src/eva2/server/go/InterfaceGOParameters.java index ee0bb7b8..9691bc27 100644 --- a/src/eva2/server/go/InterfaceGOParameters.java +++ b/src/eva2/server/go/InterfaceGOParameters.java @@ -1,7 +1,7 @@ package eva2.server.go; -import eva2.gui.GenericObjectEditor; import eva2.server.go.operators.postprocess.InterfacePostProcessParams; +import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; import eva2.server.go.problems.InterfaceOptimizationProblem; import eva2.server.go.strategies.InterfaceOptimizer; @@ -69,15 +69,13 @@ public interface InterfaceGOParameters { public void setPostProcessParams(InterfacePostProcessParams ppp); public String postProcessParamsTipText(); public void setDoPostProcessing(boolean doPP); -// public int getPostProcessSteps(); -// public void setPostProcessSteps(int ppSteps); -// public String postProcessStepsTipText(); -// -// public boolean isPostProcess(); -// public void setPostProcess(boolean postProcess); -// public String postProcessTipText(); -// -// public double getPostProcessClusterSigma(); -// public void setPostProcessClusterSigma(double postProcessClusterSigma); -// public String postProcessClusterSigmaTipText(); + + /** + * Give an instance which should be informed about elements which are additional informers. + * + * @see InterfaceAdditionalPopulationInformer + * @param o + */ + public void addInformableInstance(InterfaceNotifyOnInformers o); + public boolean removeInformableInstance(InterfaceNotifyOnInformers o); } diff --git a/src/eva2/server/go/InterfaceNotifyOnInformers.java b/src/eva2/server/go/InterfaceNotifyOnInformers.java new file mode 100644 index 00000000..b39b27a1 --- /dev/null +++ b/src/eva2/server/go/InterfaceNotifyOnInformers.java @@ -0,0 +1,12 @@ +package eva2.server.go; + +import java.util.List; + +import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; + +public interface InterfaceNotifyOnInformers { + /** + * Notify the object about informer instances. + */ + public void setInformers(List informers); +} diff --git a/src/eva2/server/go/operators/postprocess/PostProcess.java b/src/eva2/server/go/operators/postprocess/PostProcess.java index 620a8e44..4f08285a 100644 --- a/src/eva2/server/go/operators/postprocess/PostProcess.java +++ b/src/eva2/server/go/operators/postprocess/PostProcess.java @@ -3,6 +3,7 @@ package eva2.server.go.operators.postprocess; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Vector; import eva2.OptimizerFactory; import eva2.OptimizerRunnable; @@ -34,7 +35,6 @@ import eva2.server.go.problems.AbstractMultiModalProblemKnown; import eva2.server.go.problems.AbstractOptimizationProblem; import eva2.server.go.problems.FM0Problem; import eva2.server.go.problems.Interface2DBorderProblem; -import eva2.server.go.problems.InterfaceFirstOrderDerivableProblem; import eva2.server.go.problems.InterfaceInterestingHistogram; import eva2.server.go.problems.InterfaceMultimodalProblemKnown; import eva2.server.go.strategies.EvolutionStrategies; @@ -63,7 +63,7 @@ public class PostProcess { // lower limit mutation step size for HC post processing private static double minMutationStepSize = 0.0000000000000001; // used for hill climbing post processing and only alive during that period - private static OptimizerRunnable ppRunnable = null; + private static Vector ppRunnables = new Vector(); public static final String movedDistanceKey = "PostProcessingMovedBy"; public static final String movedToPositionKey = "PostProcessingMovedTo"; @@ -401,9 +401,10 @@ public class PostProcess { } hc.setPopulation(pop); // hc.initByPopulation(pop, false); - ppRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(hc, pop, problem, 0, term), true); + OptimizerRunnable ppRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(hc, pop, problem, 0, term), true); + + runPP(ppRunnable); - runPP(); } // TODO ! test this @@ -419,8 +420,9 @@ public class PostProcess { int funCallsBefore = pop.getFunctionCalls(); pop.SetFunctionCalls(baseEvals); - ppRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(gda, pop, problem, 0, term), true); - ppRunnable.getStats().createNextGenerationPerformed(gda.getPopulation(), gda, null); + OptimizerRunnable ppRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(gda, pop, problem, 0, term), true); + runPP(ppRunnable); +// ppRunnable.getStats().createNextGenerationPerformed(gda.getPopulation(), gda, null); int funCallsDone = pop.getFunctionCalls()-baseEvals; pop.SetFunctionCalls(funCallsBefore); @@ -432,7 +434,8 @@ public class PostProcess { * Search for a local minimum using nelder mead and return the solution found and the number of steps * (evaluations) actually performed. This uses the whole population as starting population for nelder mead * meaning that typically only one best is returned. - * Returns the number of function calls really performed by the method; sets the number of function calls + * Returns the number of function calls really performed by the method and a flag indicating whether the + * processing was aborted by the user. Sets the number of function calls * in the population back to the original count. * If the baseEvals parameter (which should be >= 0) is > 0, then the number of evaluations is set as * number of evaluations before the optimization using the given terminator. @@ -441,9 +444,9 @@ public class PostProcess { * @param problem * @param term * @param baseEvals - * @return + * @return pair of the number of performed function calls and a flag indicating whether the processing was aborted by the user */ - public static int processWithNMS(Population pop, AbstractOptimizationProblem problem, InterfaceTerminator term, int baseEvals) { + public static Pair processWithNMS(Population pop, AbstractOptimizationProblem problem, InterfaceTerminator term, int baseEvals) { NelderMeadSimplex nms = new NelderMeadSimplex(); nms.setProblemAndPopSize(problem); nms.setGenerationCycle(5); @@ -451,7 +454,7 @@ public class PostProcess { int funCallsBefore = pop.getFunctionCalls(); pop.SetFunctionCalls(baseEvals); - ppRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(nms, pop, problem, 0, term), true); + OptimizerRunnable ppRunnable = new OptimizerRunnable(OptimizerFactory.makeParams(nms, pop, problem, 0, term), true); // as nms creates a new population and has already evaluated them, send a signal to stats ppRunnable.getStats().createNextGenerationPerformed(nms.getPopulation(), nms, null); @@ -460,7 +463,7 @@ public class PostProcess { // System.out.println("grads: " + BeanInspector.toString(((InterfaceFirstOrderDerivableProblem)problem).getFirstOrderGradients(x))); // } - runPP(); + runPP(ppRunnable); // if (problem instanceof InterfaceFirstOrderDerivableProblem) { // double[] x = pop.getBestEAIndividual().getDoublePosition(); @@ -470,14 +473,15 @@ public class PostProcess { int funCallsDone = pop.getFunctionCalls()-baseEvals; pop.SetFunctionCalls(funCallsBefore); - return funCallsDone; + return new Pair(funCallsDone, ppRunnable.wasAborted()); } /** * Search for a local minimum using CMA and return the solution found and the number of steps * (evaluations) actually performed. This uses the whole population as starting population for nelder mead * meaning that typically only one best is returned. - * Returns the number of function calls really performed by the method; sets the number of function calls + * Returns the number of function calls really performed by the method and a flag indicating whether the + * processing was aborted by the user. Sets the number of function calls * in the population back to the original count. If the baseEvals parameter (which should be >= 0) is > 0, * then the number of evaluations is set as * number of evaluations before the optimization using the given terminator. @@ -488,7 +492,7 @@ public class PostProcess { * @param baseEvals * @return */ - public static int processWithCMA(Population pop, AbstractOptimizationProblem problem, InterfaceTerminator term, int baseEvals) { + public static Pair processWithCMA(Population pop, AbstractOptimizationProblem problem, InterfaceTerminator term, int baseEvals) { MutateESRankMuCMA mutator = new MutateESRankMuCMA(); mutator.setInitializeSigma(ESMutationInitialSigma.avgInitialDistance); EvolutionStrategies es = OptimizerFactory.createEvolutionStrategy(pop.size()/2, pop.size(), false, mutator, 1., new CrossoverESDefault(), 0., @@ -503,17 +507,17 @@ public class PostProcess { int funCallsBefore = pop.getFunctionCalls(); pop.SetFunctionCalls(baseEvals); - ppRunnable = new OptimizerRunnable(cmaParams, true); + OptimizerRunnable ppRunnable = new OptimizerRunnable(cmaParams, true); ppRunnable.getStats().createNextGenerationPerformed(cmaParams.getOptimizer().getPopulation(), cmaParams.getOptimizer(), null); - runPP(); + runPP(ppRunnable); pop.clear(); pop.addPopulation(es.getPopulation()); int funCallsDone = es.getPopulation().getFunctionCalls()-baseEvals; pop.SetFunctionCalls(funCallsBefore); - return funCallsDone; + return new Pair(funCallsDone, ppRunnable.wasAborted()); } private static boolean checkRange(AbstractEAIndividual indy) { @@ -686,18 +690,27 @@ public class PostProcess { } term = new EvaluationTerminator(stepsPerCand); } + Pair stepsAbortedFlag = null; for (int i=0; i 0.00000001; epsilon/=10.) { + if (mmkProb.fullListAvailable()) for (double epsilon=0.1; epsilon > 0.00000001; epsilon/=10.) { // out.println("no optima found: " + ((InterfaceMultimodalProblemKnown)mmProb).getNumberOfFoundOptima(pop)); - listener.println("found " + getFoundOptima(solutions, mmkProb.getRealOptima(), epsilon, true).size() + " for epsilon = " + epsilon + ", maxPeakRatio: " + AbstractMultiModalProblemKnown.getMaximumPeakRatio(mmkProb,solutions, epsilon)); + listener.println("found " + getFoundOptima(solutions, mmkProb.getRealOptima(), epsilon, true).size() + " for epsilon = " + epsilon + ", maxPeakRatio: " + mmkProb.getMaximumPeakRatio(solutions)); } } else { // TODO in this form it may cost a lot of time and cant be stopped, which is bad @@ -980,14 +1015,12 @@ public class PostProcess { Population clusteredPop, outputPop, stateBeforeLS; if (params.getPostProcessClusterSigma() > 0) { + // ##### pre clustering clusteredPop = (Population)PostProcess.clusterBest(inputPop, params.getPostProcessClusterSigma(), 0, PostProcess.KEEP_LONERS, PostProcess.BEST_ONLY).clone(); if (clusteredPop.size() < inputPop.size()) { if (listener != null) listener.println("Initial clustering reduced population size from " + inputPop.size() + " to " + clusteredPop.size()); } else if (listener != null) listener.println("Initial clustering yielded no size reduction."); } else clusteredPop = inputPop; -// if (DRAW_PPPOP) { -// plot = draw((params.getPostProcessClusterSigma()>0) ? "After first clustering" : "Initial population", null, clusteredPop, null, problem); -// } int stepsDone = 0; if (params.getPostProcessSteps() > 0) { @@ -1001,12 +1034,14 @@ public class PostProcess { } else { mutator = null; } + // #### Actuall call to post processing stepsDone = processSingleCandidates(params.getPPMethod(), clusteredPop, params.getPostProcessSteps(), stepSize, problem, mutator); if (listener != null) listener.println("Post processing: " + stepsDone + " steps done."); if (params.isWithPlot()) { plot = draw("After " + stepsDone + " steps ("+ params.getPPMethod() + ")", null, stateBeforeLS, clusteredPop, problem); } + // ##### post clustering // some individuals may have now converged again if (params.getPostProcessClusterSigma() > 0) { // so if wished, cluster again. @@ -1016,10 +1051,11 @@ public class PostProcess { } else if (listener != null) listener.println("Second clustering yielded no size reduction."); } else outputPop = clusteredPop; } else outputPop = clusteredPop; - + if (params.isWithPlot()) { plot = draw("After " + stepsDone + " steps (" + params.getPPMethod() + ")" + ((params.getPostProcessClusterSigma()>0) ? " and second clustering" : ""), null, outputPop, null, problem); } + // ##### some statistics double upBnd = PhenotypeMetric.norm(outputPop.getWorstEAIndividual().getFitness())*1.1; upBnd = Math.pow(10,Math.floor(Math.log10(upBnd)+1)); double lowBnd = 0; @@ -1040,7 +1076,7 @@ public class PostProcess { evaluateMultiModal(outputPop, problem, listener); Population nBestPop = outputPop.getBestNIndividuals(params.getPrintNBest()); // n individuals are returned and sorted, all of them if n<=0 - if (listener != null) listener.println("Best after post process:" + ((outputPop.size()>nBestPop.size()) ? ( "(first " + nBestPop.size() + " of " + outputPop.size() + ")") : "")); + if (listener != null) listener.println("Best after post process:" + ((outputPop.size()>nBestPop.size()) ? ( " (first " + nBestPop.size() + " of " + outputPop.size() + ")") : (" (" + nBestPop.size() + ")") )); //////////// output some individual data if (listener != null) for (int i=0; ifitThreshold) System.err.println("Warning: The fitness threshold to turn minimization fitness values into " + + "maximization values should be larger than any optimal fitness! (AbstractMultiModalProblemKnown)"); + if (i==0 || (minOpt>realFits[i])) minOpt = realFits[i]; + // check if the opt. was found and store the corr. found fitness + if (optsFound[i]!=null) { + foundFits[i] = new Double(optsFound[i].getFitness(fitCrit)); + } else foundFits[i]=fitThreshold; // note that it wasnt found -- will result in zero + } + // now we mirror all values with the threshold - provided they are below the threshold... + for (int i=0; irealFits[i] && (foundFits[i]-realFits[i]>1e-10)) { + // this can happen if the real fitness is wrong or if the threshold allows individuals close to better optima to + // be counted for actually inferior optima + System.err.println("Warning: found fitness is better than real fitness - wrong predefined solution or suboptimal epsilon-criterion? Diff was: " + (foundFits[i]-realFits[i])); + } + if ((realFits[i] < 0) || (foundFits[i] < 0)) EVAERROR.errorMsgOnce("warning: for the MPR calculation, negative fitness values may disturb the allover result (AbstractMultiModalProblemKnown)"); + } + // now we can call the standard calculation method + return getMaximumPeakRatioMaximization(realFits, foundFits); + } + // public double getMaximumPeakRatio(Population pop) { // double result = 0, sum = 0; // AbstractEAIndividual posOpt, opt; diff --git a/src/eva2/server/go/problems/AbstractMultiObjectiveOptimizationProblem.java b/src/eva2/server/go/problems/AbstractMultiObjectiveOptimizationProblem.java index 8626d475..7913cb9f 100644 --- a/src/eva2/server/go/problems/AbstractMultiObjectiveOptimizationProblem.java +++ b/src/eva2/server/go/problems/AbstractMultiObjectiveOptimizationProblem.java @@ -11,6 +11,7 @@ import javax.swing.JFrame; import eva2.gui.GraphPointSet; import eva2.gui.Plot; +import eva2.server.go.PopulationInterface; import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.individuals.ESIndividualDoubleData; import eva2.server.go.operators.archiving.ArchivingAllDominating; @@ -514,11 +515,11 @@ public abstract class AbstractMultiObjectiveOptimizationProblem extends Abstract * would be best, since log y can be used. But the value can depend on the problem. */ public Double getDoublePlotValue(Population pop) { - if (this.isPopulationMultiObjective(pop)) { + if (AbstractMultiObjectiveOptimizationProblem.isPopulationMultiObjective(pop)) { return new Double(this.calculateMetric(pop)); } else { // in this case the local Pareto-Front could be multi-objective - if (this.isPopulationMultiObjective(this.m_ParetoFront)) { + if (AbstractMultiObjectiveOptimizationProblem.isPopulationMultiObjective(this.m_ParetoFront)) { return new Double(this.calculateMetric(this.m_ParetoFront)); } else { return new Double(pop.getBestEAIndividual().getFitness(0)); @@ -533,29 +534,23 @@ public abstract class AbstractMultiObjectiveOptimizationProblem extends Abstract return this.m_ParetoFront; } - /** This method returns the header for the additional data that is to be written into a file - * @param pop The population that is to be refined. - * @return String - */ - public String getAdditionalFileStringHeader(Population pop) { - String result; - if (this.isPopulationMultiObjective(pop)) - result = "SMetric"; + @Override + public String[] getAdditionalFileStringHeader(PopulationInterface pop) { + String[] result = new String[1]; + if (AbstractMultiObjectiveOptimizationProblem.isPopulationMultiObjective((Population)pop)) + result[0] = "SMetric"; else - result = "BestFitness"; + result[0] = "BestFitness"; return result; } - /** This method returns the additional data that is to be written into a file - * @param pop The population that is to be refined. - * @return String - */ - public String getAdditionalFileStringValue(Population pop) { - String result = ""; - if (this.isPopulationMultiObjective(pop)) - result += this.calculateMetric(pop); + @Override + public Object[] getAdditionalFileStringValue(PopulationInterface pop) { + Object[] result = new Object[1]; + if (AbstractMultiObjectiveOptimizationProblem.isPopulationMultiObjective((Population)pop)) + result[0] = this.calculateMetric((Population)pop); else - result += pop.getBestEAIndividual().getFitness()[0]; + result[0] = ((Population)pop).getBestEAIndividual().getFitness()[0]; return result; } diff --git a/src/eva2/server/go/problems/AbstractOptimizationProblem.java b/src/eva2/server/go/problems/AbstractOptimizationProblem.java index d159d44a..7edfe60e 100644 --- a/src/eva2/server/go/problems/AbstractOptimizationProblem.java +++ b/src/eva2/server/go/problems/AbstractOptimizationProblem.java @@ -239,22 +239,22 @@ implements InterfaceOptimizationProblem /*, InterfaceParamControllable*/, Serial * @param pop The population that is to be refined. * @return String */ - public String getAdditionalFileStringHeader(PopulationInterface pop) { - if (this instanceof InterfaceInterestingHistogram) return "Solution \t Histogram(c0) \t Score"; - else return "Solution"; + public String[] getAdditionalFileStringHeader(PopulationInterface pop) { + if (this instanceof InterfaceInterestingHistogram) return new String[]{"Solution","Histogram(c0)","Score"}; + else return new String[]{"Solution"}; } /** This method returns the additional data that is to be written into a file * @param pop The population that is to be refined. * @return String */ - public String getAdditionalFileStringValue(PopulationInterface pop) { + public Object[] getAdditionalFileStringValue(PopulationInterface pop) { String solStr = AbstractEAIndividual.getDefaultDataString(pop.getBestIndividual()); if (this instanceof InterfaceInterestingHistogram) { SolutionHistogram hist = ((InterfaceInterestingHistogram)this).getHistogram(); Population sols = PostProcess.clusterBestUpdateHistogram((Population)pop, this, hist, 0, getDefaultAccuracy()); - return solStr + " \t " + hist + "\t" + hist.getScore(); - } else return solStr; + return new Object[]{solStr, hist, hist.getScore()}; + } else return new Object[]{solStr}; } /** diff --git a/src/eva2/server/go/problems/AbstractProblemDouble.java b/src/eva2/server/go/problems/AbstractProblemDouble.java index 2bc92c90..f701feea 100644 --- a/src/eva2/server/go/problems/AbstractProblemDouble.java +++ b/src/eva2/server/go/problems/AbstractProblemDouble.java @@ -1,17 +1,20 @@ package eva2.server.go.problems; -import eva2.gui.BeanInspector; import eva2.gui.GenericObjectEditor; import eva2.gui.TopoPlot; import eva2.server.go.PopulationInterface; +import eva2.server.go.enums.PostProcessMethod; import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.individuals.ESIndividualDoubleData; import eva2.server.go.individuals.InterfaceDataTypeDouble; import eva2.server.go.operators.constraint.AbstractConstraint; import eva2.server.go.operators.constraint.GenericConstraint; +import eva2.server.go.operators.postprocess.PostProcess; +import eva2.server.go.operators.terminators.FitnessConvergenceTerminator; import eva2.server.go.populations.Population; import eva2.server.go.strategies.InterfaceOptimizer; import eva2.tools.Pair; +import eva2.tools.ToolBox; import eva2.tools.diagram.ColorBarCalculator; import eva2.tools.math.Mathematics; import eva2.tools.math.RNG; @@ -139,6 +142,7 @@ public abstract class AbstractProblemDouble extends AbstractOptimizationProblem } return x; } + /** * Add all constraint violations to the individual. Expect that the fitness has already been set. * @@ -352,6 +356,70 @@ public abstract class AbstractProblemDouble extends AbstractOptimizationProblem public double functionValue(double[] point) { return eval(project2DPoint(point))[0]; } + + /** + * Add a position as a known optimum to a list of optima. This method evaluates the fitness + * and applies inverse rotation if necessary. + * + * @param optimas + * @param prob + * @param pos + */ + public static void addUnrotatedOptimum(Population optimas, AbstractProblemDouble prob, double[] pos) { + InterfaceDataTypeDouble tmpIndy; + tmpIndy = (InterfaceDataTypeDouble)prob.getIndividualTemplate().clone(); + tmpIndy.SetDoubleGenotype(pos); + if (prob.isDoRotation()) { + pos = prob.inverseRotateMaybe(pos); // theres an inverse rotation required + tmpIndy.SetDoubleGenotype(pos); + } + ((AbstractEAIndividual)tmpIndy).SetFitness(prob.eval(pos)); + if (!Mathematics.isInRange(pos, prob.makeRange())) { + System.err.println("Warning, add optimum which is out of range!"); + } + optimas.add(tmpIndy); + } + + /** + * Refine a potential solution using Nelder-Mead-Simplex. + * @param prob + * @param pos + * @return + */ + public static double[] refineSolutionNMS(AbstractProblemDouble prob, double[] pos) { + Population pop = new Population(); + InterfaceDataTypeDouble tmpIndy; + tmpIndy = (InterfaceDataTypeDouble)prob.getIndividualTemplate().clone(); + tmpIndy.SetDoubleGenotype(pos); + ((AbstractEAIndividual)tmpIndy).SetFitness(prob.eval(pos)); + pop.add(tmpIndy); + FitnessConvergenceTerminator convTerm = new FitnessConvergenceTerminator(1e-25, 10, false, true); + int calls = PostProcess.processSingleCandidatesNMCMA(PostProcessMethod.nelderMead, pop, convTerm, 0.001, prob); + return ((InterfaceDataTypeDouble)pop.getBestEAIndividual()).getDoubleData(); + } + + + /** + * Refine a candidate solution vector regarding rotations. Saves the + * new solution vector in pos and returns the number of dimensions + * that had to be modified after rotation due to range restrictions. + * + * The given position is expected to be unrotated! The returned solution + * is unrotated as well. + * + * @param pos + * @param prob + * @return + */ + public static int refineWithRotation(double[] pos, AbstractProblemDouble prob) { + double[] res = prob.inverseRotateMaybe(pos); + int modifiedInPrjct = Mathematics.projectToRange(res, prob.makeRange()); + res = AbstractProblemDouble.refineSolutionNMS(prob, res); + res = prob.rotateMaybe(res); + System.arraycopy(res, 0, pos, 0, res.length); + return modifiedInPrjct; + } + /********************************************************************************************************************** * These are for GUI */ @@ -424,19 +492,22 @@ public abstract class AbstractProblemDouble extends AbstractOptimizationProblem } @Override - public String getAdditionalFileStringHeader(PopulationInterface pop) { - String superHeader = super.getAdditionalFileStringHeader(pop); - if (isWithConstraints()) return superHeader + " \tRawFit. \tNum.Viol. \t Sum.Viol."; + public String[] getAdditionalFileStringHeader(PopulationInterface pop) { + String[] superHeader = super.getAdditionalFileStringHeader(pop); + if (isWithConstraints()) return ToolBox.appendArrays(superHeader, new String[]{"RawFit.","Num.Viol.","Sum.Viol."}); else return superHeader; } @Override - public String getAdditionalFileStringValue(PopulationInterface pop) { - String superVal = super.getAdditionalFileStringValue(pop); + public Object[] getAdditionalFileStringValue(PopulationInterface pop) { + Object[] superVal = super.getAdditionalFileStringValue(pop); if (isWithConstraints()) { AbstractEAIndividual indy = (AbstractEAIndividual)pop.getBestIndividual(); Pair violation= getConstraintViolation(indy); - return superVal + " \t" + BeanInspector.toString(indy.getData(rawFitKey)) + " \t" + violation.head() + " \t" + violation.tail(); + return ToolBox.appendArrays(superVal, new Object[]{indy.getData(rawFitKey), + violation.head(), + violation.tail()}); +// return superVal + " \t" + BeanInspector.toString(indy.getData(rawFitKey)) + " \t" + violation.head() + " \t" + violation.tail(); } else return superVal; } @@ -458,7 +529,11 @@ public abstract class AbstractProblemDouble extends AbstractOptimizationProblem if (!isShowing && showP) { TopoPlot plot = new TopoPlot(getName(), "x1", "x2"); plot.setParams(60,60, ColorBarCalculator.BLUE_TO_RED); + this.initProblem(); plot.setTopology(this, makeRange(), true); + if (this instanceof InterfaceMultimodalProblemKnown && ((InterfaceMultimodalProblemKnown)this).fullListAvailable()) { + plot.drawPopulation("Opt", ((InterfaceMultimodalProblemKnown)this).getRealOptima()); + } } isShowing = showP; } diff --git a/src/eva2/server/go/problems/F8Problem.java b/src/eva2/server/go/problems/F8Problem.java index 4de0b2c2..858bd018 100644 --- a/src/eva2/server/go/problems/F8Problem.java +++ b/src/eva2/server/go/problems/F8Problem.java @@ -1,5 +1,15 @@ package eva2.server.go.problems; +import java.util.Arrays; + +import eva2.server.go.PopulationInterface; +import eva2.server.go.individuals.AbstractEAIndividual; +import eva2.server.go.operators.distancemetric.EuclideanMetric; +import eva2.server.go.operators.postprocess.SolutionHistogram; +import eva2.server.go.populations.Population; +import eva2.tools.ToolBox; +import eva2.tools.math.Mathematics; + /** * Created by IntelliJ IDEA. @@ -8,8 +18,12 @@ package eva2.server.go.problems; * Time: 19:40:28 * To change this template use File | Settings | File Templates. */ -public class F8Problem extends AbstractProblemDoubleOffset implements InterfaceMultimodalProblem, java.io.Serializable { +public class F8Problem extends AbstractProblemDoubleOffset + implements InterfaceInterestingHistogram, InterfaceMultimodalProblem, //InterfaceFirstOrderDerivableProblem, + InterfaceMultimodalProblemKnown, java.io.Serializable { + transient protected Population m_ListOfOptima = null; + private static transient boolean state_initializing_optima = false; private double a = 20; private double b = 0.2; private double c = 2*Math.PI; @@ -29,6 +43,9 @@ public class F8Problem extends AbstractProblemDoubleOffset implements InterfaceM super(dim); setDefaultRange(f8Range); } + +// make this a multimodal problem known and add the best optima as in the niching ES papers! + /** This method returns a deep clone of the problem. * @return the clone */ @@ -73,7 +90,14 @@ public class F8Problem extends AbstractProblemDoubleOffset implements InterfaceM return result; } -/********************************************************************************************************************** + + @Override + public void initProblem() { + super.initProblem(); + initListOfOptima(); + } + + /********************************************************************************************************************** * These are for GUI */ /** This method allows the CommonJavaObjectEditorPanel to read the @@ -90,4 +114,181 @@ public class F8Problem extends AbstractProblemDoubleOffset implements InterfaceM public static String globalInfo() { return "Ackley's function."; } + public SolutionHistogram getHistogram() { + if (getProblemDimension() < 15) return new SolutionHistogram(-0.1, 7.9, 16); + else if (getProblemDimension() < 25) return new SolutionHistogram(-0.5, 15.5, 16); + else return new SolutionHistogram(0, 16, 16); + } + +// public double[] getFirstOrderGradients(double[] x) { +// double sum1=0, sum2=0; +// double[] derivs = new double[x.length]; +// x = rotateMaybe(x); +// double dim = (double)this.m_ProblemDimension; +// +// for (int i = 0; i < x.length; i++) { +// double xi = x[i]-m_XOffSet; +// sum1 += (xi)*(xi); +// sum2 += Math.cos(c * (xi)); +// } +// +// for (int i=0; ithresh) { + // add a step to tmpP + Mathematics.svvAddScaled(dx*dir, normedVect, pos, tmpP); + // evaluate tmpP + tmpFit = prob.eval(tmpP)[fitCrit]; + if (tmpFit < oldFit) { + // if tmpP is better than pos continue at new pos + double[] tmp=pos; + pos = tmpP; + tmpP=tmp; + oldFit = tmpFit; + } else { + // otherwise invert direction, reduce step, continue + dx*=0.73; + dir*=-1; + } + } + return pos; + } + + private boolean listOfOptimaNeedsUpdate() { + if (state_initializing_optima) return false; // avoid recursive call during refining with GDA + if (m_ListOfOptima==null || (m_ListOfOptima.size() != (1+2*getProblemDimension()))) { + return true; + } else { // the number of optima is corret - now check different offset or rotation by comparing one fitness value + AbstractEAIndividual indy = m_ListOfOptima.getEAIndividual(1); + double[] curFit = eval(indy.getDoublePosition()); + if (Math.abs(Mathematics.dist(curFit, indy.getFitness(), 2))>1e-10) { + return true; + } else return false; + } +// else { +// if (m_ListOfOptima.isEmpty()) return true; +// else { +// // test for correctness of the second optimum - if its gradient is nonzero, reinit optima +// AbstractEAIndividual testIndy = m_ListOfOptima.getEAIndividual(1); +// double grad[] = this.getFirstOrderGradients(testIndy.getDoublePosition()); +// for (int i=0; i1e-20) { +// m_ListOfOptima.clear(); +// return true; +// } +// } +// return false; +// } +// } + } + + private void addOptimum(double[] pos) { + AbstractProblemDouble.addUnrotatedOptimum(m_ListOfOptima, this, pos); + } + +// private double[] refineSolution(double[] pos) { +// Population pop = new Population(); +// InterfaceDataTypeDouble tmpIndy; +// tmpIndy = (InterfaceDataTypeDouble)((AbstractEAIndividual)this.m_Template).clone(); +// tmpIndy.SetDoubleGenotype(pos); +// ((AbstractEAIndividual)tmpIndy).SetFitness(eval(pos)); +// pop.add(tmpIndy); +// FitnessConvergenceTerminator convTerm = new FitnessConvergenceTerminator(1e-15, 10, false, true); +// int calls = PostProcess.processWithGDA(pop, this, convTerm, 0, 0.0000000000000001, 0.01); +// return ((InterfaceDataTypeDouble)pop.getBestEAIndividual()).getDoubleData(); +// } } \ No newline at end of file diff --git a/src/eva2/server/go/problems/FLensProblem.java b/src/eva2/server/go/problems/FLensProblem.java index 39a0787c..50d7b626 100644 --- a/src/eva2/server/go/problems/FLensProblem.java +++ b/src/eva2/server/go/problems/FLensProblem.java @@ -19,6 +19,7 @@ import javax.swing.JScrollPane; import javax.swing.JTextArea; import eva2.server.go.GOStandaloneVersion; +import eva2.server.go.PopulationInterface; import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.individuals.ESIndividualDoubleData; import eva2.server.go.individuals.InterfaceDataTypeDouble; @@ -344,26 +345,19 @@ public class FLensProblem extends AbstractOptimizationProblem implements Interfa return result; } - /** This method returns the header for the additional data that is to be written into a file - * @param pop The population that is to be refined. - * @return String - */ - public String getAdditionalFileStringHeader(Population pop) { - return "Solution"; + @Override + public String[] getAdditionalFileStringHeader(PopulationInterface pop) { + return new String[]{"Solution"}; } - - /** This method returns the additional data that is to be written into a file - * @param pop The population that is to be refined. - * @return String - */ - public String getAdditionalFileStringValue(Population pop) { + @Override + public Object[] getAdditionalFileStringValue(PopulationInterface pop) { String result ="{"; - double[] data = ((InterfaceDataTypeDouble) pop.getBestEAIndividual()).getDoubleData(); + double[] data = ((InterfaceDataTypeDouble) pop.getBestIndividual()).getDoubleData(); for (int i = 0; i < data.length; i++) result += data[i] +"; "; result += "}"; - return result; + return new Object[]{result}; } - + /** This method allows you to output a string that describes a found solution * in a way that is most suiteable for a given problem. * @param optimizer The individual that is to be shown. diff --git a/src/eva2/server/go/problems/InterfaceAdditionalPopulationInformer.java b/src/eva2/server/go/problems/InterfaceAdditionalPopulationInformer.java index be896024..54476b7c 100644 --- a/src/eva2/server/go/problems/InterfaceAdditionalPopulationInformer.java +++ b/src/eva2/server/go/problems/InterfaceAdditionalPopulationInformer.java @@ -2,16 +2,27 @@ package eva2.server.go.problems; import eva2.server.go.PopulationInterface; +/** + * An interface for an instance providing specialized statistical data on an optimization run. + * This may be statistics depending on specific optimization methods or a specific application + * problem. + * For every additional field, a field name (header) and a value at every iteration must be provided. + * + * @author mkron + * + */ public interface InterfaceAdditionalPopulationInformer { - /** This method returns the header for the additional data that is to be written into a file - * @param pop The population that is to be refined. + /** + * This method returns the header for additional statistical data. + * @param pop The population of the optimization run. * @return String */ - public String getAdditionalFileStringHeader(PopulationInterface pop); + public String[] getAdditionalFileStringHeader(PopulationInterface pop); - /** This method returns the additional data that is to be written into a file + /** + * This method returns additional statistical data. * @param pop The population that is to be refined. * @return String */ - public String getAdditionalFileStringValue(PopulationInterface pop); + public Object[] getAdditionalFileStringValue(PopulationInterface pop); } diff --git a/src/eva2/server/go/problems/MatlabProblem.java b/src/eva2/server/go/problems/MatlabProblem.java index be834cf8..19e886c4 100644 --- a/src/eva2/server/go/problems/MatlabProblem.java +++ b/src/eva2/server/go/problems/MatlabProblem.java @@ -265,7 +265,7 @@ public class MatlabProblem extends AbstractOptimizationProblem implements Interf runnable.setVerbosityLevel(verbosityLevel); if (verbosityLevel>0) runnable.setOutputTo(2); // both file + window else runnable.setOutputTo(1); // only window - runnable.setOutputAdditionalInfo(true); + runnable.setOutputFullStatsToText(true); // log("in MP optimize C\n"); if ((specParams != null) && (specParams.length > 0)) { @@ -346,12 +346,16 @@ public class MatlabProblem extends AbstractOptimizationProblem implements Interf requestPostProcessing(steps, sigma, -1); } + /** + * Try and stop the current optimization as well as any post processing + * currently running. + */ public void stopOptimize() { log(">>>>>>>>>> Stop event!\n"); if (runnable != null) { runnable.stopOpt(); } - PostProcess.stopPP(); + PostProcess.stopAllPP(); } public String getInfoString() { diff --git a/src/eva2/server/go/strategies/ClusterBasedNichingEA.java b/src/eva2/server/go/strategies/ClusterBasedNichingEA.java index 9f90a3e9..84995fe7 100644 --- a/src/eva2/server/go/strategies/ClusterBasedNichingEA.java +++ b/src/eva2/server/go/strategies/ClusterBasedNichingEA.java @@ -1141,13 +1141,18 @@ public class ClusterBasedNichingEA implements InterfacePopulationChangedEventLis return "If fitness improves less than this value within the halting window, convergence is assumed. May be set to zero."; } - public String getAdditionalFileStringHeader(PopulationInterface pop) { - return " Undiff. \t #Act.spec. \tAvg.Spec.Meas. \t #Archived."; + public String[] getAdditionalFileStringHeader(PopulationInterface pop) { + return new String[]{"Undiff.","#Act.spec.","Avg.Spec.Meas.","#Archived."}; } - public String getAdditionalFileStringValue(PopulationInterface pop) { + public Object[] getAdditionalFileStringValue(PopulationInterface pop) { // int actives = countActiveSpec(); - return m_Undifferentiated.size() + " \t " + m_Species.size() + " \t " + BeanInspector.toString(getAvgSpeciesMeasures()[0]) + " \t " + (m_Archive.size()); + return new Object[] { + m_Undifferentiated.size(), + m_Species.size(), + getAvgSpeciesMeasures()[0], + m_Archive.size()}; +// return m_Undifferentiated.size() + " \t " + m_Species.size() + " \t " + BeanInspector.toString(getAvgSpeciesMeasures()[0]) + " \t " + (m_Archive.size()); } /** diff --git a/src/eva2/server/go/strategies/ClusteringHillClimbing.java b/src/eva2/server/go/strategies/ClusteringHillClimbing.java index fee2e15e..5a87b84c 100644 --- a/src/eva2/server/go/strategies/ClusteringHillClimbing.java +++ b/src/eva2/server/go/strategies/ClusteringHillClimbing.java @@ -418,12 +418,12 @@ public class ClusteringHillClimbing implements InterfacePopulationChangedEventLi return "Set the method to be used for the hill climbing as local search"; } - public String getAdditionalFileStringHeader(PopulationInterface pop) { - return "#Indies"; + public String[] getAdditionalFileStringHeader(PopulationInterface pop) { + return new String[]{"#Indies"}; } - public String getAdditionalFileStringValue(PopulationInterface pop) { - return ""+m_Population.size(); + public Object[] getAdditionalFileStringValue(PopulationInterface pop) { + return new Object[]{m_Population.size()}; } public boolean isDoReinitialization() { diff --git a/src/eva2/server/go/strategies/ParticleSwarmOptimization.java b/src/eva2/server/go/strategies/ParticleSwarmOptimization.java index f74110d0..f6aca4a5 100644 --- a/src/eva2/server/go/strategies/ParticleSwarmOptimization.java +++ b/src/eva2/server/go/strategies/ParticleSwarmOptimization.java @@ -1991,6 +1991,10 @@ public class ParticleSwarmOptimization implements InterfaceOptimizer, java.io.Se this.emaPeriods = emaPeriods; } + /** + * This method is necessary to allow access from the Processor. + * @return + */ public ParameterControlManager getParamControl() { return paramControl; } @@ -1998,10 +2002,12 @@ public class ParticleSwarmOptimization implements InterfaceOptimizer, java.io.Se public ParamAdaption[] getParameterControl() { return paramControl.getSingleAdapters(); } - public void setParameterControl(ParamAdaption[] paramControl) { this.paramControl.setSingleAdapters(paramControl); } + public String parameterControlTipText() { + return "You may define dynamic paramter control strategies using the parameter name."; + } /** * Retrieve the set of personal best positions contained in the given population. @@ -2055,20 +2061,19 @@ public class ParticleSwarmOptimization implements InterfaceOptimizer, java.io.Se // this.doLocalSearch = doLocalSearch; // } - public String getAdditionalFileStringHeader(PopulationInterface pop) { - if (emaPeriods > 0) return " \tMeanCurSpeed \tMeanEMASpeed"; - else return " \tMeanCurSpeed"; + public String[] getAdditionalFileStringHeader(PopulationInterface pop) { + if (emaPeriods > 0) return new String[]{"MeanCurSpeed","MeanEMASpeed"}; + else return new String[]{"MeanCurSpeed"}; } - public String getAdditionalFileStringValue(PopulationInterface pop) { - String res=" \t"; + public Object[] getAdditionalFileStringValue(PopulationInterface pop) { AbstractEAIndividual indy = (AbstractEAIndividual)pop.get(0); if (emaPeriods>0) { + double relSp; if (indy instanceof InterfaceDataTypeDouble) { - res = getRelativeEMASpeed(((InterfaceDataTypeDouble)indy).getDoubleRange()) + " \t"; - } else res=Double.NaN + " \t";; - } - res += getPopulationAvgNormedVelocity((Population) pop); - return res; + relSp = getRelativeEMASpeed(((InterfaceDataTypeDouble)indy).getDoubleRange()); + } else relSp=Double.NaN; + return new Object[]{relSp, getPopulationAvgNormedVelocity((Population) pop)}; + } else return new Object[]{getPopulationAvgNormedVelocity((Population) pop)}; } } \ No newline at end of file diff --git a/src/eva2/server/modules/AbstractGOParameters.java b/src/eva2/server/modules/AbstractGOParameters.java index 5fd7e92d..a246913e 100644 --- a/src/eva2/server/modules/AbstractGOParameters.java +++ b/src/eva2/server/modules/AbstractGOParameters.java @@ -1,13 +1,17 @@ package eva2.server.modules; import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; import eva2.gui.BeanInspector; import eva2.server.go.InterfaceGOParameters; +import eva2.server.go.InterfaceNotifyOnInformers; import eva2.server.go.InterfacePopulationChangedEventListener; import eva2.server.go.InterfaceTerminator; import eva2.server.go.operators.postprocess.InterfacePostProcessParams; import eva2.server.go.operators.postprocess.PostProcessParams; +import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; import eva2.server.go.problems.InterfaceOptimizationProblem; import eva2.server.go.strategies.InterfaceOptimizer; @@ -22,11 +26,13 @@ public abstract class AbstractGOParameters implements InterfaceGOParameters, Ser protected InterfaceTerminator m_Terminator; protected InterfacePostProcessParams m_PostProc = new PostProcessParams(false); transient protected InterfacePopulationChangedEventListener m_Listener; - + transient private List toInformAboutInformers = null; + protected AbstractGOParameters() { } protected AbstractGOParameters(AbstractGOParameters Source) { + this(); this.m_Optimizer = Source.m_Optimizer; this.m_Problem = Source.m_Problem; this.m_Terminator = Source.m_Terminator; @@ -36,6 +42,7 @@ public abstract class AbstractGOParameters implements InterfaceGOParameters, Ser } public AbstractGOParameters(InterfaceOptimizer opt, InterfaceOptimizationProblem prob, InterfaceTerminator term) { + this(); m_Optimizer = opt; m_Problem = prob; m_Terminator = term; @@ -76,11 +83,37 @@ public abstract class AbstractGOParameters implements InterfaceGOParameters, Ser return sb.toString(); } + public void addInformableInstance(InterfaceNotifyOnInformers o) { + if (toInformAboutInformers==null) toInformAboutInformers=new LinkedList(); + if (!toInformAboutInformers.contains(o)) toInformAboutInformers.add(o); + o.setInformers(getInformerList()); + } + + public boolean removeInformableInstance(InterfaceNotifyOnInformers o) { + if (toInformAboutInformers==null) return false; + else return toInformAboutInformers.remove(o); + } + + private void fireNotifyOnInformers() { + if (toInformAboutInformers!=null) for (InterfaceNotifyOnInformers listener : toInformAboutInformers) { + listener.setInformers(getInformerList()); + } + } + public void setOptimizer(InterfaceOptimizer optimizer) { this.m_Optimizer = optimizer; this.m_Optimizer.SetProblem(this.m_Problem); if (this.m_Listener != null) this.m_Optimizer.addPopulationChangedEventListener(this.m_Listener); + fireNotifyOnInformers(); } + + private List getInformerList() { + LinkedList ret = new LinkedList(); + if (m_Problem instanceof InterfaceAdditionalPopulationInformer) ret.add(m_Problem); + if (m_Optimizer instanceof InterfaceAdditionalPopulationInformer) ret.add((InterfaceAdditionalPopulationInformer)m_Optimizer); + return ret; + } + public InterfaceOptimizer getOptimizer() { return this.m_Optimizer; } @@ -99,7 +132,9 @@ public abstract class AbstractGOParameters implements InterfaceGOParameters, Ser public void setProblem (InterfaceOptimizationProblem problem) { this.m_Problem = problem; this.m_Optimizer.SetProblem(this.m_Problem); + fireNotifyOnInformers(); } + public InterfaceOptimizationProblem getProblem() { return this.m_Problem; } diff --git a/src/eva2/server/modules/GenericModuleAdapter.java b/src/eva2/server/modules/GenericModuleAdapter.java index baa2f065..601755cd 100644 --- a/src/eva2/server/modules/GenericModuleAdapter.java +++ b/src/eva2/server/modules/GenericModuleAdapter.java @@ -11,6 +11,7 @@ import eva2.gui.GenericObjectEditor; import eva2.gui.JParaPanel; import eva2.gui.PanelMaker; import eva2.server.go.InterfaceGOParameters; +import eva2.server.go.InterfaceNotifyOnInformers; import eva2.server.stat.AbstractStatistics; import eva2.server.stat.InterfaceStatisticsParameter; import eva2.server.stat.StatisticsStandalone; @@ -46,7 +47,10 @@ public class GenericModuleAdapter extends AbstractModuleAdapter implements Seria m_StatisticsModul = new StatisticsStandalone(noGUIStatOut); } m_Processor = new Processor(m_StatisticsModul,this, params); - + + // the statistics want to be informed if the strategy or the optimizer (which provide statistical data as InterfaceAdditionalInformer) change. + if (m_StatisticsModul.getStatisticsParameter() instanceof InterfaceNotifyOnInformers) + params.addInformableInstance((InterfaceNotifyOnInformers)m_StatisticsModul.getStatisticsParameter()); // this prevents the optimizer property to be shown by the GOE if optimizerExpert is true GenericObjectEditor.setExpertProperty(params.getClass(), "optimizer", optimizerExpert); @@ -89,7 +93,9 @@ public class GenericModuleAdapter extends AbstractModuleAdapter implements Seria if (m_RMI && !Proxy.isProxyClass(Stat.getClass())) GUIContainer.add(new JParaPanel( RMIProxyLocal.newInstance(Stat), Stat.getName())); else GUIContainer.add(new JParaPanel(Stat, Stat.getName())); - return new EvATabbedFrameMaker(GUIContainer); + EvATabbedFrameMaker frmMkr = new EvATabbedFrameMaker(GUIContainer); + ((Processor)m_Processor).getGOParams().addInformableInstance(frmMkr); + return frmMkr; } public static String getName() { diff --git a/src/eva2/server/modules/Processor.java b/src/eva2/server/modules/Processor.java index 01bd175a..033e3363 100644 --- a/src/eva2/server/modules/Processor.java +++ b/src/eva2/server/modules/Processor.java @@ -58,6 +58,7 @@ public class Processor extends Thread implements InterfaceProcessor, InterfacePo // private int postProcessSteps = 0; private int runCounter = 0; private Population resPop = null; + private boolean userAborted = false; // transient private String m_OutputPath = ""; // transient private BufferedWriter m_OutputFile = null; @@ -110,9 +111,20 @@ public class Processor extends Thread implements InterfaceProcessor, InterfacePo return; } resPop = null; + userAborted = false; wasRestarted = false; setOptRunning(true); } + + /** + * Return true if the optimization was stopped by the user instead of + * the termination criterion. + * + * @return + */ + public boolean wasAborted() { + return userAborted; + } /** * @@ -124,6 +136,7 @@ public class Processor extends Thread implements InterfaceProcessor, InterfacePo System.err.println("ERROR: Processor is already running !!"); return; } + userAborted = false; wasRestarted = true; setOptRunning(true); } @@ -192,7 +205,7 @@ public class Processor extends Thread implements InterfaceProcessor, InterfacePo */ protected Population optimize(String infoString) { Population resultPop = null; - + if (!isOptRunning()) { System.err.println("warning, this shouldnt happen in processor! Was startOpt called?"); setOptRunning(true); @@ -233,12 +246,12 @@ public class Processor extends Thread implements InterfaceProcessor, InterfacePo } while (isOptRunning() && !this.goParams.getTerminator().isTerminated(this.goParams.getOptimizer().getAllSolutions())); runCounter++; maybeFinishParamCtrl(goParams); - + userAborted = !isOptRunning(); // stop is "normal" if opt wasnt set false by the user (and thus still true) //////////////// Default stats - m_Statistics.stopOptPerformed(isOptRunning(), goParams.getTerminator().lastTerminationMessage()); // stop is "normal" if opt wasnt set false by the user (and thus still true) + m_Statistics.stopOptPerformed(!userAborted, goParams.getTerminator().lastTerminationMessage()); // stop is "normal" if opt wasnt set false by the user (and thus still true) //////////////// PP or set results without further PP - if (isOptRunning()) { + if (!userAborted) { resultPop = performPostProcessing(); if (resultPop==null) { // post processing disabled, so use opt. solutions resultPop = goParams.getOptimizer().getAllSolutions().getSolutions(); diff --git a/src/eva2/server/stat/AbstractStatistics.java b/src/eva2/server/stat/AbstractStatistics.java index 8a45412b..df0a7f7e 100644 --- a/src/eva2/server/stat/AbstractStatistics.java +++ b/src/eva2/server/stat/AbstractStatistics.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import eva2.gui.BeanInspector; @@ -19,6 +20,9 @@ import eva2.server.go.populations.Population; import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; import eva2.server.go.strategies.InterfaceOptimizer; import eva2.tools.Pair; +import eva2.tools.StringSelection; +import eva2.tools.StringTools; +import eva2.tools.ToolBox; import eva2.tools.math.Mathematics; /** @@ -27,7 +31,18 @@ import eva2.tools.math.Mathematics; * updated per iteration in createNextGenerationPerformed and reported to listeners in stopOptPerformed. * Several different verbosity levels are regarded. * The method plotCurrentResults should be implemented to plot further results per iteration. - * + * + * All displayable data is now routed through a single pipeline, which consists in a + * list of Objects assembled in the getOutputValues method. This allows all simple data types which are + * provided by the external informer instances to be handled uniformly to the internally collected data, and + * thus they can be plotted and text-dumped in the same manner. + * Depending on the field selection state and the informers, the list of data fields is dynamically altered, + * however changes during a multi-run are ignored, since the potential of inconsistencies is too high. + * + * Listeners implementing InterfaceTextListener receive String output (human readable). + * Listeners implementing InterfaceStatisticsListener receive the raw data per iteration. + * + * @see StatsParameter * @author mkron * */ @@ -45,13 +60,18 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter * by dynamic population optimizers, also due to the graph output. */ private boolean refineMultiRuns = true; - private ArrayList meanCollection; - private Double[] additionalInfoSums = null, lastAdditionalInfoSums=null; +// private ArrayList meanCollection; + private ArrayList sumDataCollection; // collect summed-up data of multiple runs indexed per iteration + protected Object[] currentStatObjectData = null; // the raw Object data collected in an iteration + protected Double[] currentStatDoubleData = null; // the parsed doubles collected in an iteration (or null for complex data fields) + protected String[] currentHeaderData = null; // the header Strings of the currently provided data + private Double[] statDataSumOverAll = null; +// , lastAdditionalInfoSums=null; // say whether the object should be written to a file every time private boolean saveParams = true; private boolean firstPlot = true; - private int runIterCnt = 0; + private int iterationCounter = 0; // show this many iterations of the averaged performance after a full multi-run private int showAvgIntervals = 9; @@ -66,31 +86,70 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter protected double[] currentBestFit; protected double[] currentBestFeasibleFit; // protected double[] meanBestFeasibleFit; - protected double[] meanFitness; + protected double[] currentMeanFit; protected double[] currentWorstFit; // protected double[] meanBestOfRunFitness; - protected double avgPopDist; - protected double maxPopDist; + protected double currentAvgPopDist; + protected double currentMaxPopDist; protected IndividualInterface bestCurrentIndy, bestOfRunIndy, bestOfRunFeasibleIndy, bestFeasibleAllRuns, bestIndyAllRuns; // collect feasible results of a run private ArrayList runBestFeasibleList; private ArrayList runBestFitList; - private ArrayList textListeners; + private transient ArrayList textListeners; + private transient List dataListeners = null; + private List lastInformerList = null; private PopulationInterface lastSols = null; - + private String textFieldDelimiter = "\t"; + private int defaultFitCriterion = 0; // TODO this might be a user chosen int - or even more elegantly, a MOSOConverter + + protected StringSelection lastFieldSelection = null; // store the graph selection at the beginning of a multi-run + protected boolean lastIsShowFull = false; // store the "show full text" stats property at the beginning of a multi-run + public AbstractStatistics() { firstPlot = true; functionCalls = 0; functionCallSum = 0; convergenceCnt = 0; optRunsPerformed = 0; - runIterCnt = 0; + iterationCounter = 0; textListeners = new ArrayList(); } + public void addDataListener(InterfaceStatisticsListener l) { + if (dataListeners==null) { + dataListeners=new LinkedList(); + } + if (!dataListeners.contains(l)) dataListeners.add(l); + } + + public boolean removeDataListener(InterfaceStatisticsListener l) { + if (dataListeners==null) return false; + else return dataListeners.remove(l); + } + + private void fireDataListeners() { + if (dataListeners!=null) for (InterfaceStatisticsListener l : dataListeners) { + l.notifyGenerationPerformed(currentHeaderData, currentStatObjectData, currentStatDoubleData); + } + } + + /** + * Notify listeners on the start and stop of a run. + * + * @param runNumber current run (started or stopped) + * @param normal in case of stop: the stop was terminated normally (as opposted to manually) + * @param start if true, give the start signal, otherwise the stop signal + */ + private void fireDataListenersStartStop(int runNumber, boolean normal, boolean start) { + if (dataListeners!=null) for (InterfaceStatisticsListener l : dataListeners) { + if (start) l.notifyRunStarted(runNumber, m_StatsParams.getMultiRuns()); + else l.notifyRunStopped(optRunsPerformed, normal); + } + } + public void addTextListener(InterfaceTextListener listener) { if (!textListeners.contains(listener)) { textListeners.add(listener); @@ -165,6 +224,10 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter } if (runNumber == 0) { + currentHeaderData=null; + currentStatDoubleData=null; + currentStatObjectData=null; + functionCallSum = 0; firstPlot = true; optRunsPerformed = 0; @@ -177,12 +240,19 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter // meanBestFeasibleFit = null; runBestFeasibleList = new ArrayList(); runBestFitList = new ArrayList(); - if (refineMultiRuns) meanCollection = new ArrayList(); - else meanCollection = null; - additionalInfoSums = null; - lastAdditionalInfoSums = null; +// if (refineMultiRuns) meanCollection = new ArrayList(); +// else meanCollection = null; + if (refineMultiRuns) sumDataCollection = new ArrayList(); + else sumDataCollection = null; + + statDataSumOverAll = null; +// lastAdditionalInfoSums = null; feasibleFoundAfterSum=-1; numOfRunsFeasibleFound=0; + + // store the intial graph selection state, so that modifications during runtime cannot cause inconsistencies + lastFieldSelection = (StringSelection)m_StatsParams.getFieldSelection().clone(); + lastIsShowFull = m_StatsParams.isOutputAllFieldsAsText(); } feasibleFoundAfter=-1; bestCurrentIndy = null; @@ -191,7 +261,7 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter bestOfRunFeasibleIndy = null; lastInformerList = null; lastSols = null; - runIterCnt = 0; + iterationCounter = 0; if (printRunIntroVerbosity()) printToTextListener("\n****** Multirun "+runNumber); if (params != null) { if (printRunIntroVerbosity()) printToTextListener("\nModule parameters: "); @@ -200,16 +270,17 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter if (printRunIntroVerbosity()) printToTextListener("\nStatistics parameters: "); if (printRunIntroVerbosity()) printToTextListener(BeanInspector.toString(getStatisticsParameter()) + '\n'); functionCalls = 0; + fireDataListenersStartStop(runNumber, true, true); } public void stopOptPerformed(boolean normal, String stopMessage) { if (TRACE) System.out.println("AbstractStatistics.stopOptPerformed"); if (lastSols==null) System.err.println("WARNING, possibly there was no call to createNextGenerationPerformed before calling stopOptPerformed (AnstractStatistics)."); - if (runIterCnt < meanCollection.size()) { + if (iterationCounter < sumDataCollection.size()) { // no good: later run was shorter than the first one. What to do? Discard the longer one: if (TRACE) System.err.println("Error in AbstractStatistics: later run was shorter than earlier one... discarding rest..."); - for (int i=meanCollection.size()-1; i>=runIterCnt; i--) meanCollection.remove(i); + for (int i=sumDataCollection.size()-1; i>=iterationCounter; i--) sumDataCollection.remove(i); } optRunsPerformed++; functionCallSum += functionCalls; @@ -254,7 +325,6 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter } } if (printFinalVerbosity()) printToTextListener("."); - if (m_StatsParams.isOutputAdditionalInfo()) updateLastAdditionalInfo(); // if (currentBestFit!= null) { // if (printRunStoppedVerbosity()) printToTextListener(" Best Fitness: " + BeanInspector.toString(currentBestFit) + "\n"); // } @@ -262,6 +332,7 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter if (printFinalVerbosity()) printToTextListener("\n"); finalizeOutput(); } + fireDataListenersStartStop(optRunsPerformed, normal, false); } private PopulationInterface makeStatsPop() { @@ -286,6 +357,24 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter } + /** + * Calculate the mean fitness of final best individuals over the last series of multi-runs. + * + * @return + */ + public double[] getMeanBestFit(boolean requireFeasible) { + return calcMeanFit(requireFeasible ? runBestFeasibleList : runBestFitList); + } + + /** + * Calculate the median fitness of final best individuals over the last series of multi-runs. + * + * @return + */ + public double[] getMedianBestFit(boolean requireFeasible) { + return calcMedianFit(requireFeasible ? runBestFeasibleList : runBestFitList); + } + protected void finalizeOutput() { if (printFinalVerbosity()) printToTextListener("*******\n Runs performed: " + optRunsPerformed + ", reached target " + convergenceCnt + " times with threshold " + m_StatsParams.getConvergenceRateThreshold() + ", rate " + convergenceCnt/(double)m_StatsParams.getMultiRuns() + '\n'); if (printFinalVerbosity()) printToTextListener(" Average function calls: " + (functionCallSum/optRunsPerformed) + "\n"); @@ -295,41 +384,44 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter printToTextListener(" Average evaluations until feasible ind. was found in " + numOfRunsFeasibleFound + " runs: " + feasibleFoundAfterSum/numOfRunsFeasibleFound + " evaluations\n"); } - if (printFinalVerbosity() && (additionalInfoSums != null)) { - printToTextListener(" Averaged additional info sums: "); - for (int i=0; i1) { if (runBestFitList.size()>0) { // Mathematics.svDiv((double)optRunsPerformed, meanBestOfRunFitness, meanBestOfRunFitness); if (printFinalVerbosity()) { - double[] meanBestFit=calcMeanFit(runBestFitList); + double[] meanBestFit=getMeanBestFit(false); printToTextListener(" MultiRun stats: Mean best fitness: " + BeanInspector.toString(meanBestFit)+"\n"); if (meanBestFit.length==1) printToTextListener(" MultiRun stats: Variance/Std.Dev.: " + BeanInspector.toString(calcStdDevVar(runBestFitList, meanBestFit[0])) + "\n"); - printToTextListener(" MultiRun stats: Median best fitn.: " + BeanInspector.toString(calcMedianFit(runBestFitList))+"\n"); + printToTextListener(" MultiRun stats: Median best fitn.: " + BeanInspector.toString(getMedianBestFit(false))+"\n"); } } if (printFinalVerbosity() && (bestFeasibleAllRuns != null)) printIndy("Overall best feasible", bestFeasibleAllRuns); // if ((runBestFeasibleList.size()>0) && (!equalLists(runBestFeasibleList, runBestFitList))) { // is there a difference between best feasibles and best fit? if (runBestFeasibleList.size()>0) { // always output feasible stats even if theyre equal if (printFinalVerbosity()) { - double[] meanBestFeasibleFit=calcMeanFit(runBestFeasibleList); + double[] meanBestFeasibleFit=getMeanBestFit(true); printToTextListener(" MultiRun stats: Mean best feasible fitness (" + numOfRunsFeasibleFound + " runs): " + BeanInspector.toString(meanBestFeasibleFit)+"\n"); if (meanBestFeasibleFit.length==1) printToTextListener(" MultiRun stats: Variance/Std.Dev.: " + BeanInspector.toString(calcStdDevVar(runBestFeasibleList, meanBestFeasibleFit[0])) + "\n"); - printToTextListener(" MultiRun stats: Median best feasible fitn. (: " + numOfRunsFeasibleFound + " runs): " + BeanInspector.toString(calcMedianFit(runBestFeasibleList))+"\n"); + printToTextListener(" MultiRun stats: Median best feasible fitn. (: " + numOfRunsFeasibleFound + " runs): " + BeanInspector.toString(getMedianBestFit(true))+"\n"); } } - if (refineMultiRuns && (meanCollection != null)) { + if (refineMultiRuns && (sumDataCollection != null)) { if (printFinalVerbosity()) printToTextListener(" Averaged performance:\n"); - for (int i=0; i additionalFields = getAdditionalInfoHeader(lastInformerList, bestPop); + String additionalFields = getOutputHeaderString(lastInformerList, bestPop); +// String header = getOutputHeader(lastInformerList, bestPop); + List vals = getOutputValues(lastInformerList, bestPop); + + StringBuffer sbuf = new StringBuffer("Overall best statistical data: "); + sbuf.append(additionalFields); sbuf.append('\n'); - appendAdditionalInfo(lastInformerList, bestPop, sbuf); + sbuf.append(StringTools.concatValues(vals, textFieldDelimiter)); +// appendAdditionalInfo(lastInformerList, bestPop, sbuf); // getOutputLine(lastInformerList, makeStatsPop()); return sbuf.toString(); } - /** - * Perform a deep equals test on the fitness vectors of both individual lists. - * @param l1 - * @param l2 - * @return - */ - private boolean equalLists( - ArrayList l1, - ArrayList l2) { - boolean equal = true; - Iterator iter1=l1.iterator(); - Iterator iter2=l2.iterator(); - IndividualInterface indy1, indy2; - if (l1.size()!=l2.size()) return false; - else while (equal && (iter1.hasNext() && iter2.hasNext())) { - equal = Arrays.equals(iter1.next().getFitness(), iter2.next().getFitness()); - } - return equal; - } - private double[] calcStdDevVar(ArrayList list, double meanFit) { double tmp=0, sum=0; - for (Iterator iter = list.iterator(); iter.hasNext();) { - IndividualInterface indy = (IndividualInterface) iter.next(); + for (Iterator iter = list.iterator(); iter.hasNext();) { + IndividualInterface indy = iter.next(); tmp=indy.getFitness()[0]-meanFit; sum+=(tmp*tmp); } @@ -391,7 +470,7 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter */ public static double[] calcMeanFit(List list) { double[] sumFit = list.get(0).getFitness().clone(); - for (int i=1; i result, int iterationsToShow) { - double[][] mean; - StringBuffer sbuf = new StringBuffer("Iteration\tFun.Calls\tBest\tMean\tWorst\n"); - double step = result.size()/(iterationsToShow-1.); + public String refineToText(ArrayList data, int iterationsToShow) { + String hd = getOutputHeaderString(lastInformerList, null); + StringBuffer sbuf = new StringBuffer("Iteration"); + sbuf.append(textFieldDelimiter); + sbuf.append(hd); + sbuf.append("\n"); + refineToText(data, iterationsToShow, sbuf, textFieldDelimiter); + return sbuf.toString(); + } + + public static void refineToText(ArrayList data, int iterationsToShow, StringBuffer sbuf, String delim) { + double step = data.size()/(iterationsToShow-1.); int printedIteration=0; - - for(int i = 1; i < result.size()+1; i++) { + Double[] meanData; + for(int i = 1; i < data.size()+1; i++) { // print the first, last and intermediate iterations requested by the integer parameter // first one is printed always, as printedIteration=0 - if ((i==result.size()) || ((i-1)==Math.round(printedIteration*step))) { + if ((i==data.size()) || ((i-1)==Math.round(printedIteration*step))) { printedIteration++; - mean = result.get(i-1); + meanData = data.get(i-1); sbuf.append(i); - sbuf.append("\t"); - sbuf.append(BeanInspector.toString(mean[0])); - sbuf.append("\t"); - sbuf.append(BeanInspector.toString(mean[1])); - sbuf.append("\t"); - sbuf.append(BeanInspector.toString(mean[2])); - sbuf.append("\t"); - sbuf.append(BeanInspector.toString(mean[3])); + for (int k=0; k0); } - protected String getOutputHeader(List informerList, PopulationInterface pop) { - - String headline = "Fun.calls\t Best\t Mean\t Worst "; - if ((informerList == null) || !m_StatsParams.isOutputAdditionalInfo()) { - return headline; - } else { - return headline + getAdditionalInfoHeader(informerList, pop); - } - } - - protected String getAdditionalInfoHeader(List informerList, PopulationInterface pop) { - String hdr=""; - for (InterfaceAdditionalPopulationInformer informer : informerList) { - hdr = hdr + "\t " + informer.getAdditionalFileStringHeader(pop); - } - return hdr; - } - - protected Pair getOutputLine(List informerList, PopulationInterface pop) { - StringBuffer sbuf = new StringBuffer(Integer.toString(functionCalls)); - Double[] addNums = null; - sbuf.append(" \t "); - sbuf.append(BeanInspector.toString(currentBestFit)); - if (meanFitness != null) { - sbuf.append(" \t "); - sbuf.append(BeanInspector.toString(meanFitness)); - } else sbuf.append(" \t #"); - if (currentWorstFit != null) { - sbuf.append(" \t "); - sbuf.append(BeanInspector.toString(currentWorstFit)); - } else sbuf.append(" # \t"); - if (m_StatsParams.isOutputAdditionalInfo()) addNums = appendAdditionalInfo(informerList, pop, sbuf); - return new Pair(sbuf.toString(),addNums); - } - /** - * Append additional informer informations to the given StringBuffer. + * Collect all field names of both internal fields and fields of external informers. Then + * concatenate them to a string using the textFieldDelimiter of the instance. * * @param informerList * @param pop - * @param sbuf - */ - protected Double[] appendAdditionalInfo(List informerList, PopulationInterface pop, StringBuffer sbuf) { - if (informerList != null) { - StringBuffer addBuffer = new StringBuffer(); - for (InterfaceAdditionalPopulationInformer informer : informerList) { - addBuffer.append(" \t "); - addBuffer.append(informer.getAdditionalFileStringValue(pop)); - } - String addInfo = addBuffer.toString(); - Double[] retVals = parseDoubles(addInfo, "\t"); - if (sbuf!=null) sbuf.append(addInfo); - return retVals; - } - return null; - } - - /** - * Parse Double from a String separated by the given regular expression. - * For Substrings which do not convert to Double by Double.parseDouble(String), - * a null value is added as representative. - * - * @param str - * @param colSplit * @return */ - public static Double[] parseDoubles(String str, String splitRegExp) { - ArrayList vals = new ArrayList(); - String[] entries = str.split(splitRegExp); - for (int i=0; i informerList, PopulationInterface pop) { + List headlineFields = getOutputHeaderFieldNames(informerList, pop); + return StringTools.concatFields(headlineFields, textFieldDelimiter); } + + /** + * Collect all field names of both internal fields and fields of external informers. + * The length of this list depends on the field selection state. + * + * @param informerList + * @param pop + * @return + */ + protected List getOutputHeaderFieldNames(List informerList, PopulationInterface pop) { + ArrayList headlineFields = new ArrayList(5); + headlineFields.addAll(Arrays.asList(getSimpleOutputHeader())); + if (informerList != null) { + headlineFields.addAll(getAdditionalInfoHeader(informerList, pop)); + } + return headlineFields; + } + + /** + * Collect the names of data fields which are collected internally.This must correspond to the + * method {@link #getSimpleOutputValues()}. + * + * @see #getSimpleOutputValues() + * @return + */ + protected String[] getSimpleOutputHeader() { + // collect the full header by using the entries of the GraphSelectionEnum + GraphSelectionEnum[] vals = GraphSelectionEnum.values(); + ArrayListheaderEntries = new ArrayList(); + headerEntries.add("FunctionCalls"); + for (int i=0; i getOutputValues(List informerList, PopulationInterface pop) { + LinkedList values = new LinkedList(); + values.addAll(Arrays.asList(getSimpleOutputValues())); + if (informerList != null) { + for (InterfaceAdditionalPopulationInformer informer : informerList) { + List reqList = Arrays.asList(informer.getAdditionalFileStringValue(pop)); + values.addAll(reqList); + } + } + // remove those which are not requested + Iterator iter = values.iterator(); + int cnt=0; + iter.next(); // skip the first field (function calls) which is not regarded here + if (!lastIsShowFull) while (iter.hasNext()) { + iter.next(); + if (!isRequestedField(cnt++)) iter.remove(); + // the cnt variable is one behind the index in the values list, because of the function calls field. + } + return values; +// return StringTools.concatValues(values, textFieldDelimiter); + } + + /** + * Collect all field names of external informer instances. + * The length of this list depends on the field selection state. + * @param informerList + * @param pop + * @return + */ + protected List getAdditionalInfoHeader(List informerList, PopulationInterface pop) { + LinkedList additionals = new LinkedList(); + for (InterfaceAdditionalPopulationInformer informer : informerList) { + additionals.addAll(Arrays.asList(informer.getAdditionalFileStringHeader(pop))); +// hdr = hdr + "\t " + informer.getAdditionalFileStringHeader(pop); + } + Iterator iter = additionals.iterator(); + if (!lastIsShowFull) while (iter.hasNext()) { + if (!isRequestedAdditionalField(iter.next())) iter.remove(); + } + return additionals; + } + + /** + * Take the output values and convert them to a concatenated String and a Double array. + * The array will have null entries whenever a field contained non-primitive numeric types (such + * as arrays or other non-numeric data). + * The string concatenation uses the textFieldDelimiter of the instance. + * + * @param informerList + * @param pop + * @return + */ + protected Pair getOutputData(List informerList, PopulationInterface pop) { + List statValues = getOutputValues(informerList, pop); + String statValuesString = StringTools.concatValues(statValues, textFieldDelimiter); + + return new Pair(statValuesString, statValues.toArray(new Object[statValues.size()])); + } + +// /** +// * Append additional informer informations to the given StringBuffer. +// * +// * @param informerList +// * @param pop +// * @param sbuf +// */ +// protected Double[] appendAdditionalInfo(List informerList, PopulationInterface pop, StringBuffer sbuf) { +// if (informerList != null) { +// ArrayList additionalObjects = new ArrayList(5); +// +// for (InterfaceAdditionalPopulationInformer informer : informerList) { +// additionalObjects.addAll(Arrays.asList(informer.getAdditionalFileStringValue(pop))); +// } +// String addInfo = StringTools.concatValues(additionalObjects, textFieldDelimiter); +// Double[] retVals = parseDoubles(additionalObjects); +// if (sbuf!=null) sbuf.append(addInfo); +// return retVals; +// +//// StringBuffer addBuffer = new StringBuffer(); +//// for (InterfaceAdditionalPopulationInformer informer : informerList) { +//// addBuffer.append(" \t "); +//// addBuffer.append(informer.getAdditionalFileStringValue(pop)); +//// } +//// String addInfo = addBuffer.toString().trim(); +//// if (addInfo.startsWith("\t")) addInfo.substring(2); // remove first separator to avoid returning empty field as double +//// Double[] retVals = parseDoubles(addInfo, "\t"); +//// if (sbuf!=null) sbuf.append(addInfo); +//// return retVals; +// } +// return null; +// } /** * @deprecated The method {@link #createNextGenerationPerformed(PopulationInterface, List)} should be used instead. @@ -545,24 +798,24 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter currentBestFit = bestfit; currentWorstFit = worstfit; currentBestFeasibleFit = null; - meanFitness = null; + currentMeanFit = null; if (firstPlot) { - initPlots(m_StatsParams.getPlotDescriptions()); + initPlots(null, null); // if (doTextOutput()) printToTextListener(getOutputHeader(null, null)+'\n'); firstPlot = false; } - if ((runIterCnt == 0) && printHeaderByVerbosity()) printToTextListener(getOutputHeader(null, null)+'\n'); + if ((iterationCounter == 0) && printHeaderByVerbosity()) printToTextListener(getOutputHeaderString(null, null)+'\n'); if (doTextOutput() && printLineByVerbosity(calls)) { - Pair addInfo = getOutputLine(null, null); + Pair addInfo = getOutputData(null, null); printToTextListener(addInfo.head()+'\n'); if (addInfo.tail()!=null) { - additionalInfoSums = updateAdditionalInfo(additionalInfoSums, addInfo.tail()); + statDataSumOverAll = updateSum(statDataSumOverAll, ToolBox.parseDoubles(addInfo.tail())); } } plotCurrentResults(); - runIterCnt++; + iterationCounter++; } /** @@ -572,7 +825,7 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter * * @param curInfo */ - private Double[] updateAdditionalInfo(Double[] resultSum, Double[] curInfo) { + private static Double[] updateSum(Double[] resultSum, Double[] curInfo) { if (resultSum==null) { resultSum = curInfo.clone(); } else { @@ -587,18 +840,7 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter } return resultSum; } - - /** - * Re-request the last additional information from the lastInfomerList and update the - * Double value sums. - * - * @param pop - */ - private void updateLastAdditionalInfo() { - Double[] lastVals = appendAdditionalInfo(lastInformerList, lastSols, null); - lastAdditionalInfoSums = updateAdditionalInfo(lastAdditionalInfoSums, lastVals); - } - + /** * If the population returns a specific data array, this method is called instead of doing standard output * @param pop @@ -611,41 +853,34 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter /** * Called at the very first (multirun mode) plot of a fitness curve. */ - protected abstract void initPlots(List description); + protected abstract void initPlots(PopulationInterface pop, List informerList); /** - * Do some data collection on the population. The informer parameter will not be handled by this method. - * + * To set a list of informers (even before the actual run is started). + * @param informerList */ - public synchronized void createNextGenerationPerformed(PopulationInterface - pop, InterfaceOptimizer opt, List informerList) { - lastInformerList = informerList; - if (firstPlot) { - initPlots(m_StatsParams.getPlotDescriptions()); -// if (doTextOutput()) printToTextListener(getOutputHeader(informer, pop)+'\n'); - firstPlot = false; - currentBestFeasibleFit=null; - } - if ((runIterCnt==0) && printHeaderByVerbosity()) printToTextListener(getOutputHeader(informerList, pop)+'\n'); - - if (pop.getSpecificData() != null) { - plotSpecificData(pop, informerList); - return; - } - // by default plotting only the best + public void setInitialInformerList(List informerList) { + lastInformerList = informerList; + } + + /** + * Collect statistical data for the given population, such as best individual, best fitness, + * population measures. + * This should be called exactly once per generation. + * + * @param pop + */ + private void collectPopData(PopulationInterface pop) { bestCurrentIndy = pop.getBestIndividual().getClone(); if ((bestIndyAllRuns == null) || (secondIsBetter(bestIndyAllRuns, bestCurrentIndy))) { bestIndyAllRuns = bestCurrentIndy; -// printToTextListener("new best found!, last was " + BeanInspector.toString(bestIndividualAllover) + "\n"); } if ((bestOfRunIndy==null) || (secondIsBetter(bestOfRunIndy, bestCurrentIndy))) { bestOfRunIndy=bestCurrentIndy; } -// IndividualInterface WorstInd = Pop.getWorstIndividual(); if (bestCurrentIndy == null) { System.err.println("createNextGenerationPerformed BestInd==null"); } - currentBestFit = bestCurrentIndy.getFitness().clone(); if (currentBestFit == null) { System.err.println("BestFitness==null !"); @@ -656,7 +891,7 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter if (currentBestFeasibleFit==null) { // feasible indy found for the first time numOfRunsFeasibleFound++; feasibleFoundAfter=((Population)pop).getFunctionCalls(); - if (feasibleFoundAfterSum<0) feasibleFoundAfterSum=0.; // initial signalling value was -1. + if (feasibleFoundAfterSum<0) feasibleFoundAfterSum=0.; // initial signaling value was -1. feasibleFoundAfterSum+=feasibleFoundAfter; } currentBestFeasibleFit = curBestFeasible.getFitness().clone(); @@ -670,52 +905,104 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter } } else System.err.println("INVALID POPULATION (AbstractStatistics)"); - meanFitness = pop.getMeanFitness().clone(); - currentWorstFit = pop.getWorstIndividual().getFitness().clone(); + // collect these data fields only if requested by the user + if (lastIsShowFull || GraphSelectionEnum.doPlotMean(lastFieldSelection)) currentMeanFit = pop.getMeanFitness().clone(); + else currentMeanFit = null; + if (lastIsShowFull || GraphSelectionEnum.doPlotWorst(lastFieldSelection)) currentWorstFit = pop.getWorstIndividual().getFitness().clone(); + else currentWorstFit = null; + functionCalls = pop.getFunctionCalls(); - if (GraphSelectionEnum.doPlotAvgDist(m_StatsParams.getGraphSelection()) - || GraphSelectionEnum.doPlotMaxPopDist(m_StatsParams.getGraphSelection())) { + + if (lastIsShowFull || GraphSelectionEnum.doPlotAvgDist(lastFieldSelection) + || GraphSelectionEnum.doPlotMaxPopDist(lastFieldSelection)) { double[] measures = ((Population)pop).getPopulationMeasures((InterfaceDistanceMetric)null); if (measures != null) { - avgPopDist = measures[0]; - maxPopDist = measures[2]; + currentAvgPopDist = measures[0]; + currentMaxPopDist = measures[2]; } } + } + + /** + * Do some data collection on the population. The informer parameter will not be handled by this method. + * + */ + public synchronized void createNextGenerationPerformed(PopulationInterface + pop, InterfaceOptimizer opt, List informerList) { + lastInformerList = informerList; + if (firstPlot) { + initPlots(pop, informerList); +// if (doTextOutput()) printToTextListener(getOutputHeader(informer, pop)+'\n'); + firstPlot = false; + currentBestFeasibleFit=null; + } + + if (pop.getSpecificData() != null) { // this is more or less deprecated. the standard population implementation will always return null. However the ES module wont + plotSpecificData(pop, informerList); + return; + } + + collectPopData(pop); + + if (iterationCounter==0) { + List headerFields=getOutputHeaderFieldNames(informerList, pop); + currentHeaderData = headerFields.toArray(new String[headerFields.size()]); + String headerLine = StringTools.concatFields(headerFields, textFieldDelimiter); + if (printHeaderByVerbosity()) printToTextListener(headerLine+'\n'); + } - if (meanCollection != null) { + lastSols = (opt!=null) ? new Population(opt.getAllSolutions().getSolutions()) : pop; +// Pair addData = getOutputData(informerList, lastSols); + Pair addData = getOutputData(informerList, lastSols); + if (doTextOutput()) { // this is where the text output is actually written + if (printLineByVerbosity(iterationCounter)) { +// printToTextListener(functionCalls + textFieldDelimiter); + printToTextListener(addData.head()+'\n'); + } + } + currentStatObjectData = addData.tail(); + currentStatDoubleData = ToolBox.parseDoubles(currentStatObjectData); + if (currentStatObjectData!=null) { + statDataSumOverAll = updateSum(statDataSumOverAll, currentStatDoubleData); // this adds up all data of a single run + } else { + System.err.println("Warning in AbstractStatistics!"); + } + + if (sumDataCollection != null) { // Collect average data - double[][] means = null; - if ((optRunsPerformed==0) && (meanCollection.size()<=runIterCnt)) { + Double[] sumDataEntry = null; + if ((optRunsPerformed==0) && (sumDataCollection.size()<=iterationCounter)) { // in the first run, newly allocate the arrays - means = new double[4][currentBestFit.length]; - meanCollection.add(means); + // assume that all later data sets will have the same format +// means = new double[4][currentBestFit.length]; // this only fits fitness vectors! of course this is sensible for multi-crit fitnesses... + sumDataEntry = currentStatDoubleData.clone(); + sumDataCollection.add(sumDataEntry); } else { - if (meanCollection.size()<=runIterCnt) {// bad case! + if (sumDataCollection.size()<=iterationCounter) {// bad case! // may happen for dynamic pop-sizes, e.g. in Tribes, when runs do not necessarily send the // "generation performed" event the same number of times. // thus: dont do an update for events that are "too late" - means = null; - } else means = meanCollection.get(runIterCnt); + sumDataEntry = null; + } else sumDataEntry = sumDataCollection.get(iterationCounter); + if (sumDataEntry != null) updateSum(sumDataEntry, currentStatDoubleData); // this adds up data of a single iteration across multiple runs } - if (means != null) updateMeans(means, functionCalls, currentBestFit, meanFitness, currentWorstFit); } -// meanCollection.set(pop.getGenerations()-1, means); - lastSols = (opt!=null) ? new Population(opt.getAllSolutions().getSolutions()) : pop; - if (doTextOutput()) { - Pair addInfo = getOutputLine(informerList, lastSols); - - if (printLineByVerbosity(runIterCnt)) { - printToTextListener(addInfo.head()+'\n'); - } -// updateAdditionalInfo(addInfo.tail()); - if (addInfo.tail()!=null) { - additionalInfoSums = updateAdditionalInfo(additionalInfoSums, addInfo.tail()); - } - } +// if (doTextOutput()) { +// Pair addInfo = getOutputLine(informerList, lastSols); +// +// if (printLineByVerbosity(runIterCnt)) { +// printToTextListener(addInfo.head()+'\n'); +// } +// currentAdditionalInfo = addInfo.tail(); +// if (addInfo.tail()!=null) { +// additionalInfoSums = updateAdditionalInfo(additionalInfoSums, addInfo.tail()); +// } +// } plotCurrentResults(); + fireDataListeners(); - runIterCnt++; + iterationCounter++; } /** @@ -758,22 +1045,12 @@ public abstract class AbstractStatistics implements InterfaceTextListener, Inter private boolean printHeaderByVerbosity() { return (m_StatsParams.getOutputVerbosity().getSelectedTagID() >= StatsParameter.VERBOSITY_KTH_IT); } - - private void updateMeans(double[][] means, double funCalls, double[] bestFit, double[] meanFit, double[] worstFit) { - means[0][0]+=funCalls; - addSecond(means[1], bestFit); - addSecond(means[2], meanFit); - addSecond(means[3], worstFit); - } - private static void divideMean(double[][] mean, double d) { - for (int i=0; i getPlotDescriptions(); +// public List getPlotDescriptions(); // public SelectedTag getPlotData(); // public void setPlotData(SelectedTag newMethod); - public StringSelection getGraphSelection(); - public void setGraphSelection(StringSelection v); + public StringSelection getFieldSelection(); + public void setFieldSelection(StringSelection v); public String getResultFilePrefix(); public void SetResultFilePrefix(String x); @@ -60,8 +45,8 @@ public interface InterfaceStatisticsParameter { public void SetShowTextOutput(boolean show); public boolean isShowTextOutput(); - public boolean isOutputAdditionalInfo(); - public void setOutputAdditionalInfo(boolean bShowAdd); + public boolean isOutputAllFieldsAsText(); + public void setOutputAllFieldsAsText(boolean bShowFullText); public void setOutputVerbosity(SelectedTag sTag); public SelectedTag getOutputVerbosity(); diff --git a/src/eva2/server/stat/InterfaceTextListener.java b/src/eva2/server/stat/InterfaceTextListener.java index a717e5cd..2d38d068 100644 --- a/src/eva2/server/stat/InterfaceTextListener.java +++ b/src/eva2/server/stat/InterfaceTextListener.java @@ -1,5 +1,11 @@ package eva2.server.stat; +/** + * A very simple interface class to receive raw String data. + * + * @author mkron + * + */ public interface InterfaceTextListener { public void print(String str); public void println(String str); diff --git a/src/eva2/server/stat/StatisticsStandalone.java b/src/eva2/server/stat/StatisticsStandalone.java index 3a542d2a..e34e167f 100644 --- a/src/eva2/server/stat/StatisticsStandalone.java +++ b/src/eva2/server/stat/StatisticsStandalone.java @@ -13,9 +13,6 @@ package eva2.server.stat; /*==========================================================================* * IMPORTS *==========================================================================*/ -import java.io.File; -import java.io.FileOutputStream; -import java.io.PrintWriter; import java.io.Serializable; import java.net.InetAddress; import java.util.ArrayList; @@ -23,31 +20,32 @@ import java.util.List; import eva2.server.go.PopulationInterface; import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; -import eva2.tools.math.Mathematics; - -/* - * ==========================================================================* - * CLASS DECLARATION - * ========================================================================== - */ /** - * + * This simple statistics implementation can collect all Object data available during runs. + * Be aware that the memory requirements can be excessive depending on the data produced by + * the additional informers, and depending on the selected fields to be collected. + * Therefore, the default is not to log the data but just print it using the super class. + * + * @see InterfaceAdditionalPopulationInformer + * @see AbstractStatistics */ public class StatisticsStandalone extends AbstractStatistics implements InterfaceStatistics, Serializable { - private static final long serialVersionUID = 2621394534751748968L; + private static final long serialVersionUID = -8451652609212653368L; private static String m_MyHostName = "unknown"; - private String m_InfoString; - private ArrayList[]> m_Result; - private ArrayList m_ResultString; +// private String m_InfoString; + private ArrayList> m_ResultData = null; + private ArrayList m_ResultHeaderStrings = null; + private boolean collectData = false; + // private boolean m_useMedian = false; - private double m_FitnessMeanofALL; - private double m_SumOfBestFit = 0; - private double[] m_BestFitnessAtEnd; - private double m_FitnessMedianofALL; +// private double m_MeanFinalFitness; +// private double m_SumLastBestCurrentFit = 0; +// private double[] m_LastBestCurrentFit; +// private double m_FitnessMedianofALL; public StatisticsStandalone(InterfaceStatisticsParameter statParams) { super(); @@ -75,14 +73,25 @@ public class StatisticsStandalone extends AbstractStatistics implements Interfac public StatisticsStandalone() { this(new StatsParameter()); } - - protected void initPlots(List description) { - if (m_Result.size() == 0) - initContainer(new String[] {"Fitness"}); + + protected void initPlots(PopulationInterface pop, List informerList) { + if (collectData) { + m_ResultData = new ArrayList>(m_StatsParams.getMultiRuns()); + List description = getOutputHeaderFieldNames(informerList, pop); + m_ResultHeaderStrings = new ArrayList(); + for (String str : description) m_ResultHeaderStrings.add(str); + for (int i = 0; i < m_StatsParams.getMultiRuns(); i++) + m_ResultData.add(new ArrayList()); + } else { + m_ResultData=null; + m_ResultHeaderStrings = null; + } } protected void plotCurrentResults() { - ((ArrayList[]) m_Result.get(0))[optRunsPerformed].add(new double[] {functionCalls, currentBestFit[0]}); + if (collectData && (m_ResultData!=null)) { + m_ResultData.get(optRunsPerformed).add(currentStatObjectData); + } } public void plotSpecificData(PopulationInterface pop, List informerList) { @@ -90,110 +99,103 @@ public class StatisticsStandalone extends AbstractStatistics implements Interfac double[] specificData = pop.getSpecificData(); if (specificData != null) { for (int i = 0; i < specificData.length; i++) { - ((ArrayList[]) m_Result.get(i))[optRunsPerformed].add(new double[] {functionCalls, specificData[i]}); +// ((ArrayList[]) m_Result.get(i))[optRunsPerformed].add(new Double[] {new Double(functionCalls), specificData[i]}); + m_ResultData.get(optRunsPerformed).add(new Object[] {new Double(functionCalls), specificData}); } } } - private void initContainer(String[] description) { - for (int i = 0; i < description.length; i++) { - m_Result.add(new ArrayList[m_StatsParams.getMultiRuns()]); - m_ResultString.add(description[i]); - } - for (int i = 0; i < m_Result.size(); i++) - ((ArrayList[]) m_Result.get(i))[optRunsPerformed] = new ArrayList(); - } - - public void startOptPerformed(String infoString, int runNumber, Object params) { - super.startOptPerformed(infoString, runNumber, params); - if (runNumber == 0) { - m_Result = new ArrayList[]>(); - m_ResultString = new ArrayList(); - m_BestFitnessAtEnd = new double[this.m_StatsParams.getMultiRuns()]; - } else { - for (int i = 0; i < m_Result.size(); i++) - ((ArrayList[]) m_Result.get(i))[optRunsPerformed] = new ArrayList[]>(); - } - m_InfoString = infoString; - } +// public void startOptPerformed(String infoString, int runNumber, Object params) { +// super.startOptPerformed(infoString, runNumber, params); +// if (runNumber == 0) { +// m_Result = new ArrayList>(); +// m_ResultString = new ArrayList(); +//// m_LastBestCurrentFit = new double[this.m_StatsParams.getMultiRuns()]; +//// m_SumLastBestCurrentFit=0; +// } else { +// for (int i = 0; i < m_Result.size(); i++) +// ((ArrayList[]) m_Result.get(i))[optRunsPerformed] = new ArrayList[]>(); +// } +// m_InfoString = infoString; +// } - public void stopOptPerformed(boolean normal, String stopMessage) { - super.stopOptPerformed(normal, stopMessage); - if (bestCurrentIndy != null) { - m_SumOfBestFit = m_SumOfBestFit + bestCurrentIndy.getFitness()[0]; - m_BestFitnessAtEnd[optRunsPerformed-1] = bestCurrentIndy.getFitness()[0]; - } +// public void stopOptPerformed(boolean normal, String stopMessage) { +// super.stopOptPerformed(normal, stopMessage); +// if (bestCurrentIndy != null) { +// m_SumLastBestCurrentFit = m_SumLastBestCurrentFit + bestCurrentIndy.getFitness()[0]; +// m_LastBestCurrentFit[optRunsPerformed-1] = bestCurrentIndy.getFitness()[0]; +// } +// +// //System.out.println("stopOptPerformed :"+m_OptRunsPerformed); +// if (optRunsPerformed == m_StatsParams.getMultiRuns()) { +// m_MeanFinalFitness = m_SumLastBestCurrentFit / ((double) optRunsPerformed); +// //System.out.println("m_FitnessMeanofALL "+m_FitnessMeanofALL); +// m_FitnessMedianofALL = Mathematics.median(m_LastBestCurrentFit, true); +// +//// finalizeOutput(); +// } +// } - //System.out.println("stopOptPerformed :"+m_OptRunsPerformed); - if (optRunsPerformed == m_StatsParams.getMultiRuns()) { - m_FitnessMeanofALL = m_SumOfBestFit / ((double) optRunsPerformed); - //System.out.println("m_FitnessMeanofALL "+m_FitnessMeanofALL); - m_FitnessMedianofALL = Mathematics.median(m_BestFitnessAtEnd, true); - -// finalizeOutput(); - } - } - - /** - * write result of all runs. - */ - public static File[] writeResult_All_Container(ArrayList StatList, String FileName) { - File ret[] = new File[((StatisticsStandalone) StatList.get(0)).m_Result.size()]; - //System.out.println("StatList.size " + StatList.size()); - for (int counter = 0; counter < ret.length; counter++) { - ArrayList staticResult = new ArrayList(); - String staticDescription = "calls "; - for (int index = 0; index < StatList.size(); index++) { - StatisticsStandalone Stat = (StatisticsStandalone) StatList.get(index); - staticDescription = staticDescription + Stat.m_InfoString + " "; - for (int row = 0; row < ((ArrayList[]) Stat.m_Result.get(counter))[0].size(); - row++) { - double mean = 0; - double calls = 0; - double[] value = new double[((ArrayList[]) Stat.m_Result.get(counter)). - length]; - for (int i = 0; i < ((ArrayList[]) Stat.m_Result.get(counter)).length; i++) { - //double[] result = (double[]) Stat.m_QRestultContainer[i].get(row); - double[] result = (double[]) ((ArrayList[]) Stat.m_Result.get(counter))[i].get(row); - mean = mean + result[1]; - calls = result[0]; - value[i] = result[1]; - } - //mean = mean / Stat.m_QRestultContainer.length; - mean = mean / ((ArrayList[]) Stat.m_Result.get(counter)).length; -// if (m_useMedian == true) // use the median -// mean = getMedian(value); - if (row == staticResult.size()) - staticResult.add(new String("" + calls)); - String temp = (String) staticResult.get(row); - String newrow = new String(temp + " " + mean); - //System.out.println("newrow"+newrow); - staticResult.set(row, newrow); - } // end of for row - } // end of for index - try { - File d = new File(m_MyHostName); - d.mkdir(); - String info = (String) ((StatisticsStandalone) StatList.get(0)).m_ResultString.get(counter); - ret[counter] = new File(m_MyHostName + "//" + FileName + "_" + info + "_" + counter + ".txt"); - ret[counter].createNewFile(); - PrintWriter Out = new PrintWriter(new FileOutputStream(ret[counter])); - Out.println(staticDescription); - for (int i = 0; i < staticResult.size(); i++) { - Out.println((String) staticResult.get(i)); - //System.out.println("print " + (String) staticResult.get(i)); - } - Out.flush(); - Out.close(); - } catch (Exception e) { - System.out.println("Error in wreiteresult" + e.getMessage()); - e.printStackTrace(); - } - staticResult = null; - staticDescription = ""; - } - return ret; - } +// /** +// * write result of all runs. +// */ +// public static File[] writeResult_All_Container(ArrayList StatList, String FileName) { +// File ret[] = new File[((StatisticsStandalone) StatList.get(0)).m_Result.size()]; +// //System.out.println("StatList.size " + StatList.size()); +// for (int counter = 0; counter < ret.length; counter++) { +// ArrayList staticResult = new ArrayList(); +// String staticDescription = "calls "; +// for (int index = 0; index < StatList.size(); index++) { +// StatisticsStandalone Stat = (StatisticsStandalone) StatList.get(index); +// staticDescription = staticDescription + Stat.m_InfoString + " "; +// for (int row = 0; row < ((ArrayList[]) Stat.m_Result.get(counter))[0].size(); +// row++) { +// double mean = 0; +// double calls = 0; +// double[] value = new double[((ArrayList[]) Stat.m_Result.get(counter)). +// length]; +// for (int i = 0; i < ((ArrayList[]) Stat.m_Result.get(counter)).length; i++) { +// //double[] result = (double[]) Stat.m_QRestultContainer[i].get(row); +// double[] result = (double[]) ((ArrayList[]) Stat.m_Result.get(counter))[i].get(row); +// mean = mean + result[1]; +// calls = result[0]; +// value[i] = result[1]; +// } +// //mean = mean / Stat.m_QRestultContainer.length; +// mean = mean / ((ArrayList[]) Stat.m_Result.get(counter)).length; +//// if (m_useMedian == true) // use the median +//// mean = getMedian(value); +// if (row == staticResult.size()) +// staticResult.add(new String("" + calls)); +// String temp = (String) staticResult.get(row); +// String newrow = new String(temp + " " + mean); +// //System.out.println("newrow"+newrow); +// staticResult.set(row, newrow); +// } // end of for row +// } // end of for index +// try { +// File d = new File(m_MyHostName); +// d.mkdir(); +// String info = (String) ((StatisticsStandalone) StatList.get(0)).m_ResultString.get(counter); +// ret[counter] = new File(m_MyHostName + "//" + FileName + "_" + info + "_" + counter + ".txt"); +// ret[counter].createNewFile(); +// PrintWriter Out = new PrintWriter(new FileOutputStream(ret[counter])); +// Out.println(staticDescription); +// for (int i = 0; i < staticResult.size(); i++) { +// Out.println((String) staticResult.get(i)); +// //System.out.println("print " + (String) staticResult.get(i)); +// } +// Out.flush(); +// Out.close(); +// } catch (Exception e) { +// System.out.println("Error in wreiteresult" + e.getMessage()); +// e.printStackTrace(); +// } +// staticResult = null; +// staticDescription = ""; +// } +// return ret; +// } // /** // * write result of all runs. @@ -243,21 +245,52 @@ public class StatisticsStandalone extends AbstractStatistics implements Interfac // return ret; // } - /** - * - */ - public double getBestFitnessMeanofALL() { - return m_FitnessMeanofALL; - } - - /** - * - */ - public double getBestFitnessMedianofALL() { - return m_FitnessMedianofALL; - } - +// /** +// * +// */ +// public double getBestFitnessMeanofALL() { +// return m_MeanFinalFitness; +// } +// +// /** +// * +// */ +// public double getBestFitnessMedianofALL() { +// return m_FitnessMedianofALL; +// } + public String getHostName() { return m_MyHostName; } + + /** + * Check whether data collection is activated, which stores an Object[] for every iteration and + * every multi-run. + * @return + */ + public boolean isCollectData() { + return collectData; + } + + /** + * Set state of full data collection, which stores an Object[] for every iteration and + * every multi-run. + * + * @param collectFullData + */ + public void setCollectData(boolean collectFullData) { + this.collectData = collectFullData; + } + + public ArrayList> getCollectedData() { + return m_ResultData; + } + + public ArrayList getCollectedRunData(int runIndex) { + return m_ResultData.get(runIndex); + } + + public ArrayList getCollectedDataHeaders() { + return m_ResultHeaderStrings; + } } \ No newline at end of file diff --git a/src/eva2/server/stat/StatisticsWithGUI.java b/src/eva2/server/stat/StatisticsWithGUI.java index 9417bfbc..c97e03a6 100644 --- a/src/eva2/server/stat/StatisticsWithGUI.java +++ b/src/eva2/server/stat/StatisticsWithGUI.java @@ -29,21 +29,17 @@ import eva2.server.EvAServer; import eva2.server.go.PopulationInterface; import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; import eva2.tools.EVAERROR; -import eva2.tools.StringSelection; +import eva2.tools.Pair; import eva2.tools.jproxy.MainAdapterClient; import eva2.tools.jproxy.RMIProxyLocal; import eva2.tools.jproxy.RMIProxyRemote; -/*==========================================================================* - * CLASS DECLARATION - *==========================================================================*/ /** - * A statistics class to plot fitness curves in client-server mode. + * A statistics class to plot fitness curves in client-server mode. Mainly, arrays of GraphWindows + * and Graphs are managed and the selected data fields are plotted. + * */ public class StatisticsWithGUI extends AbstractStatistics implements Serializable, InterfaceStatistics { - /** - * - */ private static final long serialVersionUID = 3213603978877954103L; // Plot frames: private GraphWindow[] m_FitnessFrame; // frame for the fitness plots @@ -55,7 +51,7 @@ public class StatisticsWithGUI extends AbstractStatistics implements Serializabl private MainAdapterClient m_MainAdapterClient; // the connection to the client MainAdapter private JTextoutputFrameInterface m_ProxyPrinter; - private StringSelection lastGraphSelection; + private transient List> graphDesc=null; // list of descriptor strings and optional indices. strictly its redundant since super.lastGraphSelection is always available. However it spares some time. ////////////// protected static String m_MyHostName = null; @@ -105,12 +101,9 @@ public class StatisticsWithGUI extends AbstractStatistics implements Serializabl public synchronized void startOptPerformed(String infoString, int runNumber, Object goParams) { super.startOptPerformed(infoString, runNumber, goParams); m_GraphInfoString = infoString; - if (runNumber == 0) { - lastGraphSelection = (StringSelection)m_StatsParams.getGraphSelection().clone(); - } // m_TextCounter = m_StatisticsParameter.GetTextoutput(); - m_PlotCounter = m_StatsParams.GetPlotoutput(); +// m_PlotCounter = m_StatsParams.GetPlotoutput(); if ((m_FitnessFrame!=null) && (m_FitnessFrame[0]!=null)) { PlotInterface p = m_FitnessFrame[0].getPlotter(); if ((p!=null) && p.isValid()) ((Plot)p).getFunctionArea().clearLegend(); @@ -135,8 +128,8 @@ public class StatisticsWithGUI extends AbstractStatistics implements Serializabl for (int j = 0; j < m_FitnessGraph[i].length; j++) { m_StatGraph[i][j].setInfoString( (m_FitnessGraph[i][j].getInfo().length() > 0 ? (m_FitnessGraph[i][j].getInfo() + "_") : "" ) - + (m_StatsParams.GetInfoString().length() > 0 ? (m_StatsParams.GetInfoString() + "_") : "" ) - + m_StatsParams.GetInfoString() +// + (m_StatsParams.GetInfoString().length() > 0 ? (m_StatsParams.GetInfoString() + "_") : "" ) +// + m_StatsParams.GetInfoString() + "Mean_of_" + fullRuns + " ", (float) 2.0); if (normal && m_FitnessFrame[i].isValid()) { @@ -159,42 +152,55 @@ public class StatisticsWithGUI extends AbstractStatistics implements Serializabl if (m_ProxyPrinter != null) m_ProxyPrinter.setShow(m_StatsParams.isShowTextOutput()); } - protected void initPlots(List description) { + protected void initPlots(PopulationInterface pop, List informerList) { if (TRACE) System.out.println("initPlots"); - + if (m_StatsParams instanceof StatsParameter) { +// StringSelection ss = ((StatsParameter)m_StatsParams).getGraphSelection(); + graphDesc = lastFieldSelection.getSelectedWithIndex(); +// for (int i=0; i 1 && - m_StatsParams.GetuseStatPlot() == true) { - String Info = m_StatsParams.GetInfoString(); - m_StatGraph = new Graph[graphCount][]; + m_StatsParams.GetUseStatPlot() == true) { +// String Info = m_StatsParams.GetInfoString(); + m_StatGraph = new Graph[windowCount][]; for (int i = 0; i < m_StatGraph.length; i++) { - m_StatGraph[i] = new Graph[((String[]) description.get(i)).length]; + m_StatGraph[i] = new Graph[graphCount]; for (int j = 0; j < m_StatGraph[i].length; j++) { - String[] d = (String[]) description.get(i); - m_StatGraph[i][j] = m_FitnessFrame[i].getNewGraph(d[j] + "_" + Info + +// String[] d = (String[]) description.get(i); + m_StatGraph[i][j] = m_FitnessFrame[i].getNewGraph(graphDesc.get(j).head + "_" + //Info + m_GraphInfoString); } } @@ -220,44 +226,29 @@ public class StatisticsWithGUI extends AbstractStatistics implements Serializabl if (isValidGraph) m_FitnessGraph[graph][subGraph].setConnectedPoint(x, y); } - protected void plotCurrentResults() { - // Plots - m_PlotCounter--; - -// int fitnessplot_setting = m_StatsParams.getPlotData().getSelectedTag().getID(); - - if (m_PlotCounter == 0) { - m_PlotCounter = m_StatsParams.GetPlotoutput(); -// boolean doPlotBest = (fitnessplot_setting == StatsParameter.PLOT_BEST) -// || (fitnessplot_setting == StatsParameter.PLOT_BEST_AND_WORST) -// || (fitnessplot_setting == StatsParameter.PLOT_CURBEST_AND_RUNBEST) -// || (fitnessplot_setting == StatsParameter.PLOT_BEST_AND_MEASURES); -// boolean doPlotWorst = (fitnessplot_setting == StatsParameter.PLOT_WORST) -// || (fitnessplot_setting == StatsParameter.PLOT_BEST_AND_WORST); -// boolean doPlotMeasures = (fitnessplot_setting == StatsParameter.PLOT_BEST_AND_MEASURES); - - boolean doPlotCurrentBest = GraphSelectionEnum.doPlotCurrentBest(lastGraphSelection); - boolean doPlotRunBest = GraphSelectionEnum.doPlotRunBest(lastGraphSelection); - boolean doPlotWorst= GraphSelectionEnum.doPlotWorst(lastGraphSelection); - boolean doPlotCurBestFeasible = GraphSelectionEnum.doPlotCurrentBestFeasible(lastGraphSelection); - boolean doPlotRunBestFeasible = GraphSelectionEnum.doPlotRunBestFeasible(lastGraphSelection); - boolean doPlotAvgDist= GraphSelectionEnum.doPlotAvgDist(lastGraphSelection); - boolean doPlotMaxPopDist= GraphSelectionEnum.doPlotMaxPopDist(lastGraphSelection); - - int subGraph=0; - if (doPlotCurrentBest) plotFitnessPoint(0, subGraph++, functionCalls, currentBestFit[0]); - if (doPlotRunBest) plotFitnessPoint(0, subGraph++, functionCalls, bestOfRunIndy.getFitness()[0]); - if (doPlotWorst) plotFitnessPoint(0, subGraph++ , functionCalls, currentWorstFit[0]); - if (doPlotAvgDist) plotFitnessPoint(0, subGraph++, functionCalls, avgPopDist); - if (doPlotMaxPopDist) plotFitnessPoint(0, subGraph++, functionCalls, maxPopDist); - if (doPlotCurBestFeasible && currentBestFeasibleFit!=null) plotFitnessPoint(0, subGraph++, functionCalls, currentBestFeasibleFit[0]); - if (doPlotRunBestFeasible && bestOfRunFeasibleIndy!=null) plotFitnessPoint(0, subGraph++, functionCalls, bestOfRunFeasibleIndy.getFitness()[0]); - - } - } - /** - * + * Plots the selected data to the fitness graphs. + */ + protected void plotCurrentResults() { +// m_PlotCounter--; + +// if (m_PlotCounter == 0) { +// m_PlotCounter = m_StatsParams.GetPlotoutput(); + int subGraph=0; +// boolean doPlotAdditionalInfo = m_StatsParams.isOutputAdditionalInfo(); + for (int i=0; i informer) { double[] specificData = pop.getSpecificData(); @@ -291,9 +282,9 @@ public class StatisticsWithGUI extends AbstractStatistics implements Serializabl printToTextListener(s + "\n"); } - m_PlotCounter--; - if (m_PlotCounter == 0) { - m_PlotCounter = m_StatsParams.GetPlotoutput(); +// m_PlotCounter--; +// if (m_PlotCounter == 0) { +// m_PlotCounter = m_StatsParams.GetPlotoutput(); int index = 0; for (int i = 0; i < m_FitnessGraph.length; i++) { for (int j = 0; j < m_FitnessGraph[i].length; j++) { @@ -301,8 +292,7 @@ public class StatisticsWithGUI extends AbstractStatistics implements Serializabl index++; } } - - } +// } } public String getHostName() { diff --git a/src/eva2/server/stat/StatsParameter.java b/src/eva2/server/stat/StatsParameter.java index f4c326a7..3aed16f3 100644 --- a/src/eva2/server/stat/StatsParameter.java +++ b/src/eva2/server/stat/StatsParameter.java @@ -1,40 +1,33 @@ package eva2.server.stat; -/* - * Title: EvA2 - * Description: - * Copyright: Copyright (c) 2003 - * Company: University of Tuebingen, Computer Architecture - * @author Holger Ulmer, Felix Streichert, Hannes Planatscher - * @version: $Revision: 284 $ - * $Date: 2007-11-27 14:37:05 +0100 (Tue, 27 Nov 2007) $ - * $Author: mkron $ - */ -/*==========================================================================* - * IMPORTS - *==========================================================================*/ import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import eva2.gui.BeanInspector; import eva2.gui.GenericObjectEditor; +import eva2.server.go.InterfaceNotifyOnInformers; +import eva2.server.go.problems.InterfaceAdditionalPopulationInformer; import eva2.tools.SelectedTag; import eva2.tools.Serializer; import eva2.tools.StringSelection; -public class StatsParameter implements InterfaceStatisticsParameter, Serializable { +/** + * A set of parameters for statistics in EvA2. Several data entries are provided by the AbstractStatistics class, + * others by the additional informers. This class allows customization of entries and frequency of data output. + * Data entries can be selected using a StringSelection instance. + * There is a switch called "output full data as text" which will be interpreted by AbstractStatistics showing + * all or only the selected entities. + * + * @see AbstractStatistics + * @author mkron + */ + +public class StatsParameter implements InterfaceStatisticsParameter, InterfaceNotifyOnInformers, Serializable { + private static final long serialVersionUID = -8681061379203108390L; + private static boolean TRACE = false; -// public final static int PLOT_BEST = 0; -// public final static int PLOT_WORST = 1; -// public final static int PLOT_BEST_AND_WORST = 2; -// public final static int PLOT_BEST_AND_MEASURES = 3; -// public final static int PLOT_CURBEST_AND_RUNBEST = 4; -// public final static Tag[] TAGS_PLOT_FITNESS = { -// new Tag(PLOT_BEST, "plot best fitness"), -// new Tag(PLOT_WORST, "plot worst fitness"), -// new Tag(PLOT_BEST_AND_WORST, "both best and worst"), -// new Tag(PLOT_BEST_AND_MEASURES, "both best and population measures"), -// new Tag(PLOT_CURBEST_AND_RUNBEST, "current best and best of run") -// }; public final static int VERBOSITY_NONE = 0; public final static int VERBOSITY_FINAL = 1; @@ -50,11 +43,11 @@ public class StatsParameter implements InterfaceStatisticsParameter, Serializabl // private int m_PlotFitness = PLOT_BEST; private int m_Textoutput = 0; - private int m_Plotoutput = 1; +// private int m_Plotoutput = 1; private int m_MultiRuns = 1; private String m_ResultFilePrefix = "EvA2"; protected String m_Name = "not defined"; - protected String m_InfoString = ""; +// protected String m_InfoString = ""; private boolean m_useStatPlot = true; private boolean showAdditionalProblemInfo = false; private double m_ConvergenceRateThreshold=0.001; @@ -95,47 +88,30 @@ public class StatsParameter implements InterfaceStatisticsParameter, Serializabl * */ public String toString() { - String ret = "\r\nStatParameter:\r\nm_MultiRuns=" + m_MultiRuns + - "\nm_Textoutput=" + m_Textoutput + - "\nm_Plotoutput=" + m_Plotoutput + - "\nverbosity= " + outputVerbosity.getSelectedString() + - "\n" + outputTo.getSelectedString() + - "\n"; + String ret = "\r\nStatParameter (" + super.toString() + "):\r\nm_MultiRuns=" + m_MultiRuns + + ", m_Textoutput=" + m_Textoutput + +// ", m_Plotoutput=" + m_Plotoutput + + ", verbosity= " + outputVerbosity.getSelectedString() + + "\nTo " + outputTo.getSelectedString() + + ", " + BeanInspector.toString(graphSel.getStrings()); return ret; } - /** - * Return a list of String arrays describing the selected plot options, e.g. {"Best"} or {"Best", "Worst"}. - * For now, only one array is returned. - * - * @return a list of String arrays describing the selected plot options - */ - public ArrayList getPlotDescriptions() { - ArrayList desc = new ArrayList(); - for (int i=0; i alist = new ArrayList(); - alist.add(desc.toArray(new String[desc.size()])); - return alist; -// switch (getPlotData().getSelectedTagID()) { -// case StatsParameter.PLOT_BEST_AND_WORST: -// desc.add(new String[] {"Best", "Worst"}); -// break; -// case StatsParameter.PLOT_BEST: -// desc.add(new String[] {"Best"}); -// break; -// case StatsParameter.PLOT_WORST: -// desc.add(new String[] {"Worst"}); -// break; -// case StatsParameter.PLOT_BEST_AND_MEASURES: -// desc.add(new String[] {"Best", "AvgDist", "MaxDist"}); -// break; -// case StatsParameter.PLOT_CURBEST_AND_RUNBEST: -// desc.add(new String[] {"Cur.Best", "Run Best"}); -// break; } -// return desc; - } +// /** +// * Return a list of String arrays describing the selected plot options, e.g. {"Best"} or {"Best", "Worst"}. +// * For now, only one array is returned. +// * +// * @return a list of String arrays describing the selected plot options +// */ +// public ArrayList getPlotDescriptions() { +// ArrayList desc = new ArrayList(); +// for (int i=0; i alist = new ArrayList(); +// alist.add(desc.toArray(new String[desc.size()])); +// return alist; +// } /** * @@ -151,7 +127,7 @@ public class StatsParameter implements InterfaceStatisticsParameter, Serializabl m_ConvergenceRateThreshold = Source.m_ConvergenceRateThreshold; m_useStatPlot = Source.m_useStatPlot; m_Textoutput = Source.m_Textoutput; - m_Plotoutput = Source.m_Plotoutput; +// m_Plotoutput = Source.m_Plotoutput; // m_PlotFitness = Source.m_PlotFitness; m_MultiRuns = Source.m_MultiRuns; m_ResultFilePrefix = Source.m_ResultFilePrefix; @@ -173,21 +149,7 @@ public class StatsParameter implements InterfaceStatisticsParameter, Serializabl } public static String globalInfo() { - return "Configure statistics and output of the optimization run."; - } - - /** - * - */ - public void setPlotoutput(int i) { - m_Plotoutput = i; - } - - /** - * - */ - public int GetPlotoutput() { - return m_Plotoutput; + return "Configure statistics and output of the optimization run. Changes to the data selection state will not take effect during a run."; } // /** @@ -225,19 +187,19 @@ public class StatsParameter implements InterfaceStatisticsParameter, Serializabl return "Number of independent optimization runs to evaluate."; } - /** - * - */ - public String GetInfoString() { - return m_InfoString; - } - - /** - * - */ - public void setInfoString(String s) { - m_InfoString = s; - } +// /** +// * +// */ +// public String GetInfoString() { +// return m_InfoString; +// } +// +// /** +// * +// */ +// public void setInfoString(String s) { +// m_InfoString = s; +// } /** * @@ -247,24 +209,21 @@ public class StatsParameter implements InterfaceStatisticsParameter, Serializabl } /** - * + * Use averaged graph for multi-run plots or not */ - public boolean GetuseStatPlot() { + public boolean GetUseStatPlot() { return m_useStatPlot; } /** - * + * Activate or deactivate averaged graph for multi-run plots */ - public void setuseStatPlot(boolean x) { + public void setUseStatPlot(boolean x) { m_useStatPlot = x; } - /** - * - */ public String useStatPlotTipText() { - return "Plotting each fitness graph separate if multiruns > 1."; + return "Plotting each fitness graph separately if multiruns > 1."; } /** @@ -338,16 +297,16 @@ public class StatsParameter implements InterfaceStatisticsParameter, Serializabl // return "Indicates whether further text output should be printed"; // } - public boolean isOutputAdditionalInfo() { + public boolean isOutputAllFieldsAsText() { return showAdditionalProblemInfo; } - public void setOutputAdditionalInfo(boolean showAdd) { - showAdditionalProblemInfo = showAdd; + public void setOutputAllFieldsAsText(boolean bShowFullText) { + showAdditionalProblemInfo = bShowFullText; } - public String outputAdditionalInfoTipText() { - return "Activate to output additional problem information per iteration, such as the current solution representation."; + public String outputAllFieldsAsTextTipText() { + return "Output all available data fields or only the selected entries as text."; } public void hideHideable() { @@ -400,15 +359,40 @@ public class StatsParameter implements InterfaceStatisticsParameter, Serializabl return "Set the output destination; to deactivate output, set verbosity to none."; } - public StringSelection getGraphSelection() { + public StringSelection getFieldSelection() { return graphSel; } - public void setGraphSelection(StringSelection v) { + public void setFieldSelection(StringSelection v) { graphSel = v; } - public String graphSelectionTipText() { - return "Select the graphs to be plotted."; + public String fieldSelectionTipText() { + return "Select the data fields to be collected and plotted. Note that only simple types can be plotted to the GUI."; + } + + /** + * May be called to dynamically alter the set of graphs that can be selected, + * using a list of informer instances, which usually are the problem and the + * optimizer instance. + * + * @see InterfaceAdditionalPopulationInformer + */ + public void setInformers( + List informers) { + ArrayList headerFields = new ArrayList(); + // parse list of header elements, show additional Strings according to names. + for (InterfaceAdditionalPopulationInformer inf : informers) { + headerFields.addAll(Arrays.asList(inf.getAdditionalFileStringHeader(null))); +// header += inf.getAdditionalFileStringHeader(null); // lets hope this works with a null + } + // create additional fields to be selectable by the user, defined by the informer headers +// StringSelection ss = new StringSelection(GraphSelectionEnum.getAndAppendArray(headerFields.toArray(new String[headerFields.size()]))); + StringSelection ss = new StringSelection(GraphSelectionEnum.currentBest, headerFields); + ss.takeOverSelection(graphSel); +// System.out.println("In " + this + ": setting new informers: " + BeanInspector.toString(ss.getStrings())); + // This works! + setFieldSelection(ss); +// System.out.println("After: " + this); } } \ No newline at end of file diff --git a/src/eva2/tools/StringSelection.java b/src/eva2/tools/StringSelection.java index e2c8b4c6..6e545e5f 100644 --- a/src/eva2/tools/StringSelection.java +++ b/src/eva2/tools/StringSelection.java @@ -1,6 +1,13 @@ package eva2.tools; import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import eva2.gui.BeanInspector; /** * An array of Strings that can be selected and deselected. May be created directly from an Enum. @@ -9,17 +16,23 @@ import java.io.Serializable; * */ public class StringSelection implements Serializable { + private static final long serialVersionUID = -1512329288445831907L; private String[] strObjects; boolean[] selStates; + private transient HashMap stringToIndexHash = null; + private transient Class enumClass = null; public StringSelection(String[] sArr) { strObjects = sArr; selStates=new boolean[sArr.length]; + stringToIndexHash = null; + enumClass = null; } public StringSelection(String[] sArr, int initialSel) { this(sArr); if (initialSel e) { @@ -29,11 +42,28 @@ public class StringSelection implements Serializable { strObjects[i] = e.getClass().getEnumConstants()[i].toString(); } setSelected(e.ordinal(), true); + stringToIndexHash = null; + enumClass = e.getClass(); } public StringSelection(StringSelection stringSelection) { strObjects = stringSelection.strObjects.clone(); selStates = stringSelection.selStates.clone(); + stringToIndexHash = null; + enumClass = stringSelection.enumClass; + } + + /** + * Construct a string selection that allows all enum fields of the given type plus a list of additional + * strings to be selected. The enum fields will be first in the selection list. + * + * @param e + * @param headerFields + */ + public StringSelection(Enum e, + List headerFields) { + this(ToolBox.appendEnumAndArray(e, headerFields.toArray(new String[headerFields.size()]))); + enumClass = e.getClass(); } public Object clone() { @@ -58,15 +88,51 @@ public class StringSelection implements Serializable { /** * Returns true if the given enum is selected (as its string representation) - * within the instance. + * within the instance. This only works if the enum was used for the + * creation of this instance. * * @param e * @return */ public boolean isSelected(Enum e) { - if (isSelected(e.ordinal())) { - return e.toString().equals(strObjects[e.ordinal()]); - } else return false; +// if (isSelected(e.ordinal())) { +// return e.toString().equals(strObjects[e.ordinal()]); +// } else return false; + if (enumClass!=null) { + if (e.getClass().equals(enumClass)) return isSelected(e.ordinal()); + else { + System.err.println("Error, the string selection was constructed with a different enum class - invalid request (StringSelection.isSelected(Enum)"); + return false; + } + } else { + System.err.println("Error, the string selection was constructed without an enum class - invalid request (StringSelection.isSelected(Enum)"); + return false; + } + } + + /** + * Check if a given string is selected within this instance. If the + * String is not found, false is returned. + * + * @param str + * @return + */ + public boolean isSelected(String str) { + if (stringToIndexHash == null) { // for some time efficiency... + stringToIndexHash = new HashMap(2*strObjects.length); + for (int i=0; i ret = new ArrayList(); + for (int i=0; i> getSelectedWithIndex() { + ArrayList> ret = new ArrayList>(); + for (int i=0; i(getElement(i), i)); + return ret; + } + + /** + * Return only those selected fields which are members of the given enum. + * @param e + * @return + */ + public Enum[] getSelectedEnum(Enum[] e) { + LinkedList selectedList = new LinkedList(); + for (int i=0; i iter = selectedList.iterator(); + int i=0; + while (iter.hasNext()) { + ret[i++]=e[iter.next()]; + } + return ret; + } } \ No newline at end of file diff --git a/src/eva2/tools/StringTools.java b/src/eva2/tools/StringTools.java index 2c2b9308..ae9a5613 100644 --- a/src/eva2/tools/StringTools.java +++ b/src/eva2/tools/StringTools.java @@ -2,6 +2,7 @@ package eva2.tools; import java.util.HashMap; import java.util.LinkedList; +import java.util.List; import java.util.StringTokenizer; import eva2.gui.BeanInspector; @@ -218,4 +219,44 @@ public class StringTools { } return -1; } + + /** + * Concatenate a list of Strings using a given delimiter string. + * + * @param headlineFields + * @param delim + * @return + */ + public static String concatFields(List strings, + String delim) { + StringBuffer sb = new StringBuffer(); + int cnt=0; + for (String field : strings) { + if (cnt>0) sb.append(delim); + sb.append(field); + cnt++; + } + return sb.toString(); + } + + /** + * Concatenate a list of Objects using a given delimiter string. + * The objects are converted to strings using the BeanInspector class. + * + * @param headlineFields + * @param delim + * @return + */ + public static String concatValues(List objects, + String delim) { + StringBuffer sb = new StringBuffer(); + int cnt=0; + for (Object v : objects) { + if (cnt>0) sb.append(delim); + sb.append(BeanInspector.toString(v)); + cnt++; + } + return sb.toString(); + } } + diff --git a/src/eva2/tools/ToolBox.java b/src/eva2/tools/ToolBox.java new file mode 100644 index 00000000..65931dc5 --- /dev/null +++ b/src/eva2/tools/ToolBox.java @@ -0,0 +1,97 @@ +package eva2.tools; + +import java.util.ArrayList; +import java.util.List; + +import eva2.gui.BeanInspector; + +/** + * Collection of miscellaneous static helper methods. + * + * @author mkron + * + */ +public class ToolBox { + + /** + * Convert all items of an enum to a String array and append the given String array at the end. + * + * @param additionals + * @return + */ + public static String[] appendEnumAndArray(Enum e, String[] additionals) { + Enum[] fields = e.getClass().getEnumConstants(); + int enumLen = fields.length; //values().length; + int len = enumLen+additionals.length; + String[] ret = new String[len]; + for (int i=0; i l) { + ArrayList vals = new ArrayList(); + for (Object o : l) { + vals.add(toDouble(o)); // null if unsuccessfull + } + return vals.toArray(new Double[vals.size()]); + } + + /** + * Try to convert a Double from a given Object. Return null + * if conversion fails (e.g. because the Object is a complex data type + * which has no straight-forward numeric representation, e.g. an array). + * + * @param o + * @return + */ + public static Double toDouble(Object o) { + if (o instanceof Number) return ((Number)o).doubleValue(); + else try { + Double d = Double.parseDouble(BeanInspector.toString(o)); + return d; + } catch(Exception e) { } + return null; + } + + /** + * For an array of objects, generate an array of Double which contains the + * converted double arrays whenever this is directly possible, or null otherwise. + * + * @see BeanInspector.toString(Object) + * @param l + * @return + */ + public static Double[] parseDoubles(Object [] os) { + Double[] vals = new Double[os.length]; + for (int i=0; i