function hopfield( action, param )
%hopfield network
%This program was adapted from a similar java program by
%Hartmut S Loos.
%It can be run by typing 'hopfield' at the matlab prompt.
%The program implements a Hopfield network to remember five
%sets of patterns, and then match one of those to an input
%pattern.
%The storage and input fields can be modified directly with
%mouse presses, or can be initialized randomly with the
%'random' button.
%The weights are initialized with the 'init' button and then
%the network is run with the 'run' button, which will generate
%output in the output field.
%All variables are described at the top of the 'addGui' function.
%Andrew C. Chase
%March 21, 2002
	if nargin < 1,
		param = '';
		action = 'start';
	elseif nargin < 2,
		param = '';
	end;

	switch( action )
		case 'start'
			addGui

        	case 'runCB'
			%storePattern;
			computeOutput;
			setOutputBoxes;
		case 'initCB'
			h = findobj('Tag', 'mainWindow');
			struct = get(h, 'UserData');
			struct.inputChanged = 1;
			set(h, 'UserData', struct);
			storePattern;
			if struct.show == 1,
				showWeights;
			end;

		case 'randomCB'
			randomizeStorage;
			setStorageBoxes;
			randomizeInput;
			setInputBoxes;
		case 'radioStorageCB'
			name = gcbo;
			val=get(name, 'Value');
			if val == 0;
				val = -1;
			end;
			xy = get(name, 'UserData');
			ind1 = xy(1);
			ind2 = xy(2);
			h = findobj('Tag', 'mainWindow');
			hstruct = get(h, 'UserData');
			hstruct.storage(ind1,ind2)=val;
			set(h, 'UserData', hstruct);
		case 'radioInputCB'
			name = gcbo;
			%s = get(name, 'Tag');
			val = get(name, 'Value');
			if val == 0;
				val = -1;
			end;
			xy = get(name, 'UserData');
			ind1 = xy(1);
			h = findobj('Tag', 'mainWindow');
			hstruct = get(h, 'UserData');
			hstruct.input(ind1, 1) = val;
			%note the input has changed.
			hstruct.inputChanged =1;
			set(h, 'UserData', hstruct);
		case 'showWeightsCB'
			%get a handle to the weightWindow
			main = findobj('Tag', 'mainWindow');
			struct = get(main, 'UserData');
			h = findobj('Tag', 'weightWindow');
			onoroff = get(h, 'Visible');
			if strcmp(onoroff, 'off')
				set(h, 'Visible', 'on');
				struct.show = 1;
				showWeights;
			else
				struct.show = 0;
				set(h, 'Visible', 'off');
			end;	
			set(main, 'UserData', struct);
			
		%don't do anything when radio buttons are pushed
		case 'radioOutputCB'
	%end switch
      	end;
	
