Changeset eca05c8 in sasview for pr_inversion
- Timestamp:
- Apr 30, 2008 4:29:36 PM (17 years ago)
- Branches:
- master, ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc, costrafo411, magnetic_scatt, release-4.1.1, release-4.1.2, release-4.2.2, release_4.0.1, ticket-1009, ticket-1094-headless, ticket-1242-2d-resolution, ticket-1243, ticket-1249, ticket885, unittest-saveload
- Children:
- 34ae302
- Parents:
- 47f695c9
- Location:
- pr_inversion
- Files:
-
- 1 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
pr_inversion/c_extensions/Cinvertor.c
r9e8dc22 reca05c8 222 222 } 223 223 224 static PyObject * set_alpha(Cinvertor *self, PyObject *args) { 225 double alpha; 226 227 if (!PyArg_ParseTuple(args, "d", &alpha)) return NULL; 228 self->params.alpha = alpha; 229 return Py_BuildValue("d", self->params.alpha); 230 } 231 232 /** 233 * Gets the maximum distance 234 */ 235 static PyObject * get_alpha(Cinvertor *self, PyObject *args) { 236 return Py_BuildValue("d", self->params.alpha); 237 } 238 224 239 /** 225 240 * Gets the number of x points … … 246 261 /** 247 262 * Function to call to evaluate the residuals 248 * @param args: input q or [q,phi]249 * @return: function value263 * @param args: input parameters 264 * @return: list of residuals 250 265 */ 251 266 static PyObject * residuals(Cinvertor *self, PyObject *args) { … … 257 272 double residual, diff; 258 273 // Regularization factor 259 double lmult = 0.0; 274 double regterm = 0.0; 275 double tmp = 0.0; 260 276 261 277 PyObject *data_obj; … … 266 282 OUTVECTOR(data_obj,pars,npars); 267 283 268 //printf("npars: %i", npars);269 284 // PyList of residuals 270 285 // Should create this list only once and refill it 271 286 residuals = PyList_New(self->params.npoints); 287 288 regterm = reg_term(pars, self->params.d_max, npars); 272 289 273 290 for(i=0; i<self->params.npoints; i++) { 274 //printf("\n%i: ", i);275 291 diff = self->params.y[i] - iq(pars, self->params.d_max, npars, self->params.x[i]); 276 292 residual = diff*diff / (self->params.err[i]*self->params.err[i]); 277 //printf("%i %g\n", i, residual); 293 tmp = residual; 294 295 // regularization term 296 residual += self->params.alpha * regterm; 297 278 298 if (PyList_SetItem(residuals, i, Py_BuildValue("d",residual) ) < 0){ 279 299 PyErr_SetString(CinvertorError, … … 286 306 return residuals; 287 307 } 308 /** 309 * Function to call to evaluate the residuals 310 * for P(r) minimization (for testing purposes) 311 * @param args: input parameters 312 * @return: list of residuals 313 */ 314 static PyObject * pr_residuals(Cinvertor *self, PyObject *args) { 315 double *pars; 316 PyObject* residuals; 317 PyObject* temp; 318 double *res; 319 int i; 320 double residual, diff; 321 // Regularization factor 322 double regterm = 0.0; 323 double tmp = 0.0; 324 325 PyObject *data_obj; 326 Py_ssize_t npars; 327 328 if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL; 329 330 OUTVECTOR(data_obj,pars,npars); 331 332 // Should create this list only once and refill it 333 residuals = PyList_New(self->params.npoints); 334 335 regterm = reg_term(pars, self->params.d_max, npars); 336 337 338 for(i=0; i<self->params.npoints; i++) { 339 diff = self->params.y[i] - pr(pars, self->params.d_max, npars, self->params.x[i]); 340 residual = diff*diff / (self->params.err[i]*self->params.err[i]); 341 tmp = residual; 342 343 // regularization term 344 residual += self->params.alpha * regterm; 345 346 if (PyList_SetItem(residuals, i, Py_BuildValue("d",residual) ) < 0){ 347 PyErr_SetString(CinvertorError, 348 "Cinvertor.residuals: error setting residual."); 349 return NULL; 350 }; 351 352 } 353 354 return residuals; 355 } 288 356 289 357 /** … … 323 391 } 324 392 393 /** 394 * Function to call to evaluate P(r) with errors 395 * @param args: c-parameters and r 396 * @return: P(r) 397 */ 398 static PyObject * get_pr_err(Cinvertor *self, PyObject *args) { 399 double *pars; 400 double *pars_err; 401 double pr_err_value; 402 double r, pr_value; 403 PyObject *data_obj; 404 Py_ssize_t npars; 405 PyObject *err_obj; 406 Py_ssize_t npars2; 407 408 if (!PyArg_ParseTuple(args, "OOd", &data_obj, &err_obj, &r)) return NULL; 409 OUTVECTOR(data_obj,pars,npars); 410 OUTVECTOR(err_obj,pars_err,npars2); 411 412 pr_err(pars, pars_err, self->params.d_max, npars, r, &pr_value, &pr_err_value); 413 return Py_BuildValue("ff", pr_value, pr_err_value); 414 } 415 325 416 326 417 static PyMethodDef Cinvertor_methods[] = { 327 418 {"residuals", (PyCFunction)residuals, METH_VARARGS, "Get the list of residuals"}, 419 {"pr_residuals", (PyCFunction)pr_residuals, METH_VARARGS, "Get the list of residuals"}, 328 420 {"set_x", (PyCFunction)set_x, METH_VARARGS, ""}, 329 421 {"get_x", (PyCFunction)get_x, METH_VARARGS, ""}, … … 334 426 {"set_dmax", (PyCFunction)set_dmax, METH_VARARGS, ""}, 335 427 {"get_dmax", (PyCFunction)get_dmax, METH_VARARGS, ""}, 428 {"set_alpha", (PyCFunction)set_alpha, METH_VARARGS, ""}, 429 {"get_alpha", (PyCFunction)get_alpha, METH_VARARGS, ""}, 336 430 {"get_nx", (PyCFunction)get_nx, METH_VARARGS, ""}, 337 431 {"get_ny", (PyCFunction)get_ny, METH_VARARGS, ""}, … … 339 433 {"iq", (PyCFunction)get_iq, METH_VARARGS, ""}, 340 434 {"pr", (PyCFunction)get_pr, METH_VARARGS, ""}, 435 {"get_pr_err", (PyCFunction)get_pr_err, METH_VARARGS, ""}, 341 436 {"is_valid", (PyCFunction)is_valid, METH_VARARGS, ""}, 342 437 -
pr_inversion/c_extensions/invertor.c
r9e8dc22 reca05c8 40 40 double ortho(double d_max, int n, double r) { 41 41 return 2.0*r*sin(pi*n*r/d_max); 42 43 42 } 44 43 … … 76 75 */ 77 76 double pr(double *pars, double d_max, int n_c, double r) { 78 double sum = 0.0; 77 double sum = 0.0; 79 78 int i; 80 79 for (i=0; i<n_c; i++) { … … 84 83 } 85 84 85 /** 86 * P(r) calculated from the expansion, with errors 87 */ 88 void pr_err(double *pars, double *err, double d_max, int n_c, 89 double r, double *pr_value, double *pr_value_err) { 90 double sum = 0.0; 91 double sum_err = 0.0; 92 double func_value; 93 int i; 94 for (i=0; i<n_c; i++) { 95 func_value = ortho(d_max, i+1, r); 96 sum += pars[i] * func_value; 97 sum_err += err[i]*err[i]*func_value*func_value; 98 } 99 *pr_value = sum; 100 if (sum_err>0) { 101 *pr_value_err = sqrt(sum_err); 102 } else { 103 *pr_value_err = sum; 104 } 105 } 86 106 107 /** 108 * dP(r)/dr calculated from the expansion. 109 */ 110 double dprdr(double *pars, double d_max, int n_c, double r) { 111 double sum = 0.0; 112 int i; 113 for (i=0; i<n_c; i++) { 114 sum += pars[i] * 2.0*(sin(pi*(i+1)*r/d_max) + pi*(i+1)*r/d_max * cos(pi*(i+1)*r/d_max)); 115 } 116 return sum; 117 } 118 119 /** 120 * regularization term calculated from the expansion. 121 */ 122 double reg_term(double *pars, double d_max, int n_c) { 123 double sum = 0.0; 124 double r; 125 double deriv; 126 int i; 127 for (i=0; i<25; i++) { 128 r = d_max/25.0*i; 129 deriv = dprdr(pars, d_max, n_c, r); 130 sum += deriv*deriv; 131 } 132 return sum/25.0*d_max; 133 } 134 135 -
pr_inversion/c_extensions/invertor.h
r9e8dc22 reca05c8 10 10 int npoints; 11 11 int ny; 12 int nerr; 13 } Invertor_params; 12 int nerr; 13 double alpha; 14 } Invertor_params; 14 15 15 16 void invertor_dealloc(Invertor_params *pars); … … 24 25 double iq(double *c, double d_max, int n_c, double q); 25 26 double pr(double *c, double d_max, int n_c, double r); 27 double dprdr(double *pars, double d_max, int n_c, double r); 28 double reg_term(double *pars, double d_max, int n_c); 29 void pr_err(double *pars, double *err, double d_max, int n_c, 30 double r, double *pr_value, double *pr_value_err); 26 31 27 32 #endif -
pr_inversion/invertor.py
r9e8dc22 reca05c8 3 3 4 4 class Invertor(Cinvertor): 5 6 ## Chisqr of the last computation 7 chisqr = 0 5 8 6 9 def __init__(self): … … 14 17 """ 15 18 if name=='x': 19 if 0.0 in value: 20 raise ValueError, "Invertor: one of your q-values is zero. Delete that entry before proceeding" 16 21 return self.set_x(value) 17 22 elif name=='y': … … 21 26 elif name=='d_max': 22 27 return self.set_dmax(value) 28 elif name=='alpha': 29 return self.set_alpha(value) 23 30 24 31 return Cinvertor.__setattr__(self, name, value) … … 45 52 elif name=='d_max': 46 53 return self.get_dmax() 54 elif name=='alpha': 55 return self.get_alpha() 47 56 elif name in self.__dict__: 48 57 return self.__dict__[name] … … 62 71 out, cov_x, info, mesg, success = optimize.leastsq(self.residuals, p, full_output=1, warning=True) 63 72 73 # Compute chi^2 74 res = self.residuals(out) 75 chisqr = 0 76 for i in range(len(res)): 77 chisqr += res[i] 78 79 self.chi2 = chisqr 80 64 81 return out, cov_x 82 83 def pr_fit(self, nfunc=5): 84 """ 85 Perform inversion to P(r) 86 """ 87 from scipy import optimize 88 89 # First, check that the current data is valid 90 if self.is_valid()<=0: 91 raise RuntimeError, "Invertor.invert: Data arrays are of different length" 92 93 p = numpy.ones(nfunc) 94 out, cov_x, info, mesg, success = optimize.leastsq(self.pr_residuals, p, full_output=1, warning=True) 95 96 # Compute chi^2 97 res = self.pr_residuals(out) 98 chisqr = 0 99 for i in range(len(res)): 100 chisqr += res[i] 101 102 self.chisqr = chisqr 103 104 return out, cov_x 105 106 def pr_err(self, c, c_cov, r): 107 import math 108 c_err = numpy.zeros(len(c)) 109 for i in range(len(c)): 110 try: 111 c_err[i] = math.sqrt(math.fabs(c_cov[i][i])) 112 except: 113 import sys 114 print sys.exc_value 115 print "oups", c_cov[i][i] 116 c_err[i] = c[i] 117 118 return self.get_pr_err(c, c_err, r) 119 65 120 66 121 if __name__ == "__main__": -
pr_inversion/test/utest_invertor.py
r9e8dc22 reca05c8 8 8 9 9 10 import unittest, math, numpy 10 import unittest, math, numpy, pylab 11 11 from sans.pr.invertor import Invertor 12 12 … … 21 21 self.x_in = numpy.ones(self.ntest) 22 22 for i in range(self.ntest): 23 self.x_in[i] = 1.0* i23 self.x_in[i] = 1.0*(i+1) 24 24 25 25 … … 31 31 self.invertor.d_max = value 32 32 self.assertEqual(self.invertor.d_max, value) 33 34 def testset_alpha(self): 35 """ 36 Set and read alpha 37 """ 38 value = 15.0 39 self.invertor.alpha = value 40 self.assertEqual(self.invertor.alpha, value) 33 41 34 42 def testset_x_1(self): … … 113 121 self.assertEqual(self.invertor.test_no_data, None) 114 122 123 def test_inversion(self): 124 """ 125 Test an inversion for which we know the answer 126 """ 127 x, y, err = load("sphere_80.txt") 128 129 # Choose the right d_max... 130 self.invertor.d_max = 160.0 131 # Set a small alpha 132 self.invertor.alpha = 1e-7 133 # Set data 134 self.invertor.x = x 135 self.invertor.y = y 136 self.invertor.err = err 137 # Perform inversion 138 out, cov = self.invertor.invert(10) 139 # This is a very specific case 140 # We should make sure it always passes 141 self.assertTrue(self.invertor.chi2/len(x)<200.00) 142 143 # Check the computed P(r) with the theory 144 # for shpere of radius 80 145 x = pylab.arange(0.01, self.invertor.d_max, self.invertor.d_max/51.0) 146 y = numpy.zeros(len(x)) 147 dy = numpy.zeros(len(x)) 148 y_true = numpy.zeros(len(x)) 149 150 sum = 0.0 151 sum_true = 0.0 152 for i in range(len(x)): 153 #y[i] = self.invertor.pr(out, x[i]) 154 (y[i], dy[i]) = self.invertor.pr_err(out, cov, x[i]) 155 sum += y[i] 156 if x[i]<80.0: 157 y_true[i] = pr_theory(x[i], 80.0) 158 else: 159 y_true[i] = 0 160 sum_true += y_true[i] 161 162 y = y/sum*self.invertor.d_max/len(x) 163 dy = dy/sum*self.invertor.d_max/len(x) 164 y_true = y_true/sum_true*self.invertor.d_max/len(x) 165 166 chi2 = 0.0 167 for i in range(len(x)): 168 res = (y[i]-y_true[i])/dy[i] 169 chi2 += res*res 170 171 try: 172 self.assertTrue(chi2/51.0<10.0) 173 except: 174 print "chi2 =", chi2/51.0 175 raise 176 177 def test_q_zero(self): 178 """ 179 Test error condition where a point has q=0 180 """ 181 x, y, err = load("sphere_80.txt") 182 x[0] = 0.0 183 184 # Choose the right d_max... 185 self.invertor.d_max = 160.0 186 # Set a small alpha 187 self.invertor.alpha = 1e-7 188 # Set data 189 def doit(): 190 self.invertor.x = x 191 self.assertRaises(ValueError, doit) 192 193 194 def test_q_neg(self): 195 """ 196 Test error condition where a point has q<0 197 """ 198 x, y, err = load("sphere_80.txt") 199 x[0] = -0.2 200 201 # Choose the right d_max... 202 self.invertor.d_max = 160.0 203 # Set a small alpha 204 self.invertor.alpha = 1e-7 205 # Set data 206 self.invertor.x = x 207 self.invertor.y = y 208 self.invertor.err = err 209 # Perform inversion 210 out, cov = self.invertor.invert(4) 211 212 try: 213 self.assertTrue(self.invertor.chi2>0) 214 except: 215 print "Chi2 =", self.invertor.chi2 216 raise 217 218 def test_Iq_zero(self): 219 """ 220 Test error condition where a point has q<0 221 """ 222 x, y, err = load("sphere_80.txt") 223 y[0] = 0.0 224 225 # Choose the right d_max... 226 self.invertor.d_max = 160.0 227 # Set a small alpha 228 self.invertor.alpha = 1e-7 229 # Set data 230 self.invertor.x = x 231 self.invertor.y = y 232 self.invertor.err = err 233 # Perform inversion 234 out, cov = self.invertor.invert(4) 235 236 try: 237 self.assertTrue(self.invertor.chi2>0) 238 except: 239 print "Chi2 =", self.invertor.chi2 240 raise 241 242 243 244 def pr_theory(r, R): 245 """ 246 P(r) for a sphere 247 """ 248 if r<=2*R: 249 return 12.0* ((0.5*r/R)**2) * ((1.0-0.5*r/R)**2) * ( 2.0 + 0.5*r/R ) 250 else: 251 return 0.0 252 253 def load(path = "sphere_60_q0_2.txt"): 254 import numpy, math, sys 255 # Read the data from the data file 256 data_x = numpy.zeros(0) 257 data_y = numpy.zeros(0) 258 data_err = numpy.zeros(0) 259 if not path == None: 260 input_f = open(path,'r') 261 buff = input_f.read() 262 lines = buff.split('\n') 263 for line in lines: 264 try: 265 toks = line.split() 266 x = float(toks[0]) 267 y = float(toks[1]) 268 data_x = numpy.append(data_x, x) 269 data_y = numpy.append(data_y, y) 270 # Set the error of the first point to 5% 271 # to make theory look like data 272 scale = 0.1/math.sqrt(data_x[0]) 273 data_err = numpy.append(data_err, scale*math.sqrt(y)) 274 except: 275 pass 276 277 return data_x, data_y, data_err 278 115 279 if __name__ == '__main__': 116 280 unittest.main()
Note: See TracChangeset
for help on using the changeset viewer.