Notas para contenedor de docker:

Comando de docker para ejecución de la nota de forma local:

nota: cambiar <ruta a mi directorio> por la ruta de directorio que se desea mapear a /datos dentro del contenedor de docker.

docker run --rm -v <ruta a mi directorio>:/datos --name jupyterlab_numerical -p 8888:8888 -d palmoreck/jupyterlab_numerical:1.1.0

password para jupyterlab: qwerty

Detener el contenedor de docker:

docker stop jupyterlab_numerical

Documentación de la imagen de docker palmoreck/jupyterlab_numerical:1.1.0 en liga.


Nota generada a partir de liga1, liga2.

Dando click en: se ejecuta la nota de forma interactiva.


In [1]:
!pip3 install --user -q cvxpy


  WARNING: The scripts futurize and pasteurize are installed in '/home/miuser/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
WARNING: You are using pip version 19.3.1; however, version 20.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

En esta nota se consideran resolver problemas de optimización con restricciones lineales de igualdad de la forma:

$$\min f_o(x)$$
$$\text{sujeto a:} Ax=b$$

con variable de optimización $x \in \mathbb{R}^{n}$ y $A \in \mathbb{R}^{p \times n}, b \in \mathbb{R}^p$ conocidos.

Se asume lo siguiente:

  • $f:\mathbb{R}^n \rightarrow \mathbb{R}$ convexa y $\mathcal{C}^2(\text{dom}f_o)$.

  • $rank(A) = p < n$: tenemos menos restricciones que variables y los renglones de $A$ son linealmente independientes.

  • Existe un punto óptimo $x^*$ por lo que el problema tiene solución y el valor óptimo se denota por $p^* = f_o(x^*) = \inf f_o(x)$.

  • Los puntos iniciales $x^{(0)}$ de los métodos iterativos están en $\text{dom}f_o$ y los conjuntos $f_o(x^{(0)})$-subnivel son conjuntos cerrados.

Método de Newton aplicado a las condiciones de Karush-Kuhn-Tucker (KKT) de optimalidad

Algoritmo de Newton con punto inicial factible para problemas de optimización con restricciones de igualdad lineales

Dados un punto inicial $x$ en $\text{dom}f_o$ con $Ax=b$ y una tolerancia $\epsilon >0$.

Repetir el siguiente bloque para $k=0,1,2,...$

  1. Calcular la dirección de descenso de Newton $\Delta x_{\text{nt}}$ y el decremento de Newton al cuadrado: $\lambda^2 (x)$.
  2. Criterio de paro: finalizar el método si $\frac{\lambda^2(x)}{2} \leq \epsilon$.
  3. Búsqueda de línea. Elegir un tamaño de paso $t > 0$ (usar el cálculo de $\lambda^2 (x)$ del paso anterior).
  4. Hacer la actualización: $x = x + t\Delta x_{\text{nt}}$.

hasta convergencia (satisfacer criterio de paro).

Se comparan los resultados del paquete cvxpy con los obtenidos en la implementación hecha por el prof en algoritmos/Python, en específico algoritmos/Python/algorithms_for_ceco.py para problemas tipo CECO (Constrained Equality Convex Optimization)


In [1]:
import os

In [2]:
cur_directory = os.getcwd()

In [3]:
dir_alg_python = '/algoritmos/Python'

In [4]:
os.chdir(cur_directory + dir_alg_python)

In [5]:
import math

import numpy as np

from algorithms_for_ceco import Newtons_method_feasible_init_point
from line_search import line_search_for_residual_by_backtracking
from utils import compute_error

Primer ejemplo

$$\displaystyle \min_{x \in \mathbb{R}^2} \quad x_1^2 + 2x_1x_2 + x_2^2-2x_2$$
$$\text{sujeto a: } x_1 = 0$$

In [6]:
fo = lambda x: x[0]**2 + 2*x[0]*x[1]+x[1]**2-2*x[1]

In [7]:
A = np.array([1,0],dtype=float)

In [8]:
b = np.array([0])

In [9]:
x_ast=np.array([0,1], dtype=float)

Punto inicial $x^{(0)}$ factible ($Ax^{(0)}=b$)


In [10]:
x_0 = np.array([0,-2],dtype=float)

In [11]:
tol=1e-8
tol_backtracking=1e-14
maxiter=50
p_ast=fo(x_ast)
[x,total_of_iterations,
 Err_plot,x_plot]=Newtons_method_feasible_init_point(fo,A, x_0,tol, 
                                                     tol_backtracking, x_ast, p_ast, maxiter)


I	Normgf 	Newton Decrement	Error x_ast	Error p_ast	line search	CondHf
0	7.21e+00	1.80e+01	3.00e+00	9.00e+00	---		9.00e+03
1	7.21e+00	1.15e-05	2.40e-03	5.76e-06	1.00e+00	9.00e+03
2	7.21e+00	7.55e-15	6.68e-08	4.44e-15	1.00e+00	9.00e+03
Error of x with respect to x_ast: 6.68e-08
Approximate solution: [4.33680869e-19 9.99999933e-01]

In [12]:
x


