Main Content

Initialize Optimization Expressions

Error in Expression

Sometimes you get this mysterious error from an objective or nonlinear constraint function or expression:

Unable to perform assignment because value of type 'optim.problemdef.OptimizationExpression' is not convertible
to 'double'.

Often, this error comes from an improper initialization of an optimization expression. Typically, you initialize an variable F in a standard loop by declaring an array of zeros, such as

F = zeros(N,1);

However, if F is an optimization expression, then you must initialize it using optimexpr:

F = optimexpr(N,1);

The following topics provide examples of initialization techniques. All are based on the same example, a function that uses an internal loop.

function out = myFun(x)
out = zeros(size(x));
out(1) = x(1);
for i = 2:10
    out(i) = (x(i) - x(i-1))^3;
end
out = mean(out);
end

If you try to use myFun(x) as the objective function for an optimization variable x, you get an error

x = optimvar('x',10,"LowerBound",0,"UpperBound",10);
prob = optimproblem("Objective",myFun(x));
Unable to perform assignment because value of type 'optim.problemdef.OptimizationVariable' is not convertible
to 'double'.

Error in myFun (line 3)
out(1) = x(1);

Caused by:
    Error using double
    Conversion to double from optim.problemdef.OptimizationVariable is not possible.

However, myFun works as the objective in a solver-based problem.

rng default
x0 = 10*rand(10,1);
lb = zeros(10,1);
ub = 10 + lb;
[sol,fval] = fmincon(@myFun,x0,[],[],[],[],lb,ub)
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 =

    9.4226
   10.0000
    0.0000
    5.0000
   10.0000
    0.0000
    3.3333
    6.6667
   10.0000
    0.0000


fval =

 -262.9274

This problem has several local solutions, so you can get different answers depending on your initial point.

Modify Function To Accept an Initial Array

Rewrite the function to accept the initial value as an additional argument. You can then pass an optimization expression or a numeric array as the initial value. myFun2 uses the input variable out as the output variable, and accepts either a zero array or optimization expression.

This method has the advantages of enabling automatic differentiation, if applicable, and of not introducing extra code that runs during the solution process. The method has the disadvantage of requiring a rewrite of the function with a different function signature.

function out = myFun2(out,x)
out(1) = x(1);
for i = 2:10
    out(i) = (x(i) - x(i-1))^3;
end
out = mean(out);
end

Use myFun2 in a problem-based way.

x = optimvar('x',10,"LowerBound",0,"UpperBound",10);
out = optimexpr(size(x));
prob = optimproblem("Objective",myFun2(out,x));
rng default
x0.x = 10*rand(10,1);
[sol,fval] = solve(prob,x0)
Solving problem using fmincon.

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:

    x: [10×1 double]


fval =

 -262.9274

Use myFun2 in a solver-based way.

rng default
x0 = 10*rand(10,1);
lb = zeros(10,1);
ub = 10 + lb;
out = zeros(size(x0));
[sol,fval] = fmincon(@(x)myFun2(out,x),x0,[],[],[],[],lb,ub)
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 =

    9.4226
   10.0000
    0.0000
    5.0000
   10.0000
    0.0000
    3.3333
    6.6667
   10.0000
    0.0000


fval =

 -262.9274

Rewrite Function to Initialize Expressions Appropriately

You can explicitly check for the type of problem variables and initialize an expression appropriately. This method has the advantages of enabling automatic differentiation, if applicable, and keeping the same function signature. It has the disadvantages of requiring a rewrite of the function and of having a small amount of overhead while the solver runs.

function out = myFun3(x)
% Check for the data type of variable x
if isa(x,'double')
    out = zeros(size(x));
else
    out = optimexpr(size(x));
end
% No changes to the rest of the code
out(1) = x(1);
for i = 2:10
    out(i) = (x(i) - x(i-1))^3;
end
out = mean(out);
end

Solve the problem using optimization variables with the objective function myFun3.

x = optimvar('x',10,"LowerBound",0,"UpperBound",10);
prob = optimproblem("Objective",myFun3(x));
rng default
x0.x = 10*rand(10,1);
[sol,fval] = solve(prob,x0)
Solving problem using fmincon.

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:

    x: [10×1 double]


fval =

 -262.9274

Solve the problem using fmincon with the objective function myFun3.

rng default
x0 = 10*rand(10,1);
lb = zeros(10,1);
ub = 10 + lb;
out = zeros(size(x0));
[sol,fval] = fmincon(@myFun3,x0,[],[],[],[],lb,ub)
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 =

    9.4226
   10.0000
    0.0000
    5.0000
   10.0000
    0.0000
    3.3333
    6.6667
   10.0000
    0.0000


fval =

 -262.9274

Avoid fcn2optimexpr Conversion

You can convert the objective function to an optimization expression using fcn2optimexpr. The resulting objective function can use a standard zero array as an initialization. This method has the advantage of not requiring any rewrite of the objective function. However, the method has the disadvantages that the resulting function cannot take advantage of automatic differentiation, and the resulting solver might not be the best. For example, with a black-box objective function, solve can inappropriately choose to use fmincon instead of quadprog or lsqnonlin.

To determine whether the result is a black box, examine the output of fcn2optimexpr with Display="on". If so, to improve the solution process, rewrite your problem according to the techniques in Modify Function To Accept an Initial Array or Rewrite Function to Initialize Expressions Appropriately. This example uses the original function myFun that fails in the problem-based approach in Error in Expression.

x = optimvar('x',10,"LowerBound",0,"UpperBound",10);
obj = fcn2optimexpr(@myFun,x,Display="on");
prob = optimproblem("Objective",obj);
rng default
x0.x = 10*rand(10,1);
[sol,fval] = solve(prob,x0)
The function contains an unsupported operator. The returned expressions are nonlinear black boxes.

Solving problem using fmincon.

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:

    x: [10×1 double]


fval =

 -262.9274

While solve returns a solution, fcn2optimexpr reports that the converted function is a nonlinear black box. For a more efficient solution process, rewrite your problem according to the techniques in Modify Function To Accept an Initial Array or Rewrite Function to Initialize Expressions Appropriately.

See Also

|

Related Topics