Convert Constraints in for Loops for Static Analysis
Static analysis increases the speed of for loops in problem-based expressions, as described in Static Analysis of Optimization Expressions. Static analysis applies to expressions created by fcn2optimexpr, not to the comparison operators <=, ==, and >=. Therefore, when you have a constraint created using a for loop, subtract the right side of the constraint from both sides of the comparison so that the comparison is to zero, or to a constant. Then apply fcn2optimexpr.
This example has the constraints and for . Begin by creating optimization variables.
N = 20; x = optimvar("x",N); u = optimvar("u");
To express the constraints in a for loop, subtract the appropriate values so that the constraints are compared to 0:
Typically, you express these constraints in the following code:
for i = 1:N cons1(i) = x(i) - u - i + 1; cons2(i) = x(i) + u + i - 1; end
To apply static analysis, place this for loop in a separate helper function named forloopfcn. The resulting function appears at the end of this example. (You can easily convert a for loop in your script to a separate function, as shown in Create for Loop for Static Analysis.)
To take advantage of static analysis, convert the function to an optimization expression using fcn2optimexpr.
[cons1,cons2] = fcn2optimexpr(@forloopfcn,x,u);
Create an optimization problem and place the constraints in the problem.
prob = optimproblem; prob.Constraints.cons1 = cons1 <= 0; prob.Constraints.cons2 = cons2 >= 0;
Create an objective function that is a weighted sum of the u variable and the sum of squared differences of the x variables from the vector 1.5*(1:N).
M = 100; % Weight factor for u
prob.Objective = M*u + sum((x - 1.5*(1:N)').^2);Review the problem.
show(prob)
OptimizationProblem :
Solve for:
u, x
minimize :
x(1)^2 + x(2)^2 + x(3)^2 + x(4)^2 + x(5)^2 + x(6)^2 + x(7)^2 + x(8)^2 + x(9)^2 + x(10)^2 + x(11)^2 + x(12)^2 + x(13)^2 + x(14)^2 + x(15)^2 + x(16)^2 + x(17)^2 + x(18)^2 + x(19)^2 + x(20)^2 + 100*u - 3*x(1) - 6*x(2) - 9*x(3) - 12*x(4) - 15*x(5) - 18*x(6) - 21*x(7) - 24*x(8) - 27*x(9) - 30*x(10) - 33*x(11) - 36*x(12) - 39*x(13) - 42*x(14) - 45*x(15) - 48*x(16) - 51*x(17) - 54*x(18) - 57*x(19) - 60*x(20) + 6457.5
subject to cons1:
-u + x(1) <= 0
-u + x(2) <= 1
-u + x(3) <= 2
-u + x(4) <= 3
-u + x(5) <= 4
-u + x(6) <= 5
-u + x(7) <= 6
-u + x(8) <= 7
-u + x(9) <= 8
-u + x(10) <= 9
-u + x(11) <= 10
-u + x(12) <= 11
-u + x(13) <= 12
-u + x(14) <= 13
-u + x(15) <= 14
-u + x(16) <= 15
-u + x(17) <= 16
-u + x(18) <= 17
-u + x(19) <= 18
-u + x(20) <= 19
subject to cons2:
u + x(1) >= 0
u + x(2) >= -1
u + x(3) >= -2
u + x(4) >= -3
u + x(5) >= -4
u + x(6) >= -5
u + x(7) >= -6
u + x(8) >= -7
u + x(9) >= -8
u + x(10) >= -9
u + x(11) >= -10
u + x(12) >= -11
u + x(13) >= -12
u + x(14) >= -13
u + x(15) >= -14
u + x(16) >= -15
u + x(17) >= -16
u + x(18) >= -17
u + x(19) >= -18
u + x(20) >= -19
Solve the problem.
[sol,fval,exitflag,output] = solve(prob)
Solving problem using quadprog. 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. <stopping criteria details>
sol = struct with fields:
u: 4.1786
x: [20×1 double]
fval = 653.3036
exitflag =
OptimalSolution
output = struct with fields:
message: '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.↵↵<stopping criteria details>↵↵Optimization completed: The relative dual feasibility, 1.421085e-16,↵is less than options.OptimalityTolerance = 1.000000e-08, the complementarity measure,↵1.536130e-10, is less than options.OptimalityTolerance, and the relative maximum constraint↵violation, 0.000000e+00, is less than options.ConstraintTolerance = 1.000000e-08.'
algorithm: 'interior-point-convex'
firstorderopt: 6.1445e-09
constrviolation: 0
iterations: 8
linearsolver: 'sparse'
cgiterations: []
solver: 'quadprog'
View the x variables in the solution.
disp(sol.x)
1.5000
3.0000
4.5000
6.0000
7.5000
9.0000
10.1786
11.1786
12.1786
13.1786
14.1786
15.1786
16.1786
17.1786
18.1786
19.1786
20.1786
21.1786
22.1786
23.1786
View the constraint function values at the solution.
evaluate(cons1,sol)
ans = 1×20
-2.6786 -2.1786 -1.6786 -1.1786 -0.6786 -0.1786 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000 -0.0000
evaluate(cons2,sol)
ans = 1×20
5.6786 8.1786 10.6786 13.1786 15.6786 18.1786 20.3571 22.3571 24.3571 26.3571 28.3571 30.3571 32.3571 34.3571 36.3571 38.3571 40.3571 42.3571 44.3571 46.3571
The cons1 constraint specifies that the value is nonpositive. The first six values of cons1 are negative, and the remaining values are zero to display precision. Therefore, at the solution, the cons1 constraint is active for all values except the first six values. In contrast, the cons2 constraint specifies that the value is nonnegative. All values of cons2 are strictly positive, meaning this constraint is not active at the solution.
Helper Function
This code creates the forloopfcn helper function.
function [cons1,cons2] = forloopfcn(x,u) N = length(x); cons1 = zeros(1,N); cons2 = zeros(1,N); for i = 1:N cons1(i) = x(i) - u - i + 1; cons2(i) = x(i) + u + i - 1; end end