//=============================================================================
// Copyright 2006-2010 Daniel W. Dyer
//
// 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.
//=============================================================================
package org.uncommons.watchmaker.examples.geneticprogramming;
import java.util.Random;
import org.uncommons.maths.random.Probability;
import org.uncommons.watchmaker.framework.factories.AbstractCandidateFactory;
/**
* {@link org.uncommons.watchmaker.framework.CandidateFactory} for generating
* trees of {@link Node}s for the genetic programming example application.
* @author Daniel Dyer
*/
public class TreeFactory extends AbstractCandidateFactory<Node>
{
// The number of program parameters that each program tree will be provided.
private final int parameterCount;
// The maximum depth of a program tree. No function nodes will be created below
// this depth (branches will be terminated with parameters or constants).
private final int maximumDepth;
// Probability that a created node is a function node rather
// than a value node.
private final Probability functionProbability;
// Probability that a value (non-function) node is a parameter
// node rather than a constant node.
private final Probability parameterProbability;
/**
* @param parameterCount The number of program parameters that each
* generated program tree can will be provided when executed.
* @param maxDepth The maximum depth of generated trees.
* @param functionProbability The probability (between 0 and 1) that a
* randomly-generated node will be a function node rather than a value
* (parameter or constant) node.
* @param parameterProbability The probability that a randomly-generated
* non-function node will be a parameter node rather than a constant node.
*/
public TreeFactory(int parameterCount,
int maxDepth,
Probability functionProbability,
Probability parameterProbability)
{
if (parameterCount < 0)
{
throw new IllegalArgumentException("Parameter count must be greater than or equal to 0.");
}
if (maxDepth < 1)
{
throw new IllegalArgumentException("Max depth must be at least 1.");
}
this.parameterCount = parameterCount;
this.maximumDepth = maxDepth;
this.functionProbability = functionProbability;
this.parameterProbability = parameterProbability;
}
/**
* {@inheritDoc}
*/
public Node generateRandomCandidate(Random rng)
{
return makeNode(rng, maximumDepth);
}
/**
* Recursively constructs a tree of Nodes, up to the specified maximum depth.
* @param rng The RNG used to random create nodes.
* @param maxDepth The maximum depth of the generated tree.
* @return A tree of nodes.
*/
private Node makeNode(Random rng, int maxDepth)
{
if (functionProbability.nextEvent(rng) && maxDepth > 1)
{
// Max depth for sub-trees is one less than max depth for this node.
int depth = maxDepth - 1;
switch (rng.nextInt(5))
{
case 0: return new Addition(makeNode(rng, depth), makeNode(rng, depth));
case 1: return new Subtraction(makeNode(rng, depth), makeNode(rng, depth));
case 2: return new Multiplication(makeNode(rng, depth), makeNode(rng, depth));
case 3: return new IfThenElse(makeNode(rng, depth), makeNode(rng, depth), makeNode(rng, depth));
default: return new IsGreater(makeNode(rng, depth), makeNode(rng, depth));
}
}
else if (parameterProbability.nextEvent(rng))
{
return new Parameter(rng.nextInt(parameterCount));
}
else
{
return new Constant(rng.nextInt(11));
}
}
}