/*
* 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 examples.math.ga;
import java.io.*;
import org.jgap.*;
import examples.math.*;
import examples.math.cmd.*;
/**
* A gene for mathematical expressions.
*
* @author Michael Grove
* @since 3.4.2
*/
public class MathGene
extends BaseGene implements Gene, Serializable {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.1 $";
private Operand mOperand;
public MathGene(Configuration theConfiguration)
throws InvalidConfigurationException {
super(theConfiguration);
mOperand = new ValueOperand(0);
}
protected Object getInternalValue() {
return mOperand;
}
protected Gene newGeneInternal() {
try {
return new MathGene(getConfiguration());
} catch (InvalidConfigurationException e) {
throw new IllegalStateException(e);
}
}
public void setAllele(Object o) {
if (o instanceof Operand) {
mOperand = (Operand) o;
}
}
public String getPersistentRepresentation()
throws UnsupportedOperationException {
throw new RuntimeException("NYI");
}
public void setValueFromPersistentRepresentation(String s)
throws UnsupportedOperationException, UnsupportedRepresentationException {
throw new RuntimeException("NYI");
}
public void setToRandomValue(RandomGenerator theRandomGenerator) {
setAllele(generateRandomOperand(theRandomGenerator));
}
private Operand generateRandomOperand(RandomGenerator theRand) {
if (theRand.nextBoolean()) {
return generateRandomValueOperand(theRand);
}
else {
return generateRandomMathOperator(theRand);
}
}
private ValueOperand generateRandomValueOperand(RandomGenerator theRand) {
// TODO: generate negative value?
// TODO: should there be bounds applied?
return new ValueOperand(theRand.nextDouble() *
theRand.nextInt(Integer.MAX_VALUE));
}
private MathOperator generateRandomMathOperator(RandomGenerator theRand) {
MathOperator aOp = null;
int aType = theRand.nextInt(4);
if (aType == 0) {
aOp = new AddOperator();
}
else if (aType == 1) {
aOp = new MinusOperator();
}
else if (aType == 2) {
aOp = new MultiplyOperator();
}
else if (aType == 3) {
aOp = new DivideOperator();
}
aOp.setLeftOperand(generateRandomValueOperand(theRand));
aOp.setRightOperand(generateRandomValueOperand(theRand));
return aOp;
}
public void applyMutation(int i, double v) {
double aRate = Math.abs(v);
setAllele(mutateOperand(mOperand, aRate));
}
private Operand mutateOperand(Operand theOp, double theRate) {
Operand aReturn = null;
if (theOp instanceof ValueOperand) {
RandomGenerator aRand = getConfiguration().getRandomGenerator();
if (aRand.nextDouble() < theRate) {
// the bigger the rate, the more likely this switches from a set value to a
// math operation...
MathOperator aOp = generateRandomMathOperator(aRand);
// we switch this operand to a math operation, but lets keep its value as one
// of the operands of the operator
aOp.setLeftOperand(theOp);
aReturn = aOp;
}
else {
// lets just mutate the value
ValueOperand aOp = (ValueOperand) theOp;
aOp.setValue(aOp.value() * (1 + (theRate - .5)));
aReturn = aOp;
}
}
else if (theOp instanceof MathOperator) {
MathOperator aOp = (MathOperator) theOp;
RandomGenerator aRand = getConfiguration().getRandomGenerator();
if (aRand.nextDouble() < theRate) {
// if there's a value operand in the math operation, pull it out,
// and we'll use that. otherwise we'll generate a random one.
ValueOperand aVal = new GetValueOperandVisitor().find(aOp);
if (aVal == null) {
aVal = generateRandomValueOperand(aRand);
}
aReturn = aVal;
}
else {
int aType = aRand.nextInt(3);
if (aType == 0) {
// switch the math operation used
MathOperator aNewOp = generateRandomMathOperator(aRand);
aNewOp.setLeftOperand(aOp.getLeftOperand());
aNewOp.setRightOperand(aOp.getRightOperand());
aReturn = aNewOp;
}
else if (aType == 1) {
aOp.setLeftOperand(generateRandomOperand(aRand));
aReturn = aOp;
}
else if (aType == 2) {
aOp.setRightOperand(generateRandomOperand(aRand));
aReturn = aOp;
}
}
}
else {
throw new IllegalStateException();
}
return aReturn;
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public String toString() {
return new ReplVisitor().render(mOperand) + " = " + mOperand.value();
}
@Override
public boolean equals(Object theObj) {
if (theObj instanceof MathGene) {
MathGene aGene = (MathGene) theObj;
return aGene.toString().equals(toString());
}
else {
return false;
}
}
public int compareTo(Object o) {
if (o instanceof MathGene) {
MathGene aGene = (MathGene) o;
return new Double(mOperand.value()).compareTo(aGene.mOperand.value());
//return new ReplVisitor().render(mOperand).compareTo(new ReplVisitor().render(aGene.mOperand));
}
else {
throw new ClassCastException();
}
}
private class GetValueOperandVisitor
implements MathVisitor {
private ValueOperand mOp;
public ValueOperand find(MathOperator theOp) {
theOp.accept(this);
return mOp;
}
public void visit(ValueOperand theOp) {
if (mOp == null) {
mOp = theOp;
}
}
private void mathOpVisit(MathOperator theOp) {
if (mOp == null) {
theOp.getLeftOperand().accept(this);
}
if (mOp == null) {
theOp.getRightOperand().accept(this);
}
}
public void visit(AddOperator theOp) {
mathOpVisit(theOp);
}
public void visit(MinusOperator theOp) {
mathOpVisit(theOp);
}
public void visit(DivideOperator theOp) {
mathOpVisit(theOp);
}
public void visit(MultiplyOperator theOp) {
mathOpVisit(theOp);
}
}
}