Некоторые приложения выпуклой оптимизации

(основано на примерах к CVXPY, ссылка)


In [3]:
import numpy as np
import cvxpy as cvx
print(cvx.installed_solvers())
import matplotlib.pyplot as plt


['ECOS', 'ECOS_BB', 'CVXOPT', 'GLPK', 'GLPK_MI', 'SCS', 'MOSEK', 'OSQP']

In [4]:
USE_COLAB = False
if USE_COLAB:
    !wget -q https://github.com/amkatrutsa/MIPT-Opt/blob/master/01-Intro/kandinskiy_comp_viii.jpg?raw=true -O kandinskiy_comp_viii.jpg
    !wget -q https://github.com/amkatrutsa/MIPT-Opt/blob/master/01-Intro/lena_small.jpg?raw=true -O lena_small.jpg
    !wget -q https://github.com/amkatrutsa/MIPT-Opt/blob/master/01-Intro/test_sound.wav?raw=true -O test_sound.wav
else:
    plt.rc("text", usetex=True)

Удаление шума

  • Изображение
  • Звук

Изображение


In [5]:
from PIL import Image

# np.random.seed(1)
# Load image
orig_img = Image.open("./kandinskiy_comp_viii.jpg")
# orig_img = Image.open("./lena_small.jpg")

# Convert to arrays
Uorig = np.array(orig_img)
rows, cols, colors = Uorig.shape
print(Uorig.shape)

# Known is 1 if the pixel is known,
# 0 if the pixel was corrupted
Known = np.zeros((rows, cols, colors))
for i in range(rows):
    for j in range(cols):
        if np.random.random() > 0.7:
            for k in range(colors):
                Known[i, j, k] = 1

# Create corrupted image
Ucorr = Known * Uorig
# Convert to image
corr_img = Image.fromarray(np.uint8(Ucorr))


(425, 640, 3)

In [6]:
fig, ax = plt.subplots(1, 2, figsize=(20, 20))
ax[0].imshow(orig_img);
ax[0].set_title("Original image", fontsize=24)
ax[0].axis('off')
ax[1].imshow(corr_img);
ax[1].set_title("Corrupted image", fontsize=24)
ax[1].axis('off');


Total variation

$$ \begin{align*} & \min_{X} \sum_{i=1}^{m-1}\sum_{j=1}^{n-1} \left\|\left[ \begin{array}{} X_{i+1,j} - X_{ij} \\ X_{i, j+1} - X_{ij}\end{array} \right] \right\|_2\\ \text{s.t. } & X_{ij} = X^{known}_{ij}, \; (i, j) \in \mathcal{I} \end{align*} $$
  • $\mathcal{I}$ - множество индексов известных значений
  • Аппроксимация градиента не в квадрате!

In [4]:
# Define variables and constraints
variables = []
constraints = []
for i in range(colors):
#     Slice of image tensor for the i-th color
    U = cvx.Variable(shape=(rows, cols))
#     Append to the list of variables
    variables.append(U)
#     Append constraints: known pixels in the given image 
#     has to be equal to the pixels in reconstructed image
    constraints.append(cvx.multiply(Known[:, :, i], U) == \
                       cvx.multiply(Known[:, :, i], Ucorr[:, :, i]))

In [6]:
# Create minimization problem with created constraints and solve it
prob = cvx.Problem(cvx.Minimize(cvx.tv(*variables)), constraints)
prob.solve(verbose=True, solver="SCS", max_iters=500)



Problem
  Name                   :                 
  Objective sense        : min             
  Type                   : CONIC (conic optimization problem)
  Constraints            : 2712552         
  Cones                  : 270936          
  Scalar variables       : 2983488         
  Matrix variables       : 0               
  Integer variables      : 0               

Optimizer started.
GP based matrix reordering started.
GP based matrix reordering terminated.
Problem
  Name                   :                 
  Objective sense        : min             
  Type                   : CONIC (conic optimization problem)
  Constraints            : 2712552         
  Cones                  : 270936          
  Scalar variables       : 2983488         
  Matrix variables       : 0               
  Integer variables      : 0               

