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:
014: /**
015: * Sample fitness function for the DynamicMutation example.
016: *
017: * @author Klaus Meffert
018: * @since 2.6
019: */
020: public class DynamicMutationFitnessFunction extends FitnessFunction {
021: /** String containing the CVS revision. Read out via reflection!*/
022: private final static String CVS_REVISION = "$Revision: 1.4 $";
023:
024: private final int m_targetAmount;
025:
026: public static final int MAX_BOUND = 4000;
027:
028: public DynamicMutationFitnessFunction(int a_targetAmount) {
029: if (a_targetAmount < 1 || a_targetAmount >= MAX_BOUND) {
030: throw new IllegalArgumentException(
031: "Change amount must be between 1 and " + MAX_BOUND
032: + " cents.");
033: }
034: m_targetAmount = a_targetAmount;
035: }
036:
037: /**
038: * Determine the fitness of the given Chromosome instance. The higher the
039: * return value, the more fit the instance. This method should always
040: * return the same fitness value for two equivalent Chromosome instances.
041: *
042: * @param a_subject the Chromosome instance to evaluate
043: *
044: * @return positive double reflecting the fitness rating of the given
045: * Chromosome
046: * @since 2.0 (until 1.1: return type int)
047: * @author Neil Rotstan, Klaus Meffert, John Serri
048: */
049: public double evaluate(IChromosome a_subject) {
050: // Take care of the fitness evaluator. It could either be weighting higher
051: // fitness values higher (e.g.DefaultFitnessEvaluator). Or it could weight
052: // lower fitness values higher, because the fitness value is seen as a
053: // defect rate (e.g. DeltaFitnessEvaluator)
054: boolean defaultComparation = a_subject.getConfiguration()
055: .getFitnessEvaluator().isFitter(2, 1);
056:
057: // The fitness value measures both how close the value is to the
058: // target amount supplied by the user and the total number of coins
059: // represented by the solution. We do this in two steps: first,
060: // we consider only the represented amount of change vs. the target
061: // amount of change and return higher fitness values for amounts
062: // closer to the target, and lower fitness values for amounts further
063: // away from the target. Then we go to step 2, which returns a higher
064: // fitness value for solutions representing fewer total coins, and
065: // lower fitness values for solutions representing more total coins.
066: // ------------------------------------------------------------------
067: int changeAmount = amountOfChange(a_subject);
068: int totalCoins = getTotalNumberOfCoins(a_subject);
069: int changeDifference = Math.abs(m_targetAmount - changeAmount);
070: double fitness;
071: if (defaultComparation) {
072: fitness = 0.0d;
073: } else {
074: fitness = MAX_BOUND / 2;
075: }
076: // Step 1: Determine distance of amount represented by solution from
077: // the target amount. If the change difference is greater than zero we
078: // will divide one by the difference in change between the
079: // solution amount and the target amount. That will give the desired
080: // effect of returning higher values for amounts closer to the target
081: // amount and lower values for amounts further away from the target
082: // amount.
083: // In the case where the change difference is zero it means that we have
084: // the correct amount and we assign a higher fitness value.
085: // ---------------------------------------------------------------------
086: if (defaultComparation) {
087: fitness += changeDifferenceBonus(MAX_BOUND / 2,
088: changeDifference);
089: } else {
090: fitness -= changeDifferenceBonus(MAX_BOUND / 2,
091: changeDifference);
092: }
093: // Step 2: We divide the fitness value by a penalty based on the number of
094: // coins. The higher the number of coins the higher the penalty and the
095: // smaller the fitness value.
096: // And inversely the smaller number of coins in the solution the higher
097: // the resulting fitness value.
098: // -----------------------------------------------------------------------
099: if (defaultComparation) {
100: fitness -= computeCoinNumberPenalty(MAX_BOUND / 2,
101: totalCoins);
102: } else {
103: fitness += computeCoinNumberPenalty(MAX_BOUND / 2,
104: totalCoins);
105: }
106: // Make sure fitness value is always positive.
107: // -------------------------------------------
108: return Math.max(1.0d, fitness);
109: }
110:
111: /**
112: * Bonus calculation of fitness value.
113: * @param a_maxFitness maximum fitness value appliable
114: * @param a_changeDifference change difference in coins for the coins problem
115: * @return bonus for given change difference
116: *
117: * @author Klaus Meffert
118: * @since 2.3
119: */
120: protected double changeDifferenceBonus(double a_maxFitness,
121: int a_changeDifference) {
122: if (a_changeDifference == 0) {
123: return a_maxFitness;
124: } else {
125: // we arbitrarily work with half of the maximum fitness as basis for non-
126: // optimal solutions (concerning change difference)
127: if (a_changeDifference * a_changeDifference >= a_maxFitness / 2) {
128: return 0.0d;
129: } else {
130: return a_maxFitness / 2 - a_changeDifference
131: * a_changeDifference;
132: }
133: }
134: }
135:
136: /**
137: * Calculates the penalty to apply to the fitness value based on the ammount
138: * of coins in the solution
139: *
140: * @param a_maxFitness maximum fitness value allowed
141: * @param a_coins number of coins in the solution
142: * @return penalty for the fitness value base on the number of coins
143: *
144: * @author John Serri
145: * @since 2.2
146: */
147: protected double computeCoinNumberPenalty(double a_maxFitness,
148: int a_coins) {
149: if (a_coins == 1) {
150: // we know the solution cannot have less than one coin
151: return 0;
152: } else {
153: // The more coins the more penalty, but not more than the maximum fitness
154: // value possible. Let's avoid linear behavior and use
155: // exponential penalty calculation instead
156: return (Math.min(a_maxFitness, a_coins * a_coins));
157: }
158: }
159:
160: /**
161: * Calculates the total amount of change (in cents) represented by
162: * the given potential solution and returns that amount.
163: *
164: * @param a_potentialSolution the potential solution to evaluate
165: * @return The total amount of change (in cents) represented by the
166: * given solution
167: *
168: * @author Neil Rotstan
169: * @since 1.0
170: */
171: public static int amountOfChange(IChromosome a_potentialSolution) {
172: int numQuarters = getNumberOfCoinsAtGene(a_potentialSolution, 0);
173: int numDimes = getNumberOfCoinsAtGene(a_potentialSolution, 1);
174: int numNickels = getNumberOfCoinsAtGene(a_potentialSolution, 2);
175: int numPennies = getNumberOfCoinsAtGene(a_potentialSolution, 3);
176: return (numQuarters * 25) + (numDimes * 10) + (numNickels * 5)
177: + numPennies;
178: }
179:
180: /**
181: * Retrieves the number of coins represented by the given potential
182: * solution at the given gene position.
183: *
184: * @param a_potentialSolution the potential solution to evaluate
185: * @param a_position the gene position to evaluate
186: * @return the number of coins represented by the potential solution at the
187: * given gene position
188: *
189: * @author Neil Rotstan
190: * @since 1.0
191: */
192: public static int getNumberOfCoinsAtGene(
193: IChromosome a_potentialSolution, int a_position) {
194: Integer numCoins = (Integer) a_potentialSolution.getGene(
195: a_position).getAllele();
196: return numCoins.intValue();
197: }
198:
199: /**
200: * Returns the total number of coins represented by all of the genes in
201: * the given potential solution.
202: *
203: * @param a_potentialsolution the potential solution to evaluate
204: * @return total number of coins represented by the given Chromosome
205: *
206: * @author Neil Rotstan
207: * @since 1.0
208: */
209: public static int getTotalNumberOfCoins(
210: IChromosome a_potentialsolution) {
211: int totalCoins = 0;
212: int numberOfGenes = a_potentialsolution.size();
213: for (int i = 0; i < numberOfGenes; i++) {
214: totalCoins += getNumberOfCoinsAtGene(a_potentialsolution, i);
215: }
216: return totalCoins;
217: }
218: }
|