Linear, quadratic and nonlinear programming¶
Problem¶
Solve three small optimisation problems with MATLAB's Optimization Toolbox, one per solver:
a linear program with linprog, a quadratic program with quadprog, and a nonlinear problem
with fmincon from two different starting points. These are the solvers behind every
efficient frontier in the rest of this part.
Method¶
| Solver | Problem form | Exercise |
|---|---|---|
linprog |
\(\min f^\top x\) s.t. \(Ax \le b,\ x \ge 0\) | 112: \(\min\, -5x_1 - 4x_2 - 6x_3\) |
quadprog |
\(\min \tfrac12 x^\top H x + f^\top x\) s.t. \(Ax \le b,\ x \ge 0\) | 113: \(\min\, x_1^2 + x_2^2 - x_1x_2 - 2x_1 - 6x_2\) |
fmincon |
\(\min f(x)\) s.t. \(Ax \le b\) | 115: \(\min\, x_1 x_2 x_3\) |
For quadprog, \(H\) holds twice the coefficients of the squared terms on the diagonal and the
cross-term coefficient off the diagonal, placed symmetrically.
Code¶
clear all, clc
%% 1
H = [2 -1; -1 2] % squared terms: coefficient times 2;
% cross term: as it is, placed symmetrically
f = [-2 -6] % coefficients of the linear part
A = [1 1;
-1 2;
2 1] % coefficients of the inequality constraints
b = [2; 2; 3] % right-hand side of the inequality constraints
lb = zeros(2, 1)
[x,fval] = quadprog(H,f,A,b,[],[],lb,[])
clear all, clc
% Point 1
fun = @(x) x(1) * x(2) * x(3)
x01 = [2 4 8]
A = [1 3 4; -2 -1 -1/2]
b = [0 50]
[x,fval] = fmincon(fun,x01,A,b,[],[],[],[])
% Point 2
x02 = [-8 0 -3]
[x,fval] = fmincon(fun,x02,A,b,[],[],[],[])
% fmincon (nonlinear programming) finds two different local minima from the
% two starting points, but not a global minimum (because of the shape of
% the function, which could be for example a "saddle")
import numpy as np
from scipy.optimize import linprog # same name and almost the same arguments as MATLAB
# %% Point 1
f = np.array([-5, -4, -6])
A = np.array([[1, -1, 1],
[3, 2, 4],
[3, 2, 0]])
b = np.array([20, 42, 30])
lb = [(0, None)] * len(f) # x >= 0 for each variable (None = no upper bound)
res = linprog(f, A_ub=A, b_ub=b, bounds=lb) # MATLAB: [x, fval] = linprog(f, A, b, Aeq, beq, lb, ub)
x = res.x
fval = res.fun
print("x =", x)
print("fval =", fval)
import numpy as np
from scipy.optimize import minimize # scipy has no quadprog: minimize does the same job
# %% 1
H = np.array([[2, -1],
[-1, 2]]) # squared terms: coefficient times 2; cross term as it is
f = np.array([-2, -6]) # coefficients of the linear part
A = np.array([[1, 1],
[-1, 2],
[2, 1]]) # inequality constraints A x <= b
b = np.array([2, 2, 3])
lb = [(0, None)] * 2 # x >= 0
obj = lambda x: 0.5 * x @ H @ x + f @ x # the quadratic objective that quadprog minimises
con = {'type': 'ineq', 'fun': lambda x: b - A @ x} # scipy wants constraints written as (something) >= 0
res = minimize(obj, np.zeros(2), method='SLSQP', bounds=lb, constraints=con)
x = res.x
fval = res.fun
print("x =", x)
print("fval =", fval)
import numpy as np
from scipy.optimize import minimize # MATLAB's fmincon
# %% Point 1
fun = lambda x: x[0] * x[1] * x[2] # MATLAB: @(x) x(1) * x(2) * x(3)
x01 = np.array([2, 4, 8])
A = np.array([[1, 3, 4],
[-2, -1, -1/2]])
b = np.array([0, 50])
con = {'type': 'ineq', 'fun': lambda x: b - A @ x} # A x <= b, written as b - A x >= 0
res = minimize(fun, x01, method='SLSQP', constraints=con)
print("x =", res.x)
print("fval =", res.fun)
# %% Point 2
x02 = np.array([-8, 0, -3])
res = minimize(fun, x02, method='SLSQP', constraints=con)
print("x =", res.x)
print("fval =", res.fun)
# Two starting points, two different "solutions": see the note on the page
# (the problem has no minimum at all).
Results¶
| Exercise | Solution | Objective |
|---|---|---|
| 112, linear | \(x = (0,\ 15,\ 3)\) | −78 |
| 113, quadratic | \(x = (0.667,\ 1.333)\) | −8.00 |
| 115, from \(x_0 = (2, 4, 8)\) | \(x = (-28.38,\ 5.15,\ 3.24)\) | −472.6 |
| 115, from \(x_0 = (-8, 0, -3)\) | \(x = (-8.33,\ -16.67,\ -33.33)\) | −4,629.6 |
Exercise 115 has no minimum at all
The code's closing comment reads the two results as two local minima with no global one.
The stronger statement is that the problem is unbounded below. Take
\(x = (a,\ -(50 + 2.5a),\ a)\): both constraints hold for any \(a > 0\) (the second one with
equality), and the objective \(-a^2(50 + 2.5a)\) goes to \(-\infty\). For \(a = 100\) it is
already \(-3{,}000{,}000\), far below both points fmincon returns. Each run stops at a
point where the local optimality conditions hold, which is all a local solver can
guarantee.
Takeaways¶
- The three solvers differ in what they accept: linear objective, quadratic objective, or any smooth function. Portfolio problems are built to fit the first two, because those are solved to the global optimum.
- A nonlinear solver's answer depends on the starting point, and a "solution" can exist even when the problem has no minimum: checking boundedness is part of the modelling.