Optimizer  - threads                : 4               
Optimizer  - solved problem         : the primal      
Optimizer  - Constraints            : 907491
Optimizer  - Cones                  : 263391
Optimizer  - Scalar variables       : 1771571           conic                  : 1771571         
Optimizer  - Semi-definite variables: 0                 scalarized             : 0               
Factor     - setup time             : 42.83             dense det. time        : 26.10           
Factor     - ML order time          : 3.04              GP order time          : 5.99            
Factor     - nonzeros before factor : 1.05e+07          after factor           : 2.95e+07        
Factor     - dense dim.             : 2                 flops                  : 4.37e+09        
Factor     - GP saved nzs           : 3.31e+06          GP saved flops         : 1.01e+09        
ITE PFEAS    DFEAS    GFEAS    PRSTATUS   POBJ              DOBJ              MU       TIME  
0   8.9e+01  1.0e+00  6.6e+04  0.00e+00   1.268131397e+06   2.145713969e+05   1.0e+00  51.04 
1   1.3e+01  1.5e-01  3.8e+03  -9.94e-01  7.099061985e+06   6.084623467e+06   1.5e-01  54.11 
2   1.1e+01  1.3e-01  3.4e+03  -5.22e-01  7.389445044e+06   6.469959515e+06   1.3e-01  56.10 
3   9.5e+00  1.1e-01  3.3e+03  -1.50e-01  7.315643304e+06   6.550712713e+06   1.1e-01  58.32 
4   3.6e+00  4.1e-02  2.9e+03  3.76e-01   5.789036195e+06   5.553574496e+06   4.1e-02  60.74 
5   1.1e+00  1.3e-02  1.7e+03  1.35e+00   4.788140929e+06   4.727188686e+06   1.3e-02  63.07 
6   2.2e-01  2.5e-03  7.4e+02  1.07e+00   4.654912002e+06   4.643061184e+06   2.5e-03  65.40 
7   5.9e-02  6.6e-04  3.8e+02  1.02e+00   4.620309065e+06   4.617223131e+06   6.6e-04  67.98 
8   9.5e-03  1.1e-04  1.5e+02  1.01e+00   4.607993590e+06   4.607496035e+06   1.1e-04  70.27 
9   1.6e-03  1.8e-05  6.3e+01  1.00e+00   4.605855470e+06   4.605771179e+06   1.8e-05  72.69 
10  2.1e-04  2.4e-06  2.3e+01  1.00e+00   4.605480138e+06   4.605469140e+06   2.4e-06  75.26 
11  1.1e-04  1.2e-06  1.6e+01  1.00e+00   4.605452313e+06   4.605446770e+06   1.2e-06  77.70 
12  1.9e-05  2.1e-07  6.9e+00  1.00e+00   4.605429123e+06   4.605428130e+06   2.1e-07  80.09 
13  2.5e-06  2.8e-08  2.5e+00  1.00e+00   4.605424743e+06   4.605424611e+06   2.8e-08  82.45 
14  1.2e-06  1.4e-08  1.7e+00  1.00e+00   4.605424387e+06   4.605424325e+06   1.3e-08  84.67 
15  6.2e-07  7.2e-09  1.2e+00  1.00e+00   4.605424235e+06   4.605424203e+06   6.9e-09  89.26 
16  2.7e-07  3.2e-09  8.2e-01  1.00e+00   4.605424143e+06   4.605424129e+06   3.0e-09  92.86 
17  1.5e-07  7.2e-10  3.9e-01  1.00e+00   4.605424087e+06   4.605424084e+06   7.0e-10  100.06
Optimizer terminated. Time: 102.12  


Interior-point solution summary
  Problem status  : PRIMAL_AND_DUAL_FEASIBLE
  Solution status : OPTIMAL
  Primal.  obj: 4.6054240873e+06    nrm: 5e+02    Viol.  con: 3e-06    var: 0e+00    cones: 6e-07  
  Dual.    obj: 4.6054240841e+06    nrm: 3e+00    Viol.  con: 0e+00    var: 1e-08    cones: 2e-16  
Out[6]:
4605424.08734452

In [7]:
# Load variables values into array
rec_arr = np.zeros((rows, cols, colors), dtype=np.uint8)
for i in range(colors):
    rec_arr[:, :, i] = variables[i].value

In [8]:
fig, ax = plt.subplots(1, 2, figsize=(15, 10))
img_rec = Image.fromarray(rec_arr)
ax[0].imshow(img_rec);
ax[0].set_title("In-Painted image", fontsize=24)
ax[0].axis('off')

img_diff = Image.fromarray(np.abs(Uorig - rec_arr))
ax[1].imshow(img_diff);
ax[1].set_title("Difference image", fontsize=24)
ax[1].axis('off');


Звук


In [10]:
import scipy.io.wavfile as siowav
import IPython.display as ipd

# Load original track
sr, sound = siowav.read("./test_sound.wav")
print(sr, sound.shape, type(sound[0]))
start = 500000
fin = 1000000
sound = sound.astype("float32")
# Cut and normalize original track
sound = sound[start:fin] / np.max(sound[start:fin])
n = sound.shape[0]
print("Problem dimension = {}".format(n))
plt.plot(sound)
# plt.xscale("log")


44100 (7636608,) <class 'numpy.int16'>
Problem dimension = 500000
Out[10]:
[<matplotlib.lines.Line2D at 0x881757ac8>]

In [11]:
ipd.Audio(sound, rate=sr)


Out[11]:

In [12]:
# Create corrupted signal and normalize it 
corrupt_sound = sound + 0.1 * np.random.randn(sound.shape[0])
corrupt_sound = corrupt_sound / np.max(np.abs(corrupt_sound))
plt.plot(corrupt_sound)
# plt.xscale("log")


Out[12]:
[<matplotlib.lines.Line2D at 0x854526780>]

In [13]:
ipd.Audio(corrupt_sound, rate=sr)


Out[13]:

$\ell_1$ регуляризация

$$ \begin{align*} & \min_{x} \|x - x_c\|_2 + \lambda \sum_{i=1}^{n-1} |x_i - x_{i+1}|\\ \text{s.t. } & -1 \leq x_i \leq 1 \end{align*} $$

