/* * 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.*; /** * A Gene implementation that supports two possible values (alleles, 1 and 0) * with a fixed length of alleles. * <p> * NOTE: Since this Gene implementation only supports two different * values (1 and 0), there's only a 50% chance that invocation * of the setToRandomValue() method will actually change the value of * this Gene (if it has a value). As a result, it may be desirable to * use a higher overall mutation rate when this Gene implementation * is in use. * <p> * Partly adapted stuff from the JAGA (Java API for Genetic Algorithms) * package (see <a href="http://www.jaga.org">jaga</a>). * * @author Klaus Meffert * @since 2.0 */ public class FixedBinaryGene extends BaseGene implements IPersistentRepresentation { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.40 $"; private int m_length; private int[] m_value; private static final int WORD_LEN_BITS = 32; /** * * @param a_config the configuration to use * @param a_length the fixed length of the gene * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 2.0 */ public FixedBinaryGene(final Configuration a_config, final int a_length) throws InvalidConfigurationException { super(a_config); if (a_length < 1) { throw new IllegalArgumentException("Length must be greater than zero!"); } m_length = a_length; int bufSize = m_length / WORD_LEN_BITS; if (0 != m_length % WORD_LEN_BITS) { ++bufSize; } m_value = new int[bufSize]; for (int i = 0; i < bufSize; i++) { m_value[i] = 0; } } protected Gene newGeneInternal() { try { FixedBinaryGene result = new FixedBinaryGene(getConfiguration(), m_length); return result; } catch (InvalidConfigurationException iex) { throw new IllegalStateException(iex.getMessage()); } } public FixedBinaryGene(final Configuration a_config, final FixedBinaryGene a_toCopy) throws InvalidConfigurationException { super(a_config); m_length = a_toCopy.getLength(); int bufSize = m_length / WORD_LEN_BITS; if (0 != m_length % WORD_LEN_BITS) { ++bufSize; } m_value = new int[bufSize]; System.arraycopy(a_toCopy.getValue(), 0, m_value, 0, m_value.length); } protected int[] getValue() { return m_value; } public int getLength() { return m_length; } public Object clone() { try { return new FixedBinaryGene(getConfiguration(), this); } catch (InvalidConfigurationException iex) { throw new IllegalStateException(iex.getMessage()); } } public void setAllele(final Object a_newValue) { if (a_newValue == null) { throw new IllegalArgumentException("Allele must not be null!"); } if ( ( (int[]) a_newValue).length != getLength()) { throw new IllegalArgumentException("Length of allele must be equal to" + " fixed length set (" + getLength() + ")"); } if (getConstraintChecker() != null) { if (!getConstraintChecker().verify(this, a_newValue, null, -1)) { return; } } int[] bits = (int[]) a_newValue; for (int i = 0; i < bits.length; i++) { setBit(i, bits[i]); } } public Object getAllele() { int[] bits = new int[getLength()]; for (int i = 0; i < getLength(); i++) { if (getBit(i)) { bits[i] = 1; } else { bits[i] = 0; } } return bits; } /** * @return internal representation of the gene's state. Use getBit for reading * bits! */ public int[] getIntValues() { return m_value; } public boolean getBit(final int a_index) { checkIndex(a_index); return getUnchecked(a_index); } public void setBit(final int a_index, final boolean a_value) { checkIndex(a_index); setUnchecked(a_index, a_value); } public void setBit(final int a_index, final int a_value) { if (a_value > 0) { if (a_value != 1) { throw new IllegalArgumentException("Only values 0 and 1 are valid!"); } setBit(a_index, true); } else { if (a_value != 0) { throw new IllegalArgumentException("Only values 0 and 1 are valid!"); } setBit(a_index, false); } } public void setBit(final int a_from, final int a_to, final boolean a_value) { checkSubLength(a_from, a_to); for (int i = a_from; i < a_to; i++) { setUnchecked(i, a_value); } } public void setBit(final int a_from, final int a_to, final FixedBinaryGene a_values) { if (a_values.getLength() == 0) { throw new IllegalArgumentException("Length of values must be > 0"); } checkSubLength(a_from, a_to); int iV = 0; for (int i = a_from; i <= a_to; i++, iV++) { if (iV >= a_values.getLength()) { iV = 0; } setUnchecked(i, a_values.getUnchecked(iV)); } } public FixedBinaryGene substring(final int a_from, final int a_to) { try { int len = checkSubLength(a_from, a_to); FixedBinaryGene substring = new FixedBinaryGene(getConfiguration(), len); for (int i = a_from; i <= a_to; i++) { substring.setUnchecked(i - a_from, getUnchecked(i)); } return substring; } catch (InvalidConfigurationException iex) { throw new IllegalStateException(iex.getMessage()); } } public void flip(final int a_index) { checkIndex(a_index); int segment = a_index / WORD_LEN_BITS; int offset = a_index % WORD_LEN_BITS; int mask = 0x1 << (WORD_LEN_BITS - offset - 1); m_value[segment] ^= mask; } protected int checkSubLength(final int a_from, final int a_to) { checkIndex(a_from); checkIndex(a_to); int sublen = a_to - a_from + 1; if (0 >= sublen) { throw new IllegalArgumentException("must have 'from' <= 'to', but has " + a_from + " > " + a_to); } return sublen; } protected void checkIndex(final int a_index) { if (a_index < 0 || a_index >= getLength()) { throw new IndexOutOfBoundsException("index is " + a_index + ", but must be in [0, " + (getLength() - 1) + "]"); } } protected boolean getUnchecked(final int a_index) { int segment = a_index / WORD_LEN_BITS; int offset = a_index % WORD_LEN_BITS; int mask = 0x1 << (WORD_LEN_BITS - offset - 1); return 0 != (m_value[segment] & mask); } public void setUnchecked(final int a_index, final boolean a_value) { int segment = a_index / WORD_LEN_BITS; int offset = a_index % WORD_LEN_BITS; int mask = 0x1 << (WORD_LEN_BITS - offset - 1); if (a_value) { m_value[segment] |= mask; } else { m_value[segment] &= ~mask; } } public String getPersistentRepresentation() { return toString(); } /** * 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 UnsupportedRepresentationException if this Gene implementation * does not support the given string representation * * @author Klaus Meffert * @since 2.0 */ public void setValueFromPersistentRepresentation(String a_representation) throws UnsupportedRepresentationException { if (a_representation != null) { if (isValidRepresentation(a_representation)) { a_representation = a_representation.substring(1, a_representation.length() - 1); StringTokenizer st = new StringTokenizer(a_representation, ","); int index = 0; while (st.hasMoreTokens()) { int i = Integer.parseInt(st.nextToken()); setBit(index++, i); } if (index < getLength()) { throw new UnsupportedRepresentationException( "Invalid gene representation: " + a_representation); } } else { throw new UnsupportedRepresentationException( "Invalid gene representation: " + a_representation); } } else { throw new UnsupportedRepresentationException( "The input parameter must not be null!"); } } /** * Verifies if the String is a valid representation of this Gene type * in general (bit values will not be checked) * @param a_representation the representation to check * @return true: representation is valid in general * @author Klaus Meffert * @since 2.0 */ private boolean isValidRepresentation(final String a_representation) { if (!a_representation.startsWith("[") || !a_representation.endsWith("]")) { return false; } return true; } public void setToRandomValue(final RandomGenerator a_numberGenerator) { if (a_numberGenerator == null) { throw new IllegalArgumentException("Random Generator must not be null!"); } int len = getLength(); for (int i = 0; i < len; i++) { setBit(i, a_numberGenerator.nextBoolean()); } } /** * @return String representation * * @author Klaus Meffert * @since 2.0 */ public String toString() { int len = getLength(); String s = "FixedBinaryGene["; int value; for (int i = 0; i < len; i++) { if (getBit(i)) { value = 1; } else { value = 0; } if (i == 0) { s += value; } else { s += "," + value; } } return s + "]"; } @Override public String getBusinessKey() { return toString(); } /** * @return the size of the gene, i.e the number of atomic elements * * @author Klaus Meffert * @since 2.0 */ public int size() { return m_value.length; } /** * Applies a mutation of a given intensity (percentage) onto the atomic * element at given index * @param a_index index of atomic element, between 0 and size()-1 * @param a_percentage percentage of mutation (greater than -1 and smaller * than 1) * * @author Klaus Meffert * @since 2.0 */ public void applyMutation(final int a_index, final double a_percentage) { if (a_index < 0 || a_index >= getLength()) { throw new IllegalArgumentException( "Index must be between 0 and getLength() - 1"); } if (a_percentage > 0) { // change to 1 // --------------- if (!getBit(a_index)) { setBit(a_index, true); } } else if (a_percentage < 0) { // change to 0 // --------------- if (getBit(a_index)) { setBit(a_index, false); } } } /** * Compares this Gene with the specified object for order. A * 0 value is considered to be less than a 1 value. A null value * is considered to be less than any non-null value. * A Gene is greater than another one of the same length if it has more 1's * than the other one. If there is the same number of 1's the Gene with the * highest value (binary to int) is greater. * * @param a_other the FixedBinaryGene to be compared * @return a negative integer, zero, or a positive integer as this object * is less than, equal to, or greater than the specified object. * * @throws ClassCastException if the specified object's type prevents it * from being compared to this Gene * * @author Klaus Meffert * @since 2.0 */ public int compareTo(final Object a_other) { FixedBinaryGene otherGene = (FixedBinaryGene) a_other; // First, if the other gene is null, then this is the greater gene. // ---------------------------------------------------------------- if (otherGene == null) { return 1; } int thisLen = getLength(); int otherLen = otherGene.getLength(); if (thisLen != otherLen) { if (thisLen > otherLen) { return 1; } else { return -1; } } boolean b1, b2; for (int i = 0; i < thisLen; i++) { b1 = getBit(i); b2 = otherGene.getBit(i); if (b1) { if (!b2) { return 1; } } else { if (b2) { return -1; } } } // Compare application data, if possible. // -------------------------------------- if (isCompareApplicationData()) { return compareApplicationData(getApplicationData(), otherGene.getApplicationData()); } else { return 0; } } /** * Not called as getAllele() is overridden. * * @return same as getAllele() */ protected Object getInternalValue() { return m_value; } /** * Modified hashCode() function to return different hashcodes for differently * ordered genes in a chromosome --> does not work as internal value always * initialized! * * @return this Gene's hash code * * @author Klaus Meffert * @since 2.2 */ public int hashCode() { int result = 0; for (int i = 0; i < m_value.length; i++) { if (m_value[i] == 0) { result += 31 * result + 47; } else { result += 31 * result + 91; } } return result; } }