1 """
2 Implements iterative algorithm class.
3 """
4 import numpy as np
5 from copy import copy
6
7 from linesearch import *
8 from criterions import *
9 from norms import norm2
10
11
12 TOL = 1e-6
13 GTOL = 1e-6
14 MAXITER = None
15
16
19 return algo.iter_ >= self.maxiter
21 self.resid = np.abs(algo.last_criterion - algo.current_criterion)
22 self.resid /= algo.first_criterion
23 return self.resid < self.tol
25 return algo.current_gradient_norm < self.gtol
26 _all_tests = [_test_maxiter, _test_tol, _test_gtol]
27 - def __init__(self, maxiter=None, tol=None, gtol=None, cond=np.any):
28 self.cond = cond
29 self.maxiter = maxiter
30 self.tol = tol
31 self.gtol = gtol
32 self.all_val = [self.maxiter, self.tol, self.gtol]
33
34 self.tests_val = [val for val in self.all_val
35 if val is not None]
36 self.tests = [test
37 for test, val in zip(self._all_tests, self.all_val)
38 if val is not None]
39
40 self.resid = None
42 return self.cond([test(self, algo) for test in self.tests])
43 - def str(self, algo):
44 """
45 Returns a string with current condition values.
46 """
47 if self.resid is not None and self.tol is not None:
48 return "\t %1.2e / %1.2e" % (self.resid, self.tol)
49 else:
50 return "\t Residual"
51
52 default_stop = StopCondition(maxiter=MAXITER, tol=TOL, gtol=GTOL)
53
54
55
57 """
58 Fletcher-Reeves descent direction update method.
59 """
60 return algo.current_gradient_norm / algo.last_gradient_norm
61
63 """
64 Polak-Ribiere descent direction update method.
65 """
66 b = np.dot(algo.current_gradient.T,
67 (algo.current_gradient - algo.last_gradient))
68 b /= np.norm(algo.last_gradient)
69 return b
70
71
72
74 - def __init__(self, verbose=False, savefile=None, shape=()):
75 self.verbose = verbose
76 self.savefile = savefile
77 self.shape = shape
78 self.im = None
80 if self.verbose:
81 if algo.iter_ == 1:
82 print('Iteration \t Criterion')
83 print_str = "\t%i \t %e" % (algo.iter_, algo.current_criterion)
84 print_str += algo.stop_condition.str(algo)
85 print(print_str)
86 - def save(self, algo):
87 if self.savefile is not None:
88 var_dict = {
89 "iter":algo.iter_,
90 "criterion":algo.current_criterion,
91 "solution":algo.current_solution,
92 }
93 np.savez(self.savefile, **var_dict)
95 import pylab
96 if algo.iter_ == 1:
97 self.im = pylab.imshow(algo.current_solution.reshape(self.shape))
98 else:
99 self.im.set_data(algo.current_solution.reshape(self.shape))
100 pylab.draw()
101 pylab.show()
102 - def plot(self, algo):
103 import pylab
104 if algo.iter_ == 1:
105 self.im = pylab.plot(algo.current_solution)[0]
106 else:
107 y = algo.current_solution
108 self.im.set_ydata(y)
109 pylab.ylim((y.min(), y.max()))
110 pylab.draw()
111 pylab.show()
113 if self.verbose:
114 self.print_status(algo)
115 if self.savefile is not None:
116 self.save(algo)
117 if self.shape is not None:
118 if len(self.shape) == 1:
119 self.plot(algo)
120 elif len(self.shape) == 2:
121 self.imshow(algo)
122
123 default_callback = Callback()
124
125
126
128 """
129 Abstract class to define iterative algorithms.
130
131 Attributes
132 ----------
133
134 iter_ : int
135 Current iteration number.
136
137 Methods
138 -------
139
140 initialize : Set variables to initial state
141 iterate : perform one iteration and return current solution
142 callback : user-defined function to print status or save variables
143 cont : continue the optimization skipping initialiaztion
144 __call__ : perform the optimization unt stop_condition is reached
145 """
147 self.iter_ = 0
148 self.current_solution = None
152 """
153 Perform one iteration and returns current solution.
154 """
155 self.iter_ += 1
156 self.callback(self)
157
158 return self.current_solution
160 """
161 Perform some task at exit.
162 Does nothing by default.
163 """
164 pass
166 """
167 Perform the optimization.
168 """
169 self.initialize()
170 self.iterate()
171 self.cont()
172 self.at_exit()
173 return self.current_solution
175 """
176 Continue an interrupted estimation (like call but avoid
177 initialization).
178 """
179 while not self.stop_condition(self):
180 self.iterate()
181 return self.current_solution
182
184 """
185 Apply the conjugate gradient algorithm to a Criterion instance.
186
187 Parameters
188 ----------
189
190 criterion : Criterion
191 A Criterion instance. It should have following methods and attributes:
192 __call__ : returns criterion values at given point
193 gradient : returns gradient (1st derivative) of criterion at given point
194 n_variable: the size of the input vector of criterion
195
196 x0 : ndarray (None)
197 The first guess of the algorithm.
198
199 callback : function (default_callback)
200 Perform some printing / saving operations at each iteration.
201
202 stop_condition : function (default_stop)
203 Defines when the iterations should stop
204
205 update_type : function (fletcher_reeves)
206 Type of descent direction update : e.g. fletcher_reeves, polak_ribiere
207
208 line_search : function (optimal step)
209 Line search method to find the minimum along each direction at each
210 iteration.
211
212 Returns
213 -------
214
215 Returns an algorithm instance. Optimization is performed by
216 calling the this instance.
217
218 """
224 self.criterion = criterion
225 self.gradient = criterion.gradient
226 self.n_variables = self.criterion.n_variables
227
228 self.callback = callback
229 self.stop_condition = stop_condition
230 self.update_type = update_type
231 self.line_search = line_search
232 self.kwargs = kwargs
233
234 self.current_criterion = np.inf
235 self.current_solution = None
236 self.current_gradient = None
237 self.current_gradient_norm = None
238 self.current_descent = None
239 self.last_criterion = np.inf
240 self.last_solution = None
241 self.last_gradient = None
242 self.last_gradient_norm = None
243 self.last_descent = None
245 """
246 Initialize required values.
247 """
248 Algorithm.initialize(self)
249 self.first_guess()
250 self.first_criterion = self.criterion(self.current_solution)
251 self.current_criterion = self.first_criterion
253 """
254 Sets current_solution attribute to initial value.
255 """
256 if x0 is None:
257 self.current_solution = np.zeros(self.n_variables)
258 else:
259 self.current_solution = copy(x0)
260
262 self.last_gradient = copy(self.current_gradient)
263 self.current_gradient = self.gradient(self.current_solution)
265 self.last_gradient_norm = copy(self.current_gradient_norm)
266 self.current_gradient_norm = norm2(self.current_gradient)
268 if self.iter_ == 0:
269 self.current_descent = - self.current_gradient
270 else:
271 self.last_descent = copy(self.current_descent)
272 b = self.update_type(self)
273 self.current_descent = - self.current_gradient + b * self.last_descent
275 self.last_solution = copy(self.current_solution)
276 a = self.line_search(self)
277 self.current_solution += a * self.current_descent
279 self.last_criterion = copy(self.current_criterion)
280 self.current_criterion = self.criterion(self.current_solution)
291
293 """
294 A subclass of ConjugateGradient using a QuadraticCriterion.
295 """
296 - def __init__(self, model, data, priors=[], hypers=[], **kwargs):
301
303 """
304 A subclass of ConjugateGradient using an HuberCriterion.
305 """
306 - def __init__(self, model, data, priors=[], hypers=[], deltas=None, **kwargs):
307 store = kwargs.pop("store", True)
308 criterion = HuberCriterion(model, data, hypers=hypers, priors=priors,
309 deltas=deltas, store=store)
310 ConjugateGradient.__init__(self, criterion, **kwargs)
311
312
313
314 -def acg(model, data, priors=[], hypers=[], **kwargs):
315 algorithm = QuadraticConjugateGradient(model, data, priors=priors,
316 hypers=hypers, **kwargs)
317 sol = algorithm()
318 return sol
319
320 -def hacg(model, data, priors=[], hypers=[], deltas=None, **kwargs):
321 algorithm = HuberConjugateGradient(model, data, priors=priors,
322 hypers=hypers, deltas=deltas, **kwargs)
323 sol = algorithm()
324 return sol
325
326
327
329 """
330 Normalize hyperparamaters so that they are independent of pb size
331 """
332 nx = float(x.size)
333 ny = float(y.size)
334 return np.asarray(hyper) * ny / nx
335