source: sasview/pr_inversion/test/utest_invertor.py @ 89590cd

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 89590cd was b00b487, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Dealt with error conditions, fixed the uncertainty on the output.

  • Property mode set to 100644
File size: 16.7 KB
Line 
1"""
2    Unit tests for Invertor class
3"""
4# Disable "missing docstring" complaint
5# pylint: disable-msg=C0111
6# Disable "too many methods" complaint
7# pylint: disable-msg=R0904
8
9
10import unittest, math, numpy, pylab
11from sans.pr.invertor import Invertor
12       
13class TestFiguresOfMerit(unittest.TestCase):
14           
15    def setUp(self):
16        self.invertor = Invertor()
17        self.invertor.d_max = 100.0
18       
19        # Test array
20        self.ntest = 5
21        self.x_in = numpy.ones(self.ntest)
22        for i in range(self.ntest):
23            self.x_in[i] = 1.0*(i+1)
24       
25        x, y, err = load("sphere_80.txt")
26
27        # Choose the right d_max...
28        self.invertor.d_max = 160.0
29        # Set a small alpha
30        self.invertor.alpha = .0007
31        # Set data
32        self.invertor.x   = x
33        self.invertor.y   = y
34        self.invertor.err = err
35        # Perform inversion
36        #out, cov = self.invertor.invert(10)
37       
38        self.out, self.cov = self.invertor.lstsq(10)
39
40    def test_positive(self):
41        """
42            Test whether P(r) is positive
43        """
44        self.assertEqual(self.invertor.get_positive(self.out), 1)
45       
46    def test_positive_err(self):
47        """
48            Test whether P(r) is at least 1 sigma greater than zero
49            for all r-values
50        """
51        self.assertTrue(self.invertor.get_pos_err(self.out, self.cov)>0.9)
52       
53class TestBasicComponent(unittest.TestCase):
54   
55    def setUp(self):
56        self.invertor = Invertor()
57        self.invertor.d_max = 100.0
58       
59        # Test array
60        self.ntest = 5
61        self.x_in = numpy.ones(self.ntest)
62        for i in range(self.ntest):
63            self.x_in[i] = 1.0*(i+1)
64
65
66    def testset_dmax(self):
67        """
68            Set and read d_max
69        """
70        value = 15.0
71        self.invertor.d_max = value
72        self.assertEqual(self.invertor.d_max, value)
73       
74    def testset_alpha(self):
75        """
76            Set and read alpha
77        """
78        value = 15.0
79        self.invertor.alpha = value
80        self.assertEqual(self.invertor.alpha, value)
81       
82    def testset_x_1(self):
83        """
84            Setting and reading the x array the hard way
85        """
86        # Set x
87        self.invertor.x = self.x_in
88       
89        # Read it back
90        npts = self.invertor.get_nx()
91        x_out = numpy.ones(npts)
92       
93        self.invertor.get_x(x_out)
94
95        for i in range(self.ntest):
96            self.assertEqual(self.x_in[i], x_out[i])
97           
98    def testset_x_2(self):
99        """
100            Setting and reading the x array the easy way
101        """
102        # Set x
103        self.invertor.x = self.x_in
104       
105        # Read it back
106        x_out = self.invertor.x
107       
108        for i in range(self.ntest):
109            self.assertEqual(self.x_in[i], x_out[i])
110       
111    def testset_y(self):
112        """
113            Setting and reading the y array the easy way
114        """
115        # Set y
116        self.invertor.y = self.x_in
117       
118        # Read it back
119        y_out = self.invertor.y
120       
121        for i in range(self.ntest):
122            self.assertEqual(self.x_in[i], y_out[i])
123       
124    def testset_err(self):
125        """
126            Setting and reading the err array the easy way
127        """
128        # Set err
129        self.invertor.err = self.x_in
130       
131        # Read it back
132        err_out = self.invertor.err
133       
134        for i in range(self.ntest):
135            self.assertEqual(self.x_in[i], err_out[i])
136       
137    def test_iq(self):
138        """
139            Test iq calculation
140        """
141        q = 0.11
142        v1 = 8.0*math.pi**2/q * self.invertor.d_max *math.sin(q*self.invertor.d_max)
143        v1 /= ( math.pi**2 - (q*self.invertor.d_max)**2.0 )
144       
145        pars = numpy.ones(1)
146        self.assertAlmostEqual(self.invertor.iq(pars, q), v1, 2)
147       
148    def test_pr(self):
149        """
150            Test pr calculation
151        """
152        r = 10.0
153        v1 = 2.0*r*math.sin(math.pi*r/self.invertor.d_max)
154        pars = numpy.ones(1)
155        self.assertAlmostEqual(self.invertor.pr(pars, r), v1, 2)
156       
157    def test_getsetters(self):
158        self.invertor.new_data = 1.0
159        self.assertEqual(self.invertor.new_data, 1.0)
160       
161        self.assertEqual(self.invertor.test_no_data, None)
162       
163    def test_inversion(self):
164        """
165            Test an inversion for which we know the answer
166        """
167        x, y, err = load("sphere_80.txt")
168
169        # Choose the right d_max...
170        self.invertor.d_max = 160.0
171        # Set a small alpha
172        self.invertor.alpha = 1e-7
173        # Set data
174        self.invertor.x   = x
175        self.invertor.y   = y
176        self.invertor.err = err
177        # Perform inversion
178        out, cov = self.invertor.invert_optimize(10)
179        # This is a very specific case
180        # We should make sure it always passes
181        self.assertTrue(self.invertor.chi2/len(x)<200.00)
182       
183        # Check the computed P(r) with the theory
184        # for shpere of radius 80
185        x = pylab.arange(0.01, self.invertor.d_max, self.invertor.d_max/51.0)
186        y = numpy.zeros(len(x))
187        dy = numpy.zeros(len(x))
188        y_true = numpy.zeros(len(x))
189
190        sum = 0.0
191        sum_true = 0.0
192        for i in range(len(x)):
193            #y[i] = self.invertor.pr(out, x[i])
194            (y[i], dy[i]) = self.invertor.pr_err(out, cov, x[i])
195            sum += y[i]
196            if x[i]<80.0:
197                y_true[i] = pr_theory(x[i], 80.0)
198            else:
199                y_true[i] = 0
200            sum_true += y_true[i]
201           
202        y = y/sum*self.invertor.d_max/len(x)
203        dy = dy/sum*self.invertor.d_max/len(x)
204        y_true = y_true/sum_true*self.invertor.d_max/len(x)
205       
206        chi2 = 0.0
207        for i in range(len(x)):
208            res = (y[i]-y_true[i])/dy[i]
209            chi2 += res*res
210           
211        try:
212            self.assertTrue(chi2/51.0<10.0)
213        except:
214            print "chi2 =", chi2/51.0
215            raise
216       
217    def test_lstsq(self):
218        """
219            Test an inversion for which we know the answer
220        """
221        x, y, err = load("sphere_80.txt")
222
223        # Choose the right d_max...
224        self.invertor.d_max = 160.0
225        # Set a small alpha
226        self.invertor.alpha = .005
227        # Set data
228        self.invertor.x   = x
229        self.invertor.y   = y
230        self.invertor.err = err
231        # Perform inversion
232        #out, cov = self.invertor.invert(10)
233       
234        out, cov = self.invertor.lstsq(10)
235         
236       
237        # This is a very specific case
238        # We should make sure it always passes
239        try:
240            self.assertTrue(self.invertor.chi2/len(x)<200.00)
241        except:
242            print "Chi2(I(q)) =", self.invertor.chi2/len(x)
243            raise
244       
245        # Check the computed P(r) with the theory
246        # for shpere of radius 80
247        x = pylab.arange(0.01, self.invertor.d_max, self.invertor.d_max/51.0)
248        y = numpy.zeros(len(x))
249        dy = numpy.zeros(len(x))
250        y_true = numpy.zeros(len(x))
251
252        sum = 0.0
253        sum_true = 0.0
254        for i in range(len(x)):
255            #y[i] = self.invertor.pr(out, x[i])
256            (y[i], dy[i]) = self.invertor.pr_err(out, cov, x[i])
257            sum += y[i]
258            if x[i]<80.0:
259                y_true[i] = pr_theory(x[i], 80.0)
260            else:
261                y_true[i] = 0
262            sum_true += y_true[i]
263           
264        y = y/sum*self.invertor.d_max/len(x)
265        dy = dy/sum*self.invertor.d_max/len(x)
266        y_true = y_true/sum_true*self.invertor.d_max/len(x)
267       
268        chi2 = 0.0
269        for i in range(len(x)):
270            res = (y[i]-y_true[i])/dy[i]
271            chi2 += res*res
272           
273        try:
274            self.assertTrue(chi2/51.0<50.0)
275        except:
276            print "chi2(P(r)) =", chi2/51.0
277            raise
278       
279        # Test the number of peaks
280        self.assertEqual(self.invertor.get_peaks(out), 1)
281           
282    def test_q_zero(self):
283        """
284            Test error condition where a point has q=0
285        """
286        x, y, err = load("sphere_80.txt")
287        x[0] = 0.0
288       
289        # Choose the right d_max...
290        self.invertor.d_max = 160.0
291        # Set a small alpha
292        self.invertor.alpha = 1e-7
293        # Set data
294        def doit():
295            self.invertor.x   = x
296        self.assertRaises(ValueError, doit)
297       
298                           
299    def test_q_neg(self):
300        """
301            Test error condition where a point has q<0
302        """
303        x, y, err = load("sphere_80.txt")
304        x[0] = -0.2
305       
306        # Choose the right d_max...
307        self.invertor.d_max = 160.0
308        # Set a small alpha
309        self.invertor.alpha = 1e-7
310        # Set data
311        self.invertor.x   = x
312        self.invertor.y   = y
313        self.invertor.err = err
314        # Perform inversion
315        out, cov = self.invertor.invert(4)
316       
317        try:
318            self.assertTrue(self.invertor.chi2>0)
319        except:
320            print "Chi2 =", self.invertor.chi2
321            raise
322                           
323    def test_Iq_zero(self):
324        """
325            Test error condition where a point has q<0
326        """
327        x, y, err = load("sphere_80.txt")
328        y[0] = 0.0
329       
330        # Choose the right d_max...
331        self.invertor.d_max = 160.0
332        # Set a small alpha
333        self.invertor.alpha = 1e-7
334        # Set data
335        self.invertor.x   = x
336        self.invertor.y   = y
337        self.invertor.err = err
338        # Perform inversion
339        out, cov = self.invertor.invert(4)
340       
341        try:
342            self.assertTrue(self.invertor.chi2>0)
343        except:
344            print "Chi2 =", self.invertor.chi2
345            raise
346       
347    def no_test_time(self):
348        x, y, err = load("sphere_80.txt")
349
350        # Choose the right d_max...
351        self.invertor.d_max = 160.0
352        # Set a small alpha
353        self.invertor.alpha = 1e-7
354        # Set data
355        self.invertor.x   = x
356        self.invertor.y   = y
357        self.invertor.err = err
358   
359        # time scales like nfunc**2
360        # on a Lenovo Intel Core 2 CPU T7400 @ 2.16GHz,
361        # I get time/(nfunc)**2 = 0.022 sec
362                           
363        out, cov = self.invertor.invert(15)
364        t16 = self.invertor.elapsed
365       
366        out, cov = self.invertor.invert(30)
367        t30 = self.invertor.elapsed
368       
369        t30s = t30/30.0**2
370        self.assertTrue( (t30s-t16/16.0**2)/t30s <1.2 )
371       
372    def test_clone(self):
373        self.invertor.x = self.x_in
374        clone = self.invertor.clone()
375       
376        for i in range(len(self.x_in)):
377            self.assertEqual(self.x_in[i], clone.x[i])
378       
379    def test_save(self):
380        x, y, err = load("sphere_80.txt")
381
382        # Choose the right d_max...
383        self.invertor.d_max = 160.0
384        # Set a small alpha
385        self.invertor.alpha = .0007
386        # Set data
387        self.invertor.x   = x
388        self.invertor.y   = y
389        self.invertor.err = err
390        # Perform inversion
391       
392        out, cov = self.invertor.lstsq(10)
393       
394        # Save
395        self.invertor.to_file("test_output.txt")
396   
397    def test_load(self):
398        self.invertor.from_file("test_output.txt")
399        self.assertEqual(self.invertor.d_max, 160.0)
400        self.assertEqual(self.invertor.alpha, 0.0007)
401        self.assertEqual(self.invertor.chi2, 836.797)
402        self.assertAlmostEqual(self.invertor.pr(self.invertor.out, 10.0), 903.31577041, 4)
403       
404    def test_qmin(self):
405        self.invertor.q_min = 1.0
406        self.assertEqual(self.invertor.q_min, 1.0)
407       
408        self.invertor.q_min = None
409        self.assertEqual(self.invertor.q_min, None)
410       
411                         
412    def test_qmax(self):
413        self.invertor.q_max = 1.0
414        self.assertEqual(self.invertor.q_max, 1.0)
415       
416        self.invertor.q_max = None
417        self.assertEqual(self.invertor.q_max, None)
418
419class TestErrorConditions(unittest.TestCase):
420   
421    def setUp(self):
422        self.invertor = Invertor()
423        self.invertor.d_max = 100.0
424       
425        # Test array
426        self.ntest = 5
427        self.x_in = numpy.ones(self.ntest)
428        for i in range(self.ntest):
429            self.x_in[i] = 1.0*(i+1)
430
431    def test_negative_errs(self):
432        """
433            Test an inversion for which we know the answer
434        """
435        x, y, err = load("data_error_1.txt")
436
437        # Choose the right d_max...
438        self.invertor.d_max = 160.0
439        # Set a small alpha
440        self.invertor.alpha = .0007
441        # Set data
442        self.invertor.x   = x
443        self.invertor.y   = y
444        self.invertor.err = err
445        # Perform inversion
446       
447        out, cov = self.invertor.lstsq(10)
448         
449    def test_zero_errs(self):
450        """
451            Have zero as an error should raise an exception
452        """
453        x, y, err = load("data_error_2.txt")
454
455        # Set data
456        self.invertor.x   = x
457        self.invertor.y   = y
458        self.invertor.err = err
459        # Perform inversion
460        self.assertRaises(ValueError, self.invertor.invert, 10)
461         
462       
463    def test_invalid(self):
464        """
465            Test an inversion for which we know the answer
466        """
467        x, y, err = load("data_error_1.txt")
468
469        # Set data
470        self.invertor.x   = x
471        self.invertor.y   = y
472        err = numpy.zeros(len(x)-1)
473        self.invertor.err = err
474        # Perform inversion
475        self.assertRaises(RuntimeError, self.invertor.invert, 10)
476         
477       
478    def test_zero_q(self):
479        """
480            One of the q-values is zero.
481            An exception should be raised.
482        """
483        x, y, err = load("data_error_3.txt")
484
485        # Set data
486        self.assertRaises(ValueError, self.invertor.__setattr__, 'x', x)
487       
488    def test_zero_Iq(self):
489        """
490            One of the I(q) points has a value of zero
491            Should not complain or crash.
492        """
493        x, y, err = load("data_error_4.txt")
494
495        # Set data
496        self.invertor.x   = x
497        self.invertor.y   = y
498        self.invertor.err = err
499        # Perform inversion
500        out, cov = self.invertor.lstsq(10)
501   
502    def test_negative_q(self):
503        """
504            One q value is negative.
505            Makes not sense, but should not complain or crash.
506        """
507        x, y, err = load("data_error_5.txt")
508
509        # Set data
510        self.invertor.x   = x
511        self.invertor.y   = y
512        self.invertor.err = err
513        # Perform inversion
514        out, cov = self.invertor.lstsq(10)
515   
516    def test_negative_Iq(self):
517        """
518            One I(q) value is negative.
519            Makes not sense, but should not complain or crash.
520        """
521        x, y, err = load("data_error_6.txt")
522
523        # Set data
524        self.invertor.x   = x
525        self.invertor.y   = y
526        self.invertor.err = err
527        # Perform inversion
528        out, cov = self.invertor.lstsq(10)
529       
530    def test_nodata(self):
531        """
532             No data was loaded. Should not complain.
533        """
534        out, cov = self.invertor.lstsq(10)
535   
536
537       
538def pr_theory(r, R):
539    """
540       P(r) for a sphere
541    """
542    if r<=2*R:
543        return 12.0* ((0.5*r/R)**2) * ((1.0-0.5*r/R)**2) * ( 2.0 + 0.5*r/R )
544    else:
545        return 0.0
546   
547def load(path = "sphere_60_q0_2.txt"):
548    import numpy, math, sys
549    # Read the data from the data file
550    data_x   = numpy.zeros(0)
551    data_y   = numpy.zeros(0)
552    data_err = numpy.zeros(0)
553    scale    = None
554    if not path == None:
555        input_f = open(path,'r')
556        buff    = input_f.read()
557        lines   = buff.split('\n')
558        for line in lines:
559            try:
560                toks = line.split()
561                x = float(toks[0])
562                y = float(toks[1])
563                if len(toks)>2:
564                    err = float(toks[2])
565                else:
566                    if scale==None:
567                        scale = 0.15*math.sqrt(y)
568                    err = scale*math.sqrt(y)
569                data_x = numpy.append(data_x, x)
570                data_y = numpy.append(data_y, y)
571                data_err = numpy.append(data_err, err)
572            except:
573                pass
574               
575    return data_x, data_y, data_err
576       
577if __name__ == '__main__':
578    unittest.main()
Note: See TracBrowser for help on using the repository browser.