package jcgp.backend.modules.problem; import jcgp.backend.function.DoubleArithmetic; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; import jcgp.backend.resources.Resources; import jcgp.backend.resources.parameters.BooleanParameter; import jcgp.backend.resources.parameters.DoubleParameter; import jcgp.backend.resources.parameters.Parameter; import jcgp.backend.resources.parameters.ParameterStatus; public class SymbolicRegressionProblem extends TestCaseProblem { private DoubleParameter errorThreshold, perfectionThreshold; private BooleanParameter hitsBasedFitness; public SymbolicRegressionProblem(Resources resources) { super(resources); functionSet = new DoubleArithmetic(); 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(true, "Hits-based fitness") { @Override public void validate(Boolean newValue) { // blank } }; } @Override public void evaluate(Population population, Resources resources) { // set fittest to 0, change it whenever a fitter one is found population.setFittest(0); // for every chromosome in the population for (int i = 0; i < resources.populationSize(); i++) { // assume an initial fitness of 0 double fitness = 0; // for each test case for (int t = 0; t < testCases.size(); t++) { population.getChromosome(i).setInputs((Object[]) testCases.get(t).getInputs()); // check each output for (int o = 0; o < resources.outputs(); o++) { Double cgpValue = (Double) population.getChromosome(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.getChromosome(i).setFitness(fitness); if (fitness >= population.getFittest().getFitness()) { population.setFittest(i); } } } @Override public String toString() { return "Symbolic regression"; } @Override public void addTestCase(String[] inputs, String[] outputs) { 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]); } addTestCase(new TestCase(inputCases, outputCases)); } @Override public boolean isPerfectSolution(Chromosome fittest) { return fittest.getFitness() >= maxFitness.get() - perfectionThreshold.get(); } @Override public Parameter[] getLocalParameters() { return new Parameter[]{maxFitness, errorThreshold, perfectionThreshold, hitsBasedFitness}; } @Override public String getFileExtension() { return ".dat"; } }