In [24]:
# Create variable for reconstructed signal 
x_rec = cvx.Variable(corrupt_sound.shape[0])
# Create ac problem and solve it, \lambda = 1 
prob = cvx.Problem(cvx.Minimize(cvx.norm(x_rec - corrupt_sound) + cvx.tv(x_rec)),
                   [x_rec >= -1, x_rec <= 1])
prob.solve(verbose=True, solver=cvx.SCS, max_iters=500)


----------------------------------------------------------------------------
	SCS v2.0.2 - Splitting Conic Solver
	(c) Brendan O'Donoghue, Stanford University, 2012-2017
----------------------------------------------------------------------------
Lin-sys: sparse-indirect, nnz in A = 4499995, CG tol ~ 1/iter^(2.00)
eps = 1.00e-05, alpha = 1.50, max_iters = 500, normalize = 1, scale = 1.00
acceleration_lookback = 20, rho_x = 1.00e-03
Variables n = 1000000, constraints m = 2499999
Cones:	linear vars: 1999998
	soc vars: 500001, soc blks: 1
Setup time: 2.44e-01s
----------------------------------------------------------------------------
 Iter | pri res | dua res | rel gap | pri obj | dua obj | kap/tau | time (s)
----------------------------------------------------------------------------
     0| 2.29e+22  1.73e+22  1.00e+00 -7.66e+27  1.07e+26  3.65e+27  2.78e+00 
   100| 2.42e-02  3.71e-02  8.94e-02 -3.57e+02 -4.28e+02  1.32e-09  1.08e+02 
   200| 3.19e-03  4.95e-03  1.14e-01  1.28e+02  1.02e+02  1.09e-10  1.81e+02 
   300| 2.24e-03  3.32e-03  1.61e-01  8.82e+01  6.36e+01  2.20e-10  2.87e+02 
   400| 1.69e-03  2.64e-03  9.91e-02  1.01e+02  8.25e+01  1.31e-11  4.42e+02 
   500| 1.37e-03  2.08e-03  1.57e-01  9.67e+01  7.03e+01  1.15e-11  6.37e+02 
----------------------------------------------------------------------------
Status: Solved/Inaccurate
Hit max_iters, solution may be inaccurate
Timing: Solve time: 6.37e+02s
	Lin-sys: avg # CG iterations: 4.58, avg solve time: 2.46e-01s
	Cones: avg projection time: 3.38e-03s
	Acceleration: avg step time: 8.68e-01s
----------------------------------------------------------------------------
Error metrics:
dist(s, K) = 1.3412e-15, dist(y, K*) = 0.0000e+00, s'y/|s||y| = 1.7805e-07
primal res: |Ax + s - b|_2 / (1 + |b|_2) = 1.3705e-03
dual res:   |A'y + c|_2 / (1 + |c|_2) = 2.0773e-03
rel gap:    |c'x + b'y| / (1 + |c'x| + |b'y|) = 1.5667e-01
----------------------------------------------------------------------------
c'x = 96.6670, -b'y = 70.3445
============================================================================
Out[24]:
96.66698808735805

In [25]:
plt.plot(x_rec.value)


Out[25]:
[<matplotlib.lines.Line2D at 0x8795f0b00>]

In [26]:
ipd.Audio(x_rec.value, rate=sr)


Out[26]:

$\ell_2$ регуляризация

$$ \begin{align*} & \min_{x} \|x - x_c\|_2 + \lambda \sum_{i=1}^{n-1} (x_i - x_{i+1})^2\\ \text{s.t. } & -1 \leq x_i \leq 1 \end{align*} $$

In [27]:
# The same as in the previous cell, but with \ell_2 regularization 
x_rec = cvx.Variable(n)
prob = cvx.Problem(cvx.Minimize(cvx.norm(x_rec - corrupt_sound) + 
                                cvx.square(cvx.norm(x_rec[1:n] - x_rec[0:n-1]))),
                   [x_rec >= -1, x_rec <= 1])
prob.solve(verbose=True, solver="SCS", max_iters=500)


----------------------------------------------------------------------------
	SCS v2.0.2 - Splitting Conic Solver
	(c) Brendan O'Donoghue, Stanford University, 2012-2017
----------------------------------------------------------------------------
Lin-sys: sparse-indirect, nnz in A = 2500003, CG tol ~ 1/iter^(2.00)
eps = 1.00e-05, alpha = 1.50, max_iters = 500, normalize = 1, scale = 1.00
acceleration_lookback = 20, rho_x = 1.00e-03
Variables n = 500003, constraints m = 2000004
Cones:	linear vars: 1000000
	soc vars: 1000004, soc blks: 3
Setup time: 1.51e-01s
----------------------------------------------------------------------------
 Iter | pri res | dua res | rel gap | pri obj | dua obj | kap/tau | time (s)
