Necessary Resources

Letter Image Files

EasyBMP Resources

To Compile, Use These Commands:

g++ -o -g NeuralNet.cpp

g++ -o neuralnet EasyBMP.o NeuralNet.o

neuralnet

With Large L, Segmentation Faults Can Be Avoided Using:

limit stacksize unlimited

            
#include <stdio.h>    //
#include <stdlib.h>   //
#include <ctime>      // needed for compatible  sleep function
#include <sstream>    // needed for incorporation of variables into file names
#include "EasyBMP.h"  // needed for BMP input/output
#include <iostream>   // 
#include <string>     // 
#include <vector>     // 
#include <math.h>     // needed for floor()
#include <fstream>
//#include <windows.h>  // needed for Sleep()

using namespace std;
using std::cout;
using std::endl;

ofstream hammergy("hammergy.dat", ios::app);
ofstream flips("flips.dat");

// Changeable parameters
float	    Tstart = 0.0;					// Start temperature for annealing (put 0 for no annealing)
int const   L = 40;							// Size of neural net
int const   MC_Steps = 1000;				// Number of MC-steps
float const RN_seed = 0.4f;					// RNG seed
float 	    spindmg = 0.2;					// Initial spin damage
int 	    teachOrtho	= 0;				// Number of pseudoorthogonal images teached
bool        restrictJbool = false; 			// If true restricts J-elements to 1 and -1
float       bdamage = 0.0f;					// Amount of random braindamage 0 for no braindamage;
char*       initname = "A_40";				// Name of initial image

float	    J_lifetime = 0.0;
BMP         current_BMP;
int         Step = 0;		
char        image_directory[] = "Letter Images/";
char        output_directory[] = "Run Output/";


void MonteCarloStep(int [L][L], float [L][L][L][L], float T, int[L][L]);
float CalculateEnergy(int [L][L], float [L][L][L][L]);
float CalculateEnergyChange_SN (int [L][L], float [L][L][L][L], int, int);
void InitializeSpins(int [L][L]);
void InitializeSpins(int [L][L], char []);
void InitializeJ(int, float [L][L][L][L]);
void TeachJ(float [L][L][L][L], float, int*);
void TeachJ(float [L][L][L][L], float, char []);
void InputImage(char*, int[L][L]);
void PrintSpins(int [L][L]);
void PrintSpins_Linux(int [L][L]);
int* loadImage(char []);
void outputBMP(int [L][L], char []);
void restrictJ(float [L][L][L][L]);
void brainDamageRec(int, int, int, int, float [L][L][L][L],int [L][L]);
void brainDamageRan(float, float [L][L][L][L]);
void createOrthogonal(int, float [L][L][L][L]);
void flipS(float spindmg, int S[L][L]);
float Hamming(int [L][L], int [L][L]);
//void sleep(unsigned int);

int main(void) {
  float T;	
  // Seed Random Numbers
  srand((int)(RAND_MAX * RN_seed));

  // Create Spin State and J Matrices
  //    x  y
  int S[L][L] = {0};
  //       x  y
  int refS[L][L] = {0};
  //      ix iy jx jy
  float J[L][L][L][L] = {0.0};

  // Initialize Spin State and J Matrices
  InitializeSpins(refS, "black");  // Initializes Reference Spin State
  InitializeSpins(S, initname);    			 // Initializes Spins to an "A" in italics in a different, serif font
  //InitializeSpins(S);            			 // Initializes Spins Randomly
  //InitializeJ(2, J);		   				 // Initializes J-elements randomly

  // "Teach" J new letters, to increase the weight of the letter, increase parameter 2
  TeachJ(J, 1.0f, "A");
  TeachJ(J, 1.0f, "B");
  TeachJ(J, 1.0f, "C");
  TeachJ(J, 1.0f, "D");
  TeachJ(J, 1.0f, "E");
  TeachJ(J, 1.0f, "F");
  //TeachJ(J, 1.0f, "G");
  //TeachJ(J, 1.0f, "H");	
  //TeachJ(J, 1.0f, "I");
  //TeachJ(J, 1.0f, "J");	
	
  createOrthogonal(teachOrtho,J);

  // randomize initial spin state
  flipS(spindmg, S); 

  // Restrict J
  if(restrictJbool)
    restrictJ(J);

  brainDamageRan(bdamage,J);
  
  // MC Run
  // Print Initial Spin State
  PrintSpins_Linux(S);

  // Perform MC Steps and Print Spin State
  for (Step = 0; Step < MC_Steps; Step++) {
      PrintSpins_Linux(S);        				// <-- In windows, use PrintSpins(), in linux use PrintSpins_Linux()
      T = (Tstart/MC_Steps)*(MC_Steps - Step);			
      MonteCarloStep(S, J, T, refS);
      outputBMP(S, "Trial1");
  }
  return 0;
}

