summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml22
-rw-r--r--doc/release/1.10.4-notes.rst2
-rw-r--r--doc/release/1.11.0-notes.rst11
-rw-r--r--numpy/core/src/umath/loops.c.src9
-rw-r--r--numpy/core/src/umath/scalarmath.c.src55
-rw-r--r--numpy/core/tests/test_multiarray.py41
-rw-r--r--numpy/core/tests/test_scalarmath.py94
-rwxr-xr-xruntests.py24
8 files changed, 205 insertions, 53 deletions
diff --git a/.travis.yml b/.travis.yml
index 1832e317c..3066cbbaa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,6 +20,16 @@ cache:
directories:
- $HOME/.cache/pip
+env:
+ global:
+ - WHEELHOUSE_UPLOADER_USERNAME=travis.numpy
+ # The following is generated with the command:
+ # travis encrypt -r numpy/numpy WHEELHOUSE_UPLOADER_SECRET=tH3AP1KeY
+ - secure: "IEicLPrP2uW+jW51GRwkONQpdPqMVtQL5bdroqR/U8r9Tr\
+ XrbCVRhp4AP8JYZT0ptoBpmZWWGjmKBndB68QlMiUjQPow\
+ iFWt9Ka92CaqYdU7nqfWp9VImSndPmssjmCXJ1v1IjZPAM\
+ ahp7Qnm0rWRmA0z9SomuRUQOJQ6s684vU="
+
python:
- 2.6
- 2.7
@@ -51,16 +61,10 @@ matrix:
- python3-setuptools
- python: 2.7
env: NPY_RELAXED_STRIDES_CHECKING=0 PYTHON_OO=1
+ - python: 2.7
+ env: USE_WHEEL=1
- python: 3.5
- env:
- - USE_WHEEL=1
- - WHEELHOUSE_UPLOADER_USERNAME=travis.numpy
- # The following is generated with the command:
- # travis encrypt -r numpy/numpy WHEELHOUSE_UPLOADER_SECRET=tH3AP1KeY
- - secure: "IEicLPrP2uW+jW51GRwkONQpdPqMVtQL5bdroqR/U8r9Tr\
- XrbCVRhp4AP8JYZT0ptoBpmZWWGjmKBndB68QlMiUjQPow\
- iFWt9Ka92CaqYdU7nqfWp9VImSndPmssjmCXJ1v1IjZPAM\
- ahp7Qnm0rWRmA0z9SomuRUQOJQ6s684vU="
+ env: USE_WHEEL=1
- python: 2.7
env:
- PYTHONOPTIMIZE=2
diff --git a/doc/release/1.10.4-notes.rst b/doc/release/1.10.4-notes.rst
index 03eaf5e6b..7de732a22 100644
--- a/doc/release/1.10.4-notes.rst
+++ b/doc/release/1.10.4-notes.rst
@@ -25,7 +25,7 @@ Issues Fixed
Merged PRs
==========
-The following PRs have been merged into 1.10.3. When the PR is a backport,
+The following PRs have been merged into 1.10.4. When the PR is a backport,
the PR number for the original PR against master is listed.
* gh-6840 TST: Update travis testing script in 1.10.x
diff --git a/doc/release/1.11.0-notes.rst b/doc/release/1.11.0-notes.rst
index c4ff89230..0305688d8 100644
--- a/doc/release/1.11.0-notes.rst
+++ b/doc/release/1.11.0-notes.rst
@@ -8,9 +8,14 @@ Highlights
==========
-Dropped Support
-===============
-
+Build System Changes
+====================
+
+* Numpy now uses ``setuptools`` for its builds instead of plain distutils.
+ This fixes usage of ``install_requires='numpy'`` in the ``setup.py`` files of
+ projects that depend on Numpy (see gh-6551). It potentially affects the way
+ that build/install methods for Numpy itself behave though. Please report any
+ unexpected behavior on the Numpy issue tracker.
* Bento build support and related files have been removed.
* Single file build support and related files have been removed.
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index 563761bc0..e74ac9d40 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -1688,13 +1688,8 @@ NPY_NO_EXPORT void
BINARY_LOOP {
const @type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
- const @type@ res = npy_fmod@c@(in1,in2);
- if (res && ((in2 < 0) != (res < 0))) {
- *((@type@ *)op1) = res + in2;
- }
- else {
- *((@type@ *)op1) = res;
- }
+ const @type@ div = in1/in2;
+ *((@type@ *)op1) = in2*(div - npy_floor@c@(div));
}
}
diff --git a/numpy/core/src/umath/scalarmath.c.src b/numpy/core/src/umath/scalarmath.c.src
index c371a079f..706eccb31 100644
--- a/numpy/core/src/umath/scalarmath.c.src
+++ b/numpy/core/src/umath/scalarmath.c.src
@@ -272,10 +272,10 @@ static void
static @type@ (*_basic_@name@_floor)(@type@);
static @type@ (*_basic_@name@_sqrt)(@type@);
static @type@ (*_basic_@name@_fmod)(@type@, @type@);
-#define @name@_ctype_add(a, b, outp) *(outp) = a + b
-#define @name@_ctype_subtract(a, b, outp) *(outp) = a - b
-#define @name@_ctype_multiply(a, b, outp) *(outp) = a * b
-#define @name@_ctype_divide(a, b, outp) *(outp) = a / b
+#define @name@_ctype_add(a, b, outp) *(outp) = (a) + (b)
+#define @name@_ctype_subtract(a, b, outp) *(outp) = (a) - (b)
+#define @name@_ctype_multiply(a, b, outp) *(outp) = (a) * (b)
+#define @name@_ctype_divide(a, b, outp) *(outp) = (a) / (b)
#define @name@_ctype_true_divide @name@_ctype_divide
#define @name@_ctype_floor_divide(a, b, outp) \
*(outp) = _basic_@name@_floor((a) / (b))
@@ -316,16 +316,32 @@ static npy_half (*_basic_half_fmod)(npy_half, npy_half);
(outp)->real = (a).real * (b).real - (a).imag * (b).imag; \
(outp)->imag = (a).real * (b).imag + (a).imag * (b).real; \
} while(0)
-/* Note: complex division by zero must yield some complex inf */
+/* Algorithm identical to that in loops.c.src, for consistency */
#define @name@_ctype_divide(a, b, outp) do{ \
- @rtype@ d = (b).real*(b).real + (b).imag*(b).imag; \
- if (d != 0) { \
- (outp)->real = ((a).real*(b).real + (a).imag*(b).imag)/d; \
- (outp)->imag = ((a).imag*(b).real - (a).real*(b).imag)/d; \
+ @rtype@ in1r = (a).real; \
+ @rtype@ in1i = (a).imag; \
+ @rtype@ in2r = (b).real; \
+ @rtype@ in2i = (b).imag; \
+ @rtype@ in2r_abs = npy_fabs@c@(in2r); \
+ @rtype@ in2i_abs = npy_fabs@c@(in2i); \
+ if (in2r_abs >= in2i_abs) { \
+ if (in2r_abs == 0 && in2i_abs == 0) { \
+ /* divide by zero should yield a complex inf or nan */ \
+ (outp)->real = in1r/in2r_abs; \
+ (outp)->imag = in1i/in2i_abs; \
+ } \
+ else { \
+ @rtype@ rat = in2i/in2r; \
+ @rtype@ scl = 1.0@c@/(in2r + in2i*rat); \
+ (outp)->real = (in1r + in1i*rat)*scl; \
+ (outp)->imag = (in1i - in1r*rat)*scl; \
+ } \
} \
else { \
- (outp)->real = (a).real/d; \
- (outp)->imag = (a).imag/d; \
+ @rtype@ rat = in2r/in2i; \
+ @rtype@ scl = 1.0@c@/(in2i + in2r*rat); \
+ (outp)->real = (in1r*rat + in1i)*scl; \
+ (outp)->imag = (in1i*rat - in1r)*scl; \
} \
} while(0)
#define @name@_ctype_true_divide @name@_ctype_divide
@@ -343,23 +359,16 @@ static npy_half (*_basic_half_fmod)(npy_half, npy_half);
*/
static void
@name@_ctype_remainder(@type@ a, @type@ b, @type@ *out) {
- @type@ mod;
- mod = _basic_@name@_fmod(a, b);
- if (mod && (((b < 0) != (mod < 0)))) {
- mod += b;
- }
- *out = mod;
+ @type@ tmp = a/b;
+ *out = b * (tmp - _basic_@name@_floor(tmp));
}
/**end repeat**/
static void
half_ctype_remainder(npy_half a, npy_half b, npy_half *out) {
- float mod, fa = npy_half_to_float(a), fb = npy_half_to_float(b);
- mod = _basic_float_fmod(fa, fb);
- if (mod && (((fb < 0) != (mod < 0)))) {
- mod += fb;
- }
- *out = npy_float_to_half(mod);
+ float tmp, fa = npy_half_to_float(a), fb = npy_half_to_float(b);
+ float_ctype_remainder(fa, fb, &tmp);
+ *out = npy_float_to_half(tmp);
}
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 26617c1fc..1666af4f1 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -143,7 +143,8 @@ class TestAttributes(TestCase):
numpy_int = np.int_(0)
if sys.version_info[0] >= 3:
- # On Py3k int_ should not inherit from int, because it's not fixed-width anymore
+ # On Py3k int_ should not inherit from int, because it's not
+ # fixed-width anymore
assert_equal(isinstance(numpy_int, int), False)
else:
# Otherwise, it should inherit from int...
@@ -175,7 +176,8 @@ class TestAttributes(TestCase):
def make_array(size, offset, strides):
try:
- r = np.ndarray([size], dtype=int, buffer=x, offset=offset*x.itemsize)
+ r = np.ndarray([size], dtype=int, buffer=x,
+ offset=offset*x.itemsize)
except:
raise RuntimeError(getexception())
r.strides = strides = strides*x.itemsize
@@ -2327,6 +2329,41 @@ class TestMethods(TestCase):
assert_raises(AttributeError, lambda: a.conj())
assert_raises(AttributeError, lambda: a.conjugate())
+ def test_divmod_basic(self):
+ dt = np.typecodes['AllInteger'] + np.typecodes['Float']
+ for dt1, dt2 in itertools.product(dt, dt):
+ for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
+ if sg1 == -1 and dt1 in np.typecodes['UnsignedInteger']:
+ continue
+ if sg2 == -1 and dt2 in np.typecodes['UnsignedInteger']:
+ continue
+ fmt = 'dt1: %s, dt2: %s, sg1: %s, sg2: %s'
+ msg = fmt % (dt1, dt2, sg1, sg2)
+ a = np.array(sg1*71, dtype=dt1)
+ b = np.array(sg2*19, dtype=dt2)
+ div, rem = divmod(a, b)
+ assert_allclose(div*b + rem, a, err_msg=msg)
+ if sg2 == -1:
+ assert_(b < rem <= 0, msg)
+ else:
+ assert_(b > rem >= 0, msg)
+
+ def test_divmod_roundoff(self):
+ # gh-6127
+ dt = 'fdg'
+ for dt1, dt2 in itertools.product(dt, dt):
+ for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
+ fmt = 'dt1: %s, dt2: %s, sg1: %s, sg2: %s'
+ msg = fmt % (dt1, dt2, sg1, sg2)
+ a = np.array(sg1*78*6e-8, dtype=dt1)
+ b = np.array(sg2*6e-8, dtype=dt2)
+ div, rem = divmod(a, b)
+ assert_allclose(div*b + rem, a, err_msg=msg)
+ if sg2 == -1:
+ assert_(b < rem <= 0, msg)
+ else:
+ assert_(b > rem >= 0, msg)
+
class TestBinop(object):
def test_inplace(self):
diff --git a/numpy/core/tests/test_scalarmath.py b/numpy/core/tests/test_scalarmath.py
index 6dd9aa455..17f70f6c9 100644
--- a/numpy/core/tests/test_scalarmath.py
+++ b/numpy/core/tests/test_scalarmath.py
@@ -1,12 +1,13 @@
from __future__ import division, absolute_import, print_function
import sys
+import itertools
import numpy as np
from numpy.testing.utils import _gen_alignment_data
from numpy.testing import (
TestCase, run_module_suite, assert_, assert_equal, assert_raises,
- assert_almost_equal
+ assert_almost_equal, assert_allclose
)
types = [np.bool_, np.byte, np.ubyte, np.short, np.ushort, np.intc, np.uintc,
@@ -135,6 +136,44 @@ class TestPower(TestCase):
else:
assert_almost_equal(result, 9, err_msg=msg)
+
+class TestDivmod(TestCase):
+ def test_divmod_basic(self):
+ dt = np.typecodes['AllInteger'] + np.typecodes['Float']
+ for dt1, dt2 in itertools.product(dt, dt):
+ for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
+ if sg1 == -1 and dt1 in np.typecodes['UnsignedInteger']:
+ continue
+ if sg2 == -1 and dt2 in np.typecodes['UnsignedInteger']:
+ continue
+ fmt = 'dt1: %s, dt2: %s, sg1: %s, sg2: %s'
+ msg = fmt % (dt1, dt2, sg1, sg2)
+ a = np.array(sg1*71, dtype=dt1)[()]
+ b = np.array(sg2*19, dtype=dt2)[()]
+ div, rem = divmod(a, b)
+ assert_allclose(div*b + rem, a, err_msg=msg)
+ if sg2 == -1:
+ assert_(b < rem <= 0, msg)
+ else:
+ assert_(b > rem >= 0, msg)
+
+ def test_divmod_roundoff(self):
+ # gh-6127
+ dt = 'fdg'
+ for dt1, dt2 in itertools.product(dt, dt):
+ for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
+ fmt = 'dt1: %s, dt2: %s, sg1: %s, sg2: %s'
+ msg = fmt % (dt1, dt2, sg1, sg2)
+ a = np.array(sg1*78*6e-8, dtype=dt1)[()]
+ b = np.array(sg2*6e-8, dtype=dt2)[()]
+ div, rem = divmod(a, b)
+ assert_allclose(div*b + rem, a, err_msg=msg)
+ if sg2 == -1:
+ assert_(b < rem <= 0, msg)
+ else:
+ assert_(b > rem >= 0, msg)
+
+
class TestComplexDivision(TestCase):
def test_zero_division(self):
with np.errstate(all="ignore"):
@@ -153,6 +192,59 @@ class TestComplexDivision(TestCase):
b = t(0.)
assert_(np.isnan(b/a))
+ def test_signed_zeros(self):
+ with np.errstate(all="ignore"):
+ for t in [np.complex64, np.complex128]:
+ # tupled (numerator, denominator, expected)
+ # for testing as expected == numerator/denominator
+ data = (
+ (( 0.0,-1.0), ( 0.0, 1.0), (-1.0,-0.0)),
+ (( 0.0,-1.0), ( 0.0,-1.0), ( 1.0,-0.0)),
+ (( 0.0,-1.0), (-0.0,-1.0), ( 1.0, 0.0)),
+ (( 0.0,-1.0), (-0.0, 1.0), (-1.0, 0.0)),
+ (( 0.0, 1.0), ( 0.0,-1.0), (-1.0, 0.0)),
+ (( 0.0,-1.0), ( 0.0,-1.0), ( 1.0,-0.0)),
+ ((-0.0,-1.0), ( 0.0,-1.0), ( 1.0,-0.0)),
+ ((-0.0, 1.0), ( 0.0,-1.0), (-1.0,-0.0))
+ )
+ for cases in data:
+ n = cases[0]
+ d = cases[1]
+ ex = cases[2]
+ result = t(complex(n[0], n[1])) / t(complex(d[0], d[1]))
+ # check real and imag parts separately to avoid comparison
+ # in array context, which does not account for signed zeros
+ assert_equal(result.real, ex[0])
+ assert_equal(result.imag, ex[1])
+
+ def test_branches(self):
+ with np.errstate(all="ignore"):
+ for t in [np.complex64, np.complex128]:
+ # tupled (numerator, denominator, expected)
+ # for testing as expected == numerator/denominator
+ data = list()
+
+ # trigger branch: real(fabs(denom)) > imag(fabs(denom))
+ # followed by else condition as neither are == 0
+ data.append((( 2.0, 1.0), ( 2.0, 1.0), (1.0, 0.0)))
+
+ # trigger branch: real(fabs(denom)) > imag(fabs(denom))
+ # followed by if condition as both are == 0
+ # is performed in test_zero_division(), so this is skipped
+
+ # trigger else if branch: real(fabs(denom)) < imag(fabs(denom))
+ data.append((( 1.0, 2.0), ( 1.0, 2.0), (1.0, 0.0)))
+
+ for cases in data:
+ n = cases[0]
+ d = cases[1]
+ ex = cases[2]
+ result = t(complex(n[0], n[1])) / t(complex(d[0], d[1]))
+ # check real and imag parts separately to avoid comparison
+ # in array context, which does not account for signed zeros
+ assert_equal(result.real, ex[0])
+ assert_equal(result.imag, ex[1])
+
class TestConversion(TestCase):
def test_int_from_long(self):
diff --git a/runtests.py b/runtests.py
index 957cbef10..52905a8fc 100755
--- a/runtests.py
+++ b/runtests.py
@@ -135,8 +135,13 @@ def main(argv):
if not args.no_build:
site_dir = build_project(args)
- sys.path.insert(0, site_dir)
- os.environ['PYTHONPATH'] = site_dir
+ for dirname in os.listdir(site_dir):
+ if dirname.startswith('numpy'):
+ # The .pth file isn't re-parsed, so need to put the numpy egg
+ # produced by easy-install on the path manually.
+ egg_dir = os.path.join(site_dir, dirname)
+ sys.path.insert(0, egg_dir)
+ os.environ['PYTHONPATH'] = egg_dir
extra_argv = args.args[:]
if extra_argv and extra_argv[0] == '--':
@@ -346,6 +351,14 @@ def build_project(args):
cmd += ["-j", str(args.parallel)]
cmd += ['install', '--prefix=' + dst_dir]
+ from distutils.sysconfig import get_python_lib
+ site_dir = get_python_lib(prefix=dst_dir, plat_specific=True)
+ # easy_install won't install to a path that Python by default cannot see
+ # and isn't on the PYTHONPATH. Plus, it has to exist.
+ if not os.path.exists(site_dir):
+ os.makedirs(site_dir)
+ env['PYTHONPATH'] = site_dir
+
log_filename = os.path.join(ROOT_DIR, 'build.log')
if args.show_build_log:
@@ -383,9 +396,6 @@ def build_project(args):
print("Build failed!")
sys.exit(1)
- from distutils.sysconfig import get_python_lib
- site_dir = get_python_lib(prefix=dst_dir, plat_specific=True)
-
return site_dir
@@ -421,8 +431,8 @@ def lcov_generate():
'--output-file', LCOV_OUTPUT_FILE])
print("Generating lcov HTML output...")
- ret = subprocess.call(['genhtml', '-q', LCOV_OUTPUT_FILE,
- '--output-directory', LCOV_HTML_DIR,
+ ret = subprocess.call(['genhtml', '-q', LCOV_OUTPUT_FILE,
+ '--output-directory', LCOV_HTML_DIR,
'--legend', '--highlight'])
if ret != 0:
print("genhtml failed!")