%% Convert Nonlinear Function to Optimization Expression
% This section shows how to choose whether to convert a nonlinear function to an optimization expression or to create the expression out of supported operations on optimization variables. The section also shows how to convert a function, if necessary, by using fcn2optimexpr.
%% Use Supported Operations When Possible
% Generally, create your objective or nonlinear constraint functions by using supported operations on optimization variables and expressions. Doing so has these advantages:
% solve includes gradients calculated by automatic differentiation. See Effect of Automatic Differentiation in Problem-Based Optimization.
% solve has a wider choice of available solvers. When using fcn2optimexpr, solve uses only fmincon or fminunc.
% In general, supported operations include all elementary mathematical operations: addition, subtraction, multiplication, division, powers, and elementary functions such as exponential and trigonometric functions and their inverses. Nonsmooth operations such as max, abs, if, and case are not supported. For the complete description, see Supported Operations on Optimization Variables and Expressions.
% For example, suppose that your objective function is
% f(x,y,r) = 100\big( y - x^2\big)^2 + (r-x)^2
% where r is a parameter that you supply, and the problem is to minimize f over x and y . This objective function is a sum of squares, and takes the minimal value of 0 at the point x=r, y = r^2.
% The objective function is a polynomial, so you can write it in terms of elementary operations on optimization variables.

r = 2;
x = optimvar('x');
y = optimvar('y');
f = 100*(y - x^2)^2 + (r - x)^2;
prob = optimproblem("Objective",f);
x0.x = -1;
x0.y = 2;
[sol,fval] = solve(prob,x0) 

%
% To solve the same problem by converting the objective function using fcn2optimexpr (not recommended), first write the objective as an anonymous function.

fun = @(x,y)100*(y - x^2)^2 + (r - x)^2;
prob.Objective = fcn2optimexpr(fun,x,y);
[sol2,fval2] = solve(prob,x0)

% Notice that solve uses fminunc this time instead of the more efficient lsqnonlin, and the reported solution for y is slightly different than the correct solution 4. Furthermore, the reported fval is about 1e-9 instead of 1e-20 (the actual solution value is exactly 0). These slight inaccuracies are due to solve not using the more efficient solver.
% The remainder of this example shows how to convert a function to an optimization expression using fcn2optimexpr.
%
%% Function File
% To use a function file in the problem-based approach, you must convert the file to an expression using fcn2optimexpr.
% For example, the expfn3.m file contains the following code:

type expfn3.m

% This function is not entirely composed of supported operations because of min(eig(u)). Therefore, to use expfn3(u,v) as an optimization expression, you must first convert it using fcn2optimexpr.
% To use expfn3 as an optimization expression, first create optimization variables of the appropriate sizes.

u = optimvar('u',3,3,'LowerBound',-1,'UpperBound',1); % 3-by-3 variable
v = optimvar('v',3,'LowerBound',-2,'UpperBound',2); % 3-by-1 variable

% Convert the function file to an optimization expressions using fcn2optimexpr.

[f,g,mineval] = fcn2optimexpr(@expfn3,u,v);

% Because all returned expressions are scalar, you can save computing time by specifying the expression sizes using the 'OutputSize' name-value pair argument. Also, because expfn3 computes all of the outputs, you can save more computing time by using the ReuseEvaluation name-value pair.

[f,g,mineval] = fcn2optimexpr(@expfn3,u,v,'OutputSize',[1,1],'ReuseEvaluation',true)

%
%% Anonymous Function
% To use a general nonlinear function handle in the problem-based approach, convert the handle to an optimization expression using fcn2optimexpr. For example, write a function handle equivalent to mineval and convert it.

fun = @(u)min(eig(u));
funexpr = fcn2optimexpr(fun,u,'OutputSize',[1,1])

%
%% Create Objective
% To use the objective expression as an objective function, create an optimization problem.

prob = optimproblem;
prob.Objective = f;

%
%% Define Constraints
% Define the constraint g <= 0 in the optimization problem.

prob.Constraints.nlcons1 = g <= 0;

% Also define the constraints that u is symmetric and that \rm{mineval}\ge -1/2.

prob.Constraints.sym = u == u.';
prob.Constraints.mineval = mineval >= -1/2;

% View the problem.

show(prob)

%
%% Solve Problem
% To solve the problem, call solve. Set an initial point x0.

rng default % For reproducibility
x0.u = 0.25*randn(3);
x0.u = x0.u + x0.u.';
x0.v = 2*randn(3,1);
[sol,fval,exitflag,output] = solve(prob,x0)

%
% View the solution.

disp(sol.u)
disp(sol.v)

% The solution matrix u is symmetric. All values of v are at the bounds.
%
% Copyright 2018–2020 The MathWorks, Inc.