/* * 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.rank.fitness; import java.util.Map; import java.util.function.ToDoubleBiFunction; import org.oakgp.Assignments; import org.oakgp.node.Node; /** * Calculates the fitness of a potential solution by comparing its results against the expected values. * * @param <T> * the type of the expected values */ public final class TestDataFitnessFunction<T> implements FitnessFunction { private final Map<Assignments, T> tests; private final ToDoubleBiFunction<T, T> rankingFunction; /** * Returns a new {@code FitnessFunction} which uses the specified test data to assess the fitness of potential solutions. * * @param tests * test data which associates a collection of inputs with their expected outcomes */ public static TestDataFitnessFunction<Integer> createIntegerTestDataFitnessFunction(Map<Assignments, Integer> tests) { return new TestDataFitnessFunction<>(tests, (e, a) -> Math.abs(e - a)); } /** * Creates a {@code TestDataFitnessFunction} which uses the given test cases and ranking function to determine the suitability of candidates. * * @param tests * a collection of test cases which associate an {@code Assignments} (used as input to {@link Node#evaluate(Assignments)}) with the corresponding * expected outcome * @param rankingFunction * accepts the expected value (as the first argument) and the actual value (as the second argument) of applying a test case (as represented as an * entry in {@code tests} and returns a fitness value */ public TestDataFitnessFunction(Map<Assignments, T> tests, ToDoubleBiFunction<T, T> rankingFunction) { this.tests = tests; this.rankingFunction = rankingFunction; } /** * Evaluates the specified {@code Node} using the test data specified when this {@code FitnessFunction} was constructed. * * @param node * the potential solution that whose fitness will be determined * @return the accumulative difference between the expected and actual outputs of evaluating {@code node} using each of the inputs of the test data */ @Override public double evaluate(Node node) { // TODO there is a possibility that the returned result may be 'Infinity' or '-Infinity' - is that OK or is BigDecimal required? double diff = 0; for (Map.Entry<Assignments, T> test : tests.entrySet()) { Assignments input = test.getKey(); T expected = test.getValue(); T actual = node.evaluate(input); diff += rankingFunction.applyAsDouble(expected, actual); } return diff; } }