Code covered by the BSD License  

Highlights from
optimize

image thumbnail
from optimize by Rody Oldenhuis
optimize (non)linear (in)equality constrained functions with FMINSEARCH

...
function [sol, fval, exitflag, output] = ...
        optimize(funfcn, x0, lb, ub, A, b, Aeq, beq, nonlcon, ...
                 strictness, options, algorithm, varargin)
%OPTIMIZE        Optimize general constrained problems using Nelder-Mead algorithm
%
% Usage:
% sol = OPTIMIZE(func, x0) 
% sol = OPTIMIZE(..., x0, lb, ub)
% sol = OPTIMIZE(..., ub, A, b) 
% sol = OPTIMIZE(..., b, Aeq, beq) 
% sol = OPTIMIZE(..., beq, nonlcon) 
% sol = OPTIMIZE(..., nonlcon, strictness) 
% sol = OPTIMIZE(..., strictness, options) 
% sol = OPTIMIZE(..., options, algorithm) 
%
% [sol, fval] = OPTIMIZE(func, ...)
% [sol, fval, exitflag] = OPTIMIZE(func, ...)
% [sol, fval, exitflag, output] = OPTIMIZE(func, ...)
%
% INPUT ARGUMENTS:
%
%  fun, x0, options, varargin - see the help for FMINSEARCH.
%
%  lb - (OPTIONAL) lower bound vector or array, must have the same 
%       size as x0.
%
%       If no lower bounds exist for one of the variables, then
%       supply -inf for that variable.
%
%       If no lower bounds exist at all, then [lb] may be left empty.
%
%       Variables may be fixed in value by setting the corresponding
%       lower and upper bounds to exactly the same value.
%
%  ub - (OPTIONAL) upper bound vector or array, must have the same 
%       size as x0.
%
%       If no upper bounds exist for one of the variables, then
%       supply +inf for that variable.
%
%       If no upper bounds at all, then [ub] may be left empty.
%
%       Variables may be fixed in value by setting the corresponding
%       lower and upper bounds to exactly the same value.
%
%  A, b - (OPTIONAL) Linear inequality constraint array and right
%       hand side vector. (Note: these constraints were chosen to
%       be consistent with those of fmincon.)
%
%       This linear constraint forces the solution vector [x] to 
%       satisfy 
%                               A*x <= b
%
%       Note that in case [x] is a matrix (this is true when [x0] is
%       a matrix), the argument [b] must have corresponding size 
%       [size(A,1) x size(x0,2)], since the same equation is used to 
%       evaluate this constraint. 
%
%  Aeq, beq - (OPTIONAL) Linear equality constraint array and right
%       hand side vector. (Note: these constraints were chosen to
%       be consistent with those of fmincon.)
%
%       This linear constraint forces the solution vector [x] to 
%       satisfy 
%
%                               Aeq*x == beq
%
%       Note that in case [x] is a matrix (this is true when [x0] is
%       a matrix), the argument [beq] must have corresponding size 
%       [size(Aeq,1) x size(x0,2)], since the same equation is used to 
%       evaluate this constraint.
%
%  nonlcon - (OPTIONAL) function handle to general nonlinear constraints,
%       inequality and/or equality constraints.
%
%       [nonlcon] must return two vectors, [c] and [ceq], containing the
%       values for the nonlinear inequality constraints [c] and
%       those for the nonlinear equality constraints [ceq] at [x]. (Note: 
%       these constraints were chosen to be consistent with those of 
%       fmincon.)
%
%       These constraints force the solution to satisfy
%
%               ceq(x)  = 0
%                 c(x) <= 0,
%
%       where [c(x)] and [ceq(x)] are general non-linear functions of [x].
%
% strictness - (OPTIONAL) By default, OPTIMIZE will assume the objective 
%       (and constraint) function(s) can be evaluated at ANY point in 
%       RN-space; the initial estimate does not have to lie in the 
%       feasible region, and intermediate solutions are also allowed to step 
%       outside this area. If your function does not permit such behavior, 
%       set this argument to 'strict'. With 'strict' enabled, the linear 
%       constraints will be satisfied strictly, while the nonlinear 
%       constraints will be satisfied within options.TolCon. 
%
%       If this is also not permissible, use 'superstrict' - then all 
%       nonlinear constraints are also satisfied AT ALL TIMES, and the 
%       objective function is NEVER evaluated outside the feasible area. 
%
%       When using 'strict' or 'superstrict', the initial estimate [x0]
%       MUST be feasible. If it is not feasible, an error is produced
%       before the objective function is ever evaluated. 
%
% algorithm - (OPTIONAL) By default, an embedded version of the 
%       Nelder-Mead algorithm is used. This version is slightly more 
%       robust and internally effecient than the one implemented in 
%       FMINSEARCH. The FMINSEARCH algorithm can still be selected, by
%       setting [algorithm] to 'fminsearch'. 
%
%
% OUTPUT ARGUMENTS:
%
% sol, fval - the solution vector and the corresponding function value,
%       respectively. 
%
% exitflag - (See also the help on FMINSEARCH) A flag that specifies the 
%       reason the algorithm terminated. FMINSEARCH uses only the values
%
%           1    fminsearch converged to a solution x
%           0    Max. # of function evaluations or iterations exceeded
%          -1    Algorithm was terminated by the output function.
%
%       Since OPTIMIZE handles constrained problems, the following two
%       values were added:
%
%           2    All elements in [lb] and [ub] were equal - nothing done
%          -2    Problem is infeasible after the optimization (Some or 
%                any of the constraints are violated at the final 
%                solution).
%          -3    INF or NAN encountered during the optimization. 
%
% output - (See also the help on FMINSEARCH) A structure that contains
%       additional details on the optimization. FMINSEARCH returns
%
%           output.algorithm   Algorithm used
%           output.funcCount   Number of function evaluations 
%           output.iterations  Number of iterations 
%           output.message     Exit message
%
%       Since OPTIMIZE handles constrained problems, the following 
%       fields were added:
%
%           output.constrviolation.lin_ineq
%           output.constrviolation.lin_eq
%           output.constrviolation.nonlin_ineq
%           output.constrviolation.nonlin_ineq
%
%       All these fields contain a [M x 2]-cell array. The fist column
%       contains a logical index to the constraints, which is true if the
%       constraint was violated, false if it was satisfied. The second
%       column contains the amount of constraint violation. This amount is
%       equal to zero if the constraint was satisfied within
%       options.TolCon.
%
%
% Notes:
%
%  If options is supplied, then TolX will apply to the transformed
%  variables. All other FMINSEARCH parameters should be unaffected.
%
%  Variables which are constrained by both a lower and an upper
%  bound will use a sin() transformation. Those constrained by
%  only a lower or an upper bound will use a quadratic
%  transformation, and unconstrained variables will be left alone.
%
%  Variables may be fixed by setting their respective bounds equal.
%  In this case, the problem will be reduced in size for FMINSEARCH. 
%
%  If your problem has an EXCLUSIVE (strict) bound constraints which 
%  will not permit evaluation at the bound itself, then you must 
%  provide a slightly offset bound. An example of this is a function 
%  which contains the log of one of its parameters. If you constrain 
%  the variable to have a lower bound of zero, then OPTIMIZE may
%  try to evaluate the function exactly at zero.
%
% EXAMPLES:
%
% rosen = @(x) (1-x(1)).^2 + 105*(x(2)-x(1).^2).^2;
%
% <<Fully unconstrained problem>>
%
% optimize(rosen, [3 3])
% ans =
%    1.0000    1.0000
%
%
% <<lower bound constrained>>
%
% optimize(rosen,[3 3],[2 2],[])
% ans =
%    2.0000    4.0000
%
%
% <<x(2) fixed at 3>>
%
% optimize(rosen,[3 3],[-inf 3],[inf,3])
% ans =
%    1.7314    3.0000
%
%
% <<simple linear inequality: x(1) + x(2) <= 1>>
%
% optimize(rosen,[0 0],[],[],[1 1], 1)
% 
% ans =
%    0.6187    0.3813
% 
%
% <<nonlinear inequality: sqrt(x(1)^2 + x(2)^2) <= 1>>
% <<nonlinear equality  : x(1)^2 + x(2)^3 = 0.5>>
%
% execute this m-file:
%
%   function test_optimize
%        rosen = @(x) (1-x(1)).^2 + 105*(x(2)-x(1).^2).^2;
%
%        options = optimset('TolFun', 1e-8, 'TolX', 1e-8);
%
%        optimize(rosen, [3 3], [],[],[],[],[],[],...
%        @nonlcon, [], options)
%
%   end
%   function [c, ceq] = nonlcon(x)
%        c = norm(x) - 1;
%        ceq = x(1)^2 + x(2)^3 - 0.5;
%   end
%
% ans =
%    0.6513    0.4233
%
%
%
% Of course, any combination of the above constraints is
% also possible.
%
%
% See also: fminsearch, fminsearchcon, fminsearchbnd, fmincon.


