1 | """ |
---|
2 | This file is intended to be a temporary file to communicate in-progress code |
---|
3 | to the developers. |
---|
4 | This file should be removed after its content has been used by the team. |
---|
5 | """ |
---|
6 | |
---|
7 | |
---|
8 | # This code belongs in AbstractFitEngine |
---|
9 | class FitData1D: |
---|
10 | def setFitRange(self,qmin=None,qmax=None): |
---|
11 | """ |
---|
12 | Change the fit range. |
---|
13 | Take into account the fact that if smearing is applied, |
---|
14 | a wider range in unsmeared Q might be necessary to cover |
---|
15 | the smeared (observed) Q range. |
---|
16 | """ |
---|
17 | |
---|
18 | # Skip Q=0 point, (especially for y(q=0)=None at x[0]). |
---|
19 | #ToDo: Fix this. |
---|
20 | if qmin==0.0 and not numpy.isfinite(self.data.y[qmin]): |
---|
21 | self.qmin = min(self.data.x[self.data.x!=0]) |
---|
22 | elif qmin!=None: |
---|
23 | self.qmin = qmin |
---|
24 | |
---|
25 | if qmax !=None: |
---|
26 | self.qmax = qmax |
---|
27 | |
---|
28 | # Range used for input to smearing |
---|
29 | self._qmin_unsmeared = self.qmin |
---|
30 | self._qmax_unsmeared = self.qmax |
---|
31 | |
---|
32 | # Determine the range needed in unsmeared-Q to cover |
---|
33 | # the smeared Q range |
---|
34 | if self.smearer.__class__.__name__ == 'SlitSmearer': |
---|
35 | # The entries in the slit smearer matrix remain |
---|
36 | # large across all bins, so we keep the full Q range. |
---|
37 | self._qmin_unsmeared = min(self.data.x) |
---|
38 | self._qmax_unsmeared = max(self.data.x) |
---|
39 | elif self.smearer.__class__.__name__ == 'QSmearer': |
---|
40 | # Take 3 sigmas as the offset between smeared and unsmeared space. |
---|
41 | try: |
---|
42 | offset = 3.0*max(self.smearer.width) |
---|
43 | self._qmin_unsmeared = max([min(self.data.x), self.qmin-offset]) |
---|
44 | self._qmax_unsmeared = min([max(self.data.x), self.qmax+offset]) |
---|
45 | except: |
---|
46 | logging.error("FitData1D.setFitRange: %s" % sys.exc_value) |
---|
47 | |
---|
48 | |
---|
49 | def residuals(self, fn): |
---|
50 | """ |
---|
51 | Compute residuals. |
---|
52 | |
---|
53 | If self.smearer has been set, use if to smear |
---|
54 | the data before computing chi squared. |
---|
55 | |
---|
56 | This is a version based on the current version of residuals. |
---|
57 | |
---|
58 | It takes into account the fact that the unsmearing range |
---|
59 | might need to be wider than the smeared (observed) range. |
---|
60 | |
---|
61 | :param fn: function that return model value |
---|
62 | |
---|
63 | :return: residuals |
---|
64 | |
---|
65 | """ |
---|
66 | x,y = [numpy.asarray(v) for v in (self.x,self.y)] |
---|
67 | if self.dy ==None or self.dy==[]: |
---|
68 | dy= numpy.zeros(len(y)) |
---|
69 | else: |
---|
70 | dy= numpy.asarray(dy) |
---|
71 | |
---|
72 | dy[dy==0]=1 |
---|
73 | idx_unsmeared = (x>=self._qmin_unsmeared) & (x <= self._qmax_unsmeared) |
---|
74 | |
---|
75 | # Compute theory data f(x) |
---|
76 | idx=[] |
---|
77 | tempy=[] |
---|
78 | tempfx=[] |
---|
79 | tempdy=[] |
---|
80 | |
---|
81 | _first_bin = None |
---|
82 | for i_x in range(len(x)): |
---|
83 | try: |
---|
84 | if idx_unsmeared[i_x]==True: |
---|
85 | if _first_bin is None: |
---|
86 | _first_bin = i_x |
---|
87 | |
---|
88 | value= fn(x[i_x]) |
---|
89 | idx.append(x[i_x]>=self.qmin and x[i_x]<=self.qmax) |
---|
90 | tempfx.append( value) |
---|
91 | tempy.append(y[i_x]) |
---|
92 | tempdy.append(dy[i_x]) |
---|
93 | except: |
---|
94 | ## skip error for model.run(x) |
---|
95 | pass |
---|
96 | |
---|
97 | ## Smear theory data |
---|
98 | # The tempfx array has a length limited by the Q range. |
---|
99 | if self.smearer is not None: |
---|
100 | tempfx = self.smearer(tempfx, _first_bin) |
---|
101 | |
---|
102 | newy = numpy.asarray(tempy) |
---|
103 | newfx= numpy.asarray(tempfx) |
---|
104 | newdy= numpy.asarray(tempdy) |
---|
105 | |
---|
106 | ## Sanity check |
---|
107 | if numpy.size(newdy)!= numpy.size(newfx): |
---|
108 | raise RuntimeError, "FitData1D: invalid error array %d <> %d" % (numpy.size(newdy), numpy.size(newfx)) |
---|
109 | |
---|
110 | return (newy[idx]-newfx[idx])/newdy[idx] |
---|
111 | |
---|
112 | |
---|
113 | def residuals_alt(self, fn): |
---|
114 | """ |
---|
115 | Compute residuals. |
---|
116 | |
---|
117 | If self.smearer has been set, use if to smear |
---|
118 | the data before computing chi squared. |
---|
119 | |
---|
120 | This is a more streamlined version of the above. To use this version, |
---|
121 | the _BaseSmearer class below needs to be modified to have its __call__ |
---|
122 | method have the following signature: |
---|
123 | |
---|
124 | __call__(self, iq, first_bin, last_bin) |
---|
125 | |
---|
126 | This is because we are storing results in arrays of a length |
---|
127 | corresponding to the full Q-range. |
---|
128 | |
---|
129 | It takes into account the fact that the unsmearing range |
---|
130 | might need to be wider than the smeared (observed) range. |
---|
131 | |
---|
132 | :param fn: function that return model value |
---|
133 | |
---|
134 | :return: residuals |
---|
135 | |
---|
136 | """ |
---|
137 | # Make sure the arrays are numpy arrays, which are |
---|
138 | # expected by the fitter. |
---|
139 | x,y = [numpy.asarray(v) for v in (self.x,self.y)] |
---|
140 | if self.dy ==None or self.dy==[]: |
---|
141 | dy= numpy.zeros(len(y)) |
---|
142 | else: |
---|
143 | dy= numpy.asarray(dy) |
---|
144 | |
---|
145 | dy[dy==0]=1 |
---|
146 | idx = (x>=self.qmin) & (x <= self.qmax) |
---|
147 | idx_unsmeared = (x>=self._qmin_unsmeared) & (x <= self._qmax_unsmeared) |
---|
148 | |
---|
149 | # Compute theory data f(x) |
---|
150 | fx= numpy.zeros(len(x)) |
---|
151 | |
---|
152 | # First and last bins of the array, corresponding to |
---|
153 | # the Q range to be smeared |
---|
154 | _first_bin = None |
---|
155 | _last_bin = None |
---|
156 | for i_x in range(len(x)): |
---|
157 | try: |
---|
158 | if idx_unsmeared[i_x]==True: |
---|
159 | if _first_bin is None: |
---|
160 | _first_bin = i_x |
---|
161 | else: |
---|
162 | _last_bin = i_x |
---|
163 | |
---|
164 | value = fn(x[i_x]) |
---|
165 | fx[i_x] = value |
---|
166 | except: |
---|
167 | ## skip error for model.run(x) |
---|
168 | ## Should properly log the error |
---|
169 | pass |
---|
170 | |
---|
171 | # Smear theory data |
---|
172 | if self.smearer is not None: |
---|
173 | fx = self.smearer(fx, _first_bin, _last_bin) |
---|
174 | |
---|
175 | # Sanity check |
---|
176 | if numpy.size(dy)!= numpy.size(fx): |
---|
177 | raise RuntimeError, "FitData1D: invalid error array %d <> %d" % (numpy.size(dy), numpy.size(fx)) |
---|
178 | |
---|
179 | # Return the residuals for the smeared (observed) Q range |
---|
180 | return (y[idx]-fx[idx])/dy[idx] |
---|
181 | |
---|
182 | # The following code belongs in DataLoader.qsmearing |
---|
183 | class _BaseSmearer(object): |
---|
184 | |
---|
185 | def __init__(self): |
---|
186 | self.nbins = 0 |
---|
187 | self._weights = None |
---|
188 | |
---|
189 | def _compute_matrix(self): return NotImplemented |
---|
190 | |
---|
191 | def __call__(self, iq, first_bin=0): |
---|
192 | """ |
---|
193 | Return the smeared I(q) value at the given q. |
---|
194 | The smeared I(q) is computed using a predetermined |
---|
195 | smearing matrix for a particular binning. |
---|
196 | |
---|
197 | :param q: I(q) array |
---|
198 | :param first_bin: first bin of the given iq array if shorter than full data length |
---|
199 | |
---|
200 | :return: smeared I(q) |
---|
201 | |
---|
202 | """ |
---|
203 | # Sanity check |
---|
204 | if len(iq)+first_bin > self.nbins: |
---|
205 | raise RuntimeError, "Invalid I(q) vector: inconsistent array length %s > %s" % (str(len(iq)+first_bin), str(self.nbins)) |
---|
206 | |
---|
207 | if self._weights == None: |
---|
208 | self._compute_matrix() |
---|
209 | |
---|
210 | iq_smeared = numpy.zeros(len(iq)) |
---|
211 | # Loop over q-values |
---|
212 | idwb=[] |
---|
213 | |
---|
214 | for q_i in range(len(iq)): |
---|
215 | sum = 0.0 |
---|
216 | counts = 0.0 |
---|
217 | for i in range(len(iq)): |
---|
218 | if iq[i]==0 or self._weights[q_i+first_bin][i+first_bin]==0: |
---|
219 | continue |
---|
220 | else: |
---|
221 | sum += iq[i] * self._weights[q_i+first_bin][i+first_bin] |
---|
222 | counts += self._weights[q_i+first_bin][i+first_bin] |
---|
223 | |
---|
224 | if counts == 0: |
---|
225 | iq_smeared[q_i] = 0 |
---|
226 | else: |
---|
227 | iq_smeared[q_i] = sum/counts |
---|
228 | return iq_smeared |
---|