

import java.awt.*;
import java.io.*;
import java.util.*;


class BackpropA6
{
	private BufferedReader infile;
	private String line;

	private static int numInputUnits = 40;     //  Set to the length of input binary strings
	private static int numHiddenUnits = 20;      //  Vary this.
	private static int numOutputUnits = 1;     //  Do not vary this!
	private static int NUM_TRAINING_EPOCHS=100;//  Maybe vary this!
	private double c = 0.25;                   // The learning rate.   Maybe vary this.
	final long RANDOM_SEED = 192;              //102,112,122,132,142,152,162,172,182,192,

	Random randomizer= new Random(RANDOM_SEED);
    private double [] inputs = new double[numInputUnits];
	private double [][] weightsLayerOne= new double[numInputUnits][numHiddenUnits];
	private double [][] weightsLayerTwo= new double[numHiddenUnits][numOutputUnits];
	private double [] weightsHiddenUnitsBias = new double[numHiddenUnits];
	private double [] weightsOutputUnitsBias = new double[numOutputUnits];
	private double [] hiddenLayerOutput = new double[numHiddenUnits];
	private double [] outputLayerOutput = new double[numOutputUnits];


    private boolean showConcept = false;
	private final int MAX_NUM_EXAMPLES = 20000;
	private boolean enteringEXAMPLES = false;
	private boolean enteringPOSITIVES = false;
	private String [] trainExamplesPOS = new String[MAX_NUM_EXAMPLES];
	private String [] trainExamplesNEG = new String[MAX_NUM_EXAMPLES];
	private String [] testExamplesPOS = new String[MAX_NUM_EXAMPLES];
	private String [] testExamplesNEG = new String[MAX_NUM_EXAMPLES];
	private int numTrainPositives, numTrainNegatives,numTestPositives, numTestNegatives;


private void reset()
{
	for(int j=0; j < numHiddenUnits; j++) {
	    for(int i=0; i< numInputUnits; i++)
	       weightsLayerOne[i][j] = randomizer.nextDouble();
	    for(int k=0; k<numOutputUnits; k++)
	       weightsLayerTwo[j][k] = randomizer.nextDouble();
       }
	for(int j=0; j < numHiddenUnits; j++) weightsHiddenUnitsBias[j] =  randomizer.nextDouble();
	for(int k=0; k < numOutputUnits; k++) weightsOutputUnitsBias[k] =  randomizer.nextDouble();
}


private static int LTU(double input)
    { if(input > 0.5) return 1;  else return 0;  }

private double sigmoid(double input)
    {  return 1.0/(1.0 + Math.pow(Math.E, -input)); }

private void runNet()
    {
		double summedInput;
        for(int j=0; j < numHiddenUnits; j++) {
		    summedInput = 0.0;
		    for(int i=0; i<numInputUnits; i++)
		        summedInput += weightsLayerOne[i][j]*inputs[i];
		    summedInput +=  weightsHiddenUnitsBias[j];
			hiddenLayerOutput[j] = sigmoid(summedInput);
		 }
		for(int k=0; k < numOutputUnits; k++) {
		     summedInput = 0.0;
		     for(int j=0; j<numHiddenUnits; j++)
		         summedInput += weightsLayerTwo[j][k]*hiddenLayerOutput[j];
		     summedInput +=  weightsOutputUnitsBias[k];
			 outputLayerOutput[k] = sigmoid(summedInput);
		 }
	}

private void trainOneOutputUnitOnOneExampleForOneIteration(int k, double d)
    {
		runNet();
		double f = outputLayerOutput[k];
		double deltaK = (d-f)*f*(1-f);
		double deltaJ;

        //  First update the weight on the connection from output unit k's bias.
		weightsOutputUnitsBias[k] += c*deltaK;
		// Then update the weights going into output unit k from hidden units.
		for(int j=0; j<numHiddenUnits; j++)  {
		     // First backprop the error from output unit k to hidden layer unit j.
		         //  First calculate the backpropped error deltaJ.
		         deltaJ = hiddenLayerOutput[j]*(1-hiddenLayerOutput[j])*(deltaK*weightsLayerTwo[j][k]);
		         // Then update the weight of the bias going into hidden unit j.
		     	weightsHiddenUnitsBias[j] += c*deltaJ;
		         //  Then update the weights on the connections from the input units
		         //  into hidden unit j.
		     	 for(int i=0; i < numInputUnits; i++)
		     	    weightsLayerOne[i][j] += c*deltaJ*inputs[i];
		     // Lastly go back and update the weight going from hidden unit j to output unit k.
		     weightsLayerTwo[j][k] +=  c*deltaK*hiddenLayerOutput[j];
		}
	}

private void trainOneOutputUnitOnOneExampleForMultipleIterations(int k, double label, int numIterations)
     {
		 for(int i=0; i< numIterations; i++)
		    trainOneOutputUnitOnOneExampleForOneIteration(k,label);
	 }

private void readTrainingSet()  {
		try {
				infile = new BufferedReader(new InputStreamReader(new FileInputStream("trainPOS.txt")));
				numTrainPositives = 0;
				line = infile.readLine();
				while (line != null)  {  //  Get input vectors from file one line at a time until end of file
					trainExamplesPOS[numTrainPositives++] = line;
					line = infile.readLine();
				}
				infile.close();
			}
		catch (Throwable error) {System.out.println("Error in ReadFile trainPOS.txt:" + error);}
		try {
				infile = new BufferedReader(new InputStreamReader(new FileInputStream("trainNEG.txt")));
				numTrainNegatives = 0;
				line = infile.readLine();
				while (line != null)  {  //  Get input vectors from file one line at a time until end of file
					trainExamplesNEG[numTrainNegatives++] = line;
					line = infile.readLine();
				}
				infile.close();
			}
		catch (Throwable error) {System.out.println("Error in ReadFile trainNEG.txt:" + error);}
	}

private void readTestingSet()  {
		try {
				infile = new BufferedReader(new InputStreamReader(new FileInputStream("testPOS.txt")));
				numTestPositives = 0;
				line = infile.readLine();
				while (line != null)  {  //  Get input vectors from file one line at a time until end of file
					testExamplesPOS[numTestPositives++] = line;
					line = infile.readLine();
				}
				infile.close();
			}
		catch (Throwable error) {System.out.println("Error in ReadFile testPOS.txt:" + error);}
		try {
				infile = new BufferedReader(new InputStreamReader(new FileInputStream("testNEG.txt")));
				numTestNegatives = 0;
				line = infile.readLine();
				while (line != null)  {  //  Get input vectors from file one line at a time until end of file
					testExamplesNEG[numTestNegatives++] = line;
					line = infile.readLine();
				}
				infile.close();
			}
		catch (Throwable error) {System.out.println("Error in ReadFile testNEG.txt:" + error);}
	}
private void train()  {
         int num,p,c,dim;
         String s;
		 for(num=0; num < NUM_TRAINING_EPOCHS; num++)  {
		     for(p=0;p<numTrainPositives+numTrainNegatives; p++)  {
				 if(p<numTrainPositives)
				  {loadInputs(trainExamplesPOS[p]);
				    trainOneOutputUnitOnOneExampleForMultipleIterations(0,1,1);  }
				 if(p<numTrainNegatives)
				  { loadInputs(trainExamplesNEG[p]);
				 trainOneOutputUnitOnOneExampleForMultipleIterations(0,0,1);
			       }
                }
		  }
	  }
private int charToBit(char ch) {if(ch == '0') return 0; else return 1; }

private void loadInputs(String s)  {
				int dim=s.length();
				for(int c=0;c<dim  ; c++)  inputs[c]=charToBit(s.charAt(c)); }

private void test()  {
         int num,p,c,dim;
         int testPOSscore, testNEGscore,trainPOSscore, trainNEGscore;
         int testPOSscorePCT, testNEGscorePCT, trainPOSscorePCT, trainNEGscorePCT;
         int POSscore, NEGscore, testScore, trainScore;
         int POSscorePCT, NEGscorePCT, testScorePCT, trainScorePCT;
         int allExamplesScore, allExamplesScorePCT;
         String s;
         testPOSscore=0;
		 for(p=0;p<numTestPositives; p++)  {
			 loadInputs(testExamplesPOS[p]);  runNet();
			 if(LTU(outputLayerOutput[0]) == 1) testPOSscore++;}
         testPOSscorePCT = (int) ((100.0*testPOSscore)/numTestPositives);
         testNEGscore=0;
		 for(p=0;p<numTestNegatives; p++)  {
			 loadInputs(testExamplesNEG[p]);  runNet();
			 if(LTU(outputLayerOutput[0]) == 0) testNEGscore++; }
         testNEGscorePCT = (int) ((100.0*testNEGscore)/numTestNegatives);
         trainPOSscore=0;
		 for(p=0;p<numTrainPositives; p++)  {
			 loadInputs(trainExamplesPOS[p]);  runNet();
			 if(LTU(outputLayerOutput[0]) == 1) trainPOSscore++;}
         trainPOSscorePCT = (int) ((100.0*trainPOSscore)/numTrainPositives);
         trainNEGscore=0;
		 for(p=0;p<numTrainNegatives; p++)  {
			 loadInputs(trainExamplesNEG[p]);  runNet();
			 if(LTU(outputLayerOutput[0]) == 0) trainNEGscore++; }
         trainNEGscorePCT = (int) ((100.0*trainNEGscore)/numTrainNegatives);
         trainScore = trainPOSscore+trainNEGscore;
         testScore = testPOSscore+testNEGscore;
         POSscore = trainPOSscore+testPOSscore;
         NEGscore = trainNEGscore+testNEGscore;
         trainScorePCT = (int) ((100.0*trainScore)/(numTrainPositives+numTrainNegatives));
         testScorePCT = (int) ((100.0*testScore)/(numTestPositives+numTestNegatives));
         POSscorePCT = (int) ((100.0*POSscore)/(numTrainPositives+numTestPositives));
         NEGscorePCT = (int) ((100.0*NEGscore)/(numTrainNegatives+numTestNegatives));
		 allExamplesScore = trainScore+testScore;
		 allExamplesScorePCT = (int) ((100.0 * allExamplesScore)/(numTrainPositives+numTrainNegatives+numTestPositives+numTestNegatives));
		 System.out.println("\n\n\n");
         System.out.println("                      |  POSITIVES   |   NEGATIVES     |   ALL EXAMPLES");
         System.out.println("                      |------------------------------------------------");
         System.out.println("    TRAINING SET      |      "+trainPOSscorePCT+"%     |       "+trainNEGscorePCT+"%       |      "+trainScorePCT+"%");
         System.out.println("    TEST     SET      |      "+testPOSscorePCT+"%     |       "+testNEGscorePCT+"%       |      "+testScorePCT+"%");
         System.out.println(" ---------------------|--------------|-----------------|---------------");
         System.out.println("    OVER ALL EXAMPLES |      "+POSscorePCT+"%     |       "+NEGscorePCT+"%       |      "+allExamplesScorePCT+"%");
		 System.out.println("\n\n\n");
}


public void examineHiddenLayerNode(int j)
{
	System.out.println("  OUTPUT LAYER NODE bias weight:  " + weightsOutputUnitsBias[0] + "\n\n");
	System.out.println("  HIDDEN LAYER NODE " + j + ":         OUTPUT weight:  " + weightsLayerTwo[j][0]);
	System.out.println("    INPUTS WEIGHTS:   ");
	System.out.println("           bias weight:  " + weightsHiddenUnitsBias[j]);
	System.out.println("           input node      weights:    ");
	for(int i=0; i< numInputUnits; i++)
	System.out.println("                  "+i+"   "+weightsLayerOne[i][j]);
}

private void results()  {
	System.out.println("Num positive training examples:  " + numTrainPositives);
	loadInputs(trainExamplesPOS[0]);
	System.out.println(""+inputs[0]+", "+inputs[1]+", "+inputs[2]+", "+inputs[3]+", "+inputs[4]);
}

	public static void main(String args[]) {
		BackpropA6 bp = new BackpropA6();
		bp.readTrainingSet();
		bp.readTestingSet();
		bp.reset();
		bp.train();
		bp.test();
		for(int j=0; j<numHiddenUnits; j++) bp.examineHiddenLayerNode(j);

	} // end of main

}
















