/*
* 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.gp.terminal;
import org.jgap.*;
import org.jgap.gp.*;
import org.jgap.gp.impl.*;
import org.jgap.util.*;
/**
* A terminal is a static number that can be mutated.
*
* @author Klaus Meffert
* @since 3.0
*/
public class Terminal
extends CommandGene implements IMutateable, ICloneable {
/** String containing the CVS revision. Read out via reflection!*/
private static final String CVS_REVISION = "$Revision: 1.18 $";
private float m_value_float;
private double m_value_double;
private int m_value_int;
private long m_value_long;
private double m_lowerBounds;
private double m_upperBounds;
private boolean m_wholeNumbers;
public Terminal()
throws InvalidConfigurationException {
this(GPGenotype.getStaticGPConfiguration(), CommandGene.IntegerClass);
}
public Terminal(final GPConfiguration a_conf, Class a_returnType)
throws InvalidConfigurationException {
this(a_conf, a_returnType, 0d, 99d, false);
}
/**
* Constructor.
*
* @param a_conf GPConfiguration
* @param a_returnType Class
* @param a_minValue double
* @param a_maxValue double
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.0
*/
public Terminal(final GPConfiguration a_conf, Class a_returnType,
double a_minValue, double a_maxValue)
throws InvalidConfigurationException {
this(a_conf, a_returnType, a_minValue, a_maxValue, false);
}
/**
* Constructor.
*
* @param a_conf GPConfiguration
* @param a_returnType Class
* @param a_minValue double
* @param a_maxValue double
* @param a_wholeNumbers true: only return whole numbers (only relevant when
* using type double or float)
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.2
*/
public Terminal(final GPConfiguration a_conf, Class a_returnType,
double a_minValue, double a_maxValue, boolean a_wholeNumbers)
throws InvalidConfigurationException {
this(a_conf, a_returnType, a_minValue, a_maxValue, a_wholeNumbers, 0);
}
public Terminal(final GPConfiguration a_conf, Class a_returnType,
double a_minValue, double a_maxValue, boolean a_wholeNumbers,
int a_subReturnType)
throws InvalidConfigurationException {
this(a_conf, a_returnType, a_minValue, a_maxValue, a_wholeNumbers,
a_subReturnType, true);
}
/**
*
* @param a_conf GPConfiguration
* @param a_returnType Class
* @param a_minValue double
* @param a_maxValue double
* @param a_wholeNumbers boolean
* @param a_subReturnType int
* @param a_randomize true: randomize initial value
* @throws InvalidConfigurationException
*
* @author Klaus Meffert
* @since 3.4.1
*/
public Terminal(final GPConfiguration a_conf, Class a_returnType,
double a_minValue, double a_maxValue, boolean a_wholeNumbers,
int a_subReturnType, boolean a_randomize)
throws InvalidConfigurationException {
super(a_conf, 0, a_returnType, a_subReturnType, null);
m_lowerBounds = a_minValue;
m_upperBounds = a_maxValue;
m_wholeNumbers = a_wholeNumbers;
if (a_randomize) {
setRandomValue();
}
}
protected void setRandomValue(int a_value) {
RandomGenerator randomGen = getGPConfiguration().getRandomGenerator();
m_value_int = (int) Math.round(randomGen.nextDouble() *
(m_upperBounds - m_lowerBounds) +
m_lowerBounds);
}
protected void setRandomValue(long a_value) {
RandomGenerator randomGen = getGPConfiguration().getRandomGenerator();
m_value_long = Math.round(randomGen.nextDouble() *
(m_upperBounds - m_lowerBounds) +
m_lowerBounds);
}
protected void setRandomValue(double a_value) {
RandomGenerator randomGen = getGPConfiguration().getRandomGenerator();
m_value_double = randomGen.nextDouble() * (m_upperBounds - m_lowerBounds) +
m_lowerBounds;
if (m_wholeNumbers) {
m_value_double = Math.round(m_value_double);
}
}
protected void setRandomValue(float a_value) {
RandomGenerator randomGen = getGPConfiguration().getRandomGenerator();
m_value_float = (float) (randomGen.nextFloat() *
(m_upperBounds - m_lowerBounds) +
m_lowerBounds);
if (m_wholeNumbers) {
m_value_float = Math.round(m_value_float);
}
}
protected void setRandomValue() {
Class retType = getReturnType();
if (retType == CommandGene.FloatClass || retType == float.class) {
setRandomValue(m_value_float);
}
else if (retType == CommandGene.IntegerClass || retType == int.class) {
setRandomValue(m_value_int);
}
else if (retType == CommandGene.LongClass || retType == long.class) {
setRandomValue(m_value_long);
}
else if (retType == CommandGene.DoubleClass || retType == double.class) {
setRandomValue(m_value_double);
}
else {
throw new RuntimeException("unknown terminal type: " + retType);
}
}
public void setValue(double a_value) {
if (m_wholeNumbers) {
m_value_double = Math.round(a_value);
}
else {
m_value_double = a_value;
}
}
public void setValue(float a_value) {
if (m_wholeNumbers) {
m_value_float = Math.round(a_value);
}
else {
m_value_float = a_value;
}
}
public void setValue(int a_value) {
m_value_int = a_value;
}
public void setValue(long a_value) {
m_value_long = a_value;
}
public CommandGene applyMutation(int index, double a_percentage)
throws InvalidConfigurationException {
// If percentage is very high: do mutation not relying on
// current value but on a random value.
// ------------------------------------------------------
if (a_percentage > 0.85d) {
setRandomValue();
}
else {
Class retType = getReturnType();
if (retType == CommandGene.FloatClass) {
float newValuef;
float rangef = ( (float) m_upperBounds - (float) m_lowerBounds) *
(float) a_percentage;
if (m_value_float >= (m_upperBounds - m_lowerBounds) / 2) {
newValuef = m_value_float -
getGPConfiguration().getRandomGenerator().nextFloat() * rangef;
}
else {
newValuef = m_value_float +
getGPConfiguration().getRandomGenerator().nextFloat() * rangef;
}
// Ensure value is within bounds.
// ------------------------------
if (m_lowerBounds - newValuef > DELTA ||
newValuef - m_upperBounds > DELTA) {
setRandomValue(m_value_float);
}
else {
setValue(newValuef);
}
}
else if (retType == CommandGene.DoubleClass) {
double newValueD;
double rangeD = (m_upperBounds - m_lowerBounds) * a_percentage;
if (m_value_double >= (m_upperBounds - m_lowerBounds) / 2) {
newValueD = m_value_double -
getGPConfiguration().getRandomGenerator().nextFloat() * rangeD;
}
else {
newValueD = m_value_double +
getGPConfiguration().getRandomGenerator().nextFloat() * rangeD;
}
// Ensure value is within bounds.
// ------------------------------
if (m_lowerBounds - newValueD > DELTA ||
newValueD - m_upperBounds > DELTA) {
setRandomValue(m_value_float);
}
else {
setValue(newValueD);
}
}
else if (retType == CommandGene.IntegerClass) {
int newValueI;
double range = (m_upperBounds - m_lowerBounds) * a_percentage;
if (m_value_int >= (m_upperBounds - m_lowerBounds) / 2) {
newValueI = m_value_int -
(int) Math.round(getGPConfiguration().getRandomGenerator().
nextInt() * range);
}
else {
newValueI = m_value_int +
(int) Math.round(getGPConfiguration().getRandomGenerator().
nextFloat() * range);
}
// Ensure value is within bounds.
// ------------------------------
if (newValueI < m_lowerBounds || newValueI > m_upperBounds) {
setRandomValue(m_value_int);
}
else {
setValue(newValueI);
}
}
else if (retType == CommandGene.LongClass) {
long newValueL;
double range = (m_upperBounds - m_lowerBounds) * a_percentage;
if (m_value_long >= (m_upperBounds - m_lowerBounds) / 2) {
newValueL = m_value_long -
Math.round(getGPConfiguration().getRandomGenerator().nextInt() *
range);
}
else {
newValueL = m_value_long +
Math.round(getGPConfiguration().getRandomGenerator().nextFloat() *
range);
}
// Ensure value is within bounds.
// ------------------------------
if (newValueL < m_lowerBounds || newValueL > m_upperBounds) {
setRandomValue(m_value_long);
}
else {
setValue(newValueL);
}
}
}
return this;
}
public String toString() {
Class retType = getReturnType();
if (retType == CommandGene.FloatClass) {
return "" + m_value_float;
}
else if (retType == CommandGene.IntegerClass) {
return "" + m_value_int;
}
else if (retType == CommandGene.LongClass) {
return "" + m_value_long;
}
else if (retType == CommandGene.DoubleClass) {
return "" + m_value_double;
}
else {
return "unknown terminal type: " + retType;
}
}
public int execute_int(ProgramChromosome c, int n, Object[] args) {
return m_value_int;
}
public long execute_long(ProgramChromosome c, int n, Object[] args) {
return m_value_long;
}
public float execute_float(ProgramChromosome c, int n, Object[] args) {
return m_value_float;
}
public double execute_double(ProgramChromosome c, int n, Object[] args) {
return m_value_double;
}
/**
* Returns a string representation of the terminal.
*
* @param c ignored here
* @param n ignored here
* @param args ignored here
* @return StringBuffer with textual representation of terminal's value
*
* @author Klaus Meffert
* @since 3.2
*/
public Object execute_object(ProgramChromosome c, int n, Object[] args) {
StringBuffer value = new StringBuffer("(");
Class retType = getReturnType();
if (retType == CommandGene.FloatClass) {
value.append(m_value_float).append("f");
}
else if (retType == CommandGene.DoubleClass) {
value.append(m_value_double).append("d");
}
else if (retType == CommandGene.IntegerClass) {
value.append(m_value_int);
}
else if (retType == CommandGene.LongClass) {
value.append(m_value_long).append("l");
}
value.append(")");
return value;
}
public Class getChildType(IGPProgram a_ind, int a_chromNum) {
return null;
}
/**
* Clones the object. Simple and straight forward implementation here.
*
* @return cloned instance of this object
*
* @author Klaus Meffert
* @since 3.2
*/
public Object clone() {
try {
Terminal result = new Terminal(getGPConfiguration(), getReturnType(),
m_lowerBounds, m_upperBounds,
m_wholeNumbers, getSubReturnType(), false);
result.m_value_double = m_value_double;
result.m_value_float = m_value_float;
result.m_value_int = m_value_int;
result.m_value_long = m_value_long;
return result;
} catch (Throwable t) {
throw new CloneException(t);
}
}
}