package jcgp.population; import java.util.ArrayList; import jcgp.Parameters; import jcgp.Utilities; import jcgp.fitness.ParameterMismatchException; public class Chromosome { private Input[] inputs; private Node[][] nodes; private Output[] outputs; private ArrayList activeNodes; private int fitness = 0; private boolean recomputeActiveNodes = true; /** * Initialise a chromosome with the specified parameters. Random valid connections * are created. * * @param outputs * @param columns * @param rows * @param inputs * */ public Chromosome() { // allocate memory for all elements of the chromosome instantiateElements(); // set random connections so that the chromosome can be evaluated initialiseConnections(); } /** * Copy constructor. * * Initialise a new chromosome with the exact same connections as a given instance of Chromosome. * * @param clone the chromosome to be copied */ public Chromosome(Chromosome clone) { // allocate memory for all elements of the chromosome instantiateElements(); // initialise all connections based on argument copyConnections(clone); } /** * */ private void instantiateElements() { inputs = new Input[Parameters.getInputs()]; for (int i = 0; i < Parameters.getInputs(); i++) { inputs[i] = new Input(i); } // rows first nodes = new Node[Parameters.getRows()][Parameters.getColumns()]; for (int r = 0; r < Parameters.getRows(); r++) { for (int c = 0; c < Parameters.getColumns(); c++) { nodes[r][c] = new Node(this, r, c); } } outputs = new Output[Parameters.getOutputs()]; for (int o = 0; o < Parameters.getOutputs(); o++) { outputs[o] = new Output(this, o); } } /** * */ private void initialiseConnections() { // initialise nodes - [rows][columns] for (int r = 0; r < nodes.length; r++) { for (int c = 0; c < nodes[r].length; c++) { Connection[] connections = new Connection[Parameters.getMaxArity()]; for (int i = 0; i < connections.length; i++) { connections[i] = getRandomConnection(c); } nodes[r][c].initialise(Utilities.getRandomFunction(), connections); } } for (Output output : outputs) { output.setConnection(getRandomConnection()); } } /** * @param clone */ private void copyConnections(Chromosome clone) { // copy nodes - [rows][columns] for (int r = 0; r < nodes.length; r++) { for (int c = 0; c < nodes[r].length; c++) { // make array of connections to initialise with Connection[] connections = new Connection[Parameters.getMaxArity()]; // populate with connections equivalent to clone Connection copyConnection; for (int i = 0; i < connections.length; i++) { copyConnection = clone.getNode(r, c).getConnection(i); if (copyConnection instanceof Input) { connections[i] = inputs[((Input) copyConnection).getIndex()]; } else if (copyConnection instanceof Node) { connections[i] = nodes[((Node) copyConnection).getRow()][((Node) copyConnection).getColumn()]; } else { System.out.println("Warning: Connection of subtype " + copyConnection.getClass().toString() + " is not explicitly handled by copy constructor."); } } // initialise with copied arguments nodes[r][c].initialise(clone.getNode(r, c).getFunction(), connections); } } // do the same to outputs Connection copyOutput; for (int o = 0; o < outputs.length; o++) { copyOutput = clone.getOutput(o).getSource(); if (copyOutput instanceof Input) { outputs[o].setConnection(inputs[((Input) copyOutput).getIndex()]); } else if (copyOutput instanceof Node) { outputs[o].setConnection(nodes[((Node) copyOutput).getRow()][((Node) copyOutput).getColumn()]); } else { // something bad happened System.out.println("Warning: Connection of subtype " + copyOutput.getClass().toString() + " is not explicitly handled by copy constructor."); } } } public Node getNode(int row, int column) { return nodes[row][column]; } public Output getOutput(int index) { return outputs[index]; } public Input getInput(int index) { return inputs[index]; } public int getFitness() { return fitness; } public void setFitness(int newFitness) { fitness = newFitness; } /** * * @param values * @throws ParameterMismatchException */ public void setInputs(int ... values) throws ParameterMismatchException { // if the values provided don't match the specified number of inputs, the user should be warned if (values.length == inputs.length) { // set inputs for evaluation for (int i = 0; i < values.length; i++) { inputs[i].setValue(values[i]); } } else { throw new ParameterMismatchException(); } } /** * This method is useful for mutating chromosomes. * * @return a random element that can be mutated - Node or Output */ public MutableElement getRandomMutableElement() { // choose output or node int index = Utilities.getRandomInt(outputs.length + Parameters.getNodeCount()); if (index < outputs.length) { // outputs return outputs[index]; } else { // node index -= outputs.length; return nodes[index / Parameters.getColumns()][index % Parameters.getColumns()]; } } /** * Returns a random allowed connection respecting levels back.
* This method may always pick inputs, as they can be picked * regardless of the column. * * @param column the column to use as reference * @return a random connection */ public Connection getRandomConnection(int column) { // work out the allowed range obeying levels back int allowedColumns = ((column >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : column); int offset = ((column - allowedColumns) * nodes.length) - inputs.length; // choose input or allowed node int index = Utilities.getRandomInt(inputs.length + (nodes.length * allowedColumns)); if (index < inputs.length) { // input return inputs[index]; } else { // node // offset it to address the right columns index += offset; return nodes[index % nodes.length][index / nodes.length]; } } /** * This method will pick a completely random connection, independently * of levels back, including inputs. * * Useful for setting outputs. * * * @return a random connection */ public Connection getRandomConnection() { // choose output or node int index = Utilities.getRandomInt(inputs.length + Parameters.getNodeCount()); if (index < inputs.length) { // outputs return inputs[index]; } else { // node index -= inputs.length; return nodes[index / Parameters.getColumns()][index % Parameters.getColumns()]; } } /** * This causes the list of active nodes to be recomputed lazily (once it is actually requested). */ public void recomputeActiveNodes() { recomputeActiveNodes = true; } /** * This method computes a list of active connections (if necessary) and returns it. * * @return */ public ArrayList getActiveNodes() { computeActiveNodes(); return activeNodes; } private void computeActiveNodes() { // lazy recomputation has been triggered, do it if (recomputeActiveNodes) { recomputeActiveNodes = false; activeNodes = new ArrayList(); for (Output output : outputs) { output.getActiveNodes(activeNodes); } } } public boolean compareTo(Chromosome chromosome) { for (int r = 0; r < Parameters.getRows(); r++) { for (int c = 0; c < Parameters.getColumns(); c++) { if (!(nodes[r][c].copyOf(chromosome.getNode(r, c)))) { return false; } } } for (int o = 0; o < Parameters.getOutputs(); o++) { if (!(outputs[o].copyOf(chromosome.getOutput(o)))) { return false; } } return true; } public boolean compareActiveTo(Chromosome chromosome) { // update list if it is out of date computeActiveNodes(); if (activeNodes.size() == chromosome.getActiveNodes().size()) { for (int i = 0; i < activeNodes.size(); i++) { if (!(activeNodes.get(i).copyOf(chromosome.getActiveNodes().get(i)))){ return false; } } return true; } return false; } }