----------------------------------------------------------------------------
     0| 1.60e+22  8.94e+21  1.00e+00 -6.75e+24  4.49e+23  5.02e+24  1.28e+00 
   100| 6.41e-03  5.96e-03  4.71e-04  7.58e+01  7.57e+01  1.67e-13  5.49e+01 
   200| 3.07e-04  2.71e-04  8.68e-04  7.56e+01  7.55e+01  1.53e-13  1.00e+02 
   300| 2.16e-05  1.76e-05  7.86e-05  7.55e+01  7.55e+01  2.86e-13  1.50e+02 
   380| 2.16e-06  2.41e-06  4.29e-06  7.55e+01  7.55e+01  4.96e-13  1.86e+02 
----------------------------------------------------------------------------
Status: Solved
Timing: Solve time: 1.86e+02s
	Lin-sys: avg # CG iterations: 3.01, avg solve time: 8.80e-02s
	Cones: avg projection time: 3.56e-03s
	Acceleration: avg step time: 3.14e-01s
----------------------------------------------------------------------------
Error metrics:
dist(s, K) = 1.9256e-10, dist(y, K*) = 2.8131e-12, s'y/|s||y| = 2.5936e-13
primal res: |Ax + s - b|_2 / (1 + |b|_2) = 2.1553e-06
dual res:   |A'y + c|_2 / (1 + |c|_2) = 2.4068e-06
rel gap:    |c'x + b'y| / (1 + |c'x| + |b'y|) = 4.2939e-06
----------------------------------------------------------------------------
c'x = 75.4811, -b'y = 75.4817
============================================================================
Out[27]:
75.48107902807476

In [28]:
plt.plot(x_rec.value)


Out[28]:
[<matplotlib.lines.Line2D at 0x84b3c1828>]

In [29]:
ipd.Audio(x_rec.value, rate=sr)


Out[29]:

Выводы

  • Различная регуляризация даёт решение с различными свойствами
  • Разница между $\ell_1$ и $\ell_2$ регуляризацией
  • Способы выбора парамера $\lambda$

Управление линейной динамической системой

  • Дано $x_1, u_1, u_2, \ldots$
  • Найти $x_2, x_3, \ldots$ рекурсивно
$$ x_{t+1} = Ax_t + Bu_t $$
  • Пример: движение материальной точки с трением на плоскости
$$ m \frac{v_{t+1} - v_t}{\tau} \approx -\eta v_t + u_t \quad \frac{p_{t+1} - p_t}{\tau} \approx v_{t}, $$

где $v_{t} = (v^x_t, v^y_t)$ и $p_t = (p^x_t, p^y_t)$

Приближённо имеем следующую систему

$$ v_{t+1} = \left(1 - \frac{\tau}{m}\eta \right)v_t + \frac{\tau}{m}u_t \quad p_{t+1} = p_t + \tau v_t $$

Для записи в стандартной форме

$$ x_{t} = \begin{bmatrix} p^x_t \\ p^y_t \\ v^x_t \\ v^y_t \end{bmatrix} \quad A = \begin{bmatrix} 1 & 0 & \tau & 0 \\ 0 & 1 & 0 & \tau \\ 0 & 0 & 1 - \frac{\tau}{m}\eta & 0\\ 0 & 0 & 0 & 1 - \frac{\tau}{m}\eta \end{bmatrix} \quad B = \begin{bmatrix} 0 & 0 \\ 0 & 0 \\ \frac{\tau}{m} & 0 \\ 0 & \frac{\tau}{m} \end{bmatrix} $$

Управление

  • Дано начальное состояние системы $x_1$
  • Найти такие $u_1, \ldots, u_T$, чтобы была достигнута некоторая цель
    • было достигнуто заданное финальное состояние $x_{target}$
    • было затрачено минимум "энергии"

In [19]:
m = 1
eta = 0.1
tau = 0.01
T = 100
A = np.array([[1, 0, tau, 0], 
              [0, 1, 0, tau], 
              [0, 0, 1 - eta * tau / m, 0], 
              [0, 0, 0, 1 - eta * tau / m]])
B = np.array([[0, 0],
             [0, 0],
             [tau / m, 0],
             [0, tau / m]])

In [20]:
# Initialize original and target states of the defined system
x0 = np.array([5, 5, 20, -5])
x_target = np.array([0, 7, 0, 0])

In [21]:
# Create variables to store evolution of the system and 
# all control vectors
x = cvx.Variable((4, T + 1))
u = cvx.Variable((2, T))

states = []
for t in range(T):
#     Create objective function and constraints in every time step
    cost = cvx.sum_squares(u[:, t])
    constr = [x[:, t+1] == A*x[:, t] + B*u[:, t]]
    states.append(cvx.Problem(cvx.Minimize(cost), constr))
# Sum-up all problems: sum objectives and concatenate constraints 
prob0 = sum(states)
# Append original state and target state
complete_constr = prob0.constraints + [x[:,0] == x0, x[:, T] == x_target]
prob = cvx.Problem(prob0.objective, complete_constr)
prob.solve(verbose=True)


-----------------------------------------------------------------
           OSQP v0.5.0  -  Operator Splitting QP Solver
              (c) Bartolomeo Stellato,  Goran Banjac
        University of Oxford  -  Stanford University 2018
