/*
 * Created on Nov 22, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package jone;

/**
 * @author brian
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class FeedForwardNeuralNet implements Cloneable {

	FeedForwardNeuralNet(int [] numNeuronsInEachLayer, 
			int [] neuronTypesForLayers, int inputsize,int outputsize  )
	{
		neuronTypes = (int []) neuronTypesForLayers.clone();
		numNeurons = (int []) numNeuronsInEachLayer.clone();
		numinputs = inputsize;
		numoutputs = outputsize;
		initializeNet();
	}
	boolean print;
	NNLayer [] layers;
	int [] neuronTypes;
	int [] numNeurons;
	int numinputs, numoutputs;
	
	/**
	 * initializes the neural net with small random values in 
	 * each layer
	 *
	 */
	void initializeNet()
	{
		layers = new NNLayer[numNeurons.length];
		for(int i = 0; i < numNeurons.length; i++) {
			int wts = (i == 0) ? numinputs  : numNeurons[i-1] + 1;
			layers[i] = new NNLayer(wts,numNeurons[i],neuronTypes[i]);
		}
	}
	
	/**
	 * performs feedforward propagation 
	 * @param inputs sample input pattern
	 * @param training should be true if we are in training mode
	 * @return output of neural network
	 */
	double [] computeOutput(double [] inputs,boolean training)
	{
		double [] nextinputs = (double []) inputs.clone();
		for(int i = 0; i < layers.length; i++) {
			double [] temp = layers[i].computeNetk(nextinputs);
			nextinputs = NeuralNetTBox.applyFAddBias(temp,neuronTypes[i],training);
			
		}
		return nextinputs;
	}
	
	
	
	/**
	 * trains neural net using the backpropagation rules provided in
	 * HMC cs152 course notes
	 * @param error errors from output and desired output
	 * @param sample input sample
	 * @param nu learning rate
	 */
	void train(double [] error, double [] sample, double nu)
	{
		//calculate the sensitivities
		layers[layers.length-1].computeOutputSens(error);
		for(int i = layers.length - 2; i >= 0; i-- ) {
			layers[i].computeHiddenSens(layers[i+1]);
		}
		
		//adjust the weights
		layers[0].adjustWeights(sample,NeuralNetTBox.logsig,true,nu);
		for(int i = 1; i < layers.length; i++) {
			layers[i].adjustWeights( NeuralNetTBox.applyFAddBias(layers[i-1].getNetK(),
					neuronTypes[i-1],true),neuronTypes[i-1],(i==0),nu );
		}
	}
	/**
	 * prints each layer of the neural net
	 *
	 */
	void print()
	{
		for(int i = 0; i < layers.length; i++) {
			layers[i].print();
			Arena.logger.debug("");
		}
	}
	
	FeedForwardNeuralNet(double n1bias, FeedForwardNeuralNet n1, FeedForwardNeuralNet n2)
	{
		neuronTypes = (int []) n1.neuronTypes.clone();
		numNeurons = (int []) n1.numNeurons.clone();
		numinputs = n1.numinputs; 
		numoutputs = n1.numoutputs;
		//initializeNet();
		layers = new NNLayer[numNeurons.length];
		for(int i = 0; i < numNeurons.length; i++) {
			layers[i] = new NNLayer(n1bias,n1.layers[i],n2.layers[i]);
		}
	}
	public Object clone()
	{
		FeedForwardNeuralNet nn = new FeedForwardNeuralNet(this.numNeurons, this.neuronTypes, 
				this.numinputs, this.numoutputs);
		for(int i = 0; i < numNeurons.length; i++) {
			nn.layers[i] = new NNLayer(layers[i]);
		}
		return nn;
	}
	
	FeedForwardNeuralNet swapWeights(double scalar)
	{
		FeedForwardNeuralNet nn = (FeedForwardNeuralNet) this.clone(); // does this copy just the pointer
		for(int i = 0; i < nn.layers.length; i++) {
			nn.layers[i].swapWeights(scalar);
		}
		return nn;
	}
	
	FeedForwardNeuralNet addNoiseToWeights(double scalar, double prob)
	{
		FeedForwardNeuralNet nn = (FeedForwardNeuralNet) this.clone();
		for(int i = 0; i < nn.layers.length; i++) {
			nn.layers[i].addNoiseToWeights(scalar,prob);
		}
		return nn;
	}
	
}
