function [opt_params,Z,opt_X,details] = paramsearch(model_data,search_params)

% PARAMSEARCH -- Parameter search over one data set.
%
% opt_params = paramsearch(model_data,search_params)
% [opt_params,Z,opt_X,details] = paramsearch(...)
%
% SEARCH_PARAMS must be a structure with the following eight fields:
%  * lsfun_name -- M-file implementing the loss function. This field
%       can be omitted, in which case it defaults to 'logL'.
%  * funfun_name -- The name of the optimization function.
%       Should be either 'fmincon' (=default) or 'lsqnonlin'
%  * model_name -- M-file implementing the model (e.g. 'anchor2mt')
%  * params -- structure containing the parameters controlling the model.
%       The purpose of PARAMSEARCH1 is to find optimal values for (some of)
%       these parameters. PARAMS is the starting point for this search.
%  * p2v_templ -- params-to-vector conversion template, see PARAMS2VECTOR
%  * v2p_templ -- vector-to-params conversion template, see VECTOR2PARAMS
%  * bounds -- a matrix with two rows, cf. LB and UB arguments to FMINCON
%  * clip_prob -- minimal probability per trial, passed to LOGL.
%       (This can be omitted when a loss fun other than LOGL is used;
%        any other lsfun-specific field(s) can be supplied in its place.)
%  * target_vals -- a matrix of the values the model is trying to fit.
%       (This can be omitted when a loss fun other than CURVEDIFF is used.)
%  * optns -- optimization options; based on OPTIMSET(funfun_name)
%
% MODEL_DATA is an arbitrary structure passed to the user-defined model.
% Typically it contains fields such as 'stim', 'resp', 'fdbk', etc.
%
% OPT_PARAMS and OPT_X are the optimal parameters as determined by the
% search (via FMINCON), in param-struct and vector formats, respectively.
% Z is the log-likelihood value L at the optimum for LOGL loss function.
% Z is the summed squared error ResNorm at the optimum for CURVEDIFF loss.
% DETAILS is a structure with four fields:]
%  1. exitflag -- >0 on convergence; 0 on max.iter; <0 on no convergence
%  2. N_evals -- number of evaluations of the user-defined function
%  3. N_iters -- number of optimization iterations (see FMINCON's OUTPUT)
%  4. time -- total time spent on optimization (in seconds).
%
% Example of a full optimization sequence for a given <stim,resp> data set.
% ANCHOR2MT, ANCHOR_TEMPLATE, and ANCHOR_PARAMS are the custom-made parts.
%   params0 = anchor_params ;     % use default params as initial approx.
%   search_params = struct('model_name','anchor2mt','params',params0) ;
%   flags = [0 1 1 1 1 0 0] ; [p2v_templ,v2p_templ,bnd] = anchor_template ;
%   search_params.p2v_templ = p2v_templ ;
%   search_params.v2p_templ = v2p_templ ;
%   search_params.bounds = bnd ;
%   search_params.clip_prob = exp(-4) ;
%   search_params.optns = optimset('fmincon') ;     % MaxIter, TolX, etc.
%   model_data = struct('stim',stim,'resp',resp) ;
%   [opt_params,L] = paramsearch(model_data,search_params) ;
%
% WARNING: The strategy of passing the model data as the last argument
% to FMINCON (or LSQNONLIN) is no longer mentioned in the documentation
% of the Optimization Toolbox (ver. 3), although it is still supported
% by the code.  The documentation recommends an alternative method using
% function handles to anonymous functions or nested functions.
%
% See also BIG_PSEARCH, LOGL, FMINCON, CURVEDIFF, LSQNONLIN, PARAMS2VECTOR.

% Original coding by Alex Petrov, Ohio State University
% $Revision: 1.2 $  $Date: 2007-08-15 $
%
% Part of the utils toolbox version 1.2 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

starting_time = clock ;

if (~isfield(search_params,'lsfun_name'))
   lsfun_name = 'logL' ;   % LOGL is the default loss function
else
   lsfun_name = search_params.lsfun_name ;
end
if (~isfield(search_params,'funfun_name'))
   funfun_name = 'fmincon' ;   % FMINCON is the default function function
else
   funfun_name = search_params.funfun_name ;
end
lsfun_data = search_params ;
lsfun_data.model_data = model_data ;
params0 = search_params.params ;     % parameters of the user-defined model
optns = search_params.optns ;
lb = search_params.bounds(1,:) ;
ub = search_params.bounds(2,:) ;

x0 = params2vector(params0,search_params.p2v_templ) ;    % initial approx.
switch lower(funfun_name)
case 'fmincon'
   [opt_X,Z,exitflag,output] = fmincon(lsfun_name,x0,[],[],[],[],...
                                       lb,ub,[],optns,lsfun_data) ;
case 'lsqnonlin'
   [opt_X,Z,res,exitflag,output] = lsqnonlin(lsfun_name,x0,...
                                             lb,ub,optns,lsfun_data) ;
otherwise
    error('Unsupported optimization function. Use FMINCON or LSQNONLIN.') ;
end 
opt_params = vector2params(opt_X,params0,search_params.v2p_templ) ;

details.exitflag = exitflag ;
details.N_evals = output.funcCount ;
details.N_iters = output.iterations ;
details.time = etime(clock,starting_time) ;

%--- Return OPT_P, Z, OPT_X, DETAILS
%%% End of file PARAMSEARCH.M