function addGui

	%size of storage array is hardcoded for now
	netSize = 25;
	numberStored = 5; %number of stored patterns.
	storArray = ones(netSize, numberStored);
	storArray = storArray .* -1; %Initialize  all arrays to -1's(off)
	inputArray = ones(netSize, 1); 
	inputArray = inputArray .* -1;
	outputArray = ones(netSize, 1);
	outputArray = outputArray .* -1;
	inputChanged = 1; %keep track of whether the input pattern
			  %has changed.
	show = 0; %keep track of whether user wants weight matrix to
		  %be shown or not.
	mainstruct = struct('inputChanged', inputChanged, ...
			'netSize', netSize, 'show', show, ...
			'numberStored', numberStored, ...
			'storage', storArray, ...
			'input', inputArray, ...
			'output', outputArray, ...
			'weights', zeros(netSize, netSize));

	%%%%%%%%%%%%%%%%%NOTE%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	%If the size of the main window is changed I'm not sure
	%how well things will hold up. The position of all buttons
	%are pretty much hard coded.
        mainWindow = figure(    'Colormap', gray(20), ...
                                'Position',[50 200 500 500], ...
                                'Tag','mainWindow', ...
                                'Resize','off', ...
                                'Name','Hopfield Network', ...
                                'MenuBar','none', ...
                                'ToolBar','none', ...
                                'UserData', mainstruct);

	%Set the title
	uicontrol( 'Parent', mainWindow, ...
		'Position', [180 470 140 15], ...
		'Style', 'text', ...
		'HorizontalAlignment', 'center', ...
		'String', 'Hopfield Network');

	%create a weightWindow figure with the visibility of it
	%initially turned off.
	weightWindow = figure( 'Position', [560 200 400 420], ...
			'HitTest', 'off', ...
			'Tag','weightWindow', ...
			'Resize', 'off', ...
			'Name', 'Weights', ...	
			'MenuBar', 'none', ...
			'ToolBar', 'none', ...
			'Visible', 'off', ...
			'UserData', 'none');
	
	uicontrol('Parent', mainWindow, ...  
		'Position', [0 415 65 15], ...
		'Style', 'text', ...
		'HorizontalAlignment', 'left', ...
		'String', 'Stored');

	%The storage cells are implemented using a matlab 'uicontrol'
	%with a value of 'checkbox'
	boxSize = 14; %size of each checkbox used to represent a cell.
	gapH = 1; %Horizontal size between boxes.
	gapV = 1; %Vertical size between boxes.
	startCol = 80;
	columns = 25 -1; %number of columns in a row.
	rows = 5 -1; %total number of rows for storage.
	sizeH = boxSize + gapH * 2; %total horizontal and vertical size,
	sizeV = boxSize + gapV * 2; %for calculating placement of boxes.
	colLength = startCol + (sizeH * columns);
	startRow = 450;
	rowLength = startRow - (sizeV * rows);	
	
	%draw  Storage boxes.
	for i = startCol: sizeH: colLength,
		for j = startRow: -sizeV: rowLength,
			t1 = (i - startCol)/sizeH + 1;
			t2 = (j - startRow)/sizeV;
			t2 = abs(t2);
			t2 = t2 + 1;
			%n1 = num2str(t1);
			%n2 = num2str(t2);
			%radioName=strcat(n1, n2);
			uicontrol( 'Parent', mainWindow, ...
				'Units','pixels', ...
				'Position', [i j boxSize, boxSize], ...
				'Style', 'checkbox', ...
				'Callback', 'hopfield radioStorageCB', ...
				'Tag', 'none', ...
				'Userdata', [t1 t2], ...
				'Selected', 'off');
		end;
	end;

	%Input row
	inputHeight = 300;
	uicontrol('Parent', mainWindow, ...
                'Position', [0 inputHeight 65 15], ...
                'Style', 'text', ...
                'HorizontalAlignment', 'left', ...
                'String', 'Inputs');

	for i = startCol: sizeH: colLength,
		ind =i;
		ind = (ind - startCol) / sizeH + 1;
                 uicontrol( 'Parent', mainWindow, ...
                         'Units','pixels', ...
                         'Position', [i inputHeight boxSize, boxSize], ...
                         'Style', 'checkbox', ...
                         'Callback', 'hopfield radioInputCB', ...
			 'Tag', 'inputCheckbox', ...	
                         'Userdata', ind, ...
                         'Selected', 'off');
         end; 
		
	%output row
	outputHeight = 250;
        uicontrol('Parent', mainWindow, ...
                'Position', [0 outputHeight 65 15], ...
                'Style', 'text', ...
                'HorizontalAlignment', 'left', ...
                'String', 'Outputs');

        for i = startCol: sizeH: colLength,
		ind = i;
		ind = (ind - startCol) / sizeH + 1;
                 uicontrol( 'Parent', mainWindow, ...
                         'Units','pixels', ...
                         'Position', [i outputHeight boxSize, boxSize], ...
                         'Style', 'checkbox', ...
                         'Callback', 'hopfield radioOutputCB', ...
			 'Tag', 'outputCheckbox', ...
                         'Userdata', ind, ...
	 	        'HitTest', 'off', ...
                         'Selected', 'off');
         end;


	buttonSizeH = 60; %Horizontal and vertical size of buttons.
	buttonSizeV = 30;
	
	hGap = 15;

	startH = 50;
	yPos = 50;

	initPos = [startH yPos buttonSizeH buttonSizeV];
	uicontrol( 'Parent', mainWindow, ...
               'Units','pixels', ...
               'Position', initPos, ...
               'FontSize',10, ...
               'HorizontalAlignment', 'center', ...
               'String', 'Init', ...
               'Style','pushbutton', ...
               'Tag', 'initButton', ...
               'Callback', 'hopfield initCB', ...
               'UserData', 'nothing');

	startH = startH + buttonSizeH + hGap; 
	runPos = [startH yPos buttonSizeH buttonSizeV];
	uicontrol( 'Parent', mainWindow, ...
		'Units', 'pixels', ...
		'Position', runPos, ...
		'FontSize',10, ...
		'HorizontalAlignment', 'center', ...
		'String', 'Run', ...
		'Style', 'pushbutton', ...
		'Tag', 'runButton', ...
		'Callback', 'hopfield runCB', ...
		'UserData', 'nothing');

	startH = startH + buttonSizeH + hGap; 
	randomPos = [startH yPos buttonSizeH buttonSizeV];
	uicontrol( 'Parent', mainWindow, ...
	                'Units', 'pixels', ...
	                'Position', randomPos, ...
	                'FontSize',10, ...
	                'HorizontalAlignment', 'center', ...
       	 	'String', 'Random', ...
       	 	'Style', 'pushbutton', ...
       	  	'Tag', 'randomButton', ...
       		'Callback', 'hopfield randomCB', ...
		'UserData', 'nothing');

	startH = startH + buttonSizeH + hGap; 
	uicontrol( 'Parent', mainWindow, ...
		'Position', [startH yPos 90 20 ], ...
		'String', 'Show Weights', ...
		'Style', 'radioButton', ...
		'CallBack', 'hopfield showWeightsCB');


