source: sasmodels/sasmodels/model_test.py @ 5c962df

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 5c962df was 5c962df, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

delint

  • Property mode set to 100644
File size: 9.0 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3Run model unit tests.
4
5Usage::
6
7    python -m sasmodels.model_test [opencl|dll|opencl_and_dll] model1 model2 ...
8
9    if model1 is 'all', then all except the remaining models will be tested
10
11Each model is tested using the default parameters at q=0.1, (qx, qy)=(0.1, 0.1),
12and the ER and VR are computed.  The return values at these points are not
13considered.  The test is only to verify that the models run to completion,
14and do not produce inf or NaN.
15
16Tests are defined with the *tests* attribute in the model.py file.  *tests*
17is a list of individual tests to run, where each test consists of the
18parameter values for the test, the q-values and the expected results.  For
19the effective radius test, the q-value should be 'ER'.  For the VR test,
20the q-value should be 'VR'.  For 1-D tests, either specify the q value or
21a list of q-values, and the corresponding I(q) value, or list of I(q) values.
22
23That is::
24
25    tests = [
26        [ {parameters}, q, I(q)],
27        [ {parameters}, [q], [I(q)] ],
28        [ {parameters}, [q1, q2, ...], [I(q1), I(q2), ...]],
29
30        [ {parameters}, (qx, qy), I(qx, Iqy)],
31        [ {parameters}, [(qx1, qy1), (qx2, qy2), ...],
32                        [I(qx1, qy1), I(qx2, qy2), ...]],
33
34        [ {parameters}, 'ER', ER(pars) ],
35        [ {parameters}, 'VR', VR(pars) ],
36        ...
37    ]
38
39Parameters are *key:value* pairs, where key is one of the parameters of the
40model and value is the value to use for the test.  Any parameters not given
41in the parameter list will take on the default parameter value.
42
43Precision defaults to 5 digits (relative).
44"""
45from __future__ import print_function
46
47import sys
48import unittest
49
50import numpy as np
51
52from .core import list_models, load_model_definition, load_model, HAVE_OPENCL
53from .core import make_kernel, call_kernel, call_ER, call_VR
54from .exception import annotate_exception
55
56
57def make_suite(loaders, models):
58    """
59    Construct the pyunit test suite.
60
61    *loaders* is the list of kernel drivers to use, which is one of
62    *["dll", "opencl"]*, *["dll"]* or *["opencl"]*.  For python models,
63    the python driver is always used.
64
65    *models* is the list of models to test, or *["all"]* to test all models.
66    """
67
68    ModelTestCase = _hide_model_case_from_nosetests()
69    suite = unittest.TestSuite()
70
71    if models[0] == 'all':
72        skip = models[1:]
73        models = list_models()
74    else:
75        skip = []
76    for model_name in models:
77        if model_name in skip: continue
78        model_definition = load_model_definition(model_name)
79
80        #print('------')
81        #print('found tests in', model_name)
82        #print('------')
83
84        # if ispy then use the dll loader to call pykernel
85        # don't try to call cl kernel since it will not be
86        # available in some environmentes.
87        is_py = callable(getattr(model_definition, 'Iq', None))
88
89        if is_py:  # kernel implemented in python
90            test_name = "Model: %s, Kernel: python"%model_name
91            test_method_name = "test_%s_python" % model_name
92            test = ModelTestCase(test_name, model_definition,
93                                 test_method_name,
94                                 platform="dll",  # so that
95                                 dtype="double")
96            suite.addTest(test)
97        else:   # kernel implemented in C
98            # test using opencl if desired and available
99            if 'opencl' in loaders and HAVE_OPENCL:
100                test_name = "Model: %s, Kernel: OpenCL"%model_name
101                test_method_name = "test_%s_opencl" % model_name
102                test = ModelTestCase(test_name, model_definition,
103                                     test_method_name,
104                                     platform="ocl", dtype='single')
105                #print("defining", test_name)
106                suite.addTest(test)
107
108            # test using dll if desired
109            if 'dll' in loaders:
110                test_name = "Model: %s, Kernel: dll"%model_name
111                test_method_name = "test_%s_dll" % model_name
112                test = ModelTestCase(test_name, model_definition,
113                                     test_method_name,
114                                     platform="dll",
115                                     dtype="double")
116                suite.addTest(test)
117
118    return suite
119
120
121def _hide_model_case_from_nosetests():
122    class ModelTestCase(unittest.TestCase):
123        """
124        Test suit for a particular model with a particular kernel driver.
125
126        The test suite runs a simple smoke test to make sure the model
127        functions, then runs the list of tests at the bottom of the model
128        description file.
129        """
130        def __init__(self, test_name, definition, test_method_name,
131                     platform, dtype):
132            self.test_name = test_name
133            self.definition = definition
134            self.platform = platform
135            self.dtype = dtype
136
137            setattr(self, test_method_name, self._runTest)
138            unittest.TestCase.__init__(self, test_method_name)
139
140        def _runTest(self):
141            smoke_tests = [
142                [{}, 0.1, None],
143                [{}, (0.1, 0.1), None],
144                [{}, 'ER', None],
145                [{}, 'VR', None],
146                ]
147
148            tests = getattr(self.definition, 'tests', [])
149            try:
150                model = load_model(self.definition, dtype=self.dtype,
151                                   platform=self.platform)
152                for test in smoke_tests + tests:
153                    self._run_one_test(model, test)
154
155                if not tests and self.platform == "dll":
156                    ## Uncomment the following to make forgetting the test
157                    ## values an error.  Only do so for the "dll" tests
158                    ## to reduce noise from both opencl and dll, and because
159                    ## python kernels us
160                    #raise Exception("No test cases provided")
161                    pass
162
163            except Exception as exc:
164                annotate_exception(exc, self.test_name)
165                raise
166
167        def _run_one_test(self, model, test):
168            pars, x, y = test
169
170            if not isinstance(y, list):
171                y = [y]
172            if not isinstance(x, list):
173                x = [x]
174
175            self.assertEqual(len(y), len(x))
176
177            if x[0] == 'ER':
178                actual = [call_ER(model.info, pars)]
179            elif x[0] == 'VR':
180                actual = [call_VR(model.info, pars)]
181            elif isinstance(x[0], tuple):
182                Qx, Qy = zip(*x)
183                q_vectors = [np.array(Qx), np.array(Qy)]
184                kernel = make_kernel(model, q_vectors)
185                actual = call_kernel(kernel, pars)
186            else:
187                q_vectors = [np.array(x)]
188                kernel = make_kernel(model, q_vectors)
189                actual = call_kernel(kernel, pars)
190
191            self.assertGreater(len(actual), 0)
192            self.assertEqual(len(y), len(actual))
193
194            for xi, yi, actual_yi in zip(x, y, actual):
195                if yi is None:
196                    # smoke test --- make sure it runs and produces a value
197                    self.assertTrue(np.isfinite(actual_yi),
198                                    'invalid f(%s): %s' % (xi, actual_yi))
199                else:
200                    err = abs(yi - actual_yi)
201                    nrm = abs(yi)
202                    self.assertLess(err * 10**5, nrm,
203                                    'f(%s); expected:%s; actual:%s'
204                                    % (xi, yi, actual_yi))
205
206    return ModelTestCase
207
208
209
210def main():
211    """
212    Run tests given is sys.argv.
213
214    Returns 0 if success or 1 if any tests fail.
215    """
216    import xmlrunner
217
218    models = sys.argv[1:]
219    if models and models[0] == 'opencl':
220        if not HAVE_OPENCL:
221            print("opencl is not available")
222            return 1
223        loaders = ['opencl']
224        models = models[1:]
225    elif models and models[0] == 'dll':
226        # TODO: test if compiler is available?
227        loaders = ['dll']
228        models = models[1:]
229    elif models and models[0] == 'opencl_and_dll':
230        loaders = ['opencl', 'dll']
231        models = models[1:]
232    else:
233        loaders = ['opencl', 'dll']
234    if not models:
235        print("""\
236usage:
237  python -m sasmodels.model_test [opencl|dll|opencl_and_dll] model1 model2 ...
238
239If model1 is 'all', then all except the remaining models will be tested.
240If no compute target is specified, then models will be tested with both opencl
241and dll; the compute target is ignored for pure python models.""")
242
243        return 1
244
245    #runner = unittest.TextTestRunner()
246    runner = xmlrunner.XMLTestRunner(output='logs')
247    result = runner.run(make_suite(loaders, models))
248    return 1 if result.failures or result.errors else 0
249
250
251def model_tests():
252    """
253    Test runner visible to nosetests.
254
255    Run "nosetests sasmodels" on the command line to invoke it.
256    """
257    tests = make_suite(['opencl', 'dll'], ['all'])
258    for test_i in tests:
259        yield test_i._runTest
260
261
262if __name__ == "__main__":
263    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.