function [x, fval] = fminbndGlobal(~, fcn, LB, UB, NumCandidates, BestK, ...
    Verbose, MaxIter, TolFun)
% Attempts to find the global minimum of fcn() within bounds given by the
% vectors LB and UB. Generates NumCandidates random points within bounds,
% runs fcn() on all of them and selects the BestK best. Then runs a bounded
% fminsearch on each of the K points and returns the best overall. The
% flags Verbose, MaxIter, and TolFun are options passed directly to
% fminsearch.
%

%   Copyright 2016-2022 The MathWorks, Inc.


% Choose the best K points from N random candidates
x0 = iMinKPointsFromN(fcn, LB, UB, NumCandidates, BestK);
% Do a local optimization on each of the K points
if Verbose
    VerboseOpts = {'Display', 'iter'};
else
    VerboseOpts = {'Display', 'off'};
end

[xs, fvals] = bayesoptim.fminsearch(@boundedFcn, x0, ...
    optimset(...
    'MaxIter', MaxIter, ...
    'TolX', Inf, ...
    'TolFun', TolFun, ...
    VerboseOpts{:}));

[fval, loc] = min(fvals);
x = xs(loc,:);

    function y = boundedFcn(x)
        y = inf(size(x,1),1);
        idx = ~any(x < LB | x > UB , 2);
        if any(idx)
            y(idx) = fcn(x(idx,:));
        end
    end
end

function [X, fvals] = iMinKPointsFromN(fcn, LB, UB, N, K)
% Generate a random set of N points within bounds. Return the K points that
% best minimize fcn.
X = bayesoptim.uniformPointsWithinBounds(N, LB, UB);
fvals = fcn(X);
[~, rows] = sort(fvals, 'ascend');
X = X(rows(1:K), :);
fvals = fvals(rows(1:K));
end
