function results = big_psearch(model_data,search_params,prev_results,x0,sbj_list)

% BIG_PSEARCH -- Call PARAMSEARCH over many data sets and packs the results.
%
% results = big_psearch(model_data,search_params)
% results = big_psearch(model_data,search_params,prev_results)
% results = big_psearch(model_data,search_params,prev_results,x0)
% results = big_psearch(model_data,search_params,prev_results,x0,sbj_list)
%
% MODEL_DATA is a structure array having one struct per "subject".
% Individual subject data are passed to PARAMSEARCH, which in turn passes
% them to the user-defined model indicated in SEARCH_PARAMS.model_name.
%
% SEARCH_PARAMS is the same structure as in PARAMSEARCH (that is, with
% fields 'model_name', 'params', 'p2v_templ', 'v2p_templ', 'bounds,
% 'clip_prob', and 'optns'). In addition it may have the following fields
% to control the behavior of BIG_PSEARCH:
%  8. deadline -- time after which no new searches must be initiated.
%        The time must be in DATENUM format (see functions NOW and DATENUM).
%        If this field is missing or set to +Inf, no deadline is imposed.
%  9. fname -- file name for saving the (partial) RESULTS structure upon
%        completion of each "subject". The empty string means "Don't save."
%        If this field is missing, it defaults to ['psearch-' date].
%        That is, BIG_PSEARCH saves the intermediate results by default
%        using fnames such as 'psearch-04-Jun-2001.mat' in the current dir.
% 10. verbose -- when 1 (the default), print progress messages on the screen.
%
% The function produces a structure RESULTS with the following fields:
%  1. model_name -- as taken from SEARCH_PARAMS.model_name
%  2. descriptor -- a string, e.g. '04-Jun-2001 18:55:25' (see DATESTR(NOW))
%  3. search_params -- a copy of the input argument
%  4. opt_X -- [Nxp] matrix, where N is the number of "subjects" (that is,
%        the size of the MODEL_DATA array) and p is the number of search
%        dimensions (as specified in SEARCH_PARAMS.pv2_templ, etc.).
%  5. L -- [Nx1] vector of L values at the optimum (see LOGL, PARAMSEARCH)
%  6. exitflag -- [Nx1] vector of exit flags (see FMINCON, PARAMSEARCH)
%  7. N_evals -- number of function evaluations per subject, [Nx1]
%  8. N_iters -- number of optimization iterations per subject, [Nx1]
%  9. time -- seconds taken by each subject, [Nx1] (see PARAMSEARCH)
%[10. x0 or params0 -- starting point for the search, if supplied.]
%
% When the search has been interrupted (by the user or by deadline), it
% can later be resumed on the basis of the partially completed structure
% saved on the disk.  To that end, pass this structure as PREV_RESULTS.
% BIG_PSEARCH will find the subjects with zero entries in the TIME column,
% and call PARAMSEARCH on their respective MODEL_DATA sets. Alternatively,
% an explicit list of subject indices can be specified in SBJ_LIST.
%
% X0 can be used to specify the initial seed for each search. [ver.1.1]
% Normally, SEARCH_PARAMS.PARAMS is used for this purpose, the same for
% all searches. This can be overridden by passing an explicit X0 instead 
% of the default []. X0 can be one of the following:
%  - a [1xp] row vector, used as the starting point of all searches
%  - a [Nxp] matrix, individualized starting point for each search
%  - a single structure PARAMS0, used in all searches
%  - a structure array PARAMS0 with N elements, individualized.
%
% Example: Set BIG_PSEARCH to work until 9:00 a.m. on June 5, 2001:
%   for k=1:N  model_data(k).stim=...; model_data(k).resp=...; end
%   search_params = ... ;    % see PARAMSEARCH and ANCHOR_SEARCH_PARAMS
%   search_params.deadline = datenum(2001,6,5,9,00,00) ;
%   search_params.fname = 'my_results' ;
%   results = big_psearch(model_data,search_params) ;
% **** Suppose it is interrupted due to power blackout during the night...
% Resume on the next day:
%   prev_results = load('my_results') ;
%   results = big_psearch(model_data,search_params,prev_results) ;
%
% See also PARAMSEARCH, MCARLO_PSEARCH, LOGL, VECTOR2PARAMS.

% Original coding by Alex Petrov, Ohio State University
% $Revision: 1.1 $  $Date: 2003/12/29 18:32 $
%
% Part of the utils toolbox version 1.1 for MATLAB version 5 and up.
% http://alexpetrov.com/softw/utils/
% Copyright (c) Alexander Petrov 1999-2006, http://alexpetrov.com
% Please read the LICENSE and NO WARRANTY statement in ../utils_license.m

