function som_iris_ex4()
% SOM on IRIS for different final neighborhood sizes
% (port of your Python code to MATLAB)
rng(7); % reproducible random generator
R = 12;
C = 12; % bigger lattice
M = R * C;
iters = 4000; % more iterations for bigger maps
eta0 = 0.12;
sigma0 = max(R, C) / 2; % initial neighborhood (global ordering)
sigma_final_list = [1 2 3]; % final sigmas to test
% --- load and scale iris data (150x4, each feature to [0,1]) ---
load fisheriris % variables: meas (150x4), species
X = meas; % 150x4
S = size(X, 1);
Xmin = min(X, [], 1);
Xmax = max(X, [], 1);
X = (X - Xmin) ./ (Xmax - Xmin); % min-max scaling to [0,1]
fprintf('loaded iris: %d samples, %d features\n', S, size(X, 2));
fprintf('grid: %dx%d = %d neurons\n', R, C, M);
fprintf('testing sigma_final: [');
fprintf('%d ', sigma_final_list);
fprintf(']\n');
% grid coordinates (same idea as Python meshgrid + ravel)
[gx, gy] = meshgrid(1:C, 1:R); % gx,gy: R x C
grid2d = [gx(:), gy(:)]; % M x 2
% --- figure for topographic error ---
fig1 = figure('Color', 'w', 'Position', [100 100 800 500]);
ax1 = axes('Parent', fig1);
hold(ax1, 'on');
xlabel(ax1, 'Iteration');
ylabel(ax1, 'Topographic Error');
title(ax1, sprintf('R=%d, C=%d — Effect of final \\sigma in {1,2,3}', R, C));
grid(ax1, 'on');
box(ax1, 'on');
results = struct('sigma_final', {}, 'W', {}, 'topoErr', {}, 'topoErr_s', {});
tau = iters; % single time constant for whole run
for k = 1:numel(sigma_final_list)
sigma_final = sigma_final_list(k);
fprintf('\n=== Testing sigma_final = %d ===\n', sigma_final);
% weights in [0,1], same scale as X
W = rand(M, size(X, 2));
topoErr = zeros(iters, 1);
for t = 1:iters
% --- time-dependent sigma and eta ---
sigma_t = sigma_final + (sigma0 - sigma_final) * exp(-t / tau);
eta_t = eta0 * exp(-t / tau);
% --- random sample from X ---
idx_sample = randi(S);
x = X(idx_sample, :); % 1 x D
% --- BMU1, BMU2 ---
dists = sum((W - x).^2, 2); % M x 1
[~, idx_sorted] = sort(dists, 'ascend');
bmu1 = idx_sorted(1);
bmu2 = idx_sorted(2);
% --- Gaussian neighborhood around BMU1 in grid space ---
dgrid2 = sum((grid2d - grid2d(bmu1, :)).^2, 2); % M x 1
h = exp(-dgrid2 / (2 * sigma_t^2));
h = h / max(h); % normalize
% --- update weights ---
% implicit expansion: h (M x 1) with (x - W) (M x D)
W = W + eta_t * (h .* (x - W));
% --- topographic error: 0 if BMU1,BMU2 are neighbors, else 1 ---
topoErr(t) = ~are_neighbors(grid2d, bmu1, bmu2);
if mod(t, 1000) == 0
fprintf(' iteration %d/%d, sigma=%.3f, eta=%.5f\n', ...
t, iters, sigma_t, eta_t);
end
end
% smoothing TE with moving average (okno = 50)
win = 50;
topoErr_s = movmean(topoErr, win);
results(k).sigma_final = sigma_final;
results(k).W = W;
results(k).topoErr = topoErr;
results(k).topoErr_s = topoErr_s;
% plot TE curve
plot(ax1, 1:iters, topoErr_s, 'LineWidth', 1.5, ...
'DisplayName', sprintf('\\sigma_{final}=%d', sigma_final));
end
legend(ax1, 'Location', 'northeast');
% --- PCA projection and final lattices ---
fig2 = figure('Color', 'w', 'Position', [100 100 1200 350]);
for k = 1:numel(results)
W = results(k).W;
% PCA to 2D
[coeff, score] = pca(W); %#ok
W2 = score(:, 1:2); % M x 2
WX = reshape(W2(:, 1), R, C);
WY = reshape(W2(:, 2), R, C);
ax = subplot(1, numel(results), k, 'Parent', fig2);
hold(ax, 'on');
axis(ax, 'equal');
box(ax, 'on');
title(ax, sprintf('\\sigma_{final}=%d', results(k).sigma_final));
% horizontal lines (rows)
for r = 1:R
plot(ax, WX(r, :), WY(r, :), '-', 'LineWidth', 1.0);
end
% vertical lines (columns)
for c = 1:C
plot(ax, WX(:, c), WY(:, c), '-', 'LineWidth', 1.0);
end
% neuron markers
plot(ax, W2(:, 1), W2(:, 2), 'ko', 'MarkerSize', 2, 'MarkerFaceColor', 'k');
end
sgtitle('Final Lattices Projected by PCA');
% --- summary ---
fprintf('\n== Summary (lower TE is better) ==\n');
for k = 1:numel(results)
topoErr = results(k).topoErr;
last500 = topoErr(end-499:end);
te_mean = mean(last500);
fprintf('sigma_final=%d -> mean TE (last 500 iters): %.4f\n', ...
results(k).sigma_final, te_mean);
end
end
% =======================================================================
% local helpers
% =======================================================================
function tf = are_neighbors(grid2d, i, j)
% odleglosc Chebyshev = 1 (8-neighborhood)
% grid2d: M x 2, i,j: indices of neurons
di = abs(grid2d(i,1) - grid2d(j,1));
dj = abs(grid2d(i,2) - grid2d(j,2));
tf = max(di, dj) == 1; % true if neighbors
end