// Chooses LxL random neurons to switch
// If the energy decreases, the neuron is switched, if not it stays
// <param S> Spin state of neurons
// <param J> J matrix for neuron interactions
void MonteCarloStep(int S[L][L], float J[L][L][L][L], float T, int refS[L][L]) {
  int x, y;
  float dE;
  int counter;
  counter = 0;
  for (int i = 0; i < L*L; i++) {
      // Choose Random Neuron to Switch
      x = (int)(L * (rand() / (RAND_MAX + 1.0)));
      y = (int)(L * (rand() / (RAND_MAX + 1.0)));

      // Calculate Energy Change
      dE = CalculateEnergyChange_SN(S, J, x, y);

      if (dE < 0.0) {
          S[x][y] = -S[x][y];
          counter ++;
      } else {
          if (exp(-dE/T) > (rand() / (RAND_MAX + 1.0))) {
              S[x][y] = -S[x][y];
              counter ++;
          }
	  }
  }
  
  hammergy << Hamming(S, refS) << ' ' << CalculateEnergy(S, J) << endl;
  flips << counter << "\n" << endl;
  
}

// Calculates the total instantaneous energy
// <param S> Spin states of neurons
// <param J> J matrix for neuron interactions
// <return>  Returns the energy, E
float CalculateEnergy(int S[L][L], float J[L][L][L][L]) {
  int i, j;
  float E = 0;

  for (i = 0; i < L*L; i++) {
    for (j = i + 1; j < L*L; j++) {
      if (!(i%L == j%L && (int)floor(1.0*i/L) == (int)floor(1.0*j/L))) {
		E -= J[i%L][(int)floor(1.0*i/L)][j%L][(int)floor(1.0*j/L)] * S[i%L][(int)floor(1.0*i/L)] * S[j%L][(int)floor(1.0*j/L)];
      }
	}
  }

  return E;
}

// Calculates the Change in Energy for a Single Neuron Switch
// <param S> Spin states of neurons
// <param J> J matrix for neuron interactions
// <param x> x coordinate for neuron to switch
// <param y> y coordinate for neuron to switch
// <return>  Returns the change in energy, dE
float CalculateEnergyChange_SN (int S[L][L], float J[L][L][L][L], int x, int y) {
  int j;
  float dE = 0;

  for (j = 0; j < L*L; j++) {
    if (!(x == j%L && y == (int)floor(1.0*j/L))) {
      dE -= (float)J[x][y][j%L][(int)floor(1.0*j/L)] * (float)-S[x][y] * (float)S[j%L][(int)floor(1.0*j/L)];
    }
  }

  return dE;
}

// Initializes the Spins
// <param S> Spin states of neurons
void InitializeSpins(int S[L][L]) {
  for (int x = 0; x < L; x++) {
    for (int y = 0; y < L; y++) {
      S[x][y] = (int)floor(rand() / (RAND_MAX + 1.0) + 0.5);
      if (S[x][y] == 0) S[x][y] = -1;
    }
  }
}