%--  Handle the input arguments
N = length(model_data) ;     % number of subjects

if (nargin < 3)              % PREV_RESULTS not supplied
  prev_results = [] ;        % build up from scratch
end
if (isempty(prev_results))
  results.model_name = search_params.model_name ;
  results.descriptor = datestr(now) ;
  results.search_params = search_params ;
  results.opt_X = zeros(N,length(search_params.p2v_templ)) ;
  results.L = zeros(N,1) ;
  results.exitflag = zeros(N,1) ;
  results.N_evals = zeros(N,1) ;
  results.N_iters = zeros(N,1) ;
  results.time = zeros(N,1) ;
else
  results = prev_results ;
  results.search_params = search_params ;   % in case they've changed
end

if (nargin < 4)  x0 = [] ; end        % initial seed not supplied
if (isempty(x0))
  params0_flag = 0 ;          % use SEARCH_PARAMS.PARAMS by default
elseif (isnumeric(x0))        % explicit vector or matrix
   % Convert X0 to PARAMS0
   v2p = search_params.v2p_templ ;
   N1 = size(x0,1) ;
   for k = 1:N1
      params0(k) = vector2params(x0(k,:),search_params.params,v2p) ;
   end
   if (N1==1)                 % one for all searches
    search_params.params = params0 ;    % override
    params0_flag = 0 ;                  % one for all
  elseif (N1>=N)
    params0_flag = 1 ;        % individualized starting points
  else
    warning('X0 too short. Padding with SEARCH_PARAMS.PARAMS.') ;
    params0(N1+1:N) = search_params.params ;
    params0_flag = 1 ;
  end
  results.x0 = x0 ;
elseif (isstruct(x0))         % PARAMS0 supplied explicitly
   params0 = x0 ;
   N1 = length(params0) ;
  if (N1==1)                  % one for all searches?
    search_params.params = params0 ;    % override
    params0_flag = 0 ;                  % one for all
  elseif (N1>=N)
    params0_flag = 1 ;        % individualized starting points
  else
    warning('PARAMS0 too short. Padding with SEARCH_PARAMS.PARAMS.') ;
    params0(N1+1:N) = search_params.params ;
    % Note that this will fail if search_params.params and params0 
    % have incongruent fields. The BIG_PSEARCH is stalled in this case.
    params0_flag = 1 ;
  end
  results.params0 = params0 ;
else
  warning('Illegal X0 format. Using SEARCH_PARAMS.PARAMS instead.') ;
  params0_flag = 0 ;
end

if (nargin < 5)          % SBJ_LIST not supplied
  sbj_list = find(results.time == 0) ;
end
sbj_list = reshape(sbj_list,1,length(sbj_list)) ;   % FOR requires a row vec

if (isfield(search_params,'deadline'))
  deadline = search_params.deadline ;
else
  deadline = +Inf ;      % no deadline, default
end
if (isfield(search_params,'fname'))
  fname = search_params.fname ;
else
  fname = ['psearch-' date] ;   % e.g. 'psearch-04-Jun-2001'
end
if (isfield(search_params,'verbose'))
  verbose = search_params.verbose ;
else
  verbose = 1 ;
end

%--  Main loop
for k = sbj_list
  if (now > deadline)
    if verbose  
      fprintf('%s: Time out, cannot proceed with subject %d.\n\n',...
              datestr(now),k) ;
    end
    break
  else
    %- check for individualized starting points
    if (params0_flag)
      search_params.params = params0(k) ;
    end
    %- do the real work: call PARAMSEARCH for this subject
    [p,L,X,details] = paramsearch(model_data(k),search_params) ;
    %- record the results for this subject
    results.descriptor = datestr(now) ;     % update the time stamp
    results.opt_X(k,:) = X ;  % do not save P, as X contains the same info
    results.L(k)       = L ;
    results.exitflag(k)= details.exitflag ;
    results.N_evals(k) = details.N_evals ;
    results.N_iters(k) = details.N_iters ;
    results.time(k)    = details.time ;
    %- save partial RESULTS to disk and report to the console
    if (isempty(fname))        % saving option switched off?
      if verbose
        fprintf('%s: Done sbj %2d, L=%g\n',results.descriptor,k,L);
      end
    else
      save(fname,'results') ;  % save partial RESULTS to disk
      if verbose
        fprintf('%s: Done sbj %2d, L=%g, saved to %s.mat\n',...
                results.descriptor,k,L,fname);
      end
    end %if isempty(fname)
  end %if (now > deadline)
end % for k = sbj_list

%--- Return RESULTS
%%% End of file BIG_PSEARCH.M
