From 49f893e8999ea42f41f66a0153cb4719aa2d1e64 Mon Sep 17 00:00:00 2001 From: Marcel Kronfeld Date: Wed, 18 Mar 2009 11:10:02 +0000 Subject: [PATCH] Changes from MK rev. 255, update to ParticleFilter, and tool tips dont break at points. --- src/eva2/gui/PropertySheetPanel.java | 16 +- .../distancemetric/EuclideanMetric.java | 51 ++++ .../mutation/MutateESCorrVector.java | 259 ++++++++++++++++++ .../selection/SelectParticleWheel.java | 9 + .../ParticleFilterOptimization.java | 132 ++++++++- 5 files changed, 448 insertions(+), 19 deletions(-) create mode 100644 src/eva2/server/go/operators/distancemetric/EuclideanMetric.java create mode 100644 src/eva2/server/go/operators/mutation/MutateESCorrVector.java diff --git a/src/eva2/gui/PropertySheetPanel.java b/src/eva2/gui/PropertySheetPanel.java index 1836d3c8..6b885687 100644 --- a/src/eva2/gui/PropertySheetPanel.java +++ b/src/eva2/gui/PropertySheetPanel.java @@ -36,7 +36,6 @@ import java.beans.PropertyVetoException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; - import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; @@ -45,6 +44,7 @@ import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.SwingConstants; +import eva2.gui.GenericObjectEditor.GOEPanel; import eva2.tools.EVAHELP; import eva2.tools.StringTools; /*==========================================================================* @@ -91,6 +91,9 @@ public class PropertySheetPanel extends JPanel implements PropertyChangeListener private PropertyChangeSupport m_support = new PropertyChangeSupport(this); /** set true to use the GOE by default if no other editor is registered **/ + // If true, tool tips are used up to the first point only. + boolean stripToolTipToFirstPoint=false; + /** Creates the property sheet panel. */ public PropertySheetPanel() { @@ -105,6 +108,9 @@ public class PropertySheetPanel extends JPanel implements PropertyChangeListener */ public void propertyChange(PropertyChangeEvent evt) { if (TRACE) System.out.println("PropertySheetPanel.propertyChange() "+m_Target.getClass()+": calling wasModified"); + // GOEPanel gp=(GOEPanel)this.getParent(); + // gp.validateTarget(this); // Once trying to find an irreproducible bug + wasModified(evt); // Let our panel update before guys downstream m_support.removePropertyChangeListener(this); m_support.firePropertyChange("", null, m_Target); @@ -841,9 +847,11 @@ public class PropertySheetPanel extends JPanel implements PropertyChangeListener try { Object args[] = { }; String tempTip = (String)(meth.invoke(target, args)); - int ci = tempTip.indexOf('.'); - if (ci < 0) result = tempTip; - else result = tempTip.substring(0, ci); + result = tempTip; + if (stripToolTipToFirstPoint) { + int ci = tempTip.indexOf('.'); + if (ci > 0) result = tempTip.substring(0, ci); + } } catch (Exception ex) { } break; diff --git a/src/eva2/server/go/operators/distancemetric/EuclideanMetric.java b/src/eva2/server/go/operators/distancemetric/EuclideanMetric.java new file mode 100644 index 00000000..bee77408 --- /dev/null +++ b/src/eva2/server/go/operators/distancemetric/EuclideanMetric.java @@ -0,0 +1,51 @@ +package eva2.server.go.operators.distancemetric; + +import eva2.server.go.individuals.AbstractEAIndividual; + +/** + * The Euclidean metric just measures the Euclidean distance based on the default double representation + * as given by AbstractEAIndividual.getDoublePosition(AbstractEAIndividual). + * + * @see AbstractEAIndividual.getDoublePosition(AbstractEAIndividual) + * @author mkron + * + */ +public class EuclideanMetric implements InterfaceDistanceMetric { + + public Object clone() { + return (Object) new EuclideanMetric(this); + } + + public EuclideanMetric(EuclideanMetric a) { + } + + public EuclideanMetric() { + } + + public double distance(AbstractEAIndividual indy1, AbstractEAIndividual indy2) { + double[] dIndy1, dIndy2; + double result = 0; + + dIndy1 = AbstractEAIndividual.getDoublePosition(indy1); + dIndy2 = AbstractEAIndividual.getDoublePosition(indy2); + + for (int i = 0; (i < dIndy1.length) && (i < dIndy2.length); i++) { + result += Math.pow((dIndy1[i] - dIndy2[i]), 2); + } + return Math.sqrt(result); + } + + /** This method returns a global info string + * @return description + */ + public String globalInfo() { + return "The euclidean metric calculates euclidian distances for individuals which have a real valued interpretation."; + } + /** This method will return a naming String + * @return The name of the algorithm + */ + public String getName() { + return "Euclidean Metric"; + } +} + diff --git a/src/eva2/server/go/operators/mutation/MutateESCorrVector.java b/src/eva2/server/go/operators/mutation/MutateESCorrVector.java new file mode 100644 index 00000000..98b274f9 --- /dev/null +++ b/src/eva2/server/go/operators/mutation/MutateESCorrVector.java @@ -0,0 +1,259 @@ +package eva2.server.go.operators.mutation; + +import java.util.ArrayList; + +import eva2.gui.BeanInspector; +import eva2.server.go.individuals.AbstractEAIndividual; +import eva2.server.go.individuals.InterfaceESIndividual; +import eva2.server.go.populations.Population; +import eva2.server.go.problems.InterfaceOptimizationProblem; +import wsi.ra.math.RNG; +import eva2.tools.Mathematics; +import eva2.tools.SelectedTag; + + +/** + * The correlated vector mutations stores a "velocity" vector for each individual, + * updates the velocity by rotation and scaling, and then mutates the individual + * by adding the velocity. This was used for a particle filter localization problem + * and is less useful in general. + * + * Rotation vectors are normal distributed with mean zero, scaling factors are + * log-normally distributed around mean 1. This means that the averaged expected change + * of the mutation vector is zero. The smaller the deviations, the higher the correlations + * between successive mutation steps. + */ +public class MutateESCorrVector implements InterfaceMutation, java.io.Serializable { + protected double m_scalingDev = 0.05; + protected double m_initialVelocity = 0.02; + protected double m_LowerLimitStepSize = 0.0000001; + protected double m_UpperLimitStepSize = 0.5; + protected double m_rotationDev = 15.; + protected boolean m_checkConstraints = true; + public static final String vectorKey = "MutateESCorrVectorVector"; + public static final boolean TRACE = false; + + public MutateESCorrVector() { + } + + public MutateESCorrVector(double scalingDev) { + setScalingDev(scalingDev); + } + + public MutateESCorrVector(double scalingDev, double initialVelocity) { + setScalingDev(scalingDev); + setInitialVelocity(initialVelocity); + } + + public MutateESCorrVector(double scalingDev, double initialVelocity, double rotDev) { + setScalingDev(scalingDev); + setInitialVelocity(initialVelocity); + setRotationDev(rotDev); + } + + public MutateESCorrVector(MutateESCorrVector mutator) { + this.m_scalingDev = mutator.m_scalingDev; + this.m_initialVelocity = mutator.m_initialVelocity; + this.m_LowerLimitStepSize = mutator.m_LowerLimitStepSize; + this.m_rotationDev = mutator.m_rotationDev; + } + + /** This method will enable you to clone a given mutation operator + * @return The clone + */ + public Object clone() { + return new MutateESCorrVector(this); + } + + /** This method allows you to evaluate wether two mutation operators + * are actually the same. + * @param mutator The other mutation operator + */ + public boolean equals(Object mutator) { + if (mutator instanceof MutateESCorrVector) { + MutateESCorrVector mut = (MutateESCorrVector)mutator; + if (this.m_scalingDev != mut.m_scalingDev) return false; + if (this.m_initialVelocity != m_initialVelocity) return false; + if (this.m_LowerLimitStepSize != mut.m_LowerLimitStepSize) return false; + return true; + } else return false; + } + + /** This method allows you to init the mutation operator + * @param individual The individual that will be mutated. + * @param opt The optimization problem. + */ + public void init(AbstractEAIndividual individual, InterfaceOptimizationProblem opt) { + double[] initVelocity = calcInitialVel(m_initialVelocity, ((InterfaceESIndividual)individual).getDoubleRange()); + individual.putData(vectorKey, initVelocity); + } + + /** + * Create a random vector of length relative to the given range given by the velocity parameter. + * + * @param velocity + * @param doubleRange + * @return + */ + private double[] calcInitialVel(double velocity, double[][] doubleRange) { + double[] initVelocity = Mathematics.randomVector(doubleRange.length, 1.0); + double nrm=Mathematics.norm(initVelocity); + double[] shiftedRange=Mathematics.shiftRange(doubleRange); + // normalize to speed + Mathematics.svMult(velocity/nrm, initVelocity, initVelocity); + // and scale by ranges + Mathematics.vvMultCw(shiftedRange, initVelocity, initVelocity); + // System.out.println(Mathematics.getRelativeLength(initVelocity, doubleRange)); + return initVelocity; + } + + /** This method will mutate a given AbstractEAIndividual. If the individual + * doesn't implement InterfaceESIndividual nothing happens. + * @param individual The individual that is to be mutated + */ + public void mutate(AbstractEAIndividual individual) { +// if (TRACE) System.out.println("Before Mutate: " + AbstractEAIndividual.getDefaultDataString(individual)); + if (individual instanceof InterfaceESIndividual) { + double[] genes = ((InterfaceESIndividual)individual).getDGenotype(); + double[][] range = ((InterfaceESIndividual)individual).getDoubleRange(); + + double[] vel = (double[])individual.getData(vectorKey); + + // mutate the velocity vector and write it back + if ((m_scalingDev > 0) || (m_rotationDev > 0)) { +// for (int i = 0; i < vel.length; i++) { +// vel[i] += ((range[i][1] -range[i][0])/2)*RNG.gaussianDouble(this.m_MutationStepSize); +// } + double rotateRad = m_rotationDev*(Math.PI/360.)*RNG.gaussianDouble(1.); + // rotate with a gaussian distribution of deviation rotationDeg + Mathematics.rotateAllAxes(vel, rotateRad, false); // rotate + double rScale=Math.exp(RNG.gaussianDouble(m_scalingDev)); + + if ((m_LowerLimitStepSize > 0) || (m_UpperLimitStepSize > 0) ) { + double stepLen=Mathematics.norm(vel); + if (m_LowerLimitStepSize > 0) rScale=Math.max(rScale, m_LowerLimitStepSize/stepLen); + if (m_UpperLimitStepSize > 0) rScale=Math.min(rScale, m_UpperLimitStepSize/stepLen); + } + + Mathematics.svMult(rScale, vel, vel); // mutate speed + + individual.putData(vectorKey, vel); + if (TRACE) System.out.println("rotated by " + rotateRad + ", scaled by " + rScale); + if (TRACE) System.out.println("-- dir is " + BeanInspector.toString(vel)); + } + + // add velocity to the individual + Mathematics.vvAdd(genes, vel, genes); + + // check the range + if (m_checkConstraints) Mathematics.projectToRange(genes, range); + + // write genotype back + ((InterfaceESIndividual)individual).SetDGenotype(genes); + + } +// if (TRACE) System.out.println("After Mutate: " + AbstractEAIndividual.getDefaultDataString(individual)); + } + + /** This method allows you to perform either crossover on the strategy parameters + * or to deal in some other way with the crossover event. + * @param indy1 The original mother + * @param partners The original partners + */ + public void crossoverOnStrategyParameters(AbstractEAIndividual indy1, Population partners) { + ArrayList tmpList = new ArrayList(); + if (indy1.getMutationOperator() instanceof MutateESCorrVector) tmpList.add(new Double(((MutateESCorrVector)indy1.getMutationOperator()).m_scalingDev)); + for (int i = 0; i < partners.size(); i++) { + if (((AbstractEAIndividual)partners.get(i)).getMutationOperator() instanceof MutateESCorrVector) tmpList.add(new Double(((MutateESCorrVector)((AbstractEAIndividual)partners.get(i)).getMutationOperator()).m_scalingDev)); + } + double[] list = new double[tmpList.size()]; + for (int i = 0; i < tmpList.size(); i++) list[i] = ((Double)tmpList.get(i)).doubleValue(); + if (list.length <= 1) return; + // discreete mutation for step size + this.m_scalingDev = list[RNG.randomInt(0, list.length-1)]; + } + + /** This method allows you to get a string representation of the mutation + * operator + * @return A descriptive string. + */ + public String getStringRepresentation() { + return "ES global mutation"; + } + +/********************************************************************************************************************** + * These are for GUI + */ + /** This method allows the CommonJavaObjectEditorPanel to read the + * name to the current object. + * @return The name. + */ + public String getName() { + return "ES correlated vector mutation"; + } + /** This method returns a global info string + * @return description + */ + public String globalInfo() { + return "The correlated vector mutation stores a specific mutation vector per individual."; + } + + /** Set the initial mutation step size with this method. + * @param d The mutation operator. + */ + public void setScalingDev(double d) { + this.m_scalingDev = d; + } + public double getScalingDev() { + return this.m_scalingDev; + } + public String scalingDevTipText() { + return "Choose the devation of lognormal vector scaling."; + } + + /** Set the lower limit for the mutation step size with this method. + * @param d The mutation operator. + */ + public void setLowerLimitStepSize(double d) { + if (d < 0) d = 0; + this.m_LowerLimitStepSize = d; + } + public double getLowerLimitStepSize() { + return this.m_LowerLimitStepSize; + } + public String lowerLimitStepSizeTipText() { + return "Set the lower limit for the mutation step."; + } + + public double getRotationDev() { + return m_rotationDev; + } + + public void setRotationDev(double rotationDeg) { + this.m_rotationDev = rotationDeg; + } + + public String rotationDevTipText() { + return "Std deviation of the rotation angle distribution"; + } + + public double getInitialVelocity() { + return m_initialVelocity; + } + + public void setInitialVelocity(double velocity) { + m_initialVelocity = velocity; + } + + public double getUpperLimitStepSize() { + return m_UpperLimitStepSize; + } + + public void setUpperLimitStepSize(double upperLimitStepSize) { + m_UpperLimitStepSize = upperLimitStepSize; + } + + public String upperLimitStepSizeTipText() { + return "Set the upper limit for the mutation step."; + } +} diff --git a/src/eva2/server/go/operators/selection/SelectParticleWheel.java b/src/eva2/server/go/operators/selection/SelectParticleWheel.java index c34f2f87..0efc338a 100644 --- a/src/eva2/server/go/operators/selection/SelectParticleWheel.java +++ b/src/eva2/server/go/operators/selection/SelectParticleWheel.java @@ -4,6 +4,7 @@ import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.operators.selection.probability.InterfaceSelectionProbability; import eva2.server.go.operators.selection.probability.SelProbBoltzman; import eva2.server.go.operators.selection.probability.SelProbStandard; +import eva2.server.go.operators.selection.probability.SelProbStandardScaling; import eva2.server.go.populations.Population; import wsi.ra.math.RNG; @@ -29,6 +30,14 @@ public class SelectParticleWheel implements InterfaceSelection, java.io.Serializ public SelectParticleWheel() { } + + public SelectParticleWheel(double scalingProb) { + m_SelProbCalculator = new SelProbStandardScaling(scalingProb); + } + + public SelectParticleWheel(InterfaceSelectionProbability selProb) { + m_SelProbCalculator = selProb; + } public SelectParticleWheel(SelectParticleWheel a) { this.m_SelProbCalculator = (InterfaceSelectionProbability)a.m_SelProbCalculator.clone(); diff --git a/src/eva2/server/go/strategies/ParticleFilterOptimization.java b/src/eva2/server/go/strategies/ParticleFilterOptimization.java index cf4dba2d..8f86e826 100644 --- a/src/eva2/server/go/strategies/ParticleFilterOptimization.java +++ b/src/eva2/server/go/strategies/ParticleFilterOptimization.java @@ -1,11 +1,14 @@ package eva2.server.go.strategies; +import eva2.gui.BeanInspector; +import eva2.gui.GenericObjectEditor; import eva2.gui.Plot; import eva2.server.go.InterfacePopulationChangedEventListener; import eva2.server.go.individuals.AbstractEAIndividual; import eva2.server.go.individuals.InterfaceDataTypeDouble; -import eva2.server.go.individuals.InterfaceESIndividual; +import eva2.server.go.operators.distancemetric.EuclideanMetric; +import eva2.server.go.operators.mutation.MutateESCorrVector; import eva2.server.go.operators.mutation.MutateESFixedStepSize; import eva2.server.go.operators.selection.InterfaceSelection; import eva2.server.go.operators.selection.SelectParticleWheel; @@ -16,7 +19,8 @@ import eva2.server.go.problems.AbstractOptimizationProblem; import eva2.server.go.problems.F1Problem; import eva2.server.go.problems.InterfaceOptimizationProblem; -/** This is a Particle Filter implemented by Frank Senke, only some documentation +/** + * This is a Particle Filter implemented by Frank Senke, only some documentation * here and not completely checked whether this works on arbitrary problem * instances. MK did some adaptations, this should work on real valued problems now. * @@ -37,23 +41,37 @@ public class ParticleFilterOptimization implements InterfaceOptimizer, java.io.S private static final long serialVersionUID = 1L; private Population m_Population = new Population(); private InterfaceOptimizationProblem m_Problem = new F1Problem(); - private InterfaceSelection m_ParentSelection = new SelectParticleWheel(); + private InterfaceSelection m_ParentSelection = new SelectParticleWheel(0.5); //private boolean m_UseElitism = true; private String m_Identifier = ""; private boolean withShow = false; - private double mutationSigma = 0.05; + private double mutationSigma = 0.01; + private double randomImmigrationQuota = 0.05; + private double initialVelocity = 0.02; + private double rotationDeg = 20.; + private int popSize = 300; private int sleepTime = 0; transient private int indCount = 0; transient private InterfacePopulationChangedEventListener m_Listener; transient Plot myPlot = null; + public static final boolean TRACE = false; public ParticleFilterOptimization() { if (withShow) setWithShow(true); } - + + public ParticleFilterOptimization(double vInit, double mute, double immiQuote, double rotDeg, double selScaling) { + mutationSigma = mute; + initialVelocity = vInit; + randomImmigrationQuota = immiQuote; + rotationDeg = rotDeg; + m_ParentSelection = new SelectParticleWheel(selScaling); + if (withShow) setWithShow(true); + } + public ParticleFilterOptimization(ParticleFilterOptimization a) { this.m_Population = (Population)a.m_Population.clone(); this.m_Problem = (InterfaceOptimizationProblem)a.m_Problem.clone(); @@ -65,19 +83,28 @@ public class ParticleFilterOptimization implements InterfaceOptimizer, java.io.S if (a.withShow) setWithShow(true); } + public void hideHideable() { + GenericObjectEditor.setHideProperty(this.getClass(), "population", true); + } + public Object clone() { return (Object) new ParticleFilterOptimization(this); } public void init() { - this.m_Problem.initPopulation(this.m_Population); //System.out.println("popsize is " + m_Population.size()); //System.out.println("pops targ is " + m_Population.getPopulationSize()); - for (int i=0; i selection does only shallow copies! - parents = (Population)(this.m_ParentSelection.selectFrom(pop, this.m_Population.getPopulationSize())).clone(); + int targetSize = this.m_Population.getPopulationSize(); + if (randomImmigrationQuota>0) { + if (randomImmigrationQuota>1.) System.err.println("Error, invalid immigration quota!"); + else { + targetSize = (int)(this.m_Population.getPopulationSize() * (1.-randomImmigrationQuota)); + targetSize = Math.max(1, targetSize); // guarantee at least one to be selected + if (targetSize < this.m_Population.getPopulationSize()) doImmigr=true; + } + } + + parents = (Population)(this.m_ParentSelection.selectFrom(pop, targetSize)).clone(); + + if (doImmigr) { + // add immigrants + AbstractEAIndividual immi; + int i; + for (i=0; (i+parents.getPopulationSize()) 0, a linear motion model will be applied, otherwise the gaussian model"; + } + + public double getRotationDeg() { + return rotationDeg; + } + + public void setRotationDeg(double rotationDeg) { + this.rotationDeg = rotationDeg; + } + + public int getPopSize() { + return popSize; + } + + public void setPopSize(int popSize) { + this.popSize = popSize; + m_Population.setPopSize(popSize); } }