package DE;
import java.io.Serializable;
import java.util.Random;


/**
 * Object to implement the differential evolution algorithm described in
 * the April 1997 issue of Dr. Dobbs journal (page ?).  This is a 
 * multi-threaded implementation.
 * Written by Chris Studholme
 * Licenced for redistribution under the GNU General Public Licence
 */
public class DE implements Serializable {

    /**
     * A daemon thread to manipulate one part of the population.  After all
     * threads have completed their work, their results can be incorperated
     * into the global variables via the call updateGlobals().
     */
    class WorkerThread extends Thread {

	/** Population member to start at.
	 */
	private int start=0;
	/** Population member to stop at (exclusive).
	 */
	private int end=0;

	/** Set to true while the thread is working through population.
	 */
	private boolean working=false;

	/** Local set of vectors to become next generation.
	 */
	private ParameterSet[] newvectors;
	/** Errors associated with newvectors.
	 */
	private double[] newerrors;
	/** Best vector (lowest error) in newvectors found so far.
	 */
	private int best;
	/** Number of target vectors replaced by test vector (child).
	 */
	private int nreplace;

	/**
	 * Create worker thread, mark as daemon thread, and start.
	 */
	public WorkerThread() {
	    setDaemon(true);
	    start();
	}

	/**
	 * Send signal to start work.
	 * @param start first vector to start working on
	 * @param end last vector (exclusive) to work on
	 */
	public synchronized void startWork(int start, int end) {
	    this.start=start;
	    this.end=end;
	    working=true;
	    notifyAll();
	}

	/**
	 * Wait for thread to finish work.
	 */
	public synchronized void waitForFinish() {
	    while (working) {
		try {
		    wait();
		}
		catch (InterruptedException e) {
		}
	    }
	}

	/**
	 * Used internally to wait for work to do.
	 */
	private synchronized void waitForWork() {
	    while (!working) {
		try {
		    wait();
		}
		catch (InterruptedException e) {
		}
	    }
	}

	/**
	 * Used internally to signal that the work has been completed.
	 */
	private synchronized void signalWorkDone() {
	    working = false;
	    notifyAll();
	}

	/**
	 * Main loop to wait for work, do the work, then signal that the
	 * work is done and repeat.  This method should not be called by
	 * any thread except this one.
	 */
	public void run() {
	    do {
		waitForWork();
		doPartGeneration();
		signalWorkDone();
	    } while (true);
	}

	/**
	 * Method to do a generation of the differential evolution algorithm.
	 * This method works through the target vectors in DE.vectors[] from
	 * start to (but not including) end.  After each competition, the
	 * winner (lowest error vector) is started in the newvectors[] array
	 * in this object.  No variables in the DE object are altered by
	 * this method.  Use updateGlobals() to copy local results to the
	 * global arrays.
	 */
	private void doPartGeneration() {
	    nreplace=0;
	    best=-1;
	    if ((newvectors==null)||(newvectors.length<end)) {
		newvectors = new ParameterSet[end];
		newerrors = new double[end];
	    }
	    for (int i=start; i<end; ++i) {
		// random parent other than i
		int j = random.nextInt(nvectors-1);
		if (j>=i) ++j;
		ParameterSet parent = vectors[j];
		
		// random parameter to always come from mutated parent
		int m = random.nextInt(vectors[i].size());
		
		// create test vector
		ParameterSet testvector = vectors[i].getCopy();
		for (int n=0; n<vectors[i].size(); ++n)
		    if ((n==m)||(random.nextFloat()<CR)) {
			// two random vectors to difference for random mutation
			int k = random.nextInt(nvectors);
			int l = random.nextInt(nvectors-1);
			if (l>=k) ++l;
			// get mutated version of parameter from parent
			Parameter test = parent.get(n).
			    scaleadd(1,vectors[k].get(n).
				     scaleadd(1,vectors[l].get(n),-1),
				     F);
			// create test vector
			testvector.set(test,n);
		    }
		
		// compute error of test vector
		double error = data.computeError(testvector);
		
		if (error<errors[i]) {
		    // testvector is better, replace target vector
		    newvectors[i] = testvector;
		    newerrors[i]=error;
		    ++nreplace;
		    if ((best<0)||(newerrors[i]<newerrors[best]))
			best=i;
		}
		else {
		    newvectors[i] = vectors[i];
		    newerrors[i]=errors[i];
		}
	    }
	}

