/*
* Copyright 2015 S. Webber
*
* 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.oakgp.generate;
import java.util.Objects;
import java.util.function.IntPredicate;
import org.oakgp.Type;
import org.oakgp.function.Function;
import org.oakgp.function.Signature;
import org.oakgp.node.FunctionNode;
import org.oakgp.node.Node;
import org.oakgp.primitive.PrimitiveSet;
import org.oakgp.util.Random;
/**
* Provides different strategies for creating a tree data structure.
* <p>
* Can be used to create randomly generate the initial population of a genetic programming run.
*
* @see #full(PrimitiveSet)
* @see #grow(PrimitiveSet, Random)
*/
public final class TreeGeneratorImpl implements TreeGenerator {
private final PrimitiveSet primitiveSet;
private final IntPredicate strategy;
/**
* Creates a {@code TreeGenerator} that uses the "full" approach to creating trees.
* <p>
* The "full" approach constructs trees where all terminal nodes (i.e. leaf nodes) are at the same depth.
*
* @param primitiveSet
* the collection of functions, variables and constants from which tree will be constructed
* @return a {@code TreeGenerator} that uses the "full" approach to creating trees.
*/
public static TreeGenerator full(PrimitiveSet primitiveSet) {
return new TreeGeneratorImpl(primitiveSet, d -> d > 0);
}
/**
* Creates a {@code TreeGenerator} that uses the "grow" approach to creating trees.
* <p>
* The "grow" approach constructs trees where terminal nodes (i.e. leaf nodes) are located at random depths, within a maximum limit.
*
* @param primitiveSet
* the collection of functions, variables and constants from which tree will be constructed
* @param random
* used to randomly determine the structure of the generated trees
* @return a {@code TreeGenerator} that uses the "grow" approach to creating trees.
*/
public static TreeGenerator grow(PrimitiveSet primitiveSet, Random random) {
return new TreeGeneratorImpl(primitiveSet, d -> d > 0 && random.nextBoolean());
}
/**
* @see #full(PrimitiveSet)
* @see #grow(PrimitiveSet, Random)
*/
private TreeGeneratorImpl(PrimitiveSet primitiveSet, IntPredicate strategy) {
Objects.requireNonNull(primitiveSet);
this.primitiveSet = primitiveSet;
this.strategy = strategy;
}
@Override
public Node generate(Type type, int depth) {
if (shouldCreateFunction(type, depth)) {
Function function = primitiveSet.nextFunction(type);
Signature signature = function.getSignature();
Node[] args = new Node[signature.getArgumentTypesLength()];
for (int i = 0; i < args.length; i++) {
Type argType = signature.getArgumentType(i);
Node arg = generate(argType, depth - 1);
args[i] = arg;
}
return new FunctionNode(function, args);
} else {
return primitiveSet.nextTerminal(type);
}
}
private boolean shouldCreateFunction(Type type, int depth) {
if (!primitiveSet.hasTerminals(type)) {
return true;
} else if (!primitiveSet.hasFunctions(type)) {
return false;
} else {
return strategy.test(depth);
}
}
}