%-------------------------------------------------------------------
function dopca(data,testdata)

mu = mean(data);
data = data - repmat(mu,size(data,1),1);
sig = data'*data/size(data,1);
[u,s,v] = svd(sig);
ev = diag(s);

figure;
hold on;
title('eigenvalues of data');
plot(ev);
axis tight;
hold off;

% compute the sum of the eigenvalues less than a given value
% as the predicted squared reconstruction error 
esterr = cumsum(ev(size(data,2):-1:1));
esterr = esterr(size(data,2):-1:1);

figure;
hold on;
title('predicted average reconstruction error');
plot(esterr);
axis tight;
hold off;

xverr = zeros(size(data,2),1);
% the squared reconstruction error is the sum of the squares of the
% projections of the data onto the axes *not* included (ie the n-i+1)
% smallest eigenvectors
proj = (testdata-repmat(mu,size(testdata,1),1))*v;
for i=1:size(data,2)-1,
	xverr(i) = sum(sum(proj(:,i+1:size(data,2)).^2))/size(testdata,1);
end;
figure;
hold on;
title('average reconstruction error on test data');
plot(xverr);
axis tight;
hold off;

npics = 30;
ndim = 20;
pos = floor(linspace(1,size(proj,1),npics));
dim = ceil(logspace(0,log10(size(proj,2)),ndim));
pic = ones(ndim*16,npics*16);
for i=1:ndim,
for j=1:npics,
	pic((i-1)*16+1:i*16,(j-1)*16+1:j*16) = ...
		reshape((proj(pos(j),1:dim(i))*v(:,1:dim(i))'+mu),16,16)';
end;
end;
figure;
image(pic*64);
axis off;
axis image;
colormap gray;
for i=1:ndim,
	text(-8,8+(i-1)*16,sprintf('%d',dim(i)),'HorizontalAlignment','right');
end;

%-------------------------------------------------------------------
function testclassifier(trainx,trainy,testx,testy,ndim,classifier,varargin)

mu = mean(trainx);
d = trainx - repmat(mu,size(trainx,1),1);
sig = d'*d/size(trainx,1);
[u,s,v] = svd(sig);
% notice that I'm not subtracting off of the mean, this just means
% that the projected data won't be zero mean (otherwise the same),
% which should not effect the classifier
trainp = trainx*v;
testp = testx*v;
sqerr = zeros(size(ndim));
figure;
for i=1:size(ndim),
	outy = feval(classifier,testp(:,1:ndim(i)),...
		trainp(:,1:ndim(i)),trainy,varargin{:});
	sqerr(i) = sum(sum((testy-outy).^2));
	% just plot each time to convince me things are still happening
	plot(ndim,sqerr/200);
	pause(0.01);
end;
clf;
% note that sqerr/200 is the average miss rate (since a miss will count 
%  as 2 and a hit as 0)
title('Error rate on test data verses number of eigenvectors');
plot(ndim,sqerr/200);


%-------------------------------------------------------------------
function outy = testmlp(input,data,target)

[w,b] = trainmlp(data,target,[15 size(target,2)],1/size(data,2));
outy = flagmax(mlp(input,w,b));


%-------------------------------------------------------------------
function [w,b] = trainmlp(input, target, layers, scale),
% this is the same function as from the solutions of problem
% set 2, except that the training rates and stopping conditions
% have been modified to work better with this problem set

% initialize the network:
nex = size(input,1);
nin = size(input,2);
nlayers = size(layers,2);
layers = [nin , layers];
for i=1:nlayers,
	w{i} = scale*randn(layers(i),layers(i+1));
	b{i} = scale*randn(1,layers(i+1));
end;

% now train:
eta = 10/nex;
j = 1;
wch = 2;
while(j<10 | wch>1),
	for i=1:nlayers,
		wold{i} = w{i};
	end;
	for i=1:100,
		z = mlpforward(input,w,b); % get all of the outputs
		delta = mlpbackward(target,w,b,z);
		[w,b] = mlpupdate(input,w,b,z,delta,eta);
	end;
	wch = 0;
	for i=1:nlayers,
		wch = wch + sum(sum((wold{i}-w{i}).^2));
	end;
	wch
	if (isinf(wch) | isnan(wch)),
		for i=1:nlayers,
			w{i} = scale*randn(layers(i),layers(i+1));
			b{i} = scale*randn(1,layers(i+1));
		end;
	end;
	eta = eta*0.8;
	error = sum((z{nlayers}-target).^2)
	j = j+1;
end;


%-------------------------------------------------------------------
% script to run problem 2

load all_chars;

dopca(chars,tchars);

nd=[1 2 4 8 12 16 20 25 30 35 40 45 50 55 60 65 70 80 90 100 125 150 200 256]';
% now try a nearest-neighbor classifier
testclassifier(chars,target,tchars,correct,nd,'nearest');

testclassifier(chars,target,tchars,correct,nd,'testmlp');


