Chris@87: """ Chris@87: Utility function to facilitate testing. Chris@87: Chris@87: """ Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import os Chris@87: import sys Chris@87: import re Chris@87: import operator Chris@87: import warnings Chris@87: from functools import partial Chris@87: import shutil Chris@87: import contextlib Chris@87: from tempfile import mkdtemp Chris@87: from .nosetester import import_nose Chris@87: from numpy.core import float32, empty, arange, array_repr, ndarray Chris@87: Chris@87: if sys.version_info[0] >= 3: Chris@87: from io import StringIO Chris@87: else: Chris@87: from StringIO import StringIO Chris@87: Chris@87: __all__ = ['assert_equal', 'assert_almost_equal', 'assert_approx_equal', Chris@87: 'assert_array_equal', 'assert_array_less', 'assert_string_equal', Chris@87: 'assert_array_almost_equal', 'assert_raises', 'build_err_msg', Chris@87: 'decorate_methods', 'jiffies', 'memusage', 'print_assert_equal', Chris@87: 'raises', 'rand', 'rundocs', 'runstring', 'verbose', 'measure', Chris@87: 'assert_', 'assert_array_almost_equal_nulp', 'assert_raises_regex', Chris@87: 'assert_array_max_ulp', 'assert_warns', 'assert_no_warnings', Chris@87: 'assert_allclose', 'IgnoreException'] Chris@87: Chris@87: Chris@87: verbose = 0 Chris@87: Chris@87: Chris@87: def assert_(val, msg='') : Chris@87: """ Chris@87: Assert that works in release mode. Chris@87: Accepts callable msg to allow deferring evaluation until failure. Chris@87: Chris@87: The Python built-in ``assert`` does not work when executing code in Chris@87: optimized mode (the ``-O`` flag) - no byte-code is generated for it. Chris@87: Chris@87: For documentation on usage, refer to the Python documentation. Chris@87: Chris@87: """ Chris@87: if not val : Chris@87: try: Chris@87: smsg = msg() Chris@87: except TypeError: Chris@87: smsg = msg Chris@87: raise AssertionError(smsg) Chris@87: Chris@87: def gisnan(x): Chris@87: """like isnan, but always raise an error if type not supported instead of Chris@87: returning a TypeError object. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: isnan and other ufunc sometimes return a NotImplementedType object instead Chris@87: of raising any exception. This function is a wrapper to make sure an Chris@87: exception is always raised. Chris@87: Chris@87: This should be removed once this problem is solved at the Ufunc level.""" Chris@87: from numpy.core import isnan Chris@87: st = isnan(x) Chris@87: if isinstance(st, type(NotImplemented)): Chris@87: raise TypeError("isnan not supported for this type") Chris@87: return st Chris@87: Chris@87: def gisfinite(x): Chris@87: """like isfinite, but always raise an error if type not supported instead of Chris@87: returning a TypeError object. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: isfinite and other ufunc sometimes return a NotImplementedType object instead Chris@87: of raising any exception. This function is a wrapper to make sure an Chris@87: exception is always raised. Chris@87: Chris@87: This should be removed once this problem is solved at the Ufunc level.""" Chris@87: from numpy.core import isfinite, errstate Chris@87: with errstate(invalid='ignore'): Chris@87: st = isfinite(x) Chris@87: if isinstance(st, type(NotImplemented)): Chris@87: raise TypeError("isfinite not supported for this type") Chris@87: return st Chris@87: Chris@87: def gisinf(x): Chris@87: """like isinf, but always raise an error if type not supported instead of Chris@87: returning a TypeError object. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: isinf and other ufunc sometimes return a NotImplementedType object instead Chris@87: of raising any exception. This function is a wrapper to make sure an Chris@87: exception is always raised. Chris@87: Chris@87: This should be removed once this problem is solved at the Ufunc level.""" Chris@87: from numpy.core import isinf, errstate Chris@87: with errstate(invalid='ignore'): Chris@87: st = isinf(x) Chris@87: if isinstance(st, type(NotImplemented)): Chris@87: raise TypeError("isinf not supported for this type") Chris@87: return st Chris@87: Chris@87: def rand(*args): Chris@87: """Returns an array of random numbers with the given shape. Chris@87: Chris@87: This only uses the standard library, so it is useful for testing purposes. Chris@87: """ Chris@87: import random Chris@87: from numpy.core import zeros, float64 Chris@87: results = zeros(args, float64) Chris@87: f = results.flat Chris@87: for i in range(len(f)): Chris@87: f[i] = random.random() Chris@87: return results Chris@87: Chris@87: if sys.platform[:5]=='linux': Chris@87: def jiffies(_proc_pid_stat = '/proc/%s/stat'%(os.getpid()), Chris@87: _load_time=[]): Chris@87: """ Return number of jiffies (1/100ths of a second) that this Chris@87: process has been scheduled in user mode. See man 5 proc. """ Chris@87: import time Chris@87: if not _load_time: Chris@87: _load_time.append(time.time()) Chris@87: try: Chris@87: f=open(_proc_pid_stat, 'r') Chris@87: l = f.readline().split(' ') Chris@87: f.close() Chris@87: return int(l[13]) Chris@87: except: Chris@87: return int(100*(time.time()-_load_time[0])) Chris@87: Chris@87: def memusage(_proc_pid_stat = '/proc/%s/stat'%(os.getpid())): Chris@87: """ Return virtual memory size in bytes of the running python. Chris@87: """ Chris@87: try: Chris@87: f=open(_proc_pid_stat, 'r') Chris@87: l = f.readline().split(' ') Chris@87: f.close() Chris@87: return int(l[22]) Chris@87: except: Chris@87: return Chris@87: else: Chris@87: # os.getpid is not in all platforms available. Chris@87: # Using time is safe but inaccurate, especially when process Chris@87: # was suspended or sleeping. Chris@87: def jiffies(_load_time=[]): Chris@87: """ Return number of jiffies (1/100ths of a second) that this Chris@87: process has been scheduled in user mode. [Emulation with time.time]. """ Chris@87: import time Chris@87: if not _load_time: Chris@87: _load_time.append(time.time()) Chris@87: return int(100*(time.time()-_load_time[0])) Chris@87: def memusage(): Chris@87: """ Return memory usage of running python. [Not implemented]""" Chris@87: raise NotImplementedError Chris@87: Chris@87: if os.name=='nt' and sys.version[:3] > '2.3': Chris@87: # Code "stolen" from enthought/debug/memusage.py Chris@87: def GetPerformanceAttributes(object, counter, instance = None, Chris@87: inum=-1, format = None, machine=None): Chris@87: # NOTE: Many counters require 2 samples to give accurate results, Chris@87: # including "% Processor Time" (as by definition, at any instant, a Chris@87: # thread's CPU usage is either 0 or 100). To read counters like this, Chris@87: # you should copy this function, but keep the counter open, and call Chris@87: # CollectQueryData() each time you need to know. Chris@87: # See http://msdn.microsoft.com/library/en-us/dnperfmo/html/perfmonpt2.asp Chris@87: # My older explanation for this was that the "AddCounter" process forced Chris@87: # the CPU to 100%, but the above makes more sense :) Chris@87: import win32pdh Chris@87: if format is None: format = win32pdh.PDH_FMT_LONG Chris@87: path = win32pdh.MakeCounterPath( (machine, object, instance, None, inum, counter) ) Chris@87: hq = win32pdh.OpenQuery() Chris@87: try: Chris@87: hc = win32pdh.AddCounter(hq, path) Chris@87: try: Chris@87: win32pdh.CollectQueryData(hq) Chris@87: type, val = win32pdh.GetFormattedCounterValue(hc, format) Chris@87: return val Chris@87: finally: Chris@87: win32pdh.RemoveCounter(hc) Chris@87: finally: Chris@87: win32pdh.CloseQuery(hq) Chris@87: Chris@87: def memusage(processName="python", instance=0): Chris@87: # from win32pdhutil, part of the win32all package Chris@87: import win32pdh Chris@87: return GetPerformanceAttributes("Process", "Virtual Bytes", Chris@87: processName, instance, Chris@87: win32pdh.PDH_FMT_LONG, None) Chris@87: Chris@87: def build_err_msg(arrays, err_msg, header='Items are not equal:', Chris@87: verbose=True, names=('ACTUAL', 'DESIRED'), precision=8): Chris@87: msg = ['\n' + header] Chris@87: if err_msg: Chris@87: if err_msg.find('\n') == -1 and len(err_msg) < 79-len(header): Chris@87: msg = [msg[0] + ' ' + err_msg] Chris@87: else: Chris@87: msg.append(err_msg) Chris@87: if verbose: Chris@87: for i, a in enumerate(arrays): Chris@87: Chris@87: if isinstance(a, ndarray): Chris@87: # precision argument is only needed if the objects are ndarrays Chris@87: r_func = partial(array_repr, precision=precision) Chris@87: else: Chris@87: r_func = repr Chris@87: Chris@87: try: Chris@87: r = r_func(a) Chris@87: except: Chris@87: r = '[repr failed]' Chris@87: if r.count('\n') > 3: Chris@87: r = '\n'.join(r.splitlines()[:3]) Chris@87: r += '...' Chris@87: msg.append(' %s: %s' % (names[i], r)) Chris@87: return '\n'.join(msg) Chris@87: Chris@87: def assert_equal(actual,desired,err_msg='',verbose=True): Chris@87: """ Chris@87: Raises an AssertionError if two objects are not equal. Chris@87: Chris@87: Given two objects (scalars, lists, tuples, dictionaries or numpy arrays), Chris@87: check that all elements of these objects are equal. An exception is raised Chris@87: at the first conflicting values. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: actual : array_like Chris@87: The object to check. Chris@87: desired : array_like Chris@87: The expected object. Chris@87: err_msg : str, optional Chris@87: The error message to be printed in case of failure. Chris@87: verbose : bool, optional Chris@87: If True, the conflicting values are appended to the error message. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If actual and desired are not equal. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.testing.assert_equal([4,5], [4,6]) Chris@87: ... Chris@87: : Chris@87: Items are not equal: Chris@87: item=1 Chris@87: ACTUAL: 5 Chris@87: DESIRED: 6 Chris@87: Chris@87: """ Chris@87: if isinstance(desired, dict): Chris@87: if not isinstance(actual, dict) : Chris@87: raise AssertionError(repr(type(actual))) Chris@87: assert_equal(len(actual), len(desired), err_msg, verbose) Chris@87: for k, i in desired.items(): Chris@87: if k not in actual : Chris@87: raise AssertionError(repr(k)) Chris@87: assert_equal(actual[k], desired[k], 'key=%r\n%s' % (k, err_msg), verbose) Chris@87: return Chris@87: if isinstance(desired, (list, tuple)) and isinstance(actual, (list, tuple)): Chris@87: assert_equal(len(actual), len(desired), err_msg, verbose) Chris@87: for k in range(len(desired)): Chris@87: assert_equal(actual[k], desired[k], 'item=%r\n%s' % (k, err_msg), verbose) Chris@87: return Chris@87: from numpy.core import ndarray, isscalar, signbit Chris@87: from numpy.lib import iscomplexobj, real, imag Chris@87: if isinstance(actual, ndarray) or isinstance(desired, ndarray): Chris@87: return assert_array_equal(actual, desired, err_msg, verbose) Chris@87: msg = build_err_msg([actual, desired], err_msg, verbose=verbose) Chris@87: Chris@87: # Handle complex numbers: separate into real/imag to handle Chris@87: # nan/inf/negative zero correctly Chris@87: # XXX: catch ValueError for subclasses of ndarray where iscomplex fail Chris@87: try: Chris@87: usecomplex = iscomplexobj(actual) or iscomplexobj(desired) Chris@87: except ValueError: Chris@87: usecomplex = False Chris@87: Chris@87: if usecomplex: Chris@87: if iscomplexobj(actual): Chris@87: actualr = real(actual) Chris@87: actuali = imag(actual) Chris@87: else: Chris@87: actualr = actual Chris@87: actuali = 0 Chris@87: if iscomplexobj(desired): Chris@87: desiredr = real(desired) Chris@87: desiredi = imag(desired) Chris@87: else: Chris@87: desiredr = desired Chris@87: desiredi = 0 Chris@87: try: Chris@87: assert_equal(actualr, desiredr) Chris@87: assert_equal(actuali, desiredi) Chris@87: except AssertionError: Chris@87: raise AssertionError(msg) Chris@87: Chris@87: # Inf/nan/negative zero handling Chris@87: try: Chris@87: # isscalar test to check cases such as [np.nan] != np.nan Chris@87: if isscalar(desired) != isscalar(actual): Chris@87: raise AssertionError(msg) Chris@87: Chris@87: # If one of desired/actual is not finite, handle it specially here: Chris@87: # check that both are nan if any is a nan, and test for equality Chris@87: # otherwise Chris@87: if not (gisfinite(desired) and gisfinite(actual)): Chris@87: isdesnan = gisnan(desired) Chris@87: isactnan = gisnan(actual) Chris@87: if isdesnan or isactnan: Chris@87: if not (isdesnan and isactnan): Chris@87: raise AssertionError(msg) Chris@87: else: Chris@87: if not desired == actual: Chris@87: raise AssertionError(msg) Chris@87: return Chris@87: elif desired == 0 and actual == 0: Chris@87: if not signbit(desired) == signbit(actual): Chris@87: raise AssertionError(msg) Chris@87: # If TypeError or ValueError raised while using isnan and co, just handle Chris@87: # as before Chris@87: except (TypeError, ValueError, NotImplementedError): Chris@87: pass Chris@87: Chris@87: # Explicitly use __eq__ for comparison, ticket #2552 Chris@87: if not (desired == actual): Chris@87: raise AssertionError(msg) Chris@87: Chris@87: def print_assert_equal(test_string, actual, desired): Chris@87: """ Chris@87: Test if two objects are equal, and print an error message if test fails. Chris@87: Chris@87: The test is performed with ``actual == desired``. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: test_string : str Chris@87: The message supplied to AssertionError. Chris@87: actual : object Chris@87: The object to test for equality against `desired`. Chris@87: desired : object Chris@87: The expected result. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.testing.print_assert_equal('Test XYZ of func xyz', [0, 1], [0, 1]) Chris@87: >>> np.testing.print_assert_equal('Test XYZ of func xyz', [0, 1], [0, 2]) Chris@87: Traceback (most recent call last): Chris@87: ... Chris@87: AssertionError: Test XYZ of func xyz failed Chris@87: ACTUAL: Chris@87: [0, 1] Chris@87: DESIRED: Chris@87: [0, 2] Chris@87: Chris@87: """ Chris@87: import pprint Chris@87: Chris@87: if not (actual == desired): Chris@87: msg = StringIO() Chris@87: msg.write(test_string) Chris@87: msg.write(' failed\nACTUAL: \n') Chris@87: pprint.pprint(actual, msg) Chris@87: msg.write('DESIRED: \n') Chris@87: pprint.pprint(desired, msg) Chris@87: raise AssertionError(msg.getvalue()) Chris@87: Chris@87: def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True): Chris@87: """ Chris@87: Raises an AssertionError if two items are not equal up to desired Chris@87: precision. Chris@87: Chris@87: .. note:: It is recommended to use one of `assert_allclose`, Chris@87: `assert_array_almost_equal_nulp` or `assert_array_max_ulp` Chris@87: instead of this function for more consistent floating point Chris@87: comparisons. Chris@87: Chris@87: The test is equivalent to ``abs(desired-actual) < 0.5 * 10**(-decimal)``. Chris@87: Chris@87: Given two objects (numbers or ndarrays), check that all elements of these Chris@87: objects are almost equal. An exception is raised at conflicting values. Chris@87: For ndarrays this delegates to assert_array_almost_equal Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: actual : array_like Chris@87: The object to check. Chris@87: desired : array_like Chris@87: The expected object. Chris@87: decimal : int, optional Chris@87: Desired precision, default is 7. Chris@87: err_msg : str, optional Chris@87: The error message to be printed in case of failure. Chris@87: verbose : bool, optional Chris@87: If True, the conflicting values are appended to the error message. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If actual and desired are not equal up to specified precision. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: assert_allclose: Compare two array_like objects for equality with desired Chris@87: relative and/or absolute precision. Chris@87: assert_array_almost_equal_nulp, assert_array_max_ulp, assert_equal Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> import numpy.testing as npt Chris@87: >>> npt.assert_almost_equal(2.3333333333333, 2.33333334) Chris@87: >>> npt.assert_almost_equal(2.3333333333333, 2.33333334, decimal=10) Chris@87: ... Chris@87: : Chris@87: Items are not equal: Chris@87: ACTUAL: 2.3333333333333002 Chris@87: DESIRED: 2.3333333399999998 Chris@87: Chris@87: >>> npt.assert_almost_equal(np.array([1.0,2.3333333333333]), Chris@87: ... np.array([1.0,2.33333334]), decimal=9) Chris@87: ... Chris@87: : Chris@87: Arrays are not almost equal Chris@87: Chris@87: (mismatch 50.0%) Chris@87: x: array([ 1. , 2.33333333]) Chris@87: y: array([ 1. , 2.33333334]) Chris@87: Chris@87: """ Chris@87: from numpy.core import ndarray Chris@87: from numpy.lib import iscomplexobj, real, imag Chris@87: Chris@87: # Handle complex numbers: separate into real/imag to handle Chris@87: # nan/inf/negative zero correctly Chris@87: # XXX: catch ValueError for subclasses of ndarray where iscomplex fail Chris@87: try: Chris@87: usecomplex = iscomplexobj(actual) or iscomplexobj(desired) Chris@87: except ValueError: Chris@87: usecomplex = False Chris@87: Chris@87: def _build_err_msg(): Chris@87: header = ('Arrays are not almost equal to %d decimals' % decimal) Chris@87: return build_err_msg([actual, desired], err_msg, verbose=verbose, Chris@87: header=header) Chris@87: Chris@87: if usecomplex: Chris@87: if iscomplexobj(actual): Chris@87: actualr = real(actual) Chris@87: actuali = imag(actual) Chris@87: else: Chris@87: actualr = actual Chris@87: actuali = 0 Chris@87: if iscomplexobj(desired): Chris@87: desiredr = real(desired) Chris@87: desiredi = imag(desired) Chris@87: else: Chris@87: desiredr = desired Chris@87: desiredi = 0 Chris@87: try: Chris@87: assert_almost_equal(actualr, desiredr, decimal=decimal) Chris@87: assert_almost_equal(actuali, desiredi, decimal=decimal) Chris@87: except AssertionError: Chris@87: raise AssertionError(_build_err_msg()) Chris@87: Chris@87: if isinstance(actual, (ndarray, tuple, list)) \ Chris@87: or isinstance(desired, (ndarray, tuple, list)): Chris@87: return assert_array_almost_equal(actual, desired, decimal, err_msg) Chris@87: try: Chris@87: # If one of desired/actual is not finite, handle it specially here: Chris@87: # check that both are nan if any is a nan, and test for equality Chris@87: # otherwise Chris@87: if not (gisfinite(desired) and gisfinite(actual)): Chris@87: if gisnan(desired) or gisnan(actual): Chris@87: if not (gisnan(desired) and gisnan(actual)): Chris@87: raise AssertionError(_build_err_msg()) Chris@87: else: Chris@87: if not desired == actual: Chris@87: raise AssertionError(_build_err_msg()) Chris@87: return Chris@87: except (NotImplementedError, TypeError): Chris@87: pass Chris@87: if round(abs(desired - actual), decimal) != 0 : Chris@87: raise AssertionError(_build_err_msg()) Chris@87: Chris@87: Chris@87: def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True): Chris@87: """ Chris@87: Raises an AssertionError if two items are not equal up to significant Chris@87: digits. Chris@87: Chris@87: .. note:: It is recommended to use one of `assert_allclose`, Chris@87: `assert_array_almost_equal_nulp` or `assert_array_max_ulp` Chris@87: instead of this function for more consistent floating point Chris@87: comparisons. Chris@87: Chris@87: Given two numbers, check that they are approximately equal. Chris@87: Approximately equal is defined as the number of significant digits Chris@87: that agree. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: actual : scalar Chris@87: The object to check. Chris@87: desired : scalar Chris@87: The expected object. Chris@87: significant : int, optional Chris@87: Desired precision, default is 7. Chris@87: err_msg : str, optional Chris@87: The error message to be printed in case of failure. Chris@87: verbose : bool, optional Chris@87: If True, the conflicting values are appended to the error message. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If actual and desired are not equal up to specified precision. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: assert_allclose: Compare two array_like objects for equality with desired Chris@87: relative and/or absolute precision. Chris@87: assert_array_almost_equal_nulp, assert_array_max_ulp, assert_equal Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.testing.assert_approx_equal(0.12345677777777e-20, 0.1234567e-20) Chris@87: >>> np.testing.assert_approx_equal(0.12345670e-20, 0.12345671e-20, Chris@87: significant=8) Chris@87: >>> np.testing.assert_approx_equal(0.12345670e-20, 0.12345672e-20, Chris@87: significant=8) Chris@87: ... Chris@87: : Chris@87: Items are not equal to 8 significant digits: Chris@87: ACTUAL: 1.234567e-021 Chris@87: DESIRED: 1.2345672000000001e-021 Chris@87: Chris@87: the evaluated condition that raises the exception is Chris@87: Chris@87: >>> abs(0.12345670e-20/1e-21 - 0.12345672e-20/1e-21) >= 10**-(8-1) Chris@87: True Chris@87: Chris@87: """ Chris@87: import numpy as np Chris@87: Chris@87: (actual, desired) = map(float, (actual, desired)) Chris@87: if desired==actual: Chris@87: return Chris@87: # Normalized the numbers to be in range (-10.0,10.0) Chris@87: # scale = float(pow(10,math.floor(math.log10(0.5*(abs(desired)+abs(actual)))))) Chris@87: with np.errstate(invalid='ignore'): Chris@87: scale = 0.5*(np.abs(desired) + np.abs(actual)) Chris@87: scale = np.power(10, np.floor(np.log10(scale))) Chris@87: try: Chris@87: sc_desired = desired/scale Chris@87: except ZeroDivisionError: Chris@87: sc_desired = 0.0 Chris@87: try: Chris@87: sc_actual = actual/scale Chris@87: except ZeroDivisionError: Chris@87: sc_actual = 0.0 Chris@87: msg = build_err_msg([actual, desired], err_msg, Chris@87: header='Items are not equal to %d significant digits:' % Chris@87: significant, Chris@87: verbose=verbose) Chris@87: try: Chris@87: # If one of desired/actual is not finite, handle it specially here: Chris@87: # check that both are nan if any is a nan, and test for equality Chris@87: # otherwise Chris@87: if not (gisfinite(desired) and gisfinite(actual)): Chris@87: if gisnan(desired) or gisnan(actual): Chris@87: if not (gisnan(desired) and gisnan(actual)): Chris@87: raise AssertionError(msg) Chris@87: else: Chris@87: if not desired == actual: Chris@87: raise AssertionError(msg) Chris@87: return Chris@87: except (TypeError, NotImplementedError): Chris@87: pass Chris@87: if np.abs(sc_desired - sc_actual) >= np.power(10., -(significant-1)) : Chris@87: raise AssertionError(msg) Chris@87: Chris@87: def assert_array_compare(comparison, x, y, err_msg='', verbose=True, Chris@87: header='', precision=6): Chris@87: from numpy.core import array, isnan, isinf, any, all, inf Chris@87: x = array(x, copy=False, subok=True) Chris@87: y = array(y, copy=False, subok=True) Chris@87: Chris@87: def isnumber(x): Chris@87: return x.dtype.char in '?bhilqpBHILQPefdgFDG' Chris@87: Chris@87: def chk_same_position(x_id, y_id, hasval='nan'): Chris@87: """Handling nan/inf: check that x and y have the nan/inf at the same Chris@87: locations.""" Chris@87: try: Chris@87: assert_array_equal(x_id, y_id) Chris@87: except AssertionError: Chris@87: msg = build_err_msg([x, y], Chris@87: err_msg + '\nx and y %s location mismatch:' \ Chris@87: % (hasval), verbose=verbose, header=header, Chris@87: names=('x', 'y'), precision=precision) Chris@87: raise AssertionError(msg) Chris@87: Chris@87: try: Chris@87: cond = (x.shape==() or y.shape==()) or x.shape == y.shape Chris@87: if not cond: Chris@87: msg = build_err_msg([x, y], Chris@87: err_msg Chris@87: + '\n(shapes %s, %s mismatch)' % (x.shape, Chris@87: y.shape), Chris@87: verbose=verbose, header=header, Chris@87: names=('x', 'y'), precision=precision) Chris@87: if not cond : Chris@87: raise AssertionError(msg) Chris@87: Chris@87: if isnumber(x) and isnumber(y): Chris@87: x_isnan, y_isnan = isnan(x), isnan(y) Chris@87: x_isinf, y_isinf = isinf(x), isinf(y) Chris@87: Chris@87: # Validate that the special values are in the same place Chris@87: if any(x_isnan) or any(y_isnan): Chris@87: chk_same_position(x_isnan, y_isnan, hasval='nan') Chris@87: if any(x_isinf) or any(y_isinf): Chris@87: # Check +inf and -inf separately, since they are different Chris@87: chk_same_position(x == +inf, y == +inf, hasval='+inf') Chris@87: chk_same_position(x == -inf, y == -inf, hasval='-inf') Chris@87: Chris@87: # Combine all the special values Chris@87: x_id, y_id = x_isnan, y_isnan Chris@87: x_id |= x_isinf Chris@87: y_id |= y_isinf Chris@87: Chris@87: # Only do the comparison if actual values are left Chris@87: if all(x_id): Chris@87: return Chris@87: Chris@87: if any(x_id): Chris@87: val = comparison(x[~x_id], y[~y_id]) Chris@87: else: Chris@87: val = comparison(x, y) Chris@87: else: Chris@87: val = comparison(x, y) Chris@87: Chris@87: if isinstance(val, bool): Chris@87: cond = val Chris@87: reduced = [0] Chris@87: else: Chris@87: reduced = val.ravel() Chris@87: cond = reduced.all() Chris@87: reduced = reduced.tolist() Chris@87: if not cond: Chris@87: match = 100-100.0*reduced.count(1)/len(reduced) Chris@87: msg = build_err_msg([x, y], Chris@87: err_msg Chris@87: + '\n(mismatch %s%%)' % (match,), Chris@87: verbose=verbose, header=header, Chris@87: names=('x', 'y'), precision=precision) Chris@87: if not cond : Chris@87: raise AssertionError(msg) Chris@87: except ValueError as e: Chris@87: import traceback Chris@87: efmt = traceback.format_exc() Chris@87: header = 'error during assertion:\n\n%s\n\n%s' % (efmt, header) Chris@87: Chris@87: msg = build_err_msg([x, y], err_msg, verbose=verbose, header=header, Chris@87: names=('x', 'y'), precision=precision) Chris@87: raise ValueError(msg) Chris@87: Chris@87: def assert_array_equal(x, y, err_msg='', verbose=True): Chris@87: """ Chris@87: Raises an AssertionError if two array_like objects are not equal. Chris@87: Chris@87: Given two array_like objects, check that the shape is equal and all Chris@87: elements of these objects are equal. An exception is raised at Chris@87: shape mismatch or conflicting values. In contrast to the standard usage Chris@87: in numpy, NaNs are compared like numbers, no assertion is raised if Chris@87: both objects have NaNs in the same positions. Chris@87: Chris@87: The usual caution for verifying equality with floating point numbers is Chris@87: advised. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: x : array_like Chris@87: The actual object to check. Chris@87: y : array_like Chris@87: The desired, expected object. Chris@87: err_msg : str, optional Chris@87: The error message to be printed in case of failure. Chris@87: verbose : bool, optional Chris@87: If True, the conflicting values are appended to the error message. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If actual and desired objects are not equal. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: assert_allclose: Compare two array_like objects for equality with desired Chris@87: relative and/or absolute precision. Chris@87: assert_array_almost_equal_nulp, assert_array_max_ulp, assert_equal Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: The first assert does not raise an exception: Chris@87: Chris@87: >>> np.testing.assert_array_equal([1.0,2.33333,np.nan], Chris@87: ... [np.exp(0),2.33333, np.nan]) Chris@87: Chris@87: Assert fails with numerical inprecision with floats: Chris@87: Chris@87: >>> np.testing.assert_array_equal([1.0,np.pi,np.nan], Chris@87: ... [1, np.sqrt(np.pi)**2, np.nan]) Chris@87: ... Chris@87: : Chris@87: AssertionError: Chris@87: Arrays are not equal Chris@87: Chris@87: (mismatch 50.0%) Chris@87: x: array([ 1. , 3.14159265, NaN]) Chris@87: y: array([ 1. , 3.14159265, NaN]) Chris@87: Chris@87: Use `assert_allclose` or one of the nulp (number of floating point values) Chris@87: functions for these cases instead: Chris@87: Chris@87: >>> np.testing.assert_allclose([1.0,np.pi,np.nan], Chris@87: ... [1, np.sqrt(np.pi)**2, np.nan], Chris@87: ... rtol=1e-10, atol=0) Chris@87: Chris@87: """ Chris@87: assert_array_compare(operator.__eq__, x, y, err_msg=err_msg, Chris@87: verbose=verbose, header='Arrays are not equal') Chris@87: Chris@87: def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True): Chris@87: """ Chris@87: Raises an AssertionError if two objects are not equal up to desired Chris@87: precision. Chris@87: Chris@87: .. note:: It is recommended to use one of `assert_allclose`, Chris@87: `assert_array_almost_equal_nulp` or `assert_array_max_ulp` Chris@87: instead of this function for more consistent floating point Chris@87: comparisons. Chris@87: Chris@87: The test verifies identical shapes and verifies values with Chris@87: ``abs(desired-actual) < 0.5 * 10**(-decimal)``. Chris@87: Chris@87: Given two array_like objects, check that the shape is equal and all Chris@87: elements of these objects are almost equal. An exception is raised at Chris@87: shape mismatch or conflicting values. In contrast to the standard usage Chris@87: in numpy, NaNs are compared like numbers, no assertion is raised if Chris@87: both objects have NaNs in the same positions. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: x : array_like Chris@87: The actual object to check. Chris@87: y : array_like Chris@87: The desired, expected object. Chris@87: decimal : int, optional Chris@87: Desired precision, default is 6. Chris@87: err_msg : str, optional Chris@87: The error message to be printed in case of failure. Chris@87: verbose : bool, optional Chris@87: If True, the conflicting values are appended to the error message. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If actual and desired are not equal up to specified precision. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: assert_allclose: Compare two array_like objects for equality with desired Chris@87: relative and/or absolute precision. Chris@87: assert_array_almost_equal_nulp, assert_array_max_ulp, assert_equal Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: the first assert does not raise an exception Chris@87: Chris@87: >>> np.testing.assert_array_almost_equal([1.0,2.333,np.nan], Chris@87: [1.0,2.333,np.nan]) Chris@87: Chris@87: >>> np.testing.assert_array_almost_equal([1.0,2.33333,np.nan], Chris@87: ... [1.0,2.33339,np.nan], decimal=5) Chris@87: ... Chris@87: : Chris@87: AssertionError: Chris@87: Arrays are not almost equal Chris@87: Chris@87: (mismatch 50.0%) Chris@87: x: array([ 1. , 2.33333, NaN]) Chris@87: y: array([ 1. , 2.33339, NaN]) Chris@87: Chris@87: >>> np.testing.assert_array_almost_equal([1.0,2.33333,np.nan], Chris@87: ... [1.0,2.33333, 5], decimal=5) Chris@87: : Chris@87: ValueError: Chris@87: Arrays are not almost equal Chris@87: x: array([ 1. , 2.33333, NaN]) Chris@87: y: array([ 1. , 2.33333, 5. ]) Chris@87: Chris@87: """ Chris@87: from numpy.core import around, number, float_, result_type, array Chris@87: from numpy.core.numerictypes import issubdtype Chris@87: from numpy.core.fromnumeric import any as npany Chris@87: def compare(x, y): Chris@87: try: Chris@87: if npany(gisinf(x)) or npany( gisinf(y)): Chris@87: xinfid = gisinf(x) Chris@87: yinfid = gisinf(y) Chris@87: if not xinfid == yinfid: Chris@87: return False Chris@87: # if one item, x and y is +- inf Chris@87: if x.size == y.size == 1: Chris@87: return x == y Chris@87: x = x[~xinfid] Chris@87: y = y[~yinfid] Chris@87: except (TypeError, NotImplementedError): Chris@87: pass Chris@87: Chris@87: # make sure y is an inexact type to avoid abs(MIN_INT); will cause Chris@87: # casting of x later. Chris@87: dtype = result_type(y, 1.) Chris@87: y = array(y, dtype=dtype, copy=False, subok=True) Chris@87: z = abs(x-y) Chris@87: Chris@87: if not issubdtype(z.dtype, number): Chris@87: z = z.astype(float_) # handle object arrays Chris@87: Chris@87: return around(z, decimal) <= 10.0**(-decimal) Chris@87: Chris@87: assert_array_compare(compare, x, y, err_msg=err_msg, verbose=verbose, Chris@87: header=('Arrays are not almost equal to %d decimals' % decimal), Chris@87: precision=decimal) Chris@87: Chris@87: Chris@87: def assert_array_less(x, y, err_msg='', verbose=True): Chris@87: """ Chris@87: Raises an AssertionError if two array_like objects are not ordered by less Chris@87: than. Chris@87: Chris@87: Given two array_like objects, check that the shape is equal and all Chris@87: elements of the first object are strictly smaller than those of the Chris@87: second object. An exception is raised at shape mismatch or incorrectly Chris@87: ordered values. Shape mismatch does not raise if an object has zero Chris@87: dimension. In contrast to the standard usage in numpy, NaNs are Chris@87: compared, no assertion is raised if both objects have NaNs in the same Chris@87: positions. Chris@87: Chris@87: Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: x : array_like Chris@87: The smaller object to check. Chris@87: y : array_like Chris@87: The larger object to compare. Chris@87: err_msg : string Chris@87: The error message to be printed in case of failure. Chris@87: verbose : bool Chris@87: If True, the conflicting values are appended to the error message. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If actual and desired objects are not equal. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: assert_array_equal: tests objects for equality Chris@87: assert_array_almost_equal: test objects for equality up to precision Chris@87: Chris@87: Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.testing.assert_array_less([1.0, 1.0, np.nan], [1.1, 2.0, np.nan]) Chris@87: >>> np.testing.assert_array_less([1.0, 1.0, np.nan], [1, 2.0, np.nan]) Chris@87: ... Chris@87: : Chris@87: Arrays are not less-ordered Chris@87: (mismatch 50.0%) Chris@87: x: array([ 1., 1., NaN]) Chris@87: y: array([ 1., 2., NaN]) Chris@87: Chris@87: >>> np.testing.assert_array_less([1.0, 4.0], 3) Chris@87: ... Chris@87: : Chris@87: Arrays are not less-ordered Chris@87: (mismatch 50.0%) Chris@87: x: array([ 1., 4.]) Chris@87: y: array(3) Chris@87: Chris@87: >>> np.testing.assert_array_less([1.0, 2.0, 3.0], [4]) Chris@87: ... Chris@87: : Chris@87: Arrays are not less-ordered Chris@87: (shapes (3,), (1,) mismatch) Chris@87: x: array([ 1., 2., 3.]) Chris@87: y: array([4]) Chris@87: Chris@87: """ Chris@87: assert_array_compare(operator.__lt__, x, y, err_msg=err_msg, Chris@87: verbose=verbose, Chris@87: header='Arrays are not less-ordered') Chris@87: Chris@87: def runstring(astr, dict): Chris@87: exec(astr, dict) Chris@87: Chris@87: def assert_string_equal(actual, desired): Chris@87: """ Chris@87: Test if two strings are equal. Chris@87: Chris@87: If the given strings are equal, `assert_string_equal` does nothing. Chris@87: If they are not equal, an AssertionError is raised, and the diff Chris@87: between the strings is shown. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: actual : str Chris@87: The string to test for equality against the expected string. Chris@87: desired : str Chris@87: The expected string. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.testing.assert_string_equal('abc', 'abc') Chris@87: >>> np.testing.assert_string_equal('abc', 'abcd') Chris@87: Traceback (most recent call last): Chris@87: File "", line 1, in Chris@87: ... Chris@87: AssertionError: Differences in strings: Chris@87: - abc+ abcd? + Chris@87: Chris@87: """ Chris@87: # delay import of difflib to reduce startup time Chris@87: import difflib Chris@87: Chris@87: if not isinstance(actual, str) : Chris@87: raise AssertionError(repr(type(actual))) Chris@87: if not isinstance(desired, str): Chris@87: raise AssertionError(repr(type(desired))) Chris@87: if re.match(r'\A'+desired+r'\Z', actual, re.M): Chris@87: return Chris@87: Chris@87: diff = list(difflib.Differ().compare(actual.splitlines(1), desired.splitlines(1))) Chris@87: diff_list = [] Chris@87: while diff: Chris@87: d1 = diff.pop(0) Chris@87: if d1.startswith(' '): Chris@87: continue Chris@87: if d1.startswith('- '): Chris@87: l = [d1] Chris@87: d2 = diff.pop(0) Chris@87: if d2.startswith('? '): Chris@87: l.append(d2) Chris@87: d2 = diff.pop(0) Chris@87: if not d2.startswith('+ ') : Chris@87: raise AssertionError(repr(d2)) Chris@87: l.append(d2) Chris@87: d3 = diff.pop(0) Chris@87: if d3.startswith('? '): Chris@87: l.append(d3) Chris@87: else: Chris@87: diff.insert(0, d3) Chris@87: if re.match(r'\A'+d2[2:]+r'\Z', d1[2:]): Chris@87: continue Chris@87: diff_list.extend(l) Chris@87: continue Chris@87: raise AssertionError(repr(d1)) Chris@87: if not diff_list: Chris@87: return Chris@87: msg = 'Differences in strings:\n%s' % (''.join(diff_list)).rstrip() Chris@87: if actual != desired : Chris@87: raise AssertionError(msg) Chris@87: Chris@87: Chris@87: def rundocs(filename=None, raise_on_error=True): Chris@87: """ Chris@87: Run doctests found in the given file. Chris@87: Chris@87: By default `rundocs` raises an AssertionError on failure. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: filename : str Chris@87: The path to the file for which the doctests are run. Chris@87: raise_on_error : bool Chris@87: Whether to raise an AssertionError when a doctest fails. Default is Chris@87: True. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: The doctests can be run by the user/developer by adding the ``doctests`` Chris@87: argument to the ``test()`` call. For example, to run all tests (including Chris@87: doctests) for `numpy.lib`: Chris@87: Chris@87: >>> np.lib.test(doctests=True) #doctest: +SKIP Chris@87: """ Chris@87: import doctest, imp Chris@87: if filename is None: Chris@87: f = sys._getframe(1) Chris@87: filename = f.f_globals['__file__'] Chris@87: name = os.path.splitext(os.path.basename(filename))[0] Chris@87: path = [os.path.dirname(filename)] Chris@87: file, pathname, description = imp.find_module(name, path) Chris@87: try: Chris@87: m = imp.load_module(name, file, pathname, description) Chris@87: finally: Chris@87: file.close() Chris@87: Chris@87: tests = doctest.DocTestFinder().find(m) Chris@87: runner = doctest.DocTestRunner(verbose=False) Chris@87: Chris@87: msg = [] Chris@87: if raise_on_error: Chris@87: out = lambda s: msg.append(s) Chris@87: else: Chris@87: out = None Chris@87: Chris@87: for test in tests: Chris@87: runner.run(test, out=out) Chris@87: Chris@87: if runner.failures > 0 and raise_on_error: Chris@87: raise AssertionError("Some doctests failed:\n%s" % "\n".join(msg)) Chris@87: Chris@87: Chris@87: def raises(*args,**kwargs): Chris@87: nose = import_nose() Chris@87: return nose.tools.raises(*args,**kwargs) Chris@87: Chris@87: Chris@87: def assert_raises(*args,**kwargs): Chris@87: """ Chris@87: assert_raises(exception_class, callable, *args, **kwargs) Chris@87: Chris@87: Fail unless an exception of class exception_class is thrown Chris@87: by callable when invoked with arguments args and keyword Chris@87: arguments kwargs. If a different type of exception is Chris@87: thrown, it will not be caught, and the test case will be Chris@87: deemed to have suffered an error, exactly as for an Chris@87: unexpected exception. Chris@87: Chris@87: """ Chris@87: nose = import_nose() Chris@87: return nose.tools.assert_raises(*args,**kwargs) Chris@87: Chris@87: Chris@87: assert_raises_regex_impl = None Chris@87: Chris@87: Chris@87: def assert_raises_regex(exception_class, expected_regexp, Chris@87: callable_obj=None, *args, **kwargs): Chris@87: """ Chris@87: Fail unless an exception of class exception_class and with message that Chris@87: matches expected_regexp is thrown by callable when invoked with arguments Chris@87: args and keyword arguments kwargs. Chris@87: Chris@87: Name of this function adheres to Python 3.2+ reference, but should work in Chris@87: all versions down to 2.6. Chris@87: Chris@87: """ Chris@87: nose = import_nose() Chris@87: Chris@87: global assert_raises_regex_impl Chris@87: if assert_raises_regex_impl is None: Chris@87: try: Chris@87: # Python 3.2+ Chris@87: assert_raises_regex_impl = nose.tools.assert_raises_regex Chris@87: except AttributeError: Chris@87: try: Chris@87: # 2.7+ Chris@87: assert_raises_regex_impl = nose.tools.assert_raises_regexp Chris@87: except AttributeError: Chris@87: # 2.6 Chris@87: Chris@87: # This class is copied from Python2.7 stdlib almost verbatim Chris@87: class _AssertRaisesContext(object): Chris@87: """A context manager used to implement TestCase.assertRaises* methods.""" Chris@87: Chris@87: def __init__(self, expected, expected_regexp=None): Chris@87: self.expected = expected Chris@87: self.expected_regexp = expected_regexp Chris@87: Chris@87: def failureException(self, msg): Chris@87: return AssertionError(msg) Chris@87: Chris@87: def __enter__(self): Chris@87: return self Chris@87: Chris@87: def __exit__(self, exc_type, exc_value, tb): Chris@87: if exc_type is None: Chris@87: try: Chris@87: exc_name = self.expected.__name__ Chris@87: except AttributeError: Chris@87: exc_name = str(self.expected) Chris@87: raise self.failureException( Chris@87: "{0} not raised".format(exc_name)) Chris@87: if not issubclass(exc_type, self.expected): Chris@87: # let unexpected exceptions pass through Chris@87: return False Chris@87: self.exception = exc_value # store for later retrieval Chris@87: if self.expected_regexp is None: Chris@87: return True Chris@87: Chris@87: expected_regexp = self.expected_regexp Chris@87: if isinstance(expected_regexp, basestring): Chris@87: expected_regexp = re.compile(expected_regexp) Chris@87: if not expected_regexp.search(str(exc_value)): Chris@87: raise self.failureException( Chris@87: '"%s" does not match "%s"' % Chris@87: (expected_regexp.pattern, str(exc_value))) Chris@87: return True Chris@87: Chris@87: def impl(cls, regex, callable_obj, *a, **kw): Chris@87: mgr = _AssertRaisesContext(cls, regex) Chris@87: if callable_obj is None: Chris@87: return mgr Chris@87: with mgr: Chris@87: callable_obj(*a, **kw) Chris@87: assert_raises_regex_impl = impl Chris@87: Chris@87: return assert_raises_regex_impl(exception_class, expected_regexp, Chris@87: callable_obj, *args, **kwargs) Chris@87: Chris@87: Chris@87: def decorate_methods(cls, decorator, testmatch=None): Chris@87: """ Chris@87: Apply a decorator to all methods in a class matching a regular expression. Chris@87: Chris@87: The given decorator is applied to all public methods of `cls` that are Chris@87: matched by the regular expression `testmatch` Chris@87: (``testmatch.search(methodname)``). Methods that are private, i.e. start Chris@87: with an underscore, are ignored. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: cls : class Chris@87: Class whose methods to decorate. Chris@87: decorator : function Chris@87: Decorator to apply to methods Chris@87: testmatch : compiled regexp or str, optional Chris@87: The regular expression. Default value is None, in which case the Chris@87: nose default (``re.compile(r'(?:^|[\\b_\\.%s-])[Tt]est' % os.sep)``) Chris@87: is used. Chris@87: If `testmatch` is a string, it is compiled to a regular expression Chris@87: first. Chris@87: Chris@87: """ Chris@87: if testmatch is None: Chris@87: testmatch = re.compile(r'(?:^|[\\b_\\.%s-])[Tt]est' % os.sep) Chris@87: else: Chris@87: testmatch = re.compile(testmatch) Chris@87: cls_attr = cls.__dict__ Chris@87: Chris@87: # delayed import to reduce startup time Chris@87: from inspect import isfunction Chris@87: Chris@87: methods = [_m for _m in cls_attr.values() if isfunction(_m)] Chris@87: for function in methods: Chris@87: try: Chris@87: if hasattr(function, 'compat_func_name'): Chris@87: funcname = function.compat_func_name Chris@87: else: Chris@87: funcname = function.__name__ Chris@87: except AttributeError: Chris@87: # not a function Chris@87: continue Chris@87: if testmatch.search(funcname) and not funcname.startswith('_'): Chris@87: setattr(cls, funcname, decorator(function)) Chris@87: return Chris@87: Chris@87: Chris@87: def measure(code_str,times=1,label=None): Chris@87: """ Chris@87: Return elapsed time for executing code in the namespace of the caller. Chris@87: Chris@87: The supplied code string is compiled with the Python builtin ``compile``. Chris@87: The precision of the timing is 10 milli-seconds. If the code will execute Chris@87: fast on this timescale, it can be executed many times to get reasonable Chris@87: timing accuracy. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: code_str : str Chris@87: The code to be timed. Chris@87: times : int, optional Chris@87: The number of times the code is executed. Default is 1. The code is Chris@87: only compiled once. Chris@87: label : str, optional Chris@87: A label to identify `code_str` with. This is passed into ``compile`` Chris@87: as the second argument (for run-time error messages). Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: elapsed : float Chris@87: Total elapsed time in seconds for executing `code_str` `times` times. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> etime = np.testing.measure('for i in range(1000): np.sqrt(i**2)', Chris@87: ... times=times) Chris@87: >>> print "Time for a single execution : ", etime / times, "s" Chris@87: Time for a single execution : 0.005 s Chris@87: Chris@87: """ Chris@87: frame = sys._getframe(1) Chris@87: locs, globs = frame.f_locals, frame.f_globals Chris@87: Chris@87: code = compile(code_str, Chris@87: 'Test name: %s ' % label, Chris@87: 'exec') Chris@87: i = 0 Chris@87: elapsed = jiffies() Chris@87: while i < times: Chris@87: i += 1 Chris@87: exec(code, globs, locs) Chris@87: elapsed = jiffies() - elapsed Chris@87: return 0.01*elapsed Chris@87: Chris@87: def _assert_valid_refcount(op): Chris@87: """ Chris@87: Check that ufuncs don't mishandle refcount of object `1`. Chris@87: Used in a few regression tests. Chris@87: """ Chris@87: import numpy as np Chris@87: a = np.arange(100 * 100) Chris@87: b = np.arange(100*100).reshape(100, 100) Chris@87: c = b Chris@87: Chris@87: i = 1 Chris@87: Chris@87: rc = sys.getrefcount(i) Chris@87: for j in range(15): Chris@87: d = op(b, c) Chris@87: Chris@87: assert_(sys.getrefcount(i) >= rc) Chris@87: Chris@87: def assert_allclose(actual, desired, rtol=1e-7, atol=0, Chris@87: err_msg='', verbose=True): Chris@87: """ Chris@87: Raises an AssertionError if two objects are not equal up to desired Chris@87: tolerance. Chris@87: Chris@87: The test is equivalent to ``allclose(actual, desired, rtol, atol)``. Chris@87: It compares the difference between `actual` and `desired` to Chris@87: ``atol + rtol * abs(desired)``. Chris@87: Chris@87: .. versionadded:: 1.5.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: actual : array_like Chris@87: Array obtained. Chris@87: desired : array_like Chris@87: Array desired. Chris@87: rtol : float, optional Chris@87: Relative tolerance. Chris@87: atol : float, optional Chris@87: Absolute tolerance. Chris@87: err_msg : str, optional Chris@87: The error message to be printed in case of failure. Chris@87: verbose : bool, optional Chris@87: If True, the conflicting values are appended to the error message. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If actual and desired are not equal up to specified precision. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: assert_array_almost_equal_nulp, assert_array_max_ulp Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> x = [1e-5, 1e-3, 1e-1] Chris@87: >>> y = np.arccos(np.cos(x)) Chris@87: >>> assert_allclose(x, y, rtol=1e-5, atol=0) Chris@87: Chris@87: """ Chris@87: import numpy as np Chris@87: def compare(x, y): Chris@87: return np.allclose(x, y, rtol=rtol, atol=atol) Chris@87: Chris@87: actual, desired = np.asanyarray(actual), np.asanyarray(desired) Chris@87: header = 'Not equal to tolerance rtol=%g, atol=%g' % (rtol, atol) Chris@87: assert_array_compare(compare, actual, desired, err_msg=str(err_msg), Chris@87: verbose=verbose, header=header) Chris@87: Chris@87: def assert_array_almost_equal_nulp(x, y, nulp=1): Chris@87: """ Chris@87: Compare two arrays relatively to their spacing. Chris@87: Chris@87: This is a relatively robust method to compare two arrays whose amplitude Chris@87: is variable. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: x, y : array_like Chris@87: Input arrays. Chris@87: nulp : int, optional Chris@87: The maximum number of unit in the last place for tolerance (see Notes). Chris@87: Default is 1. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: None Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If the spacing between `x` and `y` for one or more elements is larger Chris@87: than `nulp`. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: assert_array_max_ulp : Check that all items of arrays differ in at most Chris@87: N Units in the Last Place. Chris@87: spacing : Return the distance between x and the nearest adjacent number. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: An assertion is raised if the following condition is not met:: Chris@87: Chris@87: abs(x - y) <= nulps * spacing(max(abs(x), abs(y))) Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> x = np.array([1., 1e-10, 1e-20]) Chris@87: >>> eps = np.finfo(x.dtype).eps Chris@87: >>> np.testing.assert_array_almost_equal_nulp(x, x*eps/2 + x) Chris@87: Chris@87: >>> np.testing.assert_array_almost_equal_nulp(x, x*eps + x) Chris@87: Traceback (most recent call last): Chris@87: ... Chris@87: AssertionError: X and Y are not equal to 1 ULP (max is 2) Chris@87: Chris@87: """ Chris@87: import numpy as np Chris@87: ax = np.abs(x) Chris@87: ay = np.abs(y) Chris@87: ref = nulp * np.spacing(np.where(ax > ay, ax, ay)) Chris@87: if not np.all(np.abs(x-y) <= ref): Chris@87: if np.iscomplexobj(x) or np.iscomplexobj(y): Chris@87: msg = "X and Y are not equal to %d ULP" % nulp Chris@87: else: Chris@87: max_nulp = np.max(nulp_diff(x, y)) Chris@87: msg = "X and Y are not equal to %d ULP (max is %g)" % (nulp, max_nulp) Chris@87: raise AssertionError(msg) Chris@87: Chris@87: def assert_array_max_ulp(a, b, maxulp=1, dtype=None): Chris@87: """ Chris@87: Check that all items of arrays differ in at most N Units in the Last Place. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: a, b : array_like Chris@87: Input arrays to be compared. Chris@87: maxulp : int, optional Chris@87: The maximum number of units in the last place that elements of `a` and Chris@87: `b` can differ. Default is 1. Chris@87: dtype : dtype, optional Chris@87: Data-type to convert `a` and `b` to if given. Default is None. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: ret : ndarray Chris@87: Array containing number of representable floating point numbers between Chris@87: items in `a` and `b`. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: AssertionError Chris@87: If one or more elements differ by more than `maxulp`. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: assert_array_almost_equal_nulp : Compare two arrays relatively to their Chris@87: spacing. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> a = np.linspace(0., 1., 100) Chris@87: >>> res = np.testing.assert_array_max_ulp(a, np.arcsin(np.sin(a))) Chris@87: Chris@87: """ Chris@87: import numpy as np Chris@87: ret = nulp_diff(a, b, dtype) Chris@87: if not np.all(ret <= maxulp): Chris@87: raise AssertionError("Arrays are not almost equal up to %g ULP" % \ Chris@87: maxulp) Chris@87: return ret Chris@87: Chris@87: def nulp_diff(x, y, dtype=None): Chris@87: """For each item in x and y, return the number of representable floating Chris@87: points between them. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: x : array_like Chris@87: first input array Chris@87: y : array_like Chris@87: second input array Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: nulp : array_like Chris@87: number of representable floating point numbers between each item in x Chris@87: and y. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: # By definition, epsilon is the smallest number such as 1 + eps != 1, so Chris@87: # there should be exactly one ULP between 1 and 1 + eps Chris@87: >>> nulp_diff(1, 1 + np.finfo(x.dtype).eps) Chris@87: 1.0 Chris@87: """ Chris@87: import numpy as np Chris@87: if dtype: Chris@87: x = np.array(x, dtype=dtype) Chris@87: y = np.array(y, dtype=dtype) Chris@87: else: Chris@87: x = np.array(x) Chris@87: y = np.array(y) Chris@87: Chris@87: t = np.common_type(x, y) Chris@87: if np.iscomplexobj(x) or np.iscomplexobj(y): Chris@87: raise NotImplementedError("_nulp not implemented for complex array") Chris@87: Chris@87: x = np.array(x, dtype=t) Chris@87: y = np.array(y, dtype=t) Chris@87: Chris@87: if not x.shape == y.shape: Chris@87: raise ValueError("x and y do not have the same shape: %s - %s" % \ Chris@87: (x.shape, y.shape)) Chris@87: Chris@87: def _diff(rx, ry, vdt): Chris@87: diff = np.array(rx-ry, dtype=vdt) Chris@87: return np.abs(diff) Chris@87: Chris@87: rx = integer_repr(x) Chris@87: ry = integer_repr(y) Chris@87: return _diff(rx, ry, t) Chris@87: Chris@87: def _integer_repr(x, vdt, comp): Chris@87: # Reinterpret binary representation of the float as sign-magnitude: Chris@87: # take into account two-complement representation Chris@87: # See also Chris@87: # http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm Chris@87: rx = x.view(vdt) Chris@87: if not (rx.size == 1): Chris@87: rx[rx < 0] = comp - rx[rx<0] Chris@87: else: Chris@87: if rx < 0: Chris@87: rx = comp - rx Chris@87: Chris@87: return rx Chris@87: Chris@87: def integer_repr(x): Chris@87: """Return the signed-magnitude interpretation of the binary representation of Chris@87: x.""" Chris@87: import numpy as np Chris@87: if x.dtype == np.float32: Chris@87: return _integer_repr(x, np.int32, np.int32(-2**31)) Chris@87: elif x.dtype == np.float64: Chris@87: return _integer_repr(x, np.int64, np.int64(-2**63)) Chris@87: else: Chris@87: raise ValueError("Unsupported dtype %s" % x.dtype) Chris@87: Chris@87: # The following two classes are copied from python 2.6 warnings module (context Chris@87: # manager) Chris@87: class WarningMessage(object): Chris@87: Chris@87: """ Chris@87: Holds the result of a single showwarning() call. Chris@87: Chris@87: Deprecated in 1.8.0 Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: `WarningMessage` is copied from the Python 2.6 warnings module, Chris@87: so it can be used in NumPy with older Python versions. Chris@87: Chris@87: """ Chris@87: Chris@87: _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", Chris@87: "line") Chris@87: Chris@87: def __init__(self, message, category, filename, lineno, file=None, Chris@87: line=None): Chris@87: local_values = locals() Chris@87: for attr in self._WARNING_DETAILS: Chris@87: setattr(self, attr, local_values[attr]) Chris@87: if category: Chris@87: self._category_name = category.__name__ Chris@87: else: Chris@87: self._category_name = None Chris@87: Chris@87: def __str__(self): Chris@87: return ("{message : %r, category : %r, filename : %r, lineno : %s, " Chris@87: "line : %r}" % (self.message, self._category_name, Chris@87: self.filename, self.lineno, self.line)) Chris@87: Chris@87: class WarningManager(object): Chris@87: """ Chris@87: A context manager that copies and restores the warnings filter upon Chris@87: exiting the context. Chris@87: Chris@87: The 'record' argument specifies whether warnings should be captured by a Chris@87: custom implementation of ``warnings.showwarning()`` and be appended to a Chris@87: list returned by the context manager. Otherwise None is returned by the Chris@87: context manager. The objects appended to the list are arguments whose Chris@87: attributes mirror the arguments to ``showwarning()``. Chris@87: Chris@87: The 'module' argument is to specify an alternative module to the module Chris@87: named 'warnings' and imported under that name. This argument is only useful Chris@87: when testing the warnings module itself. Chris@87: Chris@87: Deprecated in 1.8.0 Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: `WarningManager` is a copy of the ``catch_warnings`` context manager Chris@87: from the Python 2.6 warnings module, with slight modifications. Chris@87: It is copied so it can be used in NumPy with older Python versions. Chris@87: Chris@87: """ Chris@87: def __init__(self, record=False, module=None): Chris@87: self._record = record Chris@87: if module is None: Chris@87: self._module = sys.modules['warnings'] Chris@87: else: Chris@87: self._module = module Chris@87: self._entered = False Chris@87: Chris@87: def __enter__(self): Chris@87: if self._entered: Chris@87: raise RuntimeError("Cannot enter %r twice" % self) Chris@87: self._entered = True Chris@87: self._filters = self._module.filters Chris@87: self._module.filters = self._filters[:] Chris@87: self._showwarning = self._module.showwarning Chris@87: if self._record: Chris@87: log = [] Chris@87: def showwarning(*args, **kwargs): Chris@87: log.append(WarningMessage(*args, **kwargs)) Chris@87: self._module.showwarning = showwarning Chris@87: return log Chris@87: else: Chris@87: return None Chris@87: Chris@87: def __exit__(self): Chris@87: if not self._entered: Chris@87: raise RuntimeError("Cannot exit %r without entering first" % self) Chris@87: self._module.filters = self._filters Chris@87: self._module.showwarning = self._showwarning Chris@87: Chris@87: Chris@87: def assert_warns(warning_class, func, *args, **kw): Chris@87: """ Chris@87: Fail unless the given callable throws the specified warning. Chris@87: Chris@87: A warning of class warning_class should be thrown by the callable when Chris@87: invoked with arguments args and keyword arguments kwargs. Chris@87: If a different type of warning is thrown, it will not be caught, and the Chris@87: test case will be deemed to have suffered an error. Chris@87: Chris@87: .. versionadded:: 1.4.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: warning_class : class Chris@87: The class defining the warning that `func` is expected to throw. Chris@87: func : callable Chris@87: The callable to test. Chris@87: \\*args : Arguments Chris@87: Arguments passed to `func`. Chris@87: \\*\\*kwargs : Kwargs Chris@87: Keyword arguments passed to `func`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: The value returned by `func`. Chris@87: Chris@87: """ Chris@87: with warnings.catch_warnings(record=True) as l: Chris@87: warnings.simplefilter('always') Chris@87: result = func(*args, **kw) Chris@87: if not len(l) > 0: Chris@87: raise AssertionError("No warning raised when calling %s" Chris@87: % func.__name__) Chris@87: if not l[0].category is warning_class: Chris@87: raise AssertionError("First warning for %s is not a " \ Chris@87: "%s( is %s)" % (func.__name__, warning_class, l[0])) Chris@87: return result Chris@87: Chris@87: def assert_no_warnings(func, *args, **kw): Chris@87: """ Chris@87: Fail if the given callable produces any warnings. Chris@87: Chris@87: .. versionadded:: 1.7.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: func : callable Chris@87: The callable to test. Chris@87: \\*args : Arguments Chris@87: Arguments passed to `func`. Chris@87: \\*\\*kwargs : Kwargs Chris@87: Keyword arguments passed to `func`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: The value returned by `func`. Chris@87: Chris@87: """ Chris@87: with warnings.catch_warnings(record=True) as l: Chris@87: warnings.simplefilter('always') Chris@87: result = func(*args, **kw) Chris@87: if len(l) > 0: Chris@87: raise AssertionError("Got warnings when calling %s: %s" Chris@87: % (func.__name__, l)) Chris@87: return result Chris@87: Chris@87: Chris@87: def _gen_alignment_data(dtype=float32, type='binary', max_size=24): Chris@87: """ Chris@87: generator producing data with different alignment and offsets Chris@87: to test simd vectorization Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: dtype : dtype Chris@87: data type to produce Chris@87: type : string Chris@87: 'unary': create data for unary operations, creates one input Chris@87: and output array Chris@87: 'binary': create data for unary operations, creates two input Chris@87: and output array Chris@87: max_size : integer Chris@87: maximum size of data to produce Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: if type is 'unary' yields one output, one input array and a message Chris@87: containing information on the data Chris@87: if type is 'binary' yields one output array, two input array and a message Chris@87: containing information on the data Chris@87: Chris@87: """ Chris@87: ufmt = 'unary offset=(%d, %d), size=%d, dtype=%r, %s' Chris@87: bfmt = 'binary offset=(%d, %d, %d), size=%d, dtype=%r, %s' Chris@87: for o in range(3): Chris@87: for s in range(o + 2, max(o + 3, max_size)): Chris@87: if type == 'unary': Chris@87: inp = lambda : arange(s, dtype=dtype)[o:] Chris@87: out = empty((s,), dtype=dtype)[o:] Chris@87: yield out, inp(), ufmt % (o, o, s, dtype, 'out of place') Chris@87: yield inp(), inp(), ufmt % (o, o, s, dtype, 'in place') Chris@87: yield out[1:], inp()[:-1], ufmt % \ Chris@87: (o + 1, o, s - 1, dtype, 'out of place') Chris@87: yield out[:-1], inp()[1:], ufmt % \ Chris@87: (o, o + 1, s - 1, dtype, 'out of place') Chris@87: yield inp()[:-1], inp()[1:], ufmt % \ Chris@87: (o, o + 1, s - 1, dtype, 'aliased') Chris@87: yield inp()[1:], inp()[:-1], ufmt % \ Chris@87: (o + 1, o, s - 1, dtype, 'aliased') Chris@87: if type == 'binary': Chris@87: inp1 = lambda :arange(s, dtype=dtype)[o:] Chris@87: inp2 = lambda :arange(s, dtype=dtype)[o:] Chris@87: out = empty((s,), dtype=dtype)[o:] Chris@87: yield out, inp1(), inp2(), bfmt % \ Chris@87: (o, o, o, s, dtype, 'out of place') Chris@87: yield inp1(), inp1(), inp2(), bfmt % \ Chris@87: (o, o, o, s, dtype, 'in place1') Chris@87: yield inp2(), inp1(), inp2(), bfmt % \ Chris@87: (o, o, o, s, dtype, 'in place2') Chris@87: yield out[1:], inp1()[:-1], inp2()[:-1], bfmt % \ Chris@87: (o + 1, o, o, s - 1, dtype, 'out of place') Chris@87: yield out[:-1], inp1()[1:], inp2()[:-1], bfmt % \ Chris@87: (o, o + 1, o, s - 1, dtype, 'out of place') Chris@87: yield out[:-1], inp1()[:-1], inp2()[1:], bfmt % \ Chris@87: (o, o, o + 1, s - 1, dtype, 'out of place') Chris@87: yield inp1()[1:], inp1()[:-1], inp2()[:-1], bfmt % \ Chris@87: (o + 1, o, o, s - 1, dtype, 'aliased') Chris@87: yield inp1()[:-1], inp1()[1:], inp2()[:-1], bfmt % \ Chris@87: (o, o + 1, o, s - 1, dtype, 'aliased') Chris@87: yield inp1()[:-1], inp1()[:-1], inp2()[1:], bfmt % \ Chris@87: (o, o, o + 1, s - 1, dtype, 'aliased') Chris@87: Chris@87: Chris@87: class IgnoreException(Exception): Chris@87: "Ignoring this exception due to disabled feature" Chris@87: Chris@87: Chris@87: @contextlib.contextmanager Chris@87: def tempdir(*args, **kwargs): Chris@87: """Context manager to provide a temporary test folder. Chris@87: Chris@87: All arguments are passed as this to the underlying tempfile.mkdtemp Chris@87: function. Chris@87: Chris@87: """ Chris@87: tmpdir = mkdtemp(*args, **kwargs) Chris@87: yield tmpdir Chris@87: shutil.rmtree(tmpdir)