	/**
	 * Copy the local results in newvectors[], errors[], best, and
	 * nreplace to the global variables in the parent DE object.  This
	 * method should only be called after waitForFinish() has been
	 * called in all WorkerThreads.
	 */
	public void updateGlobals() {
	    System.arraycopy(newvectors,start,vectors,start,end-start);
	    System.arraycopy(newerrors,start,errors,start,end-start);
	    DE.this.nreplace += nreplace;
	    if ((best>=0)&&(errors[best]<errors[DE.this.best]))
		DE.this.best = best;
	}
    }
    
    /** Initial size of the vectors[] arary.
     */
    public static final int INITIAL_ARRAY_SIZE=8;

    /** Random number generator used by DE algorithm and can (and should) be
     * used by other classes to generate the initial population.
     */
    public static Random random = new Random();

    /** This scale factor controls the magnitude of the mutation applied to 
     * the random parent vector.  Typical values are in the range 0.4 to 1.4.
     */
    public double F = 1.0;
    /** This parameter controls the ratio of cross-over of parameters when
     * creating a test vector.  Values should be in the range of 0 to 1.  A
     * value of 0 indicates that when creating a test vector only one
     * parameter (choosen at random) will come from the mutated random vector.
     * All other parameters are copied from the target vector.  A value of
     * 1 indicates that the test vector is exactly the mutated version of the
     * random vector.  To get even mixing of parameters, use 0.5.  Note that
     * large values of this parameter may result in a loss of diversity in the
     * population.
     */
    public float CR = 0.5f;

    /** The population.
     */
    private ParameterSet[] vectors = null;
    /** Number of valid entries in vectors[].
     */
    private int nvectors = 0;

    /** Error values associated with population members.
     */
    private double[] errors = null;
    /** Index of best (lowest error) population member.
     */
    private int best=0; // best vector

    /** Number of generations required thus far.
     */
    private int ngen;

    /** The observed data set (used to do the error calculation).  
     */
    private ObservedDataSet data;

    /** Number of replacements made in last generation.
     */
    private transient int nreplace;

    /** Threads to do the actual work.
     */
    private transient WorkerThread[] threads;
    /** Number of valid entries in threads[].
     */
    private transient int nthreads=0;

    /**
     * Construct a new DE object with a specific set of observed data and
     * use the specified number of threads to do the work.
     * @param data data to fit to
     * @param nthreads number of worker threads to use
     */
    public DE(ObservedDataSet data, int nthreads) {
	this.data = data;
	this.nthreads = nthreads;
	threads=null;
    }
    /**
     * Construct a new DE object with a specific set of observed data and
     * use only 1 thread to do the work.
     * @param data data to fit to
     */
    public DE(ObservedDataSet data) {
	this(data,1);
    }

    /**
     * Set the number of threads to use to do the work.
     * @param nthreads number of worker threads to use
     */
    public void setNumberOfThreads(int nthreads) {
	if (nthreads<1)
	    nthreads=1;
	this.nthreads=nthreads;
	if (threads==null)
	    threads = new WorkerThread[nthreads];
	else if (threads.length<nthreads) {
	    // reallocate
	    WorkerThread[] newthreads = new WorkerThread[nthreads];
	    System.arraycopy(threads,0,newthreads,0,nthreads);
	    threads=newthreads;
	}
    }