-----------------------------------------------------------------
problem:  variables n = 804, constraints m = 608
          nnz(P) + nnz(A) = 1808
settings: linear system solver = qdldl,
          eps_abs = 1.0e-03, eps_rel = 1.0e-03,
          eps_prim_inf = 1.0e-04, eps_dual_inf = 1.0e-04,
          rho = 1.00e-01 (adaptive),
          sigma = 1.00e-06, alpha = 1.60, max_iter = 4000
          check_termination: on (interval 25),
          scaling: on, scaled_termination: off
          warm start: on, polish: on

iter   objective    pri res    dua res    rho        time
   1   0.0000e+00   2.00e+01   2.00e+03   1.00e-01   1.11e-03s
  75   3.3624e+05   2.52e-06   9.20e-10   9.18e+01   1.36e-03s

status:               solved
solution polish:      unsuccessful
number of iterations: 75
optimal objective:    336238.1650
run time:             2.15e-03s
optimal rho estimate: 3.16e+03

Out[21]:
336238.1650251802

In [22]:
plt.plot(x.value[0, :], x.value[1, :])
plt.grid(True)
plt.xticks(fontsize=24)
plt.yticks(fontsize=24)
plt.xlabel(r"$x$", fontsize=24)
plt.ylabel(r"$y$", fontsize=24)


Out[22]:
<matplotlib.text.Text at 0x81fb94390>

In [23]:
plt.plot(u.value[0, :], label=r"$u_x$")
plt.plot(u.value[1, :], label=r"$u_y$")
plt.legend(fontsize=24)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.xlabel("Time, $t$", fontsize=24)


Out[23]:
<matplotlib.text.Text at 0x81fc6d410>

Задача rendezvous

  • Дано два тела, каждый со своей динамикой, то есть матрицами $A$ и $B$, начальными положениями и скоростями
  • Найти $u_i$ и $v_i$ для каждого тела, так чтобы их конечные состояния были одинаковыми и была потрачено суммарно минимум энергии

Формальная постановка

$$ \begin{align*} & \min \sum_{i=1}^T \|u_i\|_2^2 + \|v_i\|_2^2 \\ \text{s.t. } & x_{t+1} = Ax_t + Bu_t, \; t = 1,\ldots,T-1\\ & z_{t+1} = Cz_t + Dv_t, \; t = 1,\ldots,T-1\\ & x_T = z_T \end{align*} $$

In [24]:
m1 = 1
eta1 = 0.1
m2 = 1
eta2 = 1
tau = 0.01
T = 100
A = np.array([[1, 0, tau, 0], 
              [0, 1, 0, tau], 
              [0, 0, 1 - eta1 * tau / m1, 0], 
              [0, 0, 0, 1 - eta1 * tau / m1]])
B = np.array([[0, 0],
             [0, 0],
             [tau / m1, 0],
             [0, tau / m1]])

C = np.array([[1, 0, tau, 0], 
              [0, 1, 0, tau], 
              [0, 0, 1 - eta2 * tau / m2, 0], 
              [0, 0, 0, 1 - eta2 * tau / m2]])
D = np.array([[0, 0],
             [0, 0],
             [tau / m2, 0],
             [0, tau / m2]])

In [25]:
x0 = np.array([0, 0, 1, 1])
z0 = np.array([10, 0, -1, 1])

In [26]:
x = cvx.Variable((4, T + 1))
z = cvx.Variable((4, T + 1))
u = cvx.Variable((2, T))
v = cvx.Variable((2, T))

states = []
for t in range(T):
    cost = cvx.sum_squares(u[:, t]) + cvx.sum_squares(v[:, t])
    constr = [x[:, t+1] == A*x[:, t] + B*u[:, t], z[:, t+1] == C*z[:, t] + D*v[:, t]]
    states.append(cvx.Problem(cvx.Minimize(cost), constr))
prob0 = sum(states)
complete_constr = prob0.constraints + [x[:, T] == z[:, T], x[:, 0] == x0, z[:, 0] == z0]
prob = cvx.Problem(prob0.objective, complete_constr)
prob.solve(verbose=True)


-----------------------------------------------------------------
           OSQP v0.5.0  -  Operator Splitting QP Solver
              (c) Bartolomeo Stellato,  Goran Banjac
        University of Oxford  -  Stanford University 2018
-----------------------------------------------------------------
problem:  variables n = 1608, constraints m = 1212
          nnz(P) + nnz(A) = 3616
settings: linear system solver = qdldl,
          eps_abs = 1.0e-03, eps_rel = 1.0e-03,
          eps_prim_inf = 1.0e-04, eps_dual_inf = 1.0e-04,
          rho = 1.00e-01 (adaptive),
          sigma = 1.00e-06, alpha = 1.60, max_iter = 4000
          check_termination: on (interval 25),
          scaling: on, scaled_termination: off
          warm start: on, polish: on

iter   objective    pri res    dua res    rho        time
   1   0.0000e+00   1.00e+01   1.00e+03   1.00e-01   4.52e-03s
  75   5.0981e+04   1.25e-05   3.33e-09   5.79e+01   5.17e-03s

