function [] = plotGradAndObj(f,X,plotOptions,varargin)
% rolfk, 06.11.2014
% plotGradAndObj plots the objective function around a given startpoint.
% the area [-sStep.*nStep : sStep : sStep.*nStep] around X is plotted.
% Each dimension of X is plotted individually = a projection of each dimension is plotted.
% Dimension m is plotted by varying only x_m and keeping all other x_i fixed
% to the initialization value.
%
% INPUT:
% % f : a matlab .m file that returns the objective value and the gradient
% gradient vector of size Nx1
% X = (x_1,...,x_N)': the point (of size Nx1) where to start plotting
% ---------- plotOptions ----------
% plotOptions.sStep = scalar or Nx1 vector
% determines the distance between points, which are evaluated
% on the objective and the gradient
% if sStep is a scalar the distance is the same for all variables
%
% plotOptions.nStep = scalar or Nx1 vector (how many points to evaluate in each variable)
% if nStep is a scalar in each direction the same number of points are evaluated
%
% plotOptions.gradStep = plot gradient arrow every gradArrowStep-th point
%
% plotOptions.dim = array of size (Mx1) with 1<=M<=N: which dimension to plot
%
% plotOptions.ShowGrad = 1 (=show Gradient=tangents), 0= do not show gradients
%
% plotOptions.arrowLength = length of tangents in the plot
%
% plotOptions.MaxHeadSize = size of arrow head of the tangents
%
% plotOptions.Marker = Marker style of the point where the tangent touches the curve
% varargin: if the gradient and objective function require more input it can be provided here.
% REQUIREMENT ON THE FUNCTION f
% [obj, grad] = f(X,aux)
% with obj being the function value and grad the array of partial derivatives.
%
% USAGE - EXAMPLE:
% plotGradAndObj('exampleObjAndGrad',x0,options,[]);
%
% in a m-file exampleObjAndGrad.m write following lines
% ---------- exampleObjAndGrad.m ----------
% function [obj, grad] = exampleObjAndGrad(x)
% % ---------- compute objective ----------
% x1 = x(1);
% x2 = x(2);
% x3 = x(3);
% x4 = x(4);
%
% obj = 1/4*x4^4*x3 + 1/3*x3^3*x2 + 1/2*x2^2 + x1*x3^2 + 1;
%
%
% % ---------- compute gradient ----------
% grad(1) = x3^2;
% grad(2) = 1/3*x3^3 + x2;
% grad(3) = 1/4*x4^4 + x3^2*x2+x1*2*x3;
% grad(4) = x4^3*x3;
%
% return
%
% in another m-file runExample.m following lines:
% ---------- runExample.m ----------
% x0 = [-1 -1 -1 -1]; % initial point
% options.sStep = 1e-2;
% options.nStep = 100;
% options.gradStep = [];
% options.dim = [1,2,3,4];
%
% % show gradients=tangents or not
% options.ShowGrad = 1;
%
% % how the arrows of the gradients=tangents look
% options.arrowLength = 0.1;
% options.MaxHeadSize = 1e-10;
% options.Marker = 'o';
%
% plotGradAndObj('exampleObjAndGrad',x0,options,[]);
% ====================================================================================================
% ---------- PLOTTING PARAMETERS ----------
% optional fields
% see http://www.mathworks.de/help/matlab/ref/quiverseries-properties.html
if (~isfield(plotOptions,'arrowLength') || isempty(plotOptions.arrowLength))
arrowLength = 0.1;
else
arrowLength = plotOptions.arrowLength;
end
if (~isfield(plotOptions,'MaxHeadSize') || isempty(plotOptions.MaxHeadSize))
MaxHeadSize = 1e-10;
else
MaxHeadSize = plotOptions.MaxHeadSize;
end
if (~isfield(plotOptions,'Marker') || isempty(plotOptions.Marker))
Marker = 'o'; % 'none'
else
Marker = plotOptions.Marker;
end
if (~isfield(plotOptions,'sStep') || isempty(plotOptions.sStep))
sStep = 1e-2;
else
sStep = plotOptions.sStep;
end
if (~isfield(plotOptions,'nStep') || isempty(plotOptions.nStep))
nStep = 100;
else
nStep = plotOptions.nStep;
end
if (~isfield(plotOptions,'gradStep') || isempty(plotOptions.gradStep))
gradStep = 10;
else
gradStep = plotOptions.gradStep;
end
if (~isfield(plotOptions,'showGrad') || isempty(plotOptions.showGrad))
showGrad = 1;
else
showGrad = plotOptions.showGrad;
end
% required field
if (~isfield(plotOptions,'dim') || isempty(plotOptions.dim))
error('Please specify the dimensions you want to plot')
else
dim = plotOptions.dim;
end
if length(nStep) == 1
nStep = nStep * ones(length(X),1);
end
if length(sStep) == 1
sStep = sStep * ones(length(X),1);
end
%% compute the point-values for the objective and gradient function
Xorg = X;
for j = 1 : length(dim) % i-th component of vector X
funVec = zeros(2*nStep(j)+1,1); % function values at different points
gradMtx = zeros(2*nStep(j)+1,length(X)); % partial derivatives at different points
% fprintf('%i of %i \n', i, length(X));
i = dim(j);
X = Xorg;
iiStep = 0;
X(i) = X(i) - nStep(i)*sStep(i);
for iStep = -nStep(i): 1: nStep(i)
iiStep = iiStep + 1;
X(i) = X(i) + sStep(i);
if isempty(varargin{:})
[obj, grad] = feval(f,X);
else
[obj, grad] = feval(f,X,varargin{:});
end
funVec(iiStep) = obj;
gradMtx(iiStep,:) = grad;
end
%% plot the objective and gradient function
x = -sStep(i)*nStep(i) : sStep(i) : sStep(i)*nStep(i);
figure, plot(x,funVec,'LineWidth', 2), title(sprintf('Dimension=%i',i));
if showGrad
hold on;
xPos = 1 : gradStep : 2*nStep(i)+1;
xQ = x(xPos); yQ=funVec(xPos)'; vQ=gradMtx(xPos,i)'; uQ=ones(1,length(xPos));
h = quiver(xQ,yQ,uQ,vQ,arrowLength,'LineWidth',2, 'ShowArrowHead', 'on','MaxHeadSize', MaxHeadSize, 'Marker', Marker );
hold off;
end
drawnow;
end
return
% ----------------------------------------------------------------------------------------------------
% I guess following script is only needed in older matlab versions, where the 'MaxHeadSize' option was not available for quiver
% ====================================================================================================
function adjust_quiver_arrowhead_size(quivergroup_handle, scaling_factor)
% downloaded from http://www.mathworks.de/matlabcentral/fileexchange/34305-adjust--quiver--arrowhead-size
% downloaded at 06.Nov.2014 by rolfk
% accompanies the code plotGradAndObj.m
%
% Copyright (c) 2011, Kevin J. Delaney
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in
% the documentation and/or other materials provided with the distribution
% * Neither the name of the BMT Scientific Marine Services nor the names
% of its contributors may be used to endorse or promote products derived
% from this software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
% Make quiver arrowheads bigger or smaller.
%
% adjust_quiver_arrowhead_size(quivergroup_handle, scaling_factor)
%
% Example:
% h = quiver(1:100, 1:100, randn(100, 100), randn(100, 100));
% adjust_quiver_arrowhead_size(h, 1.5); % Makes all arrowheads 50% bigger.
%
% Inputs:
% quivergroup_handle Handle returned by "quiver" command.
% scaling_factor Factor by which to shrink/grow arrowheads.
%
% Output: none
% Kevin J. Delaney
% December 21, 2011
% BMT Scientific Marine Services (www.scimar.com)
if ~exist('quivergroup_handle', 'var')
help(mfilename);
return
end
if isempty(quivergroup_handle) || any(~ishandle(quivergroup_handle))
errordlg('Input "quivergroup_handle" is empty or contains invalid handles.', ...
mfilename);
return
end
if length(quivergroup_handle) > 1
errordlg('Expected "quivergroup_handle" to be a single handle.', mfilename);
return
end
if ~strcmpi(get(quivergroup_handle, 'Type'), 'hggroup')
errrodlg('Input "quivergroup_handle" is not of type "hggroup".', mfilename);
return
end
if ~exist('scaling_factor', 'var') || ...
isempty(scaling_factor) || ...
~isnumeric(scaling_factor)
errordlg('Input "scaling_factor" is missing, empty or non-numeric.', ...
mfilename);
return
end
if length(scaling_factor) > 1
errordlg('Expected "scaling_factor" to be a scalar.', mfilename);
return
end
if scaling_factor <= 0
errordlg('"Scaling_factor" should be > 0.', mfilename);
return
end
line_handles = get(quivergroup_handle, 'Children');
if isempty(line_handles) || (length(line_handles) < 3) || ...
~ishandle(line_handles(2)) || ~strcmpi(get(line_handles(2), 'Type'), 'line')
errordlg('Unable to adjust arrowheads.', mfilename);
return
end
arrowhead_line = line_handles(2);
XData = get(arrowhead_line, 'XData');
YData = get(arrowhead_line, 'YData');
if isempty(XData) || isempty(YData)
return
end
% Break up XData, YData into triplets separated by NaNs.
first_nan_index = find(~isnan(XData), 1, 'first');
last_nan_index = find(~isnan(XData), 1, 'last');
for index = first_nan_index : 4 : last_nan_index
these_indices = index + (0:2);
if these_indices(end) > length(XData)
break
end
x_triplet = XData(these_indices);
y_triplet = YData(these_indices);
if any(isnan(x_triplet)) || any(isnan(y_triplet))
continue
end
% First pair.
delta_x = diff(x_triplet(1:2));
delta_y = diff(y_triplet(1:2));
x_triplet(1) = x_triplet(2) - (delta_x * scaling_factor);
y_triplet(1) = y_triplet(2) - (delta_y * scaling_factor);
% Second pair.
delta_x = diff(x_triplet(2:3));
delta_y = diff(y_triplet(2:3));
x_triplet(3) = x_triplet(2) + (delta_x * scaling_factor);
y_triplet(3) = y_triplet(2) + (delta_y * scaling_factor);
XData(these_indices) = x_triplet;
YData(these_indices) = y_triplet;
end
set(arrowhead_line, 'XData', XData, 'YData', YData);
return
% ----------------------------------------------------------------------------------------------------
Add new comment