Code Listings
Contents
Code Listings#
newton_simple.m#
function [x, fval, exitflag] = newton_simple (fun, x0, options)
% Find minimum of unconstrained multivariable function.
%
% Input:
% fun - function (returning function value, gradient
% vector value and Hessian matrix value)
% x0 - initial guess
% options - structure with fields
% .MaxIterations - max. number of iterations
% .OptimalityTolerance - tolerance for the norm of gradient
% .Display - display details for 'iter'
%
% Output:
% x - solution
% fval - objective value at solution
% exitflag - 0 = no solution found
% 1 = solution found
%
narginchk(2,3);
opt = struct ();
if ((nargin == 3) && isstruct (options))
opt = options;
end
if (~isfield (opt, 'MaxIterations') || isempty (opt.MaxIterations))
opt.MaxIterations = 30;
end
if (~isfield (opt, 'Display'))
opt.Display = 'off';
end
if (~isfield (opt, 'OptimalityTolerance'))
opt.OptimalityTolerance = 1e-9;
end
if (strcmp(opt.Display, 'iter'))
fprintf('Iteration\tStep-size\tf(x)\t\t||df(x)||\n')
end
exitflag = 0;
x = x0(:);
i = 0;
while (i < opt.MaxIterations)
[fval, fgrad, fhess] = fun(x);
if (norm (fgrad, 'inf') < opt.OptimalityTolerance)
exitflag = 1; % success!
return;
end
d = fhess \ -fgrad;
if (strcmp (opt.Display, 'iter'))
fprintf('%5d\t\t%e\t%e\t%e\n', i, norm (d, 'inf'), ...
fval, norm (fgrad, 'inf'));
end
x = x + d;
i = i + 1;
end
if (i >= opt.MaxIterations)
warning ('MaxIterations = %d reached.\n', opt.MaxIterations);
end
end
nelder_mead.m#
function [x, fval, exitflag] = nelder_mead (fun, x0, options)
% Find minimum of unconstrained multivariable function.
%
% Input:
% fun - function
% x0 - initial guess
% options - structure with fields
% .MaxIterations - max. number of iterations
% .TolFun - tolerance for function values
% .Display - display details for 'iter',
% display animation for 'full'.
%
% Output:
% x - solution
% fval - objective value at solution
% exitflag - 0 = no solution found
% 1 = solution found
%
narginchk(2,3);
opt = struct ();
if ((nargin == 3) && isstruct (options))
opt = options;
end
if (~isfield (opt, 'MaxIterations') || isempty (opt.MaxIterations))
opt.MaxIterations = 30;
end
if (~isfield (opt, 'Display'))
opt.Display = 'off';
end
if (~isfield (opt, 'TolFun'))
opt.TolFun = 1e-9;
end
if (strcmp(opt.Display, 'iter') || strcmp (opt.Display, 'full'))
disp (' Iteration Func-count min f(x) Procedure');
h1 = [];
h2 = [];
h3 = [];
end
exitflag = 0;
% Parameter, here constant.
alpha = 1;
beta = 2;
gamma = 0.5;
len = 1;
% Create start simplex.
n = length (x0);
p = (len / sqrt (2)) * (sqrt (n + 1) - 1) / n;
q = [zeros(n,1), ones(n) * p];
x = repmat (x0(:), 1, n + 1) + q ...
+ [zeros(n,1), (len / sqrt (2)) * eye(n)];
% Compute all function values.
fvals = zeros (1, n + 1);
for i = 1:(n + 1)
fvals(i) = fun(x(:,i));
end
fcount = n + 1;
% Sort simplex (left is best).
[fvals, idx] = sort (fvals);
x = x(:,idx);
i = 0;
while (i < opt.MaxIterations)
% Compute centroid.
m = sum(x(:,1:n), 2) / n;
% Compute reflect.
r = m + alpha * (m - x(:,end));
fr = fun(r);
fcount = fcount + 1;
if (strcmp (opt.Display, 'full'))
delete (h1);
delete (h2);
delete (h3);
h1 = plot3 ([x(1,:), x(1,1)], [x(2,:), x(2,1)], [fvals, fvals(1)]);
h2 = plot3 (x(1,1), x(2,1), fvals(1), 'ro');
h3 = plot3 ([x(1,end), r(1)], [x(2,end), r(2)], [fvals(end), fr], 'ro');
pause (0.5);
end
if (fr <= fvals(2)) % Case 1: reflect
if (fr < fvals(1)) % Case 2: expand
e = m + beta * (m - x(:,end));
fe = fun(e);
fcount = fcount + 1;
if (fe < fr) % Expansion successful =)
r = e;
fr = fe;
end
% Replace worst x(:,end) with r (or s).
x = [r, x(:,1:n)];
fvals = [fr, fvals(1:n)];
step_method = 'expand';
else
% Replace worst x(:,end) with r.
x = [x(:,1), r, x(:,2:n)];
fvals = [fvals(1), fr, fvals(2:n)];
step_method = 'reflect';
end
else % Case 3: Contract / Shrink
if (fr > fvals(end))
step_method = 'contract inside';
c = m - gamma * (m - x(:,end));
else
step_method = 'contract outside';
c = m + gamma * (m - x(:,end));
end
fc = fun(c);
fcount = fcount + 1;
if (fc < fvals(end))
% Replace worst x(:,end) with c.
x = [x(:,1:n), c];
fvals = [fvals(1:n), fc];
else
step_method = 'shrink';
x = (x + repmat (x(:,1), 1, n + 1)) / 2;
% Compute new function values again.
for i = 1:(n + 1)
fvals(i) = fun(x(:,i));
end
fcount = fcount + n + 1;
end
% Sort again.
[fvals, idx] = sort (fvals);
x = x(:,idx);
end
fval = fvals(1);
if (strcmp (opt.Display, 'iter') || strcmp (opt.Display, 'full'))
fprintf (' %2d %2d %+8f %s\n', ...
i, fcount, fval, step_method);
end
% Check stopping criteria.
if (var (fvals) < opt.TolFun)
x = x(:,1)';
exitflag = 1; % success!
return;
end
i = i + 1;
end
x = x(:,1)';
if (i >= opt.MaxIterations)
warning ('MaxIterations = %d reached.\n', opt.MaxIterations);
end
end
lp_solver.m#
function [x, fval, exitflag, output] = lp_solver (c, A, b, x0, options)
% LP_SOLVER Solver for linear programs (LP).
%
% Input:
%
% min c'*x
% s.t. A *x = b
% x >= 0
%
% x0 - initial guess
%
% Output:
% x - solution
% fval - objective value at solution
% exitflag - 0 = no solution found
% 1 = solution found
% output - struct with fields:
% .xpath - history of x
%
narginchk (3, 5);
opt = struct ();
if ((nargin == 5) && isstruct (options))
opt = options;
end
if (~isfield (opt, 'MaxIterations') || isempty (opt.MaxIterations))
opt.MaxIterations = 10;
end
if (~isfield (opt, 'Display'))
opt.Display = 'off';
end
if (~isfield (opt, 'OptimalityTolerance'))
opt.OptimalityTolerance = 1e-8;
end
c = c(:);
b = b(:);
n = length (c);
m = length (b);
% Hessian matrix for Newton step.
H = @(x,s) [ zeros(n), -eye(n), A'; ...
diag(s), diag(x), zeros(n, m); ...
A, zeros(m,n + m) ];
% Gradient vector for Newton step.
G = @(x,s,y,mu) [ c - s + A' * y; ...
x .* s - mu * ones(n, 1); ...
A * x - b ];
% Initial values. x should be an inner LP point.
if ((nargin > 3) && ~isempty (x0))
x = x0(:);
else
x = ones (n, 1);
end
s = ones (n, 1);
y = ones (m, 1);
mu = 0.1;
% Track central path.
output.xpath = x;
if (strcmp (opt.Display, 'iter'))
fprintf('Iteration fval ||dx|| ||dy|| ||ds||\n')
end
fval = c'*x;
exitflag = 0;
% Newton iteration.
for i = 1:opt.MaxIterations
% Newton step on optimality conditions.
d = H(x,s) \ -G(x,s,y,mu);
% Stop iteration if x does not change any more.
if (norm (d(1:n), 'inf') < opt.OptimalityTolerance)
if (strcmp (opt.Display, 'iter'))
fprintf ('\nIPM converged in step %d.\n\n', i);
end
exitflag = 1;
break;
end
% Update variables.
x = x + d(1:n);
s = s + d((1:n) + n);
y = y + d((1:m) + (n * 2));
fval = c'*x;
mu = mu^2; % Shrink mu fast.
% Output step statistics.
output.xpath(:,end+1) = x;
if (strcmp (opt.Display, 'iter'))
fprintf('%5d %.2f %.2e %.2e %.2e\n', ...
i, fval, ...
norm (d(1:n), 'inf'), ...
norm (d((1:m) + n), 'inf'), ...
norm (d((1:m) + (n * 2)), 'inf'));
end
end
if (i == opt.MaxIterations)
warning ('MaxIterations = %d reached.\n', opt.MaxIterations);
end
end
um01_experiment.m#
Requires newton_simple
and nelder_mead
function.
function um01_experiment ()
disp ('Step 1: plotting the problem')
f = @(x,y) x.^4 + 2.*x.*y + (1 + y).^2;
[x, y] = meshgrid (linspace (-3, 3, 20));
mesh (x, y, f(x,y));
grid on;
xlabel ('x');
ylabel ('y');
zlabel ('f(x,y)');
title ('f(x,y) = x^4 + 2xy + (1 + y)^2');
hold on;
plot3 (1, -2, -2, 'ro');
view (-2, 70);
disp ('Step 2: Try fminsearch')
format long;
xpath = zeros(0, 3); % Memorize path
x0 = [1, 1];
options.Display = 'iter';
[xopt, fval, exitflag] = fminsearch (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'bo-'});
disp ('Step 3: Try fminunc')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
options.GradObj = 'on';
[xopt, fval, exitflag] = fminunc (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'go-'});
disp ('Step 4: Try newton_simple')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
[xopt, fval, exitflag] = newton_simple (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'ro-'});
disp ('Step 5: Try nelder_mead')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
options.MaxIterations = 30;
[xopt, fval, exitflag] = nelder_mead (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'mo-'});
% Nested function to pass to fminsearch and fminunc.
function [fx, gx, hx] = fun (x)
fx = x(1).^4 + 2.*x(1).*x(2) + (1 + x(2)).^2;
gx = [ 4.*x(1).^3 + 2.*x(2); ...
2.*x(1) + 2.*(1 + x(2)) ];
hx = [ 12.*x(1).^2, 2; ...
2, 2 ];
xpath = [xpath; x(:)', fx]; % Memorize path
end
end
function animate_path (xpath, LineSpec)
for i = 2:size (xpath, 1)
plot3 (xpath(i-1:i,1), xpath(i-1:i,2), xpath(i-1:i,3), LineSpec{:});
pause (0.1);
drawnow ();
end
end
um02_experiment.m#
Requires newton_simple
and nelder_mead
function.
function um02_experiment ()
disp ('Step 1: plotting the problem')
f = @(x,y) x.^3 - 6.*x.*y + 8.*y.^3;
[x, y] = meshgrid (linspace (-2, 2, 20));
mesh (x, y, f(x,y));
grid on;
xlabel ('x');
ylabel ('y');
zlabel ('f(x,y)');
title ('f(x,y) = x^3 - 6xy + 8y^3');
hold on;
plot3 (1, 0.5, -1, 'ro');
view (-60, 50);
disp ('Step 2: Try fminsearch')
format long;
xpath = zeros(0, 3); % Memorize path
x0 = [1, 1];
options.Display = 'iter';
[xopt, fval, exitflag] = fminsearch (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'bo-'});
disp ('Step 3: Try fminunc')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
options.GradObj = 'on';
[xopt, fval, exitflag] = fminunc (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'go-'});
disp ('Step 4: Try newton_simple')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
[xopt, fval, exitflag] = newton_simple (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'ro-'});
disp ('Step 5: Try nelder_mead')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
options.MaxIterations = 4;
[xopt, fval, exitflag] = nelder_mead (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'mo-'});
% Nested function to pass to fminsearch and fminunc.
function [fx, gx, hx] = fun (x)
fx = x(1).^3 - 6.*x(1).*x(2) + 8.*x(2).^3;
gx = [ 3.*x(1).^2 - 6.*x(2); ...
24.*x(2).^2 - 6.*x(1) ];
hx = [ 6.*x(1), -6; ...
-6, 48.*x(2) ];
xpath = [xpath; x(:)', fx]; % Memorize path
end
end
function animate_path (xpath, LineSpec)
for i = 2:size (xpath, 1)
plot3 (xpath(i-1:i,1), xpath(i-1:i,2), xpath(i-1:i,3), LineSpec{:});
pause (0.1);
drawnow ();
end
end
um03_experiment.m#
Requires newton_simple
and nelder_mead
function.
function um03_experiment ()
disp ('Step 1: plotting the problem')
f = @(x,y) 100 * (y - x.^2).^2 + (1 - x).^2;
[x, y] = meshgrid (linspace (-2, 2, 30), linspace (-1, 4, 30));
mesh (x, y, f(x,y));
grid on;
xlabel ('x');
ylabel ('y');
zlabel ('f(x,y)');
title ('Rosenbrock function');
hold on;
plot3 (1, 1, 0, 'ro');
view (-30, 50);
disp ('Step 2: Try fminsearch')
format long;
xpath = zeros(0, 3); % Memorize path
x0 = [-0.5, -0.5];
options.Display = 'iter';
[xopt, fval, exitflag] = fminsearch (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'bo-'});
disp ('Step 3: Try fminunc')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
options.GradObj = 'on';
[xopt, fval, exitflag] = fminunc (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'go-'});
disp ('Step 4: Try newton_simple')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
[xopt, fval, exitflag] = newton_simple (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'ro-'});
disp ('Step 5: Try nelder_mead')
xpath = zeros(0, 3); % Memorize path
%x0 = [1, 1];
options.Display = 'iter';
options.MaxIterations = 4;
[xopt, fval, exitflag] = nelder_mead (@fun, x0, options);
disp ('Solution');
disp (xopt);
disp ('Objective value at solution');
disp (fval);
fprintf ('exitflag = %d\n', exitflag);
animate_path (xpath, {'mo-'});
% Nested function to pass to fminsearch and fminunc.
function [fx, gx, hx] = fun (x)
fx = 100 * (x(2) - x(1).^2).^2 + (1 - x(1)).^2;
gx = [-400 * x(1) * (x(2) - x(1).^2) - 2 * (1 - x(1)); ...
200 * (x(2) - x(1).^2)];
hx = [(1200 * x(1).^2 - 400 * x(2) + 2), -400 * x(1); ...
-400 * x(1), 200];
xpath = [xpath; x(:)', fx]; % Memorize path
end
end
function animate_path (xpath, LineSpec)
for i = 2:size (xpath, 1)
plot3 (xpath(i-1:i,1), xpath(i-1:i,2), xpath(i-1:i,3), LineSpec{:});
pause (0.1);
drawnow ();
end
end