status:               solved
solution polish:      unsuccessful
number of iterations: 75
optimal objective:    50981.2594
run time:             6.07e-03s
optimal rho estimate: 5.12e+03

Out[26]:
50981.25938336443

In [27]:
plt.plot(x.value[0, :], x.value[1, :], label="1")
plt.plot(z.value[0, :], z.value[1, :], label="2")
plt.legend(fontsize=24)
plt.xlabel(r"$x$", fontsize=24)
plt.ylabel(r"$y$", fontsize=24)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)


Out[27]:
(array([-0.1,  0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8]),
 <a list of 10 Text yticklabel objects>)

История и современное состояние

  • Подобные задачи начали решать для планирования полётов спутников и космических кораблей
  • В СССР была сильная школа оптимального управления, см принцип максимума Понтрягина и другие работы
  • Сейчас SpaceX использует CVXGEN для точной посадки своих ракет, ссылка
  • CVXGEN позволяет генерировать и очень быстро решать задачи квадратичного программирования небольшого размера, подробнее тут
  • Это достигается за счёт автоматической генерации кода для решения задачи и его компиляции под конкретную систему

Экстремальный эллипсоид (The Löwner-John ellipsoid)

Задача: дан набор точек на плоскости $x_i \in \mathbb{R}^2$, необходимо найти эллипс минимальной площади, такой что все точки $x_i$ лежат внутри него.

Приложения

  • Телекоммуникации
  • Оценка ковариационной матрицы

Параметры:

эллипс можно задать аффинным преобразованием

$$ \{x \; | \; \| x \|^2_2 \leq 1\} \to \{ u \;| \; \|u\|^2_2 \leq 1, \; u = Ax + b\} $$

Тогда площадь увеличивается в $\det (A^{-1})$ раз.

  • Детерминант не является выпуклой/вогнутой функцией
  • $\log\det(A^{-1}) = -\log\det(A)$ - выпуклая функция при $A \in \mathbb{S}^n_{++}$

Задача минимизации

$$ \begin{align*} & \min_{A, b} -\log\det(A)\\ \text{s.t. } & \|Ax_i + b\| \leq 1\\ & A \succ 0 \end{align*} $$

In [2]:
num_points = 50
n = 2
X = 2 * np.random.randn(n, num_points)
plt.figure(figsize=(10, 7))
plt.scatter(X[0, :], X[1, :], s=90)


Out[2]:
<matplotlib.collections.PathCollection at 0x125740b70>

In [3]:
# Create variables: positive semi-definite matrix A and vector b
A = cvx.Variable((n,n), PSD=True)
b = cvx.Variable((n,))
prob = cvx.Problem(cvx.Minimize(-cvx.log_det(A)), 
   [cvx.norm(A * X[:, i] + b) <= 1 for i in range(num_points)])

In [4]:
prob.solve(verbose=True)


----------------------------------------------------------------------------
	SCS v1.2.6 - Splitting Conic Solver
	(c) Brendan O'Donoghue, Stanford University, 2012-2016
----------------------------------------------------------------------------
Lin-sys: sparse-indirect, nnz in A = 447, CG tol ~ 1/iter^(2.00)
eps = 1.00e-03, alpha = 1.50, max_iters = 2500, normalize = 1, scale = 1.00
Variables n = 73, constraints m = 237
Cones:	primal zero / dual free vars: 15
	linear vars: 50
	soc vars: 150, soc blks: 50
	sd vars: 16, sd blks: 3
	exp vars: 6, dual exp vars: 0
Setup time: 1.37e-02s
----------------------------------------------------------------------------
 Iter | pri res | dua res | rel gap | pri obj | dua obj | kap/tau | time (s)
----------------------------------------------------------------------------
     0| 2.02e+00  1.74e+00  9.19e-01 -8.59e+00  2.75e+00  5.83e-15  8.31e-02 
   100| 1.54e-02  1.81e-03  3.27e-03  2.80e+00  2.82e+00  1.00e-15  9.09e-02 
   200| 6.28e-03  1.17e-03  1.28e-03  2.90e+00  2.91e+00  1.13e-15  9.34e-02 
   300| 3.87e-03  4.07e-04  6.99e-04  2.93e+00  2.94e+00  1.20e-15  9.59e-02 
   400| 2.84e-03  2.30e-04  4.12e-04  2.95e+00  2.95e+00  1.25e-15  9.85e-02 
   500| 2.37e-03  1.43e-04  2.54e-04  2.95e+00  2.95e+00  1.27e-15  1.01e-01 
   600| 2.17e-03  9.23e-05  1.62e-04  2.95e+00  2.96e+00  1.29e-15  1.03e-01 
   700| 2.08e-03  6.19e-05  1.08e-04  2.96e+00  2.96e+00  1.30e-15  1.06e-01 
   800| 2.04e-03  4.36e-05  7.59e-05  2.96e+00  2.96e+00  1.31e-15  1.09e-01 
   900| 2.03e-03  3.26e-05  5.63e-05  2.96e+00  2.96e+00  1.31e-15  1.11e-01 
  1000| 2.01e-03  2.60e-05  4.44e-05  2.96e+00  2.96e+00  1.32e-15  1.14e-01 
  1100| 2.01e-03  2.20e-05  3.71e-05  2.96e+00  2.96e+00  1.32e-15  1.16e-01 
  1200| 2.00e-03  1.95e-05  3.26e-05  2.96e+00  2.96e+00  1.32e-15  1.19e-01 
  1300| 1.99e-03  1.81e-05  2.99e-05  2.96e+00  2.96e+00  1.32e-15  1.21e-01 
  1400| 1.99e-03  1.72e-05  2.82e-05  2.97e+00  2.97e+00  1.33e-15  1.24e-01 
  1500| 7.53e-04  2.19e-04  2.32e-05  2.97e+00  2.97e+00  1.33e-15  1.26e-01 
