source: sasview/park-1.2.1/park/fitresult.py @ 59b1b92

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 59b1b92 was 95d58d3, checked in by pkienzle, 11 years ago

fix fit line test for bumps/scipy/park and enable it as part of test suite

  • Property mode set to 100644
File size: 7.6 KB
Line 
1import sys, math, time
2import numpy
3
4from formatnum import format_uncertainty
5
6class FitHandler(object):
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
21    def improvement(self):
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        """
28    def error(self, msg):
29        """
30        Model had an error; print traceback
31        """
32    def progress(self, current, expected):
33        """
34        Called each cycle of the fit, reporting the current and the
35        expected amount of work.   The meaning of these values is
36        optimizer dependent, but they can be converted into a percent
37        complete using (100*current)//expected.
38
39        Progress is updated each iteration of the fit, whatever that
40        means for the particular optimization algorithm.  It is called
41        after any calls to improvement for the iteration so that the
42        update handler can control I/O bandwidth by suppressing
43        intermediate improvements until the fit is complete.
44        """
45    def finalize(self):
46        """
47        Fit is complete; best results are reported
48        """
49    def abort(self):
50        """
51        Fit was aborted.
52        """
53
54class ConsoleUpdate(FitHandler):
55    """
56    Print progress to the console.
57    """
58    isbetter = False
59    """Record whether results improved since last update"""
60    progress_delta =  60
61    """Number of seconds between progress updates"""
62    improvement_delta = 5
63    """Number of seconds between improvement updates"""
64    def __init__(self,quiet=False,progress_delta=60,improvement_delta=5):
65        """
66        If quiet is true, only print out final summary, not progress and
67        improvements.
68        """
69        #import traceback; traceback.print_stack()
70        self.progress_time = time.time()
71        self.progress_percent = 0
72        self.improvement_time = self.progress_time
73        self.isbetter = False
74        self.quiet = quiet
75        self.progress_delta = progress_delta
76        self.improvement_delta = improvement_delta
77
78    def progress(self, k, n):
79        """
80        Report on progress.
81        """
82        if self.quiet: return
83        t = time.time()
84        p = int((100*k)//n)
85
86        # Show improvements if there are any
87        dt = t - self.improvement_time
88        if self.isbetter and dt > self.improvement_delta:
89            self.result.print_summary()
90            self.isbetter = False
91            self.improvement_time = t
92
93        # Update percent complete
94        dp = p-self.progress_percent
95        if dp < 1: return
96        dt = t - self.progress_time
97        if dt > self.progress_delta:
98            if 1 <= dp <= 2:
99                print "%d%% complete"%p
100                self.progress_percent = p
101                self.progress_time = t
102            elif 2 < dp <= 5:
103                if p//5 != self.progress_percent//5:
104                    print "%d%% complete"%(5*(p//5))
105                    self.progress_percent = p
106                    self.progress_time = t
107            else:
108                if p//10 != self.progress_percent//10:
109                    print "%d%% complete"%(10*(p//10))
110                    self.progress_percent = p
111                    self.progress_time = t
112
113    def improvement(self):
114        """
115        Called when a result is observed which is better than previous
116        results from the fit.
117        """
118        self.isbetter = True
119
120    def error(self, msg):
121        """
122        Model had an error; print traceback
123        """
124        if self.isbetter:
125            self.result.print_summary()
126        print msg
127
128    def finalize(self):
129        if self.isbetter:
130            self.result.print_summary()
131        print "Total function calls:",self.result.calls
132
133    def abort(self):
134        if self.isbetter:
135            self.result.print_summary()
136
137
138class FitParameter(object):
139    """
140    Fit result for an individual parameter.
141    """
142    def __init__(self, name, range, value):
143        self.name = name
144        self.range = range
145        self.value = value
146        self.stderr = None
147    def summarize(self):
148        """
149        Return parameter range string.
150
151        E.g.,  "       Gold .....|.... 5.2043 in [2,7]"
152        """
153        bar = ['.']*10
154        lo,hi = self.range
155        if numpy.isfinite(lo)and numpy.isfinite(hi):
156            portion = (self.value-lo)/(hi-lo)
157            if portion < 0: portion = 0.
158            elif portion >= 1: portion = 0.99999999
159            barpos = int(math.floor(portion*len(bar)))
160            bar[barpos] = '|'
161        bar = "".join(bar)
162        lostr = "[%g"%lo if numpy.isfinite(lo) else "(-inf"
163        histr = "%g]"%hi if numpy.isfinite(hi) else "inf)"
164        valstr = format_uncertainty(self.value, self.stderr)
165        return "%25s %s %s in %s,%s"  % (self.name,bar,valstr,lostr,histr)
166    def __repr__(self):
167        return "FitParameter('%s')"%self.name
168
169class FitResult(object):
170    """
171    Container for reporting fit results.
172    """
173    def __init__(self, parameters, calls, fitness):
174        self.parameters = parameters
175        """Fit parameter list, each with name, range and value attributes."""
176        self.calls = calls
177        """Number of function calls"""
178        self.fitness = fitness
179        """Value of the goodness of fit metric"""
180        self.pvec = numpy.array([p.value for p in self.parameters])
181        """Parameter vector"""
182        self.stderr = None
183        """Parameter uncertainties"""
184        self.cov = None
185        """Covariance matrix"""
186
187    def update(self, pvec, fitness, calls):
188        self.calls = calls
189        self.fitness = fitness
190        self.pvec = pvec.copy()
191        for i,p in enumerate(self.parameters):
192            p.value = pvec[i]
193
194    def calc_cov(self, fn):
195        """
196        Return the covariance matrix inv(J'J) at point p.
197        """
198        if hasattr(fn, 'jacobian'):
199            # Find cov of f at p
200            #     cov(f,p) = inv(J'J)
201            # Use SVD
202            #     J = U S V'
203            #     J'J = (U S V')' (U S V')
204            #         = V S' U' U S V'
205            #         = V S S V'
206            #     inv(J'J) = inv(V S S V')
207            #              = inv(V') inv(S S) inv(V)
208            #              = V inv (S S) V'
209            J = fn.jacobian(self.pvec)
210            u,s,vh = numpy.linalg.svd(J,0)
211            JTJinv = numpy.dot(vh.T.conj()/s**2,vh)
212            self.set_cov(JTJinv)
213
214    def set_cov(self, cov):
215        """
216        Return the covariance matrix inv(J'J) at point p.
217        """
218        self.cov = cov
219        if cov is not None:
220            self.stderr = numpy.sqrt(numpy.diag(self.cov))
221            # Set the uncertainties on the individual parameters
222            for k,p in enumerate(self.parameters):
223                p.stderr = self.stderr[k]
224        else:
225            self.stderr = None
226            # Reset the uncertainties on the individual parameters
227            for k,p in enumerate(self.parameters):
228                p.stderr = None
229
230    def __str__(self):
231        #import traceback; traceback.print_stack()
232        if self.parameters == None: return "No results"
233        L = ["P%-3d %s"%(n+1,p.summarize()) for n,p in enumerate(self.parameters)]
234        L.append("=== goodness of fit: %g"%(self.fitness))
235        return "\n".join(L)
236
237    def print_summary(self, fid=sys.stdout):
238        print >>fid, self
239
Note: See TracBrowser for help on using the repository browser.