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 org.jgap.impl;
011:
012: import java.util.*;
013: import org.jgap.*;
014:
015: /**
016: * A Gene implementation that supports an integer values for its allele.
017: * Upper and lower bounds may optionally be provided to restrict the range
018: * of legal values allowed by this Gene instance.
019: *
020: * @author Neil Rotstan
021: * @author Klaus Meffert
022: * @since 1.0
023: */
024: public class IntegerGene extends NumberGene implements
025: IPersistentRepresentation {
026: /** String containing the CVS revision. Read out via reflection!*/
027: private static final String CVS_REVISION = "$Revision: 1.45 $";
028:
029: /**
030: * Represents the constant range of values supported by integers.
031: */
032: protected final static long INTEGER_RANGE = (long) Integer.MAX_VALUE
033: - (long) Integer.MIN_VALUE;
034:
035: /**
036: * The upper bounds of values represented by this Gene. If not explicitly
037: * provided by the user, this should be set to Integer.MAX_VALUE.
038: */
039: private int m_upperBounds;
040:
041: /**
042: * The lower bounds of values represented by this Gene. If not explicitly
043: * provided by the user, this should be set to Integer.MIN_VALUE
044: */
045: private int m_lowerBounds;
046:
047: /**
048: * Constructs a new IntegerGene with default settings. No bounds will
049: * be put into effect for values (alleles) of this Gene instance, other
050: * than the standard range of integer values.<p>
051: * Attention: The configuration used is the one set with the static method
052: * Genotype.setConfiguration.
053: *
054: * @throws InvalidConfigurationException
055: *
056: * @author Neil Rostan
057: * @author Klaus Meffert
058: * @since 1.0
059: */
060: public IntegerGene() throws InvalidConfigurationException {
061: this (Genotype.getStaticConfiguration());
062: }
063:
064: /**
065: * Constructs a new IntegerGene with default settings. No bounds will
066: * be put into effect for values (alleles) of this Gene instance, other
067: * than the standard range of integer values.
068: *
069: * @param a_config the configuration to use
070: * @throws InvalidConfigurationException
071: *
072: * @author Klaus Meffert
073: * @since 3.0
074: */
075: public IntegerGene(final Configuration a_config)
076: throws InvalidConfigurationException {
077: this (a_config, Integer.MIN_VALUE, Integer.MAX_VALUE);
078: }
079:
080: /**
081: * Constructs a new IntegerGene with the specified lower and upper
082: * bounds for values (alleles) of this Gene instance.
083: *
084: * @param a_config the configuration to use
085: * @param a_lowerBounds the lowest value that this Gene may possess,
086: * inclusive
087: * @param a_upperBounds the highest value that this Gene may possess,
088: * inclusive
089: * @throws InvalidConfigurationException
090: *
091: * @author Klaus Meffert
092: * @since 2.0
093: */
094: public IntegerGene(final Configuration a_config,
095: final int a_lowerBounds, final int a_upperBounds)
096: throws InvalidConfigurationException {
097: super (a_config);
098: m_lowerBounds = a_lowerBounds;
099: m_upperBounds = a_upperBounds;
100: }
101:
102: /**
103: * Provides implementation-independent means for creating new Gene
104: * instances.
105: *
106: * @return a new Gene instance of the same type and with the same setup as
107: * this concrete Gene
108: *
109: * @author Klaus Meffert
110: * @since 2.6 (was newGene since 1.0, moved to BaseGene)
111: */
112: protected Gene newGeneInternal() {
113: try {
114: IntegerGene result = new IntegerGene(getConfiguration(),
115: m_lowerBounds, m_upperBounds);
116: return result;
117: } catch (InvalidConfigurationException iex) {
118: throw new IllegalStateException(iex.getMessage());
119: }
120: }
121:
122: /**
123: * Retrieves a string representation of this Gene that includes any
124: * information required to reconstruct it at a later time, such as its
125: * value and internal state. This string will be used to represent this
126: * Gene in XML persistence. This is an optional method but, if not
127: * implemented, XML persistence and possibly other features will not be
128: * available. An UnsupportedOperationException should be thrown if no
129: * implementation is provided.
130: *
131: * @return string representation of this Gene's current state
132: *
133: * @author Neil Rostan
134: * @since 1.0
135: */
136: public String getPersistentRepresentation() {
137: // The persistent representation includes the value, lower bound,
138: // and upper bound. Each is separated by a colon.
139: // --------------------------------------------------------------
140: String s;
141: if (getInternalValue() == null) {
142: s = "null";
143: } else {
144: s = getInternalValue().toString();
145: }
146: return s + PERSISTENT_FIELD_DELIMITER + m_lowerBounds
147: + PERSISTENT_FIELD_DELIMITER + m_upperBounds;
148: }
149:
150: /**
151: * Sets the value and internal state of this Gene from the string
152: * representation returned by a previous invocation of the
153: * getPersistentRepresentation() method. This is an optional method but,
154: * if not implemented, XML persistence and possibly other features will not
155: * be available. An UnsupportedOperationException should be thrown if no
156: * implementation is provided.
157: *
158: * @param a_representation the string representation retrieved from a
159: * prior call to the getPersistentRepresentation() method
160: *
161: * @throws UnsupportedOperationException to indicate that no implementation
162: * is provided for this method
163: * @throws UnsupportedRepresentationException if this Gene implementation
164: * does not support the given string representation
165: *
166: * @author Neil Rostan
167: * @since 1.0
168: */
169: public void setValueFromPersistentRepresentation(
170: final String a_representation)
171: throws UnsupportedRepresentationException {
172: if (a_representation != null) {
173: StringTokenizer tokenizer = new StringTokenizer(
174: a_representation, PERSISTENT_FIELD_DELIMITER);
175: // Make sure the representation contains the correct number of
176: // fields. If not, throw an exception.
177: // -----------------------------------------------------------
178: if (tokenizer.countTokens() != 3) {
179: throw new UnsupportedRepresentationException(
180: "The format of the given persistent representation "
181: + " is not recognized: it does not contain three tokens: "
182: + a_representation);
183: }
184: String valueRepresentation = tokenizer.nextToken();
185: String lowerBoundRepresentation = tokenizer.nextToken();
186: String upperBoundRepresentation = tokenizer.nextToken();
187: // First parse and set the representation of the value.
188: // ----------------------------------------------------
189: if (valueRepresentation.equals("null")) {
190: setAllele(null);
191: } else {
192: try {
193: setAllele(new Integer(Integer
194: .parseInt(valueRepresentation)));
195: } catch (NumberFormatException e) {
196: throw new UnsupportedRepresentationException(
197: "The format of the given persistent representation "
198: + "is not recognized: field 1 does not appear to be "
199: + "an integer value.");
200: }
201: }
202: // Now parse and set the lower bound.
203: // ----------------------------------
204: try {
205: m_lowerBounds = Integer
206: .parseInt(lowerBoundRepresentation);
207: } catch (NumberFormatException e) {
208: throw new UnsupportedRepresentationException(
209: "The format of the given persistent representation "
210: + "is not recognized: field 2 does not appear to be "
211: + "an integer value.");
212: }
213: // Now parse and set the upper bound.
214: // ----------------------------------
215: try {
216: m_upperBounds = Integer
217: .parseInt(upperBoundRepresentation);
218: } catch (NumberFormatException e) {
219: throw new UnsupportedRepresentationException(
220: "The format of the given persistent representation "
221: + "is not recognized: field 3 does not appear to be "
222: + "an integer value.");
223: }
224: }
225: }
226:
227: /**
228: * Retrieves the int value of this Gene, which may be more convenient in
229: * some cases than the more general getAllele() method.
230: *
231: * @return the int value of this Gene
232: *
233: * @author Neil Rostan
234: * @since 1.0
235: */
236: public int intValue() {
237: return ((Integer) getAllele()).intValue();
238: }
239:
240: /**
241: * Sets the value (allele) of this Gene to a random Integer value between
242: * the lower and upper bounds (if any) of this Gene.
243: *
244: * @param a_numberGenerator the random number generator that should be
245: * used to create any random values. It's important to use this generator to
246: * maintain the user's flexibility to configure the genetic engine to use the
247: * random number generator of their choice
248: *
249: * @author Neil Rostan
250: * @author Klaus Meffert
251: * @since 1.0
252: */
253: public void setToRandomValue(final RandomGenerator a_numberGenerator) {
254: double randomValue = (m_upperBounds - m_lowerBounds)
255: * a_numberGenerator.nextDouble() + m_lowerBounds;
256: setAllele(new Integer((int) Math.round(randomValue)));
257: }
258:
259: /**
260: * Compares to objects by first casting them into their expected type
261: * (e.g. Integer for IntegerGene) and then calling the compareTo-method
262: * of the casted type.
263: * @param a_o1 first object to be compared, always is not null
264: * @param a_o2 second object to be compared, always is not null
265: * @return a negative integer, zero, or a positive integer as this object
266: * is less than, equal to, or greater than the object provided for comparison
267: *
268: * @author Neil Rostan
269: * @since 1.0
270: */
271: protected int compareToNative(final Object a_o1, final Object a_o2) {
272: return ((Integer) a_o1).compareTo((Integer) a_o2);
273: }
274:
275: /**
276: * Maps the value of this IntegerGene to within the bounds specified by
277: * the m_upperBounds and m_lowerBounds instance variables. The value's
278: * relative position within the integer range will be preserved within the
279: * bounds range (in other words, if the value is about halfway between the
280: * integer max and min, then the resulting value will be about halfway
281: * between the upper bounds and lower bounds). If the value is null or
282: * is already within the bounds, it will be left unchanged.
283: *
284: * @author Neil Rostan
285: * @author Klaus Meffert
286: * @since 1.0
287: */
288: protected void mapValueToWithinBounds() {
289: if (getAllele() != null) {
290: Integer i_value = ((Integer) getAllele());
291: // If the value exceeds either the upper or lower bounds, then
292: // map the value to within the legal range. To do this, we basically
293: // calculate the distance between the value and the integer min,
294: // determine how many bounds units that represents, and then add
295: // that number of units to the upper bound.
296: // -----------------------------------------------------------------
297: if (i_value.intValue() > m_upperBounds
298: || i_value.intValue() < m_lowerBounds) {
299: RandomGenerator rn;
300: if (getConfiguration() != null) {
301: rn = getConfiguration().getRandomGenerator();
302: } else {
303: rn = new StockRandomGenerator();
304: }
305: if (m_upperBounds - m_lowerBounds == 0) {
306: setAllele(new Integer(m_lowerBounds));
307: } else {
308: setAllele(new Integer(rn.nextInt(m_upperBounds
309: - m_lowerBounds)
310: + m_lowerBounds));
311: }
312: }
313: }
314: }
315:
316: /**
317: * See interface Gene for description.
318: * @param a_index ignored (because there is only 1 atomic element)
319: * @param a_percentage percentage of mutation (greater than -1 and smaller
320: * than 1)
321: *
322: * @author Klaus Meffert
323: * @since 1.1
324: */
325: public void applyMutation(final int a_index,
326: final double a_percentage) {
327: double range = (m_upperBounds - m_lowerBounds) * a_percentage;
328: if (getAllele() == null) {
329: setAllele(new Integer((int) range + m_lowerBounds));
330: } else {
331: int newValue = (int) Math.round(intValue() + range);
332: setAllele(new Integer(newValue));
333: }
334: }
335:
336: /**
337: * Modified hashCode() function to return different hashcodes for differently
338: * ordered genes in a chromosome.
339: * @return -1 if no allele set, otherwise value return by BaseGene.hashCode()
340: *
341: * @author Klaus Meffert
342: * @since 2.2
343: */
344: public int hashCode() {
345: if (getInternalValue() == null) {
346: return -1;
347: } else {
348: return super .hashCode();
349: }
350: }
351:
352: /**
353: * @return string representation of this Gene's value that may be useful for
354: * display purposes
355: *
356: * @author Klaus Meffert
357: * @since 2.4
358: */
359: public String toString() {
360: String s = "IntegerGene(" + m_lowerBounds + "," + m_upperBounds
361: + ")" + "=";
362: if (getInternalValue() == null) {
363: s += "null";
364: } else {
365: s += getInternalValue().toString();
366: }
367: return s;
368: }
369:
370: /**
371: * @return the lower bounds of the integer gene
372: *
373: * @author Klaus Meffert
374: * @since 2.6
375: */
376: public int getLowerBounds() {
377: return m_lowerBounds;
378: }
379:
380: /**
381: * @return the upper bounds of the integer gene
382: *
383: * @author Klaus Meffert
384: * @since 2.6
385: */
386: public int getUpperBounds() {
387: return m_upperBounds;
388: }
389: }
|