Package org.encog.ml.prg

Source Code of org.encog.ml.prg.EncogProgram

/*
* Encog(tm) Core v3.3 - Java Version
* http://www.heatonresearch.com/encog/
* https://github.com/encog/encog-java-core
* Copyright 2008-2014 Heaton Research, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*  
* For more information on Heaton Research copyrights, licenses
* and trademarks visit:
* http://www.heatonresearch.com/copyright
*/
package org.encog.ml.prg;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.encog.ml.MLError;
import org.encog.ml.MLRegression;
import org.encog.ml.data.MLData;
import org.encog.ml.data.MLDataSet;
import org.encog.ml.data.basic.BasicMLData;
import org.encog.ml.ea.exception.EACompileError;
import org.encog.ml.ea.exception.EARuntimeError;
import org.encog.ml.ea.genome.BasicGenome;
import org.encog.ml.ea.genome.Genome;
import org.encog.ml.prg.expvalue.ExpressionValue;
import org.encog.ml.prg.expvalue.ValueType;
import org.encog.ml.prg.extension.FunctionFactory;
import org.encog.ml.prg.extension.StandardExtensions;
import org.encog.ml.prg.train.PrgPopulation;
import org.encog.ml.tree.traverse.tasks.TaskGetNodeIndex;
import org.encog.ml.tree.traverse.tasks.TaskReplaceNode;
import org.encog.parse.expression.common.ParseCommonExpression;
import org.encog.parse.expression.common.RenderCommonExpression;
import org.encog.parse.expression.epl.ParseEPL;
import org.encog.parse.expression.epl.RenderEPL;
import org.encog.parse.expression.rpn.RenderRPN;
import org.encog.util.simple.EncogUtility;

/**
* Holds an Encog Programming Language (EPL) program. A Encog program is
* internally represented as a tree structure. It can be rendered to a variety
* of forms, such as RPN, common infix expressions, or Latex. The Encog
* Workbench also allows display as a graphical tree. An Encog Program is both a
* genome and phenome. No decoding is necessary.
*
* Every Encog Program has a context. The context is the same for every Encog
* Program in a population. The context defines which opcodes should be used, as
* well as the defined variables.
*
* The actual values for the variables are not stored in the context. Rather
* they are stored in a variable holder. Each program usually has its own
* variable holder, though it is possible to share.
*/
public class EncogProgram extends BasicGenome implements MLRegression, MLError {

  /**
   * The serial id.
   */
  private static final long serialVersionUID = 1L;

  /**
   * Parse the specified program, or expression, and return the result. No
   * variables can be defined for this as a default context is used. The
   * result is returned as a boolean.
   *
   * @param str
   *            The program expression.
   * @return The value the expression was evaluated to.
   */
  public static boolean parseBoolean(final String str) {
    final EncogProgram holder = new EncogProgram(str);
    return holder.evaluate().toBooleanValue();
  }

  /**
   * Parse the specified program, or expression, and return the result. No
   * variables can be defined for this as a default context is used. The
   * result is returned as a boolean.
   *
   * @param str
   *            The program expression.
   * @return The value the expression was evaluated to.
   */
  public static ExpressionValue parseExpression(final String str) {
    final EncogProgram holder = new EncogProgram(str);
    return holder.evaluate();
  }

  /**
   * Parse the specified program, or expression, and return the result. No
   * variables can be defined for this as a default context is used. The
   * result is returned as a float.
   *
   * @param str
   *            The program expression value.
   * @return The value the expression was evaluated to.
   */
  public static double parseFloat(final String str) {
    final EncogProgram holder = new EncogProgram(str);
    return holder.evaluate().toFloatValue();
  }

  /**
   * Parse the specified program, or expression, and return the result. No
   * variables can be defined for this as a default context is used. The
   * result is returned as a string.
   *
   * @param str
   *            The program expression value.
   * @return The value the expression was evaluated to.
   */
  public static String parseString(final String str) {
    final EncogProgram holder = new EncogProgram(str);
    return holder.evaluate().toStringValue();
  }

  /**
   * The variables that will be used by this Encog program.
   */
  private EncogProgramVariables variables = new EncogProgramVariables();

  /**
   * The context that this Encog program executes in, the context is typically
   * shared with other Encog programs.
   */
  private EncogProgramContext context = new EncogProgramContext();

