Analytic center computation using a infeasible start Newton method

The set-up


In [1]:
import numpy as np
import pandas as pd
import accpm
import accpm
from IPython.display import display
%load_ext autoreload
%autoreload 1
%aimport accpm

$\DeclareMathOperator{\domain}{dom} \newcommand{\transpose}{\text{T}} \newcommand{\vec}[1]{\begin{pmatrix}#1\end{pmatrix}}$

Theory

To test the $\texttt{analytic_center}$ function we consider the following example. Suppose we want to find the analytic center $x_{ac} \in \mathbb{R}^2$ of the inequalities $x_1 \leq c_1, x_1 \geq 0, x_2 \leq c_2, x_2 \geq 0$. This is a rectange with dimensions $c_1 \times c_2$ centered at at $(\frac{c_1}{2}, \frac{c_2}{2})$ so we should have $x_{ac} = (\frac{c_1}{2}, \frac{c_2}{2})$. Now, $x_{ac}$ is the solution of the minimization problem \begin{equation*} \min_{\domain \phi} \phi(x) = - \sum_{i=1}^{4}{\log{(b_i - a_i^\transpose x)}} \end{equation*} where \begin{equation*} \domain \phi = \{x \;|\; a_i^\transpose x < b_i, i = 1, 2, 3, 4\} \end{equation*} with \begin{align*} &a_1 = \begin{bmatrix}1\\0\end{bmatrix}, &&b_1 = c_1, \\ &a_2 = \begin{bmatrix}-1\\0\end{bmatrix}, &&b_2 = 0, \\ &a_3 = \begin{bmatrix}0\\1\end{bmatrix}, &&b_3 = c_2, \\ &a_4 = \begin{bmatrix}0\\-1\end{bmatrix}, &&b_4 = 0. \end{align*} So we solve \begin{align*} &\phantom{iff}\nabla \phi(x) = \sum_{i=1}^{4 } \frac{1}{b_i - a_i^\transpose x}a_i = 0 \\ &\iff \frac{1}{c_1-x_1}\begin{bmatrix}1\\0\end{bmatrix} + \frac{1}{x_1}\begin{bmatrix}-1\\0\end{bmatrix} + \frac{1}{c_2-x_2}\begin{bmatrix}0\\1\end{bmatrix} + \frac{1}{x_2}\begin{bmatrix}0\\-1\end{bmatrix} = 0 \\ &\iff \frac{1}{c_1-x_1} - \frac{1}{x_1} = 0, \frac{1}{c_2-x_2} - \frac{1}{x_2} = 0 \\ &\iff x_1 = \frac{c_1}{2}, x_2 = \frac{c_2}{2}, \end{align*} as expected.

Testing

We test $\texttt{analytic_center}$ for varying values of $c_1, c_2$ and algorithm parameters $\texttt{alpha, beta}$:


In [2]:
def get_results(A, test_input, alpha, beta, tol=10e-8):
    expected = []
    actual = []
    result = []
    for (c1, c2) in test_input:
        b = np.array([c1, 0, c2, 0])
        ac_expected = np.asarray((c1/2, c2/2))
        ac_actual = accpm.analytic_center(A, b, alpha = alpha, beta = beta)
        expected.append(ac_expected)
        actual.append(ac_actual)
        # if np.array_equal(ac_expected, ac_actual):
        if np.linalg.norm(ac_expected - ac_actual) <= tol: 
            result.append(True)
        else:
            result.append(False)
    results = pd.DataFrame([test_input, expected, actual, result])
    results = results.transpose()
    results.columns = ['test_input', 'expected', 'actual', 'result']
    print('alpha =', alpha, 'beta =', beta)
    display(results)

Here we have results for squares of varying sizes and for varying values of $\texttt{alpha}$ and $\texttt{beta}$. In general, the algorithm performs worse on large starting polyhedrons than small starting polyhedrons. This seems acceptable given that we are most concerned with smaller polyhedrons.


In [3]:
A = np.array([[1, 0],[-1,0],[0,1],[0,-1]])
test_input = [(1, 1), (5, 5), (20, 20), (10e2, 10e2), (10e4, 10e4),
              (10e6, 10e6), (10e8, 10e8), (10e10, 10e10),  
              (0.5, 0.5), (0.1, 0.1), (0.01, 0.01), 
              (0.005, 0.005), (0.001, 0.001),(0.0005, 0.0005), (0.0001, 0.0001),
              (0.00005, 0.00005), (0.00001, 0.00001), (0.00001, 0.00001)]

In [4]:
get_results(A, test_input, alpha=0.01, beta=0.7)


alpha = 0.01 beta = 0.7
test_input expected actual result
0 (1, 1) [0.5, 0.5] [0.5, 0.5] True
1 (5, 5) [2.5, 2.5] [2.49999999986, 2.49999999986] True
2 (20, 20) [10.0, 10.0] [9.99999996446, 9.99999996446] True
3 (1000.0, 1000.0) [500.0, 500.0] [499.957946477, 499.957946477] False
4 (100000.0, 100000.0) [50000.0, 50000.0] [14975.4305308, 14975.4305308] False
5 (10000000.0, 10000000.0) [5000000.0, 5000000.0] [16370.5717206, 16370.5717206] False
6 (1000000000.0, 1000000000.0) [500000000.0, 500000000.0] [16383.8657897, 16383.8657897] False
7 (100000000000.0, 100000000000.0) [50000000000.0, 50000000000.0] [16383.9986579, 16383.9986579] False
8 (0.5, 0.5) [0.25, 0.25] [0.25, 0.25] True
9 (0.1, 0.1) [0.05, 0.05] [0.0500000001725, 0.0500000001725] True
10 (0.01, 0.01) [0.005, 0.005] [0.005, 0.005] True
11 (0.005, 0.005) [0.0025, 0.0025] [0.0025, 0.0025] True
12 (0.001, 0.001) [0.0005, 0.0005] [0.0005, 0.0005] True
13 (0.0005, 0.0005) [0.00025, 0.00025] [0.00025, 0.00025] True
14 (0.0001, 0.0001) [5e-05, 5e-05] [5e-05, 5e-05] True
15 (5e-05, 5e-05) [2.5e-05, 2.5e-05] [2.5e-05, 2.5e-05] True
16 (1e-05, 1e-05) [5e-06, 5e-06] [-3.75837697589e-06, -3.75837697589e-06] False
17 (1e-05, 1e-05) [5e-06, 5e-06] [-3.75837697589e-06, -3.75837697589e-06] False

In [5]:
get_results(A, test_input, alpha=0.01, beta=0.99)


**** Cholesky factorization FAILED or INACCURATE ****
**** Cholesky factorization FAILED or INACCURATE ****
**** Cholesky factorization FAILED or INACCURATE ****
**** Cholesky factorization FAILED or INACCURATE ****
alpha = 0.01 beta = 0.99
test_input expected actual result
0 (1, 1) [0.5, 0.5] [0.5, 0.5] True
1 (5, 5) [2.5, 2.5] [2.49999999986, 2.49999999986] True
2 (20, 20) [10.0, 10.0] [9.99999996446, 9.99999996446] True
3 (1000.0, 1000.0) [500.0, 500.0] [499.957946477, 499.957946477] False
4 (100000.0, 100000.0) [50000.0, 50000.0] [14975.4305308, 14975.4305308] False
5 (10000000.0, 10000000.0) [5000000.0, 5000000.0] [16370.5717206, 16370.5717206] False
6 (1000000000.0, 1000000000.0) [500000000.0, 500000000.0] [16383.8657897, 16383.8657897] False
7 (100000000000.0, 100000000000.0) [50000000000.0, 50000000000.0] [16383.9986579, 16383.9986579] False
8 (0.5, 0.5) [0.25, 0.25] [0.25, 0.25] True
9 (0.1, 0.1) [0.05, 0.05] [0.05, 0.05] True
10 (0.01, 0.01) [0.005, 0.005] [0.00500000000016, 0.00500000000016] True
11 (0.005, 0.005) [0.0025, 0.0025] [0.0025, 0.0025] True
12 (0.001, 0.001) [0.0005, 0.0005] [0.0005, 0.0005] True
13 (0.0005, 0.0005) [0.00025, 0.00025] [0.00025, 0.00025] True
14 (0.0001, 0.0001) [5e-05, 5e-05] [5e-05, 5e-05] True
15 (5e-05, 5e-05) [2.5e-05, 2.5e-05] [2.5e-05, 2.5e-05] True
16 (1e-05, 1e-05) [5e-06, 5e-06] [5e-06, 5e-06] True
17 (1e-05, 1e-05) [5e-06, 5e-06] [5e-06, 5e-06] True

In [6]:
get_results(A, test_input, alpha=0.49, beta=0.7)


alpha = 0.49 beta = 0.7
test_input expected actual result
0 (1, 1) [0.5, 0.5] [0.5, 0.5] True
1 (5, 5) [2.5, 2.5] [2.49999999986, 2.49999999986] True
2 (20, 20) [10.0, 10.0] [9.99999996446, 9.99999996446] True
3 (1000.0, 1000.0) [500.0, 500.0] [499.957946477, 499.957946477] False
4 (100000.0, 100000.0) [50000.0, 50000.0] [14975.4305308, 14975.4305308] False
5 (10000000.0, 10000000.0) [5000000.0, 5000000.0] [16370.5717206, 16370.5717206] False
6 (1000000000.0, 1000000000.0) [500000000.0, 500000000.0] [16383.8657897, 16383.8657897] False
7 (100000000000.0, 100000000000.0) [50000000000.0, 50000000000.0] [16383.9986579, 16383.9986579] False
8 (0.5, 0.5) [0.25, 0.25] [0.25, 0.25] True
9 (0.1, 0.1) [0.05, 0.05] [0.05, 0.05] True
10 (0.01, 0.01) [0.005, 0.005] [0.005, 0.005] True
11 (0.005, 0.005) [0.0025, 0.0025] [0.0025, 0.0025] True
12 (0.001, 0.001) [0.0005, 0.0005] [6.60127396139e-05, 6.60127396139e-05] False
13 (0.0005, 0.0005) [0.00025, 0.00025] [-1.48162874137e-05, -1.48162874137e-05] False
14 (0.0001, 0.0001) [5e-05, 5e-05] [-0.000117627949822, -0.000117627949822] False
15 (5e-05, 5e-05) [2.5e-05, 2.5e-05] [-7.18810109122e-05, -7.18810109122e-05] False
16 (1e-05, 1e-05) [5e-06, 5e-06] [-3.0071748618e-05, -3.0071748618e-05] False
17 (1e-05, 1e-05) [5e-06, 5e-06] [-3.0071748618e-05, -3.0071748618e-05] False

In [7]:
get_results(A, test_input, alpha=0.25, beta=0.7)


alpha = 0.25 beta = 0.7
test_input expected actual result
0 (1, 1) [0.5, 0.5] [0.5, 0.5] True
1 (5, 5) [2.5, 2.5] [2.49999999986, 2.49999999986] True
2 (20, 20) [10.0, 10.0] [9.99999996446, 9.99999996446] True
3 (1000.0, 1000.0) [500.0, 500.0] [499.957946477, 499.957946477] False
4 (100000.0, 100000.0) [50000.0, 50000.0] [14975.4305308, 14975.4305308] False
5 (10000000.0, 10000000.0) [5000000.0, 5000000.0] [16370.5717206, 16370.5717206] False
6 (1000000000.0, 1000000000.0) [500000000.0, 500000000.0] [16383.8657897, 16383.8657897] False
7 (100000000000.0, 100000000000.0) [50000000000.0, 50000000000.0] [16383.9986579, 16383.9986579] False
8 (0.5, 0.5) [0.25, 0.25] [0.25, 0.25] True
9 (0.1, 0.1) [0.05, 0.05] [0.05, 0.05] True
10 (0.01, 0.01) [0.005, 0.005] [0.005, 0.005] True
11 (0.005, 0.005) [0.0025, 0.0025] [0.0025, 0.0025] True
12 (0.001, 0.001) [0.0005, 0.0005] [0.0005, 0.0005] True
13 (0.0005, 0.0005) [0.00025, 0.00025] [0.00025, 0.00025] True
14 (0.0001, 0.0001) [5e-05, 5e-05] [1.19489452233e-05, 1.19489452233e-05] False
15 (5e-05, 5e-05) [2.5e-05, 2.5e-05] [-2.54320902431e-05, -2.54320902431e-05] False
16 (1e-05, 1e-05) [5e-06, 5e-06] [-1.63588955287e-05, -1.63588955287e-05] False
17 (1e-05, 1e-05) [5e-06, 5e-06] [-1.63588955287e-05, -1.63588955287e-05] False