----------------------------------------------------------------------------
Status: Solved
Timing: Solve time: 1.26e-01s
	Lin-sys: avg # CG iterations: 4.60, avg solve time: 8.84e-06s
	Cones: avg projection time: 7.13e-05s
----------------------------------------------------------------------------
Error metrics:
dist(s, K) = 5.8702e-15, dist(y, K*) = 1.8079e-09, s'y/|s||y| = 4.2087e-10
|Ax + s - b|_2 / (1 + |b|_2) = 7.5339e-04
|A'y + c|_2 / (1 + |c|_2) = 2.1919e-04
|c'x + b'y| / (1 + |c'x| + |b'y|) = 2.3216e-05
----------------------------------------------------------------------------
c'x = 2.9665, -b'y = 2.9666
============================================================================
Out[4]:
2.966477750359045

In [6]:
plt.figure(figsize=(10, 7))
plt.scatter(X[0, :], X[1, :])
phi = np.linspace(0, 2 * np.pi, num=100)
xy = np.vstack((np.cos(phi) - b.value[0], np.sin(phi) - b.value[1]))
ellips = np.linalg.solve(A.value, xy)
plt.plot(ellips[0, :], ellips[1, :])


Out[6]:
[<matplotlib.lines.Line2D at 0x12ed6eb00>]

Метод опорных векторов (SVM)

  • Дана выборка $(X, y)$, где $X \in \mathbb{R}^{m \times n}$ и $y_i \in \{ +1, -1\}$
  • Необходимо построить линейный классификатор вида $\hat{y}_i = \mathrm{sgn}(w^{\top}x_i + b)$, где $w \in \mathbb{R}^n$ и $b \in \mathbb{R}$
  • Параметры $w$ и $b$ найдём с помощью следующей задачи оптимизации
$$ \min_{w, b} \frac{1}{m} \sum_{i=1}^m \max(0, 1 - y_i(w^{\top}x_i + b)) + \gamma \|w\|_{*} $$

In [61]:
import sklearn.datasets as skldata
import sklearn.model_selection as skms

np.random.seed(8)
n = 2
m = 2000
X, y = skldata.make_blobs(n_features=n, n_samples=m, centers=2, cluster_std=4)
y = 2 * y - 1
X_train, X_test, y_train, y_test = skms.train_test_split(X, y, test_size=0.5)
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train)
m_train = m // 2
m_test = m_train



In [62]:
beta = cvx.Variable(n)
v = cvx.Variable()
loss = cvx.sum(cvx.pos(1 - cvx.multiply(y_train, X_train*beta + v)))
reg = cvx.norm(beta, 2)
lambd = cvx.Parameter(nonneg=True, value=1)
prob = cvx.Problem(cvx.Minimize(loss / m_train + lambd * reg))
prob.solve(verbose=True)


ECOS 2.0.4 - (C) embotech GmbH, Zurich Switzerland, 2012-15. Web: www.embotech.com/ECOS

It     pcost       dcost      gap   pres   dres    k/t    mu     step   sigma     IR    |   BT
 0  +9.464e-02  +4.993e+02  +4e+03  8e-01  9e+00  1e+00  2e+00    ---    ---    1  1  - |  -  - 
 1  +5.030e+02  +1.474e+04  +1e+03  2e+01  8e+02  2e+02  5e-01  0.7543  5e-02   1  1  1 |  0  0
 2  +2.581e+02  +5.319e+03  +8e+02  6e+00  4e+02  8e-01  4e-01  0.2965  4e-02   1  1  1 |  0  0
 3  +1.618e+01  +5.792e+02  +5e+02  6e-01  6e+01  3e+01  2e-01  0.5494  3e-01   1  1  1 |  0  0
 4  +1.339e+01  +1.409e+02  +2e+02  1e-01  3e+01  2e-01  9e-02  0.6793  7e-02   2  1  1 |  0  0
 5  +3.379e+00  +2.125e+01  +1e+02  2e-02  3e+00  4e-01  6e-02  0.5781  5e-01   1  1  1 |  0  0
 6  +1.385e+00  +3.447e+00  +3e+01  2e-03  2e-01  1e-02  2e-02  0.7952  9e-02   1  1  1 |  0  0
 7  +8.774e-01  +1.759e+00  +3e+01  7e-04  6e-02  8e-03  1e-02  0.4331  5e-01   1  1  1 |  0  0
 8  +6.065e-01  +7.095e-01  +6e+00  6e-05  4e-03  1e-03  3e-03  0.8473  6e-02   1  1  1 |  0  0
 9  +5.765e-01  +6.011e-01  +1e+00  1e-05  1e-03  3e-04  7e-04  0.7647  4e-02   1  1  1 |  0  0
