Main Content

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 the advantage that solve includes gradients calculated by automatic differentiation. See Effect of Automatic Differentiation in Problem-Based Optimization.

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 for Optimization Variables and Expressions.

For example, suppose that your objective function is

f(x,y,r)=100(y-x2)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=r2.

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) 
Solving problem using lsqnonlin.

Local minimum found.

Optimization completed because the size of the gradient is less than
the value of the optimality tolerance.
sol = struct with fields:
    x: 2.0000
    y: 4.0000

fval = 4.4373e-31

To solve the same problem by converting the objective function using fcn2optimexpr, first write the objective as an anonymous function.

fun = @(x,y)100*(y - x^2)^2 + (r - x)^2;

Convert the anonymous function to an optimization expression.

prob.Objective = fcn2optimexpr(fun,x,y);
[sol2,fval2] = solve(prob,x0)
Solving problem using lsqnonlin.

Local minimum found.

Optimization completed because the size of the gradient is less than
the value of the optimality tolerance.
sol2 = struct with fields:
    x: 2.0000
    y: 4.0000

fval2 = 4.4373e-31

The solution is the same as before. Generally, in software versions R2022b or later, using fcn2optimexpr gives little to no performance reduction, and in some cases gives improved performance because of static analysis. See Static Analysis of Optimization Expressions.

The remainder of this example shows more detail about using fcn2optimexpr in an optimization expression for an objective function or nonlinear constraint.

Function File

To use a function file containing unsupported operators 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
function [f,g,mineval] = expfn3(u,v)
mineval = min(eig(u));
f = v'*u*v;
f = -exp(-f);
t = u*v;
g = t'*t + sum(t) - 3;

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 argument. Also, because expfn3 computes all of the outputs, you can save more computing time by using the ReuseEvaluation name-value argument.

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

    [argout,~,~] = expfn3(u, v)

g = 
  Nonlinear OptimizationExpression

    [~,argout,~] = expfn3(u, v)

mineval = 
  Nonlinear OptimizationExpression

    [~,~,argout] = expfn3(u, v)

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])
funexpr = 
  Nonlinear OptimizationExpression

    arg1

  where:

      anonymousFunction1 = @(u)min(eig(u));
      arg1 = anonymousFunction1(u);

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 mineval-1/2.

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

View the problem.

show(prob)
  OptimizationProblem : 

	Solve for:
       u, v

	minimize :
       [argout,~,~] = expfn3(u, v)


	subject to nlcons1:
       arg_LHS <= 0

       where:

             [~,arg_LHS,~] = expfn3(u, v);

	subject to sym:
       u(2, 1) - u(1, 2) == 0
       u(3, 1) - u(1, 3) == 0
       -u(2, 1) + u(1, 2) == 0
       u(3, 2) - u(2, 3) == 0
       -u(3, 1) + u(1, 3) == 0
       -u(3, 2) + u(2, 3) == 0

	subject to mineval:
       arg_LHS >= (-0.5)

       where:

             [~,~,arg_LHS] = expfn3(u, v);

	variable bounds:
       -1 <= u(1, 1) <= 1
       -1 <= u(2, 1) <= 1
       -1 <= u(3, 1) <= 1
       -1 <= u(1, 2) <= 1
       -1 <= u(2, 2) <= 1
       -1 <= u(3, 2) <= 1
       -1 <= u(1, 3) <= 1
       -1 <= u(2, 3) <= 1
       -1 <= u(3, 3) <= 1

       -2 <= v(1) <= 2
       -2 <= v(2) <= 2
       -2 <= v(3) <= 2

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)
Solving problem using fmincon.

Feasible point with lower objective function value found, but optimality criteria not satisfied. See output.bestfeasible..


Local minimum found that satisfies the constraints.

Optimization completed because the objective function is non-decreasing in 
feasible directions, to within the value of the optimality tolerance,
and constraints are satisfied to within the value of the constraint tolerance.
sol = struct with fields:
    u: [3x3 double]
    v: [3x1 double]

fval = -403.4288
exitflag = 
    OptimalSolution

output = struct with fields:
              iterations: 84
               funcCount: 1233
         constrviolation: 2.0777e-12
                stepsize: 3.3705e-04
               algorithm: 'interior-point'
           firstorderopt: 7.2829e-04
            cgiterations: 67
                 message: 'Local minimum found that satisfies the constraints....'
            bestfeasible: [1x1 struct]
     objectivederivative: "finite-differences"
    constraintderivative: "finite-differences"
                  solver: 'fmincon'

View the solution.

disp(sol.u)
    0.9190    0.5579   -0.8611
    0.5579    0.6804    0.6225
   -0.8611    0.6225    0.9835
disp(sol.v)
    2.0000
   -2.0000
    2.0000

The solution matrix u is symmetric. All values of v are at the bounds.

Copyright 2018–2022 The MathWorks, Inc.

See Also

|

Related Topics