Changeset b9c7379 in sasmodels for sasmodels/model_test.py


Ignore:
Timestamp:
Oct 29, 2018 7:27:38 AM (5 years ago)
Author:
Adam Washington <adam.washington@…>
Branches:
master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
a08c47f
Parents:
7b5898f (diff), 153f8f6 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'beta_approx' of github.com:SasView/sasmodels into test_args

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/model_test.py

    r7b5898f rb9c7379  
    55Usage:: 
    66 
    7     python -m sasmodels.model_test [opencl|dll|opencl_and_dll] model1 model2 ... 
     7    python -m sasmodels.model_test [opencl|cuda|dll] model1 model2 ... 
    88 
    99    if model1 is 'all', then all except the remaining models will be tested 
    1010 
    1111Each model is tested using the default parameters at q=0.1, (qx, qy)=(0.1, 0.1), 
    12 and the ER and VR are computed.  The return values at these points are not 
    13 considered.  The test is only to verify that the models run to completion, 
    14 and do not produce inf or NaN. 
     12and Fq is called to make sure R_eff, volume and volume ratio are computed. 
     13The return values at these points are not considered.  The test is only to 
     14verify that the models run to completion, and do not produce inf or NaN. 
    1515 
    1616Tests are defined with the *tests* attribute in the model.py file.  *tests* 
    1717is a list of individual tests to run, where each test consists of the 
    1818parameter values for the test, the q-values and the expected results.  For 
    19 the effective radius test, the q-value should be 'ER'.  For the VR test, 
    20 the q-value should be 'VR'.  For 1-D tests, either specify the q value or 
    21 a list of q-values, and the corresponding I(q) value, or list of I(q) values. 
     19the effective radius test and volume ratio tests, use the extended output 
     20form, which checks each output of kernel.Fq. For 1-D tests, either specify 
     21the q value or a list of q-values, and the corresponding I(q) value, or 
     22list of I(q) values. 
    2223 
    2324That is:: 
     
    3233                        [I(qx1, qy1), I(qx2, qy2), ...]], 
    3334 
    34         [ {parameters}, 'ER', ER(pars) ], 
    35         [ {parameters}, 'VR', VR(pars) ], 
     35        [ {parameters}, q, F(q), F^2(q), R_eff, V, V_r ], 
    3636        ... 
    3737    ] 
     
    6060from . import core 
    6161from .core import list_models, load_model_info, build_model 
    62 from .direct_model import call_kernel, call_ER, call_VR 
     62from .direct_model import call_kernel, call_Fq 
    6363from .exception import annotate_exception 
    6464from .modelinfo import expand_pars 
    6565from .kernelcl import use_opencl 
     66from .kernelcuda import use_cuda 
     67from . import product 
    6668 
    6769# pylint: disable=unused-import 
     
    8183    Construct the pyunit test suite. 
    8284 
    83     *loaders* is the list of kernel drivers to use, which is one of 
    84     *["dll", "opencl"]*, *["dll"]* or *["opencl"]*.  For python models, 
    85     the python driver is always used. 
     85    *loaders* is the list of kernel drivers to use (dll, opencl or cuda). 
     86    For python model the python driver is always used. 
    8687 
    8788    *models* is the list of models to test, or *["all"]* to test all models. 
     
    136137 
    137138            # test using dll if desired 
    138             if 'dll' in loaders or not use_opencl(): 
     139            if 'dll' in loaders: 
    139140                test_name = "%s-dll"%model_name 
    140141                test_method_name = "test_%s_dll" % model_info.id 
     
    157158                                     test_method_name, 
    158159                                     platform="ocl", dtype=None, 
     160                                     stash=stash) 
     161                #print("defining", test_name) 
     162                suite.addTest(test) 
     163 
     164            # test using cuda if desired and available 
     165            if 'cuda' in loaders and use_cuda(): 
     166                test_name = "%s-cuda"%model_name 
     167                test_method_name = "test_%s_cuda" % model_info.id 
     168                # Using dtype=None so that the models that are only 
     169                # correct for double precision are not tested using 
     170                # single precision.  The choice is determined by the 
     171                # presence of *single=False* in the model file. 
     172                test = ModelTestCase(test_name, model_info, 
     173                                     test_method_name, 
     174                                     platform="cuda", dtype=None, 
    159175                                     stash=stash) 
    160176                #print("defining", test_name) 
     
    200216                ({}, [0.001, 0.01, 0.1], [None]*3), 
    201217                ({}, [(0.1, 0.1)]*2, [None]*2), 
    202                 # test that ER/VR will run if they exist 
    203                 ({}, 'ER', None), 
    204                 ({}, 'VR', None), 
     218                # test that Fq will run, and return R_eff, V, V_r 
     219                ({}, 0.1, None, None, None, None, None), 
    205220                ] 
    206221            tests = smoke_tests 
     
    208223            if self.info.tests is not None: 
    209224                tests += self.info.tests 
     225            S_tests = [test for test in tests if '@S' in test[0]] 
     226            P_tests = [test for test in tests if '@S' not in test[0]] 
    210227            try: 
    211228                model = build_model(self.info, dtype=self.dtype, 
    212229                                    platform=self.platform) 
    213                 results = [self.run_one(model, test) for test in tests] 
     230                results = [self.run_one(model, test) for test in P_tests] 
     231                for test in S_tests: 
     232                    # pull the S model name out of the test defn 
     233                    pars = test[0].copy() 
     234                    s_name = pars.pop('@S') 
     235                    ps_test = [pars] + list(test[1:]) 
     236                    # build the P@S model 
     237                    s_info = load_model_info(s_name) 
     238                    ps_info = product.make_product_info(self.info, s_info) 
     239                    ps_model = build_model(ps_info, dtype=self.dtype, 
     240                                           platform=self.platform) 
     241                    # run the tests 
     242                    results.append(self.run_one(ps_model, ps_test)) 
     243 
    214244                if self.stash: 
    215245                    for test, target, actual in zip(tests, self.stash[0], results): 
     
    221251 
    222252                # Check for missing tests.  Only do so for the "dll" tests 
    223                 # to reduce noise from both opencl and dll, and because 
     253                # to reduce noise from both opencl and cuda, and because 
    224254                # python kernels use platform="dll". 
    225255                if self.platform == "dll": 
     
    236266        def _find_missing_tests(self): 
    237267            # type: () -> None 
    238             """make sure there are 1D, 2D, ER and VR tests as appropriate""" 
    239             model_has_VR = callable(self.info.VR) 
    240             model_has_ER = callable(self.info.ER) 
     268            """make sure there are 1D and 2D tests as appropriate""" 
    241269            model_has_1D = True 
    242270            model_has_2D = any(p.type == 'orientation' 
     
    246274            single = [test for test in self.info.tests 
    247275                      if not isinstance(test[2], list) and test[2] is not None] 
    248             tests_has_VR = any(test[1] == 'VR' for test in single) 
    249             tests_has_ER = any(test[1] == 'ER' for test in single) 
    250276            tests_has_1D_single = any(isinstance(test[1], float) for test in single) 
    251277            tests_has_2D_single = any(isinstance(test[1], tuple) for test in single) 
     
    260286 
    261287            missing = [] 
    262             if model_has_VR and not tests_has_VR: 
    263                 missing.append("VR") 
    264             if model_has_ER and not tests_has_ER: 
    265                 missing.append("ER") 
    266288            if model_has_1D and not (tests_has_1D_single or tests_has_1D_multiple): 
    267289                missing.append("1D") 
     
    274296            # type: (KernelModel, TestCondition) -> None 
    275297            """Run a single test case.""" 
    276             user_pars, x, y = test 
     298            user_pars, x, y = test[:3] 
    277299            pars = expand_pars(self.info.parameters, user_pars) 
    278300            invalid = invalid_pars(self.info.parameters, pars) 
     
    287309            self.assertEqual(len(y), len(x)) 
    288310 
    289             if x[0] == 'ER': 
    290                 actual = np.array([call_ER(model.info, pars)]) 
    291             elif x[0] == 'VR': 
    292                 actual = np.array([call_VR(model.info, pars)]) 
    293             elif isinstance(x[0], tuple): 
     311            if isinstance(x[0], tuple): 
    294312                qx, qy = zip(*x) 
    295313                q_vectors = [np.array(qx), np.array(qy)] 
    296                 kernel = model.make_kernel(q_vectors) 
    297                 actual = call_kernel(kernel, pars) 
    298314            else: 
    299315                q_vectors = [np.array(x)] 
    300                 kernel = model.make_kernel(q_vectors) 
     316 
     317            kernel = model.make_kernel(q_vectors) 
     318            if len(test) == 3: 
    301319                actual = call_kernel(kernel, pars) 
    302  
    303             self.assertTrue(len(actual) > 0) 
    304             self.assertEqual(len(y), len(actual)) 
    305  
    306             for xi, yi, actual_yi in zip(x, y, actual): 
     320                self._check_vectors(x, y, actual, 'I') 
     321                return actual 
     322            else: 
     323                y1 = y 
     324                y2 = test[3] if not isinstance(test[3], list) else [test[3]] 
     325                F1, F2, R_eff, volume, volume_ratio = call_Fq(kernel, pars) 
     326                if F1 is not None:  # F1 is none for models with Iq instead of Fq 
     327                    self._check_vectors(x, y1, F1, 'F') 
     328                self._check_vectors(x, y2, F2, 'F^2') 
     329                self._check_scalar(test[4], R_eff, 'R_eff') 
     330                self._check_scalar(test[5], volume, 'volume') 
     331                self._check_scalar(test[6], volume_ratio, 'form:shell ratio') 
     332                return F2 
     333 
     334        def _check_scalar(self, target, actual, name): 
     335            if target is None: 
     336                # smoke test --- make sure it runs and produces a value 
     337                self.assertTrue(not np.isnan(actual), 
     338                                'invalid %s: %s' % (name, actual)) 
     339            elif np.isnan(target): 
     340                # make sure nans match 
     341                self.assertTrue(np.isnan(actual), 
     342                                '%s: expected:%s; actual:%s' 
     343                                % (name, target, actual)) 
     344            else: 
     345                # is_near does not work for infinite values, so also test 
     346                # for exact values. 
     347                self.assertTrue(target == actual or is_near(target, actual, 5), 
     348                                '%s: expected:%s; actual:%s' 
     349                                % (name, target, actual)) 
     350 
     351        def _check_vectors(self, x, target, actual, name='I'): 
     352            self.assertTrue(len(actual) > 0, 
     353                            '%s(...) expected return'%name) 
     354            if target is None: 
     355                return 
     356            self.assertEqual(len(target), len(actual), 
     357                             '%s(...) returned wrong length'%name) 
     358            for xi, yi, actual_yi in zip(x, target, actual): 
    307359                if yi is None: 
    308360                    # smoke test --- make sure it runs and produces a value 
    309361                    self.assertTrue(not np.isnan(actual_yi), 
    310                                     'invalid f(%s): %s' % (xi, actual_yi)) 
     362                                    'invalid %s(%s): %s' % (name, xi, actual_yi)) 
    311363                elif np.isnan(yi): 
     364                    # make sure nans match 
    312365                    self.assertTrue(np.isnan(actual_yi), 
    313                                     'f(%s): expected:%s; actual:%s' 
    314                                     % (xi, yi, actual_yi)) 
     366                                    '%s(%s): expected:%s; actual:%s' 
     367                                    % (name, xi, yi, actual_yi)) 
    315368                else: 
    316369                    # is_near does not work for infinite values, so also test 
    317                     # for exact values.  Note that this will not 
     370                    # for exact values. 
    318371                    self.assertTrue(yi == actual_yi or is_near(yi, actual_yi, 5), 
    319                                     'f(%s); expected:%s; actual:%s' 
    320                                     % (xi, yi, actual_yi)) 
    321             return actual 
     372                                    '%s(%s); expected:%s; actual:%s' 
     373                                    % (name, xi, yi, actual_yi)) 
    322374 
    323375    return ModelTestCase 
     
    331383    invalid = [] 
    332384    for par in sorted(pars.keys()): 
     385        # special handling of R_eff mode, which is not a usual parameter 
     386        if par == 'radius_effective_type': 
     387            continue 
    333388        parts = par.split('_pd') 
    334389        if len(parts) > 1 and parts[1] not in ("", "_n", "nsigma", "type"): 
     
    369424 
    370425    # Build a test suite containing just the model 
    371     loaders = ['opencl'] if use_opencl() else ['dll'] 
     426    loader = 'opencl' if use_opencl() else 'cuda' if use_cuda() else 'dll' 
    372427    models = [model] 
    373428    try: 
    374         suite = make_suite(loaders, models) 
     429        suite = make_suite([loader], models) 
    375430    except Exception: 
    376431        import traceback 
     
    444499    elif args.engine == "dll": 
    445500        loaders = ["dll"] 
    446     elif args.engine == "opencl_and_dll": 
    447         loaders = ['opencl', 'dll'] if use_opencl() else ['dll'] 
     501    elif args.engine == "cuda": 
     502        if not use_cuda(): 
     503            print("cuda is not available") 
     504            return 1 
     505        loaders = ['cuda'] 
    448506    else: 
    449507        # Default to running both engines 
    450         loaders = ['opencl', 'dll'] if use_opencl() else ['dll'] 
     508        loaders = ['dll'] 
     509        if use_opencl(): 
     510            loaders.append('opencl') 
     511        if use_cuda(): 
     512            loaders.append('cuda') 
    451513        args.models.insert(0, args.engine) 
    452514 
     
    463525    Run "nosetests sasmodels" on the command line to invoke it. 
    464526    """ 
    465     loaders = ['opencl', 'dll'] if use_opencl() else ['dll'] 
     527    loaders = ['dll'] 
     528    if use_opencl(): 
     529        loaders.append('opencl') 
     530    if use_cuda(): 
     531        loaders.append('cuda') 
    466532    tests = make_suite(loaders, ['all']) 
    467533    def build_test(test): 
Note: See TracChangeset for help on using the changeset viewer.