%function that will randomize the storage matrix.
%note that it does not update the view, only the model. 
function randomizeStorage
	h = findobj('Tag', 'mainWindow');
	mainStruct= get(h, 'UserData');
	storageArray = mainStruct.storage;
	R = rand(size(storageArray));
	xy = size(R);
	x = xy(1);
	y = xy(2);
	for i = 1 : x,
		for j = 1 : y,
			if R(i,j) > .6
				R(i,j) = 1;
			else
				R(i,j) = -1;
			end;
		end;
	end;
	storageArray = R;
	mainStruct.storage = storageArray;
	set(h, 'UserData', mainStruct);

%function that will randomize the input matrix.
%does not update view, only model.
function randomizeInput
        h = findobj('Tag', 'mainWindow');
        mainStruct= get(h, 'UserData');
        inputArray = mainStruct.input;
        R = rand(size(inputArray));
        xy = size(R);
	x = xy(1);
	y = xy(2);
	for i = 1 : x,
		for j = 1 : y,
			if R(i,j) > .6
				R(i,j) = 1;
			else
				R(i,j) = -1;
			end;
		end;
	end;
	mainStruct.input = R;
	set(h, 'UserData', mainStruct);

%Updates the view of the storage boxes with the matrix.
function setStorageBoxes
        h = findobj('Tag', 'mainWindow');
        mainStruct= get(h, 'UserData');
        storageArray = mainStruct.storage;
	xy = size(storageArray);
	x = xy(1);
	y = xy(2);
	%find all the storage boxes.
	st = findobj('Callback', 'hopfield radioStorageCB');
	%move through the array and set all the checkboxs
	%to the array values.
	for i = 1 : x,
		for j = 1 : y,	
			obj = findobj(st, 'UserData', [i j]);
			if storageArray(i,j) == -1
				val = 0;
			else
				val = 1;
			end;
			set(obj, 'Value', val);
		end;
	end;
		
