Changes in / [153f8f6:aa8c6e0] in sasmodels
- Files:
-
- 1 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/guide/plugin.rst
r81751c2 r81751c2 744 744 erf, erfc, tgamma, lgamma: **do not use** 745 745 Special functions that should be part of the standard, but are missing 746 or inaccurate on some platforms. Use sas_erf, sas_erfc andsas_gamma747 instead (see below). Note: lgamma(x) has not yet been tested.746 or inaccurate on some platforms. Use sas_erf, sas_erfc, sas_gamma 747 and sas_lgamma instead (see below). 748 748 749 749 Some non-standard constants and functions are also provided: … … 812 812 Gamma function sas_gamma\ $(x) = \Gamma(x)$. 813 813 814 The standard math function, tgamma(x) is unstable for $x < 1$814 The standard math function, tgamma(x), is unstable for $x < 1$ 815 815 on some platforms. 816 816 817 817 :code:`source = ["lib/sas_gamma.c", ...]` 818 818 (`sas_gamma.c <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib/sas_gamma.c>`_) 819 820 sas_gammaln(x): 821 log gamma function sas_gammaln\ $(x) = \log \Gamma(|x|)$. 822 823 The standard math function, lgamma(x), is incorrect for single 824 precision on some platforms. 825 826 :code:`source = ["lib/sas_gammainc.c", ...]` 827 (`sas_gammainc.c <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib/sas_gammainc.c>`_) 828 829 sas_gammainc(a, x), sas_gammaincc(a, x): 830 Incomplete gamma function 831 sas_gammainc\ $(a, x) = \int_0^x t^{a-1}e^{-t}\,dt / \Gamma(a)$ 832 and complementary incomplete gamma function 833 sas_gammaincc\ $(a, x) = \int_x^\infty t^{a-1}e^{-t}\,dt / \Gamma(a)$ 834 835 :code:`source = ["lib/sas_gammainc.c", ...]` 836 (`sas_gammainc.c <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib/sas_gammainc.c>`_) 819 837 820 838 sas_erf(x), sas_erfc(x): … … 854 872 If $n$ = 0 or 1, it uses sas_J0($x$) or sas_J1($x$), respectively. 855 873 874 Warning: JN(n,x) can be very inaccurate (0.1%) for x not in [0.1, 100]. 875 856 876 The standard math function jn(n, x) is not available on all platforms. 857 877 … … 862 882 Sine integral Si\ $(x) = \int_0^x \tfrac{\sin t}{t}\,dt$. 863 883 884 Warning: Si(x) can be very inaccurate (0.1%) for x in [0.1, 100]. 885 864 886 This function uses Taylor series for small and large arguments: 865 887 866 For large arguments ,888 For large arguments use the following Taylor series, 867 889 868 890 .. math:: … … 872 894 - \frac{\sin(x)}{x}\left(\frac{1}{x} - \frac{3!}{x^3} + \frac{5!}{x^5} - \frac{7!}{x^7}\right) 873 895 874 For small arguments ,896 For small arguments , 875 897 876 898 .. math:: -
explore/precision.py
ree60aa7 rfba9ca0 95 95 neg: [-100,100] 96 96 97 For arbitrary range use "start:stop:steps:scale" where scale is 98 one of log, lin, or linear. 99 97 100 *diff* is "relative", "absolute" or "none" 98 101 … … 102 105 linear = not xrange.startswith("log") 103 106 if xrange == "zoom": 104 lin_min, lin_max, lin_steps = 1000, 1010, 2000107 start, stop, steps = 1000, 1010, 2000 105 108 elif xrange == "neg": 106 lin_min, lin_max, lin_steps = -100.1, 100.1, 2000109 start, stop, steps = -100.1, 100.1, 2000 107 110 elif xrange == "linear": 108 lin_min, lin_max, lin_steps = 1, 1000, 2000109 lin_min, lin_max, lin_steps = 0.001, 2, 2000111 start, stop, steps = 1, 1000, 2000 112 start, stop, steps = 0.001, 2, 2000 110 113 elif xrange == "log": 111 log_min, log_max, log_steps = -3, 5, 400114 start, stop, steps = -3, 5, 400 112 115 elif xrange == "logq": 113 log_min, log_max, log_steps = -4, 1, 400 116 start, stop, steps = -4, 1, 400 117 elif ':' in xrange: 118 parts = xrange.split(':') 119 linear = parts[3] != "log" if len(parts) == 4 else True 120 steps = int(parts[2]) if len(parts) > 2 else 400 121 start = float(parts[0]) 122 stop = float(parts[1]) 123 114 124 else: 115 125 raise ValueError("unknown range "+xrange) … … 121 131 # value to x in the given precision. 122 132 if linear: 123 lin_min = max(lin_min, self.limits[0])124 lin_max = min(lin_max, self.limits[1])125 qrf = np.linspace( lin_min, lin_max, lin_steps, dtype='single')126 #qrf = np.linspace( lin_min, lin_max, lin_steps, dtype='double')133 start = max(start, self.limits[0]) 134 stop = min(stop, self.limits[1]) 135 qrf = np.linspace(start, stop, steps, dtype='single') 136 #qrf = np.linspace(start, stop, steps, dtype='double') 127 137 qr = [mp.mpf(float(v)) for v in qrf] 128 #qr = mp.linspace( lin_min, lin_max, lin_steps)138 #qr = mp.linspace(start, stop, steps) 129 139 else: 130 log_min = np.log10(max(10**log_min, self.limits[0]))131 log_max = np.log10(min(10**log_max, self.limits[1]))132 qrf = np.logspace( log_min, log_max, log_steps, dtype='single')133 #qrf = np.logspace( log_min, log_max, log_steps, dtype='double')140 start = np.log10(max(10**start, self.limits[0])) 141 stop = np.log10(min(10**stop, self.limits[1])) 142 qrf = np.logspace(start, stop, steps, dtype='single') 143 #qrf = np.logspace(start, stop, steps, dtype='double') 134 144 qr = [mp.mpf(float(v)) for v in qrf] 135 #qr = [10**v for v in mp.linspace( log_min, log_max, log_steps)]145 #qr = [10**v for v in mp.linspace(start, stop, steps)] 136 146 137 147 target = self.call_mpmath(qr, bits=500) … … 176 186 """ 177 187 if diff == "relative": 178 err = np.array([ abs((t-a)/t) for t, a in zip(target, actual)], 'd')188 err = np.array([(abs((t-a)/t) if t != 0 else a) for t, a in zip(target, actual)], 'd') 179 189 #err = np.clip(err, 0, 1) 180 190 pylab.loglog(x, err, '-', label=label) … … 197 207 return model_info 198 208 209 # Hack to allow second parameter A in two parameter functions 210 A = 1 211 def parse_extra_pars(): 212 global A 213 214 A_str = str(A) 215 pop = [] 216 for k, v in enumerate(sys.argv[1:]): 217 if v.startswith("A="): 218 A_str = v[2:] 219 pop.append(k+1) 220 if pop: 221 sys.argv = [v for k, v in enumerate(sys.argv) if k not in pop] 222 A = float(A_str) 223 224 parse_extra_pars() 225 199 226 200 227 # =============== FUNCTION DEFINITIONS ================ … … 297 324 ocl_function=make_ocl("return sas_gamma(q);", "sas_gamma", ["lib/sas_gamma.c"]), 298 325 limits=(-3.1, 10), 326 ) 327 add_function( 328 name="gammaln(x)", 329 mp_function=mp.loggamma, 330 np_function=scipy.special.gammaln, 331 ocl_function=make_ocl("return sas_gammaln(q);", "sas_gammaln", ["lib/sas_gammainc.c"]), 332 #ocl_function=make_ocl("return lgamma(q);", "sas_gammaln"), 333 ) 334 add_function( 335 name="gammainc(x)", 336 mp_function=lambda x, a=A: mp.gammainc(a, a=0, b=x)/mp.gamma(a), 337 np_function=lambda x, a=A: scipy.special.gammainc(a, x), 338 ocl_function=make_ocl("return sas_gammainc(%.15g,q);"%A, "sas_gammainc", ["lib/sas_gammainc.c"]), 339 ) 340 add_function( 341 name="gammaincc(x)", 342 mp_function=lambda x, a=A: mp.gammainc(a, a=x, b=mp.inf)/mp.gamma(a), 343 np_function=lambda x, a=A: scipy.special.gammaincc(a, x), 344 ocl_function=make_ocl("return sas_gammaincc(%.15g,q);"%A, "sas_gammaincc", ["lib/sas_gammainc.c"]), 299 345 ) 300 346 add_function( … … 465 511 lanczos_gamma = """\ 466 512 const double coeff[] = { 467 76.18009172947146, 468 24.01409824083091, 513 76.18009172947146, -86.50532032941677, 514 24.01409824083091, -1.231739572450155, 469 515 0.1208650973866179e-2,-0.5395239384953e-5 470 516 }; … … 477 523 """ 478 524 add_function( 479 name="log 525 name="loggamma(x)", 480 526 mp_function=mp.loggamma, 481 527 np_function=scipy.special.gammaln, … … 601 647 602 648 ALL_FUNCTIONS = set(FUNCTIONS.keys()) 603 ALL_FUNCTIONS.discard("loggamma") # OCL version not ready yet649 ALL_FUNCTIONS.discard("loggamma") # use cephes-based gammaln instead 604 650 ALL_FUNCTIONS.discard("3j1/x:taylor") 605 651 ALL_FUNCTIONS.discard("3j1/x:trig") … … 617 663 -r indicates that the relative error should be plotted (default), 618 664 -x<range> indicates the steps in x, where <range> is one of the following 619 log indicates log stepping in [10^-3, 10^5] (default) 620 logq indicates log stepping in [10^-4, 10^1] 621 linear indicates linear stepping in [1, 1000] 622 zoom indicates linear stepping in [1000, 1010] 623 neg indicates linear stepping in [-100.1, 100.1] 624 and name is "all" or one of: 665 log indicates log stepping in [10^-3, 10^5] (default) 666 logq indicates log stepping in [10^-4, 10^1] 667 linear indicates linear stepping in [1, 1000] 668 zoom indicates linear stepping in [1000, 1010] 669 neg indicates linear stepping in [-100.1, 100.1] 670 start:stop:n[:stepping] indicates an n-step plot in [start, stop] 671 or [10^start, 10^stop] if stepping is "log" (default n=400) 672 Some functions (notably gammainc/gammaincc) have an additional parameter A 673 which can be set from the command line as A=value. Default is A=1. 674 675 Name is one of: 625 676 """+names) 626 677 sys.exit(1) -
sasmodels/kernelpy.py
re44432d re44432d 42 42 self.info = model_info 43 43 self.dtype = np.dtype('d') 44 logger.info("make python model " + self.info.name) 44 45 45 46 def make_kernel(self, q_vectors): -
sasmodels/model_test.py
r81751c2 r81751c2 47 47 import sys 48 48 import unittest 49 import traceback 49 50 50 51 try: … … 76 77 # pylint: enable=unused-import 77 78 78 79 79 def make_suite(loaders, models): 80 80 # type: (List[str], List[str]) -> unittest.TestSuite … … 87 87 *models* is the list of models to test, or *["all"]* to test all models. 88 88 """ 89 ModelTestCase = _hide_model_case_from_nose()90 89 suite = unittest.TestSuite() 91 90 … … 96 95 skip = [] 97 96 for model_name in models: 98 if model_name in skip: 99 continue 100 model_info = load_model_info(model_name) 101 102 #print('------') 103 #print('found tests in', model_name) 104 #print('------') 105 106 # if ispy then use the dll loader to call pykernel 107 # don't try to call cl kernel since it will not be 108 # available in some environmentes. 109 is_py = callable(model_info.Iq) 110 111 # Some OpenCL drivers seem to be flaky, and are not producing the 112 # expected result. Since we don't have known test values yet for 113 # all of our models, we are instead going to compare the results 114 # for the 'smoke test' (that is, evaluation at q=0.1 for the default 115 # parameters just to see that the model runs to completion) between 116 # the OpenCL and the DLL. To do this, we define a 'stash' which is 117 # shared between OpenCL and DLL tests. This is just a list. If the 118 # list is empty (which it will be when DLL runs, if the DLL runs 119 # first), then the results are appended to the list. If the list 120 # is not empty (which it will be when OpenCL runs second), the results 121 # are compared to the results stored in the first element of the list. 122 # This is a horrible stateful hack which only makes sense because the 123 # test suite is thrown away after being run once. 124 stash = [] 125 126 if is_py: # kernel implemented in python 127 test_name = "%s-python"%model_name 128 test_method_name = "test_%s_python" % model_info.id 97 if model_name not in skip: 98 model_info = load_model_info(model_name) 99 _add_model_to_suite(loaders, suite, model_info) 100 101 return suite 102 103 def _add_model_to_suite(loaders, suite, model_info): 104 ModelTestCase = _hide_model_case_from_nose() 105 106 #print('------') 107 #print('found tests in', model_name) 108 #print('------') 109 110 # if ispy then use the dll loader to call pykernel 111 # don't try to call cl kernel since it will not be 112 # available in some environmentes. 113 is_py = callable(model_info.Iq) 114 115 # Some OpenCL drivers seem to be flaky, and are not producing the 116 # expected result. Since we don't have known test values yet for 117 # all of our models, we are instead going to compare the results 118 # for the 'smoke test' (that is, evaluation at q=0.1 for the default 119 # parameters just to see that the model runs to completion) between 120 # the OpenCL and the DLL. To do this, we define a 'stash' which is 121 # shared between OpenCL and DLL tests. This is just a list. If the 122 # list is empty (which it will be when DLL runs, if the DLL runs 123 # first), then the results are appended to the list. If the list 124 # is not empty (which it will be when OpenCL runs second), the results 125 # are compared to the results stored in the first element of the list. 126 # This is a horrible stateful hack which only makes sense because the 127 # test suite is thrown away after being run once. 128 stash = [] 129 130 if is_py: # kernel implemented in python 131 test_name = "%s-python"%model_info.name 132 test_method_name = "test_%s_python" % model_info.id 133 test = ModelTestCase(test_name, model_info, 134 test_method_name, 135 platform="dll", # so that 136 dtype="double", 137 stash=stash) 138 suite.addTest(test) 139 else: # kernel implemented in C 140 141 # test using dll if desired 142 if 'dll' in loaders or not use_opencl(): 143 test_name = "%s-dll"%model_info.name 144 test_method_name = "test_%s_dll" % model_info.id 129 145 test = ModelTestCase(test_name, model_info, 130 test_method_name,131 platform="dll", # so that132 dtype="double",133 stash=stash)146 test_method_name, 147 platform="dll", 148 dtype="double", 149 stash=stash) 134 150 suite.addTest(test) 135 else: # kernel implemented in C 136 137 # test using dll if desired 138 if 'dll' in loaders: 139 test_name = "%s-dll"%model_name 140 test_method_name = "test_%s_dll" % model_info.id 141 test = ModelTestCase(test_name, model_info, 142 test_method_name, 143 platform="dll", 144 dtype="double", 145 stash=stash) 146 suite.addTest(test) 147 148 # test using opencl if desired and available 149 if 'opencl' in loaders and use_opencl(): 150 test_name = "%s-opencl"%model_name 151 test_method_name = "test_%s_opencl" % model_info.id 152 # Using dtype=None so that the models that are only 153 # correct for double precision are not tested using 154 # single precision. The choice is determined by the 155 # presence of *single=False* in the model file. 156 test = ModelTestCase(test_name, model_info, 157 test_method_name, 158 platform="ocl", dtype=None, 159 stash=stash) 160 #print("defining", test_name) 161 suite.addTest(test) 162 163 # test using cuda if desired and available 164 if 'cuda' in loaders and use_cuda(): 165 test_name = "%s-cuda"%model_name 166 test_method_name = "test_%s_cuda" % model_info.id 167 # Using dtype=None so that the models that are only 168 # correct for double precision are not tested using 169 # single precision. The choice is determined by the 170 # presence of *single=False* in the model file. 171 test = ModelTestCase(test_name, model_info, 172 test_method_name, 173 platform="cuda", dtype=None, 174 stash=stash) 175 #print("defining", test_name) 176 suite.addTest(test) 177 178 return suite 151 152 # test using opencl if desired and available 153 if 'opencl' in loaders and use_opencl(): 154 test_name = "%s-opencl"%model_info.name 155 test_method_name = "test_%s_opencl" % model_info.id 156 # Using dtype=None so that the models that are only 157 # correct for double precision are not tested using 158 # single precision. The choice is determined by the 159 # presence of *single=False* in the model file. 160 test = ModelTestCase(test_name, model_info, 161 test_method_name, 162 platform="ocl", dtype=None, 163 stash=stash) 164 #print("defining", test_name) 165 suite.addTest(test) 166 167 # test using cuda if desired and available 168 if 'cuda' in loaders and use_cuda(): 169 test_name = "%s-cuda"%model_name 170 test_method_name = "test_%s_cuda" % model_info.id 171 # Using dtype=None so that the models that are only 172 # correct for double precision are not tested using 173 # single precision. The choice is determined by the 174 # presence of *single=False* in the model file. 175 test = ModelTestCase(test_name, model_info, 176 test_method_name, 177 platform="cuda", dtype=None, 178 stash=stash) 179 #print("defining", test_name) 180 suite.addTest(test) 181 179 182 180 183 def _hide_model_case_from_nose(): … … 403 406 return abs(target-actual)/shift < 1.5*10**-digits 404 407 405 def run_one(model): 406 # type: (str) -> str 407 """ 408 Run the tests for a single model, printing the results to stdout. 409 410 *model* can by a python file, which is handy for checking user defined 411 plugin models. 408 # CRUFT: old interface; should be deprecated and removed 409 def run_one(model_name): 410 # msg = "use check_model(model_info) rather than run_one(model_name)" 411 # warnings.warn(msg, category=DeprecationWarning, stacklevel=2) 412 try: 413 model_info = load_model_info(model_name) 414 except Exception: 415 output = traceback.format_exc() 416 return output 417 418 success, output = check_model(model_info) 419 return output 420 421 def check_model(model_info): 422 # type: (ModelInfo) -> str 423 """ 424 Run the tests for a single model, capturing the output. 425 426 Returns success status and the output string. 412 427 """ 413 428 # Note that running main() directly did not work from within the … … 423 438 424 439 # Build a test suite containing just the model 425 loader = 'opencl' if use_opencl() else 'cuda' if use_cuda() else 'dll' 426 models = [model] 427 try: 428 suite = make_suite([loader], models) 429 except Exception: 430 import traceback 431 stream.writeln(traceback.format_exc()) 432 return 440 loaders = ['opencl' if use_opencl() else 'cuda' if use_cuda() else 'dll'] 441 suite = unittest.TestSuite() 442 _add_model_to_suite(loaders, suite, model_info) 433 443 434 444 # Warn if there are no user defined tests. … … 445 455 for test in suite: 446 456 if not test.info.tests: 447 stream.writeln("Note: %s has no user defined tests."%model )457 stream.writeln("Note: %s has no user defined tests."%model_info.name) 448 458 break 449 459 else: … … 461 471 output = stream.getvalue() 462 472 stream.close() 463 return output473 return result.wasSuccessful(), output 464 474 465 475 -
sasmodels/sasview_model.py
r39a06c9 r39a06c9 819 819 return value, [value], [1.0] 820 820 821 @classmethod 822 def runTests(cls): 823 """ 824 Run any tests built into the model and captures the test output. 825 826 Returns success flag and output 827 """ 828 from .model_test import check_model 829 return check_model(cls._model_info) 830 821 831 def test_cylinder(): 822 832 # type: () -> float -
sasmodels/special.py
rdf69efa rfba9ca0 113 113 The standard math function, tgamma(x) is unstable for $x < 1$ 114 114 on some platforms. 115 116 sas_gammaln(x): 117 log gamma function sas_gammaln\ $(x) = \log \Gamma(|x|)$. 118 119 The standard math function, lgamma(x), is incorrect for single 120 precision on some platforms. 121 122 sas_gammainc(a, x), sas_gammaincc(a, x): 123 Incomplete gamma function 124 sas_gammainc\ $(a, x) = \int_0^x t^{a-1}e^{-t}\,dt / \Gamma(a)$ 125 and complementary incomplete gamma function 126 sas_gammaincc\ $(a, x) = \int_x^\infty t^{a-1}e^{-t}\,dt / \Gamma(a)$ 115 127 116 128 sas_erf(x), sas_erfc(x): … … 207 219 from numpy import pi, nan, inf 208 220 from scipy.special import gamma as sas_gamma 221 from scipy.special import gammaln as sas_gammaln 222 from scipy.special import gammainc as sas_gammainc 223 from scipy.special import gammaincc as sas_gammaincc 209 224 from scipy.special import erf as sas_erf 210 225 from scipy.special import erfc as sas_erfc
Note: See TracChangeset
for help on using the changeset viewer.