// Initializes the Spins
// <param S> Spin states of neurons
// <param name> File name in the image directory to load
void InitializeSpins(int S[L][L], char name[]) {
  std::string filename;
  std::stringstream filename_out;

  filename_out << image_directory << name << ".bmp";
  filename = filename_out.str();

  int* image = loadImage((char*)filename.c_str());

  for (int i = 0; i < L*L; i++) {
    S[i%L][(int)floor(1.0*i/L)] = image[i];
  }
}

// Initializes the J Matrix
// <param selection> Chooses how to initialize J
//   case 1 - All values 0
//   case 2 - Random Initialization
// <param J> J matrix for neuron interactions
void InitializeJ(int selection, float J[L][L][L][L]) {
  int ix, iy, jx, jy;

  switch (selection) {
    // Blank
  case 1:
    break;
    // Random Initialization between -5 and 5
  case 2:
    for (ix = 0; ix < L; ix++) {
      for (iy = 0; iy < L; iy++) {
        for (jx = 0; jx < L; jx++) {
          for (jy = 0; jy < L; jy++) {
            J[ix][iy][jx][jy] = (float)(rand() / (RAND_MAX + 1.0) * 10.0 - 5.0);
          }
        }
      }
    }
    break;
  }
}


// "Teaches" new J values based on an input image
// <param exposure_time> the weight of the image on the J matrix
// <param image> the image to affect the J matrix
void TeachJ(float J[L][L][L][L], float exposure_time, int* image) {
  for (int ix = 0; ix < L; ix++) {
    for (int iy = 0; iy < L; iy++) {
      for (int jx = 0; jx < L; jx++) {
        for (int jy = 0; jy < L; jy++) {
          J[ix][iy][jx][jy] = J_lifetime / (J_lifetime + exposure_time) * J[ix][iy][jx][jy] + 
              exposure_time / (J_lifetime + exposure_time) * image[ix + iy * L] * image[jx + jy * L];
        }
      }
    }
  }

  J_lifetime += exposure_time;
}

// "Teaches" new J values based on a bmp image
// <param exposure_time> the weight of the image on the J matrix
// <param name> The name of the file to be opened in the image_directory (e.g. "A"
//              with L=10 would open the file at "Letter Images/A_10.bmp"
void TeachJ(float J[L][L][L][L], float exposure_time, char name[]) {
  std::string filename;
  std::stringstream filename_out;

  filename_out << image_directory << name << "_" << L << ".bmp";
  filename = filename_out.str();

  int* image = loadImage((char*)filename.c_str());

  for (int ix = 0; ix < L; ix++) {
    for (int iy = 0; iy < L; iy++) {
      for (int jx = 0; jx < L; jx++) {
        for (int jy = 0; jy < L; jy++) {
          J[ix][iy][jx][jy] = (J_lifetime / (J_lifetime + exposure_time)) * J[ix][iy][jx][jy] + 
              (exposure_time / (J_lifetime + exposure_time)) * image[ix + iy * L] * image[jx + jy * L];
        }
      }
    }
  }

  J_lifetime += exposure_time;
}

// Prints the spin states to the terminal
// <param S> Spin states of neurons
void PrintSpins(int S[L][L]) {
  system("cls");
  for (int x = 0; x < L; x++) {
    for (int y = 0; y < L; y++) {
      if (S[x][y] == -1) {
		cout << " ";
      } else {
		cout << "#";
      }
    }
    cout << endl;
  }
}

// Prints the current spin matrix
void PrintSpins_Linux(int S[L][L]) {
  system("clear");
  for (int x = 0; x < L; x++) {
    for (int y = 0; y < L; y++) {
      if (S[x][y] == -1) {
		cout << " ";
      } else {
		cout << "#";
      }
    }
    cout << endl;
  }
}

// Loads an image from the given filename and returns a binary 2D array
// <param filename> Relative path of image
int* loadImage(char filename[]) {
  BMP image;
  image.ReadFromFile(filename);
	
  int* S_image = new int[L*L];

  for(int i = 0; i < L*L; i++) {
    if(image((int)floor(1.0*i/L),i%L)->Red + image((int)floor(1.0*i/L),i%L)->Blue + image((int)floor(1.0*i/L),i%L)->Green < 382)
      S_image[i] = 1;
    else
      S_image[i] = -1;
  }

  return S_image;
}

