ProgramHelper.java

package net.bmahe.genetics4j.gp.program;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.random.RandomGenerator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import net.bmahe.genetics4j.gp.OperationFactory;

public class ProgramHelper {
	public final static Logger logger = LogManager.getLogger(ProgramHelper.class);

	private final RandomGenerator randomGenerator;

	public ProgramHelper(final RandomGenerator _randomGenerator) {
		Objects.requireNonNull(_randomGenerator);

		this.randomGenerator = _randomGenerator;
	}

	public OperationFactory pickRandomFunction(final Program program) {
		Objects.requireNonNull(program);
		Validate.isTrue(program.functions()
				.size() > 0);

		final Set<OperationFactory> functions = program.functions();
		return functions.stream()
				.skip(randomGenerator.nextInt(functions.size()))
				.findFirst()
				.get();
	}

	public <T> OperationFactory pickRandomFunction(final Program program, final Class<T> requiredClass) {
		Objects.requireNonNull(program);
		Objects.requireNonNull(requiredClass);
		Validate.isTrue(program.functions()
				.size() > 0);

		final Set<OperationFactory> functions = program.functions();
		@SuppressWarnings("unchecked")
		final List<OperationFactory> candidates = functions.stream()
				.filter((operationFactory) -> operationFactory.returnedType()
						.isAssignableFrom(requiredClass))
				.collect(Collectors.toList());

		if (candidates == null || candidates.isEmpty()) {
			throw new IllegalArgumentException("Could not find a suitable function returning a " + requiredClass);
		}

		return candidates.get(randomGenerator.nextInt(candidates.size()));
	}

	public <T> OperationFactory pickRandomTerminal(final Program program, final Class<T> requiredClass) {
		Objects.requireNonNull(program);
		Objects.requireNonNull(requiredClass);

		final Set<OperationFactory> terminals = program.terminal();
		@SuppressWarnings("unchecked")
		final List<OperationFactory> candidates = terminals.stream()
				.filter((operationFactory) -> operationFactory.returnedType()
						.isAssignableFrom(requiredClass))
				.collect(Collectors.toList());

		if (candidates == null || candidates.isEmpty()) {
			throw new IllegalArgumentException("Could not find a suitable terminal returning a " + requiredClass);
		}

		return candidates.get(randomGenerator.nextInt(candidates.size()));
	}

	public OperationFactory pickRandomTerminal(final Program program) {
		Objects.requireNonNull(program);

		final Set<OperationFactory> terminals = program.terminal();
		final List<OperationFactory> candidates = terminals.stream()
				.collect(Collectors.toList());

		return candidates.get(randomGenerator.nextInt(candidates.size()));
	}

	public OperationFactory pickRandomFunctionOrTerminal(final Program program) {
		Objects.requireNonNull(program);

		final Set<OperationFactory> terminals = program.terminal();
		final Set<OperationFactory> functions = program.functions();
		final int totalNumberCandidates = terminals.size() + functions.size();

		final Stream<OperationFactory> candidates = Stream.concat(terminals.stream(), functions.stream());
		final int chosenCandidate = randomGenerator.nextInt(totalNumberCandidates);

		return candidates.skip(chosenCandidate)
				.findFirst()
				.get();
	}

	public <T> OperationFactory pickRandomFunctionOrTerminal(final Program program, final Class<T> requiredClass) {
		Objects.requireNonNull(program);
		Objects.requireNonNull(requiredClass);

		final Set<OperationFactory> terminals = program.terminal();
		final Set<OperationFactory> functions = program.functions();

		final Stream<OperationFactory> candidates = Stream.concat(terminals.stream(), functions.stream());

		@SuppressWarnings("unchecked")
		final List<OperationFactory> filteredCandidates = candidates
				.filter((operationFactory) -> operationFactory.returnedType()
						.isAssignableFrom(requiredClass))
				.collect(Collectors.toList());

		final int filteredCandidatesCount = filteredCandidates.size();
		if (filteredCandidatesCount == 0) {
			logger.error("No candidate terminals or functions found that can return an instance of class {}",
					requiredClass);
			logger.debug("\tKnown terminals: {}",
					program.terminal()
							.stream());
			logger.debug("\tKnown functions: {}", program.functions());

			throw new IllegalArgumentException(
					"No candidate terminals or functions found that can return an instance of class " + requiredClass);
		}

		final int chosenCandidate = randomGenerator.nextInt(filteredCandidates.size());

		return filteredCandidates.get(chosenCandidate);
	}
}