diff options
-rw-r--r-- | benchmarks/benchmarks/bench_overrides.py | 10 | ||||
-rw-r--r-- | numpy/core/code_generators/generate_umath.py | 2 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.c.src | 25 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.h.src | 3 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_type_resolution.c | 9 | ||||
-rw-r--r-- | numpy/core/tests/test_datetime.py | 82 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 4 | ||||
-rw-r--r-- | numpy/distutils/fcompiler/environment.py | 15 | ||||
-rw-r--r-- | numpy/distutils/tests/test_fcompiler.py | 41 |
9 files changed, 181 insertions, 10 deletions
diff --git a/benchmarks/benchmarks/bench_overrides.py b/benchmarks/benchmarks/bench_overrides.py index b42c5d185..58572d07d 100644 --- a/benchmarks/benchmarks/bench_overrides.py +++ b/benchmarks/benchmarks/bench_overrides.py @@ -2,7 +2,15 @@ from __future__ import absolute_import, division, print_function from .common import Benchmark -from numpy.core.overrides import array_function_dispatch +try: + from numpy.core.overrides import array_function_dispatch +except ImportError: + # Don't fail at import time with old Numpy versions + def array_function_dispatch(*args, **kwargs): + def wrap(*args, **kwargs): + return None + return wrap + import numpy as np diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index f5ee02c42..108fff631 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -315,7 +315,7 @@ defdict = { TD(intfltcmplx), [TypeDescription('m', FullTypeDescr, 'mq', 'm'), TypeDescription('m', FullTypeDescr, 'md', 'm'), - #TypeDescription('m', FullTypeDescr, 'mm', 'd'), + TypeDescription('m', FullTypeDescr, 'mm', 'q'), ], TD(O, f='PyNumber_FloorDivide'), ), diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index ae3ece77b..53afa817b 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -1619,6 +1619,31 @@ TIMEDELTA_mm_m_remainder(char **args, npy_intp *dimensions, npy_intp *steps, voi } } +NPY_NO_EXPORT void +TIMEDELTA_mm_q_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +{ + BINARY_LOOP { + const npy_timedelta in1 = *(npy_timedelta *)ip1; + const npy_timedelta in2 = *(npy_timedelta *)ip2; + if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) { + npy_set_floatstatus_invalid(); + *((npy_timedelta *)op1) = 0; + } + else if (in2 == 0) { + npy_set_floatstatus_divbyzero(); + *((npy_timedelta *)op1) = 0; + } + else { + if (((in1 > 0) != (in2 > 0)) && (in1 % in2 != 0)) { + *((npy_timedelta *)op1) = in1/in2 - 1; + } + else { + *((npy_timedelta *)op1) = in1/in2; + } + } + } +} + /* ***************************************************************************** ** FLOAT LOOPS ** diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src index 9b6327308..3c908121e 100644 --- a/numpy/core/src/umath/loops.h.src +++ b/numpy/core/src/umath/loops.h.src @@ -474,6 +474,9 @@ NPY_NO_EXPORT void TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void +TIMEDELTA_mm_q_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); + +NPY_NO_EXPORT void TIMEDELTA_mm_m_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); /* Special case equivalents to above functions */ diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c index ec60d9cfd..3cf507258 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.c +++ b/numpy/core/src/umath/ufunc_type_resolution.c @@ -1114,7 +1114,16 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc, } out_dtypes[1] = out_dtypes[0]; Py_INCREF(out_dtypes[1]); + + /* + * TODO: split function into truediv and floordiv resolvers + */ + if (strcmp(ufunc->name, "floor_divide") == 0) { + out_dtypes[2] = PyArray_DescrFromType(NPY_LONGLONG); + } + else { out_dtypes[2] = PyArray_DescrFromType(NPY_DOUBLE); + } if (out_dtypes[2] == NULL) { Py_DECREF(out_dtypes[0]); out_dtypes[0] = NULL; diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index b2ce0402a..cb7555a34 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -1081,6 +1081,86 @@ class TestDateTime(object): check(np.timedelta64(0), f, nat) check(nat, f, nat) + @pytest.mark.parametrize("op1, op2, exp", [ + # m8 same units round down + (np.timedelta64(7, 's'), + np.timedelta64(4, 's'), + 1), + # m8 same units round down with negative + (np.timedelta64(7, 's'), + np.timedelta64(-4, 's'), + -2), + # m8 same units negative no round down + (np.timedelta64(8, 's'), + np.timedelta64(-4, 's'), + -2), + # m8 different units + (np.timedelta64(1, 'm'), + np.timedelta64(31, 's'), + 1), + # m8 generic units + (np.timedelta64(1890), + np.timedelta64(31), + 60), + # Y // M works + (np.timedelta64(2, 'Y'), + np.timedelta64('13', 'M'), + 1), + # handle 1D arrays + (np.array([1, 2, 3], dtype='m8'), + np.array([2], dtype='m8'), + np.array([0, 1, 1], dtype=np.int64)), + ]) + def test_timedelta_floor_divide(self, op1, op2, exp): + assert_equal(op1 // op2, exp) + + @pytest.mark.parametrize("op1, op2", [ + # div by 0 + (np.timedelta64(10, 'us'), + np.timedelta64(0, 'us')), + # div with NaT + (np.timedelta64('NaT'), + np.timedelta64(50, 'us')), + # special case for int64 min + # in integer floor division + (np.timedelta64(np.iinfo(np.int64).min), + np.timedelta64(-1)), + ]) + def test_timedelta_floor_div_warnings(self, op1, op2): + with assert_warns(RuntimeWarning): + actual = op1 // op2 + assert_equal(actual, 0) + assert_equal(actual.dtype, np.int64) + + @pytest.mark.parametrize("val1, val2", [ + # the smallest integer that can't be represented + # exactly in a double should be preserved if we avoid + # casting to double in floordiv operation + (9007199254740993, 1), + # stress the alternate floordiv code path where + # operand signs don't match and remainder isn't 0 + (9007199254740999, -2), + ]) + def test_timedelta_floor_div_precision(self, val1, val2): + op1 = np.timedelta64(val1) + op2 = np.timedelta64(val2) + actual = op1 // op2 + # Python reference integer floor + expected = val1 // val2 + assert_equal(actual, expected) + + @pytest.mark.parametrize("val1, val2", [ + # years and months sometimes can't be unambiguously + # divided for floor division operation + (np.timedelta64(7, 'Y'), + np.timedelta64(3, 's')), + (np.timedelta64(7, 'M'), + np.timedelta64(1, 'D')), + ]) + def test_timedelta_floor_div_error(self, val1, val2): + with assert_raises_regex(TypeError, "common metadata divisor"): + val1 // val2 + def test_datetime_divide(self): for dta, tda, tdb, tdc, tdd in \ [ @@ -1111,8 +1191,6 @@ class TestDateTime(object): assert_equal(tda / tdd, 60.0) assert_equal(tdd / tda, 1.0 / 60.0) - # m8 // m8 - assert_raises(TypeError, np.floor_divide, tda, tdb) # int / m8 assert_raises(TypeError, np.divide, 2, tdb) # float / m8 diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 951c01c6d..91f4526bd 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -8000,10 +8000,10 @@ class TestAlignment(object): raise ValueError() def test_various_alignments(self): - for align in [1, 2, 3, 4, 8, 16, 32, 64, None]: + for align in [1, 2, 3, 4, 8, 12, 16, 32, 64, None]: for n in [0, 1, 3, 11]: for order in ["C", "F", None]: - for dtype in np.typecodes["All"]: + for dtype in list(np.typecodes["All"]) + ['i4,i4,i4']: if dtype == 'O': # object dtype can't be misaligned continue diff --git a/numpy/distutils/fcompiler/environment.py b/numpy/distutils/fcompiler/environment.py index 489784580..4238f35cb 100644 --- a/numpy/distutils/fcompiler/environment.py +++ b/numpy/distutils/fcompiler/environment.py @@ -1,6 +1,7 @@ from __future__ import division, absolute_import, print_function import os +import warnings from distutils.dist import Distribution __metaclass__ = type @@ -54,8 +55,18 @@ class EnvironmentConfig(object): if envvar is not None: envvar_contents = os.environ.get(envvar) if envvar_contents is not None: - if var and append and os.environ.get('NPY_DISTUTILS_APPEND_FLAGS', '0') == '1': - var = var + [envvar_contents] + if var and append: + if os.environ.get('NPY_DISTUTILS_APPEND_FLAGS', '0') == '1': + var = var + [envvar_contents] + else: + var = envvar_contents + if 'NPY_DISTUTILS_APPEND_FLAGS' not in os.environ.keys(): + msg = "{} is used as is, not appended ".format(envvar) + \ + "to flags already defined " + \ + "by numpy.distutils! Use NPY_DISTUTILS_APPEND_FLAGS=1 " + \ + "to obtain appending behavior instead (this " + \ + "behavior will become default in a future release)." + warnings.warn(msg, UserWarning, stacklevel=3) else: var = envvar_contents if confvar is not None and self._conf: diff --git a/numpy/distutils/tests/test_fcompiler.py b/numpy/distutils/tests/test_fcompiler.py index 95e44b051..ba19a97ea 100644 --- a/numpy/distutils/tests/test_fcompiler.py +++ b/numpy/distutils/tests/test_fcompiler.py @@ -1,6 +1,8 @@ from __future__ import division, absolute_import, print_function -from numpy.testing import assert_ +import pytest + +from numpy.testing import assert_, suppress_warnings import numpy.distutils.fcompiler customizable_flags = [ @@ -25,6 +27,7 @@ def test_fcompiler_flags(monkeypatch): monkeypatch.setenv(envvar, new_flag) new_flags = getattr(flag_vars, opt) + monkeypatch.delenv(envvar) assert_(new_flags == [new_flag]) @@ -33,12 +36,46 @@ def test_fcompiler_flags(monkeypatch): for opt, envvar in customizable_flags: new_flag = '-dummy-{}-flag'.format(opt) prev_flags = getattr(flag_vars, opt) - monkeypatch.setenv(envvar, new_flag) new_flags = getattr(flag_vars, opt) + monkeypatch.delenv(envvar) if prev_flags is None: assert_(new_flags == [new_flag]) else: assert_(new_flags == prev_flags + [new_flag]) + +def test_fcompiler_flags_append_warning(monkeypatch): + # Test to check that the warning for append behavior changing in future + # is triggered. Need to use a real compiler instance so that we have + # non-empty flags to start with (otherwise the "if var and append" check + # will always be false). + try: + with suppress_warnings() as sup: + sup.record() + fc = numpy.distutils.fcompiler.new_fcompiler(compiler='gnu95') + fc.customize() + except numpy.distutils.fcompiler.CompilerNotFound: + pytest.skip("gfortran not found, so can't execute this test") + + # Ensure NPY_DISTUTILS_APPEND_FLAGS not defined + monkeypatch.delenv('NPY_DISTUTILS_APPEND_FLAGS', raising=False) + + for opt, envvar in customizable_flags: + new_flag = '-dummy-{}-flag'.format(opt) + with suppress_warnings() as sup: + sup.record() + prev_flags = getattr(fc.flag_vars, opt) + + monkeypatch.setenv(envvar, new_flag) + with suppress_warnings() as sup: + sup.record() + new_flags = getattr(fc.flag_vars, opt) + if prev_flags: + # Check that warning was issued + assert len(sup.log) == 1 + + monkeypatch.delenv(envvar) + assert_(new_flags == [new_flag]) + |