Changeset b9c7379 in sasmodels for sasmodels/model_test.py
- Timestamp:
- Oct 29, 2018 7:27:38 AM (5 years ago)
- 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. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/model_test.py
r7b5898f rb9c7379 5 5 Usage:: 6 6 7 python -m sasmodels.model_test [opencl| dll|opencl_and_dll] model1 model2 ...7 python -m sasmodels.model_test [opencl|cuda|dll] model1 model2 ... 8 8 9 9 if model1 is 'all', then all except the remaining models will be tested 10 10 11 11 Each 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 not13 considered. The test is only to verify that the models run to completion, 14 and do not produce inf or NaN.12 and Fq is called to make sure R_eff, volume and volume ratio are computed. 13 The return values at these points are not considered. The test is only to 14 verify that the models run to completion, and do not produce inf or NaN. 15 15 16 16 Tests are defined with the *tests* attribute in the model.py file. *tests* 17 17 is a list of individual tests to run, where each test consists of the 18 18 parameter 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. 19 the effective radius test and volume ratio tests, use the extended output 20 form, which checks each output of kernel.Fq. For 1-D tests, either specify 21 the q value or a list of q-values, and the corresponding I(q) value, or 22 list of I(q) values. 22 23 23 24 That is:: … … 32 33 [I(qx1, qy1), I(qx2, qy2), ...]], 33 34 34 [ {parameters}, 'ER', ER(pars) ], 35 [ {parameters}, 'VR', VR(pars) ], 35 [ {parameters}, q, F(q), F^2(q), R_eff, V, V_r ], 36 36 ... 37 37 ] … … 60 60 from . import core 61 61 from .core import list_models, load_model_info, build_model 62 from .direct_model import call_kernel, call_ ER, call_VR62 from .direct_model import call_kernel, call_Fq 63 63 from .exception import annotate_exception 64 64 from .modelinfo import expand_pars 65 65 from .kernelcl import use_opencl 66 from .kernelcuda import use_cuda 67 from . import product 66 68 67 69 # pylint: disable=unused-import … … 81 83 Construct the pyunit test suite. 82 84 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. 86 87 87 88 *models* is the list of models to test, or *["all"]* to test all models. … … 136 137 137 138 # test using dll if desired 138 if 'dll' in loaders or not use_opencl():139 if 'dll' in loaders: 139 140 test_name = "%s-dll"%model_name 140 141 test_method_name = "test_%s_dll" % model_info.id … … 157 158 test_method_name, 158 159 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, 159 175 stash=stash) 160 176 #print("defining", test_name) … … 200 216 ({}, [0.001, 0.01, 0.1], [None]*3), 201 217 ({}, [(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), 205 220 ] 206 221 tests = smoke_tests … … 208 223 if self.info.tests is not None: 209 224 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]] 210 227 try: 211 228 model = build_model(self.info, dtype=self.dtype, 212 229 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 214 244 if self.stash: 215 245 for test, target, actual in zip(tests, self.stash[0], results): … … 221 251 222 252 # Check for missing tests. Only do so for the "dll" tests 223 # to reduce noise from both opencl and dll, and because253 # to reduce noise from both opencl and cuda, and because 224 254 # python kernels use platform="dll". 225 255 if self.platform == "dll": … … 236 266 def _find_missing_tests(self): 237 267 # 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""" 241 269 model_has_1D = True 242 270 model_has_2D = any(p.type == 'orientation' … … 246 274 single = [test for test in self.info.tests 247 275 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)250 276 tests_has_1D_single = any(isinstance(test[1], float) for test in single) 251 277 tests_has_2D_single = any(isinstance(test[1], tuple) for test in single) … … 260 286 261 287 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")266 288 if model_has_1D and not (tests_has_1D_single or tests_has_1D_multiple): 267 289 missing.append("1D") … … 274 296 # type: (KernelModel, TestCondition) -> None 275 297 """Run a single test case.""" 276 user_pars, x, y = test 298 user_pars, x, y = test[:3] 277 299 pars = expand_pars(self.info.parameters, user_pars) 278 300 invalid = invalid_pars(self.info.parameters, pars) … … 287 309 self.assertEqual(len(y), len(x)) 288 310 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): 294 312 qx, qy = zip(*x) 295 313 q_vectors = [np.array(qx), np.array(qy)] 296 kernel = model.make_kernel(q_vectors)297 actual = call_kernel(kernel, pars)298 314 else: 299 315 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: 301 319 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): 307 359 if yi is None: 308 360 # smoke test --- make sure it runs and produces a value 309 361 self.assertTrue(not np.isnan(actual_yi), 310 'invalid f(%s): %s' % (xi, actual_yi))362 'invalid %s(%s): %s' % (name, xi, actual_yi)) 311 363 elif np.isnan(yi): 364 # make sure nans match 312 365 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)) 315 368 else: 316 369 # is_near does not work for infinite values, so also test 317 # for exact values. Note that this will not370 # for exact values. 318 371 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)) 322 374 323 375 return ModelTestCase … … 331 383 invalid = [] 332 384 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 333 388 parts = par.split('_pd') 334 389 if len(parts) > 1 and parts[1] not in ("", "_n", "nsigma", "type"): … … 369 424 370 425 # Build a test suite containing just the model 371 loader s = ['opencl'] if use_opencl() else ['dll']426 loader = 'opencl' if use_opencl() else 'cuda' if use_cuda() else 'dll' 372 427 models = [model] 373 428 try: 374 suite = make_suite( loaders, models)429 suite = make_suite([loader], models) 375 430 except Exception: 376 431 import traceback … … 444 499 elif args.engine == "dll": 445 500 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'] 448 506 else: 449 507 # 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') 451 513 args.models.insert(0, args.engine) 452 514 … … 463 525 Run "nosetests sasmodels" on the command line to invoke it. 464 526 """ 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') 466 532 tests = make_suite(loaders, ['all']) 467 533 def build_test(test):
Note: See TracChangeset
for help on using the changeset viewer.