geva.Mapper.DerivationTree.java Source code

Java tutorial

Introduction

Here is the source code for geva.Mapper.DerivationTree.java

Source

/*
Grammatical Evolution in Java
Release: GEVA-v1.2.zip
Copyright (C) 2008 Michael O'Neill, Erik Hemberg, Anthony Brabazon, Conor Gilligan 
Contributors Patrick Middleburgh, Eliott Bartley, Jonathan Hugosson, Jeff Wrigh
    
Separate licences for asm, bsf, antlr, groovy, jscheme, commons-logging, jsci is included in the lib folder. 
Separate licence for rieps is included in src/com folder.
    
This licence refers to GEVA-v1.2.
    
This software is distributed under the terms of the GNU General Public License.
    
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
/>.
*/

/*
 * DerivationTree.java
 *
 * Created on 17 October 2006, 13:32
 *
 * DerrivationTree
 *
 */

package geva.Mapper;

import geva.Exceptions.MalformedGrammarException;
import geva.Individuals.GEChromosome;
import geva.Individuals.Phenotype;
import geva.Util.Constants;
import geva.Util.Enums;
import geva.Util.Random.MersenneTwisterFast;
import geva.Util.Structures.IntIterator;
import geva.Util.Structures.NimbleTree;
import geva.Util.Structures.TreeNode;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class is used in the mapping from genotype to phenotype in GE.
 * @author EHemberg
 */
public class DerivationTree extends NimbleTree<Symbol> {
    private static Log logger = LogFactory.getLog(DerivationTree.class);
    protected final GEGrammar grammy;
    private final GEChromosome genny;
    private int wrapCount;
    private IntIterator genIter;
    protected int geneCnt;

    @Override
    protected TreeNode<Symbol> newNode() {
        return new DerivationNode();
    }

    /**
     * Create new Derivation Tree
     * @param gram mapper
     * @param gen input
     */
    public DerivationTree(GEGrammar gram, GEChromosome gen) {
        super();
        this.getRoot().setData(gram.getStartSymbol());
        this.genny = gen;
        this.grammy = gram;
        this.genIter = genny.iterator();
        this.wrapCount = 1;
        this.geneCnt = 0;
    }

    /**
     * Copy Constructor
     * @param DerivationTree copy
     */
    public DerivationTree(DerivationTree copy) {
        super(copy);
        this.grammy = new GEGrammar(copy.grammy);
        this.genny = new GEChromosome(copy.genny);
        this.wrapCount = copy.wrapCount;
        this.geneCnt = copy.geneCnt;
    }

    /**
     * Build a derivation tree. Using the grammar as a mapper and the input is
     *  the genotype.
     * @return validity of the mapping
     */
    public boolean buildDerivationTree() {
        assert this.getCurrentNode() instanceof DerivationNode : this.getCurrentNode().getClass().getName();
        DerivationNode t;
        Boolean validBuild;
        t = (DerivationNode) this.getCurrentNode();
        this.grammy.findRule(t.getData());
        validBuild = growNode(t);
        this.genny.setUsedGenes(this.geneCnt);
        //System.out.println(this.geneCnt);
        //System.out.println(this.wrapCount);
        return validBuild;
    }

