source: sasmodels/sasmodels/model_test.py @ aa4946b

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

refactor so kernels are loaded via core.load_model

  • Property mode set to 100644
File size: 8.1 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"""
45
46import sys
47import xmlrunner
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
54
55
56def annotate_exception(exc, msg):
57    """
58    Add an annotation to the current exception, which can then be forwarded
59    to the caller using a bare "raise" statement to reraise the annotated
60    exception.
61    Example::
62        >>> D = {}
63        >>> try:
64        ...    print D['hello']
65        ... except Exception,exc:
66        ...    annotate_exception(exc, "while accessing 'D'")
67        ...    raise
68        Traceback (most recent call last):
69            ...
70        KeyError: "hello while accessing 'D'"
71    """
72    args = exc.args
73    if not args:
74        exc.args = (msg,)
75    else:
76        try:
77            arg0 = " ".join((args[0],msg))
78            exc.args = tuple([arg0] + list(args[1:]))
79        except:
80            exc.args = (" ".join((str(exc),msg)),)
81
82
83def make_suite(loaders, models):
84
85    ModelTestCase = _hide_model_case_from_nosetests()
86    suite = unittest.TestSuite()
87
88    if models[0] == 'all':
89        skip = models[1:]
90        models = list_models()
91    else:
92        skip = []
93    for model_name in models:
94        if model_name in skip: continue
95        model_definition = load_model_definition(model_name)
96
97        smoke_tests = [
98            [{},0.1,None],
99            [{},(0.1,0.1),None],
100            [{},'ER',None],
101            [{},'VR',None],
102            ]
103        tests = smoke_tests + getattr(model_definition, 'tests', [])
104
105        if tests: # in case there are no smoke tests...
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            ispy = callable(getattr(model_definition,'Iq', None))
114
115            # test using opencl if desired
116            if not ispy and ('opencl' in loaders and HAVE_OPENCL):
117                test_name = "Model: %s, Kernel: OpenCL"%model_name
118                test_method = "test_%s_opencl" % model_name
119                test = ModelTestCase(test_name, model_definition,
120                                     tests, test_method,
121                                     platform="ocl", dtype='single')
122                #print "defining", test_name
123                suite.addTest(test)
124
125            # test using dll if desired
126            if ispy or 'dll' in loaders:
127                test_name = "Model: %s, Kernel: dll"%model_name
128                test_method = "test_%s_dll" % model_name
129                test = ModelTestCase(test_name, model_definition,
130                                     tests, test_method,
131                                     platform="dll", dtype="double")
132                suite.addTest(test)
133
134    return suite
135
136
137def _hide_model_case_from_nosetests():
138    class ModelTestCase(unittest.TestCase):
139        def __init__(self, test_name, definition, tests, test_method,
140                     platform, dtype):
141            self.test_name = test_name
142            self.definition = definition
143            self.tests = tests
144            self.platform = platform
145            self.dtype = dtype
146
147            setattr(self, test_method, self._runTest)
148            unittest.TestCase.__init__(self, test_method)
149
150        def _runTest(self):
151            try:
152                model = load_model(self.definition, dtype=self.dtype,
153                                   platform=self.platform)
154                for test in self.tests:
155                    self._run_one_test(model, test)
156
157            except Exception,exc:
158                annotate_exception(exc, self.test_name)
159                raise
160
161        def _run_one_test(self, model, test):
162            pars, x, y = test
163
164            if not isinstance(y, list):
165                y = [y]
166            if not isinstance(x, list):
167                x = [x]
168
169            self.assertEqual(len(y), len(x))
170
171            if x[0] == 'ER':
172                actual = [call_ER(model.info, pars)]
173            elif x[0] == 'VR':
174                actual = [call_VR(model.info, pars)]
175            elif isinstance(x[0], tuple):
176                Qx,Qy = zip(*x)
177                q_vectors = [np.array(Qx), np.array(Qy)]
178                kernel = make_kernel(model, q_vectors)
179                actual = call_kernel(kernel, pars)
180            else:
181                q_vectors = [np.array(x)]
182                kernel = make_kernel(model, q_vectors)
183                actual = call_kernel(kernel, pars)
184
185            self.assertGreater(len(actual), 0)
186            self.assertEqual(len(y), len(actual))
187
188            for xi, yi, actual_yi in zip(x, y, actual):
189                if yi is None:
190                    # smoke test --- make sure it runs and produces a value
191                    self.assertTrue(np.isfinite(actual_yi),
192                        'invalid f(%s): %s' % (xi, actual_yi))
193                else:
194                    err = abs(yi - actual_yi)
195                    nrm = abs(yi)
196                    self.assertLess(err * 10**5, nrm,
197                        'f(%s); expected:%s; actual:%s' % (xi, yi, actual_yi))
198
199    return ModelTestCase
200
201
202
203def main():
204    """
205    Run tests given is sys.argv.
206
207    Returns 0 if success or 1 if any tests fail.
208    """
209    models = sys.argv[1:]
210    if models and models[0] == 'opencl':
211        if not HAVE_OPENCL:
212            print >>sys.stderr, "opencl is not available"
213            return 1
214        loaders = ['opencl']
215        models = models[1:]
216    elif models and models[0] == 'dll':
217        # TODO: test if compiler is available?
218        loaders = ['dll']
219        models = models[1:]
220    elif models and models[0] == 'opencl_and_dll':
221        loaders = ['opencl', 'dll']
222        models = models[1:]
223    else:
224        loaders = ['opencl', 'dll']
225    if not models:
226        print >>sys.stderr, "usage: python -m sasmodels.model_test [opencl|dll|opencl_and_dll] model1 model2 ..."
227        print >>sys.stderr, "if model1 is 'all', then all except the remaining models will be tested"
228        return 1
229
230    #runner = unittest.TextTestRunner()
231    runner = xmlrunner.XMLTestRunner(output='logs')
232    result = runner.run(make_suite(loaders, models))
233    return 1 if result.failures or result.errors else 0
234
235
236def model_tests():
237    """
238    Test runner visible to nosetests.
239
240    Run "nosetests sasmodels" on the command line to invoke it.
241    """
242    tests = make_suite(['opencl','dll'],['all'])
243    for test_i in tests:
244        yield test_i._runTest
245
246
247if __name__ == "__main__":
248    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.