10  +5.714e-01  +5.813e-01  +6e-01  5e-06  4e-04  1e-04  3e-04  0.6558  1e-01   1  1  1 |  0  0
11  +5.694e-01  +5.737e-01  +3e-01  2e-06  2e-04  4e-05  1e-04  0.6415  1e-01   1  1  1 |  0  0
12  +5.684e-01  +5.702e-01  +1e-01  1e-06  7e-05  1e-05  6e-05  0.6605  1e-01   1  1  1 |  0  0
13  +5.679e-01  +5.685e-01  +4e-02  3e-07  2e-05  4e-06  2e-05  0.6771  3e-02   1  1  1 |  0  0
14  +5.677e-01  +5.678e-01  +7e-03  6e-08  5e-06  7e-07  4e-06  0.8310  3e-02   2  1  1 |  0  0
15  +5.676e-01  +5.676e-01  +1e-03  8e-09  6e-07  3e-08  5e-07  0.9644  1e-01   1  1  1 |  0  0
16  +5.676e-01  +5.676e-01  +5e-05  4e-10  3e-08  1e-09  2e-08  0.9578  6e-03   1  1  1 |  0  0
17  +5.676e-01  +5.676e-01  +6e-07  5e-12  4e-10  2e-11  3e-10  0.9873  7e-04   2  1  1 |  0  0
18  +5.676e-01  +5.676e-01  +7e-09  6e-14  4e-12  2e-13  3e-12  0.9890  1e-04   1  1  1 |  0  0

OPTIMAL (within feastol=4.3e-12, reltol=1.2e-08, abstol=6.9e-09).
Runtime: 0.014695 seconds.

Out[62]:
0.5676182480511398

In [63]:
x = np.linspace(min(X_train[:, 0]), max(X_train[:, 0]))
plt.plot(x, (-v.value - beta.value[0] * x) / beta.value[1])
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train)


Out[63]:
<matplotlib.collections.PathCollection at 0x821c9ce50>

Зависимость от $\lambda$


In [42]:
import sklearn.datasets as skdata 
import sklearn.preprocessing as skprep

X, y = skdata.load_breast_cancer(return_X_y=True)
X = skprep.StandardScaler().fit_transform(X)

y = 2 * y - 1
print("Data shape = {}".format(X.shape))
X_train, X_test, y_train, y_test = skms.train_test_split(X, y, test_size=0.3)
m_train, n = X_train.shape
m_test = X_test.shape[0]
# Redefine the problem
beta = cvx.Variable(n)
v = cvx.Variable()
loss = cvx.sum(cvx.pos(1 - cvx.multiply(y_train, X_train*beta + v)))
reg = cvx.norm(beta, 1)
lambd = cvx.Parameter(nonneg=True)
prob = cvx.Problem(cvx.Minimize(loss / m_train + lambd * reg))


Data shape = (569, 30)

In [52]:
TRIALS = 100
train_error = np.zeros(TRIALS)
test_error = np.zeros(TRIALS)
lambda_vals = np.logspace(-4, -1, TRIALS)
beta_vals = []
for i in range(TRIALS):
    lambd.value = lambda_vals[i]
    prob.solve(solver="ECOS")
    train_error[i] = (y_train != np.sign(X_train.dot(beta.value) + v.value)).sum() * 1.0 / m_train
    test_error[i] = (y_test != np.sign(X_test.dot(beta.value) + v.value)).sum() * 1.0 / m_test
    beta_vals.append(beta.value)

In [53]:
plt.figure(figsize=(10, 7))
plt.plot(lambda_vals, train_error, label="Train error")
plt.plot(lambda_vals, test_error, label="Test error")
plt.xscale('log')
plt.legend(loc='best', fontsize=24)
plt.xlabel(r"$\lambda$", fontsize=24)
plt.xticks(fontsize=24)
_ = plt.yticks(fontsize=24)



In [54]:
plt.figure(figsize=(10, 7))
for i in range(n):
    plt.plot(lambda_vals, [wi[i] for wi in beta_vals])
plt.xlabel(r"$\lambda$", fontsize=24)
plt.ylabel(r"$w_i$", fontsize=24)
plt.xscale("log")
plt.xticks(fontsize=24)
_ = plt.yticks(fontsize=24)


Что осталось за кадром

  • Портфельная теория Марковица
  • Поиск равновесия в транспортных сетях
  • Регрессия
  • Задачи из биологии
  • Релаксации комбинаторных задач
  • Распределение энергии в сетях
  • Составление расписания потребления мощности
  • Задачи из статистики
  • Инженерные задачи про формирование массива антенн, про распределение нагрузок в механической конструкции, etc