/*
* This file is part of JGAP.
*
* JGAP offers a dual license model containing the LGPL as well as the MPL.
*
* For licensing information please see the file license.txt included with JGAP
* or have a look at the top of class org.jgap.Chromosome which representatively
* includes the JGAP license policy applicable for any file delivered with JGAP.
*/
package org.jgap.impl;
import java.util.*;
import org.jgap.*;
/**
* Extension of IntegerGene. The only difference is that this gene allele has to
* be a multiple of a specific significance, ie
*
* Gene gene = new MutipleIntegerGene(configuration, -100,100,3);
*
* The genes allele must be between -100 and 100 and be a multiple of 3.
*
* @author Chris Jones
* @since 3.5
*/
public class MutipleIntegerGene
extends NumberGene implements IPersistentRepresentation {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.2 $";
/**
* The upper bounds of values represented by this Gene. If not explicitly
* provided by the user, this should be set to Integer.MAX_VALUE.
*/
private int m_upperBounds;
/**
* The lower bounds of values represented by this Gene. If not explicitly
* provided by the user, this should be set to Integer.MIN_VALUE
*/
private int m_lowerBounds;
/**
* The lower bounds of values represented by this Gene. If not explicitly
* provided by the user, this should be set to Integer.MIN_VALUE
*/
private int m_significance;
/**
* Represents the constant range of values supported by integers.
*/
protected final static long INTEGER_RANGE = (long) Integer.MAX_VALUE -
(long) Integer.MIN_VALUE;
public MutipleIntegerGene()
throws InvalidConfigurationException {
this(Genotype.getStaticConfiguration());
}
/**
* Constructs a new IntegerGene with default settings. No bounds will
* be put into effect for values (alleles) of this Gene instance, other
* than the standard range of Integer values.
*
* @param a_config the configuration to use
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.5
*/
public MutipleIntegerGene(final Configuration a_config)
throws InvalidConfigurationException {
this(a_config, - (Integer.MAX_VALUE / 2), Integer.MAX_VALUE / 2, 1);
}
/**
* Constructs a new IntegerGene with the specified lower and upper
* bounds for values (alleles) of this Gene instance.
*
* @param a_config the configuration to use
* @param a_lowerBound the lowest value that this Gene may possess,
* inclusively
* @param a_upperBound the highest value that this Gene may possess,
* inclusively
* @throws InvalidConfigurationException
*
* @author Chris Johnes
* @since 3.5
*/
public MutipleIntegerGene(final Configuration a_config,
final int a_lowerBound, final int a_upperBound,
final int a_significance)
throws InvalidConfigurationException {
super(a_config);
m_lowerBounds = a_lowerBound;
m_upperBounds = a_upperBound;
m_significance = a_significance;
if(m_upperBounds - m_lowerBounds < m_significance) {
int test = round((m_upperBounds+m_lowerBounds)/2,m_significance);
if(test < m_lowerBounds || test > m_upperBounds) {
throw new IllegalArgumentException(
"Lower and upper bound do not match significance!");
}
}
}
/**
* Provides implementation-independent means for creating new Gene
* instances.
*
* @return a new Gene instance of the same type and with the same setup as
* this concrete Gene
*
* @author Chris Jones
* @since 3.5
*/
protected Gene newGeneInternal() {
try {
MutipleIntegerGene result = new MutipleIntegerGene(getConfiguration(),
m_lowerBounds,
m_upperBounds, m_significance);
return result;
} catch (InvalidConfigurationException iex) {
throw new IllegalStateException(iex.getMessage());
}
}
/**
* Retrieves a string representation of this Gene that includes any
* information required to reconstruct it at a later time, such as its
* value and internal state. This string will be used to represent this
* Gene in XML persistence. This is an optional method but, if not
* implemented, XML persistence and possibly other features will not be
* available. An UnsupportedOperationException should be thrown if no
* implementation is provided.
*
* @return string representation of this Gene's current state
*
* @author Chris Jones
* @since 3.5
*/
public String getPersistentRepresentation() {
// The persistent representation includes the value, lower bound,
// and upper bound. Each is separated by a colon.
// --------------------------------------------------------------
String s;
if (getInternalValue() == null) {
s = "null";
}
else {
s = getInternalValue().toString();
}
return s + PERSISTENT_FIELD_DELIMITER + m_lowerBounds +
PERSISTENT_FIELD_DELIMITER + m_upperBounds + PERSISTENT_FIELD_DELIMITER +
m_significance;
}
/**
* Sets the value and internal state of this Gene from the string
* representation returned by a previous invocation of the
* getPersistentRepresentation() method. This is an optional method but,
* if not implemented, XML persistence and possibly other features will not
* be available. An UnsupportedOperationException should be thrown if no
* implementation is provided.
*
* @param a_representation the string representation retrieved from a
* prior call to the getPersistentRepresentation() method
*
* @throws UnsupportedOperationException to indicate that no implementation
* is provided for this method
* @throws UnsupportedRepresentationException if this Gene implementation
* does not support the given string representation
*
* @author Chris Jones
* @since 3.5
*/
public void setValueFromPersistentRepresentation(final String
a_representation)
throws UnsupportedRepresentationException {
if (a_representation != null) {
StringTokenizer tokenizer =
new StringTokenizer(a_representation,
PERSISTENT_FIELD_DELIMITER);
// Make sure the representation contains the correct number of
// fields. If not, throw an exception.
// -----------------------------------------------------------
if (tokenizer.countTokens() != 4) {
throw new UnsupportedRepresentationException(
"The format of the given persistent representation " +
" is not recognized: it does not contain three tokens: " +
a_representation);
}
String valueRepresentation = tokenizer.nextToken();
String lowerBoundRepresentation = tokenizer.nextToken();
String upperBoundRepresentation = tokenizer.nextToken();
String significanceRepresentation = tokenizer.nextToken();
// First parse and set the representation of the value.
// ----------------------------------------------------
if (valueRepresentation.equals("null")) {
setAllele(null);
}
else {
try {
setAllele(new Integer(Integer.parseInt(valueRepresentation)));
} catch (NumberFormatException e) {
throw new UnsupportedRepresentationException(
"The format of the given persistent representation " +
"is not recognized: field 1 does not appear to be " +
"an integer value.");
}
}
// Now parse and set the lower bound.
// ----------------------------------
try {
m_lowerBounds =
Integer.parseInt(lowerBoundRepresentation);
} catch (NumberFormatException e) {
throw new UnsupportedRepresentationException(
"The format of the given persistent representation " +
"is not recognized: field 2 does not appear to be " +
"an integer value.");
}
// Now parse and set the upper bound.
// ----------------------------------
try {
m_upperBounds =
Integer.parseInt(upperBoundRepresentation);
} catch (NumberFormatException e) {
throw new UnsupportedRepresentationException(
"The format of the given persistent representation " +
"is not recognized: field 3 does not appear to be " +
"an integer value.");
}
try {
m_significance =
Integer.parseInt(significanceRepresentation);
} catch (NumberFormatException e) {
throw new UnsupportedRepresentationException(
"The format of the given persistent representation " +
"is not recognized: field 3 does not appear to be " +
"an integer value.");
}
}
}
/**
* Sets the value (allele) of this Gene to a random Integer value between
* the lower and upper bounds (if any) of this Gene.
*
* @param a_numberGenerator the random number generator that should be
* used to create any random values. It's important to use this generator to
* maintain the user's flexibility to configure the genetic engine to use the
* random number generator of their choice
*
* @author Chris Jones
* @since 3.5
*/
public void setToRandomValue(final RandomGenerator a_numberGenerator) {
setAllele(new Integer(getRandomValue(a_numberGenerator)));
}
private Integer getRandomValue(RandomGenerator a_numberGenerator) {
double randomValue = ( (long) m_upperBounds - (long) m_lowerBounds) *
a_numberGenerator.nextDouble() + m_lowerBounds;
return round(randomValue, m_significance);
}
private int round(double value, Integer factor) {
if (value % factor == 0) {
return (int) value;
}
int floor = (int) ( (value / factor)) * factor;
int ceiling = floor + factor;
if (ceiling - value <= value - floor && ceiling <= m_upperBounds) {
return ceiling;
}
else if (floor >= m_lowerBounds) {
return floor;
}
else {
return ceiling;
}
}
/**
* Compares to objects by first casting them into their expected type
* (e.g. Integer for IntegerGene) and then calling the compareTo-method
* of the casted type.
* @param a_o1 first object to be compared, always is not null
* @param a_o2 second object to be compared, always is not null
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the object provided for comparison
*
* @author Neil Rostan
* @since 3.5
*/
protected int compareToNative(final Object a_o1, final Object a_o2) {
return ( (Integer) a_o1).compareTo( (Integer) a_o2);
}
/**
* Maps the value of this IntegerGene to within the bounds specified by
* the m_upperBounds and m_lowerBounds instance variables. The value's
* relative position within the integer range will be preserved within the
* bounds range (in other words, if the value is about halfway between the
* integer max and min, then the resulting value will be about halfway
* between the upper bounds and lower bounds). If the value is null or
* is already within the bounds, it will be left unchanged.
*
* @author Neil Rostan
* @author Klaus Meffert
* @since 1.0
*/
protected void mapValueToWithinBounds() {
if (getAllele() != null) {
Integer i_value = ( (Integer) getAllele());
// If the value exceeds either the upper or lower bounds, then
// map the value to within the legal range. To do this, we basically
// calculate the distance between the value and the integer min,
// determine how many bounds units that represents, and then add
// that number of units to the upper bound.
// -----------------------------------------------------------------
if (i_value.intValue() > m_upperBounds ||
i_value.intValue() < m_lowerBounds) {
RandomGenerator rn;
if (getConfiguration() != null) {
rn = getConfiguration().getRandomGenerator();
}
else {
rn = new StockRandomGenerator();
}
if (m_upperBounds == m_lowerBounds) {
setAllele(new Integer(m_lowerBounds));
}
else {
setToRandomValue(rn);
}
}
}
}
/**
* See interface Gene for description.
* @param a_index ignored (because there is only 1 atomic element)
* @param a_percentage percentage of mutation (greater than -1 and smaller
* than 1)
*
* @author Chris Jones
* @since 3.5
*/
public void applyMutation(final int a_index, final double a_percentage) {
double range = ( (long) m_upperBounds - (long) m_lowerBounds) *
a_percentage;
if (getAllele() == null) {
setAllele(new Integer( (int) range + m_lowerBounds));
}
else {
Integer i_value = ( (Integer) getAllele());
int newValue = (int) Math.round(i_value.intValue() + range);
newValue = round(newValue, m_significance);
setAllele(new Integer(newValue));
}
}
/**
* Modified hashCode() function to return different hashcodes for differently
* ordered genes in a chromosome.
* @return -1 if no allele set, otherwise value return by BaseGene.hashCode()
*
* @author Klaus Meffert
* @since 3.5
*/
public int hashCode() {
if (getInternalValue() == null) {
return -1;
}
else {
return super.hashCode();
}
}
/**
* @return string representation of this Gene's value that may be useful for
* display purposes
*
* @author Chris Jones
* @since 3.5
*/
public String toString() {
String s = "IntegerGene(" + m_lowerBounds + "," + m_upperBounds + "," +
m_significance + ")" + "=";
if (getInternalValue() == null) {
s += "null";
}
else {
s += getInternalValue().toString();
}
return s;
}
/**
* @return the lower bounds of the integer gene
*
* @author Klaus Meffert
* @since 3.5
*/
public int getLowerBounds() {
return m_lowerBounds;
}
/**
* @return the upper bounds of the integer gene
*
* @author Klaus Meffert
* @since 3.5
*/
public int getUpperBounds() {
return m_upperBounds;
}
/**
* @return the upper bounds of the integer gene
*
* @author Klaus Meffert
* @since 3.5
*/
public int getSignificance() {
return m_significance;
}
}