%AutoAssociator demo BioNB330
%Bruce Land
%Oct2004

% Neurons can take the values -1 or +1 (McCulloch Pitts type but inactive
% = -1, makes the math easier). At first, all weights are set to zero. Then to
% store a pattern, you calculate the weights by using:
% 
% af=1/sqrt(N)*f1*transpose(1/sqrt(N)*f1);
% 
% where f1 is a vector with the pattern in it, N is the total number of
% neurons and af is the weight matrix.
% 
% To recall, you calculate the activation value for each neuron:
% 
% f1_new=Heavyside (af*(1/sqrt(N))*f1, 0.0)   where f1_new is the pattern
% after updating and f1 is the pattern you start from, usually a noisy version
% of the stored pattern, heavyside means that the output is +1 if>0.0, -1 if
% <= 0.0.
% 
% To add a second pattern, you calculate its weight matrix the same way and
% then add the two up.

clear all

%build the GUI
%define the figure

colordef white

fig=figure(1);
clf
figTop = 650;
figRight = 800;
set(fig,'resize','off',...
    'doublebuffer','on',...
    'color',[1 1 1]*0.8,...
    'position',[100 100 figRight figTop],...
    'name', 'BioNB 330 Linear Associator Simulator',...
    'numbertitle','off')

%===Main state controls=================
x=10; y=figTop-25;

%define the Quit button
quitbutton=uicontrol('style','pushbutton',...
    'string','Quit', ...
    'fontsize',12, ...
    'position',[figRight-65,y,50,20], ...
    'callback','quitit=1;close all;');

x=500;
y=500;
bspace = 50;
%reset the weights
resetbutton=uicontrol('style','pushbutton',...
    'string','Reset Weights', ...
    'fontsize',14, ...
    'position',[x,y,200,25], ...
    'callback', 'resetW=1;');

%clear pattern
y=y-bspace;
clearbutton=uicontrol('style','pushbutton',...
    'string','Clear Pattern', ...
    'fontsize',14, ...
    'position',[x,y,200,25], ...
    'callback','clearP=1;inputPush=1;');

%define the store weight button
y=y-bspace;
storebutton=uicontrol('style','pushbutton',...
    'string','Set Weights', ...
    'fontsize',14, ...
    'position',[x,y,200,25], ...
    'callback','setW=1;');

%recall button
y=y-bspace;
recallbutton=uicontrol('style','pushbutton',...
    'string','Recall Pattern', ...
    'fontsize',14, ...
    'position',[x,y,200,25], ...
    'callback','recallP=1;');

y=y - 2*bspace;
uicontrol('style','text',...
    'string','Similarity', ...
    'fontsize',14, ...
    'position',[x,y,200,25]);
y=y-200;
patternList=uicontrol('style','listbox',...
    'string','', ...
    'fontsize',12, ...
    'position',[x,y,200,200] ...
    );

%===input regions===============================
x=100; y=500;
inSize = 40;
delta = inSize + 5;
for i=1:5
    for j=1:5
        inUI(i,j) = uicontrol('style','pushbutton',...
            'string',' ', ...
            'fontsize',14, ...
            'userdata', -1, ...
            'backgroundcolor', 'w', ...
            'position',[x+(i-1)*delta, y-(j-1)*delta ,inSize,inSize], ...
            'callback',...
            ['if -1==get(gcbo,''userdata'');' ...
                    'set(gcbo,''userdata'',1);'...
                    'set(gcbo,''backgroundcolor'',''k'');' ...
                'else;' ...
                    'set(gcbo,''userdata'',-1);'...
                    'set(gcbo,''backgroundcolor'',''w'');' ...
                'end;' ...
                'inputPush=1;'
            ]);
    end
end

%
%===set button states===========================
quitit = 0;
resetW=1;
setW=0;
recallP=0;
clearP=0;
inputPush=0;
weights = zeros(25,25);
countP = 0;

%===main event loop===============================
while (quitit==0)
  
    %set weights
    if setW 
        setW=0;
        %get input vector (as a col vector)
        InVector = [];
        for i=1:5
            for j=1:5
                InVector = [InVector; get(inUI(i,j),'userdata')];
            end
        end
        %weight = 1/sqrt(N)*f1*transpose(1/sqrt(N)*f1) ;  
        %outer product
        weights = weights + (InVector * InVector')/25 ;
        %handle the pattern list
        countP=countP+1;
        pattern{countP} = InVector;
        listString{countP} = ['Pattern #',num2str(countP)];
        set(patternList,'string',listString)
    end
    
    %reset weights 
    if resetW 
        resetW=0;
        weights = zeros(25,25);
        set(patternList,'string','')
        countP = 0;
        listString = [];
    end
    
    %clear pattern 
    if clearP 
      clearP=0; 
      set(inUI,'userdata',-1);
      set(inUI,'backgroundcolor','w'); 
    end
    
    %produce output
    if recallP
        recallP=0;
        %f1_new=Heavyside (af*(1/sqrt(N))*f1, 0.0)
        %get input vector (as a col vector)
        InVector = [];
        for i=1:5
            for j=1:5
                InVector = [InVector; get(inUI(i,j),'userdata')];
            end
        end
        output = 2*((weights*InVector/5)>0) - 1;
        %show pattern
        for i=1:5
            for j=1:5
                set(inUI(j,i),'userdata', output(i+(j-1)*5) );
                if (output(i+(j-1)*5)>0)
                    col = [0 0 0];
                else
                    col = [1 1 1];
                end
                set(inUI(j,i),'backgroundcolor', col );
            end
        end
    end
    
    %ANY input is clicked
    if inputPush==1
        inputPush=0;
        %get the current input vector
        InVector = [];
        for i=1:5
            for j=1:5
                InVector = [InVector; get(inUI(i,j),'userdata')];
            end
        end
        if countP>0
            listString = [];
            %compute dist to each pattern
            for i=1:countP
                p = sum(abs(InVector - pattern{i}))/2;
                listString{i} = ['Pattern #',num2str(i),'  Distance=',num2str(p)];
            end
            set(patternList,'string',listString)
        end
    end
    
    %
    drawnow    
end %main while loop



