function [xopt,fval,exitflag,output] = spfminsearch(z, xbox, options)

%*****************************************************************************80
%
%% spfminsearch() Optimization of sparse grids using FMINSEARCH.  
%
%    X = SPFMINSEARCH(Z) starts search at the best available
%    sparse grid point and attempts to find a local optimizer of the
%    sparse grid interpolant Z. The entire range of the sparse
%    grid interpolant is searched.
%
%    X = SPFMINSEARCH(Z,XBOX) attempts to find a local optimizer.
%    in the search box XBOX. The size of search box XBOX must be
%    smaller or equal to the range of the interpolant.
%
%    X = SPFMINSEARCH(Z,XBOX,OPTIONS)  optimizes with the default
%    optimization parameters replaced by values in the structure
%    OPTIONS, created with the SPOPTIMSET function.  See SPOPTIMSET
%    for details.
%
%    [X,FVAL] = SPFMINSEARCH(...)  returns the value of the 
%    objective function, described in FUN, at X.
%
%    [X,FVAL,EXITFLAG] = SPFMINSEARCH(...)  returns an EXITFLAG 
%    that describes the exit condition of SPFMINSEARCH. Possible
%    values of EXITFLAG and the corresponding exit conditions are
%
%     1  SPFMINSEARCH converged to a solution X.
%     0  Maximum number of function evaluations or iterations
%        reached.
%
%    [X,FVAL,EXITFLAG,OUTPUT] = SPFMINSEARCH(...) returns a 
%    structure OUTPUT with the number of function evaluations in 
%    OUTPUT.nFevals, and the computing time in OUTPUT.time.
%    The OUTPUT result from the FMINSEARCH call are returned
%    as OUTPUT.fminsearchOutput. 
%
%    Example: (minimizing the three-hump camel-back function)
%      f = inline('12*x.^2-6.3*x.^4+x.^6+6*y*(y-x)');
%      range = [-3 3; -3 3];
%      options = spset('keepFunctionValues','on', ...
%                      'GridType', 'Chebyshev', ...
%                      'DimensionAdaptive', 'on', ...
%                      'DimAdaptDegree', 1, ...
%                      'MinPoints', 10);
%      z = spvals(f, 2, range, options)
%      [xopt, fval] = spfminsearch(z)
%
%    See also SPOPTIMSET, FMINSEARCH.

% Author : Andreas Klimke
% Version: 1.1
% Date   : June 8, 2005

% Change log:
% V1.1   : September 8, 2006
%          Added example, added exitflag, added output from
%          fminsearch, removed obsolete code segments.
% V1.0   : June 8, 2005
%          Initial version.
	
% ------------------------------------------------------------
% Sparse Grid Interpolation Toolbox
% Copyright (c) 2006 W. Andreas Klimke, Universitaet Stuttgart 
% Copyright (c) 2007-2008 W. A. Klimke. All Rights Reserved.
% See LICENSE.txt for license. 
% email: klimkeas@ians.uni-stuttgart.de
% web  : http://www.ians.uni-stuttgart.de/spinterp
% ------------------------------------------------------------

t0 = clock;

if nargin < 2, xbox = []; end;
if nargin < 3, options = []; end;

d = z.d;

% In case that no range has been provided to spvals -> set it to
% [0,1]^d. 
if isempty(z.range)
	z.range = [zeros(d,1) ones(d,1)];
end
if isempty(xbox)
	xbox = z.range;
end

% Add the Optimset options to the options structure if there are
% any.
fnames = [];
optimOptions = spoptimget(options, 'OptimsetOptions', []);
if ~isempty(optimOptions) 
	fnames = fieldnames(optimOptions);
end
for k = 1:length(fnames)
	% We only add the field if it is not there yet. This means that
	% The fields from spoptimsget have precedence.
	if ~isfield(options, fnames{k})
		options = setfield(options, fnames{k}, ...
		  getfield(optimOptions, fnames{k}));
	end
end

% Determine optimization start point(s)
[xopt, fval] = spgetstartpoint(z, xbox, options);
minimize = spoptimget(options, 'Minimize', 'on');
maximize = spoptimget(options, 'Maximize', 'off');

if strcmpi(minimize, 'on'), isminimize = 1; else isminimize = 0; end
if strcmpi(maximize, 'on'), ismaximize = 1; else ismaximize = 0; end

xmin = []; xmax = [];
ymin = []; ymax = [];
exitflagmin = []; exitflagmax = [];
outputmin = []; outputmax = [];

nfevals = 0;
if isminimize
	[xmin,ymin,exitflagmin,outputmin] = ...
			fminsearch(@fun, xopt(:,1), options, z, xbox, 0);
	nfevals = outputmin.funcCount;
end
if ismaximize
	k = 1; if isminimize, k = 2; end
	[xmax,ymax,exitflagmax,outputmax] = ...
			fminsearch(@fun, xopt(:,k), options, z, xbox, 1);
	nfevals = nfevals + outputmax.funcCount;
end

xopt = [xmin, xmax];
fval = [ymin, -ymax];
exitflag = [exitflagmin, exitflagmax];

if nargout == 4
	output.nFEvals = nfevals;
	output.time = etime(clock, t0);
  output.fminsearchOutput = [outputmin, outputmax];
end

% -----------------------------------------------------------------
function f = fun(x, z, xbox, mode)

% MODE = 0: minimize; MODE = 1: maximize;
if isfield(z, 'selectOutput')
	output = z.selectOutput;
else
	output = 1;
end

xtemp = num2cell(min(max(x,xbox(:,1)),xbox(:,2))');
if mode == 0
	f = spinterp(z, xtemp{:});
else
	f = -spinterp(z, xtemp{:});
end

% For values outside of the search box, penalize the
% result. 
d = z.d;
fevalWidth = z.fevalRange(output,2) - z.fevalRange(output,1);
for l = 1:d
	if x(l) < xbox(l,1)
		f = f + (xbox(l,1) - x(l)).^2 * fevalWidth;
	elseif x(l) > xbox(l,2)
		f = f + (x(l) - xbox(l,2)).^2 * fevalWidth;
	end
end
