function som_9_13(epochs)

% function som_9_13(epochs)
% 
% This program recreates Figure 9.13 from textbook.
% Use SOM to learn topology of input space of Iris dataset.
% 
% Inputs : epochs      - number of training epochs.
% Outputs: none.
%
% Author : Nemanja Djuric
% Date   : Sept. 25th, 2010
%
% Warning: Check line 43 of this file for compatibility issues.

% check input
if (epochs < 1)
    disp('Error: Invalid input, epochs must be a positive integer.');
    return;
end;

% definitions
def_som_size = 6;

% preprocessing, nothing special, simply move label to the end
data = load('iris.txt');
data = [data(:, 2 : 5) data(:, 1)];

[len dim] = size(data);
num_features = dim - 1;

% shuffle data
[trash index] = sort(rand(size(data, 1), 1));
data = data(index,:);

% use 70% for training, and the remaining 30% for testing
[testdata traindata] = divideset(data, 70);

% normalization, however, there is no need for that, so I commented it
% [meanv, stdv, traindata(:, 1 : num_features)] = normalize(traindata(:, 1 : num_features), [], []);
% [meanvv, stdvv, testdata(:, 1 : num_features)] = normalize(testdata(:, 1 : num_features), meanv, stdv);

% first part is old version of newsom (tested on Matlab 7.0.0.19920), if 
%   it fails then new version of newsom is invoked (Matlab 7.7.0.471)
try
    % get range of features, needed for initialization of SOM
    min_max = minmax(traindata(:, 1 : num_features)');

    % create new SOM (old version)
    net = newsom(min_max, [def_som_size def_som_size], 'gridtop');
catch
    % create new SOM (new version)
    net = newsom(traindata(:, 1 : num_features)', [def_som_size def_som_size], 'gridtop');
end;

% train the SOM
net.trainParam.epochs = epochs;
net.trainParam.show = 10;
net = train(net, traindata(:, 1 : num_features)');

% use the learned weights to plot nice figure, using training data
plot_mat = zeros(def_som_size);
weights = net.iw{1, 1};
for i = 1 : length(traindata)
    point = traindata(i, :);
    label = point(end) * 10;
    
    for j = 1 : length(weights)
        dist(j) = eucl_dist(weights(j, :), point(1 : num_features));
    end;
    
    [trash ind] = min(dist);
    x_indice = ceil(ind / def_som_size);
    y_indice = ind - (x_indice - 1) * def_som_size;
    
    plot_mat(x_indice, y_indice) = label;
end;
icons = {'k.', 'rs', 'gv', 'b^'};
icon_color = {'k', 'r', 'g', 'b'};
class_names = {'Iris Setosa', 'Iris Versicolour', 'Iris Virginica'};
figure(1), clf;
hold on;
plot(1, 1, icons{1}, 'Markersize', 10, 'MarkerFaceColor', icon_color{1});
plot(1, 1, icons{2}, 'Markersize', 10, 'MarkerFaceColor', icon_color{2});
plot(1, 1, icons{3}, 'Markersize', 10, 'MarkerFaceColor', icon_color{3});
plot(1, 1, icons{4}, 'Markersize', 10, 'MarkerFaceColor', icon_color{4});
for i = 1 : def_som_size
    for j = 1 : def_som_size
        ind = plot_mat(i, j) / 10 + 1;
        plot(i, j, icons{ind}, 'Markersize', 20, 'MarkerFaceColor', icon_color{ind});
    end;
end;
legend('Neuron did not fire', class_names{1}, class_names{2}, class_names{3}, 'Location', 'SouthOutside');
t = title('SOM tested on training data (Iris dataset)');
set(t,'fontsize', 14);
set(gca, 'XTick', [], 'YTick', []);

% use the learned weights to plot nice figure, using testing data
plot_mat = zeros(def_som_size);
weights = net.iw{1,1};
for i = 1 : length(testdata)
    point = testdata(i, :);
    label = point(end) * 10;
    
    for j = 1 : length(weights)
        dist(j) = eucl_dist(weights(j, :), point(1 : num_features));
    end;
    
    [trash ind] = min(dist);
    x_indice = ceil(ind / def_som_size);
    y_indice = ind - (x_indice - 1) * def_som_size;
    
    plot_mat(x_indice, y_indice) = label;
end;
icons = {'k.', 'rs', 'gv', 'b^'};
icon_color = {'k', 'r', 'g', 'b'};
class_names = {'Iris Setosa', 'Iris Versicolour', 'Iris Virginica'};
figure(2), clf;
hold on;
plot(1, 1, icons{1}, 'Markersize', 10, 'MarkerFaceColor', icon_color{1});
plot(1, 1, icons{2}, 'Markersize', 10, 'MarkerFaceColor', icon_color{2});
plot(1, 1, icons{3}, 'Markersize', 10, 'MarkerFaceColor', icon_color{3});
plot(1, 1, icons{4}, 'Markersize', 10, 'MarkerFaceColor', icon_color{4});
plot(1, 1, icons{1}, 'Markersize', 10, 'MarkerFaceColor', icon_color{1});
for i = 1 : def_som_size
    for j = 1 : def_som_size
        ind = plot_mat(i, j) / 10 + 1;
        plot(i, j, icons{ind}, 'Markersize', 20, 'MarkerFaceColor', icon_color{ind});
    end;
end;
legend('Neuron did not fire', class_names{1}, class_names{2}, class_names{3}, 'Location', 'SouthOutside');
t = title('SOM tested on testing data (Iris dataset)');
set(t,'fontsize', 14);
set(gca, 'XTick', [], 'YTick', []);

pause;

try
    close(1);
catch
end;
try
    close(2);
catch
end;

% function that calculates Euclidean distance between points
function e_dist = eucl_dist(point1, point2)
e_dist = sqrt(sum((point1 - point2).^2));