source: sasmodels/explore/precision.py @ ea60e08

core_shell_microgelsmagnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since ea60e08 was 2a602c7, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

check the single precision accuracy on cephes expm1() replacement function in kernel_header.c

  • Property mode set to 100755
File size: 20.8 KB
Line 
1#!/usr/bin/env python
2r"""
3Show numerical precision of various expressions.
4
5Evaluates the same function(s) in single and double precision and compares
6the results to 500 digit mpmath evaluation of the same function.
7
8Note: a quick way to generation C and python code for taylor series
9expansions from sympy:
10
11    import sympy as sp
12    x = sp.var("x")
13    f = sp.sin(x)/x
14    t = sp.series(f, n=12).removeO()  # taylor series with no O(x^n) term
15    p = sp.horner(t)   # Horner representation
16    p = p.replace(x**2, sp.var("xsq")  # simplify if alternate terms are zero
17    p = p.n(15)  # evaluate coefficients to 15 digits (optional)
18    c_code = sp.ccode(p, assign_to=sp.var("p"))  # convert to c code
19    py_code = c[:-1]  # strip semicolon to convert c to python
20
21    # mpmath has pade() rational function approximation, which might work
22    # better than the taylor series for some functions:
23    P, Q = mp.pade(sp.Poly(t.n(15),x).coeffs(), L, M)
24    P = sum(a*x**n for n,a in enumerate(reversed(P)))
25    Q = sum(a*x**n for n,a in enumerate(reversed(Q)))
26    c_code = sp.ccode(sp.horner(P)/sp.horner(Q), assign_to=sp.var("p"))
27
28    # There are richardson and shanks series accelerators in both sympy
29    # and mpmath that may be helpful.
30"""
31from __future__ import division, print_function
32
33import sys
34import os
35sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
36
37import numpy as np
38from numpy import pi, inf
39import scipy.special
40try:
41    from mpmath import mp
42except ImportError:
43    # CRUFT: mpmath split out into its own package
44    from sympy.mpmath import mp
45#import matplotlib; matplotlib.use('TkAgg')
46import pylab
47
48from sasmodels import core, data, direct_model, modelinfo
49
50class Comparator(object):
51    def __init__(self, name, mp_function, np_function, ocl_function, xaxis, limits):
52        self.name = name
53        self.mp_function = mp_function
54        self.np_function = np_function
55        self.ocl_function = ocl_function
56        self.xaxis = xaxis
57        self.limits = limits
58
59    def __repr__(self):
60        return "Comparator(%s)"%self.name
61
62    def call_mpmath(self, vec, bits=500):
63        """
64        Direct calculation using mpmath extended precision library.
65        """
66        with mp.workprec(bits):
67            return [self.mp_function(mp.mpf(x)) for x in vec]
68
69    def call_numpy(self, x, dtype):
70        """
71        Direct calculation using numpy/scipy.
72        """
73        x = np.asarray(x, dtype)
74        return self.np_function(x)
75
76    def call_ocl(self, x, dtype, platform='ocl'):
77        """
78        Calculation using sasmodels ocl libraries.
79        """
80        x = np.asarray(x, dtype)
81        model = core.build_model(self.ocl_function, dtype=dtype)
82        calculator = direct_model.DirectModel(data.empty_data1D(x), model)
83        return calculator(background=0)
84
85    def run(self, xrange="log", diff="relative"):
86        r"""
87        Compare accuracy of different methods for computing f.
88
89        *xrange* is::
90
91            log:    [10^-3,10^5]
92            logq:   [10^-4, 10^1]
93            linear: [1,1000]
94            zoom:   [1000,1010]
95            neg:    [-100,100]
96
97        *diff* is "relative", "absolute" or "none"
98
99        *x_bits* is the precision with which the x values are specified.  The
100        default 23 should reproduce the equivalent of a single precisio
101        """
102        linear = not xrange.startswith("log")
103        if xrange == "zoom":
104            lin_min, lin_max, lin_steps = 1000, 1010, 2000
105        elif xrange == "neg":
106            lin_min, lin_max, lin_steps = -100.1, 100.1, 2000
107        elif xrange == "linear":
108            lin_min, lin_max, lin_steps = 1, 1000, 2000
109        elif xrange == "log":
110            log_min, log_max, log_steps = -3, 5, 400
111        elif xrange == "logq":
112            log_min, log_max, log_steps = -4, 1, 400
113        else:
114            raise ValueError("unknown range "+xrange)
115        with mp.workprec(500):
116            # Note: we make sure that we are comparing apples to apples...
117            # The x points are set using single precision so that we are
118            # examining the accuracy of the transformation from x to f(x)
119            # rather than x to f(nearest(x)) where nearest(x) is the nearest
120            # value to x in the given precision.
121            if linear:
122                lin_min = max(lin_min, self.limits[0])
123                lin_max = min(lin_max, self.limits[1])
124                qrf = np.linspace(lin_min, lin_max, lin_steps, dtype='single')
125                #qrf = np.linspace(lin_min, lin_max, lin_steps, dtype='double')
126                qr = [mp.mpf(float(v)) for v in qrf]
127                #qr = mp.linspace(lin_min, lin_max, lin_steps)
128            else:
129                log_min = np.log10(max(10**log_min, self.limits[0]))
130                log_max = np.log10(min(10**log_max, self.limits[1]))
131                qrf = np.logspace(log_min, log_max, log_steps, dtype='single')
132                #qrf = np.logspace(log_min, log_max, log_steps, dtype='double')
133                qr = [mp.mpf(float(v)) for v in qrf]
134                #qr = [10**v for v in mp.linspace(log_min, log_max, log_steps)]
135
136        target = self.call_mpmath(qr, bits=500)
137        pylab.subplot(121)
138        self.compare(qr, 'single', target, linear, diff)
139        pylab.legend(loc='best')
140        pylab.subplot(122)
141        self.compare(qr, 'double', target, linear, diff)
142        pylab.legend(loc='best')
143        pylab.suptitle(self.name + " compared to 500-bit mpmath")
144
145    def compare(self, x, precision, target, linear=False, diff="relative"):
146        r"""
147        Compare the different computation methods using the given precision.
148        """
149        if precision == 'single':
150            #n=11; plotdiff(x, target, self.call_mpmath(x, n), 'mp %d bits'%n, diff=diff)
151            #n=23; plotdiff(x, target, self.call_mpmath(x, n), 'mp %d bits'%n, diff=diff)
152            pass
153        elif precision == 'double':
154            #n=53; plotdiff(x, target, self.call_mpmath(x, n), 'mp %d bits'%n, diff=diff)
155            #n=83; plotdiff(x, target, self.call_mpmath(x, n), 'mp %d bits'%n, diff=diff)
156            pass
157        plotdiff(x, target, self.call_numpy(x, precision), 'numpy '+precision, diff=diff)
158        plotdiff(x, target, self.call_ocl(x, precision, 0), 'OpenCL '+precision, diff=diff)
159        pylab.xlabel(self.xaxis)
160        if diff == "relative":
161            pylab.ylabel("relative error")
162        elif diff == "absolute":
163            pylab.ylabel("absolute error")
164        else:
165            pylab.ylabel(self.name)
166            pylab.semilogx(x, target, '-', label="true value")
167        if linear:
168            pylab.xscale('linear')
169
170def plotdiff(x, target, actual, label, diff):
171    """
172    Plot the computed value.
173
174    Use relative error if SHOW_DIFF, otherwise just plot the value directly.
175    """
176    if diff == "relative":
177        err = np.array([abs((t-a)/t) for t, a in zip(target, actual)], 'd')
178        #err = np.clip(err, 0, 1)
179        pylab.loglog(x, err, '-', label=label)
180    elif diff == "absolute":
181        err = np.array([abs((t-a)) for t, a in zip(target, actual)], 'd')
182        pylab.loglog(x, err, '-', label=label)
183    else:
184        limits = np.min(target), np.max(target)
185        pylab.semilogx(x, np.clip(actual, *limits), '-', label=label)
186
187def make_ocl(function, name, source=[]):
188    class Kernel(object):
189        pass
190    Kernel.__file__ = name+".py"
191    Kernel.name = name
192    Kernel.parameters = []
193    Kernel.source = source
194    Kernel.Iq = function
195    model_info = modelinfo.make_model_info(Kernel)
196    return model_info
197
198
199# =============== FUNCTION DEFINITIONS ================
200
201FUNCTIONS = {}
202def add_function(name, mp_function, np_function, ocl_function,
203                 shortname=None, xaxis="x", limits=(-inf, inf)):
204    if shortname is None:
205        shortname = name.replace('(x)', '').replace(' ', '')
206    FUNCTIONS[shortname] = Comparator(name, mp_function, np_function, ocl_function, xaxis, limits)
207
208add_function(
209    name="J0(x)",
210    mp_function=mp.j0,
211    np_function=scipy.special.j0,
212    ocl_function=make_ocl("return sas_J0(q);", "sas_J0", ["lib/polevl.c", "lib/sas_J0.c"]),
213)
214add_function(
215    name="J1(x)",
216    mp_function=mp.j1,
217    np_function=scipy.special.j1,
218    ocl_function=make_ocl("return sas_J1(q);", "sas_J1", ["lib/polevl.c", "lib/sas_J1.c"]),
219)
220add_function(
221    name="JN(-3, x)",
222    mp_function=lambda x: mp.besselj(-3, x),
223    np_function=lambda x: scipy.special.jn(-3, x),
224    ocl_function=make_ocl("return sas_JN(-3, q);", "sas_JN",
225                          ["lib/polevl.c", "lib/sas_J0.c", "lib/sas_J1.c", "lib/sas_JN.c"]),
226    shortname="J-3",
227)
228add_function(
229    name="JN(3, x)",
230    mp_function=lambda x: mp.besselj(3, x),
231    np_function=lambda x: scipy.special.jn(3, x),
232    ocl_function=make_ocl("return sas_JN(3, q);", "sas_JN",
233                          ["lib/polevl.c", "lib/sas_J0.c", "lib/sas_J1.c", "lib/sas_JN.c"]),
234    shortname="J3",
235)
236add_function(
237    name="JN(2, x)",
238    mp_function=lambda x: mp.besselj(2, x),
239    np_function=lambda x: scipy.special.jn(2, x),
240    ocl_function=make_ocl("return sas_JN(2, q);", "sas_JN",
241                          ["lib/polevl.c", "lib/sas_J0.c", "lib/sas_J1.c", "lib/sas_JN.c"]),
242    shortname="J2",
243)
244add_function(
245    name="2 J1(x)/x",
246    mp_function=lambda x: 2*mp.j1(x)/x,
247    np_function=lambda x: 2*scipy.special.j1(x)/x,
248    ocl_function=make_ocl("return sas_2J1x_x(q);", "sas_2J1x_x", ["lib/polevl.c", "lib/sas_J1.c"]),
249)
250add_function(
251    name="J1(x)",
252    mp_function=mp.j1,
253    np_function=scipy.special.j1,
254    ocl_function=make_ocl("return sas_J1(q);", "sas_J1", ["lib/polevl.c", "lib/sas_J1.c"]),
255)
256add_function(
257    name="Si(x)",
258    mp_function=mp.si,
259    np_function=lambda x: scipy.special.sici(x)[0],
260    ocl_function=make_ocl("return sas_Si(q);", "sas_Si", ["lib/sas_Si.c"]),
261)
262#import fnlib
263#add_function(
264#    name="fnlibJ1",
265#    mp_function=mp.j1,
266#    np_function=fnlib.J1,
267#    ocl_function=make_ocl("return sas_J1(q);", "sas_J1", ["lib/polevl.c", "lib/sas_J1.c"]),
268#)
269add_function(
270    name="sin(x)",
271    mp_function=mp.sin,
272    np_function=np.sin,
273    #ocl_function=make_ocl("double sn, cn; SINCOS(q,sn,cn); return sn;", "sas_sin"),
274    ocl_function=make_ocl("return sin(q);", "sas_sin"),
275)
276add_function(
277    name="sin(x)/x",
278    mp_function=lambda x: mp.sin(x)/x if x != 0 else 1,
279    ## scipy sinc function is inaccurate and has an implied pi*x term
280    #np_function=lambda x: scipy.special.sinc(x/pi),
281    ## numpy sin(x)/x needs to check for x=0
282    np_function=lambda x: np.sin(x)/x,
283    ocl_function=make_ocl("return sas_sinx_x(q);", "sas_sinc"),
284)
285add_function(
286    name="cos(x)",
287    mp_function=mp.cos,
288    np_function=np.cos,
289    #ocl_function=make_ocl("double sn, cn; SINCOS(q,sn,cn); return cn;", "sas_cos"),
290    ocl_function=make_ocl("return cos(q);", "sas_cos"),
291)
292add_function(
293    name="gamma(x)",
294    mp_function=mp.gamma,
295    np_function=scipy.special.gamma,
296    ocl_function=make_ocl("return sas_gamma(q);", "sas_gamma", ["lib/sas_gamma.c"]),
297    limits=(-3.1, 10),
298)
299add_function(
300    name="erf(x)",
301    mp_function=mp.erf,
302    np_function=scipy.special.erf,
303    ocl_function=make_ocl("return sas_erf(q);", "sas_erf", ["lib/polevl.c", "lib/sas_erf.c"]),
304    limits=(-5., 5.),
305)
306add_function(
307    name="erfc(x)",
308    mp_function=mp.erfc,
309    np_function=scipy.special.erfc,
310    ocl_function=make_ocl("return sas_erfc(q);", "sas_erfc", ["lib/polevl.c", "lib/sas_erf.c"]),
311    limits=(-5., 5.),
312)
313add_function(
314    name="expm1(x)",
315    mp_function=mp.expm1,
316    np_function=np.expm1,
317    ocl_function=make_ocl("return expm1(q);", "sas_expm1"),
318    limits=(-5., 5.),
319)
320add_function(
321    name="arctan(x)",
322    mp_function=mp.atan,
323    np_function=np.arctan,
324    ocl_function=make_ocl("return atan(q);", "sas_arctan"),
325)
326add_function(
327    name="3 j1(x)/x",
328    mp_function=lambda x: 3*(mp.sin(x)/x - mp.cos(x))/(x*x),
329    # Note: no taylor expansion near 0
330    np_function=lambda x: 3*(np.sin(x)/x - np.cos(x))/(x*x),
331    ocl_function=make_ocl("return sas_3j1x_x(q);", "sas_j1c", ["lib/sas_3j1x_x.c"]),
332)
333add_function(
334    name="(1-cos(x))/x^2",
335    mp_function=lambda x: (1 - mp.cos(x))/(x*x),
336    np_function=lambda x: (1 - np.cos(x))/(x*x),
337    ocl_function=make_ocl("return (1-cos(q))/q/q;", "sas_1mcosx_x2"),
338)
339add_function(
340    name="(1-sin(x)/x)/x",
341    mp_function=lambda x: 1/x - mp.sin(x)/(x*x),
342    np_function=lambda x: 1/x - np.sin(x)/(x*x),
343    ocl_function=make_ocl("return (1-sas_sinx_x(q))/q;", "sas_1msinx_x_x"),
344)
345add_function(
346    name="(1/2+(1-cos(x))/x^2-sin(x)/x)/x",
347    mp_function=lambda x: (0.5 - mp.sin(x)/x + (1-mp.cos(x))/(x*x))/x,
348    np_function=lambda x: (0.5 - np.sin(x)/x + (1-np.cos(x))/(x*x))/x,
349    ocl_function=make_ocl("return (0.5-sin(q)/q + (1-cos(q))/q/q)/q;", "sas_T2"),
350)
351add_function(
352    name="fmod_2pi",
353    mp_function=lambda x: mp.fmod(x, 2*mp.pi),
354    np_function=lambda x: np.fmod(x, 2*np.pi),
355    ocl_function=make_ocl("return fmod(q, 2*M_PI);", "sas_fmod"),
356)
357
358RADIUS=3000
359LENGTH=30
360THETA=45
361def mp_cyl(x):
362    f = mp.mpf
363    theta = f(THETA)*mp.pi/f(180)
364    qr = x * f(RADIUS)*mp.sin(theta)
365    qh = x * f(LENGTH)/f(2)*mp.cos(theta)
366    be = f(2)*mp.j1(qr)/qr
367    si = mp.sin(qh)/qh
368    background = f(0)
369    #background = f(1)/f(1000)
370    volume = mp.pi*f(RADIUS)**f(2)*f(LENGTH)
371    contrast = f(5)
372    units = f(1)/f(10000)
373    #return be
374    #return si
375    return units*(volume*contrast*be*si)**f(2)/volume + background
376def np_cyl(x):
377    f = np.float64 if x.dtype == np.float64 else np.float32
378    theta = f(THETA)*f(np.pi)/f(180)
379    qr = x * f(RADIUS)*np.sin(theta)
380    qh = x * f(LENGTH)/f(2)*np.cos(theta)
381    be = f(2)*scipy.special.j1(qr)/qr
382    si = np.sin(qh)/qh
383    background = f(0)
384    #background = f(1)/f(1000)
385    volume = f(np.pi)*f(RADIUS)**2*f(LENGTH)
386    contrast = f(5)
387    units = f(1)/f(10000)
388    #return be
389    #return si
390    return units*(volume*contrast*be*si)**f(2)/volume + background
391ocl_cyl = """\
392    double THETA = %(THETA).15e*M_PI_180;
393    double qr = q*%(RADIUS).15e*sin(THETA);
394    double qh = q*0.5*%(LENGTH).15e*cos(THETA);
395    double be = sas_2J1x_x(qr);
396    double si = sas_sinx_x(qh);
397    double background = 0;
398    //double background = 0.001;
399    double volume = M_PI*square(%(RADIUS).15e)*%(LENGTH).15e;
400    double contrast = 5.0;
401    double units = 1e-4;
402    //return be;
403    //return si;
404    return units*square(volume*contrast*be*si)/volume + background;
405"""%{"LENGTH":LENGTH, "RADIUS": RADIUS, "THETA": THETA}
406add_function(
407    name="cylinder(r=%g, l=%g, theta=%g)"%(RADIUS, LENGTH, THETA),
408    mp_function=mp_cyl,
409    np_function=np_cyl,
410    ocl_function=make_ocl(ocl_cyl, "ocl_cyl", ["lib/polevl.c", "lib/sas_J1.c"]),
411    shortname="cylinder",
412    xaxis="$q/A^{-1}$",
413)
414
415lanczos_gamma = """\
416    const double coeff[] = {
417            76.18009172947146,     -86.50532032941677,
418            24.01409824083091,     -1.231739572450155,
419            0.1208650973866179e-2,-0.5395239384953e-5
420            };
421    const double x = q;
422    double tmp  = x + 5.5;
423    tmp -= (x + 0.5)*log(tmp);
424    double ser = 1.000000000190015;
425    for (int k=0; k < 6; k++) ser += coeff[k]/(x + k+1);
426    return -tmp + log(2.5066282746310005*ser/x);
427"""
428add_function(
429    name="log gamma(x)",
430    mp_function=mp.loggamma,
431    np_function=scipy.special.gammaln,
432    ocl_function=make_ocl(lanczos_gamma, "lgamma"),
433)
434
435replacement_expm1 = """\
436      double x = (double)q;  // go back to float for single precision kernels
437      // Adapted from the cephes math library.
438      // Copyright 1984 - 1992 by Stephen L. Moshier
439      if (x != x || x == 0.0) {
440         return x; // NaN and +/- 0
441      } else if (x < -0.5 || x > 0.5) {
442         return exp(x) - 1.0;
443      } else {
444         const double xsq = x*x;
445         const double p = (((
446            +1.2617719307481059087798E-4)*xsq
447            +3.0299440770744196129956E-2)*xsq
448            +9.9999999999999999991025E-1);
449         const double q = ((((
450            +3.0019850513866445504159E-6)*xsq
451            +2.5244834034968410419224E-3)*xsq
452            +2.2726554820815502876593E-1)*xsq
453            +2.0000000000000000000897E0);
454         double r = x * p;
455         r =  r / (q - r);
456         return r+r;
457       }
458"""
459add_function(
460    name="sas_expm1(x)",
461    mp_function=mp.expm1,
462    np_function=np.expm1,
463    ocl_function=make_ocl(replacement_expm1, "sas_expm1"),
464)
465
466# Alternate versions of 3 j1(x)/x, for posterity
467def taylor_3j1x_x(x):
468    """
469    Calculation using taylor series.
470    """
471    # Generate coefficients using the precision of the target value.
472    n = 5
473    cinv = [3991680, -45360, 840, -30, 3]
474    three = x.dtype.type(3)
475    p = three/np.array(cinv, x.dtype)
476    return np.polyval(p[-n:], x*x)
477add_function(
478    name="3 j1(x)/x: taylor",
479    mp_function=lambda x: 3*(mp.sin(x)/x - mp.cos(x))/(x*x),
480    np_function=taylor_3j1x_x,
481    ocl_function=make_ocl("return sas_3j1x_x(q);", "sas_j1c", ["lib/sas_3j1x_x.c"]),
482)
483def trig_3j1x_x(x):
484    r"""
485    Direct calculation using linear combination of sin/cos.
486
487    Use the following trig identity:
488
489    .. math::
490
491        a \sin(x) + b \cos(x) = c \sin(x + \phi)
492
493    where $c = \surd(a^2+b^2)$ and $\phi = \tan^{-1}(b/a) to calculate the
494    numerator $\sin(x) - x\cos(x)$.
495    """
496    one = x.dtype.type(1)
497    three = x.dtype.type(3)
498    c = np.sqrt(one + x*x)
499    phi = np.arctan2(-x, one)
500    return three*(c*np.sin(x+phi))/(x*x*x)
501add_function(
502    name="3 j1(x)/x: trig",
503    mp_function=lambda x: 3*(mp.sin(x)/x - mp.cos(x))/(x*x),
504    np_function=trig_3j1x_x,
505    ocl_function=make_ocl("return sas_3j1x_x(q);", "sas_j1c", ["lib/sas_3j1x_x.c"]),
506)
507def np_2J1x_x(x):
508    """
509    numpy implementation of 2J1(x)/x using single precision algorithm
510    """
511    # pylint: disable=bad-continuation
512    f = x.dtype.type
513    ax = abs(x)
514    if ax < f(8.0):
515        y = x*x
516        ans1 = f(2)*(f(72362614232.0)
517                  + y*(f(-7895059235.0)
518                  + y*(f(242396853.1)
519                  + y*(f(-2972611.439)
520                  + y*(f(15704.48260)
521                  + y*(f(-30.16036606)))))))
522        ans2 = (f(144725228442.0)
523                  + y*(f(2300535178.0)
524                  + y*(f(18583304.74)
525                  + y*(f(99447.43394)
526                  + y*(f(376.9991397)
527                  + y)))))
528        return ans1/ans2
529    else:
530        y = f(64.0)/(ax*ax)
531        xx = ax - f(2.356194491)
532        ans1 = (f(1.0)
533                  + y*(f(0.183105e-2)
534                  + y*(f(-0.3516396496e-4)
535                  + y*(f(0.2457520174e-5)
536                  + y*f(-0.240337019e-6)))))
537        ans2 = (f(0.04687499995)
538                  + y*(f(-0.2002690873e-3)
539                  + y*(f(0.8449199096e-5)
540                  + y*(f(-0.88228987e-6)
541                  + y*f(0.105787412e-6)))))
542        sn, cn = np.sin(xx), np.cos(xx)
543        ans = np.sqrt(f(0.636619772)/ax) * (cn*ans1 - (f(8.0)/ax)*sn*ans2) * f(2)/x
544        return -ans if (x < f(0.0)) else ans
545add_function(
546    name="2 J1(x)/x:alt",
547    mp_function=lambda x: 2*mp.j1(x)/x,
548    np_function=lambda x: np.asarray([np_2J1x_x(v) for v in x], x.dtype),
549    ocl_function=make_ocl("return sas_2J1x_x(q);", "sas_2J1x_x", ["lib/polevl.c", "lib/sas_J1.c"]),
550)
551
552ALL_FUNCTIONS = set(FUNCTIONS.keys())
553ALL_FUNCTIONS.discard("loggamma")  # OCL version not ready yet
554ALL_FUNCTIONS.discard("3j1/x:taylor")
555ALL_FUNCTIONS.discard("3j1/x:trig")
556ALL_FUNCTIONS.discard("2J1/x:alt")
557
558# =============== MAIN PROGRAM ================
559
560def usage():
561    names = ", ".join(sorted(ALL_FUNCTIONS))
562    print("""\
563usage: precision.py [-f/a/r] [-x<range>] name...
564where
565    -f indicates that the function value should be plotted,
566    -a indicates that the absolute error should be plotted,
567    -r indicates that the relative error should be plotted (default),
568    -x<range> indicates the steps in x, where <range> is one of the following
569      log indicates log stepping in [10^-3, 10^5] (default)
570      logq indicates log stepping in [10^-4, 10^1]
571      linear indicates linear stepping in [1, 1000]
572      zoom indicates linear stepping in [1000, 1010]
573      neg indicates linear stepping in [-100.1, 100.1]
574and name is "all [first]" or one of:
575    """+names)
576    sys.exit(1)
577
578def main():
579    import sys
580    diff = "relative"
581    xrange = "log"
582    options = [v for v in sys.argv[1:] if v.startswith('-')]
583    for opt in options:
584        if opt == '-f':
585            diff = "none"
586        elif opt == '-r':
587            diff = "relative"
588        elif opt == '-a':
589            diff = "absolute"
590        elif opt.startswith('-x'):
591            xrange = opt[2:]
592        else:
593            usage()
594
595    names = [v for v in sys.argv[1:] if not v.startswith('-')]
596    if not names:
597        usage()
598
599    if names[0] == "all":
600        cutoff = names[1] if len(names) > 1 else ""
601        names = list(sorted(ALL_FUNCTIONS))
602        names = [k for k in names if k >= cutoff]
603    if any(k not in FUNCTIONS for k in names):
604        usage()
605    multiple = len(names) > 1
606    pylab.interactive(multiple)
607    for k in names:
608        pylab.clf()
609        comparator = FUNCTIONS[k]
610        comparator.run(xrange=xrange, diff=diff)
611        if multiple:
612            raw_input()
613    if not multiple:
614        pylab.show()
615
616if __name__ == "__main__":
617    main()
Note: See TracBrowser for help on using the repository browser.