package ac.essex.gp.problems;

import ac.essex.gp.params.GPParams;
import ac.essex.gp.params.NodeParams;
import ac.essex.gp.individuals.Individual;
import ac.essex.gp.util.DataStack;
import ac.essex.gp.nodes.meta.*;
import ac.essex.gp.Evolve;
import ac.essex.gp.interfaces.ConsoleInterface;

/**
 * <p>
 * MetaProblem uses the sxGP system itself to derive the best set of
 * parameters for another problem
 * </p>
 *
 * <p/>
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version,
 * provided that any use properly credits the author.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details at http://www.gnu.org
 * </p>
 *
 * @author Olly Oechsle, University of Essex, Date: 18-Jan-2007
 * @version 1.0 [Experimental]
 */
public class MetaProblem extends Problem {

    public String getName() {
        return "Meta GP Problem";
    }

    Problem otherProblem;

    public MetaProblem(Problem otherProblem) {
        this.otherProblem = otherProblem;
    }

    public void initialise(Evolve e, GPParams params) {

        // We'd like to program the following sxGp parameters
        params.registerNode(new BreedSizePercentage());
        params.registerNode(new CrossoverProbability());
        params.registerNode(new ERCJitterProbability());
        params.registerNode(new ERCMutateProbability());
        params.registerNode(new PointMutationProbability());
        params.registerNode(new PopulationSize());
        params.registerNode(new TournamentSizePercentage());
        params.registerNode(new MaxTreeSize());

        // end of the GA
        params.registerNode(new End());

        // we'll only be using voids for this
        params.setReturnType(NodeParams.VOID);


    }

    public void customiseParameters(GPParams params) {

        // Allow up to 6 parameters per individual
        params.setMaxTreeSize(9);

        // Small population - this will be very slow
        params.setPopulationSize(20);

        params.setEliteCount(1);

        // run for a while
        params.setGenerations(100);

        // Crossover won't work well, so leave it
        params.setCrossoverProbability(0);

        // optimisation is not required
        params.setOptimisationEnabled(false);

    }


    public int getClassCount() {
        return 1;
    }

    public void evaluate(Individual ind, DataStack data) {

        // Create a new params file
        GPParams otherParams = new GPParams();

        // allow the other problem to initialise it first (sets up its own nodes etc)
        otherProblem.initialise(null, otherParams);

        // ensure the other problem only runs for 10 generations
        otherParams.setGenerations(10);

        // Add it to the data stack so the params may be edited by the GA
        data.add("params", otherParams);

        // execute the individual - makes various changes to the params file
        ind.execute(data);

        // We'll run the gp on the console
        ConsoleInterface gpinterface = new ConsoleInterface();

        double score = 0;

        try {

            // Run the GP multiple times (helps remove variability caused by the random numbers)
            for (int i = 0; i < 5; i++) {

                // The Evolve constructor allows us to insert the custom params file
                Evolve gp = new Evolve(otherProblem, otherParams, gpinterface);

                gp.run();

                // Score is based on GP efficiency - how many individuals were evaluated
                score += gpinterface.getTotalIndividualsEvaluated();

                // add a penalty if the GP didn't get the ideal individual
                if (!gpinterface.bestIndividualWasIdeal()) score += 100;

            }

        } catch (Exception e) {
            // if this configuration causes errors, ignore it.
            score = Double.MAX_VALUE;
        }

        // set the fitness of the individual
        ind.setKozaFitness(score);

    }

}
