Changes in explore/precision.py [237c9cf:2a602c7] in sasmodels


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • explore/precision.py

    r237c9cf r2a602c7  
    11#!/usr/bin/env python 
    22r""" 
    3 Show numerical precision of $2 J_1(x)/x$. 
     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. 
    430""" 
    531from __future__ import division, print_function 
     
    81107        elif xrange == "linear": 
    82108            lin_min, lin_max, lin_steps = 1, 1000, 2000 
    83             lin_min, lin_max, lin_steps = 0.001, 2, 2000 
    84109        elif xrange == "log": 
    85110            log_min, log_max, log_steps = -3, 5, 400 
     
    287312) 
    288313add_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( 
    289321    name="arctan(x)", 
    290322    mp_function=mp.atan, 
     
    322354    np_function=lambda x: np.fmod(x, 2*np.pi), 
    323355    ocl_function=make_ocl("return fmod(q, 2*M_PI);", "sas_fmod"), 
    324 ) 
    325 add_function( 
    326     name="debye", 
    327     mp_function=lambda x: 2*(mp.exp(-x**2) + x**2 - 1)/x**4, 
    328     np_function=lambda x: 2*(np.expm1(-x**2) + x**2)/x**4, 
    329     ocl_function=make_ocl(""" 
    330     const double qsq = q*q; 
    331     if (qsq < 1.0) { // Pade approximation 
    332         const double x = qsq; 
    333         if (0) { // 0.36 single 
    334             // PadeApproximant[2*Exp[-x^2] + x^2-1)/x^4, {x, 0, 4}] 
    335             return (x*x/180. + 1.)/((1./30.*x + 1./3.)*x + 1); 
    336         } else if (0) { // 1.0 for single 
    337             // padeapproximant[2*exp[-x^2] + x^2-1)/x^4, {x, 0, 6}] 
    338             const double A1=1./24., A2=1./84, A3=-1./3360; 
    339             const double B1=3./8., B2=3./56., B3=1./336.; 
    340             return (((A3*x + A2)*x + A1)*x + 1.)/(((B3*x + B2)*x + B1)*x + 1.); 
    341         } else if (1) { // 1.0 for single, 0.25 for double 
    342             // PadeApproximant[2*Exp[-x^2] + x^2-1)/x^4, {x, 0, 8}] 
    343             const double A1=1./15., A2=1./60, A3=0., A4=1./75600.; 
    344             const double B1=2./5., B2=1./15., B3=1./180., B4=1./5040.; 
    345             return ((((A4*x + A3)*x + A2)*x + A1)*x + 1.) 
    346                   /((((B4*x + B3)*x + B2)*x + B1)*x + 1.); 
    347         } else { // 1.0 for single, 0.5 for double 
    348             // PadeApproximant[2*Exp[-x^2] + x^2-1)/x^4, {x, 0, 8}] 
    349             const double A1=1./12., A2=2./99., A3=1./2640., A4=1./23760., A5=-1./1995840.; 
    350             const double B1=5./12., B2=5./66., B3=1./132., B4=1./2376., B5=1./95040.; 
    351             return (((((A5*x + A4)*x + A3)*x + A2)*x + A1)*x + 1.) 
    352                   /(((((B5*x + B4)*x + B3)*x + B2)*x + B1)*x + 1.); 
    353         } 
    354     } else if (qsq < 1.) { // Taylor series; 0.9 for single, 0.25 for double 
    355         const double x = qsq; 
    356         const double C0 = +1.; 
    357         const double C1 = -1./3.; 
    358         const double C2 = +1./12.; 
    359         const double C3 = -1./60.; 
    360         const double C4 = +1./360.; 
    361         const double C5 = -1./2520.; 
    362         const double C6 = +1./20160.; 
    363         const double C7 = -1./181440.; 
    364         //return ((((C5*x + C4)*x + C3)*x + C2)*x + C1)*x + C0; 
    365         //return (((((C6*x + C5)*x + C4)*x + C3)*x + C2)*x + C1)*x + C0; 
    366         return ((((((C7*x + C6)*x + C5)*x + C4)*x + C3)*x + C2)*x + C1)*x + C0; 
    367     } else { 
    368         return 2.*(expm1(-qsq) + qsq)/(qsq*qsq); 
    369     } 
    370     """, "sas_debye"), 
    371356) 
    372357 
     
    448433) 
    449434 
     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 
    450466# Alternate versions of 3 j1(x)/x, for posterity 
    451467def taylor_3j1x_x(x): 
Note: See TracChangeset for help on using the changeset viewer.