Out[12]:
array([4.33680869e-19, 9.99999933e-01])

In [13]:
total_of_iterations


Out[13]:
3

In [14]:
x_plot.shape


Out[14]:
(2, 3)

In [15]:
x_plot


Out[15]:
array([[ 0.00000000e+00,  0.00000000e+00,  4.33680869e-19],
       [-2.00000000e+00,  1.00239973e+00,  9.99999933e-01]])

In [16]:
compute_error(x_ast,x)


Out[16]:
6.682210107467057e-08

En el siguiente ejemplo si usamos el algoritmo anterior para punto inicial no factible ($Ax^{(0)} \neq b$) obsérvese que no funciona


In [17]:
x_0 = np.array([1,-2],dtype=float)

In [18]:
tol=1e-8
tol_backtracking=1e-14
maxiter=50
p_ast=fo(x_ast)
[x,total_of_iterations,
 Err_plot,x_plot] = Newtons_method_feasible_init_point(fo,A, x_0,tol, 
                                                       tol_backtracking, x_ast, p_ast, maxiter)


I	Normgf 	Newton Decrement	Error x_ast	Error p_ast	line search	CondHf
0	4.47e+00	8.01e+00	3.16e+00	6.00e+00	---		2.03e+07
1	4.47e+00	5.12e-06	1.41e+00	2.00e+00	1.00e+00	2.03e+07
2	4.47e+00	9.86e-16	1.41e+00	2.00e+00	1.00e+00	2.03e+07
Error of x with respect to x_ast: 1.41e+00
Approximate solution: [ 1.00000000e+00 -3.34455919e-08]

In [19]:
compute_error(x_ast,x)


Out[19]:
1.4142135860226999

Error relativo muy alto

Segundo ejemplo

$$\displaystyle \min_{x \in \mathbb{R}^2} \quad \frac{1}{2}(x_1^2 + x_2^2)$$
$$\text{sujeto a: } 2x_1 -x_2 = 5$$

In [20]:
fo = lambda x: 1/2*(x[0]**2  + x[1]**2)

In [21]:
A = np.array([2,-1],dtype=float)

In [22]:
b=5

In [23]:
x_ast = np.array([2,-1],dtype=float)

Punto inicial $x^{(0)}$ factible ($Ax^{(0)}=b$)


In [24]:
x_0 = np.array([0,-5],dtype=float)

In [25]:
tol=1e-8
tol_backtracking=1e-14
maxiter=50
p_ast=fo(x_ast)
[x,total_of_iterations,
 Err_plot,x_plot] = Newtons_method_feasible_init_point(fo,A, x_0,tol, 
                                                       tol_backtracking, x_ast, p_ast, maxiter)


I	Normgf 	Newton Decrement	Error x_ast	Error p_ast	line search	CondHf
0	5.00e+00	2.00e+01	2.00e+00	4.00e+00	---		1.00e+00
1	5.00e+00	3.94e-06	8.88e-04	7.89e-07	1.00e+00	1.00e+00
2	5.00e+00	3.56e-12	8.63e-07	7.45e-13	1.00e+00	1.00e+00
Error of x with respect to x_ast: 8.63e-07
Approximate solution: [ 1.99999914 -1.00000173]

In [26]:
x


Out[26]:
array([ 1.99999914, -1.00000173])

In [27]:
x_ast


Out[27]:
array([ 2., -1.])

In [28]:
compute_error(x_ast,x)


Out[28]:
8.631750624932266e-07

Tercer ejemplo

$$\displaystyle \min_{x \in \mathbb{R}^2} \quad x_1^2 + x_2^2$$
$$\text{sujeto a :} x_1+x_2 = 1$$

In [29]:
fo = lambda x: x[0]**2+x[1]**2

In [30]:
x_ast = np.array([.5,.5],dtype=float)

In [31]:
A = np.array([1,1],dtype=float)

In [32]:
b=1

Punto inicial $x^{(0)}$ factible ($Ax^{(0)}=b$)


In [33]:
x_0 = np.array([2,-1],dtype=float)

In [34]:
tol=1e-8
tol_backtracking=1e-14
maxiter=50
p_ast=fo(x_ast)
[x,total_of_iterations,
 Err_plot,x_plot] = Newtons_method_feasible_init_point(fo,A, x_0,tol, 
                                                       tol_backtracking, x_ast, p_ast, maxiter)


I	Normgf 	Newton Decrement	Error x_ast	Error p_ast	line search	CondHf
0	4.47e+00	9.00e+00	3.00e+00	9.00e+00	---		1.00e+00
1	4.47e+00	7.11e-08	2.67e-04	7.11e-08	1.00e+00	1.00e+00
2	4.47e+00	1.81e-16	2.46e-12	1.11e-16	1.00e+00	1.00e+00
Error of x with respect to x_ast: 2.46e-12
Approximate solution: [0.5 0.5]

In [35]:
compute_error(x_ast,x)


Out[35]:
2.4647506264441902e-12

Cuarto ejemplo