  /**
   * The root node of the program.
   */
  private ProgramNode rootNode;
 
  /**
   * Holds extra data that might be needed by user extended opcodes.
   */
  private Map<String,Object> extraData = new HashMap<String,Object>();

  /**
   * Construct the Encog program and create a default context and variable
   * holder. Use all available opcodes.
   */
  public EncogProgram() {
    this(new EncogProgramContext(), new EncogProgramVariables());
    StandardExtensions.createAll(this.context);
  }

  /**
   * Construct the Encog program with the specified context, but create a new
   * variable holder.
   *
   * @param theContext
   *            The context.
   */
  public EncogProgram(final EncogProgramContext theContext) {
    this(theContext, new EncogProgramVariables());
  }

  /**
   * Construct an Encog program using the specified context and variable
   * holder.
   *
   * @param theContext
   *            The context.
   * @param theVariables
   *            The variable holder.
   */
  public EncogProgram(final EncogProgramContext theContext,
      final EncogProgramVariables theVariables) {
    this.context = theContext;
    this.variables = theVariables;

    // define variables
    for (final VariableMapping v : this.context.getDefinedVariables()) {
      this.variables.defineVariable(v);
    }
  }

  /**
   * Construct an Encog program using the specified expression, but create an
   * empty context and variable holder.
   *
   * @param expression
   *            The expression.
   */
  public EncogProgram(final String expression) {
    this();
    compileExpression(expression);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public double calculateError(final MLDataSet data) {
    return EncogUtility.calculateRegressionError(this, data);
  }

  /**
   * Compile the specified EPL into an actual program node structure, for
   * later execution.
   *
   * @param code The code to compile.
   * @return The root node.
   */
  public ProgramNode compileEPL(final String code) {
    final ParseEPL parser = new ParseEPL(this);
    this.rootNode = parser.parse(code);
    return this.rootNode;
  }

  /**
   * Compile the specified expression.
   *
   * @param expression
   *            The expression.
   * @return The program node that this was compiled into.
   */
  public ProgramNode compileExpression(final String expression) {
    final ParseCommonExpression parser = new ParseCommonExpression(this);
    this.rootNode = parser.parse(expression);
    return this.rootNode;
  }

  /**
   * Compute the output from the input MLData. The individual values of the
   * input will be mapped to the variables defined in the context. The order
   * is the same between the input and the defined variables. The input will
   * be mapped to the appropriate types. Enums will use their ordinal number.
   * The result will be a single number MLData.
   *
   * @param input
   *            The input to the program.
   * @return A single numer MLData.
   */
  @Override
  public MLData compute(final MLData input) {
    if (input.size() != getInputCount()) {
      throw new EACompileError("Invalid input count.");
    }

    for (int i = 0; i < input.size(); i++) {
      this.variables.setVariable(i, input.getData(i));
    }

    final ExpressionValue v = this.rootNode.evaluate();
    final VariableMapping resultMapping = getResultType();

    final MLData result = new BasicMLData(1);
    boolean success = false;

    switch (resultMapping.getVariableType()) {
    case floatingType:
      if (v.isNumeric()) {
        result.setData(0, v.toFloatValue());
        success = true;
      }
      break;
    case stringType:
      result.setData(0, v.toFloatValue());
      success = true;
      break;
    case booleanType:
      if (v.isBoolean()) {
        result.setData(0, v.toBooleanValue() ? 1.0 : 0.0);
        success = true;
      }
      break;
    case intType:
      if (v.isNumeric()) {
        result.setData(0, v.toIntValue());
        success = true;
      }
      break;
    case enumType:
      if (v.isEnum()) {
        result.setData(0, v.toIntValue());
        success = true;
      }
      break;
    }

    if (!success) {
      throw new EARuntimeError("EncogProgram produced "
          + v.getExpressionType().toString() + " but "
          + resultMapping.getVariableType().toString()
          + " was expected.");
    }

    return result;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void copy(final Genome source) {
    // not needed, already a genome
  }

  /**
   * @return The string as a common "infix" expression.
   */
  public String dumpAsCommonExpression() {
    final RenderCommonExpression render = new RenderCommonExpression();
    return render.render(this);
  }

  /**
   * Execute the program and return the result.
   *
   * @return The result of running the program.
   */
  public ExpressionValue evaluate() {
    return this.rootNode.evaluate();
  }

  /**
   * Find the specified node by index. The tree is traversed to do this. This
   * is typically used to select a random node.
   *
   * @param index
   *            The index being sought.
   * @return The program node found.
   */
  public ProgramNode findNode(final int index) {
    return (ProgramNode) TaskGetNodeIndex.process(index, this.rootNode);
  }

  /**
   * @return The string as an EPL expression. EPL is the format that
   *         EncogPrograms are usually persisted as.
   */
  public String generateEPL() {
    final RenderEPL render = new RenderEPL();
    return render.render(this);
  }

  /**
   * @return The program context. The program context may be shared over
   *         multiple programs.
   */
  public EncogProgramContext getContext() {
    return this.context;
  }

  /**
   * @return The function factory from the context.
   */
  public FunctionFactory getFunctions() {
    return this.context.getFunctions();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int getInputCount() {
    return this.variables.size();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int getOutputCount() {
    return 1;
  }

  /**
   * @return The variable mapping for the result type. This is obtained from
   *         the context.
   */
  private VariableMapping getResultType() {
    return ((PrgPopulation) getPopulation()).getContext().getResult();
  }

  /**
   * @return The return type, from the context.
   */
  public ValueType getReturnType() {
    return this.context.getResult().getVariableType();
  }

  /**
   * @return The root node of the program.
   */
  public ProgramNode getRootNode() {
    return this.rootNode;
  }

  /**
   * @return The variable holder.
   */
  public EncogProgramVariables getVariables() {
    return this.variables;
  }

  /**
   * Replace the specified node with another.
   *
   * @param replaceThisNode
   *            The node to replace.
   * @param replaceWith
   *            The node that is replacing that node.
   */
  public void replaceNode(final ProgramNode replaceThisNode,
      final ProgramNode replaceWith) {
    if (replaceThisNode == this.rootNode) {
      this.rootNode = replaceWith;
    } else {
      TaskReplaceNode
          .process(this.rootNode, replaceThisNode, replaceWith);
    }
  }

  /**
   * Select a random variable from the defined variables.
   *
   * @param rnd
   *            A random number generator.
   * @param desiredTypes
   *            The desired types that the variable can be.
   * @return The index of the defined variable, or -1 if unable to define.
   */
  public int selectRandomVariable(final Random rnd,
      final List<ValueType> desiredTypes) {
    List<VariableMapping> selectionSet = this.context
        .findVariablesByTypes(desiredTypes);
    if (selectionSet.size() == 0
        && desiredTypes.contains(ValueType.intType)) {
      final List<ValueType> floatList = new ArrayList<ValueType>();
      floatList.add(ValueType.floatingType);
      selectionSet = this.context.findVariablesByTypes(floatList);
    }

    if (selectionSet.size() == 0) {
      return -1;
    }

    final VariableMapping selected = selectionSet.get(rnd
        .nextInt(selectionSet.size()));
    return getContext().getDefinedVariables().indexOf(selected);
  }

  /**
   * Set the root node for the program.
   *
   * @param theRootNode
   *            The new root node.
   */
  public void setRootNode(final ProgramNode theRootNode) {
    this.rootNode = theRootNode;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int size() {
    return this.rootNode.size();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String toString() {
    final RenderRPN render = new RenderRPN();
    final String code = render.render(this);
    final StringBuilder result = new StringBuilder();
    result.append("[EncogProgram: size=");
    result.append(size());
    result.append(", score=");
    result.append(getScore());
    result.append(",code=");
    result.append(code);
    result.append("]");
    return result.toString();
  }

  /**
   * Get extra data that might be needed by user extended opcodes.
   * @param name The name the data was stored under.
   * @return The extra data.
   */
  public Object getExtraData(final String name) {
    return this.extraData.get(name);
  }
 
  /**
   * Set extra data that might be needed by extensions.
   * @param name The name of the data stored.
   * @param value The data.
   */
  public void setExtraData(final String name, final Object value) {
    this.extraData.put(name, value);
  }
}
TOP

Related Classes of org.encog.ml.prg.EncogProgram

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.