package pbilTwin;

/**
 * This class implements a deterministic variant of the Genetic Algorithm described in the script, section 5.1.1.
 */
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class PBILTwin {
	/** The length of the genotype of the individuals **/
	private int m_GenotypeLength;
	/** The number of individuals in the population **/
	private int m_Mu;
	/** The number of children generated **/
	private double m_Lambda;
	/** The number of single optimization steps to be evaluated **/
	private int m_OptimizationSteps;
	/** The number of times the experiment is repeated **/
	private int m_MultiRuns;
	/** Number of steps needed to reach best result **/
	private int m_OptimizationStepsNeeded = 0;
	/** mean fitness **/
	private double meanFitness;

	private double[] propVector;
	private double learnrate;

	/**
	 * This constructor sets up GeneticAlgorithm
	 * 
	 * @param genotypeLength
	 *            The length of the genotype of the individuals
	 * @param mu
	 *            The number of individuals in the population
	 * @param lambda
	 *            The number of children generated in each iteration
	 * @param optimizationSteps
	 *            The number of single optimization steps to be evaluated
	 *            (adjust as necessary)
	 * @param multiRuns
	 *            The number of times the experiment is repeated (at least 10)
	 */
	public PBILTwin(int genotypeLength, int mu, double lambda,
			int optimizationSteps, int multiRuns) {
		assert multiRuns > 9; // use run configurations: vmargument: -ea

		this.m_GenotypeLength = genotypeLength;
		this.m_Lambda = lambda;
		this.m_Mu = mu;
		this.m_OptimizationSteps = optimizationSteps;
		this.m_MultiRuns = multiRuns;
		this.propVector = new double[m_GenotypeLength];

		// this.learnrate = 1 / lambda;
		this.learnrate = 0.05;

	}

	/**
	 * This method will initialize the GeneticAlgorithm
	 */
	public void initialize() {

		m_OptimizationStepsNeeded = 0;
		for (int i = 0; i < propVector.length; i++) {
			propVector[i] = 0.5;
		}
	}

	/**
	 * This method will optimize the evaulateAsMaxiBits Problem. Use
	 * m_FitnessCallsNeeded to return the number of FitnessCalls (e.g., calling
	 * evaluateAsMaxiBits()) needed to find the optimum. The optimization should
	 * terminate after m_FitnessCalls.
	 */
	public void optimize() {
		for (int i = 0; i < m_OptimizationSteps; i++) {
			List<PBIndividualTwin> population = new ArrayList<PBIndividualTwin>();
			for (int j = 0; j < m_Mu; j++) {
				population.add(new PBIndividualTwin(propVector));
			}
			Collections.sort(population, Collections.reverseOrder()); // comparable
																		// implemente
																		// in
			// PBilIndividual m_population =
			// m_population.subList(0, m_Mu);
			// for (int j = 0; j < population.size(); j++) {
			// System.out.println(population.get(j).getStringRepresentation());
			// }
			List<PBIndividualTwin> m_Best = population.subList(0, m_Mu);
			// System.out.println("Print best:");
			// System.out.println(m_Best.get(0).getStringRepresentation());
			for (PBIndividualTwin p : m_Best) {
				for (int k = 0; k < m_GenotypeLength; k++) {
					propVector[k] = (propVector[k] * (1 - learnrate))
							+ ((p.getGenotype().get(k) ? 1 : 0) * learnrate);
				}
			}
			// System.out.println(Arrays.toString(propVector));
			double fitnessPV = 0;
			for (int j = 0; j < propVector.length; j++) {
				double p = propVector[j];
				if (j < (propVector.length / 2)) {
					fitnessPV += p;
				} else {
					fitnessPV -= p;
				}
			}
			double oldFitness = meanFitness;
			meanFitness = (m_GenotypeLength / 2) - Math.abs(fitnessPV);
			if (oldFitness != meanFitness) {
				m_OptimizationStepsNeeded++;
			}

		}
	}

	/**
	 * This main method will start a simple GeneticAlgorithm search. No
	 * arguments necessary.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO: parameters for the GeneticAlgorithm, adjust these values as
		// necessary
		int genotypeLength = 50;
		int mu = 15;
		int lambda = 50;
		int optimizationSteps = 1000;
		// int optimizationSteps = 100;
		int multiRuns = 100;
		PBILTwin program = new PBILTwin(genotypeLength, mu, lambda,
				optimizationSteps, multiRuns);
		int TmpMeanCalls = 0, TmpMeanFitness = 0;
		// perform repeated optimization
		for (int i = 0; i < program.m_MultiRuns; i++) {

			program.initialize();
			program.optimize();
			TmpMeanCalls += program.m_OptimizationStepsNeeded;
			TmpMeanFitness += program.meanFitness;
		}
		TmpMeanCalls = TmpMeanCalls / program.m_MultiRuns;
		TmpMeanFitness = TmpMeanFitness / program.m_MultiRuns;
		System.out.println("(" + program.m_MultiRuns + "/"
				+ program.m_OptimizationSteps + ") Mean Fitness : "
				+ TmpMeanFitness + " Mean Calls needed: " + TmpMeanCalls);
	}
}