    /** Extract range for value from non-terminal specification,
     * where specification is in the format <GECodonValue{x, y, z}> (set-mode)
     * or <GECodonValue(x, y)> (range-mode -- range can be open, closed, or half-open).
     * @param s symbol string
     * @param codon codon value
     * @return string value
     * @throws MalformedGrammarException The specification s is malformed
     */
    String getGECodonValue(String s, int codon) throws MalformedGrammarException {

        String c = Constants.GE_CODON_VALUE_PARSING;
        if (s.indexOf("{") != -1) {
            // in "set"-mode: uses set-notation {x, y, z}
            if (s.indexOf("}") == -1) {
                throw new MalformedGrammarException("Bad GECodonValue specification: " + s);
            }

            String GECodonValueRE = c + "\\{(.*)\\}" + ">";
            Pattern pattern = Pattern.compile(GECodonValueRE);
            Matcher matcher = pattern.matcher(s);
            if (matcher.find()) {
                if (matcher.groupCount() == 1) {
                    String content = matcher.group(1);
                    String[] setValues = content.split(",");

                    // Success: found good values in set-mode. Return.
                    return setValues[codon % setValues.length].trim();
                } else {
                    throw new MalformedGrammarException(
                            "Bad GECodonValue specification (found too many matches): " + s);
                }
            } else {
                throw new MalformedGrammarException("Bad GECodonValue specification (didn't find a match): " + s);
            }
        }

        else if (s.indexOf("(") != -1 || s.indexOf("[") != -1) {
            // in "range"-mode: uses open, closed, or half-open ranges:
            // [x, y], (x, y), [x, y), or (x, y]
            if (s.indexOf(")") == -1 && s.indexOf("]") == -1) {
                throw new MalformedGrammarException(
                        "Bad GECodonValue specification (didn't find closing brackets): " + s);
            }

            // first group gets lower-boundary indicator ( or [;
            // second group gets content, eg 4.1, 4.9;
            // third group gets upper-boundary indicator ) or ].
            String GECodonValueRE = c + "([\\(\\[])(.*)([\\)\\]])" + ">";
            Pattern pattern = Pattern.compile(GECodonValueRE);
            Matcher matcher = pattern.matcher(s);
            String content;
            String[] boundaryValues;
            if (matcher.find()) {
                //       for (int i = 0; i <= matcher.groupCount(); i++) {
                //           System.out.println("debug: i = " + i + "; group = " + matcher.group(i));
                //       }

                if (matcher.groupCount() != 3) {
                    throw new MalformedGrammarException(
                            "Bad GECodonValue specification (didn't find opening and closing brackets and boundaries): "
                                    + s);
                }

                // Get the text
                content = matcher.group(2);
                boundaryValues = content.split(",");
                if (boundaryValues.length != 2) {
                    throw new MalformedGrammarException(
                            "Bad GECodonValue specification (didn't find upper and lower boundaries): " + s);
                }
            } else {
                throw new MalformedGrammarException("Bad GECodonValue specification (didn't find a match): " + s);
            }

            int low, high;
            double lowd, highd;
            boolean intMode;
            try {
                intMode = true;
                low = Integer.parseInt(boundaryValues[0].trim());
                high = Integer.parseInt(boundaryValues[1].trim());
                // dummy values, these won't be used
                lowd = 0.0;
                highd = 0.0;
            } catch (Exception e) {
                intMode = false;
                lowd = Double.parseDouble(boundaryValues[0].trim());
                highd = Double.parseDouble(boundaryValues[1].trim());
                low = 0;
                high = genny.getMaxCodonValue();
            }

            if (matcher.group(1).equals("(")) {
                // it's an open range: exclude the low value
                // System.out.println("range semi-open at the bottom");
                low += 1;
                // FIXME what a hack
                lowd += 0.0001;
            }
            if (matcher.group(3).equals(")")) {
                // it's an open range: exclude the high value
                // System.out.println("range semi-open at the top");
                high -= 1;
                // FIXME what a hack
                highd -= 0.0001;
            }

            if (intMode == true) {
                if (low > high) {
                    throw new MalformedGrammarException("Bad GECodonValue specification (bad boundaries): " + s);
                }

                // Success: found good boundaries in integer mode: return.
                return String.valueOf(low + (codon % (high - low + 1)));
            } else {
                if (lowd > highd) {
                    throw new MalformedGrammarException("Bad GECodonValue specification (bad boundaries): " + s);
                }
                // Success: found good boundaries in double-mode: return.
                return String
                        .valueOf(lowd + (highd - lowd) * (codon) / Double.valueOf((genny.getMaxCodonValue() - 0)));
            }

        } else {
            // may be in legacy mode
            logger.warn("Warning: DerivationTree attempting to use GECodonValue legacy format");
            return getGECodonValueLegacyFormat(s, codon);
        }
    }