$$\displaystyle \min_{x \in \mathbb{R}^2} \quad e^{x_1+3x_2-0.1} + e^{x_1 -3x_2-0.1} + e^{-x_1-0.1}$$
$$\text{sujeto a:} x_1 + 3x_2 = 0$$

In [36]:
fo = lambda x: math.exp(x[0] + 3*x[1]-0.1) + math.exp(x[0]  -3*x[1]-0.1) + math.exp(-x[0]-0.1)

In [37]:
x_ast = np.array([-0.23104907880100917,0.0770163596518852],dtype=float)

In [38]:
A = np.array([1,3],dtype=float)

In [39]:
b=0

Punto inicial $x^{(0)}$ factible ($Ax^{(0)}=b$)


In [40]:
x_0 = np.array([0,0],dtype=float)

In [41]:
tol=1e-8
tol_backtracking=1e-14
maxiter=50
p_ast=fo(x_ast)
[x,total_of_iterations,
 Err_plot,x_plot] = Newtons_method_feasible_init_point(fo,A, x_0,tol, 
                                                       tol_backtracking, x_ast, p_ast, maxiter)


I	Normgf 	Newton Decrement	Error x_ast	Error p_ast	line search	CondHf
0	9.05e-01	1.81e-01	1.00e+00	3.81e-02	---		6.00e+00
1	9.05e-01	3.30e-03	1.34e-01	6.37e-04	1.00e+00	6.00e+00
2	9.05e-01	8.46e-07	2.15e-03	1.62e-07	1.00e+00	6.00e+00
3	9.05e-01	7.41e-14	6.52e-07	1.98e-11	1.00e+00	6.00e+00
Error of x with respect to x_ast: 6.52e-07
Approximate solution: [-0.23104893  0.07701631]

In [42]:
compute_error(x_ast, x)


Out[42]:
6.524588707146708e-07

Quinto ejemplo: con más restricciones de igualdad

Resolver: $$\displaystyle \min_{x \in \mathbb{R}^3} \quad ||x||_2^2$$

$$\text{sujeto a: }\begin{array}{l} \begin{array}{c} x_1 + x_2 + x_3 = 1 \\ x_1 + x_2 + 2x_3 = 3 \end{array} \end{array} $$

In [43]:
fo = lambda x: x.dot(x)

In [44]:
x_ast = np.array([-0.5,-0.5,2. ], dtype=float)

In [45]:
A = np.array([[1, 1, 1],
              [1, 1, 2]],dtype=float)

In [46]:
b = np.array([1,3])

Punto inicial $x^{(0)}$ factible ($Ax^{(0)}=b$)


In [47]:
x_0 = np.array([3,-4,2], dtype=float)
#x_0 = np.array([2,-3,2], dtype=float) #este es otro punto factible

In [48]:
tol=1e-8
tol_backtracking=1e-14
p_ast=fo(x_ast)

In [49]:
[x,total_of_iterations,
 Err_plot,x_plot] = Newtons_method_feasible_init_point(fo,A, x_0,tol, 
                                                       tol_backtracking, x_ast, p_ast, maxiter)


I	Normgf 	Newton Decrement	Error x_ast	Error p_ast	line search	CondHf
0	1.08e+01	4.90e+01	2.33e+00	5.44e+00	---		1.00e+00
1	1.08e+01	3.14e-05	1.87e-03	3.48e-06	1.00e+00	1.00e+00
2	1.08e+01	5.83e-13	2.52e-07	6.45e-14	1.00e+00	1.00e+00
Error of x with respect to x_ast: 2.52e-07
Approximate solution: [-0.49999962 -0.50000038  2.        ]

In [50]:
compute_error(x_ast,x)


Out[50]:
2.518419818612709e-07

Comparación del quinto ejemplo con cvxpy


In [51]:
import cvxpy as cp

In [52]:
x = cp.Variable(3)
A = np.array([[1, 1, 1],
              [1, 1, 2]])
b = np.array([1,3])

In [53]:
obj = cp.Minimize(cp.norm(x,2))

constraints = [A@x == b]

In [54]:
prob = cp.Problem(obj, constraints)

In [55]:
prob.solve()


Out[55]:
2.1213203277662585

In [56]:
print("optimal value", prob.value)


optimal value 2.1213203277662585

In [57]:
print("optimal var", x.value)


optimal var [-0.5 -0.5  2. ]

In [58]:
A@x.value


Out[58]:
array([1., 3.])

In [59]:
np.linalg.norm(x.value)


Out[59]:
2.121320343558957

Otros puntos iniciales


In [60]:
vec = [1,-2,2]
A@vec


Out[60]:
array([1, 3])

In [61]:
np.linalg.norm(vec)


Out[61]:
3.0

In [62]:
vec = [2,-3,2]
A@vec


Out[62]:
array([1, 3])

In [63]:
np.linalg.norm(vec)


Out[63]:
4.123105625617661

In [64]:
vec = [3,-4,2]
A@vec


Out[64]:
array([1, 3])

In [65]:
np.linalg.norm(vec)


Out[65]:
5.385164807134504

Referencias:

  • S. P. Boyd, L. Vandenberghe, Convex Optimization, Cambridge University Press, 2009.