New GA mutation and crossover operators with subsegment-maintenance

This commit is contained in:
Marcel Kronfeld 2011-01-19 10:53:09 +00:00
parent af42465438
commit e8c14b0aa8
6 changed files with 351 additions and 27 deletions

View File

@ -92,6 +92,23 @@ public abstract class AbstractEAIndividual implements IndividualInterface, java.
*/
public abstract Object clone();
/**
* Set the init/mutation/crossover operator and probabilities to the given values.
*
* @param initOp
* @param mutOp
* @param pMut
* @param coOp
* @param pCross
*/
public void setOperators(InterfaceInitialization initOp, InterfaceMutation mutOp, double pMut, InterfaceCrossover coOp, double pCross) {
m_InitOperator = initOp;
m_MutationProbability = pMut;
m_MutationOperator = mutOp;
m_CrossoverProbability = pCross;
m_CrossoverOperator = coOp;
}
/**
* Set the mutation/crossover operator and probabilities to the given values.
*
@ -101,10 +118,7 @@ public abstract class AbstractEAIndividual implements IndividualInterface, java.
* @param pCross
*/
public void setOperators(InterfaceMutation mutOp, double pMut, InterfaceCrossover coOp, double pCross) {
m_MutationProbability = pMut;
m_MutationOperator = mutOp;
m_CrossoverProbability = pCross;
m_CrossoverOperator = coOp;
setOperators(new DefaultInitialization(), mutOp, pMut, coOp, pCross);
}
/**

View File

@ -32,6 +32,11 @@ public class GAIndividualBinaryData extends AbstractEAIndividual implements Inte
this.m_Genotype = new BitSet();
}
public GAIndividualBinaryData(int genotypeLen) {
this();
this.setBinaryDataLength(genotypeLen);
}
public GAIndividualBinaryData(GAIndividualBinaryData individual) {
if (individual.m_Phenotype != null)
this.m_Phenotype = (BitSet) individual.m_Phenotype.clone();
@ -131,7 +136,7 @@ public class GAIndividualBinaryData extends AbstractEAIndividual implements Inte
result += "})\n Value: ";
result += "{";
for (int i = 0; i < this.m_GenotypeLength; i++) {
if (i%10==0) result+="|";
if (i%8==0) result+="|";
if (this.m_Genotype.get(i)) result += "1";
else result += "0";
}

View File

@ -10,14 +10,15 @@ import eva2.server.go.problems.InterfaceOptimizationProblem;
import eva2.tools.math.RNG;
/**
* Created by IntelliJ IDEA.
* User: streiche
* Date: 18.03.2003
* Time: 12:45:06
* To change this template use Options | File Templates.
* The famous n-point crossover operator on a binary genotype. Genotypes of
* parent individuals are recombined by exchanging subsegments within randomly
* selected points. Therefore, far-away allels (larger GA schemas) are more likely to be split
* between individuals.
*
* @author mkron, streiche
*/
public class CrossoverGANPoint implements InterfaceCrossover, java.io.Serializable {
private InterfaceOptimizationProblem m_OptimizationProblem;
// private InterfaceOptimizationProblem m_OptimizationProblem;
private int m_NumberOfCrossovers = 3;
public CrossoverGANPoint() {
@ -30,7 +31,7 @@ public class CrossoverGANPoint implements InterfaceCrossover, java.io.Serializab
}
public CrossoverGANPoint(CrossoverGANPoint mutator) {
this.m_OptimizationProblem = mutator.m_OptimizationProblem;
// this.m_OptimizationProblem = mutator.m_OptimizationProblem;
this.m_NumberOfCrossovers = mutator.m_NumberOfCrossovers;
}
@ -41,7 +42,7 @@ public class CrossoverGANPoint implements InterfaceCrossover, java.io.Serializab
return new CrossoverGANPoint(this);
}
/** This method performs crossover on two individuals. If the individuals do
/** This method performs crossover on multiple individuals. If the individuals do
* not implement InterfaceGAIndividual, then nothing will happen.
* @param indy1 The first individual
* @param partners The second individual
@ -58,7 +59,7 @@ public class CrossoverGANPoint implements InterfaceCrossover, java.io.Serializab
if ((indy1 instanceof InterfaceGAIndividual) && (partners.get(0) instanceof InterfaceGAIndividual)) {
int length = ((InterfaceGAIndividual)indy1).getGenotypeLength();
int mixer = RNG.randomInt(0, partners.size());
int[] crossoverPoints = new int[this.m_NumberOfCrossovers];
int[] crossoverPoints = null;
BitSet[][] tmpBitSet = new BitSet[2][partners.size()+1];
tmpBitSet[0][0] = ((InterfaceGAIndividual)indy1).getBGenotype();
@ -69,10 +70,8 @@ public class CrossoverGANPoint implements InterfaceCrossover, java.io.Serializab
length = Math.max(length, ((InterfaceGAIndividual)partners.get(i)).getGenotypeLength());
}
for (int i = 0; i < this.m_NumberOfCrossovers; i++) {
crossoverPoints[i] = RNG.randomInt(0, length-1);
//System.out.println("crpoint: "+crossoverPoints[i]);
}
crossoverPoints=getCrossoverPoints(length, m_NumberOfCrossovers);
for (int i = 0; i < length; i++) {
for (int j = 0; j < this.m_NumberOfCrossovers; j++) {
if (i == crossoverPoints[j]) mixer++;
@ -88,13 +87,27 @@ public class CrossoverGANPoint implements InterfaceCrossover, java.io.Serializab
for (int i = 0; i < result.length; i++) ((InterfaceGAIndividual)result[i]).SetBGenotype(tmpBitSet[1][i]);
}
//in case the crossover was successfull lets give the mutation operators a chance to mate the strategy parameters
//in case the crossover was successful lets give the mutation operators a chance to mate the strategy parameters
for (int i = 0; i < result.length; i++) result[i].getMutationOperator().crossoverOnStrategyParameters(indy1, partners);
//for (int i = 0; i < result.length; i++) System.out.println("After Crossover: " +result[i].getSolutionRepresentationFor());
return result;
}
/** This method allows you to evaluate wether two crossover operators
/**
* Select the crossover points within the genotype of given length.
* @param length
* @param numberOfCrossovers
* @return
*/
protected int[] getCrossoverPoints(int length, int numberOfCrossovers) {
int[] crossoverPoints = new int[numberOfCrossovers];
for (int i = 0; i < numberOfCrossovers; i++) {
crossoverPoints[i] = RNG.randomInt(0, length-1);
}
return crossoverPoints;
}
/** This method allows you to evaluate wether two crossover operators
* are actually the same.
* @param crossover The other crossover operator
*/
@ -114,7 +127,7 @@ public class CrossoverGANPoint implements InterfaceCrossover, java.io.Serializab
* @param opt The optimization problem.
*/
public void init(AbstractEAIndividual individual, InterfaceOptimizationProblem opt) {
this.m_OptimizationProblem = opt;
// this.m_OptimizationProblem = opt;
}
public String getStringRepresentation() {

View File

@ -0,0 +1,70 @@
package eva2.server.go.operators.crossover;
import eva2.tools.math.RNG;
/**
* A variation of the GA n-point crossover. Restricts crossover to segment bounds
* of fixed length, so crossings occur at multiples of the segment length only. Segments
* will not be destroyed.
*
* @author mkron
*
*/
public class CrossoverGANPointSegmentwise extends CrossoverGANPoint {
int segmentLength=8;
public CrossoverGANPointSegmentwise() {
super();
}
public CrossoverGANPointSegmentwise(CrossoverGANPointSegmentwise o) {
super(o);
this.segmentLength=o.segmentLength;
}
public CrossoverGANPointSegmentwise(int nPoints, int segmentLen) {
super(nPoints);
setSegmentLength(segmentLen);
}
public Object clone() {
return new CrossoverGANPointSegmentwise(this);
}
@Override
public boolean equals(Object crossover) {
if (super.equals(crossover) && (crossover instanceof CrossoverGANPointSegmentwise)) {
return ((CrossoverGANPointSegmentwise)crossover).segmentLength==this.segmentLength;
} else return false;
}
@Override
protected int[] getCrossoverPoints(int length, int numberOfCrossovers) {
int[] cPoints = new int[numberOfCrossovers];
int i=0;
while (i<numberOfCrossovers && (i<length/segmentLength)) {
cPoints[i]=segmentLength*RNG.randomInt(length/segmentLength);
i++;
}
return cPoints;
}
public String getName() {
return "GA N-Point segment-wise crossover";
}
public static String globalInfo() {
return "This is an n-point crossover between m individuals which also splits at certain segment limits. Crossover points are selected from multiples of the segment length.";
}
public int getSegmentLength() {
return segmentLength;
}
public void setSegmentLength(int segmentLength) {
this.segmentLength = segmentLength;
}
public String segmentLengthTipText() {
return "The fixed length of segments (genes) which are not split by crossover.";
}
}

View File

@ -4,6 +4,7 @@ import java.util.BitSet;
import eva2.server.go.individuals.AbstractEAIndividual;
import eva2.server.go.individuals.InterfaceGAIndividual;
import eva2.server.go.individuals.InterfaceGIIndividual;
import eva2.server.go.problems.InterfaceOptimizationProblem;
import eva2.tools.EVAERROR;
import eva2.tools.math.RNG;
@ -65,23 +66,25 @@ public class GAInitializeSegmentwise implements InterfaceInitialization, java.io
if (k<genotypeLen) genotype.set(k, nextSeg.get(k-i));
}
}
// write back the genotype (it may have been cloned, who knows...)
gaIndy.SetBGenotype(genotype);
} else { // the number of bits to set may vary from segment to segment.
if (bitsPerSegmentArray.length * segmentLength != genotypeLen) EVAERROR.errorMsgOnce("Warning, potential mismatch between segment lengths and genotype length in " + this.getClass());
if (bitsPerSegmentArray.length * segmentLength < genotypeLen) System.err.println("Warning, " + (genotypeLen - bitsPerSegmentArray.length * segmentLength) + " bits will not be initialized!");
for (int s=0; s<bitsPerSegmentArray.length; s++) {
// look at each segment individually
BitSet nextSeg=RNG.randomBitSet(bitsPerSegmentArray[s], bitsPerSegment);
for (int k=(s)*bitsPerSegment; k<(s+1)*bitsPerSegment; k++) {
if (k<genotypeLen) genotype.set(k, nextSeg.get(k-(s*bitsPerSegment)));
BitSet nextSeg=RNG.randomBitSet(bitsPerSegmentArray[s], segmentLength);
for (int k=(s)*segmentLength; k<(s+1)*segmentLength; k++) {
if (k<genotypeLen) genotype.set(k, nextSeg.get(k-(s*segmentLength)));
}
}
}
// write back the genotype (it may have been cloned, who knows...)
gaIndy.SetBGenotype(genotype);
// System.out.println(genotype.cardinality());
} else if (indy instanceof InterfaceGIIndividual) {
// TODO ADD INTEGER IMPLEMENTATION???
} else throw new RuntimeException("Error: "+ this.getClass() + " must be used with individuals of type " + InterfaceGAIndividual.class + "!");
}
public int[] getBitsPerSegmentArray() {
return bitsPerSegmentArray;
}