    /** Extract range for value from non-terminal specification,
     * where specification is in the legacy format <GECodonValue-3+17>
     * @param s symbol string
     * @param codon codon value
     * @return string value
     */
    String getGECodonValueLegacyFormat(String s, int codon) {
        int low = 0;
        int high = -1;
        int i = Constants.GE_CODON_VALUE_PARSING.length(); //Start value for codon counter
        String codon_value;
        char currentChar = s.charAt(i);// currentChar is first character after "<GECodonValue"
        // Look for range definitions
        while (currentChar != '>') {
            if (currentChar == '-') {// Low range specification
                currentChar = s.charAt(i++);
                while ((currentChar >= '0') && (currentChar <= '9')) {
                    low = (low * 10) + (currentChar - '0');
                    currentChar = s.charAt(i++);
                }
            } else if (currentChar == '+') {// High range specification
                currentChar = s.charAt(i++);
                while ((currentChar >= '0') && (currentChar <= '9')) {
                    if (high == -1) {
                        high = 0;
                    }
                    high = (high * 10) + (currentChar - '0');
                    currentChar = s.charAt(i++);
                }
            } else {// Ignore errors
                currentChar = s.charAt(i++);
            }
        }
        // High range was not specified, so set it to maximum
        if (high == -1) {
            high = genny.getMaxCodonValue();
        }
        if (high == low) {// Catch division by zero
            codon_value = String.valueOf(low);
        } else {
            codon = (codon % (high - low + 1)) + low;
            codon_value = String.valueOf(codon);
        }
        return codon_value;
    }

    /**
     * Grows the nodes of the tree in a recursive procedure.
     * @param t start node
     * @return validity of growth
     **/
    @SuppressWarnings({ "LoopStatementThatDoesntLoop" })
    boolean growNode(DerivationNode t) {
        Symbol s = t.getData();
        if (this.getDepth() > grammy.getMaxDerivationTreeDepth()) {
            logger.warn("maxDerivationTreeDepth exceeded:" + this.getDepth() + ">" + grammy.maxDerivationTreeDepth);
            return false;
        }
        if (this.geneCnt > this.genny.getMaxChromosomeLength()) {
            logger.warn("maxGEChromosomeLength exceeded:" + this.geneCnt + ">" + genny.getMaxChromosomeLength());
            return false;
        }

        if (s.getType() == Enums.SymbolType.NTSymbol) {
            Rule r = this.grammy.findRule(s);
            int numProd = r.size();
            if (!this.genIter.hasNext()) {
                this.wrapCount++;
                this.genIter = genny.iterator();
            }

            //Use a codon if there is more then one production
            while (this.wrapCount <= this.grammy.getMaxWraps()) {
                Production p;
                int currentCodonValue;
                if (numProd > 1) {
                    currentCodonValue = this.genIter.next();
                    t.setCodonIndex(geneCnt,
                            /* currentCodonValue,*/
                            currentCodonValue % numProd);
                    this.geneCnt++;
                    p = r.get(currentCodonValue % numProd);
                } else {
                    p = r.get(0);
                }
                Iterator<Symbol> symIt = p.iterator();
                DerivationNode newTree;
                Symbol newSym;
                while (symIt.hasNext()) {
                    newSym = symIt.next();
                    // Check for GECodonValue
                    if (newSym.getType() == Enums.SymbolType.NTSymbol
                            && newSym.getSymbolString().startsWith(Constants.GE_CODON_VALUE_PARSING)) {
                        String value;

                        // Create a new symbol from the GE codon value
                        // GE Codon uses a codon from the genotype
                        if (!this.genIter.hasNext()) {
                            this.genIter = genny.iterator();
                            this.wrapCount++;
                        }
                        this.geneCnt++;
                        currentCodonValue = this.genIter.next();

                        try {
                            value = getGECodonValue(newSym.getSymbolString(), currentCodonValue);
                        } catch (MalformedGrammarException e) {
                            logger.error("Malformed GECodonValue specification in grammar");
                            return false;
                        }
                        newSym = new Symbol(value, Enums.SymbolType.TSymbol);
                    }
                    newTree = new DerivationNode(t, newSym);
                    t.add(newTree);
                    this.setCurrentLevel(this.getCurrentLevel() + 1);
                    if (this.getDepth() < this.getCurrentLevel()) {
                        this.setDepth(this.getCurrentLevel());
                    }
                    if (!growNode(newTree)) {
                        return false;
                    }
                    this.setCurrentLevel(this.getCurrentLevel() - 1);
                }
                return true;
            }
            return false;
        } else {
            this.grammy.getPhenotype().add(s);

            //System.out.print(s.getSymbolString() + " ");
            //System.out.print(" p:"+this.grammy.phenotype.getString() + " ");
            return true;
        }
    }

    /**
     * Get the number of wraps used
     * @return wraps used
     */
    public int getWrapCount() {
        return wrapCount;
    }

    /**
     * Get the input used, called genes
     * @return number of inputs used
     */
    public int getGeneCnt() {
        return geneCnt;
    }

    public boolean derive() {
        return this.buildDerivationTree();
    }
}