package jcgp.backend.modules.problem; import jcgp.backend.function.SymbolicRegressionFunctions; import jcgp.backend.parameters.BooleanParameter; import jcgp.backend.parameters.DoubleParameter; import jcgp.backend.parameters.ParameterStatus; import jcgp.backend.population.Population; import jcgp.backend.resources.Resources; /** * Symbolic regression functions *

* Using this problem type, regression problems can be solved. * {@code parseData()} must be used to load the desired function * data in the standard CGP .dat format. *

* This problem uses quite a few parameters: * * * * @see SymbolicRegressionFunctions * @author Eduardo Pedroni * */ public class SymbolicRegressionProblem extends TestCaseProblem { private DoubleParameter errorThreshold, perfectionThreshold; private BooleanParameter hitsBasedFitness; /** * Creates a new instance of SymbolicRegressionProblem. * * @param resources a reference to the experiment's resources. */ public SymbolicRegressionProblem(Resources resources) { super(resources); setFunctionSet(new SymbolicRegressionFunctions()); setName("Symbolic regression"); setFileExtension(".dat"); errorThreshold = new DoubleParameter(0.01, "Error threshold") { @Override public void validate(Number newValue) { if (newValue.doubleValue() < 0) { status = ParameterStatus.INVALID; status.setDetails("Error threshold must be a positive value."); } else if (newValue.doubleValue() == 0) { status = ParameterStatus.WARNING; status.setDetails("An error threshold of 0 is very rigorous and difficult to achieve."); } else { status = ParameterStatus.VALID; } } }; perfectionThreshold = new DoubleParameter(0.000001, "Perfection threshold") { @Override public void validate(Number newValue) { if (newValue.doubleValue() < 0) { status = ParameterStatus.INVALID; status.setDetails("Perfection threshold must be a positive value."); } else if (newValue.doubleValue() == 0) { status = ParameterStatus.WARNING; status.setDetails("A perfection threshold of 0 is very rigorous and difficult to achieve."); } else { status = ParameterStatus.VALID; } } }; hitsBasedFitness = new BooleanParameter(false, "Hits-based fitness"); registerParameters(errorThreshold, perfectionThreshold, hitsBasedFitness); } @Override public void evaluate(Population population, Resources resources) { // for every chromosome in the population for (int i = 0; i < getResources().populationSize(); i++) { // assume an initial fitness of 0 double fitness = 0; // for each test case for (int t = 0; t < testCases.size(); t++) { population.get(i).setInputs((Object[]) testCases.get(t).getInputs()); // check each output for (int o = 0; o < getResources().outputs(); o++) { Double cgpValue = (Double) population.get(i).getOutput(o).calculate(); Double dataValue = testCases.get(t).getOutput(o); if (hitsBasedFitness.get()) { if (Math.abs(cgpValue - dataValue) <= errorThreshold.get()) { fitness++; } } else { fitness += 1 - Math.abs(cgpValue - dataValue); } } } // assign the resulting fitness to the respective individual population.get(i).setFitness(fitness); } } @Override public TestCase parseTestCase(String[] inputs, String[] outputs) { // cast the test case values to UnsignedInteger Double[] inputCases = new Double[inputs.length]; Double[] outputCases = new Double[outputs.length]; for (int i = 0; i < inputCases.length; i++) { inputCases[i] = Double.parseDouble(inputs[i]); } for (int o = 0; o < outputCases.length; o++) { outputCases[o] = Double.parseDouble(outputs[o]); } return new TestCase(inputCases, outputCases); } @Override public int hasPerfectSolution(Population population) { // higher fitness is better for (int i = 0; i < getResources().populationSize(); i++) { if (population.get(i).getFitness() >= maxFitness.get() - perfectionThreshold.get()) { return i; } } return -1; } }