From ddbebe2c484a4d74d00c8b765cdfd042ca91e5ad Mon Sep 17 00:00:00 2001 From: Marcel Kronfeld Date: Wed, 27 Apr 2011 14:27:53 +0000 Subject: [PATCH] Adding ScatterSearch and modifications of BSS/BOA (seitz rev. 930) --- src/eva2/EvAInfo.java | 2 + src/eva2/server/go/strategies/BOA.java | 6 +- .../go/strategies/BinaryScatterSearch.java | 70 +- .../server/go/strategies/ScatterSearch.java | 931 ++++++++++++++++++ 4 files changed, 985 insertions(+), 24 deletions(-) create mode 100644 src/eva2/server/go/strategies/ScatterSearch.java diff --git a/src/eva2/EvAInfo.java b/src/eva2/EvAInfo.java index d1b947f1..01218785 100644 --- a/src/eva2/EvAInfo.java +++ b/src/eva2/EvAInfo.java @@ -9,6 +9,8 @@ import eva2.tools.BasicResourceLoader; * Main product and version information strings. * * ---- Changelog + * 2.047: Added ScatterSearch (real-valued), BinaryScatterSearch, and the Bayesian Optimization Algorithm (thanks + * to Alexander Seitz) to the base package. * 2.046: Adaptions to the MatlabInterface: explicit data types are used now, added integer problem support. * Additional Integer operators: segment-wise mutation and crossover. Added an abstraction over individual * initialization methods. Added the ERPStarter class which is an example for running a csv-configured diff --git a/src/eva2/server/go/strategies/BOA.java b/src/eva2/server/go/strategies/BOA.java index 6b852a9f..e94486b6 100644 --- a/src/eva2/server/go/strategies/BOA.java +++ b/src/eva2/server/go/strategies/BOA.java @@ -126,9 +126,11 @@ public class BOA implements InterfaceOptimizer, java.io.Serializable { */ private void evaluate(AbstractEAIndividual indy){ // evaluate the given individual if it is not null - if(indy != null){ - this.problem.evaluate(indy); + if(indy == null){ + System.err.println("tried to evaluate null"); + return; } + this.problem.evaluate(indy); // increment the number of evaluations this.population.incrFunctionCalls(); } diff --git a/src/eva2/server/go/strategies/BinaryScatterSearch.java b/src/eva2/server/go/strategies/BinaryScatterSearch.java index 5006fef1..3d7f3bd1 100644 --- a/src/eva2/server/go/strategies/BinaryScatterSearch.java +++ b/src/eva2/server/go/strategies/BinaryScatterSearch.java @@ -35,7 +35,7 @@ import eva2.tools.math.RNG; * Computers and Operations research, vol. 37, no. 11, pp. 1977-1986 (2010) */ public class BinaryScatterSearch implements InterfaceOptimizer, java.io.Serializable, InterfacePopulationChangedEventListener { - private static boolean TRACE = false; + private static boolean TRACE = true; transient private InterfacePopulationChangedEventListener m_Listener = null; private String m_Identifier = "BinaryScatterSearch"; @@ -81,10 +81,10 @@ public class BinaryScatterSearch implements InterfaceOptimizer, java.io.Serializ this.g1 = b.g1; this.g2 = b.g2; this.firstTime = b.firstTime; - this.template = b.template; - this.problem = b.problem; - this.pool = b.pool; - this.refSet = b.refSet; + this.template = (AbstractEAIndividual) b.template.clone(); + this.problem = (AbstractOptimizationProblem) b.problem.clone(); + this.pool = (Population) b.pool.clone(); + this.refSet = (Population) b.refSet.clone(); this.cross = b.cross; } @@ -165,9 +165,11 @@ public class BinaryScatterSearch implements InterfaceOptimizer, java.io.Serializ */ private void evaluate(AbstractEAIndividual indy){ // evaluate the given individual if it is not null - if(indy != null){ - this.problem.evaluate(indy); + if(indy == null){ + System.err.println("tried to evaluate null"); + return; } + this.problem.evaluate(indy); // increment the number of evaluations this.refSet.incrFunctionCalls(); } @@ -218,22 +220,46 @@ public class BinaryScatterSearch implements InterfaceOptimizer, java.io.Serializ * @return a diversified Population with all the Individuals in the initial Population */ private Population diversify(Population pop){ - pop = generateG1(pop); - pop = generateG2(pop); - pop = generateG3(pop); + int numToInit = this.poolSize - pop.size(); + if (numToInit>0) { + pop.addAll(generateG1((int)(numToInit * this.g1))); + if (TRACE) System.out.println("s1: " + pop.size()); + generateG2(pop, (int)(numToInit * this.g2)); + if (TRACE) System.out.println("s2: " + pop.size()); + generateG3(pop, poolSize-pop.size()); + if (TRACE) System.out.println("s3: " + pop.size()); + } return pop; } /** - * generate a new Population with diverse Individuals starting with 000...000, then 010101...01, 101010...10, 001001001...001, 110110110...110 and so on + * Generate a new Population with diverse Individuals starting with 000...000, + * then 010101...01, 101010...10, 001001001...001, 110110110...110 and so on + * The returned population is evaluated. * @param pop the initial Population * @return the new Population */ - private Population generateG1(Population pop){ + private Population generateG1(int numToInit){ + boolean method1 = true; + Population pop = generateG1Pop(numToInit, this.template); + for (int i=0; i0){ result = pop.getBestEAIndividual(); @@ -656,7 +682,7 @@ public class BinaryScatterSearch implements InterfaceOptimizer, java.io.Serializ this.refSet.incrFunctionCallsBy(this.pool.size()); // increase the number of evaluations by the number of evaluations that are performed in the crossover-step for(int i=0; i 0) { +// System.out.println("bla"); +// } + // set funcalls to real value + refSet.SetFunctionCalls(((Population)source).getFunctionCalls()); + +// System.out.println("FunCallIntervalReached at " + (((Population)source).getFunctionCalls())); + + this.firePropertyChangedEvent(Population.nextGenerationPerformed); + } + // do not react to NextGenerationPerformed + //else System.err.println("ERROR, event was " + name); + } + + public void optimize() { + // Diversification + // Refset Formation + // L: Combination of refset elements + // if (localSolverCall) then + // if (pass filters) then compute local solvers + // refset update + // if (uncombined elements) then goto L + // if (!stop criterion) then + // refset regeneration + // goto L + if (!firstTime) { + if (lastImprovementCount == 0) refSet = regenerateRefSet(); + } + firstTime = false; + + problem.evaluatePopulationStart(refSet); + int funCallsStart = refSet.getFunctionCalls(); + do { + if (combinations == null || combinations.size() == 0) { + if (TRACE) { + System.out.println("Improvements: " + lastImprovementCount); + System.out.println("---------- best: " + refSet.getBestEAIndividual().getFitness(0)); + } + combinations = generateCombinations(refSet); + oldRefSet = (Population) refSet.clone(); + lastImprovementCount = 0; + } + if (TRACE) System.out.println("No combinations: " + combinations.size()); + if (combinations.size()>0) { + updateRefSet(refSet, combinations, oldRefSet); + } + } while (refSet.getFunctionCalls()-funCallsStart < generationCycle); + problem.evaluatePopulationEnd(refSet); + + if (TRACE) { + System.out.println("Improvements: " + lastImprovementCount); + } + + } + + private boolean isDoLocalSolver(AbstractEAIndividual cand, Population refSet) { +// if (lastLocalSearch + localSearchInterval < refSet.getGeneration()) { + if (doLocalSearch) { + // filter: only check those within 50% of the worst indy relative to the best. + if (relativeFitCriterion) { + double fitRange = refSet.getWorstFitness()[0] - refSet.getBestFitness()[0]; + return (cand.getFitness(0) < (refSet.getBestFitness()[0]+(fitRange*localSearchFitnessFilter))); + } else { + // absolute fitness criterion + return (cand.getFitness(0) < localSearchFitnessFilter); + } + } else return false; + } + + private Population regenerateRefSet() { + Population diversifiedPop = diversify(); + int keep = refSetSize/2; + Population newRefSet = refSet.cloneWithoutInds(); + if (TRACE) System.out.println("regen after " + refSet.getFunctionCalls() + ", best is " + refSet.getBestEAIndividual().getFitness(0)); + + newRefSet.addAll(refSet.getBestNIndividuals(keep, fitCrit)); + + if (TRACE) System.out.println("keeping " + keep + " indies from former ref set, best is " + newRefSet.getBestEAIndividual().getFitness(0)); + + int h=newRefSet.size(); + ArrayList distVects = new ArrayList(); + for (int i=1; i dmax) { + dmax = vals[j]; + } + } + return dmax; + } + + private double[] calcVectP(AbstractEAIndividual candidate, AbstractEAIndividual best, ArrayList distVects) { + // p = (best - candidate)*transposed(M) + double[] diff = getDiffVect(best, candidate); + return multScalarTransposed(diff, distVects); + } + + private double[] multScalarTransposed(double[] diff, ArrayList distVects) { + // d[0]*m[0][0], d[1]*m[0][1] etc. + double[] res = new double[distVects.size()]; + for (int i=0; i lsRet = localSolver(bestCand, localSearchSteps); + if ((Math.abs(lsRet.tail() - localSearchSteps)/localSearchSteps) > 0.05) System.err.println("Warning, more than 5% difference in local search step"); + bestCand = lsRet.head(); + refSet.incrFunctionCallsBy(lsRet.tail()); + if (TRACE) { + System.out.println("best cand after: " + bestCand.getFitness(0)); + } + } + + if (bestCand.isDominatingEqual(worstRef)) { + if (TRACE) System.out.println("cand is dominating worst ref!"); + if (diversityCriterionFulfilled(bestCand, refSet, oldRefSet)) { +// System.out.println("diversity criterion is fulfilled! replacing fit " + worstRef.getFitness(0)); + int replIndex = refSet.indexOf(worstRef); + refSet.set(replIndex, bestCand); + lastImprovementCount++; + } else if (bestCand.isDominating(refSet.getBestEAIndividual())) { + // exception: always accept best solution found so far + int closestIndex = getClosestIndy(bestCand, refSet); +// if (TRACE) System.out.println("replacing due to best fit"); + refSet.set(closestIndex, bestCand); + lastImprovementCount++; + } +// System.out.println("Improvements: " + lastImprovementCount); + candidates.remove(bestIndex); + } else { + if (TRACE) System.out.println("cand is too bad!"); + // if the best candidate is worse and no local search is performed, all following will be worse - at least in the uni-criterial case + // so we can just clear the rest of the candidates + if (!doLocalSearch && (bestCand.getFitness().length == 1)) candidates.clear(); + else candidates.remove(bestIndex); + } + } + + private Pair localSolver(AbstractEAIndividual cand, int hcSteps) { + if (localSearchMethod.getSelectedTagID()==0) return localSolverHC(cand, hcSteps); + else return PostProcess.localSolverNMS(cand, hcSteps, nelderMeadInitPerturbation, problem); + } + + private Pair localSolverHC(AbstractEAIndividual cand, int hcSteps) { + // use HC for a start... +// double[] fitBefore = cand.getFitness(); + Population hcPop = new Population(1); + hcPop.add(cand); + int stepsDone = PostProcess.processWithHC(hcPop, problem, hcSteps); +// if (TRACE) { +// System.out.println("local search result: from " + BeanInspector.toString(fitBefore) + " to " + BeanInspector.toString(hcPop.getEAIndividual(0).getFitness())); +// } + return new Pair(hcPop.getEAIndividual(0), stepsDone); + } + + private int getClosestIndy(AbstractEAIndividual indy, Population refSet) { + double tmpDst, dist = PhenotypeMetric.dist(indy, refSet.getEAIndividual(0)); + int sel = 0; + for (int i=1; i minDiversityEpsilon)); + + if (minDistFulfilled && (improvementEpsilon > 0)) { + boolean minImprovementFulfilled = (cand.getFitness(0) < ((1.-improvementEpsilon) * popComPheno.getBestEAIndividual().getFitness(0))); + return minImprovementFulfilled; + } else return minDistFulfilled; + } + + /** + * Recombines the refset to new indies which are also evaluated. + * @param refSet + * @return + */ + private Population generateCombinations(Population refSet) { + // 3 pair types: better-better, better-worse, worse-worse (half of the pop); + Population combs = new Population(); + Population refSorted = refSet.getBestNIndividuals(refSet.size(), fitCrit); + int half = refSet.size()/2; + for (int i=0; i highestMin) { + highestMin = dtmp; + sel = i; + } + } + return sel; + } + + private Population diversify() { + return diversify(new Population()); + } + + private Population diversify(Population pop) { + int[][] freq = new int[probDim][intervals]; + if (pop.size() > 0) { // count the interval appearances of already given individuals. + for (int k=0; k= 1.0000001) System.err.println("Check this: sum>=1 in selectInterv"); + return sel; + } + + /** + * Create probDim individuals where each dimension is initialized within + * subinterval i for individual i. + * + * @param interval + * @return + */ + private AbstractEAIndividual createDiagIndies(int interval) { + AbstractEAIndividual indy = (AbstractEAIndividual)template.clone(); + InterfaceDataTypeDouble dblIndy = (InterfaceDataTypeDouble)indy; + double[] genes = dblIndy.getDoubleData(); + for (int i=0; i 0) { + ss.setDoLocalSearch(true); + ss.setLocalSearchSteps(localSearchSteps); + ss.setLocalSearchFitnessFilter(localSearchFitnessFilter); + } else ss.setDoLocalSearch(false); + Population pop = new Population(); + pop.setTargetSize(refSetSize); + pop.init(); + problem.initPopulation(pop); + ss.initByPopulation(pop, true); + + return OptimizerFactory.makeParams(ss, pop, problem, 0, term); + } + + /** + * @return the relativeFitCriterion + */ + public boolean isLocalSearchRelativeFilter() { + return relativeFitCriterion; + } + + /** + * @param relativeFitCriterion the relativeFitCriterion to set + */ + public void setLocalSearchRelativeFilter(boolean relativeFitCriterion) { + this.relativeFitCriterion = relativeFitCriterion; + } + + public String localSearchRelativeFilterTipText() { + return "If selected, local search will be triggered by relative fitness, else by absolute"; + } + + /** + * @return the nelderMeadInitPerturbation + */ + public double getNelderMeadInitPerturbation() { + return nelderMeadInitPerturbation; + } + + /** + * @param nelderMeadInitPerturbation the nelderMeadInitPerturbation to set + */ + public void setNelderMeadInitPerturbation(double nelderMeadInitPerturbation) { + this.nelderMeadInitPerturbation = nelderMeadInitPerturbation; + } + + public String nelderMeadInitPerturbationTipText() { + return "The relative range of the initial perturbation for creating the initial Nelder-Mead-Simplex"; + } + + /** + * @return the localSearchMethod + */ + public SelectedTag getLocalSearchMethod() { + return localSearchMethod; + } + + /** + * @param localSearchMethod the localSearchMethod to set + */ + public void setLocalSearchMethod(SelectedTag localSearchMethod) { + this.localSearchMethod = localSearchMethod; + setLSShowProps(); + } + + public String localSearchMethodTipText() { + return "The local search method to use"; + } + + /** + * @return the poolSize + */ + public int getPoolSize() { + return poolSize; + } + + /** + * @param poolSize the poolSize to set + */ + public void setPoolSize(int poolSize) { + this.poolSize = poolSize; + } + + public String poolSizeTipText() { + return "The number of individuals created in the diversification step"; + } + + public double getImprovementEpsilon() { + return improvementEpsilon; + } + public void setImprovementEpsilon(double improvementEpsilon) { + this.improvementEpsilon = improvementEpsilon; + } + public String improvementEpsilonTipText() { + return "Minimal relative fitness improvement for a candidate to enter the refSet - set to zero to deactivate."; + } + + public double getMinDiversityEpsilon() { + return minDiversityEpsilon; + } + public void setMinDiversityEpsilon(double minDiversityEpsilon) { + this.minDiversityEpsilon = minDiversityEpsilon; + } + public String minDiversityEpsilonTipText() { + return "Minimal distance to other individuals in the refSet for a candidate to enter the refSet - set to zero to deactivate."; + } +}