1 import sys, math, time
2 import numpy
3
4 from park.util.formatnum import format_uncertainty as format
5
7 """
8 Abstract interface for fit thread handler.
9
10 The methods in this class are called by the optimizer as the fit
11 progresses.
12
13 Note that it is up to the optimizer to call the fit handler correctly,
14 reporting all status changes and maintaining the 'done' flag.
15 """
16 done = False
17 """True when the fit job is complete"""
18 result = None
19 """The current best result of the fit"""
20
22 """
23 Called when a result is observed which is better than previous
24 results from the fit.
25
26 result is a FitResult object, with parameters, #calls and fitness.
27 """
29 """
30 Model had an error; print traceback
31 """
32 raise Exception(msg)
34 """
35 Called each cycle of the fit, reporting the current and the
36 expected amount of work. The meaning of these values is
37 optimizer dependent, but they can be converted into a percent
38 complete using (100*current)//expected.
39
40 Progress is updated each iteration of the fit, whatever that
41 means for the particular optimization algorithm. It is called
42 after any calls to improvement for the iteration so that the
43 update handler can control I/O bandwidth by suppressing
44 intermediate improvements until the fit is complete.
45 """
47 """
48 Fit is complete; best results are reported
49 """
51 """
52 Fit was aborted.
53 """
54
55
56
57
58
59
60
62 """
63 Operations required to support functionality of GA refl.
64 """
67
69 """
70 Reset the bounds on parameter p.
71
72 Range can be::
73 None: let p vary in (-inf,inf)
74 value: fix p at the value
75 (low,high): vary p in range (low,high)
76 """
77
79 """
80 Reset the fit population so that parameter p varies uniformly
81 within its range.
82
83 This is not a sensible option for all optimizers.
84 """
85
86 - def localopt(self, p=None, optimizer=None):
87 """
88 Run a local optimizer at point p.
89
90 The result will be sent to the FitMonitor. The user may opt to
91 introduce this new individual back into the fit population.
92
93 If no p is given, then run from the best current value in the fit.
94
95 If no optimizer is given, then run a quasi-Newton optimizer
96 such as secant or dog-leg for functions without derivatives
97 or tnc for functions with derivatives.
98
99 Note: it is theoretically possible to split the minimization
100 into D0 and D1 portions, using different algorithms for each.
101 """
102
104 """
105 Introduce an individual to the fit population.
106
107 The suggested value may come from a local optimization from point
108 p or from the user playing with the parameters in the foreground
109 while the global optimization is running in the background.
110 """
111
112 - def lamarkian(self, p=None, optimizer=None):
113 """
114 Lamarkian evolution.
115
116 Improve the fittest individual in the population using a local
117 optimizer and reintroduce it into the population. This strategy
118 is meaningful for a number of the stochastic optimizers including
119 simulated annealing since it allows the user to control the goal
120 directed behaviour.
121
122 This is a server-side combination of localopt and suggest, without
123 the need to cycle the results through FitMonitor.
124 """
125
127 """
128 Search near p.
129
130 Sigma is the size of the attractor relative to the entire search space.
131
132 This is a user supplied heuristic telling the stochastic search
133 to prefer to look near point p when proposing new members of the
134 population. This will not affect the calculated objective value
135
136 The attractor translates into a scaled gaussian peak.
137 """
138
139 - def repel(self, p, I=1, sigma=0.1):
140 """
141 Avoid p.
142 Avoid a particular region of the fit space when choosing new
143 individuals in the population.
144
145 Having identified a bad local optimum in the fit space, have
146 the stochastic optimizers avoid the this region using a penalty
147 function based
148 """
149
150
152 """
153 Print progress to the console.
154 """
155 isbetter = False
156 """Record whether results improved since last update"""
157 progress_delta = 60
158 """Number of seconds between progress updates"""
159 improvement_delta = 5
160 """Number of seconds between improvement updates"""
161 - def __init__(self,quiet=False,progress_delta=60,improvement_delta=5):
162 """
163 If quiet is true, only print out final summary, not progress and
164 improvements.
165 """
166 self.progress_time = time.time()
167 self.progress_percent = 0
168 self.improvement_time = self.progress_time
169 self.isbetter = False
170 self.quiet = quiet
171 self.progress_delta = progress_delta
172 self.improvement_delta = improvement_delta
173
175 """
176 Report on progress.
177 """
178 if self.quiet: return
179 t = time.time()
180 p = int((100*k)//n)
181
182
183 dt = t - self.improvement_time
184 if self.isbetter and dt > self.improvement_delta:
185 self.result.print_summary()
186 self.isbetter = False
187 self.improvement_time = t
188
189
190 dp = p-self.progress_percent
191 if dp < 1: return
192 dt = t - self.progress_time
193 if dt > self.progress_delta:
194 if 1 <= dp <= 2:
195 print "%d%% complete"%p
196 self.progress_percent = p
197 self.progress_time = t
198 elif 2 < dp <= 5:
199 if p//5 != self.progress_percent//5:
200 print "%d%% complete"%(5*(p//5))
201 self.progress_percent = p
202 self.progress_time = t
203 else:
204 if p//10 != self.progress_percent//10:
205 print "%d%% complete"%(10*(p//10))
206 self.progress_percent = p
207 self.progress_time = t
208
210 """
211 Called when a result is observed which is better than previous
212 results from the fit.
213 """
214 self.isbetter = True
215
223
227
231
232
234 """
235 Fit result for an individual parameter.
236 """
237 - def __init__(self, name, range, value):
243 """
244 Return parameter range string.
245
246 E.g., " Gold .....|.... 5.2043 in [2,7]"
247 """
248 bar = ['.']*10
249 lo,hi = self.range
250 if numpy.isfinite(lo)and numpy.isfinite(hi):
251 portion = (self.value-lo)/(hi-lo)
252 if portion < 0: portion = 0.
253 elif portion >= 1: portion = 0.99999999
254 barpos = int(math.floor(portion*len(bar)))
255 bar[barpos] = '|'
256 bar = "".join(bar)
257 lostr = "[%g"%lo if numpy.isfinite(lo) else "(-inf"
258 histr = "%g]"%hi if numpy.isfinite(hi) else "inf)"
259 valstr = format(self.value, self.stderr)
260 return "%25s %s %s in %s,%s" % (self.name,bar,valstr,lostr,histr)
262 return "FitParameter('%s')"%self.name
263
265 """
266 Container for reporting fit results.
267 """
268 - def __init__(self, parameters, calls, fitness):
269 self.parameters = parameters
270 """Fit parameter list, each with name, range and value attributes."""
271 self.calls = calls
272 """Number of function calls"""
273 self.fitness = fitness
274 """Value of the goodness of fit metric"""
275 self.pvec = numpy.array([p.value for p in self.parameters])
276 """Parameter vector"""
277 self.stderr = None
278 """Parameter uncertainties"""
279 self.cov = None
280 """Covariance matrix"""
281
283 """
284 Return the covariance matrix inv(J'J) at point p.
285 """
286 if hasattr(fn, 'jacobian'):
287
288
289
290
291
292
293
294
295
296
297 J = fn.jacobian(self.pvec)
298 u,s,vh = numpy.linalg.svd(J,0)
299 JTJinv = numpy.dot(vh.T.conj()/s**2,vh)
300 self.set_cov(JTJinv)
301
303 """
304 Return the covariance matrix inv(J'J) at point p.
305 """
306 self.cov = cov
307 if cov is not None:
308 self.stderr = numpy.sqrt(numpy.diag(self.cov))
309
310 for k,p in enumerate(self.parameters):
311 p.stderr = self.stderr[k]
312 else:
313 self.stderr = None
314
315 for k,p in enumerate(self.parameters):
316 p.stderr = None
317
319 if self.parameters == None: return
320 for n,p in enumerate(self.parameters):
321 print >>fid, "P%-3d %s"%(n+1,p.summarize())
322 print >>fid, "=== goodness of fit: %g"%(self.fitness)
323