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.supergene;
011:
012: import org.jgap.*;
013: import org.jgap.impl.*;
014:
015: /**
016: * Abstract class for testing Supergene performance.
017: *
018: * @author Neil Rotstan
019: * @author Klaus Meffert
020: * @author Audrius Meskauskas
021: * @since 2.0
022: * */
023: public abstract class AbstractSupergeneTest {
024: /** String containing the CVS revision. Read out via reflection!*/
025: private static final String CVS_REVISION = "$Revision: 1.5 $";
026:
027: /**
028: * Gene index for the dimes gene
029: */
030: public static final int DIMES = 0;
031:
032: /**
033: * Gene index for the quarters gene.
034: */
035: public static final int QUARTERS = 1;
036:
037: /**
038: * Gene index for the nickels gene
039: * Only used in the alternative presentation */
040: public static final int NICKELS = 2;
041:
042: /**
043: * Gene index for the pennies gene.
044: * Only used in the alternative presentation */
045: public static final int PENNIES = 3;
046:
047: /**
048: * The total number of times we'll let the population evolve.
049: */
050: public static int MAX_ALLOWED_EVOLUTIONS = 200;
051:
052: /**
053: * Chromosome size.
054: */
055: public static int POPULATION_SIZE = 2000;
056:
057: public static boolean REPORT_ENABLED = true;
058:
059: /**
060: * @param a_conf the configuration to use
061: *
062: * @return created Dimes gene instance
063: */
064: protected Gene getDimesGene(Configuration a_conf) {
065: try {
066: return new IntegerGene(a_conf, 0, 2); // 10?
067: } catch (InvalidConfigurationException iex) {
068: throw new IllegalStateException(iex.getMessage());
069: }
070: };
071:
072: /**
073: * @param a_conf the configuration to use
074: *
075: * @return created Nickels gene instance
076: */
077: protected Gene getNickelsGene(Configuration a_conf) {
078: try {
079: return new IntegerGene(a_conf, 0, 5);
080: } catch (InvalidConfigurationException iex) {
081: throw new IllegalStateException(iex.getMessage());
082: }
083: }
084:
085: /**
086: * @param a_conf the configuration to use
087: *
088: * @return created Pennies (1) gene instance
089: */
090: protected Gene getPenniesGene(Configuration a_conf) {
091: try {
092: return new IntegerGene(a_conf, 0, 7);
093: } catch (InvalidConfigurationException iex) {
094: throw new IllegalStateException(iex.getMessage());
095: }
096: }
097:
098: /**
099: * @param a_conf the configuration to use
100: *
101: * @return created Quarters gene instance
102: */
103: protected Gene getQuartersGene(Configuration a_conf) {
104: try {
105: return new IntegerGene(a_conf, 0, 3);
106: } catch (InvalidConfigurationException iex) {
107: throw new IllegalStateException(iex.getMessage());
108: }
109: }
110:
111: /** Compute the money value from the coin information. */
112: public static int amountOfChange(int a_numQuarters, int a_numDimes,
113: int a_numNickels, int a_numPennies) {
114: return (a_numQuarters * 25) + (a_numDimes * 10)
115: + (a_numNickels * 5) + a_numPennies;
116: };
117:
118: /**
119: * Executes the genetic algorithm to determine the minimum number of
120: * coins necessary to make up the given target amount of change. The
121: * solution will then be written to System.out.
122: *
123: * @param a_targetChangeAmount the target amount of change for which this
124: * method is attempting to produce the minimum number of coins
125: *
126: * @return absolute difference between the required and computed change
127: * amount
128: * @throws Exception
129: */
130: public abstract int makeChangeForAmount(int a_targetChangeAmount)
131: throws Exception;
132:
133: /**
134: * Write report on eveluation to the given stream.
135: * @param a_fitnessFunction p_SupergeneChangeFitnessFunction
136: * @param a_population Genotype
137: * @return Chromosome
138: */
139: public IChromosome report(
140: SupergeneChangeFitnessFunction a_fitnessFunction,
141: Genotype a_population) {
142: IChromosome bestSolutionSoFar = a_population
143: .getFittestChromosome();
144: if (!REPORT_ENABLED) {
145: return bestSolutionSoFar;
146: }
147: System.out
148: .println("\nThe best solution has a fitness value of "
149: + bestSolutionSoFar.getFitnessValue());
150: System.out.println("It contained the following: ");
151: System.out.println("\t"
152: + a_fitnessFunction.getNumberOfCoinsAtGene(
153: bestSolutionSoFar, QUARTERS) + " quarters.");
154: System.out.println("\t"
155: + a_fitnessFunction.getNumberOfCoinsAtGene(
156: bestSolutionSoFar, DIMES) + " dimes.");
157: System.out.println("\t"
158: + a_fitnessFunction.getNumberOfCoinsAtGene(
159: bestSolutionSoFar, NICKELS) + " nickels.");
160: System.out.println("\t"
161: + a_fitnessFunction.getNumberOfCoinsAtGene(
162: bestSolutionSoFar, PENNIES) + " pennies.");
163: System.out.println("For a total of "
164: + a_fitnessFunction.amountOfChange(bestSolutionSoFar)
165: + " cents in "
166: + a_fitnessFunction
167: .getTotalNumberOfCoins(bestSolutionSoFar)
168: + " coins.");
169: return bestSolutionSoFar;
170: }
171:
172: /**
173: * If set to true (required for strict tests), only tasks with existing
174: * solutions will be submitted as a test tasks.
175: */
176: public static boolean EXISTING_SOLUTIONS_ONLY = false;
177:
178: /**
179: * Test the method, returns the sum of all differences between
180: * the required and obtained excange amount. One exception counts
181: * as 1000 on the error score.
182: */
183: public int test() {
184: int s = 0;
185: int e;
186: for (int amount = 20; amount < 100; amount++) {
187: try {
188: if (REPORT_ENABLED) {
189: System.out.println("EXCHANGING " + amount + " ");
190: }
191: // Do not solve cases without solutions
192: if (EXISTING_SOLUTIONS_ONLY) {
193: if (!Force.solve(amount)) {
194: continue;
195: }
196: }
197: // Need to reset the configuration because it needs to be changed each
198: // time when looping.
199: // -------------------------------------------------------------------
200: DefaultConfiguration.reset();
201: e = makeChangeForAmount(amount);
202: if (REPORT_ENABLED) {
203: System.out.println(" err " + e);
204: System.out.println("---------------");
205: }
206: s = s + e;
207: } catch (Exception ex) {
208: ex.printStackTrace();
209: s += 1000;
210: }
211: }
212: if (REPORT_ENABLED) {
213: System.out.println("Sum of errors " + s);
214: }
215: return s;
216: }
217:
218: /**
219: * Find and print the solution, return the solution error.
220: *
221: * @param a_conf the configuration to use
222: *
223: * @return absolute difference between the required and computed change
224: */
225: protected int solve(Configuration a_conf, int a_targetChangeAmount,
226: SupergeneChangeFitnessFunction a_fitnessFunction,
227: Gene[] a_sampleGenes) throws InvalidConfigurationException {
228: IChromosome sampleChromosome = new Chromosome(a_conf,
229: a_sampleGenes);
230: a_conf.setSampleChromosome(sampleChromosome);
231: // Finally, we need to tell the Configuration object how many
232: // Chromosomes we want in our population. The more Chromosomes,
233: // the larger number of potential solutions (which is good for
234: // finding the answer), but the longer it will take to evolve
235: // the population (which could be seen as bad). We'll just set
236: // the population size to 500 here.
237: // ------------------------------------------------------------
238: a_conf.setPopulationSize(POPULATION_SIZE);
239: // Create random initial population of Chromosomes.
240: // ------------------------------------------------
241: Genotype population = Genotype.randomInitialGenotype(a_conf);
242: int s;
243: Evolution:
244: // Evolve the population, break if the the change solution is found.
245: // -----------------------------------------------------------------
246: for (int i = 0; i < MAX_ALLOWED_EVOLUTIONS; i++) {
247: population.evolve();
248: s = Math.abs(a_fitnessFunction.amountOfChange(population
249: .getFittestChromosome())
250: - a_targetChangeAmount);
251: if (s == 0) {
252: break Evolution;
253: }
254: }
255: // Display the best solution we found.
256: // -----------------------------------
257: IChromosome bestSolutionSoFar = report(a_fitnessFunction,
258: population);
259: return Math.abs(a_fitnessFunction
260: .amountOfChange(bestSolutionSoFar)
261: - a_targetChangeAmount);
262: }
263: }
|