quala main
Quasi-Newton and other accelerators
Loading...
Searching...
No Matches
test_anderson.py
Go to the documentation of this file.
1from typing import List
2import casadi as cs
3import numpy.random as nprand
4import numpy.linalg as la
5import numpy as np
6import quala as qa
7import os
8
9n = 10 # problem dimension
10ε = 1e-12 # assertion tolerance
11
12# Positive definite “QP” with a twist (including a sin for nonlinearity)
13rng = nprand.default_rng(seed=123)
14A = rng.random(size=(n, n))
15Q, _ = la.qr(A)
16D = np.diag(rng.normal(scale=4, size=(n, )))
17A = Q.T @ D.T @ D @ Q
18q = rng.normal(scale=1, size=(n, ))
19print(f"cond(A)={la.cond(A)}")
20x_ = cs.SX.sym('x', n)
21px = x_ + 0.2 * cs.sin(x_[::-1])
22f_ = 0.5 * (x_.T @ A @ px) + cs.dot(q, x_)
23f = cs.Function("f", [x_], [f_])
24grad_f_ = cs.gradient(f_, x_)
25grad_f = cs.Function("grad_f", [x_], [grad_f_])
26hess_f_ = cs.jacobian(grad_f_, x_)
27hess_f = cs.Function("hess_f", [x_], [hess_f_])
28L = la.norm(D)**2
29# Uncomment the next line for gradient descent instead of Newton:
30# hess_f = lambda _: L * cs.DM.eye(n)
31
32# solve r(x) = -(H(x))⁻¹ ∇f(x) = 0 or g(x) = r(x) + x = x
33
34
36 x = np.ones((n, 1))
37 m = n - 2
38 print(f"m={m}")
39 aa = qa.AndersonAccel({'memory': m}, n)
40
41 Gk = np.zeros((n, 0))
42 Rk = np.zeros((n, 0))
43 r_prev = np.nan * np.ones((n, 1))
44
45 p_stack = cs.DM.zeros(n, 0)
46
47 res_aa: List[float] = []
48 x_aa_diff_quala: List[float] = []
49 x_aa_diff_david: List[float] = []
50 for i in range(3 * n):
51 r = -la.solve(hess_f(x).full(), grad_f(x).full()) # Newton step (res)
52 g = r + x
53 res_aa.append(la.norm(r))
54 print(f"{i}: {res_aa[-1]}")
55
56 # Initialize
57 if i == 0:
58 # quala implementation
59 aa.initialize(g, r)
60
61 # Python implementation
62 r_prev = r.copy()
63 Gk = g.copy()
64
65 # David's implementation
66 x_stack = x.copy()
67 p_stack = r.copy()
68
69 x = g.copy()
70 # Anderson update
71 else:
72 mk = min(i, m)
73 print(f" mk={mk}")
74
75 # quala implementation
76 x_aa_quala = aa.compute(g, r)
77 x_aa_quala = np.array([x_aa_quala]).T
78
79 # Python implementation
80 Rk = np.hstack((Rk, r - r_prev))
81 Gk = np.hstack((Gk, g))
82 γ_LS, _, _, _ = la.lstsq(Rk[:, -mk:], r, rcond=None)
83 α_LS = np.nan * np.ones((mk + 1, ))
84 α_LS[0] = γ_LS[0]
85 for j in range(1, mk):
86 α_LS[j] = γ_LS[j] - γ_LS[j - 1]
87 α_LS[mk] = 1 - γ_LS[mk - 1]
88 x_aa_py = np.zeros((n, 1))
89 for j in range(0, mk + 1):
90 gj = Gk[:, [i - mk + j]]
91 x_aa_py += α_LS[j] * gj
92 r_prev = r.copy()
93
94 # David's implementation
95 x_tmp = x.copy() # <?> (current iterate)
96 p_tmp = r.copy() # <?> (current residual, Newton step)
97 if i <= m: # Fixed
98 print(" mk <= m")
99 p_stack = cs.horzcat(p_tmp, p_stack)
100 x_stack = cs.horzcat(x_tmp, x_stack)
101 else:
102 print(" mk > m")
103 p_stack = cs.horzcat(p_tmp, p_stack[:, 0:-1])
104 x_stack = cs.horzcat(x_tmp, x_stack[:, 0:-1])
105
106 F_k = p_stack[:, 0:-1] - p_stack[:, 1:]
107 E_k = x_stack[:, 0:-1] - x_stack[:, 1:]
108
109 pinv_Fk = np.linalg.pinv(F_k)
110 gamma_k = pinv_Fk @ p_tmp
111
112 x_aa_david = x_stack[:, 0] + p_tmp - (E_k + F_k) @ gamma_k
113
114 # Compare results
115 rel_diff = lambda x, y: la.norm(x - y) / la.norm(x)
116 x_aa_diff_quala.append(rel_diff(x_aa_py, x_aa_quala))
117 print(f" py-quala: {x_aa_diff_quala[-1]}")
118 assert x_aa_diff_quala[-1] < ε, f"Failed on iteration {i}"
119 x_aa_diff_david.append(rel_diff(x_aa_py, x_aa_david))
120 print(f" py-david: {x_aa_diff_david[-1]}")
121 assert x_aa_diff_david[-1] < ε, f"Failed on iteration {i}"
122
123 # Next iterate
124 x = x_aa_quala.copy()
125
126 if not "PYTEST_CURRENT_TEST" in os.environ:
127 import matplotlib.pyplot as plt
128 plt.rcParams.update({
129 "text.usetex": True,
130 "font.family": "serif",
131 "font.size": 14,
132 "lines.linewidth": 1,
133 })
134
135 plt.figure()
136 plt.title("Residual")
137 plt.semilogy(res_aa, '.-')
138 plt.ylim([1e-16, None])
139 plt.xlabel("Iteration $k$")
140 plt.ylabel(r"$\|r(x^k)\|$")
141 plt.tight_layout()
142
143 plt.figure()
144 plt.title("Difference between iterations")
145 plt.semilogy(x_aa_diff_quala, '.-', label="Python vs quala")
146 plt.semilogy(x_aa_diff_david, '.-', label="Python vs David")
147 plt.legend()
148 plt.xlabel("Iteration $k$")
149 plt.ylabel(r"Rel. difference $\|x^a_{k+1}-x^b_{k+1}\|/\|x^a_{k+1}\|$")
150 plt.tight_layout()
151 plt.show()
152
153 r = -la.solve(hess_f(x).full(), grad_f(x).full()) # Newton step (res)
154 print(f"final: {la.norm(r)}")
155 print(f"x:\r\n{x}")
156 print(f"r:\r\n{r}")
157 assert np.allclose(r, np.zeros((n, )), rtol=ε, atol=ε)
158
159
160if __name__ == '__main__':
def test_anderson()