View File

@ -0,0 +1,219 @@
package eva2.server.go.operators.mutation;
import java.util.BitSet;
import eva2.server.go.individuals.AbstractEAIndividual;
import eva2.server.go.individuals.InterfaceGAIndividual;
import eva2.server.go.populations.Population;
import eva2.server.go.problems.InterfaceOptimizationProblem;
import eva2.tools.EVAERROR;
import eva2.tools.math.RNG;
/**
* Swap two random bits of a GA individual within subsequences (segments) of fixed length.
* If preferPairs is true, unequal pairs
* are picked with some preference (by trying for >= s/2 times, where s is the binary
* segment length).
* Multiple mutations per segment can occur if the boolean switch is activated, meaning
* that further mutations are performed recursively with p_mut. Thus, the probability
* to perform k mutations per segment is (p_mut)^k. However, more than s mutations per segment will
* never be performed.
*
* User: mkron
* Date: 05.08.2004
* Time: 17:45:36
* To change this template use File | Settings | File Templates.
*/
public class MutateGASwapBitsSegmentwise implements InterfaceMutation, java.io.Serializable {
private double mutationProbPerSegment = 0.7;
private boolean multiplesPerSegment = false;
private int segmentLength = 8;
private boolean preferPairs = true; // if true, pairs of (1,0) are swapped with higher probability
public MutateGASwapBitsSegmentwise() {
}
public MutateGASwapBitsSegmentwise(MutateGASwapBitsSegmentwise mutator) {
this.mutationProbPerSegment = mutator.mutationProbPerSegment;
this.setPreferTrueChange(mutator.isPreferTrueChange());
this.multiplesPerSegment = mutator.multiplesPerSegment;
this.segmentLength = mutator.segmentLength;
}
/**
* A constructor setting all properties.
*
* @param p_mut
* @param multPerSeg
* @param segmentLen
* @param prefPairs
*/
public MutateGASwapBitsSegmentwise(double p_mut, boolean multPerSeg, int segmentLen, boolean prefPairs) {
mutationProbPerSegment=p_mut;
multiplesPerSegment=multPerSeg;
segmentLength=segmentLen;
preferPairs=prefPairs;
}
/** This method will enable you to clone a given mutation operator
* @return The clone
*/
public Object clone() {
return new MutateGASwapBitsSegmentwise(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 MutateGASwapBitsSegmentwise) {
MutateGASwapBitsSegmentwise mut = (MutateGASwapBitsSegmentwise)mutator;
if (this.mutationProbPerSegment != mut.mutationProbPerSegment) return false;
if (this.segmentLength != mut.segmentLength) return false;
if (this.multiplesPerSegment != mut.multiplesPerSegment) return false;
if (this.preferPairs != mut.preferPairs) 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) {
}
/** 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) {
// nothing to do here
}
/** This method will mutate a given AbstractEAIndividual. If the individual
* doesn't implement InterfaceGAIndividual nothing happens.
* @param individual The individual that is to be mutated
*/
public void mutate(AbstractEAIndividual individual) {
//System.out.println("Before Mutate: " +((GAIndividual)individual).getSolutionRepresentationFor());
if (individual instanceof InterfaceGAIndividual) {
BitSet tmpBitSet = ((InterfaceGAIndividual)individual).getBGenotype();
int genLen=((InterfaceGAIndividual)individual).getGenotypeLength();
for (int i=0; i<genLen; i+=segmentLength) {
if (i+segmentLength>genLen) { // avoid to violate genotype length in a segment mutation
EVAERROR.errorMsgOnce("Warning, genotype length is not a multiple of the segment length.. ignoring last bits in " + this.getClass());
break;
}
if (RNG.flipCoin(mutationProbPerSegment)) {
int cntMutes=0;
// swap bits within a segment within certain probability
do { // this may happen multiple times depending on the settings
swapBitsInSegmentAt(tmpBitSet, i, segmentLength);
cntMutes++;
// multiples only if the corresponding switch is true and another flipCoin succeeds.
// more than segmentLength mutations will never be performed per segment
} while(multiplesPerSegment && cntMutes<segmentLength && RNG.flipCoin(mutationProbPerSegment));
}
}
((InterfaceGAIndividual)individual).SetBGenotype(tmpBitSet); // write back the genotype
}
//System.out.println("After Mutate: " +((GAIndividual)individual).getSolutionRepresentationFor());
}
/**
* Swap one pair of bits within an indicated segment.
*
* @param tmpBitSet
* @param i
* @param segmentLength2
*/
private void swapBitsInSegmentAt(BitSet bs, int i, int segLen) {
int indexOne = getRandomIndex(bs, i, segLen, true); // may prefer true bits
int indexTwo = getRandomIndex(bs, i, segLen, false); // may prefer false bits
boolean tmpBit = bs.get(indexTwo);
bs.set(indexTwo, bs.get(indexOne));
bs.set(indexOne, tmpBit);
}
private int getRandomIndex(BitSet bs, int offset, int len, boolean maybePrefered) {
int k = RNG.randomInt(offset, offset+len-1);
if (isPreferTrueChange()) {
int maxTries=(1+len)/2;
while (!(maybePrefered==bs.get(k)) && (maxTries>=0)) {
k=RNG.randomInt(offset, offset+len-1); ; // try next random position
maxTries--;
}
}
return k;
}
// private int getRandomSecondIndex(int firstIndex, AbstractEAIndividual individual) {
// int genoLen = ((InterfaceGAIndividual)individual).getGenotypeLength();
// return RNG.randomInt(0, genoLen);
// }
/** This method allows you to get a string representation of the mutation
* operator
* @return A descriptive string.
*/
public String getStringRepresentation() {
return "GA swap bits segment-wise 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 "GA swap bits segment-wise mutation";
}
/** This method returns a global info string
* @return description
*/
public static String globalInfo() {
return "This mutation operator swaps bits in subsegments of the genotype.";
}
public void setPreferTrueChange(boolean preferPairs) {
this.preferPairs = preferPairs;
}
public boolean isPreferTrueChange() {
return preferPairs;
}
public String preferTrueChangeTipText() {
return "If set to true, mutation events will prefer swapping 1 and 0";
}
public double getMutationProbPerSegment() {
return mutationProbPerSegment;
}
public void setMutationProbPerSegment(double mutationProbPerSegment) {
this.mutationProbPerSegment = mutationProbPerSegment;
}
public boolean isMultiplesPerSegment() {
return multiplesPerSegment;
}
public void setMultiplesPerSegment(boolean multiplesPerSegment) {
this.multiplesPerSegment = multiplesPerSegment;
}
public int getSegmentLength() {
return segmentLength;
}
public void setSegmentLength(int segmentLength) {
this.segmentLength = segmentLength;
}
public String segmentLengthTipText() {
return "The length of sub-segments to regard (substrings of the GA genotype)";
}
}