% Current version: v3

% Author info
%{
 Author     : Rody P.S. Oldenhuis
   Delft University of Technology
   E-mail   : oldenhuis@dds.nl

 FMINSEARCHBND, FMINSEARCHCON and most of the
 help for OPTIMIZE witten by
            : John D'Errico
   E-mail   : woodchips@rochester.rr.com

 Last edited: 05/August/2009
 Uploaded   : 05/August/2009

 [ Please report bugs to oldenhuis@dds.nl ]
%}

%CHANGELOG
%{
 CHANGELOG

 Aug 6, 2009
 v3: - removed dependency on the OPTIMSET() from the 
       optimization toolbox.
     - Included basic global optimization routine. If
       [x0] is omitted, but [ub] and [lb] are given, 
       a number of initial values will be optimized, 
       which are randomly generated in the boundaries 
       [lb] and [ub].
     - added one exitflag (-3). This is the exitflag
       for when the globalized algorithm has found 
       nothing else than INF or NAN function values.

 Aug 5, 2009
 v2: - Included NelderMead algorithm, a slightly more
       robust and internally efficient method than 
       FMINSEARCH. Of course, FMINSEARCH can still be
       selected through yet another argument...
     - one-dimensional functions could not be 
       optimized because of a bad reference to 
       elements of [lb] and [ub]; problem fixed. 
     - I forgot that [strictness] and [options] can 
       also be empty; included an ISEMPTY() check. 
     - fixed a problem with the outputFcn; this 
       function was still evaluated under the 
       assumption that [x0] is always a vector. 
       Inserted a reshaping operation. 

 Aug 1, 2009
 v1: - x0 can now be a matrix, just as in FMINSEARCH
     - minor bug fixed with the strictness setting: 
       with multiple nonlinear constraints, an ANY()
       was necessary, which was not used. 
       
 May 24, 2009.
 v0: - first release
%}
    
    %% initialize

    % process input
    narg = nargin;
    error(nargchk(2, inf, narg));    
    if (narg < 12)||isempty(algorithm ), algorithm = 'fminsearch'; end   
    if (narg < 11)||isempty(options   ), options = optimset; end   
    if (narg < 10)||isempty(strictness), strictness = 'loose'; end   
    if (narg <  9), nonlcon = []; end        
    if (narg <  8), beq = []; end
    if (narg <  7), Aeq = []; end
    if (narg <  6), b   = []; end
    if (narg <  5), A   = []; end
    if (narg <  4), lb  = []; end
    if (narg <  3), ub  = []; end     
    
    % no x0 given means optimize globally.
    if isempty(x0)
        [sol, fval, exitflag, output] = optimize_globally(varargin{:}); 
        return
    end
    
    % get tolerance on constraints
    try
        % part of optimization toolbox
        tolCon = optimget(options, 'TolCon', 1e-6);
    catch %#ok
        tolCon = 1e-6;
    end
    
    % check for an output function. If there is any, 
    % use wrapper function to call it with untransformed variable
    if ~isempty(options.OutputFcn)
        OutputFcn = options.OutputFcn;
        options.OutputFcn = @OutputFcn_wrapper;
    end
    
    % define number of dimensions
    N = numel(x0); 
    
    % adjust bounds when they are empty
    if isempty(lb), lb = -inf(size(x0)); end
    if isempty(ub), ub = +inf(size(x0)); end
       
    % check the user-provided input with nested function check_input
    check_input;
        
    % resize and reshape all input to be of completely predictable size
    new_x = x0;      ub = ub(:);
    x0 = x0(:);      lb = lb(:);                
    
    % replicate lb or ub when they are scalars, and x0 is not
    if isscalar(lb) && (N ~= 1), lb = repmat(lb, size(x0)); end
    if isscalar(ub) && (N ~= 1), ub = repmat(ub, size(x0)); end    
    
    % determine the type of bounds
    nf_lb   = ~isfinite(lb);               nf_ub   = ~isfinite(ub);    
    lb_only = ~nf_lb &  nf_ub;             ub_only =  nf_lb & ~nf_ub;
    unconst =  nf_lb &  nf_ub;             fix_var =  lb == ub;
    lb_ub   = ~nf_lb & ~nf_ub & ~fix_var;    
    
    % if all variables are fixed, simply return
    if length(lb(fix_var)) == N            
        sol = reshape(lb,size(new_x));   output.iterations = 0;
        output.funcCount = 0;    fval = funfcn(sol);         
        output.message = 'Lower and upper bound were set equal - nothing to do. ';
        exitflag = 2; [output, exitflag] = finalize(lb, output, exitflag);         
        if exitflag ~= -2
            output.message = sprintf(...
                '%s\nFortunately, the solution is feasible using OPTIONS.TolCon of %1.6f.',...
                output.message, tolCon);
        end, return;
    end    
    
    % force the initial estimate inside the given bounds
    x0(x0 < lb) = lb(x0 < lb);  x0(x0 > ub) = ub(x0 > ub);
        
    % transform initial estimate to its unconstrained counterpart
    xin = x0;                                        % fixed and unconstrained variables   
    xin(lb_only) = sqrt(x0(lb_only) - lb(lb_only));  % lower bounds only   
    xin(ub_only) = sqrt(ub(ub_only) - x0(ub_only));  % upper bounds only
    xin(lb_ub)   = real(asin( 2*(x0(lb_ub) - lb(lb_ub))./ ...
                     (ub(lb_ub) - lb(lb_ub)) - 1));  % both upper and lower bounds
                 
    % remove fixed variables from the initial estimate
    xin(fix_var) = [];
        
    % some constant matrices to speed things up (slightly)
    Nzero = zeros(N, 1);    Np1zero = zeros(N, N+1);   exp200 = exp(200);
        
    % define the transformed & penalized function
    funfncT = @(x) funfncP(X(x), varargin{:});
    
    % optimize the problem
    switch lower(algorithm)
        case 'fminsearch'
            % optimize the problem with FMINSEARCH
            [presol, fval, exitflag, output] = fminsearch(funfncT, xin, options, varargin{:});
        case 'neldermead'
            % optimize the problem with embedded, slightly more efficient NelderMead
            [presol, fval, exitflag, output] = NelderMead(funfncT, xin, options, varargin{:});
        otherwise
            error('optimize:invalid_algorithm',...
                  'Invalid algorithm selected. Valid options are ''NelderMead'' and ''FMINSEARCH''.');
    end
    
    % and transform everything back to original (bounded) variables
    sol = new_x;    sol(:) = X(presol);    % with the same size as the original x0
    
    % append constraint violations to the output structure, and change the
    % exitflag accordingly
    [output, exitflag] = finalize(sol, output, exitflag);
    
    %% nested functions (the actual work)
    
    % check user provided input
    function check_input
        
        % dimensions & weird input
        if (numel(lb) ~= N && ~isscalar(lb)) || (numel(ub) ~= N && ~isscalar(ub))
            error('optimize:lb_ub_incompatible_size',...
                'Size of either [lb] or [ub] incompatible with size of [x0].')
        end
        if ~isempty(A) && isempty(b)
            warning('optimize:Aeq_but_not_beq', ...
                ['I received the matrix [A], but you omitted the corresponding vector [b].',...
                '\nI`ll assume a zero-vector for [b]...'])
            b = zeros(size(A,1), size(x0,2));
        end
        if ~isempty(Aeq) && isempty(beq)
            warning('optimize:Aeq_but_not_beq', ...
                ['I received the matrix [Aeq], but you omitted the corresponding vector [beq].',...
                '\nI`ll assume a zero-vector for [beq]...'])
            beq = zeros(size(Aeq,1), size(x0,2));
        end
        if isempty(Aeq) && ~isempty(beq)
            warning('optimize:beq_but_not_Aeq', ...
                ['I received the vector [beq], but you omitted the corresponding matrix [Aeq].',...
                '\nI`ll ignore the given [beq]...'])
            beq = [];
        end
        if isempty(A) && ~isempty(b)
            warning('optimize:b_but_not_A', ...
                ['I received the vector [b], but you omitted the corresponding matrix [A].',...
                '\nI`ll ignore the given [b]...'])
            b = [];
        end
        if ~isempty(A) && ~isempty(b) && size(b,1)~=size(A,1)
            error('optimize:b_incompatible_with_A',...
                'The size of [b] is incompatible with that of [A].')
        end
        if ~isempty(Aeq) && ~isempty(beq) && size(beq,1)~=size(Aeq,1)
            error('optimize:b_incompatible_with_A',...
                'The size of [beq] is incompatible with that of [Aeq].')
        end
        if ~isvector(x0) && ~isempty(A) && (size(A,2) ~= size(x0,1))
            error('optimize:A_incompatible_size',...
                'Linear constraint matrix [A] has incompatible size for given [x0].')
        end
        if ~isvector(x0) && ~isempty(Aeq) && (size(Aeq,2) ~= size(x0,1))
            error('optimize:Aeq_incompatible_size',...
                'Linear constraint matrix [Aeq] has incompatible size for given [x0].')
        end
        if ~isempty(b) && size(b,2)~=size(x0,2)
            error('optimize:x0_vector_but_not_b',...
                  'Given linear constraint vector [b] has incompatible size with given [x0].')
        end
        if ~isempty(beq) && size(beq,2)~=size(x0,2)
            error('optimize:x0_vector_but_not_beq',...
                  'Given linear constraint vector [beq] has incompatible size with given [x0].')
        end                
          
        % functions are not function handles
        if ~isa(funfcn, 'function_handle')
            error('optimize:func_not_a_function',...
                  'Objective function must be given as a function handle.')
        end
        if ~isempty(nonlcon) && ~isa(nonlcon, 'function_handle')
            error('optimize:nonlcon_not_a_function', ...
                 'non-linear constraint function must be a function handle.')
        end
        
        % test the feasibility of the initial solution (when strict or
        % superstrict behavior has been enabled)        
        if strcmpi(strictness, 'strict') || strcmpi(strictness, 'superstrict')
            superstrict = strcmpi(strictness, 'superstrict');           
            if ~isempty(A) && any(any(A*x0 > b))
                error('optimize:x0_doesnt_satisfy_linear_ineq', ...
                    ['Initial estimate does not satisfy linear inequality.', ...
                    '\nPlease provide an initial estimate inside the feasible region.']);
            end
            if ~isempty(Aeq) && any(any(Aeq*x0 ~= beq))
                error('optimize:x0_doesnt_satisfy_linear_eq', ...
                    ['Initial estimate does not satisfy linear equality.', ...
                    '\nPlease provide an initial estimate inside the feasible region.']);
            end
            if ~isempty(nonlcon)                
                [c, ceq] = nonlcon(x0); %#ok
                if ~isempty(c) && any(c(:) > ~superstrict*tolCon) 
                    error('optimize:x0_doesnt_satisfy_nonlinear_ineq', ...
                        ['Initial estimate does not satisfy nonlinear inequality.', ...
                        '\nPlease provide an initial estimate inside the feasible region.']);
                end
                if ~isempty(ceq) && any(abs(ceq(:)) >= ~superstrict*tolCon) 
                    error('optimize:x0_doesnt_satisfy_nonlinear_eq', ...
                        ['Initial estimate does not satisfy nonlinear equality.', ...
                        '\nPlease provide an initial estimate inside the feasible region.']);
                end
            end
        end    
        
    end % check_input
    
    % create transformed variable to conform to upper and lower bounds
    function xx = X(x)
        if (size(x, 2) == 1), xx = Nzero; else xx = Np1zero; end               
        for i = 1:size(x, 2)   
            xx(lb_only, i) = lb(lb_only,:) + x(lb_only(~fix_var), i).^2;
            xx(ub_only, i) = ub(ub_only,:) - x(ub_only(~fix_var), i).^2;
            xx(fix_var, i) = lb(fix_var,:);
            xx(unconst, i) = x(unconst(~fix_var), i);
            xx(lb_ub, i)   = lb(lb_ub,:) + ...
                (ub(lb_ub,:) - lb(lb_ub,:)) .* (sin(x(lb_ub(~fix_var), i)) + 1)/2;
        end
    end % X
    
    % create penalized function. Penalize with linear penalty function if 
    % violation is severe, otherwise, use exponential penalty. If the
    % 'strict' option has been set, check the constraints, and return INF
    % if any of them are violated.
    function P_fval = funfncP(x, varargin)
        
        % initialize function value
        if (size(x, 2) == 1), P_fval = 0; else P_fval = Nzero.'; end        
        
        % initialize x_new array
        x_new = new_x;
        
        % evaluate every vector in x
        for i = 1:size(x, 2)
            
            % reshape x, so it has the same size as the given x0
            x_new(:) = x(:, i);
            
            % initially, we are optimistic
            linear_eq_Penalty   = 0;   nonlin_eq_Penalty   = 0;
            linear_ineq_Penalty = 0;   nonlin_ineq_Penalty = 0;
            
            % Penalize the linear equality constraint violation 
            % required: Aeq*x = beq   
            if ~isempty(Aeq) && ~isempty(beq)                
                lin_eq = abs(Aeq*x_new - beq);     
                sumlin_eq = sum(abs(lin_eq(:)));
                if strcmpi(strictness, 'strict') && any(lin_eq > 0)
                    P_fval = inf; return
                end                    
                if sumlin_eq > 200
                    linear_eq_Penalty = exp(200) - 1 + sumlin_eq;
                else
                    linear_eq_Penalty = exp(sum(lin_eq)) - 1; 
                end
            end
            
            % Penalize the linear inequality constraint violation 
            % required: A*x <= b
            if ~isempty(A) && ~isempty(b)
                lin_ineq = A*x_new - b;                     
                lin_ineq(lin_ineq <= 0) = 0;
                sumlin_ineq = sum(lin_ineq(:)); 
                if strcmpi(strictness, 'strict') && any(lin_ineq > 0)
                    P_fval = inf; return
                end  
                if sumlin_ineq > 200
                    linear_ineq_Penalty = exp(200) - 1 + sumlin_ineq;
                else
                    linear_ineq_Penalty = exp(sumlin_ineq) - 1; 
                end
            end
            
            % Penalize the non-linear constraint violations
            % required: ceq = 0 and c <= 0
            if ~isempty(nonlcon)
                [c, ceq] = nonlcon(x_new); %#ok
                if ~isempty(c)
                    c = c(:); sumc = sum(c(c > 0));
                    if (strcmpi(strictness, 'strict') || strcmpi(strictness, 'superstrict'))...
                        && any(c > ~superstrict*tolCon)
                        P_fval = inf; return
                    end                       
                    if sumc > 200
                        nonlin_ineq_Penalty = exp200 - 1 + sumc;
                    else
                        nonlin_ineq_Penalty = exp(sumc) - 1;
                    end
                end
                if ~isempty(ceq)
                    ceq = abs(ceq(:));  sumceq = sum(ceq(ceq > 0));
                    if (strcmpi(strictness, 'strict') || strcmpi(strictness, 'superstrict'))...
                        && any(ceq >= ~superstrict*tolCon)
                        P_fval = inf; return
                    end                                        
                    if sumceq > 200
                        nonlin_eq_Penalty = exp200 - 1 + sumceq;
                    else
                        nonlin_eq_Penalty = exp(sumceq) - 1;
                    end                    
                end
            end
            
            % evaluate the objective function
            obj_fval = funfcn(x_new, varargin{:});              
            
            % return penalized function value
            P_fval(i) = obj_fval + linear_eq_Penalty + linear_ineq_Penalty + ...
                nonlin_eq_Penalty + nonlin_ineq_Penalty; %#ok
        end
    end % funfncP
    
    % simple wrapper function for output functions; these need to be 
    % evaluated with the untransformed variables
    function stop = OutputFcn_wrapper(x, varargin)
        x_new = new_x;  x_new(:) = X(x);
        stop = OutputFcn(x_new, varargin{:});
    end % OutputFcn_wrapper
    
    % finalize the output
    function [output, exitflag] = finalize(x, output, exitflag)        
        
        % make sure the algorithm is correct
        output.algorithm  = 'Nelder-Mead simplex direct search';
        
        % reshape x so it has the same size as x0
        x_new = new_x; x_new(:) = x;
        
        % finalize the output
        violated1 = false;   violated2 = false;
        violated3 = false;   violated4 = false;
        if ~isempty(A)
            Ax        = A*x_new;       
            violated1 = Ax >= b + tolCon;
            violation = Ax - b;
            violation(~violated1) = 0;
            output.constrviolation.lin_ineq{1} = violated1;
            output.constrviolation.lin_ineq{2} = violation;
        end
        if ~isempty(Aeq)
            Aeqx = Aeq*x_new;   
            violated2 = abs(Aeqx - beq) > tolCon;            
            violation = Aeqx - beq;
            violation(~violated2) = 0;
            output.constrviolation.lin_eq{1} = violated2;
            output.constrviolation.lin_eq{2} = violation;
        end
        if ~isempty(nonlcon)
            [c, ceq] = nonlcon(x_new); %#ok
            if ~isempty(ceq)
                violated3 = abs(ceq) > tolCon;
                ceq(~violated3) = 0;
                output.constrviolation.nonl_eq{1} = violated3;
                output.constrviolation.nonl_eq{2} = ceq;
            end
            if ~isempty(c)
                violated4 = c > tolCon; c(~violated4) = 0;
                output.constrviolation.nonl_ineq{1} = violated4;
                output.constrviolation.nonl_ineq{2} = c;                
            end            
        end
        if exitflag == -3
            output.message = sprintf(...
                ' No finite function values encountered.\n');
        end
        if any([violated1(:); violated2(:); violated3(:); violated4(:)])
            exitflag = -2;
            output.message = sprintf(...
                ['%s\n Unfortunately, the solution is infeasible for the given value ',...
                'of OPTIONS.TolCon of %1.6e'], output.message, tolCon);
        else
            if exitflag == 1
                output.message = sprintf(...
                    ['%s\b\n and all constraints are satisfied using ',...
                     'OPTIONS.TolCon of %1.6e.'], output.message, tolCon);
            end
        end
    end % finalize  
    
    % optimize globally
    function [sol, fval, exitflag, output] = optimize_globally(varargin)
                
        % first perform error checks
        if isempty(ub) || isempty(lb)
            error('optimize:lbub_undefined',...
                  'When optimizing globally ([x0] is empty), both [lb] and [ub] must be defined.')
        end       
        
        % use 25*(number of dimensions) individuals
        popsize = 25*numel(lb);          
        
        % initialize population of random starting points
        population = repmat(lb, [1,1,popsize]) + ...
            rand(size(lb,1), size(lb,2), popsize) .* repmat(ub-lb, [1,1,popsize]);
                
        % reset maximum allowable function evaluations
        maxiters   = optimget(options, 'MaxIter');
        MaxFunEval = floor( optimget(options, 'MaxFunEvals') / popsize);
        options = optimset(options, 'MaxFunEvals', MaxFunEval);    
        
        % slightly loosen options for global method
        global_options = optimset(options, ...
            'TolX'  , 1e2 * options.TolX,...
            'TolFun', 1e2 * options.TolFun);
        
        % initialize loop
        best_fval = inf; iterations = 0; evaluations = 0; new_x = population(:,:,1);
        sol = NaN(size(lb)); fval = inf; exitflag = []; output = struct;
        
        % loop through each individual, and use it as initial value
        for ii = 1:popsize
                        
            % optimize current problem
            [sol_i, fval_i, exitflag_i, output_i] = ...
                optimize(funfcn, population(:,:,ii), lb, ub, A, b, Aeq, beq, nonlcon,...
                strictness, global_options, algorithm, varargin{:});
            
            % add number of evaluations and iterations to total
            evaluations = evaluations + output_i.funcCount;
            iterations  = iterations + output_i.iterations;
            
            % keep track of the best solution found so far
            if fval_i < best_fval              
                % output values                
                fval   = fval_i;   exitflag = exitflag_i;
                sol    = sol_i;    output = output_i;
                % and store the new best
                best_fval = fval_i;
            end
            
            % reset output structure
            output.funcCount = evaluations;
            output.iterations = iterations;
            
            % evaluations has been taken care of, but
            % iterations not yet            
            if iterations >= maxiters
                % finalize solution
                [output, exitflag] = finalize(sol, output, exitflag);
                % and return
                return
            end
            
        end % for   
        
        % check for INF or NaN values
        if ~isfinite(fval)            
            exitflag = -3;            
            % finalize solution
            [output, exitflag] = finalize(sol, output, exitflag);      
            % and return
            return
        end
        
        % perform the final iteration on the best solution found
        if evaluations < options.MaxFunEvals
            % reset max. number of function evaluations
            options = optimset(options, 'MaxFunEvals', options.MaxFunEvals - evaluations);
            % optimize with stricter options
            [sol, fval, exitflag, output_i] = ...
                optimize(funfcn, sol, lb, ub, A, b, Aeq, beq, nonlcon,...
                strictness, options, algorithm, varargin{:});
            % adjust output
            output.funcCount = output.funcCount + output_i.funcCount;
            output.iterations = output.iterations + output_i.iterations;            
        end        
        
    end % optimize_globally
    
    % Embedded Nelder-Mead algorithm
    function [sol, fval, exitflag, output] = NelderMead(funfcn, x0, options, varargin)
                
        % Nelder-Mead algorithm control factors
        alpha = 1;     beta  = 0.5;
        gamma = 2;     delta = 0.5;
        a     = 1/20; % (a) is the size of the initial simplex. 
                      % 1/20 is 5% of the initial values.        
        % constants
        N                         = length(x0);
        VSfactor_reflect          = alpha^(1/N);
        VSfactor_expand           = (alpha*gamma)^(1/N);
        VSfactor_inside_contract  = beta^(1/N);
        VSfactor_outside_contract = (alpha*beta)^(1/N);
        VSfactor_shrink           = delta;        
        
        % check for output functions
        outputfcn = false;
        if ~isempty(options.OutputFcn) 
            outputfcn = true; 
            OutputFcn = options.OutputFcn; 
        end
        
        % initial values
        iterations   = -1; sort_inds = (1:N).';
        operation    = 0;  op        = 'initial simplex';
        volume_ratio = 1;
        stop = false;
        
        % parse options
        if (narg == 2) || isempty(options), options = optimset; end
        reltol_x = optimget(options, 'TolX', 1e-12);
        reltol_f = optimget(options, 'TolFun', 1e-12);
        max_evaluations = optimget(options, 'MaxFunEvals', 200*N);
        max_iterations  = optimget(options, 'MaxIter', 1e4);
        display = optimget(options, 'Display', 'off');
        
        % generate initial simplex
        p = a/N/sqrt(2) * (sqrt(N+1) + N - 1) *  eye(N);
        q = a/N/sqrt(2) * (sqrt(N+1) - 1)     * ~eye(N);
        x = x0(:, ones(1,N));
        x = [x0, x + p + q];             
        
        % function is known to be ``vectorized''
        f = funfcn(x);    evaluations = N+1;        
        
         % first evaluate output function        
        if outputfcn
            optimValues.iteration = iterations;
            optimValues.funcCount = evaluations;
            optimValues.procedure = op;                                
            outputFcn(x, optimValues, 'init', varargin{:});            
        end
        
        % sort and re-label initial simplex
        [f, inds] = sort(f);    x = x(:, inds);
        
        % compute initial centroid
        C = sum(x(:, 1:end-1), 2)/N;
        
        % display header if per-iteration display is selected
        if strcmpi(display, 'iter')
            fprintf(1, ['\n\t\tf(1)\t\tfunc. evals.\toperation\n', ...
                '\t==================================================\n'])
        end
        
        % main loop
        while true
            
            % evaluate output function
            if outputfcn
                optimValues.iteration = iterations;
                optimValues.funcCount = evaluations;
                optimValues.procedure = op;
                outputFcn(x, optimValues, 'iter', varargin{:});
            end
                       
            % increase number of iterations
            iterations = iterations + 1;
            
            % display string for per-iteration display
            if strcmpi(display, 'iter')
                fprintf(1, '\t%+1.6e', f(1));
                fprintf(1, '\t\t%4.0d', evaluations);
                fprintf(1, '\t\t%s\n', op);
            end
            
            % re-sort function values
            x_replaced = x(:, end);
            switch operation
                case 2      % shrink steps
                    [f, inds] = sort(f);   x = x(:, inds);
                otherwise   % non-shrink steps
                    inds = f(end) <= f(1:end-1);
                    f = [f(sort_inds(~inds)), f(end), f(sort_inds(inds))];
                    x = [x(:, sort_inds(~inds)), x_replaced, x(:, sort_inds(inds))];
            end
            
            % update centroid (Singer & Singer are wrong here...
            % shrink & non-shrink steps should be treated the same)
            C = C + (x_replaced -  x(:, end))/N;
            
            % Algorithm termination conditions
            term_f = abs(f(end) - f(1)) / (abs(f(end)) + abs(f(1)))  < reltol_f;
            fail   = (iterations >= max_iterations) || (evaluations >= max_evaluations);
            term_x = volume_ratio < reltol_x;
            if (term_x || term_f || fail), break, end
                        
            % non-shrink steps are taken most of the time. Set this as the
            % default operation.
            operation = 1;
                
            % try to reflect the simplex
            xr = C + alpha*(C - x(:, end));
            fr = funfcn(xr);
            evaluations = evaluations + 1;
            
            % accept the reflection point
            if fr < f(end-1)
                x(:, end) = xr;
                f(end) = fr;
                
                % try to expand the simplex
                if fr < f(1)                    
                    xe = C + gamma*(xr - C);
                    fe = funfcn(xe);
                    evaluations = evaluations + 1;
                    
                    % accept expand
                    if (fe < f(1))
                        op = 'expand';
                        volume_ratio = VSfactor_expand * volume_ratio;
                        x(:, end) = xe;
                        f(end) = fe;
                        continue;
                    end
                end
                
                % otherwise, just continue
                op = 'reflect';
                volume_ratio = VSfactor_reflect * volume_ratio;
                continue;
                
                % otherwise, try to contract the simplex
            else
                                
                % outside contraction
                if fr < f(end)
                    xc = C + beta*(xr - C);
                    insouts = 1;
                    
                    % inside contraction
                else
                    xc = C + beta*(x(:, end) - C);
                    insouts = 2;
                end
                fc = funfcn(xc);
                evaluations = evaluations + 1;
                
                % accept contraction
                if fc < min(fr, f(end))
                    switch insouts
                        case 1
                            op = 'outside contraction';
                            volume_ratio = VSfactor_outside_contract * volume_ratio;
                        case 2
                            op = 'inside contraction';
                            volume_ratio = VSfactor_inside_contract * volume_ratio;
                    end
                    x(:, end) = xc;
                    f(end) = fc;
                    continue;
                    
                % everything else has failed - shrink the simplex towards x1
                else      
                    % first evaluate output function
                    op = 'shrink';
                    if outputfcn
                        optimValues.iteration = iterations;
                        optimValues.funcCount = evaluations;
                        optimValues.procedure = op;                        
                        stop = outputFcn(x, optimValues, 'interrupt', varargin{:});
                        if stop, break, end
                    end
                    % then shrink                    
                    operation = 2;
                    xones = x(:, ones(1, N+1));
                    x = xones + delta*(x - xones);
                    f = funfcn(x);
                    evaluations = evaluations + N + 1;
                    volume_ratio = VSfactor_shrink * volume_ratio;
                end
            end            
        end
        
        % evaluate output function        
        if outputfcn
            optimValues.iteration = iterations;
            optimValues.funcCount = evaluations;
            optimValues.procedure = 'Final simplex.';
            options.OutputFcn(x, optimValues, 'done', varargin{:});            
        end
        
        % end values
        sol  = x(:, 1);
        fval = f(1);        
        
        % exitflag
        if (term_x || term_f), exitflag = 1; end % normal convergence
        if stop, exitflag = -1; end              % stopped by stopfunction
        if fail, exitflag = 0; end               % max. iterations or max. func. eval. exceeded        
        
        % create output structure        
        output.iterations = iterations;
        output.funcCount  = evaluations;
        if exitflag == 1
            output.message = sprintf(['Optimization terminated:\n',...
               ' the current x satisfies the termination criteria using OPTIONS.TolX of %d \n',...
               ' and F(X) satisfies the convergence criteria using OPTIONS.TolFun of %d \n'],...
               reltol_x, reltol_f);
        end
        if exitflag == 0
            if (iterations >= max_iterations)
                output.message = sprintf(['Optimization terminated:\n',...
               ' Maximum amount of iterations exceeded; Increase ''MaxIters'' option.\n']);
            elseif (evaluations >= max_evaluations)
                output.message = sprintf(['Optimization terminated:\n',...
               ' Maximum amount of function evaluations exceeded; Increase ''MaxFunevals'' option.\n']);
            end            
        end
        if exitflag == -1
            output.message = sprintf('Optimization terminated by user-provded output function.\n');
        end
    end % NelderMead
    
end % function

Contact us at files@mathworks.com