
/*
 * GA.class
 * Part of package project
 *
 * Author: Philip Bradley
 *
 */

package project;


import java.util.Random;

public class GA implements Cloneable {

  private GeometricGraph g;
  private int[][] adjacancyMatrix;
  private Population gaPopulation;
  private Random numberGen;
  private int order = 0;
  private int partitionSize = 0;
  private int partitionFactor = 0;
  private double solutionCost = 0;
  private int populationSize = 0;
  
  public GA(GeometricGraph graph, int k, int popSize, int maxNonImprovingIterations, int localOptCutoff) {
    Solution[] offSpring = new Solution[2];
    gaPopulation = new Population(popSize);
    int mutatePoint = 3; 
    populationSize = popSize;
    g = graph;
    order = graph.getOrder();
    boolean replacedSolution;
    KerLin kl = new KerLin(g, k);
    int iterationCount = 0;
    long startTime;
    long finishTime;
    
    System.out.println("# Performing GA on graph of order " + order + " and size " + g.getSize());
    
    if (g.isConnected()) {
      System.out.println("# Graph is connected.");
      g.BFSReordering(0);
    }
    
    if ((order % k) == 0) {		// Only divide graph evenly
      adjacancyMatrix = g.getAdjacancyMatrix();
      partitionFactor = k;
      partitionSize = (int)order/k;
      offSpring[0] = (Solution)null;
      offSpring[1] = (Solution)null;
      
      numberGen = new Random();
      int splitSize = 0;
      int index = 0;   
      int count = 0;
      
      System.out.println("# Creating new population....");
      for (int i=0; i<populationSize; i++) {
	gaPopulation.replace(new Solution(g, partitionFactor));
	System.out.print('#');
      }
      System.out.println("");
      System.out.println("# Best solution: " + gaPopulation.getLowestCost());
      
      // Main GA loop
      startTime = System.currentTimeMillis();		      // This must be outside the try block
                                                              // otherwise compile error: may not be initialise
      
      try {
	while (count < maxNonImprovingIterations) {	
	  // count is incremented every time an offspring is generated but not replaced into the population	
	  
	  offSpring = gaPopulation.getParents();
	  offSpring = offSpring[0].randomSinglePointCrossOver(offSpring[1]);

	  offSpring[0] = offSpring[0].mutate(mutatePoint, 5);
	  offSpring[1] = offSpring[1].mutate(mutatePoint, 5);
	  offSpring[0].postProcess();
	  offSpring[1].postProcess();

	  KerLin kl1 = new KerLin(g, k);
	  kl1.setSolution(offSpring[0].getPartition());
	  offSpring[0].setPartition(kl1.kerLinQuickOptimise(order));

	  kl1.setSolution(offSpring[1].getPartition());
	  offSpring[1].setPartition(kl1.kerLinQuickOptimise(order));

	  replacedSolution = gaPopulation.replace(offSpring[1]); 
	  replacedSolution |= gaPopulation.replace(offSpring[0]);
	  
	  if (replacedSolution) {
	    count = 0;
	  } else {
	    count++;
	  }
	  iterationCount++;
	  
	  System.out.print("# Iteration: " + iterationCount + " Best solution: " + gaPopulation.getLowestCost());
	  System.out.println(" Worst: " + gaPopulation.getHighestCost());
	}
      } catch (Exception e) {
	System.err.println("Error: " + e.getMessage());
	System.exit(1);
      }
      finishTime = System.currentTimeMillis();
      System.out.print("# Total Iterations: " + iterationCount + " Running Time: " + (finishTime - startTime));
      System.out.println(" Best Solution: " +  gaPopulation.getLowestCost());
    } else {	
      System.err.println("Error: Can only divide graph evenly!");
    }
  }
  
  public static void main(String[] args) {
    
    if (args.length == 5) {
      String graphName = new String(args[0]);
      Integer split = new Integer(args[1]);
      Integer populationSize = new Integer(args[2]);
      Integer iterations = new Integer(args[3]);
      Integer localOptCutoff = new Integer(args[4]);
      
      System.out.print("# DeSerialising....");
      GeometricGraph g = GeometricGraph.readFromFile(graphName);
      System.out.println(" Finished!");
      GA ga = new GA(g, split.intValue(), populationSize.intValue(), iterations.intValue(), localOptCutoff.intValue());
    } else {
      System.err.println("Usage Error: GA GraphFileName SplitSize PopulationSize MaxNonImprovingIterations KerLinxchange size");
    }
  }
}

