%usage: [params, fval, fval_all] = fgridsearch(fh, varargin)
%
%This function looks for the minimum of the function fh by testing all
%parameter combinations possible by combining the parameter ranges specified in
%varargin. For finding parameters which _maximise_ a function f(x) one needs to
%minimise -f(x). Please have a look in the example section for further details.
%
%Note that each argument besides the function handle must be a numeric vector.
%
%EXAMPLE I - Minimise a simple function
%  %Here nine tests are performed and the minimum 0 at locatvon [0 0] is found.
%  fh = @(x) x(1)^2 + x(2)^2;
%  [params, fval] = fgridsearch(fh , [-1, 0, 1], [-1, 0, 1])
%
%EXAMPLE II - Maximise the function of expample I.
%  %Here nine tests are performed and the minimum -2 at location [-1 -1] is
%  %found. Since we inverted the function, this corresponds to a maximimum of
%  %value 2.
%  fh = @(x) -(x(1)^2 + x(2)^2);
%  [params, fval] = fgridsearch(fh , [-1, 0, 1], [-1, 0, 1])
%
%EXAMPLE III - Use fgridsearch for a complicated task.
%  %Here fgridsearch is used to explore several starting points for a
%  %fminsearch optimisation. That way the global minimum is found.
%  fh = @(x) -10*exp(-(sumsq(x - [-100 -100]))) - 3*exp(-sumsq(x));
%  gh = @(x) nthargout(2, @fminsearch, fh, x); %nthargout is available only
%                                              %under octave
%  range = linspace(-200, 200, 10);
%  [params, fval] = fgridsearch(gh, range, range);
%
%
%LICENSE INFORMATION:
%Copyright (C) 2016 Max Görner, max.goerner@tu-dresden.de
%
%This program is free software: you can redistribute it and/or modify it under
%the terms of the GNU General Public License as published by the Free Software
%Foundation, either version 3 of the License, or (at your option) any later
%version.
%
%This program is distributed in the hope that it will be useful, but WITHOUT ANY
%WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
%PARTICULAR PURPOSE. See the GNU General Public License for more details.
%
%You should have received a copy of the GNU General Public License along with
%this program. If not, see <http://www.gnu.org/licenses/>.

function [params, fval, fval_all] = fgridsearch(fh, varargin)
  with_last = double(isstruct(varargin{end})); %if last argument is struct,
                                               %then we don't include it into
                                               %our parameter combinations
  varargin_end = numel(varargin) - with_last;

  %Create array of parameter combinations
  ndgrid_ret_cell = cell(1, varargin_end);
  [ndgrid_ret_cell{:}] = ndgrid(varargin{1:varargin_end});
  param_combs_cell = cellfun(@(c) c(:), ndgrid_ret_cell, 'UniformOutput', false);
  param_combs = [param_combs_cell{:}];

  n_combinations = size(param_combs, 1);
  %nargin without first and maybe without the last argument and with the
  %function value
  fval_all = zeros(n_combinations, nargin - 1 - with_last + 1);
  for pcomb = 1:n_combinations
    cur_comb = param_combs(pcomb, :);
    ret = fh(cur_comb);
    fval_all(pcomb, :) = [ret cur_comb];
  end%for

  [fval, min_ind] = min(fval_all(:, 1), [], 1); %find minimum along the rows
  params = fval_all(min_ind, 2:end);
end%function
