1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 from Numeric import Float64, dot, identity, matrixmultiply, outerproduct
25
26 from base_classes import Line_search, Min
27
28
29 -def bfgs(func=None, dfunc=None, args=(), x0=None, min_options=None, func_tol=1e-25, grad_tol=None, maxiter=1e6, a0=1.0, mu=0.0001, eta=0.9, full_output=0, print_flag=0, print_prefix=""):
30 """Quasi-Newton BFGS minimisation."""
31
32 if print_flag:
33 if print_flag >= 2:
34 print print_prefix
35 print print_prefix
36 print print_prefix + "Quasi-Newton BFGS minimisation"
37 print print_prefix + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
38 min = Bfgs(func, dfunc, args, x0, min_options, func_tol, grad_tol, maxiter, a0, mu, eta, full_output, print_flag, print_prefix)
39 if min.init_failure:
40 print print_prefix + "Initialisation of minimisation has failed."
41 return None
42 results = min.minimise()
43 return results
44
45
46 -class Bfgs(Line_search, Min):
47 - def __init__(self, func, dfunc, args, x0, min_options, func_tol, grad_tol, maxiter, a0, mu, eta, full_output, print_flag, print_prefix):
48 """Class for Quasi-Newton BFGS minimisation specific functions.
49
50 Unless you know what you are doing, you should call the function 'bfgs' rather than using
51 this class.
52 """
53
54
55 self.func = func
56 self.dfunc = dfunc
57 self.args = args
58 self.xk = x0
59 self.func_tol = func_tol
60 self.grad_tol = grad_tol
61 self.maxiter = maxiter
62 self.full_output = full_output
63 self.print_flag = print_flag
64 self.print_prefix = print_prefix
65
66
67 self.a0 = a0
68
69
70 self.mu = mu
71 self.eta = eta
72
73
74 self.init_failure = 0
75
76
77 self.line_search_options(min_options)
78 self.setup_line_search()
79
80
81 self.f_count = 0
82 self.g_count = 0
83 self.h_count = 0
84
85
86 self.warning = None
87
88
89 self.setup_conv_tests()
90
91
92 self.setup_bfgs()
93
94
95 self.update = self.update_bfgs
96
97
99 """The new parameter function.
100
101 Find the search direction, do a line search, and get xk+1 and fk+1.
102 """
103
104
105 self.pk = -matrixmultiply(self.Hk, self.dfk)
106
107
108 self.line_search()
109
110
111 self.xk_new = self.xk + self.alpha * self.pk
112 self.fk_new, self.f_count = apply(self.func, (self.xk_new,)+self.args), self.f_count + 1
113 self.dfk_new, self.g_count = apply(self.dfunc, (self.xk_new,)+self.args), self.g_count + 1
114
115
116 if self.print_flag >= 2:
117 print self.print_prefix + "pk: " + `self.pk`
118 print self.print_prefix + "alpha: " + `self.alpha`
119 print self.print_prefix + "xk: " + `self.xk`
120 print self.print_prefix + "xk+1: " + `self.xk_new`
121 print self.print_prefix + "fk: " + `self.fk`
122 print self.print_prefix + "fk+1: " + `self.fk_new`
123
124
126 """Setup function.
127
128 Create the identity matrix I and calculate the function, gradient and initial BFGS inverse
129 Hessian matrix.
130 """
131
132
133 self.I = identity(len(self.xk), Float64)
134
135
136 self.fk, self.f_count = apply(self.func, (self.xk,)+self.args), self.f_count + 1
137 self.dfk, self.g_count = apply(self.dfunc, (self.xk,)+self.args), self.g_count + 1
138 self.Hk = self.I * 1.0
139
140
142 """Function to update the function value, gradient vector, and the BFGS matrix."""
143
144
145 sk = self.xk_new - self.xk
146 yk = self.dfk_new - self.dfk
147 dot_yk_sk = dot(yk, sk)
148
149
150 if dot_yk_sk == 0:
151 self.warning = "The BFGS matrix is indefinite. This should not occur."
152 rk = 1e99
153 else:
154 rk = 1.0 / dot_yk_sk
155
156
157 if self.k == 0:
158 self.Hk = dot_yk_sk / dot(yk, yk) * self.I
159
160 self.Hk = matrixmultiply(self.I - rk*outerproduct(sk, yk), self.Hk)
161 self.Hk = matrixmultiply(self.Hk, self.I - rk*outerproduct(yk, sk))
162 self.Hk = self.Hk + rk*outerproduct(sk, sk)
163
164
165 self.xk = self.xk_new * 1.0
166 self.fk = self.fk_new
167 self.dfk = self.dfk_new * 1.0
168