1 | r""" |
---|
2 | Show numerical precision of $2 J_1(x)/x$. |
---|
3 | """ |
---|
4 | import sys; sys.path.insert(0, '..') |
---|
5 | |
---|
6 | import numpy as np |
---|
7 | try: |
---|
8 | from mpmath import mp |
---|
9 | except: |
---|
10 | # CRUFT: mpmath split out into its own package |
---|
11 | from sympy.mpmath import mp |
---|
12 | #import matplotlib; matplotlib.use('TkAgg') |
---|
13 | import pylab |
---|
14 | |
---|
15 | |
---|
16 | SHOW_DIFF = True # True if show diff rather than function value |
---|
17 | #SHOW_DIFF = False # True if show diff rather than function value |
---|
18 | LINEAR_X = False # True if q is linearly spaced instead of log spaced |
---|
19 | #LINEAR_X = True # True if q is linearly spaced instead of log spaced |
---|
20 | FUNCTION = "2*J1(x)/x" |
---|
21 | |
---|
22 | def mp_fn(vec, bits=500): |
---|
23 | """ |
---|
24 | Direct calculation using sympy multiprecision library. |
---|
25 | """ |
---|
26 | with mp.workprec(bits): |
---|
27 | return [_mp_fn(mp.mpf(x)) for x in vec] |
---|
28 | |
---|
29 | def _mp_fn(x): |
---|
30 | """ |
---|
31 | Actual function that gets evaluated. The caller just vectorizes. |
---|
32 | """ |
---|
33 | return mp.mpf(2)*mp.j1(x)/x |
---|
34 | |
---|
35 | def np_fn(x, dtype): |
---|
36 | """ |
---|
37 | Direct calculation using scipy. |
---|
38 | """ |
---|
39 | from scipy.special import j1 as J1 |
---|
40 | x = np.asarray(x, dtype) |
---|
41 | return np.asarray(2, dtype)*J1(x)/x |
---|
42 | |
---|
43 | def sasmodels_fn(x, dtype, platform='ocl'): |
---|
44 | """ |
---|
45 | Calculation using pade approximant. |
---|
46 | """ |
---|
47 | from sasmodels import core, data, direct_model |
---|
48 | model = core.load_model('bessel', dtype=dtype) |
---|
49 | calculator = direct_model.DirectModel(data.empty_data1D(x), model) |
---|
50 | return calculator(background=0) |
---|
51 | |
---|
52 | def plotdiff(x, target, actual, label): |
---|
53 | """ |
---|
54 | Plot the computed value. |
---|
55 | |
---|
56 | Use relative error if SHOW_DIFF, otherwise just plot the value directly. |
---|
57 | """ |
---|
58 | if SHOW_DIFF: |
---|
59 | err = abs((target-actual)/target) |
---|
60 | #err = np.clip(err, 0, 1) |
---|
61 | pylab.loglog(x, err, '-', label=label) |
---|
62 | else: |
---|
63 | limits = np.min(target), np.max(target) |
---|
64 | pylab.semilogx(x, np.clip(actual,*limits), '-', label=label) |
---|
65 | |
---|
66 | def compare(x, precision, target): |
---|
67 | r""" |
---|
68 | Compare the different computation methods using the given precision. |
---|
69 | """ |
---|
70 | #plotdiff(x, target, mp_fn(x, 11), 'mp 11 bits') |
---|
71 | plotdiff(x, target, np_fn(x, precision), 'numpy '+precision) |
---|
72 | plotdiff(x, target, sasmodels_fn(x, precision, 0), 'sasmodels '+precision) |
---|
73 | pylab.xlabel("qr (1/Ang)") |
---|
74 | if SHOW_DIFF: |
---|
75 | pylab.ylabel("relative error") |
---|
76 | else: |
---|
77 | pylab.ylabel(FUNCTION) |
---|
78 | pylab.semilogx(x, target, '-', label="true value") |
---|
79 | if LINEAR_X: |
---|
80 | pylab.xscale('linear') |
---|
81 | |
---|
82 | def main(): |
---|
83 | r""" |
---|
84 | Compare accuracy of different methods for computing $3 j_1(x)/x$. |
---|
85 | :return: |
---|
86 | """ |
---|
87 | if LINEAR_X: |
---|
88 | qr = np.linspace(1,1000,2000) |
---|
89 | else: |
---|
90 | qr = np.logspace(-3,5,400) |
---|
91 | target = np.asarray(mp_fn(qr), 'double') |
---|
92 | pylab.subplot(121) |
---|
93 | compare(qr, 'single', target) |
---|
94 | pylab.legend(loc='best') |
---|
95 | pylab.subplot(122) |
---|
96 | compare(qr, 'double', target) |
---|
97 | pylab.legend(loc='best') |
---|
98 | pylab.suptitle(FUNCTION) |
---|
99 | |
---|
100 | if __name__ == "__main__": |
---|
101 | #print "\n".join(str(x) for x in mp_J1c([1e-6,1e-5,1e-4,1e-3])) |
---|
102 | main() |
---|
103 | pylab.show() |
---|