    /**
     * Add new member to population and compute error for that member.  The
     * parameter set should not be altered after it has been added.
     * @param set new population member
     */
    public void addVector(ParameterSet set) {
	if (vectors==null) {
	    vectors = new ParameterSet[INITIAL_ARRAY_SIZE];
	    errors = new double[INITIAL_ARRAY_SIZE];
	    nvectors=0;
	}
	else if (vectors.length<=nvectors) {
	    // reallocate
	    ParameterSet[] newvectors = new ParameterSet[nvectors*2];
	    System.arraycopy(vectors,0,newvectors,0,nvectors);
	    vectors=newvectors;
	    double[] newerrors = new double[nvectors*2];
	    System.arraycopy(errors,0,newerrors,0,nvectors);
	    errors=newerrors;
	}
	errors[nvectors] = data.computeError(set);
	if (errors[nvectors]<errors[best])
	    best=nvectors;
	vectors[nvectors++] = set;
    }

    /**
     * Recompute the error associated with each population member.  This method
     * should be called if any of the population members are altered after
     * they have been added to this object.  
     */
    public void recomputeError() {
	for (int i=0; i<nvectors; ++i) {
	    errors[i] = data.computeError(vectors[i]);
	    if (errors[i]<errors[best])
		best=i;
	}
    }

    /**
     * Return the data set object initially passed to the constructor.
     * @return data set object passed to the constructor
     */
    public ObservedDataSet getDataSet() {
	return data;
    }

    /**
     * Get the error of the current best (lowest error) population member.
     * @return error of best vector
     */
    public double getError() {
	return errors[best];
    }

    /**
     * Get the best (lowest error) population member.
     * @return best vector
     */
    public ParameterSet getParameterSet() {
	return vectors[best];
    }

    /**
     * Get the best (lowest error) population member.
     * @return best vector
     */
    public ParameterSet getVector() {
	return vectors[best];
    }

    /**
     * Get entire population.  This population may be used as a bases to create
     * a new population or to systematically alter the current population.  In
     * the latter case, after altering the population and before continuing to
     * use this DE object, you must call the recomputeError() method.
     * @return array of population vectors
     */
    public ParameterSet[] getAllVectors() {
	ParameterSet[] result = new ParameterSet[nvectors];
	System.arraycopy(vectors,0,result,0,nvectors);
	return result;
    }

    /**
     * Return the number of generations the population has been through.
     * @return number of gererations
     */
    public int getNumberOfGenerations() {
      return ngen;
    }

    /**
     * Return the number of replacements made during the last generation.  A
     * replacement occurs when a test vector successfully wins a competition
     * against the target vector (ie. the test vector has lower error than the
     * target vector). 
     * @return number of replacements
     */
    public int getNumberOfReplacements() {
      return nreplace;
    }

    /**
     * Do a single generation of the differential evolution algorithm.  The
     * work required to do a generation is actually performed by the worker
     * threads; however, this method will not return until the generation
     * is complete.  
     * @return error of best vector
     */
    public double doItteration() {
	nreplace = 0;

	if ((threads==null)||(nthreads<1))
	    setNumberOfThreads(nthreads);

	// setup all threads and start them
	for (int i=0; i<nthreads; ++i) {
	    if (threads[i]==null)
		threads[i]=new WorkerThread();
	    threads[i].startWork(i*nvectors/nthreads,
				 (i+1)*nvectors/nthreads);
	}

	// wait for threads to finish
	for (int i=0; i<nthreads; ++i) {
	    threads[i].waitForFinish();
	}

	// update global variables
	for (int i=0; i<nthreads; ++i) {
	    threads[i].updateGlobals();
	}

	++ngen;
	return errors[best];
    }


    /**
     * Run a series of generations of the differential evolution algorithm
     * until either the maximum number of generations has been completed
     * or the error of the best vector falls below the specified threshold.
     * Any generations done before this call by a call to doItteration() or
     * an earlier call to solve() are included in the tally of the number
     * of generations completed.
     * @param errorthres error threshold
     * @param maxgen maximum number of generations
     * @return error of best vector
     */
    public double solve(double errorthres, int maxgen) {
	double error = Double.POSITIVE_INFINITY;
	ngen = 0;
	while ((ngen<maxgen)&&(error>errorthres))
	    error = doItteration();
	return error;
    }
}

