001: /*
002: * This file is part of JGAP.
003: *
004: * JGAP offers a dual license model containing the LGPL as well as the MPL.
005: *
006: * For licensing information please see the file license.txt included with JGAP
007: * or have a look at the top of class org.jgap.Chromosome which representatively
008: * includes the JGAP license policy applicable for any file delivered with JGAP.
009: */
010: package examples.dynamicMutation;
011:
012: import org.jgap.*;
013: import org.jgap.impl.*;
014:
015: /**
016: * Experiment on how to dynamically adapt the mutation rate for different
017: * genes. This example works with coins (see MinimizingMakeChange for
018: * documentation). The idea is that a quarter has more impact onto the solution
019: * than a penny, so a quarter should mutate less frequently, probably.
020: *
021: * @author Klaus Meffert
022: * @since 2.6
023: */
024: public class DynamicMutationExample {
025: /** String containing the CVS revision. Read out via reflection!*/
026: private final static String CVS_REVISION = "$Revision: 1.6 $";
027:
028: /**
029: * The total number of times we'll let the population evolve.
030: */
031: private static final int MAX_ALLOWED_EVOLUTIONS = 200;
032:
033: /**
034: * Executes the genetic algorithm to determine the minimum number of
035: * coins necessary to make up the given target amount of change. The
036: * solution will then be written to System.out.
037: *
038: * @param a_targetChangeAmount the target amount of change for which this
039: * method is attempting to produce the minimum number of coins
040: * @throws Exception
041: *
042: * @author Neil Rotstan
043: * @author Klaus Meffert
044: * @since 1.0
045: */
046: public static void makeChangeForAmount(int a_targetChangeAmount)
047: throws Exception {
048: // Start with a DefaultConfiguration, which comes setup with the
049: // most common settings.
050: // -------------------------------------------------------------
051: Configuration conf = new DefaultConfiguration();
052: // Add custom mutation operator
053: conf.getGeneticOperators().clear();
054: // IUniversalRateCalculator mutCalc = new CoinsMutationRateCalc();
055: TwoWayMutationOperator mutOp = new TwoWayMutationOperator(conf,
056: 7);
057: conf.addGeneticOperator(mutOp);
058: conf.addGeneticOperator(new CrossoverOperator(conf));
059: conf.setPreservFittestIndividual(!true);
060: conf.setKeepPopulationSizeConstant(false);
061: // Set the fitness function we want to use, which is our
062: // MinimizingMakeChangeFitnessFunction. We construct it with
063: // the target amount of change passed in to this method.
064: // ---------------------------------------------------------
065: FitnessFunction myFunc = new DynamicMutationFitnessFunction(
066: a_targetChangeAmount);
067: // conf.setFitnessFunction(myFunc);
068: conf
069: .setBulkFitnessFunction(new BulkFitnessOffsetRemover(
070: myFunc));
071: conf.reset();
072: conf.setFitnessEvaluator(new DeltaFitnessEvaluator());
073: // Now we need to tell the Configuration object how we want our
074: // Chromosomes to be setup. We do that by actually creating a
075: // sample Chromosome and then setting it on the Configuration
076: // object. As mentioned earlier, we want our Chromosomes to each
077: // have four genes, one for each of the coin types. We want the
078: // values (alleles) of those genes to be integers, which represent
079: // how many coins of that type we have. We therefore use the
080: // IntegerGene class to represent each of the genes. That class
081: // also lets us specify a lower and upper bound, which we set
082: // to sensible values for each coin type.
083: // --------------------------------------------------------------
084: Gene[] sampleGenes = new Gene[4];
085: sampleGenes[0] = new IntegerGene(conf, 0, 3 * 10); // Quarters
086: sampleGenes[1] = new IntegerGene(conf, 0, 2 * 10); // Dimes
087: sampleGenes[2] = new IntegerGene(conf, 0, 1 * 10); // Nickels
088: sampleGenes[3] = new IntegerGene(conf, 0, 4 * 10); // Pennies
089: IChromosome sampleChromosome = new Chromosome(conf, sampleGenes);
090: conf.setSampleChromosome(sampleChromosome);
091: // Finally, we need to tell the Configuration object how many
092: // Chromosomes we want in our population. The more Chromosomes,
093: // the larger number of potential solutions (which is good for
094: // finding the answer), but the longer it will take to evolve
095: // the population (which could be seen as bad).
096: // ------------------------------------------------------------
097: conf.setPopulationSize(80);
098: // Create random initial population of Chromosomes.
099: // Here we try to read in a previous run via XMLManager.readFile(..)
100: // for demonstration purpose!
101: // -----------------------------------------------------------------
102: Genotype population;
103: // Initialize the population randomly
104: population = Genotype.randomInitialGenotype(conf);
105: // Evolve the population. Since we don't know what the best answer
106: // is going to be, we just evolve the max number of times.
107: // ---------------------------------------------------------------
108: for (int i = 0; i < MAX_ALLOWED_EVOLUTIONS; i++) {
109: population.evolve();
110: }
111: // Display the best solution we found.
112: // -----------------------------------
113: IChromosome bestSolutionSoFar = population
114: .getFittestChromosome();
115: System.out.println("The best solution has a fitness value of "
116: + bestSolutionSoFar.getFitnessValue());
117: System.out.println("It contained the following: ");
118: System.out.println("\t"
119: + DynamicMutationFitnessFunction
120: .getNumberOfCoinsAtGene(bestSolutionSoFar, 0)
121: + " quarters.");
122: System.out.println("\t"
123: + DynamicMutationFitnessFunction
124: .getNumberOfCoinsAtGene(bestSolutionSoFar, 1)
125: + " dimes.");
126: System.out.println("\t"
127: + DynamicMutationFitnessFunction
128: .getNumberOfCoinsAtGene(bestSolutionSoFar, 2)
129: + " nickels.");
130: System.out.println("\t"
131: + DynamicMutationFitnessFunction
132: .getNumberOfCoinsAtGene(bestSolutionSoFar, 3)
133: + " pennies.");
134: System.out.println("For a total of "
135: + DynamicMutationFitnessFunction
136: .amountOfChange(bestSolutionSoFar)
137: + " cents in "
138: + DynamicMutationFitnessFunction
139: .getTotalNumberOfCoins(bestSolutionSoFar)
140: + " coins.");
141: }
142:
143: /**
144: * Main method. A single command-line argument is expected, which is the
145: * amount of change to create (in other words, 75 would be equal to 75
146: * cents).
147: *
148: * @param args amount of change in cents to create
149: * @throws Exception
150: *
151: * @author Neil Rotstan
152: * @author Klaus Meffert
153: * @since 1.0
154: */
155: public static void main(String[] args) throws Exception {
156: if (args.length != 1) {
157: System.out
158: .println("Syntax: DynamicMutationExample <amount>");
159: } else {
160: int amount = 0;
161: try {
162: amount = Integer.parseInt(args[0]);
163: } catch (NumberFormatException e) {
164: System.out
165: .println("The <amount> argument must be a valid integer value");
166: System.exit(1);
167: }
168: if (amount < 1
169: || amount >= DynamicMutationFitnessFunction.MAX_BOUND) {
170: System.out
171: .println("The <amount> argument must be between 1 and "
172: + (DynamicMutationFitnessFunction.MAX_BOUND - 1)
173: + ".");
174: } else {
175: makeChangeForAmount(amount);
176: }
177: }
178: }
179:
180: /**
181: * This class only is an experiment!
182: *
183: * @author Klaus Meffert
184: * @since 2.6
185: */
186: public static class CoinsMutationRateCalc implements
187: IUniversalRateCalculator {
188: private int m_evolution;
189:
190: private double m_rate0 = 0.2d;
191:
192: private double m_rate1 = 0.6d;
193:
194: private double m_rate2 = 0.7d;
195:
196: private double m_rate3 = 1.0d;
197:
198: public int calculateCurrentRate() {
199: int size;
200: size = 15;
201: if (size < 1) {
202: size = 1;
203: }
204: return size;
205: }
206:
207: public boolean toBePermutated(IChromosome a_chrom,
208: int a_geneIndex) {
209: RandomGenerator generator = a_chrom.getConfiguration()
210: .getRandomGenerator();
211: double mult = 0.0d;
212: switch (a_geneIndex) {
213: case 0:
214: mult = get(0);
215: break;
216: case 1:
217: mult = m_rate1;
218: break;
219: case 2:
220: mult = m_rate2;
221: break;
222: case 3:
223: mult = m_rate3;
224: m_evolution++;
225: break;
226: }
227: return (generator.nextDouble() < (1 / calculateCurrentRate())
228: * mult);
229: }
230:
231: private double get(int a_index) {
232: if (m_evolution > 90) {
233: m_rate0 = 1.0d;
234: } else if (m_evolution > 60) {
235: m_rate0 = 0.75d;
236: } else if (m_evolution > 30) {
237: m_rate0 = 0.5d;
238: } else if (m_evolution > 15) {
239: m_rate0 = 0.4d;
240: }
241: return m_rate0;
242: }
243: }
244: }
|