%Updates the view of the storage boxes with the matrix.
function setInputBoxes
        h = findobj('Tag', 'mainWindow');
        mainStruct= get(h, 'UserData');
        inputArray = mainStruct.input;
        xy = size(inputArray);
        x = xy(1);
        y = xy(2);
        %find all the input boxes.
        st = findobj('Callback', 'hopfield radioInputCB');
        %move through the array and set all the checkboxs
        %to the array values.
        for i = 1 : x,
	for j = 1 : y,
               	 obj = findobj(st, 'UserData', i);
               	 if inputArray(i,j) == -1
               	         val = 0;
               	 else
               	         val = 1;
	 end;
                set(obj, 'Value', val);
	end;
        end;


%Updates the view of the storage boxes with the matrix.
function setOutputBoxes
        h = findobj('Tag', 'mainWindow');
        mainStruct= get(h, 'UserData');
        outputArray = mainStruct.output;
        xy = size(outputArray);
        x = xy(1);
        y = xy(2);
        %find all the output boxes.
        st = findobj('Callback', 'hopfield radioOutputCB');
        %move through the array and set all the checkboxs
        %to the array values.
        for i = 1 : x,
	  for j = 1 : y
		  obj = findobj(st, 'UserData', i);
	                if outputArray(i,j) == -1
	                        val = 0;
	                else
	                        val = 1;
	                end;
	end;
	set(obj, 'Value', val);
        end;

%store the user inputed pattern into the proper matrix
function storePattern
	h = findobj('Tag', 'mainWindow');
	struct= get(h, 'UserData');
	normFac = struct.numberStored * sqrt(struct.netSize);
	w = struct.weights;	
	%make one long array with all nodes in it.
	in = struct.input;
	out = struct.output;
	stor = struct.storage;

	%reset weights
	for i = 1 : struct.netSize,
		for j = 1 : struct.netSize,
			w(i, j) = 0;
		end;
	end;	

	%store patterns
	for p = 1 : struct.numberStored,
		for i = 1 : struct.netSize,
			for j = 1 : struct.netSize
				if i ~= j
					w(i, j) = w(i, j) + stor(i, p) * stor(j,p) / normFac;
				end;
			end;
		end;
	end;
	struct.weights = w;
	set(h, 'UserData', struct);

function computeOutput
	h = findobj('Tag', 'mainWindow');
	struct = get(h, 'UserData');
	outArray = struct.output;
	inArray = struct.input;
	w = struct.weights;
	%set the output array to the input array if
	%the input array has changed. Start from the
	%the input pattern, and then let the output
	%array changed through the weights.
	if struct.inputChanged
		outArray = inArray;
		struct.inputChanged = 0;
		set(h, 'UserData', struct);
		setOutputBoxes;
		drawnow;
	end;

	%%%%%%%%%%%Show the change in the Output array %%%%%%%%%
		
	%k index is never used, just a counter.
	for k = 1 : struct.netSize,
		%random number between 1 and 25
		%a random row from the weight matrix
		i = floor(rand * 25 + 1);
		a = 0;	
		%sum the mulitplication of the random row from the
		%weight matrix by the output matrix
		a = sum(w(i, :) .* outArray'); 
		if a < 0
			outArray(i) = -1;
		else
			outArray(i) = 1;
		end;
		struct.output=outArray;
		set(h, 'UserData', struct);
		setOutputBoxes;
		drawnow;
	end;
	struct.output = outArray;
	set(h, 'UserData', struct);

function showWeights
	main = findobj('Tag', 'mainWindow');
	struct = get(main, 'UserData');
	w = struct.weights;
	weightWin = findobj('Tag', 'weightWindow');
	% give the weight window focus
	figure(weightWin);
	%pseudocolor plot
	%this color map can be other colors such as bone, cool, hot, or flag.
	%there will be some weirdness in X if the colormaps don't match between
	%the main window and this one.
	colormap(gray(20));
	pcolor(w);
	%map the axis matrix style.
	axis ij;
	%map the axis cartesian style
	%axis xy;	
	axis square;	
