/* * Artificial Intelligence for Humans * Volume 2: Nature Inspired Algorithms * Java Version * http://www.aifh.org * http://www.jeffheaton.com * * Code repository: * https://github.com/jeffheaton/aifh * * Copyright 2014 by Jeff Heaton * * 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 com.heatonresearch.aifh.genetic.trees; import com.heatonresearch.aifh.randomize.GenerateRandom; import java.util.HashSet; import java.util.Set; /** * Evaluate a tree. Used for genetic programming. * * References: * * http://en.wikipedia.org/wiki/Reservoir_sampling * */ public abstract class EvaluateTree { /** * Choose a random tree node. Uses reservoir sampling. * @param rnd A random number generator. * @param parent The parent node. * @param current The current node in our traversal. * @param index The current index, it is an array for pass by reference. * @param reservoir The reservoir. */ private void internalSampleRandomNode(GenerateRandom rnd, TreeGenomeNode parent, TreeGenomeNode current, int[] index, RandomNodeResult reservoir) { int currentIndex = index[0]; index[0]++; // determine if we replace the reservoir int j = rnd.nextInt(0, currentIndex + 1); if (j == 0) { reservoir.setParent(parent); reservoir.setChild(current); } // traverse on to the children for (TreeGenomeNode child : current.getChildren()) { internalSampleRandomNode(rnd, current, child, index, reservoir); } } /** * Choose a random node from the tree. Uses reservoir sampling. * @param rnd Random number generator. * @param root The root of the tree. * @return A random node. */ public RandomNodeResult sampleRandomNode(GenerateRandom rnd, TreeGenomeNode root) { int[] index = new int[1]; RandomNodeResult reservoir = new RandomNodeResult(); index[0] = 0; internalSampleRandomNode(rnd, null, root, index, reservoir); return reservoir; } /** * @return The first opcode for the variable and constant nodes. */ public abstract int getVarConstOpcode(); /** * @return The number of constants supported. */ public abstract int getNumConst(); /** * @return The number of variables supported. */ public abstract int getNumVar(); /** * Evaluate the specified node. * @param node The node to evaluate. * @param varValues The variable values. * @return The result of the evaluation. */ public abstract double evaluate(TreeGenomeNode node, double[] varValues); /** * Determine the number of children the specified opcode can have. * @param opcode The opcode. * @return The number of children this opcode can have. */ public abstract int determineChildCount(int opcode); /** * @return The total number of opcodes. */ public int opcodeCount() { return getVarConstOpcode() + getNumVar() + getNumConst(); } /** * Choose a random opcode, choose between both leafs and nodes. * @param rnd A random number generator. * @return A random opcode. */ public int chooseRandomOpcode(GenerateRandom rnd) { return rnd.nextInt(0, opcodeCount()); } /** * Choose a random opcode, choose between only leafs. * @param rnd A random number generator. * @return A random opcode. */ public int chooseRandomLeafOpcode(GenerateRandom rnd) { return getVarConstOpcode() + rnd.nextInt(getNumVar() + getNumConst()); } /** * Choose a random opcode, choose between only nodes. * @param rnd A random number generator. * @return A random opcode. */ public int chooseRandomNodeOpcode(GenerateRandom rnd) { return rnd.nextInt(getVarConstOpcode()); } /** * Grow the tree randomly by the specified max depth. * @param rnd A random number generator. * @param maxDepth The max depth. * @return The tree. */ public TreeGenomeNode grow(GenerateRandom rnd, int maxDepth) { if (maxDepth == 1) { return new TreeGenomeNode(chooseRandomLeafOpcode(rnd)); } else { TreeGenomeNode result = new TreeGenomeNode(chooseRandomNodeOpcode(rnd)); int childCount = determineChildCount(result.getOpcode()); for (int i = 0; i < childCount; i++) { result.getChildren().add(grow(rnd, maxDepth - 1)); } return result; } } /** * @return A set of leaf opcodes. */ public Set<Integer> getLeafSet() { Set<Integer> result = new HashSet<Integer>(); for (int i = this.getVarConstOpcode(); i < opcodeCount(); i++) { result.add(i); } return result; } /** * @return A set of node opcodes. */ public Set<Integer> getNodeSet() { Set<Integer> result = new HashSet<Integer>(); for (int i = 0; i < this.getVarConstOpcode(); i++) { result.add(i); } return result; } }