Individual.java
package progen.kernel.population;
import java.util.HashMap;
import java.util.Map;
import progen.context.ProGenContext;
import progen.kernel.functions.ADF;
import progen.kernel.functions.Terminal;
import progen.kernel.grammar.Grammar;
import progen.kernel.grammar.GrammarTerminalSymbol;
import progen.kernel.tree.Tree;
import progen.roles.Task;
import progen.userprogram.UserProgram;
/**
* Clase que representa la información necesaria para representar un individuo
* completo en programación genética.
*
* @author jirsis
* @since 1.0
*
*/
public class Individual implements Task, Comparable<Individual>, Cloneable {
private static final String COLON_SPACE_SYMBOL = ": ";
private static final String RETURN_SYMBOL = "\n";
private static final String ADF_PROPERTY = "ADF";
private static final String RPB_PROPERTY = "RPB";
private static final String PROGEN_TOTAL_RPB_PROPERTY = "progen.total.RPB";
private static final long serialVersionUID = -3776497075849167016L;
/**
* Árboles que definen el individuo como tal. Se almacenan tanto RPBs como
* ADFs y se identifican siguiendo la nomenclatura RPBi, ADFi, donde i es un
* número [0, máximo árbol definido).
*/
private Map<String, Tree> trees;
/**
* Resultados de la evaluación de los árboles. Únicamente tiene sentido
* almacenar el resultado de los RPB dado que son éstos los únicos que pueden
* ser evaluados.
*/
private Map<String, Object> results;
/**
* Gramáticas que generan todos y cada unos de los árboles que forman el
* individuo concreto.
*/
private Map<String, Grammar> grammars;
/**
* Número total de árboles RPB en un individuo concreto.
*/
private int totalRPB;
/**
* Número total de árboles ADF en un individuo concreto.
*/
private int totalADF;
/**
* Valor del <code>raw-fitness</code> resultante de ejecutar la función de
* evaluación para este individuo.
*/
private double rawFitness;
/**
* Indica si es necesario reevaluar los árboles de un individuo antes de
* devolver los resultados. Es una optimización que evita tener que recalcular
* constantemente todos los árboles aún cuando no hayan cambiado los valores
* de las variables definidas en dichos árboles.
*/
private boolean updated;
/**
* Tabla en la que se almacena el valor concreto de todas las variables
* disponibles en la ejecución de un individuo.
*/
private Map<String, Object> variables;
/**
* Representación del individuo para ser impresa de alguna forma. Modifica la
* versión estándar en la que se muestran todos los árboles (ADF y RPB) que
* definen un individuo.
*/
private String printabeIndividual;
/**
* Constructor genérico de la clase. Recibe el conjunto de gramáticas que se
* utilizarán para generar los árboles que definen el indidivuo.
*
* @param grammars
* Gramáticas que generarán todos los árboles, tanto RPBs como ADFs
* que conforman un individuo concreto.
*/
public Individual(Map<String, Grammar> grammars) {
Tree tree;
trees = new HashMap<String, Tree>();
results = new HashMap<String, Object>();
totalRPB = ProGenContext.getOptionalProperty(PROGEN_TOTAL_RPB_PROPERTY, 1);
ProGenContext.setProperty(PROGEN_TOTAL_RPB_PROPERTY, String.valueOf(totalRPB));
totalADF = ProGenContext.getOptionalProperty("progen.total.ADF", 0);
for (int i = 0; i < totalRPB; i++) {
tree = new Tree();
tree.generate(grammars.get(RPB_PROPERTY + i));
trees.put(RPB_PROPERTY + i, tree);
}
for (int i = 0; i < totalADF; i++) {
tree = new Tree();
tree.generate(grammars.get(ADF_PROPERTY + i));
trees.put(ADF_PROPERTY + i, tree);
}
this.grammars = grammars;
this.rawFitness = Double.MAX_VALUE;
this.updated = true;
this.variables = new HashMap<String, Object>();
this.printabeIndividual = null;
}
/**
* Constructor de copia que crea un nuevo individuo a partir de otro
* proporcionado como parámetro.
*
* @param individual
* El individuo a copiar.
*/
public Individual(Individual individual) {
this.rawFitness = individual.rawFitness;
this.results = new HashMap<String, Object>();
this.totalRPB = individual.totalRPB;
this.totalADF = individual.totalADF;
this.grammars = individual.grammars;
this.updated = individual.updated;
this.trees = new HashMap<String, Tree>();
for (String key : individual.trees.keySet()) {
this.trees.put(key, new Tree(individual.trees.get(key)));
}
this.variables = new HashMap<String, Object>();
for (String key : individual.variables.keySet()) {
this.variables.put(key, individual.variables.get(key));
}
}
/**
* Devuelve los árboles que contiene el individuo.
*
* @return Los árboles que contiene el individuo.
*/
public Map<String, Tree> getTrees() {
return trees;
}
/**
* Devuelve las gramáticas utilizadas para generar los distintos árboles.
*
* @return Las gramáticas utilizadas para generar los distintos árboles.
*/
public Map<String, Grammar> getGrammars() {
return grammars;
}
/**
* Representación en forma de <code>String</code> de un individuo, es decir,
* se obtiene representación de todos los árboles que contiene.
*
* @return La representación del individuo.
*/
@Override
public String toString() {
final StringBuilder individual = new StringBuilder();
if (this.printabeIndividual != null && this.printabeIndividual.length() > 0) {
individual.append(printabeIndividual);
} else {
int RPB = 0;
int adf = 0;
while (trees.get(RPB_PROPERTY + RPB) != null) {
individual.append(RETURN_SYMBOL+ RPB_PROPERTY + RPB + COLON_SPACE_SYMBOL + trees.get(RPB_PROPERTY + RPB));
RPB++;
}
while (trees.get(ADF_PROPERTY + adf) != null) {
individual.append(RETURN_SYMBOL+ADF_PROPERTY + adf + COLON_SPACE_SYMBOL + trees.get(ADF_PROPERTY + adf));
adf++;
}
}
return individual.toString();
}
/**
* Definición de un valor concreto para todas las variables que existan en
* cualquier árbol.
*
* @param variable
* Identificador de la variable a la que se le va a asignar el valor.
* @param value
* Valor concreto que tendrá la variable.
*/
public void setVariable(String variable, Object value) {
this.updated = true;
for (String idGrammar : grammars.keySet()) {
final Grammar grammar = grammars.get(idGrammar);
for (GrammarTerminalSymbol function : grammar.getGrammarTerminalSymbols()) {
if (function.getSymbol().equals(variable)) {
((Terminal) function.getFunction()).setValue(value);
}
}
}
}
/**
* Función que evalúa y obtiene un resultado de la ejecución de todos los
* árboles que tiene un individuo concreto. El resultado se deja almacenado en
* la variable para tal efecto.
*
* @param variables
* Almacén que contiene todas las variables definidas por un
* identificador y su valor concreto.
* @param userprogram
* Definición del problema que ha tenido que implementar el usuario.
*/
private void evaluateTree(Map<String, Object> variables, UserProgram userprogram) {
// se actualizan los ADF correspondientes para que utilicen el arbol de
// este individuo
for (int i = 0; i < totalADF; i++) {
for (int j = 0; j < totalRPB; j++) {
for (GrammarTerminalSymbol adf : grammars.get(RPB_PROPERTY + j).getGrammarTerminalSymbols()) {
if (adf.getSymbol().compareTo(ADF_PROPERTY + i) == 0) {
((ADF) adf.getFunction()).setADFTree(trees.get(ADF_PROPERTY + i));
}
}
}
}
for (int i = 0; i < totalRPB; i++) {
results.put(RPB_PROPERTY + i, trees.get(RPB_PROPERTY + i).evaluate(userprogram, variables));
}
this.updated = false;
}
/**
* Devuelve el resultado de ejecutar el árbol solicitado como parámetro.
* Únicamente tiene sentido solicitar los resultados de los árboles de tipo
* RPB.
*
* @param idTree
* Identificador del árbol del que se quiere obtener el resultado de
* su ejecución.
* @param userProgram
* Referencia al programa modelado por el usuario.
* @return Object del tipo que devuelve el function set que defina la
* gramática que se utilizá para definir ese árbol.
*/
public Object evaluate(String idTree, UserProgram userProgram) {
// TODO: comprobar que se está solicianto de un RPB, en caso contrario dar
// un fallo.
if (updated) {
this.evaluateTree(variables, userProgram);
}
return results.get(idTree);
}
/**
* Devuelve únicamente el valor de la ejecución del árbol identificado como
* <code>RPB0</code>, ya que no se tiene sentido definir un individuo en el
* que no exista ni siquiera este árbol.
*
* @param userProgram
* Referencia al programa modelado por el usuario. *
* @return Object del tipo que devuelve el function-set que defina la
* gramática que se utilizá para definir ese árbol.
*/
public Object evaluate(UserProgram userProgram) {
return this.evaluate("RPB0", userProgram);
}
/**
* Especifica el valor del <i>rawFitness</i> de este individuo, según se haya
* definido en el UserProgram de un problema concreto
*
* @see progen.userprogram.UserProgram
*
* @param fitness
* el valor del <i>rawFitness</i>.
*/
public void setRawFitness(double fitness) {
this.rawFitness = fitness;
}
/**
* Devuelve el valor del <i>rawFitness</i> de este individuo.
*
* @return fitness el valor del <i>rawFitness</i> de este individuo.
*/
public double getRawFitness() {
return rawFitness;
}
/**
* Devuelve el valor del <i>adjustedFitness</i> según la definición:
* <code>adjustedFitness=1/(1+rawFitness);</code>
*
* @return el valor del <i>adjustedFitness</i>
*/
public double getAdjustedFitness() {
return 1 / (1 + rawFitness);
}
@Override
public void calculate(UserProgram userProgram) {
this.rawFitness = userProgram.fitness(this);
}
@Override
public boolean isDone() {
return !updated;
}
/**
* Compara un individuo con otro proporcionado como parámetro.
*
* @param other
* El individuo con el que comparar.
* @return Devuelve la diferencia entre el rawFitness de los dos, de la forma
* <code>this-other</code>
*/
@Override
public int compareTo(Individual other) {
return Double.compare(this.rawFitness, other.rawFitness);
}
@Override
public boolean equals(Object other) {
boolean equals = false;
if (other instanceof Individual) {
equals = this.equalsIndividual((Individual) other);
} else {
equals = false;
}
return equals;
}
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* Compara dos individuos para comprobar si son iguales o no. Se considerará
* que dos individuos son iguales únicamente si todos sus árboles son iguales.
*
* @param other
* Individuo con el que comparar
* @return <code>true</code> si los dos individuos son iguales.
*/
private boolean equalsIndividual(Individual other) {
boolean isEquals = true;
if (other == null) {
isEquals = false;
} else {
for (int i = 0; i < totalRPB; i++) {
isEquals = isEquals && trees.get(RPB_PROPERTY + i).toString().compareTo(other.getTrees().get(RPB_PROPERTY + i).toString()) == 0;
}
for (int i = 0; i < totalADF; i++) {
isEquals = isEquals && trees.get(ADF_PROPERTY + i).toString().compareTo(other.getTrees().get(ADF_PROPERTY + i).toString()) == 0;
}
}
return isEquals;
}
@Override
public Object getCalculateResult() {
return this;
}
@Override
public Individual clone() {
try {
super.clone();
} catch (CloneNotSupportedException e) {
// ignore this
}
return new Individual(this);
}
/**
* Devuelve el número total de árboles RPB que componen el individuo.
*
* @return el número total de RPB.
*/
public int getTotalRPB() {
return totalRPB;
}
/**
* Devuelve el número total de árboles ADF que componen el individuo.
*
* @return el número total de ADF.
*/
public int getTotalADF() {
return totalADF;
}
/**
* @param printable
*/
public void setPrintableIndividual(String printable) {
this.printabeIndividual = printable;
}
}