%% Output Function for Problem-Based Optimization
% This example shows how to use an output function to plot and store the history of the iterations for a nonlinear problem. This history includes the evaluated points, the search directions that the solver uses to generate points, and the objective function values at the evaluated points.
% For the solver-based approach to this example, see Output Functions.
% Plot functions have the same syntax as output functions, so this example also applies to plot functions.
% For both the solver-based approach and the problem-based approach, write the output function according to the solver-based approach. In the solver-based approach, you use a single vector variable, usually denoted x, instead of a collection of optimization variables of various sizes. So to write an output function for the problem-based approach, you must understand the correspondence between your optimization variables and the single solver-based x. To map between optimization variables and x, use varindex. In this example, to avoid confusion with an optimization variable named x, use "in" as the vector variable name.
%
%% Problem Description
% The problem is to minimize the following function of variables x and y:
% f=\exp(x)\left (4x^2 + 2y^2 + 4xy + 2y+1\right ).
% In addition, the problem has two nonlinear constraints:
% x + y - xy \ge 1.5\\xy \ge -10.
%
%% Problem-Based Setup
% To set up the problem in the problem-based approach, define optimization variables and an optimization problem object.

x = optimvar('x');
y = optimvar('y');
prob = optimproblem;

% Define the objective function as an expression in the optimization variables.

f = exp(x)*(4*x^2 + 2*y^2 + 4*x*y + 2*y + 1);

% Include the objective function in prob.

prob.Objective = f;

% To include the nonlinear constraints, create optimization constraint expressions.

cons1 = x + y - x*y >= 1.5;
cons2 = x*y >= -10;
prob.Constraints.cons1 = cons1;
prob.Constraints.cons2 = cons2;

% Because this is a nonlinear problem, you must include an initial point structure x0. Use x0.x = –1 and x0.y = 1.

x0.x = -1;
x0.y = 1;

%
%% Output Function
% The outfun output function records a history of the points generated by fmincon during its iterations. The output function also plots the points and keeps a separate history of the search directions for the sqp algorithm. The search direction is a vector from the previous point to the next point that fmincon tries. During its final step, the output function saves the history in workspace variables, and saves a history of the objective function values at each iterative step.
% For the required syntax of optimization output functions, see Output Function Syntax.
% An output function takes a single vector variable as an input. But the current problem has two variables. To find the mapping between the optimization variables and the input variable, use varindex.

idx = varindex(prob);
idx.x
idx.y

% The mapping shows that x is variable 1 and y is variable 2. So, if the input variable is named in, then x = in(1) and y = in(2).

type outfun

% Include the output function in the optimization by setting the OutputFcn option. Also, set the Algorithm option to use the 'sqp' algorithm instead of the default 'interior-point' algorithm. Pass idx to the output function as an extra parameter in the last input. See Passing Extra Parameters.

outputfn = @(in,optimValues,state)outfun(in,optimValues,state,idx);
opts = optimoptions('fmincon','Algorithm','sqp','OutputFcn',outputfn);

%
%% Run Optimization Using Output Function
% Run the optimization, including the output function, by using the 'Options' name-value pair argument.

[sol,fval,eflag,output] = solve(prob,x0,'Options',opts)

% Examine the iteration history. Each row of the optimhistory matrix represents one point. The last few points are very close, which explains why the plotted sequence shows overprinted numbers for points 8, 9, and 10.

disp('Locations');disp(optimhistory)

% Examine the searchdirhistory and functionhistory arrays.

disp('Search Directions');disp(searchdirhistory)
disp('Function Values');disp(functionhistory)

%
%% Unsupported Functions Require fcn2optimexpr
% If your objective function or nonlinear constraint functions are not composed of elementary functions, you must convert the functions to optimization expressions using fcn2optimexpr. See Convert Nonlinear Function to Optimization Expression. For this example, you would enter the following code:

fun = @(x,y)exp(x)*(4*x^2 + 2*y^2 + 4*x*y + 2*y + 1);
f = fcn2optimexpr(fun,x,y);

% For the list of supported functions, see Supported Operations on Optimization Variables and Expressions.
%
% Copyright 2018 The MathWorks, Inc.