// Prints the current spin state to a .bmp image file in the output directory
// <param S> Spin states of neurons
// <param name> File name of current run (e.g. "test" in monte carlo step 1
//              will produce the file "Run Output/test_1.bmp"
void outputBMP(int S[L][L], char name[]) {
  std::string filename;
  std::stringstream filename_out;

  filename_out << output_directory << "TEMP" << "_" << Step << ".bmp";
  filename = filename_out.str();
  current_BMP.SetSize(L,L);

  for(int x = 0;  x < L; x++) {
    for(int y = 0; y < L; y++) {
      if(S[x][y] == -1) {
        current_BMP(y,x)->Red = 255;
        current_BMP(y,x)->Green = 255;
        current_BMP(y,x)->Blue = 255;
      } else {
        current_BMP(y,x)->Red = 0;
        current_BMP(y,x)->Green = 0;
        current_BMP(y,x)->Blue = 0;
      }
    }
  }

  current_BMP.WriteToFile((char*)filename.c_str());
}

// Restricts elements in J to +1 or -1
void restrictJ(float J[L][L][L][L]){
  for (int ix = 0; ix < L; ix++) {
    for (int iy = 0; iy < L; iy++) {
      for (int jx = 0; jx < L; jx++) {
        for (int jy = 0; jy < L; jy++) {
          if(J[ix][iy][jx][jy]>=0)
            J[ix][iy][jx][jy]=1;
          else
            J[ix][iy][jx][jy]=-1;
        }
      }
    }
  }
}

// Simulates killing neurons in rectangle starting at (sx,sy) and extending dx and dy
void brainDamageRec(int sx, int sy, int dx, int dy, float J[L][L][L][L], int S[L][L]){
  for (int x = sx; x < sx+dx; x++) {
    for (int y = sy; y < sy+dy; y++) {
      S[y][x] = 1;
      for (int ix = 0; ix < L; ix++) {
        for (int iy = 0; iy < L; iy++) {
          J[y][x][iy][ix] = 0;
          J[iy][ix][y][x] = 0;
        }
      }
    }
  }
}

// Sets elements of J to 0 with a probability of rate
void brainDamageRan(float rate, float J[L][L][L][L]){
  if(rate != 0){
    for (int ix = 0; ix < L; ix++) {
      for (int iy = 0; iy < L; iy++) {
        for (int jx = 0; jx < L; jx++) {
          for (int jy = 0; jy < L; jy++) {
            if((rand() / (RAND_MAX + 1.0))<rate)
              J[ix][iy][jx][jy]=0;
          }
        }
      }
    }
  }
}

// Creates and teaches pseudoorthogonal images
void createOrthogonal(int n,float J[L][L][L][L]){
  int * S1;
  S1 = new int [L*L]; 
  for(int i=0; i<n; i++){
    for (int x = 0; x < L*L; x++) {
      if((rand() / (RAND_MAX + 1.0)) < 0.5)
		S1[x] = 1;
      else
		S1[x] = -1;
    }

    TeachJ(J,1.0f,S1);
  }
}

// Sets initial spindamage
void flipS(float spindmg, int S[L][L]) {
  if(spindmg != 0){
    for (int x = 0; x < L; x++) {
      for (int y = 0; y < L; y++) {
        if((rand() / (RAND_MAX + 1.0))<spindmg)
	  S[x][y] = int(((rand() % 2)-0.5)*2);
      }
    }
  }
}

// Calculates Hamming Distance
float Hamming(int S[L][L], int refS[L][L]) {
  float hamming = 0.0f;

  for (int x = 0; x < L; x++) {
    for (int y = 0; y < L; y++) {
      hamming += (1.0 / L*L) * pow(refS[x][y] - S[x][y], 2.0);
    }
  }
  return hamming;
}