summaryrefslogtreecommitdiff
path: root/numpy/core
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2019-10-15 20:20:20 +0100
committerGitHub <noreply@github.com>2019-10-15 20:20:20 +0100
commit10a7a4a815105e16828fe83fb89778c3bbafe692 (patch)
tree2c73effc6bf4b8404e63564f78661caff034b255 /numpy/core
parentd0731e118a5c40d866702f1b5da2be4d4f52ded9 (diff)
parent83da5faca3a313c5d37226b86fa781956f8d162b (diff)
downloadnumpy-10a7a4a815105e16828fe83fb89778c3bbafe692.tar.gz
Merge branch 'master' into master
Diffstat (limited to 'numpy/core')
-rw-r--r--numpy/core/__init__.py56
-rw-r--r--numpy/core/_add_newdocs.py2825
-rw-r--r--numpy/core/_aliased_types.py0
-rw-r--r--numpy/core/_asarray.py324
-rw-r--r--numpy/core/_dtype.py89
-rw-r--r--numpy/core/_dtype_ctypes.py113
-rw-r--r--numpy/core/_exceptions.py200
-rw-r--r--numpy/core/_internal.py153
-rw-r--r--numpy/core/_methods.py123
-rw-r--r--numpy/core/_type_aliases.py30
-rw-r--r--numpy/core/_ufunc_config.py458
-rw-r--r--numpy/core/arrayprint.py310
-rw-r--r--numpy/core/code_generators/cversions.txt7
-rw-r--r--numpy/core/code_generators/genapi.py20
-rw-r--r--numpy/core/code_generators/generate_numpy_api.py5
-rw-r--r--numpy/core/code_generators/generate_umath.py292
-rw-r--r--numpy/core/code_generators/numpy_api.py2
-rw-r--r--numpy/core/code_generators/ufunc_docstrings.py357
-rw-r--r--numpy/core/defchararray.py226
-rw-r--r--numpy/core/einsumfunc.py123
-rw-r--r--numpy/core/fromnumeric.py668
-rw-r--r--numpy/core/function_base.py274
-rw-r--r--numpy/core/getlimits.py360
-rw-r--r--numpy/core/include/numpy/ndarrayobject.h14
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h43
-rw-r--r--numpy/core/include/numpy/npy_1_7_deprecated_api.h3
-rw-r--r--numpy/core/include/numpy/npy_3kcompat.h51
-rw-r--r--numpy/core/include/numpy/npy_common.h21
-rw-r--r--numpy/core/include/numpy/npy_math.h95
-rw-r--r--numpy/core/include/numpy/ufuncobject.h64
-rw-r--r--numpy/core/info.py87
-rw-r--r--numpy/core/machar.py4
-rw-r--r--numpy/core/memmap.py30
-rw-r--r--numpy/core/multiarray.py1600
-rw-r--r--numpy/core/numeric.py1183
-rw-r--r--numpy/core/numerictypes.py55
-rw-r--r--numpy/core/overrides.py230
-rw-r--r--numpy/core/records.py184
-rw-r--r--numpy/core/setup.py182
-rw-r--r--numpy/core/setup_common.py108
-rw-r--r--numpy/core/shape_base.py345
-rw-r--r--numpy/core/src/common/array_assign.c8
-rw-r--r--numpy/core/src/common/array_assign.h6
-rw-r--r--numpy/core/src/common/cblasfuncs.c7
-rw-r--r--numpy/core/src/common/get_attr_string.h1
-rw-r--r--numpy/core/src/common/lowlevel_strided_loops.h3
-rw-r--r--numpy/core/src/common/npy_ctypes.h50
-rw-r--r--numpy/core/src/common/npy_longdouble.c100
-rw-r--r--numpy/core/src/common/npy_longdouble.h10
-rw-r--r--numpy/core/src/common/npy_partition.h.src3
-rw-r--r--numpy/core/src/common/npy_sort.h204
-rw-r--r--numpy/core/src/common/npy_sort.h.src100
-rw-r--r--numpy/core/src/common/numpyos.c (renamed from numpy/core/src/multiarray/numpyos.c)28
-rw-r--r--numpy/core/src/common/numpyos.h (renamed from numpy/core/src/multiarray/numpyos.h)7
-rw-r--r--numpy/core/src/common/ufunc_override.c208
-rw-r--r--numpy/core/src/common/ufunc_override.h31
-rw-r--r--numpy/core/src/multiarray/_multiarray_tests.c.src132
-rw-r--r--numpy/core/src/multiarray/alloc.c18
-rw-r--r--numpy/core/src/multiarray/array_assign_array.c73
-rw-r--r--numpy/core/src/multiarray/array_assign_scalar.c17
-rw-r--r--numpy/core/src/multiarray/arrayfunction_override.c376
-rw-r--r--numpy/core/src/multiarray/arrayfunction_override.h16
-rw-r--r--numpy/core/src/multiarray/arrayobject.c72
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src475
-rw-r--r--numpy/core/src/multiarray/buffer.c33
-rw-r--r--numpy/core/src/multiarray/buffer.h2
-rw-r--r--numpy/core/src/multiarray/calculation.c21
-rw-r--r--numpy/core/src/multiarray/common.c95
-rw-r--r--numpy/core/src/multiarray/common.h4
-rw-r--r--numpy/core/src/multiarray/compiled_base.c617
-rw-r--r--numpy/core/src/multiarray/conversion_utils.c71
-rw-r--r--numpy/core/src/multiarray/convert.c45
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c295
-rw-r--r--numpy/core/src/multiarray/convert_datatype.h18
-rw-r--r--numpy/core/src/multiarray/ctors.c656
-rw-r--r--numpy/core/src/multiarray/ctors.h33
-rw-r--r--numpy/core/src/multiarray/datetime.c403
-rw-r--r--numpy/core/src/multiarray/datetime_busday.c12
-rw-r--r--numpy/core/src/multiarray/descriptor.c448
-rw-r--r--numpy/core/src/multiarray/descriptor.h16
-rw-r--r--numpy/core/src/multiarray/dragon4.c40
-rw-r--r--numpy/core/src/multiarray/dragon4.h38
-rw-r--r--numpy/core/src/multiarray/dtype_transfer.c90
-rw-r--r--numpy/core/src/multiarray/einsum.c.src188
-rw-r--r--numpy/core/src/multiarray/flagsobject.c88
-rw-r--r--numpy/core/src/multiarray/getset.c30
-rw-r--r--numpy/core/src/multiarray/hashdescr.c22
-rw-r--r--numpy/core/src/multiarray/item_selection.c270
-rw-r--r--numpy/core/src/multiarray/item_selection.h4
-rw-r--r--numpy/core/src/multiarray/iterators.c482
-rw-r--r--numpy/core/src/multiarray/iterators.h14
-rw-r--r--numpy/core/src/multiarray/lowlevel_strided_loops.c.src101
-rw-r--r--numpy/core/src/multiarray/mapping.c231
-rw-r--r--numpy/core/src/multiarray/methods.c397
-rw-r--r--numpy/core/src/multiarray/methods.h27
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c388
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.h2
-rw-r--r--numpy/core/src/multiarray/nditer_api.c7
-rw-r--r--numpy/core/src/multiarray/nditer_constr.c73
-rw-r--r--numpy/core/src/multiarray/nditer_pywrap.c12
-rw-r--r--numpy/core/src/multiarray/number.c63
-rw-r--r--numpy/core/src/multiarray/number.h6
-rw-r--r--numpy/core/src/multiarray/refcount.c118
-rw-r--r--numpy/core/src/multiarray/scalarapi.c17
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src157
-rw-r--r--numpy/core/src/multiarray/shape.c33
-rw-r--r--numpy/core/src/multiarray/temp_elide.c2
-rw-r--r--numpy/core/src/multiarray/typeinfo.c49
-rw-r--r--numpy/core/src/multiarray/typeinfo.h12
-rw-r--r--numpy/core/src/multiarray/usertypes.c33
-rw-r--r--numpy/core/src/npymath/halffloat.c31
-rw-r--r--numpy/core/src/npymath/ieee754.c.src152
-rw-r--r--numpy/core/src/npymath/npy_math_complex.c.src43
-rw-r--r--numpy/core/src/npymath/npy_math_internal.h.src45
-rw-r--r--numpy/core/src/npysort/npysort_common.h8
-rw-r--r--numpy/core/src/npysort/radixsort.c.src231
-rw-r--r--numpy/core/src/npysort/selection.c.src2
-rw-r--r--numpy/core/src/npysort/timsort.c.src2574
-rw-r--r--numpy/core/src/umath/_rational_tests.c.src4
-rw-r--r--numpy/core/src/umath/_struct_ufunc_tests.c.src62
-rw-r--r--numpy/core/src/umath/_umath_tests.c.src139
-rw-r--r--numpy/core/src/umath/clip.c.src119
-rw-r--r--numpy/core/src/umath/clip.h.src18
-rw-r--r--numpy/core/src/umath/cpuid.c45
-rw-r--r--numpy/core/src/umath/fast_loop_macros.h234
-rw-r--r--numpy/core/src/umath/funcs.inc.src53
-rw-r--r--numpy/core/src/umath/loops.c.src631
-rw-r--r--numpy/core/src/umath/loops.h.src78
-rw-r--r--numpy/core/src/umath/matmul.c.src504
-rw-r--r--numpy/core/src/umath/matmul.h.src12
-rw-r--r--numpy/core/src/umath/override.c100
-rw-r--r--numpy/core/src/umath/reduction.c50
-rw-r--r--numpy/core/src/umath/scalarmath.c.src137
-rw-r--r--numpy/core/src/umath/simd.inc.src1455
-rw-r--r--numpy/core/src/umath/ufunc_object.c1071
-rw-r--r--numpy/core/src/umath/ufunc_type_resolution.c705
-rw-r--r--numpy/core/src/umath/ufunc_type_resolution.h31
-rw-r--r--numpy/core/src/umath/umathmodule.c24
-rw-r--r--numpy/core/tests/data/umath-validation-set-README15
-rw-r--r--numpy/core/tests/data/umath-validation-set-cos707
-rw-r--r--numpy/core/tests/data/umath-validation-set-exp135
-rw-r--r--numpy/core/tests/data/umath-validation-set-log118
-rw-r--r--numpy/core/tests/data/umath-validation-set-sin707
-rw-r--r--numpy/core/tests/test__exceptions.py42
-rw-r--r--numpy/core/tests/test_api.py12
-rw-r--r--numpy/core/tests/test_arrayprint.py13
-rw-r--r--numpy/core/tests/test_datetime.py282
-rw-r--r--numpy/core/tests/test_deprecations.py163
-rw-r--r--numpy/core/tests/test_dtype.py522
-rw-r--r--numpy/core/tests/test_einsum.py11
-rw-r--r--numpy/core/tests/test_errstate.py8
-rw-r--r--numpy/core/tests/test_extint128.py2
-rw-r--r--numpy/core/tests/test_function_base.py68
-rw-r--r--numpy/core/tests/test_getlimits.py15
-rw-r--r--numpy/core/tests/test_half.py99
-rw-r--r--numpy/core/tests/test_indexing.py22
-rw-r--r--numpy/core/tests/test_item_selection.py10
-rw-r--r--numpy/core/tests/test_longdouble.py64
-rw-r--r--numpy/core/tests/test_mem_overlap.py2
-rw-r--r--numpy/core/tests/test_memmap.py10
-rw-r--r--numpy/core/tests/test_multiarray.py1194
-rw-r--r--numpy/core/tests/test_nditer.py91
-rw-r--r--numpy/core/tests/test_numeric.py404
-rw-r--r--numpy/core/tests/test_numerictypes.py38
-rw-r--r--numpy/core/tests/test_overrides.py283
-rw-r--r--numpy/core/tests/test_records.py75
-rw-r--r--numpy/core/tests/test_regression.py155
-rw-r--r--numpy/core/tests/test_scalar_methods.py109
-rw-r--r--numpy/core/tests/test_scalarbuffer.py2
-rw-r--r--numpy/core/tests/test_scalarinherit.py5
-rw-r--r--numpy/core/tests/test_scalarmath.py38
-rw-r--r--numpy/core/tests/test_scalarprint.py4
-rw-r--r--numpy/core/tests/test_shape_base.py208
-rw-r--r--numpy/core/tests/test_ufunc.py620
-rw-r--r--numpy/core/tests/test_umath.py323
-rw-r--r--numpy/core/tests/test_umath_accuracy.py54
-rw-r--r--numpy/core/tests/test_umath_complex.py3
-rw-r--r--numpy/core/umath.py4
178 files changed, 24401 insertions, 9816 deletions
diff --git a/numpy/core/__init__.py b/numpy/core/__init__.py
index 80ce84f00..c3b3f0392 100644
--- a/numpy/core/__init__.py
+++ b/numpy/core/__init__.py
@@ -1,11 +1,19 @@
+"""
+Contains the core of NumPy: ndarray, ufuncs, dtypes, etc.
+
+Please note that this module is private. All functions and objects
+are available in the main ``numpy`` namespace - use that instead.
+
+"""
+
from __future__ import division, absolute_import, print_function
-from .info import __doc__
from numpy.version import version as __version__
+import os
+
# disables OpenBLAS affinity setting of the main thread that limits
# python threads or processes to one core
-import os
env_added = []
for envkey in ['OPENBLAS_MAIN_FREE', 'GOTOBLAS_MAIN_FREE']:
if envkey not in os.environ:
@@ -20,17 +28,12 @@ except ImportError as exc:
IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE!
-Importing the multiarray numpy extension module failed. Most
-likely you are trying to import a failed build of numpy.
-Here is how to proceed:
-- If you're working with a numpy git repository, try `git clean -xdf`
- (removes all files not under version control) and rebuild numpy.
-- If you are simply trying to use the numpy version that you have installed:
- your installation is broken - please reinstall numpy.
-- If you have already reinstalled and that did not fix the problem, then:
- 1. Check that you are using the Python you expect (you're using %s),
+Importing the numpy c-extensions failed.
+- Try uninstalling and reinstalling numpy.
+- If you have already done that, then:
+ 1. Check that you expected to use Python%d.%d from "%s",
and that you have no directories in your PATH or PYTHONPATH that can
- interfere with the Python and numpy versions you're trying to use.
+ interfere with the Python and numpy version "%s" you're trying to use.
2. If (1) looks fine, you can open a new issue at
https://github.com/numpy/numpy/issues. Please include details on:
- how you installed Python
@@ -39,11 +42,15 @@ Here is how to proceed:
- whether or not you have multiple versions of Python installed
- if you built from source, your compiler versions and ideally a build log
- Note: this error has many possible causes, so please don't comment on
- an existing issue about this - open a new one instead.
+- If you're working with a numpy git repository, try `git clean -xdf`
+ (removes all files not under version control) and rebuild numpy.
+
+Note: this error has many possible causes, so please don't comment on
+an existing issue about this - open a new one instead.
Original error was: %s
-""" % (sys.executable, exc)
+""" % (sys.version_info[0], sys.version_info[1], sys.executable,
+ __version__, exc)
raise ImportError(msg)
finally:
for envkey in env_added:
@@ -53,7 +60,19 @@ del env_added
del os
from . import umath
-from . import _internal # for freeze programs
+
+# Check that multiarray,umath are pure python modules wrapping
+# _multiarray_umath and not either of the old c-extension modules
+if not (hasattr(multiarray, '_multiarray_umath') and
+ hasattr(umath, '_multiarray_umath')):
+ import sys
+ path = sys.modules['numpy'].__path__
+ msg = ("Something is wrong with the numpy installation. "
+ "While importing we detected an older version of "
+ "numpy in {}. One method of fixing this is to repeatedly uninstall "
+ "numpy until none is found, then reinstall this version.")
+ raise ImportError(msg.format(path))
+
from . import numerictypes as nt
multiarray.set_typeDict(nt.sctypeDict)
from . import numeric
@@ -83,6 +102,11 @@ from .numeric import absolute as abs
# do this after everything else, to minimize the chance of this misleadingly
# appearing in an import-time traceback
from . import _add_newdocs
+# add these for module-freeze analysis (like PyInstaller)
+from . import _dtype_ctypes
+from . import _internal
+from . import _dtype
+from . import _methods
__all__ = ['char', 'rec', 'memmap']
__all__ += numeric.__all__
diff --git a/numpy/core/_add_newdocs.py b/numpy/core/_add_newdocs.py
index 1c82cfde4..dbe3d226f 100644
--- a/numpy/core/_add_newdocs.py
+++ b/numpy/core/_add_newdocs.py
@@ -10,6 +10,8 @@ NOTE: Many of the methods of ndarray have corresponding functions.
"""
from __future__ import division, absolute_import, print_function
+import sys
+
from numpy.core import numerictypes as _numerictypes
from numpy.core import dtype
from numpy.core.function_base import add_newdoc
@@ -49,7 +51,7 @@ add_newdoc('numpy.core', 'flatiter',
>>> x = np.arange(6).reshape(2, 3)
>>> fl = x.flat
>>> type(fl)
- <type 'numpy.flatiter'>
+ <class 'numpy.flatiter'>
>>> for item in fl:
... print(item)
...
@@ -92,7 +94,7 @@ add_newdoc('numpy.core', 'flatiter', ('coords',
>>> fl = x.flat
>>> fl.coords
(0, 0)
- >>> fl.next()
+ >>> next(fl)
0
>>> fl.coords
(0, 1)
@@ -111,7 +113,7 @@ add_newdoc('numpy.core', 'flatiter', ('index',
>>> fl = x.flat
>>> fl.index
0
- >>> fl.next()
+ >>> next(fl)
0
>>> fl.index
1
@@ -161,62 +163,63 @@ add_newdoc('numpy.core', 'nditer',
----------
op : ndarray or sequence of array_like
The array(s) to iterate over.
+
flags : sequence of str, optional
- Flags to control the behavior of the iterator.
+ Flags to control the behavior of the iterator.
- * "buffered" enables buffering when required.
- * "c_index" causes a C-order index to be tracked.
- * "f_index" causes a Fortran-order index to be tracked.
- * "multi_index" causes a multi-index, or a tuple of indices
+ * ``buffered`` enables buffering when required.
+ * ``c_index`` causes a C-order index to be tracked.
+ * ``f_index`` causes a Fortran-order index to be tracked.
+ * ``multi_index`` causes a multi-index, or a tuple of indices
with one per iteration dimension, to be tracked.
- * "common_dtype" causes all the operands to be converted to
+ * ``common_dtype`` causes all the operands to be converted to
a common data type, with copying or buffering as necessary.
- * "copy_if_overlap" causes the iterator to determine if read
+ * ``copy_if_overlap`` causes the iterator to determine if read
operands have overlap with write operands, and make temporary
copies as necessary to avoid overlap. False positives (needless
copying) are possible in some cases.
- * "delay_bufalloc" delays allocation of the buffers until
- a reset() call is made. Allows "allocate" operands to
+ * ``delay_bufalloc`` delays allocation of the buffers until
+ a reset() call is made. Allows ``allocate`` operands to
be initialized before their values are copied into the buffers.
- * "external_loop" causes the `values` given to be
+ * ``external_loop`` causes the ``values`` given to be
one-dimensional arrays with multiple values instead of
zero-dimensional arrays.
- * "grow_inner" allows the `value` array sizes to be made
- larger than the buffer size when both "buffered" and
- "external_loop" is used.
- * "ranged" allows the iterator to be restricted to a sub-range
+ * ``grow_inner`` allows the ``value`` array sizes to be made
+ larger than the buffer size when both ``buffered`` and
+ ``external_loop`` is used.
+ * ``ranged`` allows the iterator to be restricted to a sub-range
of the iterindex values.
- * "refs_ok" enables iteration of reference types, such as
+ * ``refs_ok`` enables iteration of reference types, such as
object arrays.
- * "reduce_ok" enables iteration of "readwrite" operands
+ * ``reduce_ok`` enables iteration of ``readwrite`` operands
which are broadcasted, also known as reduction operands.
- * "zerosize_ok" allows `itersize` to be zero.
+ * ``zerosize_ok`` allows `itersize` to be zero.
op_flags : list of list of str, optional
- This is a list of flags for each operand. At minimum, one of
- "readonly", "readwrite", or "writeonly" must be specified.
-
- * "readonly" indicates the operand will only be read from.
- * "readwrite" indicates the operand will be read from and written to.
- * "writeonly" indicates the operand will only be written to.
- * "no_broadcast" prevents the operand from being broadcasted.
- * "contig" forces the operand data to be contiguous.
- * "aligned" forces the operand data to be aligned.
- * "nbo" forces the operand data to be in native byte order.
- * "copy" allows a temporary read-only copy if required.
- * "updateifcopy" allows a temporary read-write copy if required.
- * "allocate" causes the array to be allocated if it is None
- in the `op` parameter.
- * "no_subtype" prevents an "allocate" operand from using a subtype.
- * "arraymask" indicates that this operand is the mask to use
+ This is a list of flags for each operand. At minimum, one of
+ ``readonly``, ``readwrite``, or ``writeonly`` must be specified.
+
+ * ``readonly`` indicates the operand will only be read from.
+ * ``readwrite`` indicates the operand will be read from and written to.
+ * ``writeonly`` indicates the operand will only be written to.
+ * ``no_broadcast`` prevents the operand from being broadcasted.
+ * ``contig`` forces the operand data to be contiguous.
+ * ``aligned`` forces the operand data to be aligned.
+ * ``nbo`` forces the operand data to be in native byte order.
+ * ``copy`` allows a temporary read-only copy if required.
+ * ``updateifcopy`` allows a temporary read-write copy if required.
+ * ``allocate`` causes the array to be allocated if it is None
+ in the ``op`` parameter.
+ * ``no_subtype`` prevents an ``allocate`` operand from using a subtype.
+ * ``arraymask`` indicates that this operand is the mask to use
for selecting elements when writing to operands with the
'writemasked' flag set. The iterator does not enforce this,
but when writing from a buffer back to the array, it only
copies those elements indicated by this mask.
- * 'writemasked' indicates that only elements where the chosen
- 'arraymask' operand is True will be written to.
- * "overlap_assume_elementwise" can be used to mark operands that are
+ * ``writemasked`` indicates that only elements where the chosen
+ ``arraymask`` operand is True will be written to.
+ * ``overlap_assume_elementwise`` can be used to mark operands that are
accessed only in the iterator order, to allow less conservative
- copying when "copy_if_overlap" is present.
+ copying when ``copy_if_overlap`` is present.
op_dtypes : dtype or tuple of dtype(s), optional
The required data type(s) of the operands. If copying or buffering
is enabled, the data will be converted to/from their original types.
@@ -225,7 +228,7 @@ add_newdoc('numpy.core', 'nditer',
Fortran order, 'A' means 'F' order if all the arrays are Fortran
contiguous, 'C' order otherwise, and 'K' means as close to the
order the array elements appear in memory as possible. This also
- affects the element memory order of "allocate" operands, as they
+ affects the element memory order of ``allocate`` operands, as they
are allocated to be compatible with iteration order.
Default is 'K'.
casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
@@ -233,20 +236,20 @@ add_newdoc('numpy.core', 'nditer',
or buffering. Setting this to 'unsafe' is not recommended,
as it can adversely affect accumulations.
- * 'no' means the data types should not be cast at all.
- * 'equiv' means only byte-order changes are allowed.
- * 'safe' means only casts which can preserve values are allowed.
- * 'same_kind' means only safe casts or casts within a kind,
- like float64 to float32, are allowed.
- * 'unsafe' means any data conversions may be done.
+ * 'no' means the data types should not be cast at all.
+ * 'equiv' means only byte-order changes are allowed.
+ * 'safe' means only casts which can preserve values are allowed.
+ * 'same_kind' means only safe casts or casts within a kind,
+ like float64 to float32, are allowed.
+ * 'unsafe' means any data conversions may be done.
op_axes : list of list of ints, optional
If provided, is a list of ints or None for each operands.
The list of axes for an operand is a mapping from the dimensions
of the iterator to the dimensions of the operand. A value of
-1 can be placed for entries, causing that dimension to be
- treated as "newaxis".
+ treated as `newaxis`.
itershape : tuple of ints, optional
- The desired shape of the iterator. This allows "allocate" operands
+ The desired shape of the iterator. This allows ``allocate`` operands
with a dimension mapped by op_axes not corresponding to a dimension
of a different operand to get a value not equal to 1 for that
dimension.
@@ -263,19 +266,19 @@ add_newdoc('numpy.core', 'nditer',
finished : bool
Whether the iteration over the operands is finished or not.
has_delayed_bufalloc : bool
- If True, the iterator was created with the "delay_bufalloc" flag,
+ If True, the iterator was created with the ``delay_bufalloc`` flag,
and no reset() function was called on it yet.
has_index : bool
- If True, the iterator was created with either the "c_index" or
- the "f_index" flag, and the property `index` can be used to
+ If True, the iterator was created with either the ``c_index`` or
+ the ``f_index`` flag, and the property `index` can be used to
retrieve it.
has_multi_index : bool
- If True, the iterator was created with the "multi_index" flag,
+ If True, the iterator was created with the ``multi_index`` flag,
and the property `multi_index` can be used to retrieve it.
index
- When the "c_index" or "f_index" flag was used, this property
+ When the ``c_index`` or ``f_index`` flag was used, this property
provides access to the index. Raises a ValueError if accessed
- and `has_index` is False.
+ and ``has_index`` is False.
iterationneedsapi : bool
Whether iteration requires access to the Python API, for example
if one of the operands is an object array.
@@ -288,11 +291,11 @@ add_newdoc('numpy.core', 'nditer',
and optimized iterator access pattern. Valid only before the iterator
is closed.
multi_index
- When the "multi_index" flag was used, this property
+ When the ``multi_index`` flag was used, this property
provides access to the index. Raises a ValueError if accessed
- accessed and `has_multi_index` is False.
+ accessed and ``has_multi_index`` is False.
ndim : int
- The iterator's dimension.
+ The dimensions of the iterator.
nop : int
The number of iterator operands.
operands : tuple of operand(s)
@@ -301,8 +304,8 @@ add_newdoc('numpy.core', 'nditer',
shape : tuple of ints
Shape tuple, the shape of the iterator.
value
- Value of `operands` at current iteration. Normally, this is a
- tuple of array scalars, but if the flag "external_loop" is used,
+ Value of ``operands`` at current iteration. Normally, this is a
+ tuple of array scalars, but if the flag ``external_loop`` is used,
it is a tuple of one dimensional arrays.
Notes
@@ -313,96 +316,93 @@ add_newdoc('numpy.core', 'nditer',
The Python exposure supplies two iteration interfaces, one which follows
the Python iterator protocol, and another which mirrors the C-style
do-while pattern. The native Python approach is better in most cases, but
- if you need the iterator's coordinates or index, use the C-style pattern.
+ if you need the coordinates or index of an iterator, use the C-style pattern.
Examples
--------
Here is how we might write an ``iter_add`` function, using the
- Python iterator protocol::
-
- def iter_add_py(x, y, out=None):
- addop = np.add
- it = np.nditer([x, y, out], [],
- [['readonly'], ['readonly'], ['writeonly','allocate']])
- with it:
- for (a, b, c) in it:
- addop(a, b, out=c)
- return it.operands[2]
-
- Here is the same function, but following the C-style pattern::
-
- def iter_add(x, y, out=None):
- addop = np.add
-
- it = np.nditer([x, y, out], [],
- [['readonly'], ['readonly'], ['writeonly','allocate']])
- with it:
- while not it.finished:
- addop(it[0], it[1], out=it[2])
- it.iternext()
-
- return it.operands[2]
-
- Here is an example outer product function::
-
- def outer_it(x, y, out=None):
- mulop = np.multiply
-
- it = np.nditer([x, y, out], ['external_loop'],
- [['readonly'], ['readonly'], ['writeonly', 'allocate']],
- op_axes=[list(range(x.ndim)) + [-1] * y.ndim,
- [-1] * x.ndim + list(range(y.ndim)),
- None])
- with it:
- for (a, b, c) in it:
- mulop(a, b, out=c)
- return it.operands[2]
-
- >>> a = np.arange(2)+1
- >>> b = np.arange(3)+1
- >>> outer_it(a,b)
- array([[1, 2, 3],
- [2, 4, 6]])
-
- Here is an example function which operates like a "lambda" ufunc::
-
- def luf(lamdaexpr, *args, **kwargs):
- "luf(lambdaexpr, op1, ..., opn, out=None, order='K', casting='safe', buffersize=0)"
- nargs = len(args)
- op = (kwargs.get('out',None),) + args
- it = np.nditer(op, ['buffered','external_loop'],
- [['writeonly','allocate','no_broadcast']] +
- [['readonly','nbo','aligned']]*nargs,
- order=kwargs.get('order','K'),
- casting=kwargs.get('casting','safe'),
- buffersize=kwargs.get('buffersize',0))
- while not it.finished:
- it[0] = lamdaexpr(*it[1:])
- it.iternext()
- return it.operands[0]
-
- >>> a = np.arange(5)
- >>> b = np.ones(5)
- >>> luf(lambda i,j:i*i + j/2, a, b)
- array([ 0.5, 1.5, 4.5, 9.5, 16.5])
-
- If operand flags `"writeonly"` or `"readwrite"` are used the operands may
- be views into the original data with the `WRITEBACKIFCOPY` flag. In this case
- nditer must be used as a context manager or the nditer.close
- method must be called before using the result. The temporary
- data will be written back to the original data when the `__exit__`
- function is called but not before:
-
- >>> a = np.arange(6, dtype='i4')[::-2]
- >>> with nditer(a, [],
- ... [['writeonly', 'updateifcopy']],
- ... casting='unsafe',
- ... op_dtypes=[np.dtype('f4')]) as i:
- ... x = i.operands[0]
- ... x[:] = [-1, -2, -3]
- ... # a still unchanged here
- >>> a, x
- array([-1, -2, -3]), array([-1, -2, -3])
+ Python iterator protocol:
+
+ >>> def iter_add_py(x, y, out=None):
+ ... addop = np.add
+ ... it = np.nditer([x, y, out], [],
+ ... [['readonly'], ['readonly'], ['writeonly','allocate']])
+ ... with it:
+ ... for (a, b, c) in it:
+ ... addop(a, b, out=c)
+ ... return it.operands[2]
+
+ Here is the same function, but following the C-style pattern:
+
+ >>> def iter_add(x, y, out=None):
+ ... addop = np.add
+ ... it = np.nditer([x, y, out], [],
+ ... [['readonly'], ['readonly'], ['writeonly','allocate']])
+ ... with it:
+ ... while not it.finished:
+ ... addop(it[0], it[1], out=it[2])
+ ... it.iternext()
+ ... return it.operands[2]
+
+ Here is an example outer product function:
+
+ >>> def outer_it(x, y, out=None):
+ ... mulop = np.multiply
+ ... it = np.nditer([x, y, out], ['external_loop'],
+ ... [['readonly'], ['readonly'], ['writeonly', 'allocate']],
+ ... op_axes=[list(range(x.ndim)) + [-1] * y.ndim,
+ ... [-1] * x.ndim + list(range(y.ndim)),
+ ... None])
+ ... with it:
+ ... for (a, b, c) in it:
+ ... mulop(a, b, out=c)
+ ... return it.operands[2]
+
+ >>> a = np.arange(2)+1
+ >>> b = np.arange(3)+1
+ >>> outer_it(a,b)
+ array([[1, 2, 3],
+ [2, 4, 6]])
+
+ Here is an example function which operates like a "lambda" ufunc:
+
+ >>> def luf(lamdaexpr, *args, **kwargs):
+ ... '''luf(lambdaexpr, op1, ..., opn, out=None, order='K', casting='safe', buffersize=0)'''
+ ... nargs = len(args)
+ ... op = (kwargs.get('out',None),) + args
+ ... it = np.nditer(op, ['buffered','external_loop'],
+ ... [['writeonly','allocate','no_broadcast']] +
+ ... [['readonly','nbo','aligned']]*nargs,
+ ... order=kwargs.get('order','K'),
+ ... casting=kwargs.get('casting','safe'),
+ ... buffersize=kwargs.get('buffersize',0))
+ ... while not it.finished:
+ ... it[0] = lamdaexpr(*it[1:])
+ ... it.iternext()
+ ... return it.operands[0]
+
+ >>> a = np.arange(5)
+ >>> b = np.ones(5)
+ >>> luf(lambda i,j:i*i + j/2, a, b)
+ array([ 0.5, 1.5, 4.5, 9.5, 16.5])
+
+ If operand flags `"writeonly"` or `"readwrite"` are used the
+ operands may be views into the original data with the
+ `WRITEBACKIFCOPY` flag. In this case `nditer` must be used as a
+ context manager or the `nditer.close` method must be called before
+ using the result. The temporary data will be written back to the
+ original data when the `__exit__` function is called but not before:
+
+ >>> a = np.arange(6, dtype='i4')[::-2]
+ >>> with np.nditer(a, [],
+ ... [['writeonly', 'updateifcopy']],
+ ... casting='unsafe',
+ ... op_dtypes=[np.dtype('f4')]) as i:
+ ... x = i.operands[0]
+ ... x[:] = [-1, -2, -3]
+ ... # a still unchanged here
+ >>> a, x
+ (array([-1, -2, -3], dtype=int32), array([-1., -2., -3.], dtype=float32))
It is important to note that once the iterator is exited, dangling
references (like `x` in the example) may or may not share data with
@@ -413,6 +413,8 @@ add_newdoc('numpy.core', 'nditer',
`x.data` will still point at some part of `a.data`, and writing to
one will affect the other.
+ Context management and the `close` method appeared in version 1.15.0.
+
""")
# nditer methods
@@ -428,10 +430,10 @@ add_newdoc('numpy.core', 'nditer', ('copy',
>>> x = np.arange(10)
>>> y = x + 1
>>> it = np.nditer([x, y])
- >>> it.next()
+ >>> next(it)
(array(0), array(1))
>>> it2 = it.copy()
- >>> it2.next()
+ >>> next(it2)
(array(1), array(2))
"""))
@@ -544,7 +546,6 @@ add_newdoc('numpy.core', 'nested_iters',
... print(i.multi_index)
... for y in j:
... print('', j.multi_index, y)
-
(0,)
(0, 0) 0
(0, 1) 1
@@ -569,6 +570,8 @@ add_newdoc('numpy.core', 'nditer', ('close',
Resolve all writeback semantics in writeable operands.
+ .. versionadded:: 1.15.0
+
See Also
--------
@@ -617,9 +620,9 @@ add_newdoc('numpy.core', 'broadcast',
>>> out = np.empty(b.shape)
>>> out.flat = [u+v for (u,v) in b]
>>> out
- array([[ 5., 6., 7.],
- [ 6., 7., 8.],
- [ 7., 8., 9.]])
+ array([[5., 6., 7.],
+ [6., 7., 8.],
+ [7., 8., 9.]])
Compare against built-in broadcasting:
@@ -643,7 +646,7 @@ add_newdoc('numpy.core', 'broadcast', ('index',
>>> b = np.broadcast(x, y)
>>> b.index
0
- >>> b.next(), b.next(), b.next()
+ >>> next(b), next(b), next(b)
((1, 4), (1, 5), (1, 6))
>>> b.index
3
@@ -667,7 +670,7 @@ add_newdoc('numpy.core', 'broadcast', ('iters',
>>> y = np.array([[4], [5], [6]])
>>> b = np.broadcast(x, y)
>>> row, col = b.iters
- >>> row.next(), col.next()
+ >>> next(row), next(col)
(1, 4)
"""))
@@ -762,11 +765,11 @@ add_newdoc('numpy.core', 'broadcast', ('reset',
Examples
--------
>>> x = np.array([1, 2, 3])
- >>> y = np.array([[4], [5], [6]]
+ >>> y = np.array([[4], [5], [6]])
>>> b = np.broadcast(x, y)
>>> b.index
0
- >>> b.next(), b.next(), b.next()
+ >>> next(b), next(b), next(b)
((1, 4), (2, 4), (3, 4))
>>> b.index
3
@@ -796,8 +799,7 @@ add_newdoc('numpy.core.multiarray', 'array',
dtype : data-type, optional
The desired data-type for the array. If not given, then the type will
be determined as the minimum type required to hold the objects in the
- sequence. This argument can only be used to 'upcast' the array. For
- downcasting, use the .astype(t) method.
+ sequence.
copy : bool, optional
If true (default), then the object is copied. Otherwise, a copy will
only be made if __array__ returns a copy, if obj is a nested sequence,
@@ -939,74 +941,14 @@ add_newdoc('numpy.core.multiarray', 'empty',
--------
>>> np.empty([2, 2])
array([[ -9.74499359e+001, 6.69583040e-309],
- [ 2.13182611e-314, 3.06959433e-309]]) #random
+ [ 2.13182611e-314, 3.06959433e-309]]) #uninitialized
>>> np.empty([2, 2], dtype=int)
array([[-1073741821, -1067949133],
- [ 496041986, 19249760]]) #random
-
- """)
-
-add_newdoc('numpy.core.multiarray', 'empty_like',
- """
- empty_like(prototype, dtype=None, order='K', subok=True)
-
- Return a new array with the same shape and type as a given array.
-
- Parameters
- ----------
- prototype : array_like
- The shape and data-type of `prototype` define these same attributes
- of the returned array.
- dtype : data-type, optional
- Overrides the data type of the result.
-
- .. versionadded:: 1.6.0
- order : {'C', 'F', 'A', or 'K'}, optional
- Overrides the memory layout of the result. 'C' means C-order,
- 'F' means F-order, 'A' means 'F' if ``prototype`` is Fortran
- contiguous, 'C' otherwise. 'K' means match the layout of ``prototype``
- as closely as possible.
-
- .. versionadded:: 1.6.0
- subok : bool, optional.
- If True, then the newly created array will use the sub-class
- type of 'a', otherwise it will be a base-class array. Defaults
- to True.
-
- Returns
- -------
- out : ndarray
- Array of uninitialized (arbitrary) data with the same
- shape and type as `prototype`.
-
- See Also
- --------
- ones_like : Return an array of ones with shape and type of input.
- zeros_like : Return an array of zeros with shape and type of input.
- full_like : Return a new array with shape of input filled with value.
- empty : Return a new uninitialized array.
-
- Notes
- -----
- This function does *not* initialize the returned array; to do that use
- `zeros_like` or `ones_like` instead. It may be marginally faster than
- the functions that do set the array values.
-
- Examples
- --------
- >>> a = ([1,2,3], [4,5,6]) # a is array-like
- >>> np.empty_like(a)
- array([[-1073741821, -1073741821, 3], #random
- [ 0, 0, -1073741821]])
- >>> a = np.array([[1., 2., 3.],[4.,5.,6.]])
- >>> np.empty_like(a)
- array([[ -2.00000715e+000, 1.48219694e-323, -2.00000572e+000],#random
- [ 4.38791518e-305, -2.00000715e+000, 4.17269252e-309]])
+ [ 496041986, 19249760]]) #uninitialized
""")
-
add_newdoc('numpy.core.multiarray', 'scalar',
"""
scalar(dtype, obj)
@@ -1104,9 +1046,14 @@ add_newdoc('numpy.core.multiarray', 'fromstring',
elements is also ignored.
.. deprecated:: 1.14
- If this argument is not provided, `fromstring` falls back on the
- behaviour of `frombuffer` after encoding unicode string inputs as
- either utf-8 (python 3), or the default encoding (python 2).
+ Passing ``sep=''``, the default, is deprecated since it will
+ trigger the deprecated binary mode of this function. This mode
+ interprets `string` as binary bytes, rather than ASCII text with
+ decimal numbers, an operation which is better spelt
+ ``frombuffer(string, dtype, count)``. If `string` contains unicode
+ text, the binary mode of `fromstring` will first encode it into
+ bytes using either utf-8 (python 3) or the default encoding
+ (python 2), neither of which produce sane results.
Returns
-------
@@ -1132,6 +1079,43 @@ add_newdoc('numpy.core.multiarray', 'fromstring',
""")
+add_newdoc('numpy.core.multiarray', 'compare_chararrays',
+ """
+ compare_chararrays(a, b, cmp_op, rstrip)
+
+ Performs element-wise comparison of two string arrays using the
+ comparison operator specified by `cmp_op`.
+
+ Parameters
+ ----------
+ a, b : array_like
+ Arrays to be compared.
+ cmp_op : {"<", "<=", "==", ">=", ">", "!="}
+ Type of comparison.
+ rstrip : Boolean
+ If True, the spaces at the end of Strings are removed before the comparison.
+
+ Returns
+ -------
+ out : ndarray
+ The output array of type Boolean with the same shape as a and b.
+
+ Raises
+ ------
+ ValueError
+ If `cmp_op` is not valid.
+ TypeError
+ If at least one of `a` or `b` is a non-string array
+
+ Examples
+ --------
+ >>> a = np.array(["a", "b", "cde"])
+ >>> b = np.array(["a", "a", "dec"])
+ >>> np.compare_chararrays(a, b, ">", True)
+ array([False, True, False])
+
+ """)
+
add_newdoc('numpy.core.multiarray', 'fromiter',
"""
fromiter(iterable, dtype, count=-1)
@@ -1168,7 +1152,7 @@ add_newdoc('numpy.core.multiarray', 'fromiter',
add_newdoc('numpy.core.multiarray', 'fromfile',
"""
- fromfile(file, dtype=float, count=-1, sep='')
+ fromfile(file, dtype=float, count=-1, sep='', offset=0)
Construct an array from data in a text or binary file.
@@ -1178,8 +1162,12 @@ add_newdoc('numpy.core.multiarray', 'fromfile',
Parameters
----------
- file : file or str
+ file : file or str or Path
Open file object or filename.
+
+ .. versionchanged:: 1.17.0
+ `pathlib.Path` objects are now accepted.
+
dtype : data-type
Data type of the returned array.
For binary files, it is used to determine the size and byte-order
@@ -1193,6 +1181,11 @@ add_newdoc('numpy.core.multiarray', 'fromfile',
Spaces (" ") in the separator match zero or more whitespace characters.
A separator consisting only of spaces must match at least one
whitespace.
+ offset : int
+ The offset (in bytes) from the file's current position. Defaults to 0.
+ Only permitted for binary files.
+
+ .. versionadded:: 1.17.0
See also
--------
@@ -1212,32 +1205,32 @@ add_newdoc('numpy.core.multiarray', 'fromfile',
--------
Construct an ndarray:
- >>> dt = np.dtype([('time', [('min', int), ('sec', int)]),
+ >>> dt = np.dtype([('time', [('min', np.int64), ('sec', np.int64)]),
... ('temp', float)])
>>> x = np.zeros((1,), dtype=dt)
>>> x['time']['min'] = 10; x['temp'] = 98.25
>>> x
array([((10, 0), 98.25)],
- dtype=[('time', [('min', '<i4'), ('sec', '<i4')]), ('temp', '<f8')])
+ dtype=[('time', [('min', '<i8'), ('sec', '<i8')]), ('temp', '<f8')])
Save the raw data to disk:
- >>> import os
- >>> fname = os.tmpnam()
+ >>> import tempfile
+ >>> fname = tempfile.mkstemp()[1]
>>> x.tofile(fname)
Read the raw data from disk:
>>> np.fromfile(fname, dtype=dt)
array([((10, 0), 98.25)],
- dtype=[('time', [('min', '<i4'), ('sec', '<i4')]), ('temp', '<f8')])
+ dtype=[('time', [('min', '<i8'), ('sec', '<i8')]), ('temp', '<f8')])
The recommended way to store and load data:
>>> np.save(fname, x)
>>> np.load(fname + '.npy')
array([((10, 0), 98.25)],
- dtype=[('time', [('min', '<i4'), ('sec', '<i4')]), ('temp', '<f8')])
+ dtype=[('time', [('min', '<i8'), ('sec', '<i8')]), ('temp', '<f8')])
""")
@@ -1265,17 +1258,16 @@ add_newdoc('numpy.core.multiarray', 'frombuffer',
>>> dt = np.dtype(int)
>>> dt = dt.newbyteorder('>')
- >>> np.frombuffer(buf, dtype=dt)
+ >>> np.frombuffer(buf, dtype=dt) # doctest: +SKIP
The data of the resulting array will not be byteswapped, but will be
interpreted correctly.
Examples
--------
- >>> s = 'hello world'
+ >>> s = b'hello world'
>>> np.frombuffer(s, dtype='S1', count=5, offset=6)
- array(['w', 'o', 'r', 'l', 'd'],
- dtype='|S1')
+ array([b'w', b'o', b'r', b'l', b'd'], dtype='|S1')
>>> np.frombuffer(b'\\x01\\x02', dtype=np.uint8)
array([1, 2], dtype=uint8)
@@ -1284,163 +1276,6 @@ add_newdoc('numpy.core.multiarray', 'frombuffer',
""")
-add_newdoc('numpy.core.multiarray', 'concatenate',
- """
- concatenate((a1, a2, ...), axis=0, out=None)
-
- Join a sequence of arrays along an existing axis.
-
- Parameters
- ----------
- a1, a2, ... : sequence of array_like
- The arrays must have the same shape, except in the dimension
- corresponding to `axis` (the first, by default).
- axis : int, optional
- The axis along which the arrays will be joined. If axis is None,
- arrays are flattened before use. Default is 0.
- out : ndarray, optional
- If provided, the destination to place the result. The shape must be
- correct, matching that of what concatenate would have returned if no
- out argument were specified.
-
- Returns
- -------
- res : ndarray
- The concatenated array.
-
- See Also
- --------
- ma.concatenate : Concatenate function that preserves input masks.
- array_split : Split an array into multiple sub-arrays of equal or
- near-equal size.
- split : Split array into a list of multiple sub-arrays of equal size.
- hsplit : Split array into multiple sub-arrays horizontally (column wise)
- vsplit : Split array into multiple sub-arrays vertically (row wise)
- dsplit : Split array into multiple sub-arrays along the 3rd axis (depth).
- stack : Stack a sequence of arrays along a new axis.
- hstack : Stack arrays in sequence horizontally (column wise)
- vstack : Stack arrays in sequence vertically (row wise)
- dstack : Stack arrays in sequence depth wise (along third dimension)
- block : Assemble arrays from blocks.
-
- Notes
- -----
- When one or more of the arrays to be concatenated is a MaskedArray,
- this function will return a MaskedArray object instead of an ndarray,
- but the input masks are *not* preserved. In cases where a MaskedArray
- is expected as input, use the ma.concatenate function from the masked
- array module instead.
-
- Examples
- --------
- >>> a = np.array([[1, 2], [3, 4]])
- >>> b = np.array([[5, 6]])
- >>> np.concatenate((a, b), axis=0)
- array([[1, 2],
- [3, 4],
- [5, 6]])
- >>> np.concatenate((a, b.T), axis=1)
- array([[1, 2, 5],
- [3, 4, 6]])
- >>> np.concatenate((a, b), axis=None)
- array([1, 2, 3, 4, 5, 6])
-
- This function will not preserve masking of MaskedArray inputs.
-
- >>> a = np.ma.arange(3)
- >>> a[1] = np.ma.masked
- >>> b = np.arange(2, 5)
- >>> a
- masked_array(data=[0, --, 2],
- mask=[False, True, False],
- fill_value=999999)
- >>> b
- array([2, 3, 4])
- >>> np.concatenate([a, b])
- masked_array(data=[0, 1, 2, 2, 3, 4],
- mask=False,
- fill_value=999999)
- >>> np.ma.concatenate([a, b])
- masked_array(data=[0, --, 2, 2, 3, 4],
- mask=[False, True, False, False, False, False],
- fill_value=999999)
-
- """)
-
-add_newdoc('numpy.core', 'inner',
- """
- inner(a, b)
-
- Inner product of two arrays.
-
- Ordinary inner product of vectors for 1-D arrays (without complex
- conjugation), in higher dimensions a sum product over the last axes.
-
- Parameters
- ----------
- a, b : array_like
- If `a` and `b` are nonscalar, their last dimensions must match.
-
- Returns
- -------
- out : ndarray
- `out.shape = a.shape[:-1] + b.shape[:-1]`
-
- Raises
- ------
- ValueError
- If the last dimension of `a` and `b` has different size.
-
- See Also
- --------
- tensordot : Sum products over arbitrary axes.
- dot : Generalised matrix product, using second last dimension of `b`.
- einsum : Einstein summation convention.
-
- Notes
- -----
- For vectors (1-D arrays) it computes the ordinary inner-product::
-
- np.inner(a, b) = sum(a[:]*b[:])
-
- More generally, if `ndim(a) = r > 0` and `ndim(b) = s > 0`::
-
- np.inner(a, b) = np.tensordot(a, b, axes=(-1,-1))
-
- or explicitly::
-
- np.inner(a, b)[i0,...,ir-1,j0,...,js-1]
- = sum(a[i0,...,ir-1,:]*b[j0,...,js-1,:])
-
- In addition `a` or `b` may be scalars, in which case::
-
- np.inner(a,b) = a*b
-
- Examples
- --------
- Ordinary inner product for vectors:
-
- >>> a = np.array([1,2,3])
- >>> b = np.array([0,1,0])
- >>> np.inner(a, b)
- 2
-
- A multidimensional example:
-
- >>> a = np.arange(24).reshape((2,3,4))
- >>> b = np.arange(4)
- >>> np.inner(a, b)
- array([[ 14, 38, 62],
- [ 86, 110, 134]])
-
- An example where `b` is a scalar:
-
- >>> np.inner(np.eye(2), 7)
- array([[ 7., 0.],
- [ 0., 7.]])
-
- """)
-
add_newdoc('numpy.core', 'fastCopyAndTranspose',
"""_fastCopyAndTranspose(a)""")
@@ -1511,7 +1346,7 @@ add_newdoc('numpy.core.multiarray', 'arange',
add_newdoc('numpy.core.multiarray', '_get_ndarray_c_version',
"""_get_ndarray_c_version()
- Return the compile time NDARRAY_VERSION number.
+ Return the compile time NPY_VERSION (formerly called NDARRAY_VERSION) number.
""")
@@ -1537,6 +1372,12 @@ add_newdoc('numpy.core.multiarray', 'set_numeric_ops',
Set numerical operators for array objects.
+ .. deprecated:: 1.16
+
+ For the general case, use :c:func:`PyUFunc_ReplaceLoopBySignature`.
+ For ndarray subclasses, define the ``__array_ufunc__`` method and
+ override the relevant ufunc.
+
Parameters
----------
op1, op2, ... : callable
@@ -1575,263 +1416,6 @@ add_newdoc('numpy.core.multiarray', 'set_numeric_ops',
""")
-add_newdoc('numpy.core.multiarray', 'where',
- """
- where(condition, [x, y])
-
- Return elements chosen from `x` or `y` depending on `condition`.
-
- .. note::
- When only `condition` is provided, this function is a shorthand for
- ``np.asarray(condition).nonzero()``. Using `nonzero` directly should be
- preferred, as it behaves correctly for subclasses. The rest of this
- documentation covers only the case where all three arguments are
- provided.
-
- Parameters
- ----------
- condition : array_like, bool
- Where True, yield `x`, otherwise yield `y`.
- x, y : array_like
- Values from which to choose. `x`, `y` and `condition` need to be
- broadcastable to some shape.
-
- Returns
- -------
- out : ndarray
- An array with elements from `x` where `condition` is True, and elements
- from `y` elsewhere.
-
- See Also
- --------
- choose
- nonzero : The function that is called when x and y are omitted
-
- Notes
- -----
- If all the arrays are 1-D, `where` is equivalent to::
-
- [xv if c else yv
- for c, xv, yv in zip(condition, x, y)]
-
- Examples
- --------
- >>> a = np.arange(10)
- >>> a
- array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
- >>> np.where(a < 5, a, 10*a)
- array([ 0, 1, 2, 3, 4, 50, 60, 70, 80, 90])
-
- This can be used on multidimensional arrays too:
-
- >>> np.where([[True, False], [True, True]],
- ... [[1, 2], [3, 4]],
- ... [[9, 8], [7, 6]])
- array([[1, 8],
- [3, 4]])
-
- The shapes of x, y, and the condition are broadcast together:
-
- >>> x, y = np.ogrid[:3, :4]
- >>> np.where(x < y, x, 10 + y) # both x and 10+y are broadcast
- array([[10, 0, 0, 0],
- [10, 11, 1, 1],
- [10, 11, 12, 2]])
-
- >>> a = np.array([[0, 1, 2],
- ... [0, 2, 4],
- ... [0, 3, 6]])
- >>> np.where(a < 4, a, -1) # -1 is broadcast
- array([[ 0, 1, 2],
- [ 0, 2, -1],
- [ 0, 3, -1]])
- """)
-
-
-add_newdoc('numpy.core.multiarray', 'lexsort',
- """
- lexsort(keys, axis=-1)
-
- Perform an indirect stable sort using a sequence of keys.
-
- Given multiple sorting keys, which can be interpreted as columns in a
- spreadsheet, lexsort returns an array of integer indices that describes
- the sort order by multiple columns. The last key in the sequence is used
- for the primary sort order, the second-to-last key for the secondary sort
- order, and so on. The keys argument must be a sequence of objects that
- can be converted to arrays of the same shape. If a 2D array is provided
- for the keys argument, it's rows are interpreted as the sorting keys and
- sorting is according to the last row, second last row etc.
-
- Parameters
- ----------
- keys : (k, N) array or tuple containing k (N,)-shaped sequences
- The `k` different "columns" to be sorted. The last column (or row if
- `keys` is a 2D array) is the primary sort key.
- axis : int, optional
- Axis to be indirectly sorted. By default, sort over the last axis.
-
- Returns
- -------
- indices : (N,) ndarray of ints
- Array of indices that sort the keys along the specified axis.
-
- See Also
- --------
- argsort : Indirect sort.
- ndarray.sort : In-place sort.
- sort : Return a sorted copy of an array.
-
- Examples
- --------
- Sort names: first by surname, then by name.
-
- >>> surnames = ('Hertz', 'Galilei', 'Hertz')
- >>> first_names = ('Heinrich', 'Galileo', 'Gustav')
- >>> ind = np.lexsort((first_names, surnames))
- >>> ind
- array([1, 2, 0])
-
- >>> [surnames[i] + ", " + first_names[i] for i in ind]
- ['Galilei, Galileo', 'Hertz, Gustav', 'Hertz, Heinrich']
-
- Sort two columns of numbers:
-
- >>> a = [1,5,1,4,3,4,4] # First column
- >>> b = [9,4,0,4,0,2,1] # Second column
- >>> ind = np.lexsort((b,a)) # Sort by a, then by b
- >>> print(ind)
- [2 0 4 6 5 3 1]
-
- >>> [(a[i],b[i]) for i in ind]
- [(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)]
-
- Note that sorting is first according to the elements of ``a``.
- Secondary sorting is according to the elements of ``b``.
-
- A normal ``argsort`` would have yielded:
-
- >>> [(a[i],b[i]) for i in np.argsort(a)]
- [(1, 9), (1, 0), (3, 0), (4, 4), (4, 2), (4, 1), (5, 4)]
-
- Structured arrays are sorted lexically by ``argsort``:
-
- >>> x = np.array([(1,9), (5,4), (1,0), (4,4), (3,0), (4,2), (4,1)],
- ... dtype=np.dtype([('x', int), ('y', int)]))
-
- >>> np.argsort(x) # or np.argsort(x, order=('x', 'y'))
- array([2, 0, 4, 6, 5, 3, 1])
-
- """)
-
-add_newdoc('numpy.core.multiarray', 'can_cast',
- """
- can_cast(from_, to, casting='safe')
-
- Returns True if cast between data types can occur according to the
- casting rule. If from is a scalar or array scalar, also returns
- True if the scalar value can be cast without overflow or truncation
- to an integer.
-
- Parameters
- ----------
- from_ : dtype, dtype specifier, scalar, or array
- Data type, scalar, or array to cast from.
- to : dtype or dtype specifier
- Data type to cast to.
- casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
- Controls what kind of data casting may occur.
-
- * 'no' means the data types should not be cast at all.
- * 'equiv' means only byte-order changes are allowed.
- * 'safe' means only casts which can preserve values are allowed.
- * 'same_kind' means only safe casts or casts within a kind,
- like float64 to float32, are allowed.
- * 'unsafe' means any data conversions may be done.
-
- Returns
- -------
- out : bool
- True if cast can occur according to the casting rule.
-
- Notes
- -----
- Starting in NumPy 1.9, can_cast function now returns False in 'safe'
- casting mode for integer/float dtype and string dtype if the string dtype
- length is not long enough to store the max integer/float value converted
- to a string. Previously can_cast in 'safe' mode returned True for
- integer/float dtype and a string dtype of any length.
-
- See also
- --------
- dtype, result_type
-
- Examples
- --------
- Basic examples
-
- >>> np.can_cast(np.int32, np.int64)
- True
- >>> np.can_cast(np.float64, complex)
- True
- >>> np.can_cast(complex, float)
- False
-
- >>> np.can_cast('i8', 'f8')
- True
- >>> np.can_cast('i8', 'f4')
- False
- >>> np.can_cast('i4', 'S4')
- False
-
- Casting scalars
-
- >>> np.can_cast(100, 'i1')
- True
- >>> np.can_cast(150, 'i1')
- False
- >>> np.can_cast(150, 'u1')
- True
-
- >>> np.can_cast(3.5e100, np.float32)
- False
- >>> np.can_cast(1000.0, np.float32)
- True
-
- Array scalar checks the value, array does not
-
- >>> np.can_cast(np.array(1000.0), np.float32)
- True
- >>> np.can_cast(np.array([1000.0]), np.float32)
- False
-
- Using the casting rules
-
- >>> np.can_cast('i8', 'i8', 'no')
- True
- >>> np.can_cast('<i8', '>i8', 'no')
- False
-
- >>> np.can_cast('<i8', '>i8', 'equiv')
- True
- >>> np.can_cast('<i4', '>i8', 'equiv')
- False
-
- >>> np.can_cast('<i4', '>i8', 'safe')
- True
- >>> np.can_cast('<i8', '>i4', 'safe')
- False
-
- >>> np.can_cast('<i8', '>i4', 'same_kind')
- True
- >>> np.can_cast('<i8', '>u4', 'same_kind')
- False
-
- >>> np.can_cast('<i8', '>u4', 'unsafe')
- True
-
- """)
-
add_newdoc('numpy.core.multiarray', 'promote_types',
"""
promote_types(type1, type2)
@@ -1892,443 +1476,64 @@ add_newdoc('numpy.core.multiarray', 'promote_types',
""")
-add_newdoc('numpy.core.multiarray', 'min_scalar_type',
- """
- min_scalar_type(a)
+if sys.version_info.major < 3:
+ add_newdoc('numpy.core.multiarray', 'newbuffer',
+ """
+ newbuffer(size)
- For scalar ``a``, returns the data type with the smallest size
- and smallest scalar kind which can hold its value. For non-scalar
- array ``a``, returns the vector's dtype unmodified.
+ Return a new uninitialized buffer object.
- Floating point values are not demoted to integers,
- and complex values are not demoted to floats.
+ Parameters
+ ----------
+ size : int
+ Size in bytes of returned buffer object.
- Parameters
- ----------
- a : scalar or array_like
- The value whose minimal data type is to be found.
+ Returns
+ -------
+ newbuffer : buffer object
+ Returned, uninitialized buffer object of `size` bytes.
- Returns
- -------
- out : dtype
- The minimal data type.
+ """)
- Notes
- -----
- .. versionadded:: 1.6.0
+ add_newdoc('numpy.core.multiarray', 'getbuffer',
+ """
+ getbuffer(obj [,offset[, size]])
- See Also
- --------
- result_type, promote_types, dtype, can_cast
-
- Examples
- --------
- >>> np.min_scalar_type(10)
- dtype('uint8')
-
- >>> np.min_scalar_type(-260)
- dtype('int16')
-
- >>> np.min_scalar_type(3.1)
- dtype('float16')
-
- >>> np.min_scalar_type(1e50)
- dtype('float64')
-
- >>> np.min_scalar_type(np.arange(4,dtype='f8'))
- dtype('float64')
-
- """)
-
-add_newdoc('numpy.core.multiarray', 'result_type',
- """
- result_type(*arrays_and_dtypes)
-
- Returns the type that results from applying the NumPy
- type promotion rules to the arguments.
-
- Type promotion in NumPy works similarly to the rules in languages
- like C++, with some slight differences. When both scalars and
- arrays are used, the array's type takes precedence and the actual value
- of the scalar is taken into account.
-
- For example, calculating 3*a, where a is an array of 32-bit floats,
- intuitively should result in a 32-bit float output. If the 3 is a
- 32-bit integer, the NumPy rules indicate it can't convert losslessly
- into a 32-bit float, so a 64-bit float should be the result type.
- By examining the value of the constant, '3', we see that it fits in
- an 8-bit integer, which can be cast losslessly into the 32-bit float.
-
- Parameters
- ----------
- arrays_and_dtypes : list of arrays and dtypes
- The operands of some operation whose result type is needed.
-
- Returns
- -------
- out : dtype
- The result type.
-
- See also
- --------
- dtype, promote_types, min_scalar_type, can_cast
-
- Notes
- -----
- .. versionadded:: 1.6.0
-
- The specific algorithm used is as follows.
-
- Categories are determined by first checking which of boolean,
- integer (int/uint), or floating point (float/complex) the maximum
- kind of all the arrays and the scalars are.
-
- If there are only scalars or the maximum category of the scalars
- is higher than the maximum category of the arrays,
- the data types are combined with :func:`promote_types`
- to produce the return value.
-
- Otherwise, `min_scalar_type` is called on each array, and
- the resulting data types are all combined with :func:`promote_types`
- to produce the return value.
-
- The set of int values is not a subset of the uint values for types
- with the same number of bits, something not reflected in
- :func:`min_scalar_type`, but handled as a special case in `result_type`.
-
- Examples
- --------
- >>> np.result_type(3, np.arange(7, dtype='i1'))
- dtype('int8')
-
- >>> np.result_type('i4', 'c8')
- dtype('complex128')
-
- >>> np.result_type(3.0, -2)
- dtype('float64')
-
- """)
-
-add_newdoc('numpy.core.multiarray', 'newbuffer',
- """
- newbuffer(size)
-
- Return a new uninitialized buffer object.
-
- Parameters
- ----------
- size : int
- Size in bytes of returned buffer object.
-
- Returns
- -------
- newbuffer : buffer object
- Returned, uninitialized buffer object of `size` bytes.
-
- """)
-
-add_newdoc('numpy.core.multiarray', 'getbuffer',
- """
- getbuffer(obj [,offset[, size]])
-
- Create a buffer object from the given object referencing a slice of
- length size starting at offset.
-
- Default is the entire buffer. A read-write buffer is attempted followed
- by a read-only buffer.
-
- Parameters
- ----------
- obj : object
-
- offset : int, optional
-
- size : int, optional
-
- Returns
- -------
- buffer_obj : buffer
-
- Examples
- --------
- >>> buf = np.getbuffer(np.ones(5), 1, 3)
- >>> len(buf)
- 3
- >>> buf[0]
- '\\x00'
- >>> buf
- <read-write buffer for 0x8af1e70, size 3, offset 1 at 0x8ba4ec0>
-
- """)
-
-add_newdoc('numpy.core', 'dot',
- """
- dot(a, b, out=None)
-
- Dot product of two arrays. Specifically,
-
- - If both `a` and `b` are 1-D arrays, it is inner product of vectors
- (without complex conjugation).
-
- - If both `a` and `b` are 2-D arrays, it is matrix multiplication,
- but using :func:`matmul` or ``a @ b`` is preferred.
-
- - If either `a` or `b` is 0-D (scalar), it is equivalent to :func:`multiply`
- and using ``numpy.multiply(a, b)`` or ``a * b`` is preferred.
-
- - If `a` is an N-D array and `b` is a 1-D array, it is a sum product over
- the last axis of `a` and `b`.
-
- - If `a` is an N-D array and `b` is an M-D array (where ``M>=2``), it is a
- sum product over the last axis of `a` and the second-to-last axis of `b`::
-
- dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
-
- Parameters
- ----------
- a : array_like
- First argument.
- b : array_like
- Second argument.
- out : ndarray, optional
- Output argument. This must have the exact kind that would be returned
- if it was not used. In particular, it must have the right type, must be
- C-contiguous, and its dtype must be the dtype that would be returned
- for `dot(a,b)`. This is a performance feature. Therefore, if these
- conditions are not met, an exception is raised, instead of attempting
- to be flexible.
-
- Returns
- -------
- output : ndarray
- Returns the dot product of `a` and `b`. If `a` and `b` are both
- scalars or both 1-D arrays then a scalar is returned; otherwise
- an array is returned.
- If `out` is given, then it is returned.
-
- Raises
- ------
- ValueError
- If the last dimension of `a` is not the same size as
- the second-to-last dimension of `b`.
-
- See Also
- --------
- vdot : Complex-conjugating dot product.
- tensordot : Sum products over arbitrary axes.
- einsum : Einstein summation convention.
- matmul : '@' operator as method with out parameter.
-
- Examples
- --------
- >>> np.dot(3, 4)
- 12
-
- Neither argument is complex-conjugated:
-
- >>> np.dot([2j, 3j], [2j, 3j])
- (-13+0j)
-
- For 2-D arrays it is the matrix product:
-
- >>> a = [[1, 0], [0, 1]]
- >>> b = [[4, 1], [2, 2]]
- >>> np.dot(a, b)
- array([[4, 1],
- [2, 2]])
-
- >>> a = np.arange(3*4*5*6).reshape((3,4,5,6))
- >>> b = np.arange(3*4*5*6)[::-1].reshape((5,4,6,3))
- >>> np.dot(a, b)[2,3,2,1,2,2]
- 499128
- >>> sum(a[2,3,2,:] * b[1,2,:,2])
- 499128
-
- """)
-
-add_newdoc('numpy.core', 'matmul',
- """
- matmul(a, b, out=None)
-
- Matrix product of two arrays.
-
- The behavior depends on the arguments in the following way.
+ Create a buffer object from the given object referencing a slice of
+ length size starting at offset.
- - If both arguments are 2-D they are multiplied like conventional
- matrices.
- - If either argument is N-D, N > 2, it is treated as a stack of
- matrices residing in the last two indexes and broadcast accordingly.
- - If the first argument is 1-D, it is promoted to a matrix by
- prepending a 1 to its dimensions. After matrix multiplication
- the prepended 1 is removed.
- - If the second argument is 1-D, it is promoted to a matrix by
- appending a 1 to its dimensions. After matrix multiplication
- the appended 1 is removed.
+ Default is the entire buffer. A read-write buffer is attempted followed
+ by a read-only buffer.
- Multiplication by a scalar is not allowed, use ``*`` instead. Note that
- multiplying a stack of matrices with a vector will result in a stack of
- vectors, but matmul will not recognize it as such.
+ Parameters
+ ----------
+ obj : object
- ``matmul`` differs from ``dot`` in two important ways.
+ offset : int, optional
- - Multiplication by scalars is not allowed.
- - Stacks of matrices are broadcast together as if the matrices
- were elements.
+ size : int, optional
- .. warning::
- This function is preliminary and included in NumPy 1.10.0 for testing
- and documentation. Its semantics will not change, but the number and
- order of the optional arguments will.
+ Returns
+ -------
+ buffer_obj : buffer
- .. versionadded:: 1.10.0
+ Examples
+ --------
+ >>> buf = np.getbuffer(np.ones(5), 1, 3)
+ >>> len(buf)
+ 3
+ >>> buf[0]
+ '\\x00'
+ >>> buf
+ <read-write buffer for 0x8af1e70, size 3, offset 1 at 0x8ba4ec0>
- Parameters
- ----------
- a : array_like
- First argument.
- b : array_like
- Second argument.
- out : ndarray, optional
- Output argument. This must have the exact kind that would be returned
- if it was not used. In particular, it must have the right type, must be
- C-contiguous, and its dtype must be the dtype that would be returned
- for `dot(a,b)`. This is a performance feature. Therefore, if these
- conditions are not met, an exception is raised, instead of attempting
- to be flexible.
-
- Returns
- -------
- output : ndarray
- Returns the dot product of `a` and `b`. If `a` and `b` are both
- 1-D arrays then a scalar is returned; otherwise an array is
- returned. If `out` is given, then it is returned.
-
- Raises
- ------
- ValueError
- If the last dimension of `a` is not the same size as
- the second-to-last dimension of `b`.
-
- If scalar value is passed.
-
- See Also
- --------
- vdot : Complex-conjugating dot product.
- tensordot : Sum products over arbitrary axes.
- einsum : Einstein summation convention.
- dot : alternative matrix product with different broadcasting rules.
-
- Notes
- -----
- The matmul function implements the semantics of the `@` operator introduced
- in Python 3.5 following PEP465.
-
- Examples
- --------
- For 2-D arrays it is the matrix product:
-
- >>> a = [[1, 0], [0, 1]]
- >>> b = [[4, 1], [2, 2]]
- >>> np.matmul(a, b)
- array([[4, 1],
- [2, 2]])
-
- For 2-D mixed with 1-D, the result is the usual.
-
- >>> a = [[1, 0], [0, 1]]
- >>> b = [1, 2]
- >>> np.matmul(a, b)
- array([1, 2])
- >>> np.matmul(b, a)
- array([1, 2])
-
-
- Broadcasting is conventional for stacks of arrays
-
- >>> a = np.arange(2*2*4).reshape((2,2,4))
- >>> b = np.arange(2*2*4).reshape((2,4,2))
- >>> np.matmul(a,b).shape
- (2, 2, 2)
- >>> np.matmul(a,b)[0,1,1]
- 98
- >>> sum(a[0,1,:] * b[0,:,1])
- 98
-
- Vector, vector returns the scalar inner product, but neither argument
- is complex-conjugated:
-
- >>> np.matmul([2j, 3j], [2j, 3j])
- (-13+0j)
-
- Scalar multiplication raises an error.
-
- >>> np.matmul([1,2], 3)
- Traceback (most recent call last):
- ...
- ValueError: Scalar operands are not allowed, use '*' instead
-
- """)
-
-add_newdoc('numpy.core', 'vdot',
- """
- vdot(a, b)
-
- Return the dot product of two vectors.
-
- The vdot(`a`, `b`) function handles complex numbers differently than
- dot(`a`, `b`). If the first argument is complex the complex conjugate
- of the first argument is used for the calculation of the dot product.
-
- Note that `vdot` handles multidimensional arrays differently than `dot`:
- it does *not* perform a matrix product, but flattens input arguments
- to 1-D vectors first. Consequently, it should only be used for vectors.
-
- Parameters
- ----------
- a : array_like
- If `a` is complex the complex conjugate is taken before calculation
- of the dot product.
- b : array_like
- Second argument to the dot product.
-
- Returns
- -------
- output : ndarray
- Dot product of `a` and `b`. Can be an int, float, or
- complex depending on the types of `a` and `b`.
-
- See Also
- --------
- dot : Return the dot product without using the complex conjugate of the
- first argument.
-
- Examples
- --------
- >>> a = np.array([1+2j,3+4j])
- >>> b = np.array([5+6j,7+8j])
- >>> np.vdot(a, b)
- (70-8j)
- >>> np.vdot(b, a)
- (70+8j)
-
- Note that higher-dimensional arrays are flattened!
-
- >>> a = np.array([[1, 4], [5, 6]])
- >>> b = np.array([[4, 1], [2, 2]])
- >>> np.vdot(a, b)
- 30
- >>> np.vdot(b, a)
- 30
- >>> 1*4 + 4*1 + 5*2 + 6*2
- 30
-
- """)
+ """)
add_newdoc('numpy.core.multiarray', 'c_einsum',
"""
c_einsum(subscripts, *operands, out=None, dtype=None, order='K',
casting='safe')
-
+
*This documentation shadows that of the native python implementation of the `einsum` function,
except all references and examples related to the `optimize` argument (v 0.12.0) have been removed.*
@@ -2752,8 +1957,8 @@ add_newdoc('numpy.core.multiarray', 'ndarray',
First mode, `buffer` is None:
>>> np.ndarray(shape=(2,2), dtype=float, order='F')
- array([[ -1.13698227e+002, 4.25087011e-303],
- [ 2.88528414e-306, 3.27025015e-309]]) #random
+ array([[0.0e+000, 0.0e+000], # random
+ [ nan, 2.5e-323]])
Second mode:
@@ -2788,13 +1993,6 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_struct__',
"""Array protocol: C-struct side."""))
-add_newdoc('numpy.core.multiarray', 'ndarray', ('_as_parameter_',
- """Allow the array to be interpreted as a ctypes object by returning the
- data-memory location as an integer
-
- """))
-
-
add_newdoc('numpy.core.multiarray', 'ndarray', ('base',
"""
Base object if memory is from some other object.
@@ -2844,32 +2042,30 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('ctypes',
-----
Below are the public attributes of this object which were documented
in "Guide to NumPy" (we have omitted undocumented public attributes,
- as well as documented private attributes):
+ as well as documented private attributes):
.. autoattribute:: numpy.core._internal._ctypes.data
+ :noindex:
.. autoattribute:: numpy.core._internal._ctypes.shape
+ :noindex:
.. autoattribute:: numpy.core._internal._ctypes.strides
+ :noindex:
.. automethod:: numpy.core._internal._ctypes.data_as
+ :noindex:
.. automethod:: numpy.core._internal._ctypes.shape_as
+ :noindex:
.. automethod:: numpy.core._internal._ctypes.strides_as
-
- Be careful using the ctypes attribute - especially on temporary
- arrays or arrays constructed on the fly. For example, calling
- ``(a+b).ctypes.data_as(ctypes.c_void_p)`` returns a pointer to memory
- that is invalid because the array created as (a+b) is deallocated
- before the next Python statement. You can avoid this problem using
- either ``c=a+b`` or ``ct=(a+b).ctypes``. In the latter case, ct will
- hold a reference to the array until ct is deleted or re-assigned.
+ :noindex:
If the ctypes module is not available, then the ctypes attribute
of array objects still returns something useful, but ctypes objects
are not returned and errors may be raised instead. In particular,
- the object will still have the as parameter attribute which will
+ the object will still have the ``as_parameter`` attribute which will
return an integer equal to the data attribute.
Examples
@@ -3067,7 +2263,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('flat',
>>> x.T.flat[3]
5
>>> type(x.flat)
- <type 'numpy.flatiter'>
+ <class 'numpy.flatiter'>
An assignment example:
@@ -3186,7 +2382,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('size',
Notes
-----
- `a.size` returns a standard arbitrary precision Python integer. This
+ `a.size` returns a standard arbitrary precision Python integer. This
may not be the case with other methods of obtaining the same value
(like the suggested ``np.prod(a.shape)``, which returns an instance
of ``np.int_``), and may be relevant if the value is used further in
@@ -3267,8 +2463,9 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('strides',
add_newdoc('numpy.core.multiarray', 'ndarray', ('T',
"""
- Same as self.transpose(), except that self is returned if
- self.ndim < 2.
+ The transposed array.
+
+ Same as ``self.transpose()``.
Examples
--------
@@ -3285,6 +2482,10 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('T',
>>> x.T
array([ 1., 2., 3., 4.])
+ See Also
+ --------
+ transpose
+
"""))
@@ -3426,7 +2627,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('argmin',
add_newdoc('numpy.core.multiarray', 'ndarray', ('argsort',
"""
- a.argsort(axis=-1, kind='quicksort', order=None)
+ a.argsort(axis=-1, kind=None, order=None)
Returns the indices that would sort this array.
@@ -3502,10 +2703,15 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('astype',
Notes
-----
- Starting in NumPy 1.9, astype method now returns an error if the string
- dtype to cast to is not long enough in 'safe' casting mode to hold the max
- value of integer/float array that is being casted. Previously the casting
- was allowed even if the result was truncated.
+ .. versionchanged:: 1.17.0
+ Casting between a simple data type and a structured one is possible only
+ for "unsafe" casting. Casting to multiple fields is allowed, but
+ casting from multiple fields is not.
+
+ .. versionchanged:: 1.9.0
+ Casting from numeric to string types in 'safe' casting mode requires
+ that the string dtype length is long enough to store the max
+ integer/float value converted.
Raises
------
@@ -3517,7 +2723,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('astype',
--------
>>> x = np.array([1, 2, 2.5])
>>> x
- array([ 1. , 2. , 2.5])
+ array([1. , 2. , 2.5])
>>> x.astype(int)
array([1, 2, 2])
@@ -3533,6 +2739,8 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('byteswap',
Toggle between low-endian and big-endian data representation by
returning a byteswapped array, optionally swapped in-place.
+ Arrays of byte-strings are not swapped. The real and imaginary
+ parts of a complex number are swapped individually.
Parameters
----------
@@ -3548,19 +2756,31 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('byteswap',
Examples
--------
>>> A = np.array([1, 256, 8755], dtype=np.int16)
- >>> map(hex, A)
+ >>> list(map(hex, A))
['0x1', '0x100', '0x2233']
>>> A.byteswap(inplace=True)
array([ 256, 1, 13090], dtype=int16)
- >>> map(hex, A)
+ >>> list(map(hex, A))
['0x100', '0x1', '0x3322']
- Arrays of strings are not swapped
+ Arrays of byte-strings are not swapped
- >>> A = np.array(['ceg', 'fac'])
+ >>> A = np.array([b'ceg', b'fac'])
>>> A.byteswap()
- array(['ceg', 'fac'],
- dtype='|S3')
+ array([b'ceg', b'fac'], dtype='|S3')
+
+ ``A.newbyteorder().byteswap()`` produces an array with the same values
+ but different representation in memory
+
+ >>> A = np.array([1, 2, 3])
+ >>> A.view(np.uint8)
+ array([1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0,
+ 0, 0], dtype=uint8)
+ >>> A.newbyteorder().byteswap(inplace=True)
+ array([1, 2, 3])
+ >>> A.view(np.uint8)
+ array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 3], dtype=uint8)
"""))
@@ -3582,7 +2802,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('choose',
add_newdoc('numpy.core.multiarray', 'ndarray', ('clip',
"""
- a.clip(min=None, max=None, out=None)
+ a.clip(min=None, max=None, out=None, **kwargs)
Return an array whose values are limited to ``[min, max]``.
One of max or min must be given.
@@ -3748,14 +2968,14 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('dot',
>>> a = np.eye(2)
>>> b = np.ones((2, 2)) * 2
>>> a.dot(b)
- array([[ 2., 2.],
- [ 2., 2.]])
+ array([[2., 2.],
+ [2., 2.]])
This array method can be conveniently chained:
>>> a.dot(b).dot(b)
- array([[ 8., 8.],
- [ 8., 8.]])
+ array([[8., 8.],
+ [8., 8.]])
"""))
@@ -3768,9 +2988,12 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('dump',
Parameters
----------
- file : str
+ file : str or Path
A string naming the dump file.
+ .. versionchanged:: 1.17.0
+ `pathlib.Path` objects are now accepted.
+
"""))
@@ -3808,7 +3031,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('fill',
>>> a = np.empty(2)
>>> a.fill(1)
>>> a
- array([ 1., 1.])
+ array([1., 1.])
"""))
@@ -3877,18 +3100,18 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('getfield',
>>> x = np.diag([1.+1.j]*2)
>>> x[1, 1] = 2 + 4.j
>>> x
- array([[ 1.+1.j, 0.+0.j],
- [ 0.+0.j, 2.+4.j]])
+ array([[1.+1.j, 0.+0.j],
+ [0.+0.j, 2.+4.j]])
>>> x.getfield(np.float64)
- array([[ 1., 0.],
- [ 0., 2.]])
+ array([[1., 0.],
+ [0., 2.]])
By choosing an offset of 8 bytes we can select the complex part of the
array for our view:
>>> x.getfield(np.float64, offset=8)
- array([[ 1., 0.],
- [ 0., 4.]])
+ array([[1., 0.],
+ [0., 4.]])
"""))
@@ -3934,19 +3157,20 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('item',
Examples
--------
+ >>> np.random.seed(123)
>>> x = np.random.randint(9, size=(3, 3))
>>> x
- array([[3, 1, 7],
- [2, 8, 3],
- [8, 5, 3]])
+ array([[2, 2, 6],
+ [1, 3, 6],
+ [1, 0, 1]])
>>> x.item(3)
- 2
+ 1
>>> x.item(7)
- 5
+ 0
>>> x.item((0, 1))
- 1
+ 2
>>> x.item((2, 2))
- 3
+ 1
"""))
@@ -3982,24 +3206,25 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('itemset',
Examples
--------
+ >>> np.random.seed(123)
>>> x = np.random.randint(9, size=(3, 3))
>>> x
- array([[3, 1, 7],
- [2, 8, 3],
- [8, 5, 3]])
+ array([[2, 2, 6],
+ [1, 3, 6],
+ [1, 0, 1]])
>>> x.itemset(4, 0)
>>> x.itemset((2, 2), 9)
>>> x
- array([[3, 1, 7],
- [2, 0, 3],
- [8, 5, 9]])
+ array([[2, 2, 6],
+ [1, 0, 6],
+ [1, 0, 9]])
"""))
add_newdoc('numpy.core.multiarray', 'ndarray', ('max',
"""
- a.max(axis=None, out=None, keepdims=False)
+ a.max(axis=None, out=None, keepdims=False, initial=<no value>, where=True)
Return the maximum along a given axis.
@@ -4029,7 +3254,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('mean',
add_newdoc('numpy.core.multiarray', 'ndarray', ('min',
"""
- a.min(axis=None, out=None, keepdims=False)
+ a.min(axis=None, out=None, keepdims=False, initial=<no value>, where=True)
Return the minimum along a given axis.
@@ -4042,87 +3267,6 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('min',
"""))
-add_newdoc('numpy.core.multiarray', 'shares_memory',
- """
- shares_memory(a, b, max_work=None)
-
- Determine if two arrays share memory
-
- Parameters
- ----------
- a, b : ndarray
- Input arrays
- max_work : int, optional
- Effort to spend on solving the overlap problem (maximum number
- of candidate solutions to consider). The following special
- values are recognized:
-
- max_work=MAY_SHARE_EXACT (default)
- The problem is solved exactly. In this case, the function returns
- True only if there is an element shared between the arrays.
- max_work=MAY_SHARE_BOUNDS
- Only the memory bounds of a and b are checked.
-
- Raises
- ------
- numpy.TooHardError
- Exceeded max_work.
-
- Returns
- -------
- out : bool
-
- See Also
- --------
- may_share_memory
-
- Examples
- --------
- >>> np.may_share_memory(np.array([1,2]), np.array([5,8,9]))
- False
-
- """)
-
-
-add_newdoc('numpy.core.multiarray', 'may_share_memory',
- """
- may_share_memory(a, b, max_work=None)
-
- Determine if two arrays might share memory
-
- A return of True does not necessarily mean that the two arrays
- share any element. It just means that they *might*.
-
- Only the memory bounds of a and b are checked by default.
-
- Parameters
- ----------
- a, b : ndarray
- Input arrays
- max_work : int, optional
- Effort to spend on solving the overlap problem. See
- `shares_memory` for details. Default for ``may_share_memory``
- is to do a bounds check.
-
- Returns
- -------
- out : bool
-
- See Also
- --------
- shares_memory
-
- Examples
- --------
- >>> np.may_share_memory(np.array([1,2]), np.array([5,8,9]))
- False
- >>> x = np.zeros([3, 4])
- >>> np.may_share_memory(x[:,0], x[:,1])
- True
-
- """)
-
-
add_newdoc('numpy.core.multiarray', 'ndarray', ('newbyteorder',
"""
arr.newbyteorder(new_order='S')
@@ -4182,7 +3326,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('nonzero',
add_newdoc('numpy.core.multiarray', 'ndarray', ('prod',
"""
- a.prod(axis=None, dtype=None, out=None, keepdims=False)
+ a.prod(axis=None, dtype=None, out=None, keepdims=False, initial=1, where=True)
Return the product of the array elements over the given axis
@@ -4224,81 +3368,6 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('put',
"""))
-add_newdoc('numpy.core.multiarray', 'copyto',
- """
- copyto(dst, src, casting='same_kind', where=True)
-
- Copies values from one array to another, broadcasting as necessary.
-
- Raises a TypeError if the `casting` rule is violated, and if
- `where` is provided, it selects which elements to copy.
-
- .. versionadded:: 1.7.0
-
- Parameters
- ----------
- dst : ndarray
- The array into which values are copied.
- src : array_like
- The array from which values are copied.
- casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
- Controls what kind of data casting may occur when copying.
-
- * 'no' means the data types should not be cast at all.
- * 'equiv' means only byte-order changes are allowed.
- * 'safe' means only casts which can preserve values are allowed.
- * 'same_kind' means only safe casts or casts within a kind,
- like float64 to float32, are allowed.
- * 'unsafe' means any data conversions may be done.
- where : array_like of bool, optional
- A boolean array which is broadcasted to match the dimensions
- of `dst`, and selects elements to copy from `src` to `dst`
- wherever it contains the value True.
-
- """)
-
-add_newdoc('numpy.core.multiarray', 'putmask',
- """
- putmask(a, mask, values)
-
- Changes elements of an array based on conditional and input values.
-
- Sets ``a.flat[n] = values[n]`` for each n where ``mask.flat[n]==True``.
-
- If `values` is not the same size as `a` and `mask` then it will repeat.
- This gives behavior different from ``a[mask] = values``.
-
- Parameters
- ----------
- a : array_like
- Target array.
- mask : array_like
- Boolean mask array. It has to be the same shape as `a`.
- values : array_like
- Values to put into `a` where `mask` is True. If `values` is smaller
- than `a` it will be repeated.
-
- See Also
- --------
- place, put, take, copyto
-
- Examples
- --------
- >>> x = np.arange(6).reshape(2, 3)
- >>> np.putmask(x, x>2, x**2)
- >>> x
- array([[ 0, 1, 2],
- [ 9, 16, 25]])
-
- If `values` is smaller than `a` it is repeated:
-
- >>> x = np.arange(5)
- >>> np.putmask(x, x>1, [-33, -44])
- >>> x
- array([ 0, 1, -33, -44, -33])
-
- """)
-
add_newdoc('numpy.core.multiarray', 'ndarray', ('ravel',
"""
@@ -4433,7 +3502,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('resize',
>>> a.resize((1, 1))
Traceback (most recent call last):
...
- ValueError: cannot resize an array that has been referenced ...
+ ValueError: cannot resize an array that references or is referenced ...
Unless `refcheck` is False:
@@ -4506,23 +3575,23 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('setfield',
--------
>>> x = np.eye(3)
>>> x.getfield(np.float64)
- array([[ 1., 0., 0.],
- [ 0., 1., 0.],
- [ 0., 0., 1.]])
+ array([[1., 0., 0.],
+ [0., 1., 0.],
+ [0., 0., 1.]])
>>> x.setfield(3, np.int32)
>>> x.getfield(np.int32)
array([[3, 3, 3],
[3, 3, 3],
- [3, 3, 3]])
+ [3, 3, 3]], dtype=int32)
>>> x
- array([[ 1.00000000e+000, 1.48219694e-323, 1.48219694e-323],
- [ 1.48219694e-323, 1.00000000e+000, 1.48219694e-323],
- [ 1.48219694e-323, 1.48219694e-323, 1.00000000e+000]])
+ array([[1.0e+000, 1.5e-323, 1.5e-323],
+ [1.5e-323, 1.0e+000, 1.5e-323],
+ [1.5e-323, 1.5e-323, 1.0e+000]])
>>> x.setfield(np.eye(3), np.int32)
>>> x
- array([[ 1., 0., 0.],
- [ 0., 1., 0.],
- [ 0., 0., 1.]])
+ array([[1., 0., 0.],
+ [0., 1., 0.],
+ [0., 0., 1.]])
"""))
@@ -4575,6 +3644,9 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('setflags',
Examples
--------
+ >>> y = np.array([[3, 1, 7],
+ ... [2, 0, 0],
+ ... [8, 5, 9]])
>>> y
array([[3, 1, 7],
[2, 0, 0],
@@ -4606,9 +3678,9 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('setflags',
add_newdoc('numpy.core.multiarray', 'ndarray', ('sort',
"""
- a.sort(axis=-1, kind='quicksort', order=None)
+ a.sort(axis=-1, kind=None, order=None)
- Sort an array, in-place.
+ Sort an array in-place. Refer to `numpy.sort` for full documentation.
Parameters
----------
@@ -4616,7 +3688,14 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('sort',
Axis along which to sort. Default is -1, which means sort along the
last axis.
kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional
- Sorting algorithm. Default is 'quicksort'.
+ Sorting algorithm. The default is 'quicksort'. Note that both 'stable'
+ and 'mergesort' use timsort under the covers and, in general, the
+ actual implementation will vary with datatype. The 'mergesort' option
+ is retained for backwards compatibility.
+
+ .. versionchanged:: 1.15.0.
+ The 'stable' option was added.
+
order : str or list of str, optional
When `a` is an array with fields defined, this argument specifies
which fields to compare first, second, etc. A single field can
@@ -4634,7 +3713,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('sort',
Notes
-----
- See ``sort`` for notes on the different sorting algorithms.
+ See `numpy.sort` for notes on the different sorting algorithms.
Examples
--------
@@ -4654,8 +3733,8 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('sort',
>>> a = np.array([('a', 2), ('c', 1)], dtype=[('x', 'S1'), ('y', int)])
>>> a.sort(order='y')
>>> a
- array([('c', 1), ('a', 2)],
- dtype=[('x', '|S1'), ('y', '<i4')])
+ array([(b'c', 1), (b'a', 2)],
+ dtype=[('x', 'S1'), ('y', '<i8')])
"""))
@@ -4711,6 +3790,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('partition',
array([2, 1, 3, 4])
>>> a.partition((1, 3))
+ >>> a
array([1, 2, 3, 4])
"""))
@@ -4747,7 +3827,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('std',
add_newdoc('numpy.core.multiarray', 'ndarray', ('sum',
"""
- a.sum(axis=None, dtype=None, out=None, keepdims=False)
+ a.sum(axis=None, dtype=None, out=None, keepdims=False, initial=0, where=True)
Return the sum of the array elements over the given axis.
@@ -4802,8 +3882,12 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('tofile',
Parameters
----------
- fid : file or str
+ fid : file or str or Path
An open file object, or a string containing a filename.
+
+ .. versionchanged:: 1.17.0
+ `pathlib.Path` objects are now accepted.
+
sep : str
Separator between array items for text output.
If "" (empty), a binary file is written, equivalent to
@@ -4821,7 +3905,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('tofile',
machines with different endianness. Some of these problems can be overcome
by outputting the data as text files, at the expense of speed and file
size.
-
+
When fid is a file object, array contents are directly written to the
file, bypassing the file object's ``write`` method. As a result, tofile
cannot be used with files objects supporting compression (e.g., GzipFile)
@@ -4834,10 +3918,14 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('tolist',
"""
a.tolist()
- Return the array as a (possibly nested) list.
+ Return the array as an ``a.ndim``-levels deep nested list of Python scalars.
Return a copy of the array data as a (nested) Python list.
- Data items are converted to the nearest compatible Python type.
+ Data items are converted to the nearest compatible builtin Python type, via
+ the `~numpy.ndarray.item` function.
+
+ If ``a.ndim`` is 0, then since the depth of the nested list is 0, it will
+ not be a list at all, but a simple Python scalar.
Parameters
----------
@@ -4845,24 +3933,41 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('tolist',
Returns
-------
- y : list
+ y : object, or list of object, or list of list of object, or ...
The possibly nested list of array elements.
Notes
-----
- The array may be recreated, ``a = np.array(a.tolist())``.
+ The array may be recreated via ``a = np.array(a.tolist())``, although this
+ may sometimes lose precision.
Examples
--------
+ For a 1D array, ``a.tolist()`` is almost the same as ``list(a)``:
+
>>> a = np.array([1, 2])
+ >>> list(a)
+ [1, 2]
>>> a.tolist()
[1, 2]
+
+ However, for a 2D array, ``tolist`` applies recursively:
+
>>> a = np.array([[1, 2], [3, 4]])
>>> list(a)
[array([1, 2]), array([3, 4])]
>>> a.tolist()
[[1, 2], [3, 4]]
+ The base case for this recursion is a 0D array:
+
+ >>> a = np.array(1)
+ >>> list(a)
+ Traceback (most recent call last):
+ ...
+ TypeError: iteration over a 0-d array
+ >>> a.tolist()
+ 1
"""))
@@ -4892,13 +3997,13 @@ tobytesdoc = """
Examples
--------
- >>> x = np.array([[0, 1], [2, 3]])
+ >>> x = np.array([[0, 1], [2, 3]], dtype='<u2')
>>> x.tobytes()
- b'\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x03\\x00\\x00\\x00'
+ b'\\x00\\x00\\x01\\x00\\x02\\x00\\x03\\x00'
>>> x.tobytes('C') == x.tobytes()
True
>>> x.tobytes('F')
- b'\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x00\\x00\\x00'
+ b'\\x00\\x00\\x02\\x00\\x01\\x00\\x03\\x00'
"""
@@ -4934,9 +4039,11 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('transpose',
Returns a view of the array with axes transposed.
- For a 1-D array, this has no effect. (To change between column and
- row vectors, first cast the 1-D array into a matrix object.)
- For a 2-D array, this is the usual matrix transpose.
+ For a 1-D array this has no effect, as a transposed vector is simply the
+ same vector. To convert a 1-D array into a 2D column vector, an additional
+ dimension must be added. `np.atleast2d(a).T` achieves this, as does
+ `a[:, np.newaxis]`.
+ For a 2-D array, this is a standard matrix transpose.
For an n-D array, if axes are given, their order indicates how the
axes are permuted (see Examples). If axes are not provided and
``a.shape = (i[0], i[1], ... i[n-2], i[n-1])``, then
@@ -4962,6 +4069,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('transpose',
See Also
--------
ndarray.T : Array property returning the array transposed.
+ ndarray.reshape : Give a new shape to an array without changing its data.
Examples
--------
@@ -5048,7 +4156,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('view',
>>> y
matrix([[513]], dtype=int16)
>>> print(type(y))
- <class 'numpy.matrixlib.defmatrix.matrix'>
+ <class 'numpy.matrix'>
Creating a view on a structured array so it can be used in calculations
@@ -5058,19 +4166,19 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('view',
array([[1, 2],
[3, 4]], dtype=int8)
>>> xv.mean(0)
- array([ 2., 3.])
+ array([2., 3.])
Making changes to the view changes the underlying array
>>> xv[0,1] = 20
- >>> print(x)
- [(1, 20) (3, 4)]
+ >>> x
+ array([(1, 20), (3, 4)], dtype=[('a', 'i1'), ('b', 'i1')])
Using a view to convert an array to a recarray:
>>> z = x.view(np.recarray)
>>> z.a
- array([1], dtype=int8)
+ array([1, 3], dtype=int8)
Views share data:
@@ -5088,8 +4196,8 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('view',
[4, 5]], dtype=int16)
>>> y.view(dtype=[('width', np.int16), ('length', np.int16)])
Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: new type not compatible with array.
+ ...
+ ValueError: To change to a dtype of a different size, the array must be C-contiguous
>>> z = y.copy()
>>> z.view(dtype=[('width', np.int16), ('length', np.int16)])
array([[(1, 2)],
@@ -5140,10 +4248,9 @@ add_newdoc('numpy.core.umath', 'frompyfunc',
>>> oct_array = np.frompyfunc(oct, 1, 1)
>>> oct_array(np.array((10, 30, 100)))
- array([012, 036, 0144], dtype=object)
+ array(['0o12', '0o36', '0o144'], dtype=object)
>>> np.array((oct(10), oct(30), oct(100))) # for comparison
- array(['012', '036', '0144'],
- dtype='|S4')
+ array(['0o12', '0o36', '0o144'], dtype='<U5')
""")
@@ -5190,7 +4297,7 @@ add_newdoc('numpy.core.umath', 'geterrobj',
Examples
--------
>>> np.geterrobj() # first get the defaults
- [10000, 0, None]
+ [8192, 521, None]
>>> def err_handler(type, flag):
... print("Floating point error (%s), with flag %s" % (type, flag))
@@ -5199,13 +4306,13 @@ add_newdoc('numpy.core.umath', 'geterrobj',
>>> old_err = np.seterr(divide='raise')
>>> old_handler = np.seterrcall(err_handler)
>>> np.geterrobj()
- [20000, 2, <function err_handler at 0x91dcaac>]
+ [8192, 521, <function err_handler at 0x91dcaac>]
>>> old_err = np.seterr(all='ignore')
>>> np.base_repr(np.geterrobj()[1], 8)
'0'
>>> old_err = np.seterr(divide='warn', over='log', under='call',
- invalid='print')
+ ... invalid='print')
>>> np.base_repr(np.geterrobj()[1], 8)
'4351'
@@ -5254,7 +4361,7 @@ add_newdoc('numpy.core.umath', 'seterrobj',
--------
>>> old_errobj = np.geterrobj() # first get the defaults
>>> old_errobj
- [10000, 0, None]
+ [8192, 521, None]
>>> def err_handler(type, flag):
... print("Floating point error (%s), with flag %s" % (type, flag))
@@ -5277,180 +4384,6 @@ add_newdoc('numpy.core.umath', 'seterrobj',
#
##############################################################################
-add_newdoc('numpy.core.multiarray', 'bincount',
- """
- bincount(x, weights=None, minlength=0)
-
- Count number of occurrences of each value in array of non-negative ints.
-
- The number of bins (of size 1) is one larger than the largest value in
- `x`. If `minlength` is specified, there will be at least this number
- of bins in the output array (though it will be longer if necessary,
- depending on the contents of `x`).
- Each bin gives the number of occurrences of its index value in `x`.
- If `weights` is specified the input array is weighted by it, i.e. if a
- value ``n`` is found at position ``i``, ``out[n] += weight[i]`` instead
- of ``out[n] += 1``.
-
- Parameters
- ----------
- x : array_like, 1 dimension, nonnegative ints
- Input array.
- weights : array_like, optional
- Weights, array of the same shape as `x`.
- minlength : int, optional
- A minimum number of bins for the output array.
-
- .. versionadded:: 1.6.0
-
- Returns
- -------
- out : ndarray of ints
- The result of binning the input array.
- The length of `out` is equal to ``np.amax(x)+1``.
-
- Raises
- ------
- ValueError
- If the input is not 1-dimensional, or contains elements with negative
- values, or if `minlength` is negative.
- TypeError
- If the type of the input is float or complex.
-
- See Also
- --------
- histogram, digitize, unique
-
- Examples
- --------
- >>> np.bincount(np.arange(5))
- array([1, 1, 1, 1, 1])
- >>> np.bincount(np.array([0, 1, 1, 3, 2, 1, 7]))
- array([1, 3, 1, 1, 0, 0, 0, 1])
-
- >>> x = np.array([0, 1, 1, 3, 2, 1, 7, 23])
- >>> np.bincount(x).size == np.amax(x)+1
- True
-
- The input array needs to be of integer dtype, otherwise a
- TypeError is raised:
-
- >>> np.bincount(np.arange(5, dtype=float))
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: array cannot be safely cast to required type
-
- A possible use of ``bincount`` is to perform sums over
- variable-size chunks of an array, using the ``weights`` keyword.
-
- >>> w = np.array([0.3, 0.5, 0.2, 0.7, 1., -0.6]) # weights
- >>> x = np.array([0, 1, 1, 2, 2, 2])
- >>> np.bincount(x, weights=w)
- array([ 0.3, 0.7, 1.1])
-
- """)
-
-add_newdoc('numpy.core.multiarray', 'ravel_multi_index',
- """
- ravel_multi_index(multi_index, dims, mode='raise', order='C')
-
- Converts a tuple of index arrays into an array of flat
- indices, applying boundary modes to the multi-index.
-
- Parameters
- ----------
- multi_index : tuple of array_like
- A tuple of integer arrays, one array for each dimension.
- dims : tuple of ints
- The shape of array into which the indices from ``multi_index`` apply.
- mode : {'raise', 'wrap', 'clip'}, optional
- Specifies how out-of-bounds indices are handled. Can specify
- either one mode or a tuple of modes, one mode per index.
-
- * 'raise' -- raise an error (default)
- * 'wrap' -- wrap around
- * 'clip' -- clip to the range
-
- In 'clip' mode, a negative index which would normally
- wrap will clip to 0 instead.
- order : {'C', 'F'}, optional
- Determines whether the multi-index should be viewed as
- indexing in row-major (C-style) or column-major
- (Fortran-style) order.
-
- Returns
- -------
- raveled_indices : ndarray
- An array of indices into the flattened version of an array
- of dimensions ``dims``.
-
- See Also
- --------
- unravel_index
-
- Notes
- -----
- .. versionadded:: 1.6.0
-
- Examples
- --------
- >>> arr = np.array([[3,6,6],[4,5,1]])
- >>> np.ravel_multi_index(arr, (7,6))
- array([22, 41, 37])
- >>> np.ravel_multi_index(arr, (7,6), order='F')
- array([31, 41, 13])
- >>> np.ravel_multi_index(arr, (4,6), mode='clip')
- array([22, 23, 19])
- >>> np.ravel_multi_index(arr, (4,4), mode=('clip','wrap'))
- array([12, 13, 13])
-
- >>> np.ravel_multi_index((3,1,4,1), (6,7,8,9))
- 1621
- """)
-
-add_newdoc('numpy.core.multiarray', 'unravel_index',
- """
- unravel_index(indices, dims, order='C')
-
- Converts a flat index or array of flat indices into a tuple
- of coordinate arrays.
-
- Parameters
- ----------
- indices : array_like
- An integer array whose elements are indices into the flattened
- version of an array of dimensions ``dims``. Before version 1.6.0,
- this function accepted just one index value.
- dims : tuple of ints
- The shape of the array to use for unraveling ``indices``.
- order : {'C', 'F'}, optional
- Determines whether the indices should be viewed as indexing in
- row-major (C-style) or column-major (Fortran-style) order.
-
- .. versionadded:: 1.6.0
-
- Returns
- -------
- unraveled_coords : tuple of ndarray
- Each array in the tuple has the same shape as the ``indices``
- array.
-
- See Also
- --------
- ravel_multi_index
-
- Examples
- --------
- >>> np.unravel_index([22, 41, 37], (7,6))
- (array([3, 6, 6]), array([4, 5, 1]))
- >>> np.unravel_index([31, 41, 13], (7,6), order='F')
- (array([3, 6, 6]), array([4, 5, 1]))
-
- >>> np.unravel_index(1621, (6,7,8,9))
- (3, 1, 4, 1)
-
- """)
-
add_newdoc('numpy.core.multiarray', 'add_docstring',
"""
add_docstring(obj, docstring)
@@ -5487,94 +4420,6 @@ add_newdoc('numpy.core.umath', '_add_newdoc_ufunc',
and then throwing away the ufunc.
""")
-add_newdoc('numpy.core.multiarray', 'packbits',
- """
- packbits(myarray, axis=None)
-
- Packs the elements of a binary-valued array into bits in a uint8 array.
-
- The result is padded to full bytes by inserting zero bits at the end.
-
- Parameters
- ----------
- myarray : array_like
- An array of integers or booleans whose elements should be packed to
- bits.
- axis : int, optional
- The dimension over which bit-packing is done.
- ``None`` implies packing the flattened array.
-
- Returns
- -------
- packed : ndarray
- Array of type uint8 whose elements represent bits corresponding to the
- logical (0 or nonzero) value of the input elements. The shape of
- `packed` has the same number of dimensions as the input (unless `axis`
- is None, in which case the output is 1-D).
-
- See Also
- --------
- unpackbits: Unpacks elements of a uint8 array into a binary-valued output
- array.
-
- Examples
- --------
- >>> a = np.array([[[1,0,1],
- ... [0,1,0]],
- ... [[1,1,0],
- ... [0,0,1]]])
- >>> b = np.packbits(a, axis=-1)
- >>> b
- array([[[160],[64]],[[192],[32]]], dtype=uint8)
-
- Note that in binary 160 = 1010 0000, 64 = 0100 0000, 192 = 1100 0000,
- and 32 = 0010 0000.
-
- """)
-
-add_newdoc('numpy.core.multiarray', 'unpackbits',
- """
- unpackbits(myarray, axis=None)
-
- Unpacks elements of a uint8 array into a binary-valued output array.
-
- Each element of `myarray` represents a bit-field that should be unpacked
- into a binary-valued output array. The shape of the output array is either
- 1-D (if `axis` is None) or the same shape as the input array with unpacking
- done along the axis specified.
-
- Parameters
- ----------
- myarray : ndarray, uint8 type
- Input array.
- axis : int, optional
- The dimension over which bit-unpacking is done.
- ``None`` implies unpacking the flattened array.
-
- Returns
- -------
- unpacked : ndarray, uint8 type
- The elements are binary-valued (0 or 1).
-
- See Also
- --------
- packbits : Packs the elements of a binary-valued array into bits in a uint8
- array.
-
- Examples
- --------
- >>> a = np.array([[2], [7], [23]], dtype=np.uint8)
- >>> a
- array([[ 2],
- [ 7],
- [23]], dtype=uint8)
- >>> b = np.unpackbits(a, axis=1)
- >>> b
- array([[0, 0, 0, 0, 0, 0, 1, 0],
- [0, 0, 0, 0, 0, 1, 1, 1],
- [0, 0, 0, 1, 0, 1, 1, 1]], dtype=uint8)
-
- """)
add_newdoc('numpy.core._multiarray_tests', 'format_float_OSprintf_g',
"""
@@ -5655,10 +4500,12 @@ add_newdoc('numpy.core', 'ufunc',
number of outputs; use `None` for uninitialized outputs to be
allocated by the ufunc.
where : array_like, optional
- Values of True indicate to calculate the ufunc at that position, values
- of False indicate to leave the value in the output alone. Note that if
- an uninitialized return array is created via the default ``out=None``,
- then the elements where the values are False will remain uninitialized.
+ This condition is broadcast over the input. At locations where the
+ condition is True, the `out` array will be set to the ufunc result.
+ Elsewhere, the `out` array will retain its original value.
+ Note that if an uninitialized `out` array is created via the default
+ ``out=None``, locations within it where the condition is False will
+ remain uninitialized.
**kwargs
For other keyword-only arguments, see the :ref:`ufunc docs <ufuncs.kwargs>`.
@@ -5865,7 +4712,7 @@ add_newdoc('numpy.core', 'ufunc', ('signature',
add_newdoc('numpy.core', 'ufunc', ('reduce',
"""
- reduce(a, axis=0, dtype=None, out=None, keepdims=False, initial)
+ reduce(a, axis=0, dtype=None, out=None, keepdims=False, initial=<no value>, where=True)
Reduces `a`'s dimension by one, by applying ufunc along one axis.
@@ -5910,7 +4757,7 @@ add_newdoc('numpy.core', 'ufunc', ('reduce',
out : ndarray, None, or tuple of ndarray and None, optional
A location into which the result is stored. If not provided or `None`,
a freshly-allocated array is returned. For consistency with
- :ref:`ufunc.__call__`, if given as a keyword, this may be wrapped in a
+ ``ufunc.__call__``, if given as a keyword, this may be wrapped in a
1-element tuple.
.. versionchanged:: 1.13.0
@@ -5927,9 +4774,17 @@ add_newdoc('numpy.core', 'ufunc', ('reduce',
to None - otherwise it defaults to ufunc.identity.
If ``None`` is given, the first element of the reduction is used,
and an error is thrown if the reduction is empty.
-
+
.. versionadded:: 1.15.0
+ where : array_like of bool, optional
+ A boolean array which is broadcasted to match the dimensions
+ of `a`, and selects elements to include in the reduction. Note
+ that for ufuncs like ``minimum`` that do not have an identity
+ defined, one has to pass in also ``initial``.
+
+ .. versionadded:: 1.17.0
+
Returns
-------
r : ndarray
@@ -5960,20 +4815,25 @@ add_newdoc('numpy.core', 'ufunc', ('reduce',
>>> np.add.reduce(X, 2)
array([[ 1, 5],
[ 9, 13]])
-
- You can use the ``initial`` keyword argument to initialize the reduction with a
- different value.
-
+
+ You can use the ``initial`` keyword argument to initialize the reduction
+ with a different value, and ``where`` to select specific elements to include:
+
>>> np.add.reduce([10], initial=5)
15
- >>> np.add.reduce(np.ones((2, 2, 2)), axis=(0, 2), initializer=10)
+ >>> np.add.reduce(np.ones((2, 2, 2)), axis=(0, 2), initial=10)
array([14., 14.])
-
+ >>> a = np.array([10., np.nan, 10])
+ >>> np.add.reduce(a, where=~np.isnan(a))
+ 20.0
+
Allows reductions of empty arrays where they would normally fail, i.e.
for ufuncs without an identity.
-
+
>>> np.minimum.reduce([], initial=np.inf)
inf
+ >>> np.minimum.reduce([[1., 2.], [3., 4.]], initial=10., where=[True, False])
+ array([ 1., 10.])
>>> np.minimum.reduce([])
Traceback (most recent call last):
...
@@ -6014,7 +4874,7 @@ add_newdoc('numpy.core', 'ufunc', ('accumulate',
out : ndarray, None, or tuple of ndarray and None, optional
A location into which the result is stored. If not provided or `None`,
a freshly-allocated array is returned. For consistency with
- :ref:`ufunc.__call__`, if given as a keyword, this may be wrapped in a
+ ``ufunc.__call__``, if given as a keyword, this may be wrapped in a
1-element tuple.
.. versionchanged:: 1.13.0
@@ -6039,23 +4899,23 @@ add_newdoc('numpy.core', 'ufunc', ('accumulate',
>>> I = np.eye(2)
>>> I
- array([[ 1., 0.],
- [ 0., 1.]])
+ array([[1., 0.],
+ [0., 1.]])
Accumulate along axis 0 (rows), down columns:
>>> np.add.accumulate(I, 0)
- array([[ 1., 0.],
- [ 1., 1.]])
+ array([[1., 0.],
+ [1., 1.]])
>>> np.add.accumulate(I) # no axis specified = axis zero
- array([[ 1., 0.],
- [ 1., 1.]])
+ array([[1., 0.],
+ [1., 1.]])
Accumulate along axis 1 (columns), through rows:
>>> np.add.accumulate(I, 1)
- array([[ 1., 1.],
- [ 0., 1.]])
+ array([[1., 1.],
+ [0., 1.]])
"""))
@@ -6096,7 +4956,7 @@ add_newdoc('numpy.core', 'ufunc', ('reduceat',
out : ndarray, None, or tuple of ndarray and None, optional
A location into which the result is stored. If not provided or `None`,
a freshly-allocated array is returned. For consistency with
- :ref:`ufunc.__call__`, if given as a keyword, this may be wrapped in a
+ ``ufunc.__call__``, if given as a keyword, this may be wrapped in a
1-element tuple.
.. versionchanged:: 1.13.0
@@ -6132,10 +4992,10 @@ add_newdoc('numpy.core', 'ufunc', ('reduceat',
>>> x = np.linspace(0, 15, 16).reshape(4,4)
>>> x
- array([[ 0., 1., 2., 3.],
- [ 4., 5., 6., 7.],
- [ 8., 9., 10., 11.],
- [ 12., 13., 14., 15.]])
+ array([[ 0., 1., 2., 3.],
+ [ 4., 5., 6., 7.],
+ [ 8., 9., 10., 11.],
+ [12., 13., 14., 15.]])
::
@@ -6147,11 +5007,11 @@ add_newdoc('numpy.core', 'ufunc', ('reduceat',
# [row1 + row2 + row3 + row4]
>>> np.add.reduceat(x, [0, 3, 1, 2, 0])
- array([[ 12., 15., 18., 21.],
- [ 12., 13., 14., 15.],
- [ 4., 5., 6., 7.],
- [ 8., 9., 10., 11.],
- [ 24., 28., 32., 36.]])
+ array([[12., 15., 18., 21.],
+ [12., 13., 14., 15.],
+ [ 4., 5., 6., 7.],
+ [ 8., 9., 10., 11.],
+ [24., 28., 32., 36.]])
::
@@ -6159,10 +5019,10 @@ add_newdoc('numpy.core', 'ufunc', ('reduceat',
# [col1 * col2 * col3, col4]
>>> np.multiply.reduceat(x, [0, 3], 1)
- array([[ 0., 3.],
- [ 120., 7.],
- [ 720., 11.],
- [ 2184., 15.]])
+ array([[ 0., 3.],
+ [ 120., 7.],
+ [ 720., 11.],
+ [2184., 15.]])
"""))
@@ -6261,14 +5121,14 @@ add_newdoc('numpy.core', 'ufunc', ('at',
>>> a = np.array([1, 2, 3, 4])
>>> np.negative.at(a, [0, 1])
- >>> print(a)
- array([-1, -2, 3, 4])
+ >>> a
+ array([-1, -2, 3, 4])
Increment items 0 and 1, and increment item 2 twice:
>>> a = np.array([1, 2, 3, 4])
>>> np.add.at(a, [0, 1, 2, 2], 1)
- >>> print(a)
+ >>> a
array([2, 3, 5, 4])
Add items 0 and 1 in first array to second array,
@@ -6277,7 +5137,7 @@ add_newdoc('numpy.core', 'ufunc', ('at',
>>> a = np.array([1, 2, 3, 4])
>>> b = np.array([1, 2])
>>> np.add.at(a, [0, 1], b)
- >>> print(a)
+ >>> a
array([2, 4, 3, 4])
"""))
@@ -6342,13 +5202,13 @@ add_newdoc('numpy.core.multiarray', 'dtype',
Structured type, two fields: the first field contains an unsigned int, the
second an int32:
- >>> np.dtype([('f1', np.uint), ('f2', np.int32)])
- dtype([('f1', '<u4'), ('f2', '<i4')])
+ >>> np.dtype([('f1', np.uint64), ('f2', np.int32)])
+ dtype([('f1', '<u8'), ('f2', '<i4')])
Using array-protocol type strings:
>>> np.dtype([('a','f8'),('b','S10')])
- dtype([('a', '<f8'), ('b', '|S10')])
+ dtype([('a', '<f8'), ('b', 'S10')])
Using comma-separated field formats. The shape is (2,3):
@@ -6358,24 +5218,24 @@ add_newdoc('numpy.core.multiarray', 'dtype',
Using tuples. ``int`` is a fixed type, 3 the field's shape. ``void``
is a flexible type, here of size 10:
- >>> np.dtype([('hello',(int,3)),('world',np.void,10)])
- dtype([('hello', '<i4', 3), ('world', '|V10')])
+ >>> np.dtype([('hello',(np.int64,3)),('world',np.void,10)])
+ dtype([('hello', '<i8', (3,)), ('world', 'V10')])
Subdivide ``int16`` into 2 ``int8``'s, called x and y. 0 and 1 are
the offsets in bytes:
>>> np.dtype((np.int16, {'x':(np.int8,0), 'y':(np.int8,1)}))
- dtype(('<i2', [('x', '|i1'), ('y', '|i1')]))
+ dtype((numpy.int16, [('x', 'i1'), ('y', 'i1')]))
Using dictionaries. Two fields named 'gender' and 'age':
>>> np.dtype({'names':['gender','age'], 'formats':['S1',np.uint8]})
- dtype([('gender', '|S1'), ('age', '|u1')])
+ dtype([('gender', 'S1'), ('age', 'u1')])
Offsets in bytes, here 0 and 25:
>>> np.dtype({'surname':('S25',0),'age':(np.uint8,25)})
- dtype([('surname', '|S25'), ('age', '|u1')])
+ dtype([('surname', 'S25'), ('age', 'u1')])
""")
@@ -6391,6 +5251,17 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('alignment',
More information is available in the C-API section of the manual.
+ Examples
+ --------
+
+ >>> x = np.dtype('i4')
+ >>> x.alignment
+ 4
+
+ >>> x = np.dtype(float)
+ >>> x.alignment
+ 8
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('byteorder',
@@ -6437,17 +5308,38 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('byteorder',
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('char',
- """A unique character code for each of the 21 different built-in types."""))
+ """A unique character code for each of the 21 different built-in types.
+
+ Examples
+ --------
+
+ >>> x = np.dtype(float)
+ >>> x.char
+ 'd'
+
+ """))
add_newdoc('numpy.core.multiarray', 'dtype', ('descr',
"""
- PEP3118 interface description of the data-type.
+ `__array_interface__` description of the data-type.
The format is that required by the 'descr' key in the
- PEP3118 `__array_interface__` attribute.
+ `__array_interface__` attribute.
+
+ Warning: This attribute exists specifically for `__array_interface__`,
+ and is not a datatype description compatible with `np.dtype`.
+
+ Examples
+ --------
+
+ >>> x = np.dtype(float)
+ >>> x.descr
+ [('', '<f8')]
+
+ >>> dt = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))])
+ >>> dt.descr
+ [('name', '<U16'), ('grades', '<f8', (2,))]
- Warning: This attribute exists specifically for PEP3118 compliance, and
- is not a datatype description compatible with `np.dtype`.
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('fields',
@@ -6488,6 +5380,18 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('flags',
of these flags is in C-API documentation; they are largely useful
for user-defined data-types.
+ The following example demonstrates that operations on this particular
+ dtype requires Python C-API.
+
+ Examples
+ --------
+
+ >>> x = np.dtype([('a', np.int32, 8), ('b', np.float64, 6)])
+ >>> x.flags
+ 16
+ >>> np.core.multiarray.NEEDS_PYAPI
+ 16
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('hasobject',
@@ -6545,6 +5449,7 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('isalignedstruct',
field alignment. This flag is sticky, so when combining multiple
structs together, it is preserved and produces new dtypes which
are also aligned.
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('itemsize',
@@ -6554,6 +5459,19 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('itemsize',
For 18 of the 21 types this number is fixed by the data-type.
For the flexible data-types, this number can be anything.
+ Examples
+ --------
+
+ >>> arr = np.array([[1, 2], [3, 4]])
+ >>> arr.dtype
+ dtype('int64')
+ >>> arr.itemsize
+ 8
+
+ >>> dt = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))])
+ >>> dt.itemsize
+ 80
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('kind',
@@ -6574,6 +5492,19 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('kind',
V void
= ======================
+ Examples
+ --------
+
+ >>> dt = np.dtype('i4')
+ >>> dt.kind
+ 'i'
+ >>> dt = np.dtype('f8')
+ >>> dt.kind
+ 'f'
+ >>> dt = np.dtype([('field1', 'f8')])
+ >>> dt.kind
+ 'V'
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('name',
@@ -6582,6 +5513,16 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('name',
Un-sized flexible data-type objects do not have this attribute.
+ Examples
+ --------
+
+ >>> x = np.dtype(float)
+ >>> x.name
+ 'float64'
+ >>> x = np.dtype([('a', np.int32, 8), ('b', np.float64, 6)])
+ >>> x.name
+ 'void640'
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('names',
@@ -6605,6 +5546,17 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('num',
These are roughly ordered from least-to-most precision.
+ Examples
+ --------
+
+ >>> dt = np.dtype(str)
+ >>> dt.num
+ 19
+
+ >>> dt = np.dtype(float)
+ >>> dt.num
+ 12
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('shape',
@@ -6612,6 +5564,17 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('shape',
Shape tuple of the sub-array if this data type describes a sub-array,
and ``()`` otherwise.
+ Examples
+ --------
+
+ >>> dt = np.dtype(('i4', 4))
+ >>> dt.shape
+ (4,)
+
+ >>> dt = np.dtype(('i4', (2, 3)))
+ >>> dt.shape
+ (2, 3)
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('ndim',
@@ -6621,6 +5584,20 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('ndim',
.. versionadded:: 1.13.0
+ Examples
+ --------
+ >>> x = np.dtype(float)
+ >>> x.ndim
+ 0
+
+ >>> x = np.dtype((float, 8))
+ >>> x.ndim
+ 1
+
+ >>> x = np.dtype(('i4', (3, 4)))
+ >>> x.ndim
+ 2
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('str',
@@ -6638,6 +5615,41 @@ add_newdoc('numpy.core.multiarray', 'dtype', ('subdtype',
then the extra dimensions implied by *shape* are tacked on to
the end of the retrieved array.
+ See Also
+ --------
+ dtype.base
+
+ Examples
+ --------
+ >>> x = numpy.dtype('8f')
+ >>> x.subdtype
+ (dtype('float32'), (8,))
+
+ >>> x = numpy.dtype('i2')
+ >>> x.subdtype
+ >>>
+
+ """))
+
+add_newdoc('numpy.core.multiarray', 'dtype', ('base',
+ """
+ Returns dtype for the base element of the subarrays,
+ regardless of their dimension or shape.
+
+ See Also
+ --------
+ dtype.subdtype
+
+ Examples
+ --------
+ >>> x = numpy.dtype('8f')
+ >>> x.base
+ dtype('float32')
+
+ >>> x = numpy.dtype('i2')
+ >>> x.base
+ dtype('int16')
+
"""))
add_newdoc('numpy.core.multiarray', 'dtype', ('type',
@@ -6779,7 +5791,7 @@ add_newdoc('numpy.core.multiarray', 'busdaycalendar',
... holidays=['2011-07-01', '2011-07-04', '2011-07-17'])
>>> # Default is Monday to Friday weekdays
... bdd.weekmask
- array([ True, True, True, True, True, False, False], dtype='bool')
+ array([ True, True, True, True, True, False, False])
>>> # Any holidays already on the weekend are removed
... bdd.holidays
array(['2011-07-01', '2011-07-04'], dtype='datetime64[D]')
@@ -6791,211 +5803,6 @@ add_newdoc('numpy.core.multiarray', 'busdaycalendar', ('weekmask',
add_newdoc('numpy.core.multiarray', 'busdaycalendar', ('holidays',
"""A copy of the holiday array indicating additional invalid days."""))
-add_newdoc('numpy.core.multiarray', 'is_busday',
- """
- is_busday(dates, weekmask='1111100', holidays=None, busdaycal=None, out=None)
-
- Calculates which of the given dates are valid days, and which are not.
-
- .. versionadded:: 1.7.0
-
- Parameters
- ----------
- dates : array_like of datetime64[D]
- The array of dates to process.
- weekmask : str or array_like of bool, optional
- A seven-element array indicating which of Monday through Sunday are
- valid days. May be specified as a length-seven list or array, like
- [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
- like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
- weekdays, optionally separated by white space. Valid abbreviations
- are: Mon Tue Wed Thu Fri Sat Sun
- holidays : array_like of datetime64[D], optional
- An array of dates to consider as invalid dates. They may be
- specified in any order, and NaT (not-a-time) dates are ignored.
- This list is saved in a normalized form that is suited for
- fast calculations of valid days.
- busdaycal : busdaycalendar, optional
- A `busdaycalendar` object which specifies the valid days. If this
- parameter is provided, neither weekmask nor holidays may be
- provided.
- out : array of bool, optional
- If provided, this array is filled with the result.
-
- Returns
- -------
- out : array of bool
- An array with the same shape as ``dates``, containing True for
- each valid day, and False for each invalid day.
-
- See Also
- --------
- busdaycalendar: An object that specifies a custom set of valid days.
- busday_offset : Applies an offset counted in valid days.
- busday_count : Counts how many valid days are in a half-open date range.
-
- Examples
- --------
- >>> # The weekdays are Friday, Saturday, and Monday
- ... np.is_busday(['2011-07-01', '2011-07-02', '2011-07-18'],
- ... holidays=['2011-07-01', '2011-07-04', '2011-07-17'])
- array([False, False, True], dtype='bool')
- """)
-
-add_newdoc('numpy.core.multiarray', 'busday_offset',
- """
- busday_offset(dates, offsets, roll='raise', weekmask='1111100', holidays=None, busdaycal=None, out=None)
-
- First adjusts the date to fall on a valid day according to
- the ``roll`` rule, then applies offsets to the given dates
- counted in valid days.
-
- .. versionadded:: 1.7.0
-
- Parameters
- ----------
- dates : array_like of datetime64[D]
- The array of dates to process.
- offsets : array_like of int
- The array of offsets, which is broadcast with ``dates``.
- roll : {'raise', 'nat', 'forward', 'following', 'backward', 'preceding', 'modifiedfollowing', 'modifiedpreceding'}, optional
- How to treat dates that do not fall on a valid day. The default
- is 'raise'.
-
- * 'raise' means to raise an exception for an invalid day.
- * 'nat' means to return a NaT (not-a-time) for an invalid day.
- * 'forward' and 'following' mean to take the first valid day
- later in time.
- * 'backward' and 'preceding' mean to take the first valid day
- earlier in time.
- * 'modifiedfollowing' means to take the first valid day
- later in time unless it is across a Month boundary, in which
- case to take the first valid day earlier in time.
- * 'modifiedpreceding' means to take the first valid day
- earlier in time unless it is across a Month boundary, in which
- case to take the first valid day later in time.
- weekmask : str or array_like of bool, optional
- A seven-element array indicating which of Monday through Sunday are
- valid days. May be specified as a length-seven list or array, like
- [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
- like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
- weekdays, optionally separated by white space. Valid abbreviations
- are: Mon Tue Wed Thu Fri Sat Sun
- holidays : array_like of datetime64[D], optional
- An array of dates to consider as invalid dates. They may be
- specified in any order, and NaT (not-a-time) dates are ignored.
- This list is saved in a normalized form that is suited for
- fast calculations of valid days.
- busdaycal : busdaycalendar, optional
- A `busdaycalendar` object which specifies the valid days. If this
- parameter is provided, neither weekmask nor holidays may be
- provided.
- out : array of datetime64[D], optional
- If provided, this array is filled with the result.
-
- Returns
- -------
- out : array of datetime64[D]
- An array with a shape from broadcasting ``dates`` and ``offsets``
- together, containing the dates with offsets applied.
-
- See Also
- --------
- busdaycalendar: An object that specifies a custom set of valid days.
- is_busday : Returns a boolean array indicating valid days.
- busday_count : Counts how many valid days are in a half-open date range.
-
- Examples
- --------
- >>> # First business day in October 2011 (not accounting for holidays)
- ... np.busday_offset('2011-10', 0, roll='forward')
- numpy.datetime64('2011-10-03','D')
- >>> # Last business day in February 2012 (not accounting for holidays)
- ... np.busday_offset('2012-03', -1, roll='forward')
- numpy.datetime64('2012-02-29','D')
- >>> # Third Wednesday in January 2011
- ... np.busday_offset('2011-01', 2, roll='forward', weekmask='Wed')
- numpy.datetime64('2011-01-19','D')
- >>> # 2012 Mother's Day in Canada and the U.S.
- ... np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun')
- numpy.datetime64('2012-05-13','D')
-
- >>> # First business day on or after a date
- ... np.busday_offset('2011-03-20', 0, roll='forward')
- numpy.datetime64('2011-03-21','D')
- >>> np.busday_offset('2011-03-22', 0, roll='forward')
- numpy.datetime64('2011-03-22','D')
- >>> # First business day after a date
- ... np.busday_offset('2011-03-20', 1, roll='backward')
- numpy.datetime64('2011-03-21','D')
- >>> np.busday_offset('2011-03-22', 1, roll='backward')
- numpy.datetime64('2011-03-23','D')
- """)
-
-add_newdoc('numpy.core.multiarray', 'busday_count',
- """
- busday_count(begindates, enddates, weekmask='1111100', holidays=[], busdaycal=None, out=None)
-
- Counts the number of valid days between `begindates` and
- `enddates`, not including the day of `enddates`.
-
- If ``enddates`` specifies a date value that is earlier than the
- corresponding ``begindates`` date value, the count will be negative.
-
- .. versionadded:: 1.7.0
-
- Parameters
- ----------
- begindates : array_like of datetime64[D]
- The array of the first dates for counting.
- enddates : array_like of datetime64[D]
- The array of the end dates for counting, which are excluded
- from the count themselves.
- weekmask : str or array_like of bool, optional
- A seven-element array indicating which of Monday through Sunday are
- valid days. May be specified as a length-seven list or array, like
- [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
- like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
- weekdays, optionally separated by white space. Valid abbreviations
- are: Mon Tue Wed Thu Fri Sat Sun
- holidays : array_like of datetime64[D], optional
- An array of dates to consider as invalid dates. They may be
- specified in any order, and NaT (not-a-time) dates are ignored.
- This list is saved in a normalized form that is suited for
- fast calculations of valid days.
- busdaycal : busdaycalendar, optional
- A `busdaycalendar` object which specifies the valid days. If this
- parameter is provided, neither weekmask nor holidays may be
- provided.
- out : array of int, optional
- If provided, this array is filled with the result.
-
- Returns
- -------
- out : array of int
- An array with a shape from broadcasting ``begindates`` and ``enddates``
- together, containing the number of valid days between
- the begin and end dates.
-
- See Also
- --------
- busdaycalendar: An object that specifies a custom set of valid days.
- is_busday : Returns a boolean array indicating valid days.
- busday_offset : Applies an offset counted in valid days.
-
- Examples
- --------
- >>> # Number of weekdays in January 2011
- ... np.busday_count('2011-01', '2011-02')
- 21
- >>> # Number of weekdays in 2011
- ... np.busday_count('2011', '2012')
- 260
- >>> # Number of Saturdays in 2011
- ... np.busday_count('2011', '2012', weekmask='Sat')
- 53
- """)
-
add_newdoc('numpy.core.multiarray', 'normalize_axis_index',
"""
normalize_axis_index(axis, ndim, msg_prefix=None)
@@ -7047,67 +5854,6 @@ add_newdoc('numpy.core.multiarray', 'normalize_axis_index',
AxisError: axes_arg: axis -4 is out of bounds for array of dimension 3
""")
-add_newdoc('numpy.core.multiarray', 'datetime_as_string',
- """
- datetime_as_string(arr, unit=None, timezone='naive', casting='same_kind')
-
- Convert an array of datetimes into an array of strings.
-
- Parameters
- ----------
- arr : array_like of datetime64
- The array of UTC timestamps to format.
- unit : str
- One of None, 'auto', or a :ref:`datetime unit <arrays.dtypes.dateunits>`.
- timezone : {'naive', 'UTC', 'local'} or tzinfo
- Timezone information to use when displaying the datetime. If 'UTC', end
- with a Z to indicate UTC time. If 'local', convert to the local timezone
- first, and suffix with a +-#### timezone offset. If a tzinfo object,
- then do as with 'local', but use the specified timezone.
- casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}
- Casting to allow when changing between datetime units.
-
- Returns
- -------
- str_arr : ndarray
- An array of strings the same shape as `arr`.
-
- Examples
- --------
- >>> d = np.arange('2002-10-27T04:30', 4*60, 60, dtype='M8[m]')
- >>> d
- array(['2002-10-27T04:30', '2002-10-27T05:30', '2002-10-27T06:30',
- '2002-10-27T07:30'], dtype='datetime64[m]')
-
- Setting the timezone to UTC shows the same information, but with a Z suffix
-
- >>> np.datetime_as_string(d, timezone='UTC')
- array(['2002-10-27T04:30Z', '2002-10-27T05:30Z', '2002-10-27T06:30Z',
- '2002-10-27T07:30Z'], dtype='<U35')
-
- Note that we picked datetimes that cross a DST boundary. Passing in a
- ``pytz`` timezone object will print the appropriate offset
-
- >>> np.datetime_as_string(d, timezone=pytz.timezone('US/Eastern'))
- array(['2002-10-27T00:30-0400', '2002-10-27T01:30-0400',
- '2002-10-27T01:30-0500', '2002-10-27T02:30-0500'], dtype='<U39')
-
- Passing in a unit will change the precision
-
- >>> np.datetime_as_string(d, unit='h')
- array(['2002-10-27T04', '2002-10-27T05', '2002-10-27T06', '2002-10-27T07'],
- dtype='<U32')
- >>> np.datetime_as_string(d, unit='s')
- array(['2002-10-27T04:30:00', '2002-10-27T05:30:00', '2002-10-27T06:30:00',
- '2002-10-27T07:30:00'], dtype='<U38')
-
- 'casting' can be used to specify whether precision can be changed
-
- >>> np.datetime_as_string(d, unit='h', casting='safe')
- TypeError: Cannot create a datetime string as units 'h' from a NumPy
- datetime with units 'm' according to the rule 'safe'
- """)
-
add_newdoc('numpy.core.multiarray', 'datetime_data',
"""
datetime_data(dtype, /)
@@ -7142,7 +5888,7 @@ add_newdoc('numpy.core.multiarray', 'datetime_data',
as a timedelta
>>> np.datetime64('2010', np.datetime_data(dt_25s))
- numpy.datetime64('2010-01-01T00:00:00', '25s')
+ numpy.datetime64('2010-01-01T00:00:00','25s')
""")
@@ -7174,9 +5920,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('T',
albeit unimplemented, all the attributes of the ndarray class so as to
provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7188,9 +5932,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('base',
albeit unimplemented, all the attributes of the ndarray class so as to
a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7240,9 +5982,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('all',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7254,9 +5994,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('any',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7268,9 +6006,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('argmax',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7282,9 +6018,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('argmin',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7296,9 +6030,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('argsort',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7310,9 +6042,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('astype',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7324,9 +6054,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('byteswap',
albeit unimplemented, all the attributes of the ndarray class so as to
provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7338,9 +6066,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('choose',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7352,9 +6078,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('clip',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7366,9 +6090,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('compress',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7380,9 +6102,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('conjugate',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7394,9 +6114,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('copy',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7408,9 +6126,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('cumprod',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7422,9 +6138,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('cumsum',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7436,9 +6150,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('diagonal',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7450,9 +6162,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('dump',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7464,9 +6174,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('dumps',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7478,9 +6186,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('fill',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7492,9 +6198,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('flatten',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7506,9 +6210,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('getfield',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7520,9 +6222,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('item',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7534,9 +6234,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('itemset',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7548,9 +6246,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('max',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7562,9 +6258,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('mean',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7576,9 +6270,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('min',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7623,9 +6315,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('nonzero',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7637,9 +6327,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('prod',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7651,9 +6339,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('ptp',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7665,9 +6351,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('put',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7679,9 +6363,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('ravel',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7693,9 +6375,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('repeat',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7707,9 +6387,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('reshape',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7721,9 +6399,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('resize',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7735,9 +6411,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('round',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7749,9 +6423,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('searchsorted',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7763,9 +6435,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('setfield',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7777,9 +6447,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('setflags',
albeit unimplemented, all the attributes of the ndarray class so as to
provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7791,9 +6459,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('sort',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7805,9 +6471,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('squeeze',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7819,9 +6483,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('std',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7833,9 +6495,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('sum',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7847,9 +6507,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('swapaxes',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7861,9 +6519,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('take',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7875,9 +6531,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('tofile',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7889,9 +6543,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('tolist',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7903,9 +6555,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('tostring',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7917,9 +6567,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('trace',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7931,9 +6579,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('transpose',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7945,9 +6591,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('var',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7959,9 +6603,7 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('view',
albeit unimplemented, all the attributes of the ndarray class
so as to provide a uniform API.
- See Also
- --------
- The corresponding attribute of the derived class of interest.
+ See also the corresponding attribute of the derived class of interest.
"""))
@@ -7976,25 +6618,25 @@ add_newdoc('numpy.core.numerictypes', 'generic', ('view',
add_newdoc('numpy.core.numerictypes', 'number',
"""
Abstract base class of all numeric scalar types.
-
+
""")
add_newdoc('numpy.core.numerictypes', 'integer',
"""
Abstract base class of all integer scalar types.
-
+
""")
add_newdoc('numpy.core.numerictypes', 'signedinteger',
"""
Abstract base class of all signed integer scalar types.
-
+
""")
add_newdoc('numpy.core.numerictypes', 'unsignedinteger',
"""
Abstract base class of all unsigned integer scalar types.
-
+
""")
add_newdoc('numpy.core.numerictypes', 'inexact',
@@ -8002,20 +6644,20 @@ add_newdoc('numpy.core.numerictypes', 'inexact',
Abstract base class of all numeric scalar types with a (potentially)
inexact representation of the values in its range, such as
floating-point numbers.
-
+
""")
add_newdoc('numpy.core.numerictypes', 'floating',
"""
Abstract base class of all floating-point scalar types.
-
+
""")
add_newdoc('numpy.core.numerictypes', 'complexfloating',
"""
Abstract base class of all complex number scalar types that are made up of
floating-point numbers.
-
+
""")
add_newdoc('numpy.core.numerictypes', 'flexible',
@@ -8023,13 +6665,13 @@ add_newdoc('numpy.core.numerictypes', 'flexible',
Abstract base class of all scalar types without predefined length.
The actual size of these types depends on the specific `np.dtype`
instantiation.
-
+
""")
add_newdoc('numpy.core.numerictypes', 'character',
"""
Abstract base class of all character string scalar types.
-
+
""")
@@ -8193,3 +6835,22 @@ add_newdoc_for_scalar_type('object_', [],
"""
Any Python object.
""")
+
+# TODO: work out how to put this on the base class, np.floating
+for float_name in ('half', 'single', 'double', 'longdouble'):
+ add_newdoc('numpy.core.numerictypes', float_name, ('as_integer_ratio',
+ """
+ {ftype}.as_integer_ratio() -> (int, int)
+
+ Return a pair of integers, whose ratio is exactly equal to the original
+ floating point number, and with a positive denominator.
+ Raise OverflowError on infinities and a ValueError on NaNs.
+
+ >>> np.{ftype}(10.0).as_integer_ratio()
+ (10, 1)
+ >>> np.{ftype}(0.0).as_integer_ratio()
+ (0, 1)
+ >>> np.{ftype}(-.25).as_integer_ratio()
+ (-1, 4)
+ """.format(ftype=float_name)))
+
diff --git a/numpy/core/_aliased_types.py b/numpy/core/_aliased_types.py
deleted file mode 100644
index e69de29bb..000000000
--- a/numpy/core/_aliased_types.py
+++ /dev/null
diff --git a/numpy/core/_asarray.py b/numpy/core/_asarray.py
new file mode 100644
index 000000000..0ad4161f4
--- /dev/null
+++ b/numpy/core/_asarray.py
@@ -0,0 +1,324 @@
+"""
+Functions in the ``as*array`` family that promote array-likes into arrays.
+
+`require` fits this category despite its name not matching this pattern.
+"""
+from __future__ import division, absolute_import, print_function
+
+from .overrides import set_module
+from .multiarray import array
+
+
+__all__ = [
+ "asarray", "asanyarray", "ascontiguousarray", "asfortranarray", "require",
+]
+
+@set_module('numpy')
+def asarray(a, dtype=None, order=None):
+ """Convert the input to an array.
+
+ Parameters
+ ----------
+ a : array_like
+ Input data, in any form that can be converted to an array. This
+ includes lists, lists of tuples, tuples, tuples of tuples, tuples
+ of lists and ndarrays.
+ dtype : data-type, optional
+ By default, the data-type is inferred from the input data.
+ order : {'C', 'F'}, optional
+ Whether to use row-major (C-style) or
+ column-major (Fortran-style) memory representation.
+ Defaults to 'C'.
+
+ Returns
+ -------
+ out : ndarray
+ Array interpretation of `a`. No copy is performed if the input
+ is already an ndarray with matching dtype and order. If `a` is a
+ subclass of ndarray, a base class ndarray is returned.
+
+ See Also
+ --------
+ asanyarray : Similar function which passes through subclasses.
+ ascontiguousarray : Convert input to a contiguous array.
+ asfarray : Convert input to a floating point ndarray.
+ asfortranarray : Convert input to an ndarray with column-major
+ memory order.
+ asarray_chkfinite : Similar function which checks input for NaNs and Infs.
+ fromiter : Create an array from an iterator.
+ fromfunction : Construct an array by executing a function on grid
+ positions.
+
+ Examples
+ --------
+ Convert a list into an array:
+
+ >>> a = [1, 2]
+ >>> np.asarray(a)
+ array([1, 2])
+
+ Existing arrays are not copied:
+
+ >>> a = np.array([1, 2])
+ >>> np.asarray(a) is a
+ True
+
+ If `dtype` is set, array is copied only if dtype does not match:
+
+ >>> a = np.array([1, 2], dtype=np.float32)
+ >>> np.asarray(a, dtype=np.float32) is a
+ True
+ >>> np.asarray(a, dtype=np.float64) is a
+ False
+
+ Contrary to `asanyarray`, ndarray subclasses are not passed through:
+
+ >>> issubclass(np.recarray, np.ndarray)
+ True
+ >>> a = np.array([(1.0, 2), (3.0, 4)], dtype='f4,i4').view(np.recarray)
+ >>> np.asarray(a) is a
+ False
+ >>> np.asanyarray(a) is a
+ True
+
+ """
+ return array(a, dtype, copy=False, order=order)
+
+
+@set_module('numpy')
+def asanyarray(a, dtype=None, order=None):
+ """Convert the input to an ndarray, but pass ndarray subclasses through.
+
+ Parameters
+ ----------
+ a : array_like
+ Input data, in any form that can be converted to an array. This
+ includes scalars, lists, lists of tuples, tuples, tuples of tuples,
+ tuples of lists, and ndarrays.
+ dtype : data-type, optional
+ By default, the data-type is inferred from the input data.
+ order : {'C', 'F'}, optional
+ Whether to use row-major (C-style) or column-major
+ (Fortran-style) memory representation. Defaults to 'C'.
+
+ Returns
+ -------
+ out : ndarray or an ndarray subclass
+ Array interpretation of `a`. If `a` is an ndarray or a subclass
+ of ndarray, it is returned as-is and no copy is performed.
+
+ See Also
+ --------
+ asarray : Similar function which always returns ndarrays.
+ ascontiguousarray : Convert input to a contiguous array.
+ asfarray : Convert input to a floating point ndarray.
+ asfortranarray : Convert input to an ndarray with column-major
+ memory order.
+ asarray_chkfinite : Similar function which checks input for NaNs and
+ Infs.
+ fromiter : Create an array from an iterator.
+ fromfunction : Construct an array by executing a function on grid
+ positions.
+
+ Examples
+ --------
+ Convert a list into an array:
+
+ >>> a = [1, 2]
+ >>> np.asanyarray(a)
+ array([1, 2])
+
+ Instances of `ndarray` subclasses are passed through as-is:
+
+ >>> a = np.array([(1.0, 2), (3.0, 4)], dtype='f4,i4').view(np.recarray)
+ >>> np.asanyarray(a) is a
+ True
+
+ """
+ return array(a, dtype, copy=False, order=order, subok=True)
+
+
+@set_module('numpy')
+def ascontiguousarray(a, dtype=None):
+ """
+ Return a contiguous array (ndim >= 1) in memory (C order).
+
+ Parameters
+ ----------
+ a : array_like
+ Input array.
+ dtype : str or dtype object, optional
+ Data-type of returned array.
+
+ Returns
+ -------
+ out : ndarray
+ Contiguous array of same shape and content as `a`, with type `dtype`
+ if specified.
+
+ See Also
+ --------
+ asfortranarray : Convert input to an ndarray with column-major
+ memory order.
+ require : Return an ndarray that satisfies requirements.
+ ndarray.flags : Information about the memory layout of the array.
+
+ Examples
+ --------
+ >>> x = np.arange(6).reshape(2,3)
+ >>> np.ascontiguousarray(x, dtype=np.float32)
+ array([[0., 1., 2.],
+ [3., 4., 5.]], dtype=float32)
+ >>> x.flags['C_CONTIGUOUS']
+ True
+
+ Note: This function returns an array with at least one-dimension (1-d)
+ so it will not preserve 0-d arrays.
+
+ """
+ return array(a, dtype, copy=False, order='C', ndmin=1)
+
+
+@set_module('numpy')
+def asfortranarray(a, dtype=None):
+ """
+ Return an array (ndim >= 1) laid out in Fortran order in memory.
+
+ Parameters
+ ----------
+ a : array_like
+ Input array.
+ dtype : str or dtype object, optional
+ By default, the data-type is inferred from the input data.
+
+ Returns
+ -------
+ out : ndarray
+ The input `a` in Fortran, or column-major, order.
+
+ See Also
+ --------
+ ascontiguousarray : Convert input to a contiguous (C order) array.
+ asanyarray : Convert input to an ndarray with either row or
+ column-major memory order.
+ require : Return an ndarray that satisfies requirements.
+ ndarray.flags : Information about the memory layout of the array.
+
+ Examples
+ --------
+ >>> x = np.arange(6).reshape(2,3)
+ >>> y = np.asfortranarray(x)
+ >>> x.flags['F_CONTIGUOUS']
+ False
+ >>> y.flags['F_CONTIGUOUS']
+ True
+
+ Note: This function returns an array with at least one-dimension (1-d)
+ so it will not preserve 0-d arrays.
+
+ """
+ return array(a, dtype, copy=False, order='F', ndmin=1)
+
+
+@set_module('numpy')
+def require(a, dtype=None, requirements=None):
+ """
+ Return an ndarray of the provided type that satisfies requirements.
+
+ This function is useful to be sure that an array with the correct flags
+ is returned for passing to compiled code (perhaps through ctypes).
+
+ Parameters
+ ----------
+ a : array_like
+ The object to be converted to a type-and-requirement-satisfying array.
+ dtype : data-type
+ The required data-type. If None preserve the current dtype. If your
+ application requires the data to be in native byteorder, include
+ a byteorder specification as a part of the dtype specification.
+ requirements : str or list of str
+ The requirements list can be any of the following
+
+ * 'F_CONTIGUOUS' ('F') - ensure a Fortran-contiguous array
+ * 'C_CONTIGUOUS' ('C') - ensure a C-contiguous array
+ * 'ALIGNED' ('A') - ensure a data-type aligned array
+ * 'WRITEABLE' ('W') - ensure a writable array
+ * 'OWNDATA' ('O') - ensure an array that owns its own data
+ * 'ENSUREARRAY', ('E') - ensure a base array, instead of a subclass
+
+ Returns
+ -------
+ out : ndarray
+ Array with specified requirements and type if given.
+
+ See Also
+ --------
+ asarray : Convert input to an ndarray.
+ asanyarray : Convert to an ndarray, but pass through ndarray subclasses.
+ ascontiguousarray : Convert input to a contiguous array.
+ asfortranarray : Convert input to an ndarray with column-major
+ memory order.
+ ndarray.flags : Information about the memory layout of the array.
+
+ Notes
+ -----
+ The returned array will be guaranteed to have the listed requirements
+ by making a copy if needed.
+
+ Examples
+ --------
+ >>> x = np.arange(6).reshape(2,3)
+ >>> x.flags
+ C_CONTIGUOUS : True
+ F_CONTIGUOUS : False
+ OWNDATA : False
+ WRITEABLE : True
+ ALIGNED : True
+ WRITEBACKIFCOPY : False
+ UPDATEIFCOPY : False
+
+ >>> y = np.require(x, dtype=np.float32, requirements=['A', 'O', 'W', 'F'])
+ >>> y.flags
+ C_CONTIGUOUS : False
+ F_CONTIGUOUS : True
+ OWNDATA : True
+ WRITEABLE : True
+ ALIGNED : True
+ WRITEBACKIFCOPY : False
+ UPDATEIFCOPY : False
+
+ """
+ possible_flags = {'C': 'C', 'C_CONTIGUOUS': 'C', 'CONTIGUOUS': 'C',
+ 'F': 'F', 'F_CONTIGUOUS': 'F', 'FORTRAN': 'F',
+ 'A': 'A', 'ALIGNED': 'A',
+ 'W': 'W', 'WRITEABLE': 'W',
+ 'O': 'O', 'OWNDATA': 'O',
+ 'E': 'E', 'ENSUREARRAY': 'E'}
+ if not requirements:
+ return asanyarray(a, dtype=dtype)
+ else:
+ requirements = {possible_flags[x.upper()] for x in requirements}
+
+ if 'E' in requirements:
+ requirements.remove('E')
+ subok = False
+ else:
+ subok = True
+
+ order = 'A'
+ if requirements >= {'C', 'F'}:
+ raise ValueError('Cannot specify both "C" and "F" order')
+ elif 'F' in requirements:
+ order = 'F'
+ requirements.remove('F')
+ elif 'C' in requirements:
+ order = 'C'
+ requirements.remove('C')
+
+ arr = array(a, dtype=dtype, order=order, copy=False, subok=subok)
+
+ for prop in requirements:
+ if not arr.flags[prop]:
+ arr = arr.copy(order)
+ break
+ return arr
diff --git a/numpy/core/_dtype.py b/numpy/core/_dtype.py
index 26c44eaaf..df1ff180e 100644
--- a/numpy/core/_dtype.py
+++ b/numpy/core/_dtype.py
@@ -5,9 +5,44 @@ String handling is much easier to do correctly in python.
"""
from __future__ import division, absolute_import, print_function
+import sys
+
import numpy as np
+_kind_to_stem = {
+ 'u': 'uint',
+ 'i': 'int',
+ 'c': 'complex',
+ 'f': 'float',
+ 'b': 'bool',
+ 'V': 'void',
+ 'O': 'object',
+ 'M': 'datetime',
+ 'm': 'timedelta'
+}
+if sys.version_info[0] >= 3:
+ _kind_to_stem.update({
+ 'S': 'bytes',
+ 'U': 'str'
+ })
+else:
+ _kind_to_stem.update({
+ 'S': 'string',
+ 'U': 'unicode'
+ })
+
+
+def _kind_name(dtype):
+ try:
+ return _kind_to_stem[dtype.kind]
+ except KeyError:
+ raise RuntimeError(
+ "internal dtype error, unknown kind {!r}"
+ .format(dtype.kind)
+ )
+
+
def __str__(dtype):
if dtype.fields is not None:
return _struct_str(dtype, include_align=True)
@@ -103,7 +138,9 @@ def _scalar_str(dtype, short):
else:
return "'%sU%d'" % (byteorder, dtype.itemsize / 4)
- elif dtype.type == np.void:
+ # unlike the other types, subclasses of void are preserved - but
+ # historically the repr does not actually reveal the subclass
+ elif issubclass(dtype.type, np.void):
if _isunsized(dtype):
return "'V'"
else:
@@ -122,20 +159,7 @@ def _scalar_str(dtype, short):
# Longer repr, like 'float64'
else:
- kindstrs = {
- 'u': "uint",
- 'i': "int",
- 'f': "float",
- 'c': "complex"
- }
- try:
- kindstr = kindstrs[dtype.kind]
- except KeyError:
- raise RuntimeError(
- "internal dtype repr error, unknown kind {!r}"
- .format(dtype.kind)
- )
- return "'%s%d'" % (kindstr, 8*dtype.itemsize)
+ return "'%s%d'" % (_kind_name(dtype), 8*dtype.itemsize)
elif dtype.isbuiltin == 2:
return dtype.type.__name__
@@ -228,7 +252,7 @@ def _is_packed(dtype):
from a list of the field names and dtypes with no additional
dtype parameters.
- Duplicates the C `is_dtype_struct_simple_unaligned_layout` functio.
+ Duplicates the C `is_dtype_struct_simple_unaligned_layout` function.
"""
total_offset = 0
for name in dtype.names:
@@ -292,26 +316,39 @@ def _subarray_str(dtype):
)
+def _name_includes_bit_suffix(dtype):
+ if dtype.type == np.object_:
+ # pointer size varies by system, best to omit it
+ return False
+ elif dtype.type == np.bool_:
+ # implied
+ return False
+ elif np.issubdtype(dtype, np.flexible) and _isunsized(dtype):
+ # unspecified
+ return False
+ else:
+ return True
+
+
def _name_get(dtype):
- # provides dtype.name.__get__
+ # provides dtype.name.__get__, documented as returning a "bit name"
if dtype.isbuiltin == 2:
# user dtypes don't promise to do anything special
return dtype.type.__name__
- # Builtin classes are documented as returning a "bit name"
- name = dtype.type.__name__
-
- # handle bool_, str_, etc
- if name[-1] == '_':
- name = name[:-1]
+ if issubclass(dtype.type, np.void):
+ # historically, void subclasses preserve their name, eg `record64`
+ name = dtype.type.__name__
+ else:
+ name = _kind_name(dtype)
- # append bit counts to str, unicode, and void
- if np.issubdtype(dtype, np.flexible) and not _isunsized(dtype):
+ # append bit counts
+ if _name_includes_bit_suffix(dtype):
name += "{}".format(dtype.itemsize * 8)
# append metadata to datetimes
- elif dtype.type in (np.datetime64, np.timedelta64):
+ if dtype.type in (np.datetime64, np.timedelta64):
name += _datetime_metadata_str(dtype)
return name
diff --git a/numpy/core/_dtype_ctypes.py b/numpy/core/_dtype_ctypes.py
new file mode 100644
index 000000000..708241289
--- /dev/null
+++ b/numpy/core/_dtype_ctypes.py
@@ -0,0 +1,113 @@
+"""
+Conversion from ctypes to dtype.
+
+In an ideal world, we could achieve this through the PEP3118 buffer protocol,
+something like::
+
+ def dtype_from_ctypes_type(t):
+ # needed to ensure that the shape of `t` is within memoryview.format
+ class DummyStruct(ctypes.Structure):
+ _fields_ = [('a', t)]
+
+ # empty to avoid memory allocation
+ ctype_0 = (DummyStruct * 0)()
+ mv = memoryview(ctype_0)
+
+ # convert the struct, and slice back out the field
+ return _dtype_from_pep3118(mv.format)['a']
+
+Unfortunately, this fails because:
+
+* ctypes cannot handle length-0 arrays with PEP3118 (bpo-32782)
+* PEP3118 cannot represent unions, but both numpy and ctypes can
+* ctypes cannot handle big-endian structs with PEP3118 (bpo-32780)
+"""
+import _ctypes
+import ctypes
+
+import numpy as np
+
+
+def _from_ctypes_array(t):
+ return np.dtype((dtype_from_ctypes_type(t._type_), (t._length_,)))
+
+
+def _from_ctypes_structure(t):
+ for item in t._fields_:
+ if len(item) > 2:
+ raise TypeError(
+ "ctypes bitfields have no dtype equivalent")
+
+ if hasattr(t, "_pack_"):
+ formats = []
+ offsets = []
+ names = []
+ current_offset = 0
+ for fname, ftyp in t._fields_:
+ names.append(fname)
+ formats.append(dtype_from_ctypes_type(ftyp))
+ # Each type has a default offset, this is platform dependent for some types.
+ effective_pack = min(t._pack_, ctypes.alignment(ftyp))
+ current_offset = ((current_offset + effective_pack - 1) // effective_pack) * effective_pack
+ offsets.append(current_offset)
+ current_offset += ctypes.sizeof(ftyp)
+
+ return np.dtype(dict(
+ formats=formats,
+ offsets=offsets,
+ names=names,
+ itemsize=ctypes.sizeof(t)))
+ else:
+ fields = []
+ for fname, ftyp in t._fields_:
+ fields.append((fname, dtype_from_ctypes_type(ftyp)))
+
+ # by default, ctypes structs are aligned
+ return np.dtype(fields, align=True)
+
+
+def _from_ctypes_scalar(t):
+ """
+ Return the dtype type with endianness included if it's the case
+ """
+ if getattr(t, '__ctype_be__', None) is t:
+ return np.dtype('>' + t._type_)
+ elif getattr(t, '__ctype_le__', None) is t:
+ return np.dtype('<' + t._type_)
+ else:
+ return np.dtype(t._type_)
+
+
+def _from_ctypes_union(t):
+ formats = []
+ offsets = []
+ names = []
+ for fname, ftyp in t._fields_:
+ names.append(fname)
+ formats.append(dtype_from_ctypes_type(ftyp))
+ offsets.append(0) # Union fields are offset to 0
+
+ return np.dtype(dict(
+ formats=formats,
+ offsets=offsets,
+ names=names,
+ itemsize=ctypes.sizeof(t)))
+
+
+def dtype_from_ctypes_type(t):
+ """
+ Construct a dtype object from a ctypes type
+ """
+ if issubclass(t, _ctypes.Array):
+ return _from_ctypes_array(t)
+ elif issubclass(t, _ctypes._Pointer):
+ raise TypeError("ctypes pointers have no dtype equivalent")
+ elif issubclass(t, _ctypes.Structure):
+ return _from_ctypes_structure(t)
+ elif issubclass(t, _ctypes.Union):
+ return _from_ctypes_union(t)
+ elif isinstance(getattr(t, '_type_', None), str):
+ return _from_ctypes_scalar(t)
+ else:
+ raise NotImplementedError(
+ "Unknown ctypes type {}".format(t.__name__))
diff --git a/numpy/core/_exceptions.py b/numpy/core/_exceptions.py
new file mode 100644
index 000000000..88a45561f
--- /dev/null
+++ b/numpy/core/_exceptions.py
@@ -0,0 +1,200 @@
+"""
+Various richly-typed exceptions, that also help us deal with string formatting
+in python where it's easier.
+
+By putting the formatting in `__str__`, we also avoid paying the cost for
+users who silence the exceptions.
+"""
+from numpy.core.overrides import set_module
+
+def _unpack_tuple(tup):
+ if len(tup) == 1:
+ return tup[0]
+ else:
+ return tup
+
+
+def _display_as_base(cls):
+ """
+ A decorator that makes an exception class look like its base.
+
+ We use this to hide subclasses that are implementation details - the user
+ should catch the base type, which is what the traceback will show them.
+
+ Classes decorated with this decorator are subject to removal without a
+ deprecation warning.
+ """
+ assert issubclass(cls, Exception)
+ cls.__name__ = cls.__base__.__name__
+ cls.__qualname__ = cls.__base__.__qualname__
+ set_module(cls.__base__.__module__)(cls)
+ return cls
+
+
+class UFuncTypeError(TypeError):
+ """ Base class for all ufunc exceptions """
+ def __init__(self, ufunc):
+ self.ufunc = ufunc
+
+
+@_display_as_base
+class _UFuncBinaryResolutionError(UFuncTypeError):
+ """ Thrown when a binary resolution fails """
+ def __init__(self, ufunc, dtypes):
+ super().__init__(ufunc)
+ self.dtypes = tuple(dtypes)
+ assert len(self.dtypes) == 2
+
+ def __str__(self):
+ return (
+ "ufunc {!r} cannot use operands with types {!r} and {!r}"
+ ).format(
+ self.ufunc.__name__, *self.dtypes
+ )
+
+
+@_display_as_base
+class _UFuncNoLoopError(UFuncTypeError):
+ """ Thrown when a ufunc loop cannot be found """
+ def __init__(self, ufunc, dtypes):
+ super().__init__(ufunc)
+ self.dtypes = tuple(dtypes)
+
+ def __str__(self):
+ return (
+ "ufunc {!r} did not contain a loop with signature matching types "
+ "{!r} -> {!r}"
+ ).format(
+ self.ufunc.__name__,
+ _unpack_tuple(self.dtypes[:self.ufunc.nin]),
+ _unpack_tuple(self.dtypes[self.ufunc.nin:])
+ )
+
+
+@_display_as_base
+class _UFuncCastingError(UFuncTypeError):
+ def __init__(self, ufunc, casting, from_, to):
+ super().__init__(ufunc)
+ self.casting = casting
+ self.from_ = from_
+ self.to = to
+
+
+@_display_as_base
+class _UFuncInputCastingError(_UFuncCastingError):
+ """ Thrown when a ufunc input cannot be casted """
+ def __init__(self, ufunc, casting, from_, to, i):
+ super().__init__(ufunc, casting, from_, to)
+ self.in_i = i
+
+ def __str__(self):
+ # only show the number if more than one input exists
+ i_str = "{} ".format(self.in_i) if self.ufunc.nin != 1 else ""
+ return (
+ "Cannot cast ufunc {!r} input {}from {!r} to {!r} with casting "
+ "rule {!r}"
+ ).format(
+ self.ufunc.__name__, i_str, self.from_, self.to, self.casting
+ )
+
+
+@_display_as_base
+class _UFuncOutputCastingError(_UFuncCastingError):
+ """ Thrown when a ufunc output cannot be casted """
+ def __init__(self, ufunc, casting, from_, to, i):
+ super().__init__(ufunc, casting, from_, to)
+ self.out_i = i
+
+ def __str__(self):
+ # only show the number if more than one output exists
+ i_str = "{} ".format(self.out_i) if self.ufunc.nout != 1 else ""
+ return (
+ "Cannot cast ufunc {!r} output {}from {!r} to {!r} with casting "
+ "rule {!r}"
+ ).format(
+ self.ufunc.__name__, i_str, self.from_, self.to, self.casting
+ )
+
+
+# Exception used in shares_memory()
+@set_module('numpy')
+class TooHardError(RuntimeError):
+ pass
+
+
+@set_module('numpy')
+class AxisError(ValueError, IndexError):
+ """ Axis supplied was invalid. """
+ def __init__(self, axis, ndim=None, msg_prefix=None):
+ # single-argument form just delegates to base class
+ if ndim is None and msg_prefix is None:
+ msg = axis
+
+ # do the string formatting here, to save work in the C code
+ else:
+ msg = ("axis {} is out of bounds for array of dimension {}"
+ .format(axis, ndim))
+ if msg_prefix is not None:
+ msg = "{}: {}".format(msg_prefix, msg)
+
+ super(AxisError, self).__init__(msg)
+
+
+@_display_as_base
+class _ArrayMemoryError(MemoryError):
+ """ Thrown when an array cannot be allocated"""
+ def __init__(self, shape, dtype):
+ self.shape = shape
+ self.dtype = dtype
+
+ @property
+ def _total_size(self):
+ num_bytes = self.dtype.itemsize
+ for dim in self.shape:
+ num_bytes *= dim
+ return num_bytes
+
+ @staticmethod
+ def _size_to_string(num_bytes):
+ """ Convert a number of bytes into a binary size string """
+ import math
+
+ # https://en.wikipedia.org/wiki/Binary_prefix
+ LOG2_STEP = 10
+ STEP = 1024
+ units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']
+
+ unit_i = max(num_bytes.bit_length() - 1, 1) // LOG2_STEP
+ unit_val = 1 << (unit_i * LOG2_STEP)
+ n_units = num_bytes / unit_val
+ del unit_val
+
+ # ensure we pick a unit that is correct after rounding
+ if round(n_units) == STEP:
+ unit_i += 1
+ n_units /= STEP
+
+ # deal with sizes so large that we don't have units for them
+ if unit_i >= len(units):
+ new_unit_i = len(units) - 1
+ n_units *= 1 << ((unit_i - new_unit_i) * LOG2_STEP)
+ unit_i = new_unit_i
+
+ unit_name = units[unit_i]
+ # format with a sensible number of digits
+ if unit_i == 0:
+ # no decimal point on bytes
+ return '{:.0f} {}'.format(n_units, unit_name)
+ elif round(n_units) < 1000:
+ # 3 significant figures, if none are dropped to the left of the .
+ return '{:#.3g} {}'.format(n_units, unit_name)
+ else:
+ # just give all the digits otherwise
+ return '{:#.0f} {}'.format(n_units, unit_name)
+
+ def __str__(self):
+ size_str = self._size_to_string(self._total_size)
+ return (
+ "Unable to allocate {} for an array with shape {} and data type {}"
+ .format(size_str, self.shape, self.dtype)
+ )
diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py
index c4d967dc2..b0ea603e1 100644
--- a/numpy/core/_internal.py
+++ b/numpy/core/_internal.py
@@ -8,6 +8,7 @@ from __future__ import division, absolute_import, print_function
import re
import sys
+import platform
from numpy.compat import unicode
from .multiarray import dtype, array, ndarray
@@ -16,6 +17,8 @@ try:
except ImportError:
ctypes = None
+IS_PYPY = platform.python_implementation() == 'PyPy'
+
if (sys.byteorder == 'little'):
_nbo = b'<'
else:
@@ -143,7 +146,7 @@ def _reconstruct(subtype, shape, dtype):
# format_re was originally from numarray by J. Todd Miller
format_re = re.compile(br'(?P<order1>[<>|=]?)'
- br'(?P<repeats> *[(]?[ ,0-9L]*[)]? *)'
+ br'(?P<repeats> *[(]?[ ,0-9]*[)]? *)'
br'(?P<order2>[<>|=]?)'
br'(?P<dtype>[A-Za-z0-9.?]*(?:\[[a-zA-Z0-9,.]+\])?)')
sep_re = re.compile(br'\s*,\s*')
@@ -237,19 +240,68 @@ _getintp_ctype.cache = None
class _missing_ctypes(object):
def cast(self, num, obj):
- return num
+ return num.value
+
+ class c_void_p(object):
+ def __init__(self, ptr):
+ self.value = ptr
+
+
+class _unsafe_first_element_pointer(object):
+ """
+ Helper to allow viewing an array as a ctypes pointer to the first element
+
+ This avoids:
+ * dealing with strides
+ * `.view` rejecting object-containing arrays
+ * `memoryview` not supporting overlapping fields
+ """
+ def __init__(self, arr):
+ self.base = arr
+
+ @property
+ def __array_interface__(self):
+ i = dict(
+ shape=(),
+ typestr='|V0',
+ data=(self.base.__array_interface__['data'][0], False),
+ strides=(),
+ version=3,
+ )
+ return i
+
+
+def _get_void_ptr(arr):
+ """
+ Get a `ctypes.c_void_p` to arr.data, that keeps a reference to the array
+ """
+ import numpy as np
+ # convert to a 0d array that has a data pointer referrign to the start
+ # of arr. This holds a reference to arr.
+ simple_arr = np.asarray(_unsafe_first_element_pointer(arr))
+
+ # create a `char[0]` using the same memory.
+ c_arr = (ctypes.c_char * 0).from_buffer(simple_arr)
+
+ # finally cast to void*
+ return ctypes.cast(ctypes.pointer(c_arr), ctypes.c_void_p)
- def c_void_p(self, num):
- return num
class _ctypes(object):
def __init__(self, array, ptr=None):
+ self._arr = array
+
if ctypes:
self._ctypes = ctypes
+ # get a void pointer to the buffer, which keeps the array alive
+ self._data = _get_void_ptr(array)
+ assert self._data.value == ptr
else:
+ # fake a pointer-like object that holds onto the reference
self._ctypes = _missing_ctypes()
- self._arr = array
- self._data = ptr
+ self._data = self._ctypes.c_void_p(ptr)
+ self._data._objects = array
+
if self._arr.ndim == 0:
self._zerod = True
else:
@@ -262,6 +314,8 @@ class _ctypes(object):
``self.data_as(ctypes.c_void_p)``. Perhaps you want to use the data as a
pointer to a ctypes array of floating-point data:
``self.data_as(ctypes.POINTER(ctypes.c_double))``.
+
+ The returned pointer will keep a reference to the array.
"""
return self._ctypes.cast(self._data, obj)
@@ -283,7 +337,8 @@ class _ctypes(object):
return None
return (obj*self._arr.ndim)(*self._arr.strides)
- def get_data(self):
+ @property
+ def data(self):
"""
A pointer to the memory area of the array as a Python integer.
This memory area may contain data that is not aligned, or not in correct
@@ -292,10 +347,16 @@ class _ctypes(object):
attribute to arbitrary C-code to avoid trouble that can include Python
crashing. User Beware! The value of this attribute is exactly the same
as ``self._array_interface_['data'][0]``.
+
+ Note that unlike `data_as`, a reference will not be kept to the array:
+ code like ``ctypes.c_void_p((a + b).ctypes.data)`` will result in a
+ pointer to a deallocated array, and should be spelt
+ ``(a + b).ctypes.data_as(ctypes.c_void_p)``
"""
- return self._data
+ return self._data.value
- def get_shape(self):
+ @property
+ def shape(self):
"""
(c_intp*self.ndim): A ctypes array of length self.ndim where
the basetype is the C-integer corresponding to ``dtype('p')`` on this
@@ -306,7 +367,8 @@ class _ctypes(object):
"""
return self.shape_as(_getintp_ctype())
- def get_strides(self):
+ @property
+ def strides(self):
"""
(c_intp*self.ndim): A ctypes array of length self.ndim where
the basetype is the same as for the shape attribute. This ctypes array
@@ -316,13 +378,20 @@ class _ctypes(object):
"""
return self.strides_as(_getintp_ctype())
- def get_as_parameter(self):
- return self._ctypes.c_void_p(self._data)
+ @property
+ def _as_parameter_(self):
+ """
+ Overrides the ctypes semi-magic method
- data = property(get_data)
- shape = property(get_shape)
- strides = property(get_strides)
- _as_parameter_ = property(get_as_parameter, None, doc="_as parameter_")
+ Enables `c_func(some_array.ctypes)`
+ """
+ return self._data
+
+ # kept for compatibility
+ get_data = data.fget
+ get_shape = shape.fget
+ get_strides = strides.fget
+ get_as_parameter = _as_parameter_.fget
def _newnames(datatype, order):
@@ -390,7 +459,7 @@ def _getfield_is_safe(oldtype, newtype, offset):
if newtype.hasobject or oldtype.hasobject:
if offset == 0 and newtype == oldtype:
return
- if oldtype.names:
+ if oldtype.names is not None:
for name in oldtype.names:
if (oldtype.fields[name][1] == offset and
oldtype.fields[name][0] == newtype):
@@ -482,6 +551,12 @@ _pep3118_standard_map = {
}
_pep3118_standard_typechars = ''.join(_pep3118_standard_map.keys())
+_pep3118_unsupported_map = {
+ 'u': 'UCS-2 strings',
+ '&': 'pointers',
+ 't': 'bitfields',
+ 'X': 'function pointers',
+}
class _Stream(object):
def __init__(self, s):
@@ -593,6 +668,11 @@ def __dtype_from_pep3118(stream, is_subdtype):
stream.byteorder, stream.byteorder)
value = dtype(numpy_byteorder + dtypechar)
align = value.alignment
+ elif stream.next in _pep3118_unsupported_map:
+ desc = _pep3118_unsupported_map[stream.next]
+ raise NotImplementedError(
+ "Unrepresentable PEP 3118 data type {!r} ({})"
+ .format(stream.next, desc))
else:
raise ValueError("Unknown PEP 3118 data type specifier %r" % stream.s)
@@ -717,27 +797,6 @@ def _gcd(a, b):
def _lcm(a, b):
return a // _gcd(a, b) * b
-# Exception used in shares_memory()
-class TooHardError(RuntimeError):
- pass
-
-class AxisError(ValueError, IndexError):
- """ Axis supplied was invalid. """
- def __init__(self, axis, ndim=None, msg_prefix=None):
- # single-argument form just delegates to base class
- if ndim is None and msg_prefix is None:
- msg = axis
-
- # do the string formatting here, to save work in the C code
- else:
- msg = ("axis {} is out of bounds for array of dimension {}"
- .format(axis, ndim))
- if msg_prefix is not None:
- msg = "{}: {}".format(msg_prefix, msg)
-
- super(AxisError, self).__init__(msg)
-
-
def array_ufunc_errmsg_formatter(dummy, ufunc, method, *inputs, **kwargs):
""" Format the error message for when __array_ufunc__ gives up. """
args_string = ', '.join(['{!r}'.format(arg) for arg in inputs] +
@@ -750,6 +809,13 @@ def array_ufunc_errmsg_formatter(dummy, ufunc, method, *inputs, **kwargs):
.format(ufunc, method, args_string, types_string))
+def array_function_errmsg_formatter(public_api, types):
+ """ Format the error message for when __array_ufunc__ gives up. """
+ func_name = '{}.{}'.format(public_api.__module__, public_api.__name__)
+ return ("no implementation found for '{}' on types that implement "
+ '__array_function__: {}'.format(func_name, list(types)))
+
+
def _ufunc_doc_signature_formatter(ufunc):
"""
Builds a signature string which resembles PEP 457
@@ -796,13 +862,18 @@ def _ufunc_doc_signature_formatter(ufunc):
)
-def _is_from_ctypes(obj):
- # determine if an object comes from ctypes, in order to work around
+def npy_ctypes_check(cls):
+ # determine if a class comes from ctypes, in order to work around
# a bug in the buffer protocol for those objects, bpo-10746
try:
# ctypes class are new-style, so have an __mro__. This probably fails
# for ctypes classes with multiple inheritance.
- ctype_base = type(obj).__mro__[-2]
+ if IS_PYPY:
+ # (..., _ctypes.basics._CData, Bufferable, object)
+ ctype_base = cls.__mro__[-3]
+ else:
+ # # (..., _ctypes._CData, object)
+ ctype_base = cls.__mro__[-2]
# right now, they're part of the _ctypes module
return 'ctypes' in ctype_base.__module__
except Exception:
diff --git a/numpy/core/_methods.py b/numpy/core/_methods.py
index 8974f0ce1..269e509b8 100644
--- a/numpy/core/_methods.py
+++ b/numpy/core/_methods.py
@@ -9,9 +9,11 @@ import warnings
from numpy.core import multiarray as mu
from numpy.core import umath as um
-from numpy.core.numeric import asanyarray
+from numpy.core._asarray import asanyarray
from numpy.core import numerictypes as nt
+from numpy.core import _exceptions
from numpy._globals import _NoValue
+from numpy.compat import pickle, os_fspath, contextlib_nullcontext
# save those O(100) nanoseconds!
umr_maximum = um.maximum.reduce
@@ -24,20 +26,20 @@ umr_all = um.logical_and.reduce
# avoid keyword arguments to speed up parsing, saves about 15%-20% for very
# small reductions
def _amax(a, axis=None, out=None, keepdims=False,
- initial=_NoValue):
- return umr_maximum(a, axis, None, out, keepdims, initial)
+ initial=_NoValue, where=True):
+ return umr_maximum(a, axis, None, out, keepdims, initial, where)
def _amin(a, axis=None, out=None, keepdims=False,
- initial=_NoValue):
- return umr_minimum(a, axis, None, out, keepdims, initial)
+ initial=_NoValue, where=True):
+ return umr_minimum(a, axis, None, out, keepdims, initial, where)
def _sum(a, axis=None, dtype=None, out=None, keepdims=False,
- initial=_NoValue):
- return umr_sum(a, axis, dtype, out, keepdims, initial)
+ initial=_NoValue, where=True):
+ return umr_sum(a, axis, dtype, out, keepdims, initial, where)
def _prod(a, axis=None, dtype=None, out=None, keepdims=False,
- initial=_NoValue):
- return umr_prod(a, axis, dtype, out, keepdims, initial)
+ initial=_NoValue, where=True):
+ return umr_prod(a, axis, dtype, out, keepdims, initial, where)
def _any(a, axis=None, dtype=None, out=None, keepdims=False):
return umr_any(a, axis, dtype, out, keepdims)
@@ -55,6 +57,80 @@ def _count_reduce_items(arr, axis):
items *= arr.shape[ax]
return items
+# Numpy 1.17.0, 2019-02-24
+# Various clip behavior deprecations, marked with _clip_dep as a prefix.
+
+def _clip_dep_is_scalar_nan(a):
+ # guarded to protect circular imports
+ from numpy.core.fromnumeric import ndim
+ if ndim(a) != 0:
+ return False
+ try:
+ return um.isnan(a)
+ except TypeError:
+ return False
+
+def _clip_dep_is_byte_swapped(a):
+ if isinstance(a, mu.ndarray):
+ return not a.dtype.isnative
+ return False
+
+def _clip_dep_invoke_with_casting(ufunc, *args, out=None, casting=None, **kwargs):
+ # normal path
+ if casting is not None:
+ return ufunc(*args, out=out, casting=casting, **kwargs)
+
+ # try to deal with broken casting rules
+ try:
+ return ufunc(*args, out=out, **kwargs)
+ except _exceptions._UFuncOutputCastingError as e:
+ # Numpy 1.17.0, 2019-02-24
+ warnings.warn(
+ "Converting the output of clip from {!r} to {!r} is deprecated. "
+ "Pass `casting=\"unsafe\"` explicitly to silence this warning, or "
+ "correct the type of the variables.".format(e.from_, e.to),
+ DeprecationWarning,
+ stacklevel=2
+ )
+ return ufunc(*args, out=out, casting="unsafe", **kwargs)
+
+def _clip(a, min=None, max=None, out=None, *, casting=None, **kwargs):
+ if min is None and max is None:
+ raise ValueError("One of max or min must be given")
+
+ # Numpy 1.17.0, 2019-02-24
+ # This deprecation probably incurs a substantial slowdown for small arrays,
+ # it will be good to get rid of it.
+ if not _clip_dep_is_byte_swapped(a) and not _clip_dep_is_byte_swapped(out):
+ using_deprecated_nan = False
+ if _clip_dep_is_scalar_nan(min):
+ min = -float('inf')
+ using_deprecated_nan = True
+ if _clip_dep_is_scalar_nan(max):
+ max = float('inf')
+ using_deprecated_nan = True
+ if using_deprecated_nan:
+ warnings.warn(
+ "Passing `np.nan` to mean no clipping in np.clip has always "
+ "been unreliable, and is now deprecated. "
+ "In future, this will always return nan, like it already does "
+ "when min or max are arrays that contain nan. "
+ "To skip a bound, pass either None or an np.inf of an "
+ "appropriate sign.",
+ DeprecationWarning,
+ stacklevel=2
+ )
+
+ if min is None:
+ return _clip_dep_invoke_with_casting(
+ um.minimum, a, max, out=out, casting=casting, **kwargs)
+ elif max is None:
+ return _clip_dep_invoke_with_casting(
+ um.maximum, a, min, out=out, casting=casting, **kwargs)
+ else:
+ return _clip_dep_invoke_with_casting(
+ um.clip, a, min, max, out=out, casting=casting, **kwargs)
+
def _mean(a, axis=None, dtype=None, out=None, keepdims=False):
arr = asanyarray(a)
@@ -115,10 +191,11 @@ def _var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False):
# Note that x may not be inexact and that we need it to be an array,
# not a scalar.
x = asanyarray(arr - arrmean)
- if issubclass(arr.dtype.type, nt.complexfloating):
- x = um.multiply(x, um.conjugate(x), out=x).real
- else:
+ if issubclass(arr.dtype.type, (nt.floating, nt.integer)):
x = um.multiply(x, x, out=x)
+ else:
+ x = um.multiply(x, um.conjugate(x), out=x).real
+
ret = umr_sum(x, axis, dtype, out, keepdims)
# Compute degrees of freedom and make sure it is not negative.
@@ -155,17 +232,13 @@ def _ptp(a, axis=None, out=None, keepdims=False):
out
)
-_NDARRAY_ARRAY_FUNCTION = mu.ndarray.__array_function__
-
-def _array_function(self, func, types, args, kwargs):
- # TODO: rewrite this in C
- # Cannot handle items that have __array_function__ other than our own.
- for t in types:
- if t is not mu.ndarray:
- method = getattr(t, '__array_function__', _NDARRAY_ARRAY_FUNCTION)
- if method is not _NDARRAY_ARRAY_FUNCTION:
- return NotImplemented
+def _dump(self, file, protocol=2):
+ if hasattr(file, 'write'):
+ ctx = contextlib_nullcontext(file)
+ else:
+ ctx = open(os_fspath(file), "wb")
+ with ctx as f:
+ pickle.dump(self, f, protocol=protocol)
- # Arguments contain no overrides, so we can safely call the
- # overloaded function again.
- return func(*args, **kwargs)
+def _dumps(self, protocol=2):
+ return pickle.dumps(self, protocol=protocol)
diff --git a/numpy/core/_type_aliases.py b/numpy/core/_type_aliases.py
index 8d629aa07..d6e1a1fb7 100644
--- a/numpy/core/_type_aliases.py
+++ b/numpy/core/_type_aliases.py
@@ -29,6 +29,7 @@ from numpy.compat import unicode
from numpy._globals import VisibleDeprecationWarning
from numpy.core._string_helpers import english_lower, english_capitalize
from numpy.core.multiarray import typeinfo, dtype
+from numpy.core._dtype import _kind_name
sctypeDict = {} # Contains all leaf-node scalar types with aliases
@@ -59,29 +60,7 @@ for k, v in typeinfo.items():
else:
_concrete_typeinfo[k] = v
-_concrete_types = set(v.type for k, v in _concrete_typeinfo.items())
-
-_kind_to_stem = {
- 'u': 'uint',
- 'i': 'int',
- 'c': 'complex',
- 'f': 'float',
- 'b': 'bool',
- 'V': 'void',
- 'O': 'object',
- 'M': 'datetime',
- 'm': 'timedelta'
-}
-if sys.version_info[0] >= 3:
- _kind_to_stem.update({
- 'S': 'bytes',
- 'U': 'str'
- })
-else:
- _kind_to_stem.update({
- 'S': 'string',
- 'U': 'unicode'
- })
+_concrete_types = {v.type for k, v in _concrete_typeinfo.items()}
def _bits_of(obj):
@@ -100,8 +79,9 @@ def _bits_of(obj):
def bitname(obj):
"""Return a bit-width name for a given type object"""
bits = _bits_of(obj)
- char = dtype(obj).kind
- base = _kind_to_stem[char]
+ dt = dtype(obj)
+ char = dt.kind
+ base = _kind_name(dt)
if base == 'object':
bits = 0
diff --git a/numpy/core/_ufunc_config.py b/numpy/core/_ufunc_config.py
new file mode 100644
index 000000000..c3951cc09
--- /dev/null
+++ b/numpy/core/_ufunc_config.py
@@ -0,0 +1,458 @@
+"""
+Functions for changing global ufunc configuration
+
+This provides helpers which wrap `umath.geterrobj` and `umath.seterrobj`
+"""
+from __future__ import division, absolute_import, print_function
+
+try:
+ # Accessing collections abstract classes from collections
+ # has been deprecated since Python 3.3
+ import collections.abc as collections_abc
+except ImportError:
+ import collections as collections_abc
+import contextlib
+
+from .overrides import set_module
+from .umath import (
+ UFUNC_BUFSIZE_DEFAULT,
+ ERR_IGNORE, ERR_WARN, ERR_RAISE, ERR_CALL, ERR_PRINT, ERR_LOG, ERR_DEFAULT,
+ SHIFT_DIVIDEBYZERO, SHIFT_OVERFLOW, SHIFT_UNDERFLOW, SHIFT_INVALID,
+)
+from . import umath
+
+__all__ = [
+ "seterr", "geterr", "setbufsize", "getbufsize", "seterrcall", "geterrcall",
+ "errstate",
+]
+
+_errdict = {"ignore": ERR_IGNORE,
+ "warn": ERR_WARN,
+ "raise": ERR_RAISE,
+ "call": ERR_CALL,
+ "print": ERR_PRINT,
+ "log": ERR_LOG}
+
+_errdict_rev = {value: key for key, value in _errdict.items()}
+
+
+@set_module('numpy')
+def seterr(all=None, divide=None, over=None, under=None, invalid=None):
+ """
+ Set how floating-point errors are handled.
+
+ Note that operations on integer scalar types (such as `int16`) are
+ handled like floating point, and are affected by these settings.
+
+ Parameters
+ ----------
+ all : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
+ Set treatment for all types of floating-point errors at once:
+
+ - ignore: Take no action when the exception occurs.
+ - warn: Print a `RuntimeWarning` (via the Python `warnings` module).
+ - raise: Raise a `FloatingPointError`.
+ - call: Call a function specified using the `seterrcall` function.
+ - print: Print a warning directly to ``stdout``.
+ - log: Record error in a Log object specified by `seterrcall`.
+
+ The default is not to change the current behavior.
+ divide : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
+ Treatment for division by zero.
+ over : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
+ Treatment for floating-point overflow.
+ under : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
+ Treatment for floating-point underflow.
+ invalid : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
+ Treatment for invalid floating-point operation.
+
+ Returns
+ -------
+ old_settings : dict
+ Dictionary containing the old settings.
+
+ See also
+ --------
+ seterrcall : Set a callback function for the 'call' mode.
+ geterr, geterrcall, errstate
+
+ Notes
+ -----
+ The floating-point exceptions are defined in the IEEE 754 standard [1]_:
+
+ - Division by zero: infinite result obtained from finite numbers.
+ - Overflow: result too large to be expressed.
+ - Underflow: result so close to zero that some precision
+ was lost.
+ - Invalid operation: result is not an expressible number, typically
+ indicates that a NaN was produced.
+
+ .. [1] https://en.wikipedia.org/wiki/IEEE_754
+
+ Examples
+ --------
+ >>> old_settings = np.seterr(all='ignore') #seterr to known value
+ >>> np.seterr(over='raise')
+ {'divide': 'ignore', 'over': 'ignore', 'under': 'ignore', 'invalid': 'ignore'}
+ >>> np.seterr(**old_settings) # reset to default
+ {'divide': 'ignore', 'over': 'raise', 'under': 'ignore', 'invalid': 'ignore'}
+
+ >>> np.int16(32000) * np.int16(3)
+ 30464
+ >>> old_settings = np.seterr(all='warn', over='raise')
+ >>> np.int16(32000) * np.int16(3)
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ FloatingPointError: overflow encountered in short_scalars
+
+ >>> from collections import OrderedDict
+ >>> old_settings = np.seterr(all='print')
+ >>> OrderedDict(np.geterr())
+ OrderedDict([('divide', 'print'), ('over', 'print'), ('under', 'print'), ('invalid', 'print')])
+ >>> np.int16(32000) * np.int16(3)
+ 30464
+
+ """
+
+ pyvals = umath.geterrobj()
+ old = geterr()
+
+ if divide is None:
+ divide = all or old['divide']
+ if over is None:
+ over = all or old['over']
+ if under is None:
+ under = all or old['under']
+ if invalid is None:
+ invalid = all or old['invalid']
+
+ maskvalue = ((_errdict[divide] << SHIFT_DIVIDEBYZERO) +
+ (_errdict[over] << SHIFT_OVERFLOW) +
+ (_errdict[under] << SHIFT_UNDERFLOW) +
+ (_errdict[invalid] << SHIFT_INVALID))
+
+ pyvals[1] = maskvalue
+ umath.seterrobj(pyvals)
+ return old
+
+
+@set_module('numpy')
+def geterr():
+ """
+ Get the current way of handling floating-point errors.
+
+ Returns
+ -------
+ res : dict
+ A dictionary with keys "divide", "over", "under", and "invalid",
+ whose values are from the strings "ignore", "print", "log", "warn",
+ "raise", and "call". The keys represent possible floating-point
+ exceptions, and the values define how these exceptions are handled.
+
+ See Also
+ --------
+ geterrcall, seterr, seterrcall
+
+ Notes
+ -----
+ For complete documentation of the types of floating-point exceptions and
+ treatment options, see `seterr`.
+
+ Examples
+ --------
+ >>> from collections import OrderedDict
+ >>> sorted(np.geterr().items())
+ [('divide', 'warn'), ('invalid', 'warn'), ('over', 'warn'), ('under', 'ignore')]
+ >>> np.arange(3.) / np.arange(3.)
+ array([nan, 1., 1.])
+
+ >>> oldsettings = np.seterr(all='warn', over='raise')
+ >>> OrderedDict(sorted(np.geterr().items()))
+ OrderedDict([('divide', 'warn'), ('invalid', 'warn'), ('over', 'raise'), ('under', 'warn')])
+ >>> np.arange(3.) / np.arange(3.)
+ array([nan, 1., 1.])
+
+ """
+ maskvalue = umath.geterrobj()[1]
+ mask = 7
+ res = {}
+ val = (maskvalue >> SHIFT_DIVIDEBYZERO) & mask
+ res['divide'] = _errdict_rev[val]
+ val = (maskvalue >> SHIFT_OVERFLOW) & mask
+ res['over'] = _errdict_rev[val]
+ val = (maskvalue >> SHIFT_UNDERFLOW) & mask
+ res['under'] = _errdict_rev[val]
+ val = (maskvalue >> SHIFT_INVALID) & mask
+ res['invalid'] = _errdict_rev[val]
+ return res
+
+
+@set_module('numpy')
+def setbufsize(size):
+ """
+ Set the size of the buffer used in ufuncs.
+
+ Parameters
+ ----------
+ size : int
+ Size of buffer.
+
+ """
+ if size > 10e6:
+ raise ValueError("Buffer size, %s, is too big." % size)
+ if size < 5:
+ raise ValueError("Buffer size, %s, is too small." % size)
+ if size % 16 != 0:
+ raise ValueError("Buffer size, %s, is not a multiple of 16." % size)
+
+ pyvals = umath.geterrobj()
+ old = getbufsize()
+ pyvals[0] = size
+ umath.seterrobj(pyvals)
+ return old
+
+
+@set_module('numpy')
+def getbufsize():
+ """
+ Return the size of the buffer used in ufuncs.
+
+ Returns
+ -------
+ getbufsize : int
+ Size of ufunc buffer in bytes.
+
+ """
+ return umath.geterrobj()[0]
+
+
+@set_module('numpy')
+def seterrcall(func):
+ """
+ Set the floating-point error callback function or log object.
+
+ There are two ways to capture floating-point error messages. The first
+ is to set the error-handler to 'call', using `seterr`. Then, set
+ the function to call using this function.
+
+ The second is to set the error-handler to 'log', using `seterr`.
+ Floating-point errors then trigger a call to the 'write' method of
+ the provided object.
+
+ Parameters
+ ----------
+ func : callable f(err, flag) or object with write method
+ Function to call upon floating-point errors ('call'-mode) or
+ object whose 'write' method is used to log such message ('log'-mode).
+
+ The call function takes two arguments. The first is a string describing
+ the type of error (such as "divide by zero", "overflow", "underflow",
+ or "invalid value"), and the second is the status flag. The flag is a
+ byte, whose four least-significant bits indicate the type of error, one
+ of "divide", "over", "under", "invalid"::
+
+ [0 0 0 0 divide over under invalid]
+
+ In other words, ``flags = divide + 2*over + 4*under + 8*invalid``.
+
+ If an object is provided, its write method should take one argument,
+ a string.
+
+ Returns
+ -------
+ h : callable, log instance or None
+ The old error handler.
+
+ See Also
+ --------
+ seterr, geterr, geterrcall
+
+ Examples
+ --------
+ Callback upon error:
+
+ >>> def err_handler(type, flag):
+ ... print("Floating point error (%s), with flag %s" % (type, flag))
+ ...
+
+ >>> saved_handler = np.seterrcall(err_handler)
+ >>> save_err = np.seterr(all='call')
+ >>> from collections import OrderedDict
+
+ >>> np.array([1, 2, 3]) / 0.0
+ Floating point error (divide by zero), with flag 1
+ array([inf, inf, inf])
+
+ >>> np.seterrcall(saved_handler)
+ <function err_handler at 0x...>
+ >>> OrderedDict(sorted(np.seterr(**save_err).items()))
+ OrderedDict([('divide', 'call'), ('invalid', 'call'), ('over', 'call'), ('under', 'call')])
+
+ Log error message:
+
+ >>> class Log(object):
+ ... def write(self, msg):
+ ... print("LOG: %s" % msg)
+ ...
+
+ >>> log = Log()
+ >>> saved_handler = np.seterrcall(log)
+ >>> save_err = np.seterr(all='log')
+
+ >>> np.array([1, 2, 3]) / 0.0
+ LOG: Warning: divide by zero encountered in true_divide
+ array([inf, inf, inf])
+
+ >>> np.seterrcall(saved_handler)
+ <numpy.core.numeric.Log object at 0x...>
+ >>> OrderedDict(sorted(np.seterr(**save_err).items()))
+ OrderedDict([('divide', 'log'), ('invalid', 'log'), ('over', 'log'), ('under', 'log')])
+
+ """
+ if func is not None and not isinstance(func, collections_abc.Callable):
+ if not hasattr(func, 'write') or not isinstance(func.write, collections_abc.Callable):
+ raise ValueError("Only callable can be used as callback")
+ pyvals = umath.geterrobj()
+ old = geterrcall()
+ pyvals[2] = func
+ umath.seterrobj(pyvals)
+ return old
+
+
+@set_module('numpy')
+def geterrcall():
+ """
+ Return the current callback function used on floating-point errors.
+
+ When the error handling for a floating-point error (one of "divide",
+ "over", "under", or "invalid") is set to 'call' or 'log', the function
+ that is called or the log instance that is written to is returned by
+ `geterrcall`. This function or log instance has been set with
+ `seterrcall`.
+
+ Returns
+ -------
+ errobj : callable, log instance or None
+ The current error handler. If no handler was set through `seterrcall`,
+ ``None`` is returned.
+
+ See Also
+ --------
+ seterrcall, seterr, geterr
+
+ Notes
+ -----
+ For complete documentation of the types of floating-point exceptions and
+ treatment options, see `seterr`.
+
+ Examples
+ --------
+ >>> np.geterrcall() # we did not yet set a handler, returns None
+
+ >>> oldsettings = np.seterr(all='call')
+ >>> def err_handler(type, flag):
+ ... print("Floating point error (%s), with flag %s" % (type, flag))
+ >>> oldhandler = np.seterrcall(err_handler)
+ >>> np.array([1, 2, 3]) / 0.0
+ Floating point error (divide by zero), with flag 1
+ array([inf, inf, inf])
+
+ >>> cur_handler = np.geterrcall()
+ >>> cur_handler is err_handler
+ True
+
+ """
+ return umath.geterrobj()[2]
+
+
+class _unspecified(object):
+ pass
+
+
+_Unspecified = _unspecified()
+
+
+@set_module('numpy')
+class errstate(contextlib.ContextDecorator):
+ """
+ errstate(**kwargs)
+
+ Context manager for floating-point error handling.
+
+ Using an instance of `errstate` as a context manager allows statements in
+ that context to execute with a known error handling behavior. Upon entering
+ the context the error handling is set with `seterr` and `seterrcall`, and
+ upon exiting it is reset to what it was before.
+
+ .. versionchanged:: 1.17.0
+ `errstate` is also usable as a function decorator, saving
+ a level of indentation if an entire function is wrapped.
+ See :py:class:`contextlib.ContextDecorator` for more information.
+
+ Parameters
+ ----------
+ kwargs : {divide, over, under, invalid}
+ Keyword arguments. The valid keywords are the possible floating-point
+ exceptions. Each keyword should have a string value that defines the
+ treatment for the particular error. Possible values are
+ {'ignore', 'warn', 'raise', 'call', 'print', 'log'}.
+
+ See Also
+ --------
+ seterr, geterr, seterrcall, geterrcall
+
+ Notes
+ -----
+ For complete documentation of the types of floating-point exceptions and
+ treatment options, see `seterr`.
+
+ Examples
+ --------
+ >>> from collections import OrderedDict
+ >>> olderr = np.seterr(all='ignore') # Set error handling to known state.
+
+ >>> np.arange(3) / 0.
+ array([nan, inf, inf])
+ >>> with np.errstate(divide='warn'):
+ ... np.arange(3) / 0.
+ array([nan, inf, inf])
+
+ >>> np.sqrt(-1)
+ nan
+ >>> with np.errstate(invalid='raise'):
+ ... np.sqrt(-1)
+ Traceback (most recent call last):
+ File "<stdin>", line 2, in <module>
+ FloatingPointError: invalid value encountered in sqrt
+
+ Outside the context the error handling behavior has not changed:
+
+ >>> OrderedDict(sorted(np.geterr().items()))
+ OrderedDict([('divide', 'ignore'), ('invalid', 'ignore'), ('over', 'ignore'), ('under', 'ignore')])
+
+ """
+ # Note that we don't want to run the above doctests because they will fail
+ # without a from __future__ import with_statement
+
+ def __init__(self, **kwargs):
+ self.call = kwargs.pop('call', _Unspecified)
+ self.kwargs = kwargs
+
+ def __enter__(self):
+ self.oldstate = seterr(**self.kwargs)
+ if self.call is not _Unspecified:
+ self.oldcall = seterrcall(self.call)
+
+ def __exit__(self, *exc_info):
+ seterr(**self.oldstate)
+ if self.call is not _Unspecified:
+ seterrcall(self.oldcall)
+
+
+def _setdef():
+ defval = [UFUNC_BUFSIZE_DEFAULT, ERR_DEFAULT, None]
+ umath.seterrobj(defval)
+
+
+# set the default values
+_setdef()
diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py
index 960e64ca3..8a7626d9d 100644
--- a/numpy/core/arrayprint.py
+++ b/numpy/core/arrayprint.py
@@ -26,6 +26,7 @@ __docformat__ = 'restructuredtext'
import sys
import functools
+import numbers
if sys.version_info[0] >= 3:
try:
from _thread import get_ident
@@ -48,6 +49,7 @@ from .fromnumeric import ravel, any
from .numeric import concatenate, asarray, errstate
from .numerictypes import (longlong, intc, int_, float_, complex_, bool_,
flexible)
+from .overrides import array_function_dispatch, set_module
import warnings
import contextlib
@@ -85,9 +87,17 @@ def _make_options_dict(precision=None, threshold=None, edgeitems=None,
if legacy not in [None, False, '1.13']:
warnings.warn("legacy printing option can currently only be '1.13' or "
"`False`", stacklevel=3)
-
+ if threshold is not None:
+ # forbid the bad threshold arg suggested by stack overflow, gh-12351
+ if not isinstance(threshold, numbers.Number):
+ raise TypeError("threshold must be numeric")
+ if np.isnan(threshold):
+ raise ValueError("threshold must be non-NAN, try "
+ "sys.maxsize for untruncated representation")
return options
+
+@set_module('numpy')
def set_printoptions(precision=None, threshold=None, edgeitems=None,
linewidth=None, suppress=None, nanstr=None, infstr=None,
formatter=None, sign=None, floatmode=None, **kwarg):
@@ -106,6 +116,7 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
threshold : int, optional
Total number of array elements which trigger summarization
rather than full repr (default 1000).
+ To always use the full repr without summarization, pass `sys.maxsize`.
edgeitems : int, optional
Number of array items in summary at beginning and end of
each dimension (default 3).
@@ -155,7 +166,8 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
- 'str_kind' : sets 'str' and 'numpystr'
floatmode : str, optional
Controls the interpretation of the `precision` option for
- floating-point types. Can take the following values:
+ floating-point types. Can take the following values
+ (default maxprec_equal):
* 'fixed': Always print exactly `precision` fractional digits,
even if this would print more or fewer digits than
@@ -182,32 +194,34 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
See Also
--------
- get_printoptions, set_string_function, array2string
+ get_printoptions, printoptions, set_string_function, array2string
Notes
-----
`formatter` is always reset with a call to `set_printoptions`.
+ Use `printoptions` as a context manager to set the values temporarily.
+
Examples
--------
Floating point precision can be set:
>>> np.set_printoptions(precision=4)
- >>> print(np.array([1.123456789]))
- [ 1.1235]
+ >>> np.array([1.123456789])
+ [1.1235]
Long arrays can be summarised:
>>> np.set_printoptions(threshold=5)
- >>> print(np.arange(10))
- [0 1 2 ..., 7 8 9]
+ >>> np.arange(10)
+ array([0, 1, 2, ..., 7, 8, 9])
Small results can be suppressed:
>>> eps = np.finfo(float).eps
>>> x = np.arange(4.)
>>> x**2 - (x + eps)**2
- array([ -4.9304e-32, -4.4409e-16, 0.0000e+00, 0.0000e+00])
+ array([-4.9304e-32, -4.4409e-16, 0.0000e+00, 0.0000e+00])
>>> np.set_printoptions(suppress=True)
>>> x**2 - (x + eps)**2
array([-0., -0., 0., 0.])
@@ -224,9 +238,16 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
To put back the default options, you can use:
- >>> np.set_printoptions(edgeitems=3,infstr='inf',
+ >>> np.set_printoptions(edgeitems=3, infstr='inf',
... linewidth=75, nanstr='nan', precision=8,
... suppress=False, threshold=1000, formatter=None)
+
+ Also to temporarily override options, use `printoptions` as a context manager:
+
+ >>> with np.printoptions(precision=2, suppress=True, threshold=5):
+ ... np.linspace(0, 10, 10)
+ array([ 0. , 1.11, 2.22, ..., 7.78, 8.89, 10. ])
+
"""
legacy = kwarg.pop('legacy', None)
if kwarg:
@@ -249,6 +270,7 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
set_legacy_print_mode(0)
+@set_module('numpy')
def get_printoptions():
"""
Return the current print options.
@@ -272,12 +294,13 @@ def get_printoptions():
See Also
--------
- set_printoptions, set_string_function
+ set_printoptions, printoptions, set_string_function
"""
return _format_options.copy()
+@set_module('numpy')
@contextlib.contextmanager
def printoptions(*args, **kwargs):
"""Context manager for setting print options.
@@ -289,9 +312,10 @@ def printoptions(*args, **kwargs):
Examples
--------
+ >>> from numpy.testing import assert_equal
>>> with np.printoptions(precision=2):
- ... print(np.array([2.0])) / 3
- [0.67]
+ ... np.array([2.0]) / 3
+ array([0.67])
The `as`-clause of the `with`-statement gives the current print options:
@@ -496,6 +520,16 @@ def _array2string(a, options, separator=' ', prefix=""):
return lst
+def _array2string_dispatcher(
+ a, max_line_width=None, precision=None,
+ suppress_small=None, separator=None, prefix=None,
+ style=None, formatter=None, threshold=None,
+ edgeitems=None, sign=None, floatmode=None, suffix=None,
+ **kwarg):
+ return (a,)
+
+
+@array_function_dispatch(_array2string_dispatcher, module='numpy')
def array2string(a, max_line_width=None, precision=None,
suppress_small=None, separator=' ', prefix="",
style=np._NoValue, formatter=None, threshold=None,
@@ -509,14 +543,17 @@ def array2string(a, max_line_width=None, precision=None,
a : array_like
Input array.
max_line_width : int, optional
- The maximum number of columns the string should span. Newline
- characters splits the string appropriately after array elements.
+ Inserts newlines if text is longer than `max_line_width`.
+ Defaults to ``numpy.get_printoptions()['linewidth']``.
precision : int or None, optional
- Floating point precision. Default is the current printing
- precision (usually 8), which can be altered using `set_printoptions`.
+ Floating point precision.
+ Defaults to ``numpy.get_printoptions()['precision']``.
suppress_small : bool, optional
- Represent very small numbers as zero. A number is "very small" if it
- is smaller than the current printing precision.
+ Represent numbers "very close" to zero as zero; default is False.
+ Very close is defined by precision: if the precision is 8, e.g.,
+ numbers smaller (in absolute value) than 5e-9 are represented as
+ zero.
+ Defaults to ``numpy.get_printoptions()['suppress']``.
separator : str, optional
Inserted between elements.
prefix : str, optional
@@ -563,17 +600,22 @@ def array2string(a, max_line_width=None, precision=None,
threshold : int, optional
Total number of array elements which trigger summarization
rather than full repr.
+ Defaults to ``numpy.get_printoptions()['threshold']``.
edgeitems : int, optional
Number of array items in summary at beginning and end of
each dimension.
+ Defaults to ``numpy.get_printoptions()['edgeitems']``.
sign : string, either '-', '+', or ' ', optional
Controls printing of the sign of floating-point types. If '+', always
print the sign of positive values. If ' ', always prints a space
(whitespace character) in the sign position of positive values. If
'-', omit the sign character of positive values.
+ Defaults to ``numpy.get_printoptions()['sign']``.
floatmode : str, optional
Controls the interpretation of the `precision` option for
- floating-point types. Can take the following values:
+ floating-point types.
+ Defaults to ``numpy.get_printoptions()['floatmode']``.
+ Can take the following values:
- 'fixed': Always print exactly `precision` fractional digits,
even if this would print more or fewer digits than
@@ -624,9 +666,9 @@ def array2string(a, max_line_width=None, precision=None,
Examples
--------
>>> x = np.array([1e-16,1,2,3])
- >>> print(np.array2string(x, precision=2, separator=',',
- ... suppress_small=True))
- [ 0., 1., 2., 3.]
+ >>> np.array2string(x, precision=2, separator=',',
+ ... suppress_small=True)
+ '[0.,1.,2.,3.]'
>>> x = np.arange(3.)
>>> np.array2string(x, formatter={'float_kind':lambda x: "%.2f" % x})
@@ -634,7 +676,7 @@ def array2string(a, max_line_width=None, precision=None,
>>> x = np.arange(3)
>>> np.array2string(x, formatter={'int':lambda x: hex(x)})
- '[0x0L 0x1L 0x2L]'
+ '[0x0 0x1 0x2]'
"""
legacy = kwarg.pop('legacy', None)
@@ -652,7 +694,7 @@ def array2string(a, max_line_width=None, precision=None,
if style is np._NoValue:
style = repr
- if a.shape == () and not a.dtype.names:
+ if a.shape == () and a.dtype.names is None:
return style(a.item())
elif style is not np._NoValue:
# Deprecation 11-9-2017 v1.14
@@ -951,20 +993,8 @@ class FloatingFormat(object):
pad_left=self.pad_left,
pad_right=self.pad_right)
-# for back-compatibility, we keep the classes for each float type too
-class FloatFormat(FloatingFormat):
- def __init__(self, *args, **kwargs):
- warnings.warn("FloatFormat has been replaced by FloatingFormat",
- DeprecationWarning, stacklevel=2)
- super(FloatFormat, self).__init__(*args, **kwargs)
-
-
-class LongFloatFormat(FloatingFormat):
- def __init__(self, *args, **kwargs):
- warnings.warn("LongFloatFormat has been replaced by FloatingFormat",
- DeprecationWarning, stacklevel=2)
- super(LongFloatFormat, self).__init__(*args, **kwargs)
+@set_module('numpy')
def format_float_scientific(x, precision=None, unique=True, trim='k',
sign=False, pad_left=None, exp_digits=None):
"""
@@ -1032,6 +1062,8 @@ def format_float_scientific(x, precision=None, unique=True, trim='k',
trim=trim, sign=sign, pad_left=pad_left,
exp_digits=exp_digits)
+
+@set_module('numpy')
def format_float_positional(x, precision=None, unique=True,
fractional=True, trim='k', sign=False,
pad_left=None, pad_right=None):
@@ -1159,21 +1191,6 @@ class ComplexFloatingFormat(object):
return r + i
-# for back-compatibility, we keep the classes for each complex type too
-class ComplexFormat(ComplexFloatingFormat):
- def __init__(self, *args, **kwargs):
- warnings.warn(
- "ComplexFormat has been replaced by ComplexFloatingFormat",
- DeprecationWarning, stacklevel=2)
- super(ComplexFormat, self).__init__(*args, **kwargs)
-
-class LongComplexFormat(ComplexFloatingFormat):
- def __init__(self, *args, **kwargs):
- warnings.warn(
- "LongComplexFormat has been replaced by ComplexFloatingFormat",
- DeprecationWarning, stacklevel=2)
- super(LongComplexFormat, self).__init__(*args, **kwargs)
-
class _TimelikeFormat(object):
def __init__(self, data):
@@ -1284,16 +1301,6 @@ class StructuredVoidFormat(object):
return "({})".format(", ".join(str_fields))
-# for backwards compatibility
-class StructureFormat(StructuredVoidFormat):
- def __init__(self, *args, **kwargs):
- # NumPy 1.14, 2018-02-14
- warnings.warn(
- "StructureFormat has been replaced by StructuredVoidFormat",
- DeprecationWarning, stacklevel=2)
- super(StructureFormat, self).__init__(*args, **kwargs)
-
-
def _void_scalar_repr(x):
"""
Implements the repr for structured-void scalars. It is called from the
@@ -1333,7 +1340,7 @@ def dtype_is_implied(dtype):
>>> np.core.arrayprint.dtype_is_implied(np.int8)
False
>>> np.array([1, 2, 3], np.int8)
- array([1, 2, 3], dtype=np.int8)
+ array([1, 2, 3], dtype=int8)
"""
dtype = np.dtype(dtype)
if _format_options['legacy'] == '1.13' and dtype.type == bool_:
@@ -1353,6 +1360,7 @@ def dtype_short_repr(dtype):
The intent is roughly that the following holds
>>> from numpy import *
+ >>> dt = np.int64([1, 2]).dtype
>>> assert eval(dtype_short_repr(dt)) == dt
"""
if dtype.names is not None:
@@ -1370,48 +1378,10 @@ def dtype_short_repr(dtype):
return typename
-def array_repr(arr, max_line_width=None, precision=None, suppress_small=None):
- """
- Return the string representation of an array.
-
- Parameters
- ----------
- arr : ndarray
- Input array.
- max_line_width : int, optional
- The maximum number of columns the string should span. Newline
- characters split the string appropriately after array elements.
- precision : int, optional
- Floating point precision. Default is the current printing precision
- (usually 8), which can be altered using `set_printoptions`.
- suppress_small : bool, optional
- Represent very small numbers as zero, default is False. Very small
- is defined by `precision`, if the precision is 8 then
- numbers smaller than 5e-9 are represented as zero.
-
- Returns
- -------
- string : str
- The string representation of an array.
-
- See Also
- --------
- array_str, array2string, set_printoptions
-
- Examples
- --------
- >>> np.array_repr(np.array([1,2]))
- 'array([1, 2])'
- >>> np.array_repr(np.ma.array([0.]))
- 'MaskedArray([ 0.])'
- >>> np.array_repr(np.array([], np.int32))
- 'array([], dtype=int32)'
-
- >>> x = np.array([1e-6, 4e-7, 2, 3])
- >>> np.array_repr(x, precision=6, suppress_small=True)
- 'array([ 0.000001, 0. , 2. , 3. ])'
-
- """
+def _array_repr_implementation(
+ arr, max_line_width=None, precision=None, suppress_small=None,
+ array2string=array2string):
+ """Internal version of array_repr() that allows overriding array2string."""
if max_line_width is None:
max_line_width = _format_options['linewidth']
@@ -1454,42 +1424,72 @@ def array_repr(arr, max_line_width=None, precision=None, suppress_small=None):
return arr_str + spacer + dtype_str
-_guarded_str = _recursive_guard()(str)
-def array_str(a, max_line_width=None, precision=None, suppress_small=None):
- """
- Return a string representation of the data in an array.
+def _array_repr_dispatcher(
+ arr, max_line_width=None, precision=None, suppress_small=None):
+ return (arr,)
- The data in the array is returned as a single string. This function is
- similar to `array_repr`, the difference being that `array_repr` also
- returns information on the kind of array and its data type.
+
+@array_function_dispatch(_array_repr_dispatcher, module='numpy')
+def array_repr(arr, max_line_width=None, precision=None, suppress_small=None):
+ """
+ Return the string representation of an array.
Parameters
----------
- a : ndarray
+ arr : ndarray
Input array.
max_line_width : int, optional
- Inserts newlines if text is longer than `max_line_width`. The
- default is, indirectly, 75.
+ Inserts newlines if text is longer than `max_line_width`.
+ Defaults to ``numpy.get_printoptions()['linewidth']``.
precision : int, optional
- Floating point precision. Default is the current printing precision
- (usually 8), which can be altered using `set_printoptions`.
+ Floating point precision.
+ Defaults to ``numpy.get_printoptions()['precision']``.
suppress_small : bool, optional
Represent numbers "very close" to zero as zero; default is False.
Very close is defined by precision: if the precision is 8, e.g.,
numbers smaller (in absolute value) than 5e-9 are represented as
zero.
+ Defaults to ``numpy.get_printoptions()['suppress']``.
+
+ Returns
+ -------
+ string : str
+ The string representation of an array.
See Also
--------
- array2string, array_repr, set_printoptions
+ array_str, array2string, set_printoptions
Examples
--------
- >>> np.array_str(np.arange(3))
- '[0 1 2]'
+ >>> np.array_repr(np.array([1,2]))
+ 'array([1, 2])'
+ >>> np.array_repr(np.ma.array([0.]))
+ 'MaskedArray([0.])'
+ >>> np.array_repr(np.array([], np.int32))
+ 'array([], dtype=int32)'
+
+ >>> x = np.array([1e-6, 4e-7, 2, 3])
+ >>> np.array_repr(x, precision=6, suppress_small=True)
+ 'array([0.000001, 0. , 2. , 3. ])'
"""
+ return _array_repr_implementation(
+ arr, max_line_width, precision, suppress_small)
+
+
+@_recursive_guard()
+def _guarded_repr_or_str(v):
+ if isinstance(v, bytes):
+ return repr(v)
+ return str(v)
+
+
+def _array_str_implementation(
+ a, max_line_width=None, precision=None, suppress_small=None,
+ array2string=array2string):
+ """Internal version of array_str() that allows overriding array2string."""
if (_format_options['legacy'] == '1.13' and
a.shape == () and not a.dtype.names):
return str(a.item())
@@ -1501,10 +1501,64 @@ def array_str(a, max_line_width=None, precision=None, suppress_small=None):
# obtain a scalar and call str on it, avoiding problems for subclasses
# for which indexing with () returns a 0d instead of a scalar by using
# ndarray's getindex. Also guard against recursive 0d object arrays.
- return _guarded_str(np.ndarray.__getitem__(a, ()))
+ return _guarded_repr_or_str(np.ndarray.__getitem__(a, ()))
return array2string(a, max_line_width, precision, suppress_small, ' ', "")
+
+def _array_str_dispatcher(
+ a, max_line_width=None, precision=None, suppress_small=None):
+ return (a,)
+
+
+@array_function_dispatch(_array_str_dispatcher, module='numpy')
+def array_str(a, max_line_width=None, precision=None, suppress_small=None):
+ """
+ Return a string representation of the data in an array.
+
+ The data in the array is returned as a single string. This function is
+ similar to `array_repr`, the difference being that `array_repr` also
+ returns information on the kind of array and its data type.
+
+ Parameters
+ ----------
+ a : ndarray
+ Input array.
+ max_line_width : int, optional
+ Inserts newlines if text is longer than `max_line_width`.
+ Defaults to ``numpy.get_printoptions()['linewidth']``.
+ precision : int, optional
+ Floating point precision.
+ Defaults to ``numpy.get_printoptions()['precision']``.
+ suppress_small : bool, optional
+ Represent numbers "very close" to zero as zero; default is False.
+ Very close is defined by precision: if the precision is 8, e.g.,
+ numbers smaller (in absolute value) than 5e-9 are represented as
+ zero.
+ Defaults to ``numpy.get_printoptions()['suppress']``.
+
+ See Also
+ --------
+ array2string, array_repr, set_printoptions
+
+ Examples
+ --------
+ >>> np.array_str(np.arange(3))
+ '[0 1 2]'
+
+ """
+ return _array_str_implementation(
+ a, max_line_width, precision, suppress_small)
+
+
+# needed if __array_function__ is disabled
+_array2string_impl = getattr(array2string, '__wrapped__', array2string)
+_default_array_str = functools.partial(_array_str_implementation,
+ array2string=_array2string_impl)
+_default_array_repr = functools.partial(_array_repr_implementation,
+ array2string=_array2string_impl)
+
+
def set_string_function(f, repr=True):
"""
Set a Python function to be used when pretty printing arrays.
@@ -1534,8 +1588,8 @@ def set_string_function(f, repr=True):
>>> a = np.arange(10)
>>> a
HA! - What are you going to do now?
- >>> print(a)
- [0 1 2 3 4 5 6 7 8 9]
+ >>> _ = a
+ >>> # [0 1 2 3 4 5 6 7 8 9]
We can reset the function to the default:
@@ -1553,16 +1607,16 @@ def set_string_function(f, repr=True):
>>> x.__str__()
'random'
>>> x.__repr__()
- 'array([ 0, 1, 2, 3])'
+ 'array([0, 1, 2, 3])'
"""
if f is None:
if repr:
- return multiarray.set_string_function(array_repr, 1)
+ return multiarray.set_string_function(_default_array_repr, 1)
else:
- return multiarray.set_string_function(array_str, 0)
+ return multiarray.set_string_function(_default_array_str, 0)
else:
return multiarray.set_string_function(f, repr)
-set_string_function(array_str, 0)
-set_string_function(array_repr, 1)
+set_string_function(_default_array_str, False)
+set_string_function(_default_array_repr, True)
diff --git a/numpy/core/code_generators/cversions.txt b/numpy/core/code_generators/cversions.txt
index 43c32eac6..00f10df57 100644
--- a/numpy/core/code_generators/cversions.txt
+++ b/numpy/core/code_generators/cversions.txt
@@ -39,7 +39,12 @@
0x0000000b = edb1ba83730c650fd9bc5772a919cda7
# Version 12 (NumPy 1.14) Added PyArray_ResolveWritebackIfCopy,
-# Version 12 (NumPy 1.15) No change.
# PyArray_SetWritebackIfCopyBase and deprecated PyArray_SetUpdateIfCopyBase.
+# Version 12 (NumPy 1.15) No change.
0x0000000c = a1bc756c5782853ec2e3616cf66869d8
+# Version 13 (NumPy 1.16)
+# Deprecate PyArray_SetNumericOps and PyArray_GetNumericOps,
+# Add fields core_dim_flags and core_dim_sizes to PyUFuncObject.
+# Add PyUFunc_FromFuncAndDataAndSignatureAndIdentity to ufunc_funcs_api.
+0x0000000d = 5b0e8bbded00b166125974fc71e80a33
diff --git a/numpy/core/code_generators/genapi.py b/numpy/core/code_generators/genapi.py
index 42c564a97..7336e5e13 100644
--- a/numpy/core/code_generators/genapi.py
+++ b/numpy/core/code_generators/genapi.py
@@ -19,6 +19,7 @@ __docformat__ = 'restructuredtext'
# The files under src/ that are scanned for API functions
API_FILES = [join('multiarray', 'alloc.c'),
+ join('multiarray', 'arrayfunction_override.c'),
join('multiarray', 'array_assign_array.c'),
join('multiarray', 'array_assign_scalar.c'),
join('multiarray', 'arrayobject.c'),
@@ -163,9 +164,7 @@ def skip_brackets(s, lbrac, rbrac):
def split_arguments(argstr):
arguments = []
- bracket_counts = {'(': 0, '[': 0}
current_argument = []
- state = 0
i = 0
def finish_arg():
if current_argument:
@@ -260,7 +259,8 @@ def find_functions(filename, tag='API'):
elif state == STATE_ARGS:
if line.startswith('{'):
# finished
- fargs_str = ' '.join(function_args).rstrip(' )')
+ # remove any white space and the closing bracket:
+ fargs_str = ' '.join(function_args).rstrip()[:-1].rstrip()
fargs = split_arguments(fargs_str)
f = Function(function_name, return_type, fargs,
'\n'.join(doclist))
@@ -400,9 +400,7 @@ class FunctionApi(object):
return " (void *) %s" % self.name
def internal_define(self):
- annstr = []
- for a in self.annotations:
- annstr.append(str(a))
+ annstr = [str(a) for a in self.annotations]
annstr = ' '.join(annstr)
astr = """\
NPY_NO_EXPORT %s %s %s \\\n (%s);""" % (annstr, self.return_type,
@@ -463,10 +461,7 @@ def get_api_functions(tagname, api_dict):
functions = []
for f in API_FILES:
functions.extend(find_functions(f, tagname))
- dfunctions = []
- for func in functions:
- o = api_dict[func.name][0]
- dfunctions.append( (o, func) )
+ dfunctions = [(api_dict[func.name][0], func) for func in functions]
dfunctions.sort()
return [a[1] for a in dfunctions]
@@ -489,14 +484,11 @@ def get_versions_hash():
d = []
file = os.path.join(os.path.dirname(__file__), 'cversions.txt')
- fid = open(file, 'r')
- try:
+ with open(file, 'r') as fid:
for line in fid:
m = VERRE.match(line)
if m:
d.append((int(m.group(1), 16), m.group(2)))
- finally:
- fid.close()
return dict(d)
diff --git a/numpy/core/code_generators/generate_numpy_api.py b/numpy/core/code_generators/generate_numpy_api.py
index 7f2541667..5e04fb86d 100644
--- a/numpy/core/code_generators/generate_numpy_api.py
+++ b/numpy/core/code_generators/generate_numpy_api.py
@@ -50,7 +50,6 @@ _import_array(void)
PyObject *c_api = NULL;
if (numpy == NULL) {
- PyErr_SetString(PyExc_ImportError, "numpy.core._multiarray_umath failed to import");
return -1;
}
c_api = PyObject_GetAttrString(numpy, "_ARRAY_API");
@@ -193,7 +192,9 @@ def do_generate_api(targets, sources):
genapi.check_api_dict(multiarray_api_index)
numpyapi_list = genapi.get_api_functions('NUMPY_API',
- multiarray_funcs)
+ multiarray_funcs)
+
+ # FIXME: ordered_funcs_api is unused
ordered_funcs_api = genapi.order_dict(multiarray_funcs)
# Create dict name -> *Api instance
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py
index 6dc01877b..0d3bbffe9 100644
--- a/numpy/core/code_generators/generate_umath.py
+++ b/numpy/core/code_generators/generate_umath.py
@@ -10,11 +10,14 @@ sys.path.insert(0, os.path.dirname(__file__))
import ufunc_docstrings as docstrings
sys.path.pop(0)
-Zero = "PyUFunc_Zero"
-One = "PyUFunc_One"
-None_ = "PyUFunc_None"
-AllOnes = "PyUFunc_MinusOne"
-ReorderableNone = "PyUFunc_ReorderableNone"
+Zero = "PyInt_FromLong(0)"
+One = "PyInt_FromLong(1)"
+True_ = "(Py_INCREF(Py_True), Py_True)"
+False_ = "(Py_INCREF(Py_False), Py_False)"
+None_ = object()
+AllOnes = "PyInt_FromLong(-1)"
+MinusInfinity = 'PyFloat_FromDouble(-NPY_INFINITY)'
+ReorderableNone = "(Py_INCREF(Py_None), Py_None)"
# Sentinel value to specify using the full type description in the
# function name
@@ -71,13 +74,18 @@ class TypeDescription(object):
assert len(self.out) == nout
self.astype = self.astype_dict.get(self.type, None)
-_fdata_map = dict(e='npy_%sf', f='npy_%sf', d='npy_%s', g='npy_%sl',
- F='nc_%sf', D='nc_%s', G='nc_%sl')
+_fdata_map = dict(
+ e='npy_%sf',
+ f='npy_%sf',
+ d='npy_%s',
+ g='npy_%sl',
+ F='nc_%sf',
+ D='nc_%s',
+ G='nc_%sl'
+)
+
def build_func_data(types, f):
- func_data = []
- for t in types:
- d = _fdata_map.get(t, '%s') % (f,)
- func_data.append(d)
+ func_data = [_fdata_map.get(t, '%s') % (f,) for t in types]
return func_data
def TD(types, f=None, astype=None, in_=None, out=None, simd=None):
@@ -124,7 +132,7 @@ class Ufunc(object):
type_descriptions : list of TypeDescription objects
"""
def __init__(self, nin, nout, identity, docstring, typereso,
- *type_descriptions):
+ *type_descriptions, **kwargs):
self.nin = nin
self.nout = nout
if identity is None:
@@ -133,10 +141,13 @@ class Ufunc(object):
self.docstring = docstring
self.typereso = typereso
self.type_descriptions = []
+ self.signature = kwargs.pop('signature', None)
for td in type_descriptions:
self.type_descriptions.extend(td)
for td in self.type_descriptions:
td.finish_signature(self.nin, self.nout)
+ if kwargs:
+ raise ValueError('unknown kwargs %r' % str(kwargs))
# String-handling utilities to avoid locale-dependence.
@@ -188,31 +199,32 @@ def english_upper(s):
# output specification (optional)
# ]
-chartoname = {'?': 'bool',
- 'b': 'byte',
- 'B': 'ubyte',
- 'h': 'short',
- 'H': 'ushort',
- 'i': 'int',
- 'I': 'uint',
- 'l': 'long',
- 'L': 'ulong',
- 'q': 'longlong',
- 'Q': 'ulonglong',
- 'e': 'half',
- 'f': 'float',
- 'd': 'double',
- 'g': 'longdouble',
- 'F': 'cfloat',
- 'D': 'cdouble',
- 'G': 'clongdouble',
- 'M': 'datetime',
- 'm': 'timedelta',
- 'O': 'OBJECT',
- # '.' is like 'O', but calls a method of the object instead
- # of a function
- 'P': 'OBJECT',
- }
+chartoname = {
+ '?': 'bool',
+ 'b': 'byte',
+ 'B': 'ubyte',
+ 'h': 'short',
+ 'H': 'ushort',
+ 'i': 'int',
+ 'I': 'uint',
+ 'l': 'long',
+ 'L': 'ulong',
+ 'q': 'longlong',
+ 'Q': 'ulonglong',
+ 'e': 'half',
+ 'f': 'float',
+ 'd': 'double',
+ 'g': 'longdouble',
+ 'F': 'cfloat',
+ 'D': 'cdouble',
+ 'G': 'clongdouble',
+ 'M': 'datetime',
+ 'm': 'timedelta',
+ 'O': 'OBJECT',
+ # '.' is like 'O', but calls a method of the object instead
+ # of a function
+ 'P': 'OBJECT',
+}
all = '?bBhHiIlLqQefdgFDGOMm'
O = 'O'
@@ -312,7 +324,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'),
),
@@ -346,14 +358,14 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.square'),
None,
- TD(ints+inexact, simd=[('avx2', ints)]),
+ TD(ints+inexact, simd=[('avx2', ints), ('fma', 'fd'), ('avx512f', 'fd')]),
TD(O, f='Py_square'),
),
'reciprocal':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.reciprocal'),
None,
- TD(ints+inexact, simd=[('avx2', ints)]),
+ TD(ints+inexact, simd=[('avx2', ints), ('fma', 'fd'), ('avx512f','fd')]),
TD(O, f='Py_reciprocal'),
),
# This is no longer used as numpy.ones_like, however it is
@@ -383,7 +395,7 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.absolute'),
'PyUFunc_AbsoluteTypeResolver',
- TD(bints+flts+timedeltaonly),
+ TD(bints+flts+timedeltaonly, simd=[('fma', 'fd'), ('avx512f', 'fd')]),
TD(cmplx, out=('f', 'd', 'g')),
TD(O, f='PyNumber_Absolute'),
),
@@ -404,7 +416,7 @@ defdict = {
'positive':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.positive'),
- 'PyUFunc_SimpleUnaryOperationTypeResolver',
+ 'PyUFunc_SimpleUniformOperationTypeResolver',
TD(ints+flts+timedeltaonly),
TD(cmplx, f='pos'),
TD(O, f='PyNumber_Positive'),
@@ -412,7 +424,7 @@ defdict = {
'sign':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.sign'),
- 'PyUFunc_SimpleUnaryOperationTypeResolver',
+ 'PyUFunc_SimpleUniformOperationTypeResolver',
TD(nobool_or_datetime),
),
'greater':
@@ -458,7 +470,7 @@ defdict = {
[TypeDescription('O', FullTypeDescr, 'OO', 'O')],
),
'logical_and':
- Ufunc(2, 1, One,
+ Ufunc(2, 1, True_,
docstrings.get('numpy.core.umath.logical_and'),
'PyUFunc_SimpleBinaryComparisonTypeResolver',
TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)]),
@@ -472,14 +484,14 @@ defdict = {
TD(O, f='npy_ObjectLogicalNot'),
),
'logical_or':
- Ufunc(2, 1, Zero,
+ Ufunc(2, 1, False_,
docstrings.get('numpy.core.umath.logical_or'),
'PyUFunc_SimpleBinaryComparisonTypeResolver',
TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)]),
TD(O, f='npy_ObjectLogicalOr'),
),
'logical_xor':
- Ufunc(2, 1, Zero,
+ Ufunc(2, 1, False_,
docstrings.get('numpy.core.umath.logical_xor'),
'PyUFunc_SimpleBinaryComparisonTypeResolver',
TD(nodatetime_or_obj, out='?'),
@@ -488,33 +500,40 @@ defdict = {
'maximum':
Ufunc(2, 1, ReorderableNone,
docstrings.get('numpy.core.umath.maximum'),
- 'PyUFunc_SimpleBinaryOperationTypeResolver',
+ 'PyUFunc_SimpleUniformOperationTypeResolver',
TD(noobj),
TD(O, f='npy_ObjectMax')
),
'minimum':
Ufunc(2, 1, ReorderableNone,
docstrings.get('numpy.core.umath.minimum'),
- 'PyUFunc_SimpleBinaryOperationTypeResolver',
+ 'PyUFunc_SimpleUniformOperationTypeResolver',
TD(noobj),
TD(O, f='npy_ObjectMin')
),
+'clip':
+ Ufunc(3, 1, ReorderableNone,
+ docstrings.get('numpy.core.umath.clip'),
+ 'PyUFunc_SimpleUniformOperationTypeResolver',
+ TD(noobj),
+ [TypeDescription('O', 'npy_ObjectClip', 'OOO', 'O')]
+ ),
'fmax':
Ufunc(2, 1, ReorderableNone,
docstrings.get('numpy.core.umath.fmax'),
- 'PyUFunc_SimpleBinaryOperationTypeResolver',
+ 'PyUFunc_SimpleUniformOperationTypeResolver',
TD(noobj),
TD(O, f='npy_ObjectMax')
),
'fmin':
Ufunc(2, 1, ReorderableNone,
docstrings.get('numpy.core.umath.fmin'),
- 'PyUFunc_SimpleBinaryOperationTypeResolver',
+ 'PyUFunc_SimpleUniformOperationTypeResolver',
TD(noobj),
TD(O, f='npy_ObjectMin')
),
'logaddexp':
- Ufunc(2, 1, None,
+ Ufunc(2, 1, MinusInfinity,
docstrings.get('numpy.core.umath.logaddexp'),
None,
TD(flts, f="logaddexp", astype={'e':'f'})
@@ -643,14 +662,18 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.cos'),
None,
- TD(inexact, f='cos', astype={'e':'f'}),
+ TD('e', f='cos', astype={'e':'f'}),
+ TD('f', simd=[('fma', 'f'), ('avx512f', 'f')]),
+ TD('fdg' + cmplx, f='cos'),
TD(P, f='cos'),
),
'sin':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.sin'),
None,
- TD(inexact, f='sin', astype={'e':'f'}),
+ TD('e', f='sin', astype={'e':'f'}),
+ TD('f', simd=[('fma', 'f'), ('avx512f', 'f')]),
+ TD('fdg' + cmplx, f='sin'),
TD(P, f='sin'),
),
'tan':
@@ -685,7 +708,9 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.exp'),
None,
- TD(inexact, f='exp', astype={'e':'f'}),
+ TD('e', f='exp', astype={'e':'f'}),
+ TD('f', simd=[('fma', 'f'), ('avx512f', 'f')]),
+ TD('fdg' + cmplx, f='exp'),
TD(P, f='exp'),
),
'exp2':
@@ -706,7 +731,9 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.log'),
None,
- TD(inexact, f='log', astype={'e':'f'}),
+ TD('e', f='log', astype={'e':'f'}),
+ TD('f', simd=[('fma', 'f'), ('avx512f', 'f')]),
+ TD('fdg' + cmplx, f='log'),
TD(P, f='log'),
),
'log2':
@@ -735,8 +762,8 @@ defdict = {
docstrings.get('numpy.core.umath.sqrt'),
None,
TD('e', f='sqrt', astype={'e':'f'}),
- TD(inexactvec),
- TD(inexact, f='sqrt', astype={'e':'f'}),
+ TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]),
+ TD('fdg' + cmplx, f='sqrt'),
TD(P, f='sqrt'),
),
'cbrt':
@@ -750,15 +777,19 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.ceil'),
None,
- TD(flts, f='ceil', astype={'e':'f'}),
- TD(P, f='ceil'),
+ TD('e', f='ceil', astype={'e':'f'}),
+ TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]),
+ TD('fdg', f='ceil'),
+ TD(O, f='npy_ObjectCeil'),
),
'trunc':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.trunc'),
None,
- TD(flts, f='trunc', astype={'e':'f'}),
- TD(P, f='trunc'),
+ TD('e', f='trunc', astype={'e':'f'}),
+ TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]),
+ TD('fdg', f='trunc'),
+ TD(O, f='npy_ObjectTrunc'),
),
'fabs':
Ufunc(1, 1, None,
@@ -771,14 +802,18 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.floor'),
None,
- TD(flts, f='floor', astype={'e':'f'}),
- TD(P, f='floor'),
+ TD('e', f='floor', astype={'e':'f'}),
+ TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]),
+ TD('fdg', f='floor'),
+ TD(O, f='npy_ObjectFloor'),
),
'rint':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.rint'),
None,
- TD(inexact, f='rint', astype={'e':'f'}),
+ TD('e', f='rint', astype={'e':'f'}),
+ TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]),
+ TD('fdg' + cmplx, f='rint'),
TD(P, f='rint'),
),
'arctan2':
@@ -791,15 +826,17 @@ defdict = {
'remainder':
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.remainder'),
- None,
+ 'PyUFunc_RemainderTypeResolver',
TD(intflt),
+ [TypeDescription('m', FullTypeDescr, 'mm', 'm')],
TD(O, f='PyNumber_Remainder'),
),
'divmod':
Ufunc(2, 2, None,
docstrings.get('numpy.core.umath.divmod'),
- None,
+ 'PyUFunc_DivmodTypeResolver',
TD(intflt),
+ [TypeDescription('m', FullTypeDescr, 'mm', 'qm')],
# TD(O, f='PyNumber_Divmod'), # gh-9730
),
'hypot':
@@ -813,7 +850,7 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isnan'),
None,
- TD(inexact, out='?'),
+ TD(nodatetime_or_obj, out='?'),
),
'isnat':
Ufunc(1, 1, None,
@@ -825,13 +862,13 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isinf'),
None,
- TD(inexact, out='?'),
+ TD(nodatetime_or_obj, out='?'),
),
'isfinite':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isfinite'),
- None,
- TD(inexact, out='?'),
+ 'PyUFunc_IsFiniteTypeResolver',
+ TD(noobj, out='?'),
),
'signbit':
Ufunc(1, 1, None,
@@ -890,17 +927,25 @@ defdict = {
'gcd' :
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.gcd'),
- "PyUFunc_SimpleBinaryOperationTypeResolver",
+ "PyUFunc_SimpleUniformOperationTypeResolver",
TD(ints),
TD('O', f='npy_ObjectGCD'),
),
'lcm' :
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.lcm'),
- "PyUFunc_SimpleBinaryOperationTypeResolver",
+ "PyUFunc_SimpleUniformOperationTypeResolver",
TD(ints),
TD('O', f='npy_ObjectLCM'),
- )
+ ),
+'matmul' :
+ Ufunc(2, 1, None,
+ docstrings.get('numpy.core.umath.matmul'),
+ "PyUFunc_SimpleUniformOperationTypeResolver",
+ TD(notimes_or_obj),
+ TD(O),
+ signature='(n?,k),(k,m?)->(n?,m?)',
+ ),
}
if sys.version_info[0] >= 3:
@@ -914,25 +959,35 @@ def indent(st, spaces):
indented = re.sub(r' +$', r'', indented)
return indented
-chartotype1 = {'e': 'e_e',
- 'f': 'f_f',
- 'd': 'd_d',
- 'g': 'g_g',
- 'F': 'F_F',
- 'D': 'D_D',
- 'G': 'G_G',
- 'O': 'O_O',
- 'P': 'O_O_method'}
+# maps [nin, nout][type] to a suffix
+arity_lookup = {
+ (1, 1): {
+ 'e': 'e_e',
+ 'f': 'f_f',
+ 'd': 'd_d',
+ 'g': 'g_g',
+ 'F': 'F_F',
+ 'D': 'D_D',
+ 'G': 'G_G',
+ 'O': 'O_O',
+ 'P': 'O_O_method',
+ },
+ (2, 1): {
+ 'e': 'ee_e',
+ 'f': 'ff_f',
+ 'd': 'dd_d',
+ 'g': 'gg_g',
+ 'F': 'FF_F',
+ 'D': 'DD_D',
+ 'G': 'GG_G',
+ 'O': 'OO_O',
+ 'P': 'OO_O_method',
+ },
+ (3, 1): {
+ 'O': 'OOO_O',
+ }
+}
-chartotype2 = {'e': 'ee_e',
- 'f': 'ff_f',
- 'd': 'dd_d',
- 'g': 'gg_g',
- 'F': 'FF_F',
- 'D': 'DD_D',
- 'G': 'GG_G',
- 'O': 'OO_O',
- 'P': 'OO_O_method'}
#for each name
# 1) create functions, data, and signature
# 2) fill in functions and data in InitOperators
@@ -982,11 +1037,9 @@ def make_arrays(funcdict):
))
else:
funclist.append('NULL')
- if (uf.nin, uf.nout) == (2, 1):
- thedict = chartotype2
- elif (uf.nin, uf.nout) == (1, 1):
- thedict = chartotype1
- else:
+ try:
+ thedict = arity_lookup[uf.nin, uf.nout]
+ except KeyError:
raise ValueError("Could not handle {}[{}]".format(name, t.type))
astype = ''
@@ -1046,19 +1099,44 @@ def make_ufuncs(funcdict):
# string literal in C code. We split at endlines because textwrap.wrap
# do not play well with \n
docstring = '\\n\"\"'.join(docstring.split(r"\n"))
+ if uf.signature is None:
+ sig = "NULL"
+ else:
+ sig = '"{}"'.format(uf.signature)
fmt = textwrap.dedent("""\
- f = PyUFunc_FromFuncAndData(
+ identity = {identity_expr};
+ if ({has_identity} && identity == NULL) {{
+ return -1;
+ }}
+ f = PyUFunc_FromFuncAndDataAndSignatureAndIdentity(
{name}_functions, {name}_data, {name}_signatures, {nloops},
{nin}, {nout}, {identity}, "{name}",
- "{doc}", 0
+ "{doc}", 0, {sig}, identity
);
+ if ({has_identity}) {{
+ Py_DECREF(identity);
+ }}
if (f == NULL) {{
return -1;
- }}""")
- mlist.append(fmt.format(
+ }}
+ """)
+ args = dict(
name=name, nloops=len(uf.type_descriptions),
- nin=uf.nin, nout=uf.nout, identity=uf.identity, doc=docstring
- ))
+ nin=uf.nin, nout=uf.nout,
+ has_identity='0' if uf.identity is None_ else '1',
+ identity='PyUFunc_IdentityValue',
+ identity_expr=uf.identity,
+ doc=docstring,
+ sig=sig,
+ )
+
+ # Only PyUFunc_None means don't reorder - we pass this using the old
+ # argument
+ if uf.identity is None_:
+ args['identity'] = 'PyUFunc_None'
+ args['identity_expr'] = 'NULL'
+
+ mlist.append(fmt.format(**args))
if uf.typereso is not None:
mlist.append(
r"((PyUFuncObject *)f)->type_resolver = &%s;" % uf.typereso)
@@ -1082,11 +1160,14 @@ def make_code(funcdict, filename):
#include "cpuid.h"
#include "ufunc_object.h"
#include "ufunc_type_resolution.h"
+ #include "loops.h"
+ #include "matmul.h"
+ #include "clip.h"
%s
static int
InitOperators(PyObject *dictionary) {
- PyObject *f;
+ PyObject *f, *identity;
%s
%s
@@ -1099,7 +1180,6 @@ def make_code(funcdict, filename):
if __name__ == "__main__":
filename = __file__
- fid = open('__umath_generated.c', 'w')
code = make_code(defdict, filename)
- fid.write(code)
- fid.close()
+ with open('__umath_generated.c', 'w') as fid:
+ fid.write(code)
diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py
index d8a9ee6b4..a71c236fd 100644
--- a/numpy/core/code_generators/numpy_api.py
+++ b/numpy/core/code_generators/numpy_api.py
@@ -402,6 +402,8 @@ ufunc_funcs_api = {
# End 1.7 API
'PyUFunc_RegisterLoopForDescr': (41,),
# End 1.8 API
+ 'PyUFunc_FromFuncAndDataAndSignatureAndIdentity': (42,),
+ # End 1.16 API
}
# List of all the dicts which define the C API
diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py
index 6e5cb25af..1ac477b54 100644
--- a/numpy/core/code_generators/ufunc_docstrings.py
+++ b/numpy/core/code_generators/ufunc_docstrings.py
@@ -26,12 +26,19 @@ subst = {
a freshly-allocated array is returned. A tuple (possible only as a
keyword argument) must have length equal to the number of outputs.
where : array_like, optional
- Values of True indicate to calculate the ufunc at that position, values
- of False indicate to leave the value in the output alone.
+ This condition is broadcast over the input. At locations where the
+ condition is True, the `out` array will be set to the ufunc result.
+ Elsewhere, the `out` array will retain its original value.
+ Note that if an uninitialized `out` array is created via the default
+ ``out=None``, locations within it where the condition is False will
+ remain uninitialized.
**kwargs
For other keyword-only arguments, see the
:ref:`ufunc docs <ufuncs.kwargs>`.
""").strip(),
+ 'BROADCASTABLE_2': ("If ``x1.shape != x2.shape``, they must be "
+ "broadcastable to a common shape (which becomes the "
+ "shape of the output)."),
'OUT_SCALAR_1': "This is a scalar if `x` is a scalar.",
'OUT_SCALAR_2': "This is a scalar if both `x1` and `x2` are scalars.",
}
@@ -39,13 +46,20 @@ subst = {
def add_newdoc(place, name, doc):
doc = textwrap.dedent(doc).strip()
- if name[0] != '_':
+ skip = (
+ # gufuncs do not use the OUT_SCALAR replacement strings
+ 'matmul',
+ # clip has 3 inputs, which is not handled by this
+ 'clip',
+ )
+ if name[0] != '_' and name not in skip:
if '\nx :' in doc:
assert '$OUT_SCALAR_1' in doc, "in {}".format(name)
elif '\nx2 :' in doc or '\nx1, x2 :' in doc:
assert '$OUT_SCALAR_2' in doc, "in {}".format(name)
else:
assert False, "Could not detect number of inputs in {}".format(name)
+
for k, v in subst.items():
doc = doc.replace('$' + k, v)
@@ -103,9 +117,7 @@ add_newdoc('numpy.core.umath', 'add',
Parameters
----------
x1, x2 : array_like
- The arrays to be added. If ``x1.shape != x2.shape``, they must be
- broadcastable to a common shape (which may be the shape of one or
- the other).
+ The arrays to be added. $BROADCASTABLE_2
$PARAMS
Returns
@@ -431,8 +443,7 @@ add_newdoc('numpy.core.umath', 'arctan2',
x1 : array_like, real-valued
`y`-coordinates.
x2 : array_like, real-valued
- `x`-coordinates. `x2` must be broadcastable to match the shape of
- `x1` or vice versa.
+ `x`-coordinates. $BROADCASTABLE_2
$PARAMS
Returns
@@ -555,7 +566,7 @@ add_newdoc('numpy.core.umath', 'bitwise_and',
Parameters
----------
x1, x2 : array_like
- Only integer and boolean types are handled.
+ Only integer and boolean types are handled. $BROADCASTABLE_2
$PARAMS
Returns
@@ -608,7 +619,7 @@ add_newdoc('numpy.core.umath', 'bitwise_or',
Parameters
----------
x1, x2 : array_like
- Only integer and boolean types are handled.
+ Only integer and boolean types are handled. $BROADCASTABLE_2
$PARAMS
Returns
@@ -647,8 +658,8 @@ add_newdoc('numpy.core.umath', 'bitwise_or',
array([ 6, 5, 255])
>>> np.array([2, 5, 255]) | np.array([4, 4, 4])
array([ 6, 5, 255])
- >>> np.bitwise_or(np.array([2, 5, 255, 2147483647L], dtype=np.int32),
- ... np.array([4, 4, 4, 2147483647L], dtype=np.int32))
+ >>> np.bitwise_or(np.array([2, 5, 255, 2147483647], dtype=np.int32),
+ ... np.array([4, 4, 4, 2147483647], dtype=np.int32))
array([ 6, 5, 255, 2147483647])
>>> np.bitwise_or([True, True], [False, True])
array([ True, True])
@@ -666,7 +677,7 @@ add_newdoc('numpy.core.umath', 'bitwise_xor',
Parameters
----------
x1, x2 : array_like
- Only integer and boolean types are handled.
+ Only integer and boolean types are handled. $BROADCASTABLE_2
$PARAMS
Returns
@@ -792,6 +803,13 @@ add_newdoc('numpy.core.umath', 'conjugate',
The complex conjugate of `x`, with same dtype as `y`.
$OUT_SCALAR_1
+ Notes
+ -----
+ `conj` is an alias for `conjugate`:
+
+ >>> np.conj is np.conjugate
+ True
+
Examples
--------
>>> np.conjugate(1+2j)
@@ -836,6 +854,7 @@ add_newdoc('numpy.core.umath', 'cos',
array([ 1.00000000e+00, 6.12303177e-17, -1.00000000e+00])
>>>
>>> # Example of providing the optional output parameter
+ >>> out1 = np.array([0], dtype='d')
>>> out2 = np.cos([0.1], out1)
>>> out2 is out1
True
@@ -844,7 +863,7 @@ add_newdoc('numpy.core.umath', 'cos',
>>> np.cos(np.zeros((3,3)),np.zeros((2,2)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
- ValueError: invalid return array shape
+ ValueError: operands could not be broadcast together with shapes (3,3) (2,2)
""")
@@ -911,7 +930,7 @@ add_newdoc('numpy.core.umath', 'degrees',
270., 300., 330.])
>>> out = np.zeros((rad.shape))
- >>> r = degrees(rad, out)
+ >>> r = np.degrees(rad, out)
>>> np.all(r == out)
True
@@ -968,7 +987,7 @@ add_newdoc('numpy.core.umath', 'heaviside',
x1 : array_like
Input values.
x2 : array_like
- The value of the function when x1 is 0.
+ The value of the function when x1 is 0. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1003,7 +1022,7 @@ add_newdoc('numpy.core.umath', 'divide',
x1 : array_like
Dividend array.
x2 : array_like
- Divisor array.
+ Divisor array. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1072,7 +1091,7 @@ add_newdoc('numpy.core.umath', 'equal',
Parameters
----------
x1, x2 : array_like
- Input arrays of the same shape.
+ Input arrays. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1311,7 +1330,7 @@ add_newdoc('numpy.core.umath', 'floor_divide',
"""
Return the largest integer smaller or equal to the division of the inputs.
It is equivalent to the Python ``//`` operator and pairs with the
- Python ``%`` (`remainder`), function so that ``b = a % b + b * (a // b)``
+ Python ``%`` (`remainder`), function so that ``a = a % b + b * (a // b)``
up to roundoff.
Parameters
@@ -1319,7 +1338,7 @@ add_newdoc('numpy.core.umath', 'floor_divide',
x1 : array_like
Numerator.
x2 : array_like
- Denominator.
+ Denominator. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1359,7 +1378,7 @@ add_newdoc('numpy.core.umath', 'fmod',
x1 : array_like
Dividend.
x2 : array_like
- Divisor.
+ Divisor. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1409,9 +1428,7 @@ add_newdoc('numpy.core.umath', 'greater',
Parameters
----------
x1, x2 : array_like
- Input arrays. If ``x1.shape != x2.shape``, they must be
- broadcastable to a common shape (which may be the shape of one or
- the other).
+ Input arrays. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1447,9 +1464,7 @@ add_newdoc('numpy.core.umath', 'greater_equal',
Parameters
----------
x1, x2 : array_like
- Input arrays. If ``x1.shape != x2.shape``, they must be
- broadcastable to a common shape (which may be the shape of one or
- the other).
+ Input arrays. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1482,7 +1497,7 @@ add_newdoc('numpy.core.umath', 'hypot',
Parameters
----------
x1, x2 : array_like
- Leg of the triangle(s).
+ Leg of the triangle(s). $BROADCASTABLE_2
$PARAMS
Returns
@@ -1558,33 +1573,31 @@ add_newdoc('numpy.core.umath', 'invert',
We've seen that 13 is represented by ``00001101``.
The invert or bit-wise NOT of 13 is then:
- >>> np.invert(np.array([13], dtype=uint8))
- array([242], dtype=uint8)
+ >>> x = np.invert(np.array(13, dtype=np.uint8))
+ >>> x
+ 242
>>> np.binary_repr(x, width=8)
- '00001101'
- >>> np.binary_repr(242, width=8)
'11110010'
The result depends on the bit-width:
- >>> np.invert(np.array([13], dtype=uint16))
- array([65522], dtype=uint16)
+ >>> x = np.invert(np.array(13, dtype=np.uint16))
+ >>> x
+ 65522
>>> np.binary_repr(x, width=16)
- '0000000000001101'
- >>> np.binary_repr(65522, width=16)
'1111111111110010'
When using signed integer types the result is the two's complement of
the result for the unsigned type:
- >>> np.invert(np.array([13], dtype=int8))
+ >>> np.invert(np.array([13], dtype=np.int8))
array([-14], dtype=int8)
>>> np.binary_repr(-14, width=8)
'11110010'
Booleans are accepted as well:
- >>> np.invert(array([True, False]))
+ >>> np.invert(np.array([True, False]))
array([False, True])
""")
@@ -1783,6 +1796,7 @@ add_newdoc('numpy.core.umath', 'left_shift',
Input values.
x2 : array_like of integer type
Number of zeros to append to `x1`. Has to be non-negative.
+ $BROADCASTABLE_2
$PARAMS
Returns
@@ -1818,9 +1832,7 @@ add_newdoc('numpy.core.umath', 'less',
Parameters
----------
x1, x2 : array_like
- Input arrays. If ``x1.shape != x2.shape``, they must be
- broadcastable to a common shape (which may be the shape of one or
- the other).
+ Input arrays. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1848,9 +1860,7 @@ add_newdoc('numpy.core.umath', 'less_equal',
Parameters
----------
x1, x2 : array_like
- Input arrays. If ``x1.shape != x2.shape``, they must be
- broadcastable to a common shape (which may be the shape of one or
- the other).
+ Input arrays. $BROADCASTABLE_2
$PARAMS
Returns
@@ -1968,7 +1978,7 @@ add_newdoc('numpy.core.umath', 'log10',
Examples
--------
>>> np.log10([1e-15, -3.])
- array([-15., NaN])
+ array([-15., nan])
""")
@@ -2034,7 +2044,7 @@ add_newdoc('numpy.core.umath', 'logaddexp',
Parameters
----------
x1, x2 : array_like
- Input values.
+ Input values. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2076,7 +2086,7 @@ add_newdoc('numpy.core.umath', 'logaddexp2',
Parameters
----------
x1, x2 : array_like
- Input values.
+ Input values. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2167,14 +2177,14 @@ add_newdoc('numpy.core.umath', 'logical_and',
Parameters
----------
x1, x2 : array_like
- Input arrays. `x1` and `x2` must be of the same shape.
+ Input arrays. $BROADCASTABLE_2
$PARAMS
Returns
-------
y : ndarray or bool
- Boolean result with the same shape as `x1` and `x2` of the logical
- AND operation on corresponding elements of `x1` and `x2`.
+ Boolean result of the logical AND operation applied to the elements
+ of `x1` and `x2`; the shape is determined by broadcasting.
$OUT_SCALAR_2
See Also
@@ -2237,14 +2247,14 @@ add_newdoc('numpy.core.umath', 'logical_or',
----------
x1, x2 : array_like
Logical OR is applied to the elements of `x1` and `x2`.
- They have to be of the same shape.
+ $BROADCASTABLE_2
$PARAMS
Returns
-------
y : ndarray or bool
- Boolean result with the same shape as `x1` and `x2` of the logical
- OR operation on elements of `x1` and `x2`.
+ Boolean result of the logical OR operation applied to the elements
+ of `x1` and `x2`; the shape is determined by broadcasting.
$OUT_SCALAR_2
See Also
@@ -2272,16 +2282,14 @@ add_newdoc('numpy.core.umath', 'logical_xor',
Parameters
----------
x1, x2 : array_like
- Logical XOR is applied to the elements of `x1` and `x2`. They must
- be broadcastable to the same shape.
+ Logical XOR is applied to the elements of `x1` and `x2`. $BROADCASTABLE_2
$PARAMS
Returns
-------
y : bool or ndarray of bool
Boolean result of the logical XOR operation applied to the elements
- of `x1` and `x2`; the shape is determined by whether or not
- broadcasting of one or both arrays was required.
+ of `x1` and `x2`; the shape is determined by broadcasting.
$OUT_SCALAR_2
See Also
@@ -2321,8 +2329,7 @@ add_newdoc('numpy.core.umath', 'maximum',
Parameters
----------
x1, x2 : array_like
- The arrays holding the elements to be compared. They must have
- the same shape, or shapes that can be broadcast to a single shape.
+ The arrays holding the elements to be compared. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2360,7 +2367,7 @@ add_newdoc('numpy.core.umath', 'maximum',
[ 0.5, 2. ]])
>>> np.maximum([np.nan, 0, np.nan], [0, np.nan, np.nan])
- array([ NaN, NaN, NaN])
+ array([nan, nan, nan])
>>> np.maximum(np.Inf, 1)
inf
@@ -2380,8 +2387,7 @@ add_newdoc('numpy.core.umath', 'minimum',
Parameters
----------
x1, x2 : array_like
- The arrays holding the elements to be compared. They must have
- the same shape, or shapes that can be broadcast to a single shape.
+ The arrays holding the elements to be compared. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2419,7 +2425,7 @@ add_newdoc('numpy.core.umath', 'minimum',
[ 0. , 1. ]])
>>> np.minimum([np.nan, 0, np.nan],[0, np.nan, np.nan])
- array([ NaN, NaN, NaN])
+ array([nan, nan, nan])
>>> np.minimum(-np.Inf, 1)
-inf
@@ -2439,8 +2445,7 @@ add_newdoc('numpy.core.umath', 'fmax',
Parameters
----------
x1, x2 : array_like
- The arrays holding the elements to be compared. They must have
- the same shape.
+ The arrays holding the elements to be compared. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2479,7 +2484,7 @@ add_newdoc('numpy.core.umath', 'fmax',
[ 0.5, 2. ]])
>>> np.fmax([np.nan, 0, np.nan],[0, np.nan, np.nan])
- array([ 0., 0., NaN])
+ array([ 0., 0., nan])
""")
@@ -2497,8 +2502,7 @@ add_newdoc('numpy.core.umath', 'fmin',
Parameters
----------
x1, x2 : array_like
- The arrays holding the elements to be compared. They must have
- the same shape.
+ The arrays holding the elements to be compared. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2537,8 +2541,171 @@ add_newdoc('numpy.core.umath', 'fmin',
[ 0. , 1. ]])
>>> np.fmin([np.nan, 0, np.nan],[0, np.nan, np.nan])
- array([ 0., 0., NaN])
+ array([ 0., 0., nan])
+
+ """)
+
+add_newdoc('numpy.core.umath', 'clip',
+ """
+ Clip (limit) the values in an array.
+
+ Given an interval, values outside the interval are clipped to
+ the interval edges. For example, if an interval of ``[0, 1]``
+ is specified, values smaller than 0 become 0, and values larger
+ than 1 become 1.
+
+ Equivalent to but faster than ``np.minimum(np.maximum(a, a_min), a_max)``.
+
+ Parameters
+ ----------
+ a : array_like
+ Array containing elements to clip.
+ a_min : array_like
+ Minimum value.
+ a_max : array_like
+ Maximum value.
+ out : ndarray, optional
+ The results will be placed in this array. It may be the input
+ array for in-place clipping. `out` must be of the right shape
+ to hold the output. Its type is preserved.
+ $PARAMS
+
+ See Also
+ --------
+ numpy.clip :
+ Wrapper that makes the ``a_min`` and ``a_max`` arguments optional,
+ dispatching to one of `~numpy.core.umath.clip`,
+ `~numpy.core.umath.minimum`, and `~numpy.core.umath.maximum`.
+
+ Returns
+ -------
+ clipped_array : ndarray
+ An array with the elements of `a`, but where values
+ < `a_min` are replaced with `a_min`, and those > `a_max`
+ with `a_max`.
+ """)
+
+add_newdoc('numpy.core.umath', 'matmul',
+ """
+ Matrix product of two arrays.
+
+ Parameters
+ ----------
+ x1, x2 : array_like
+ Input arrays, scalars not allowed.
+ out : ndarray, optional
+ A location into which the result is stored. If provided, it must have
+ a shape that matches the signature `(n,k),(k,m)->(n,m)`. If not
+ provided or `None`, a freshly-allocated array is returned.
+ **kwargs
+ For other keyword-only arguments, see the
+ :ref:`ufunc docs <ufuncs.kwargs>`.
+
+ .. versionadded:: 1.16
+ Now handles ufunc kwargs
+ Returns
+ -------
+ y : ndarray
+ The matrix product of the inputs.
+ This is a scalar only when both x1, x2 are 1-d vectors.
+
+ Raises
+ ------
+ ValueError
+ If the last dimension of `a` is not the same size as
+ the second-to-last dimension of `b`.
+
+ If a scalar value is passed in.
+
+ See Also
+ --------
+ vdot : Complex-conjugating dot product.
+ tensordot : Sum products over arbitrary axes.
+ einsum : Einstein summation convention.
+ dot : alternative matrix product with different broadcasting rules.
+
+ Notes
+ -----
+
+ The behavior depends on the arguments in the following way.
+
+ - If both arguments are 2-D they are multiplied like conventional
+ matrices.
+ - If either argument is N-D, N > 2, it is treated as a stack of
+ matrices residing in the last two indexes and broadcast accordingly.
+ - If the first argument is 1-D, it is promoted to a matrix by
+ prepending a 1 to its dimensions. After matrix multiplication
+ the prepended 1 is removed.
+ - If the second argument is 1-D, it is promoted to a matrix by
+ appending a 1 to its dimensions. After matrix multiplication
+ the appended 1 is removed.
+
+ ``matmul`` differs from ``dot`` in two important ways:
+
+ - Multiplication by scalars is not allowed, use ``*`` instead.
+ - Stacks of matrices are broadcast together as if the matrices
+ were elements, respecting the signature ``(n,k),(k,m)->(n,m)``:
+
+ >>> a = np.ones([9, 5, 7, 4])
+ >>> c = np.ones([9, 5, 4, 3])
+ >>> np.dot(a, c).shape
+ (9, 5, 7, 9, 5, 3)
+ >>> np.matmul(a, c).shape
+ (9, 5, 7, 3)
+ >>> # n is 7, k is 4, m is 3
+
+ The matmul function implements the semantics of the `@` operator introduced
+ in Python 3.5 following PEP465.
+
+ Examples
+ --------
+ For 2-D arrays it is the matrix product:
+
+ >>> a = np.array([[1, 0],
+ ... [0, 1]])
+ >>> b = np.array([[4, 1],
+ ... [2, 2]])
+ >>> np.matmul(a, b)
+ array([[4, 1],
+ [2, 2]])
+
+ For 2-D mixed with 1-D, the result is the usual.
+
+ >>> a = np.array([[1, 0],
+ ... [0, 1]])
+ >>> b = np.array([1, 2])
+ >>> np.matmul(a, b)
+ array([1, 2])
+ >>> np.matmul(b, a)
+ array([1, 2])
+
+
+ Broadcasting is conventional for stacks of arrays
+
+ >>> a = np.arange(2 * 2 * 4).reshape((2, 2, 4))
+ >>> b = np.arange(2 * 2 * 4).reshape((2, 4, 2))
+ >>> np.matmul(a,b).shape
+ (2, 2, 2)
+ >>> np.matmul(a, b)[0, 1, 1]
+ 98
+ >>> sum(a[0, 1, :] * b[0 , :, 1])
+ 98
+
+ Vector, vector returns the scalar inner product, but neither argument
+ is complex-conjugated:
+
+ >>> np.matmul([2j, 3j], [2j, 3j])
+ (-13+0j)
+
+ Scalar multiplication raises an error.
+
+ >>> np.matmul([1,2], 3)
+ Traceback (most recent call last):
+ ...
+ ValueError: matmul: Input operand 1 does not have enough dimensions ...
+
+ .. versionadded:: 1.10.0
""")
add_newdoc('numpy.core.umath', 'modf',
@@ -2588,14 +2755,13 @@ add_newdoc('numpy.core.umath', 'multiply',
Parameters
----------
x1, x2 : array_like
- Input arrays to be multiplied.
+ Input arrays to be multiplied. $BROADCASTABLE_2
$PARAMS
Returns
-------
y : ndarray
- The product of `x1` and `x2`, element-wise. Returns a scalar if
- both `x1` and `x2` are scalars.
+ The product of `x1` and `x2`, element-wise.
$OUT_SCALAR_2
Notes
@@ -2670,7 +2836,7 @@ add_newdoc('numpy.core.umath', 'not_equal',
Parameters
----------
x1, x2 : array_like
- Input arrays.
+ Input arrays. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2719,7 +2885,7 @@ add_newdoc('numpy.core.umath', 'power',
x1 : array_like
The bases.
x2 : array_like
- The exponents.
+ The exponents. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2778,7 +2944,7 @@ add_newdoc('numpy.core.umath', 'float_power',
x1 : array_like
The bases.
x2 : array_like
- The exponents.
+ The exponents. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2950,7 +3116,7 @@ add_newdoc('numpy.core.umath', 'remainder',
x1 : array_like
Dividend array.
x2 : array_like
- Divisor array.
+ Divisor array. $BROADCASTABLE_2
$PARAMS
Returns
@@ -2970,6 +3136,7 @@ add_newdoc('numpy.core.umath', 'remainder',
-----
Returns 0 when `x2` is 0 and both `x1` and `x2` are (arrays of)
integers.
+ ``mod`` is an alias of ``remainder``.
Examples
--------
@@ -2995,7 +3162,7 @@ add_newdoc('numpy.core.umath', 'divmod',
x1 : array_like
Dividend array.
x2 : array_like
- Divisor array.
+ Divisor array. $BROADCASTABLE_2
$PARAMS
Returns
@@ -3034,7 +3201,7 @@ add_newdoc('numpy.core.umath', 'right_shift',
x1 : array_like, int
Input values.
x2 : array_like, int
- Number of bits to remove at the right of `x1`.
+ Number of bits to remove at the right of `x1`. $BROADCASTABLE_2
$PARAMS
Returns
@@ -3161,16 +3328,14 @@ add_newdoc('numpy.core.umath', 'copysign',
"""
Change the sign of x1 to that of x2, element-wise.
- If both arguments are arrays or sequences, they have to be of the same
- length. If `x2` is a scalar, its sign will be copied to all elements of
- `x1`.
+ If `x2` is a scalar, its sign will be copied to all elements of `x1`.
Parameters
----------
x1 : array_like
Values to change the sign of.
x2 : array_like
- The sign of `x2` is copied to `x1`.
+ The sign of `x2` is copied to `x1`. $BROADCASTABLE_2
$PARAMS
Returns
@@ -3205,6 +3370,7 @@ add_newdoc('numpy.core.umath', 'nextafter',
Values to find the next representable value of.
x2 : array_like
The direction where to look for the next representable value of `x1`.
+ $BROADCASTABLE_2
$PARAMS
Returns
@@ -3353,6 +3519,7 @@ add_newdoc('numpy.core.umath', 'sinh',
>>> # Discrepancy due to vagaries of floating point arithmetic.
>>> # Example of providing the optional output parameter
+ >>> out1 = np.array([0], dtype='d')
>>> out2 = np.sinh([0.1], out1)
>>> out2 is out1
True
@@ -3361,7 +3528,7 @@ add_newdoc('numpy.core.umath', 'sinh',
>>> np.sinh(np.zeros((3,3)),np.zeros((2,2)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
- ValueError: invalid return array shape
+ ValueError: operands could not be broadcast together with shapes (3,3) (2,2)
""")
@@ -3406,8 +3573,8 @@ add_newdoc('numpy.core.umath', 'sqrt',
>>> np.sqrt([4, -1, -3+4J])
array([ 2.+0.j, 0.+1.j, 1.+2.j])
- >>> np.sqrt([4, -1, numpy.inf])
- array([ 2., NaN, Inf])
+ >>> np.sqrt([4, -1, np.inf])
+ array([ 2., nan, inf])
""")
@@ -3475,7 +3642,7 @@ add_newdoc('numpy.core.umath', 'subtract',
Parameters
----------
x1, x2 : array_like
- The arrays to be subtracted from each other.
+ The arrays to be subtracted from each other. $BROADCASTABLE_2
$PARAMS
Returns
@@ -3538,6 +3705,7 @@ add_newdoc('numpy.core.umath', 'tan',
>>>
>>> # Example of providing the optional output parameter illustrating
>>> # that what is returned is a reference to said parameter
+ >>> out1 = np.array([0], dtype='d')
>>> out2 = np.cos([0.1], out1)
>>> out2 is out1
True
@@ -3546,7 +3714,7 @@ add_newdoc('numpy.core.umath', 'tan',
>>> np.cos(np.zeros((3,3)),np.zeros((2,2)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
- ValueError: invalid return array shape
+ ValueError: operands could not be broadcast together with shapes (3,3) (2,2)
""")
@@ -3589,6 +3757,7 @@ add_newdoc('numpy.core.umath', 'tanh',
>>> # Example of providing the optional output parameter illustrating
>>> # that what is returned is a reference to said parameter
+ >>> out1 = np.array([0], dtype='d')
>>> out2 = np.tanh([0.1], out1)
>>> out2 is out1
True
@@ -3597,7 +3766,7 @@ add_newdoc('numpy.core.umath', 'tanh',
>>> np.tanh(np.zeros((3,3)),np.zeros((2,2)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
- ValueError: invalid return array shape
+ ValueError: operands could not be broadcast together with shapes (3,3) (2,2)
""")
@@ -3614,7 +3783,7 @@ add_newdoc('numpy.core.umath', 'true_divide',
x1 : array_like
Dividend array.
x2 : array_like
- Divisor array.
+ Divisor array. $BROADCASTABLE_2
$PARAMS
Returns
@@ -3639,8 +3808,6 @@ add_newdoc('numpy.core.umath', 'true_divide',
>>> np.true_divide(x, 4)
array([ 0. , 0.25, 0.5 , 0.75, 1. ])
- >>> x/4
- array([0, 0, 0, 0, 1])
>>> x//4
array([0, 0, 0, 0, 1])
@@ -3713,7 +3880,7 @@ add_newdoc('numpy.core.umath', 'ldexp',
x1 : array_like
Array of multipliers.
x2 : array_like, int
- Array of twos exponents.
+ Array of twos exponents. $BROADCASTABLE_2
$PARAMS
Returns
@@ -3736,7 +3903,7 @@ add_newdoc('numpy.core.umath', 'ldexp',
Examples
--------
>>> np.ldexp(5, np.arange(4))
- array([ 5., 10., 20., 40.], dtype=float32)
+ array([ 5., 10., 20., 40.], dtype=float16)
>>> x = np.arange(6)
>>> np.ldexp(*np.frexp(x))
@@ -3751,7 +3918,7 @@ add_newdoc('numpy.core.umath', 'gcd',
Parameters
----------
x1, x2 : array_like, int
- Arrays of values
+ Arrays of values. $BROADCASTABLE_2
Returns
-------
@@ -3781,7 +3948,7 @@ add_newdoc('numpy.core.umath', 'lcm',
Parameters
----------
x1, x2 : array_like, int
- Arrays of values
+ Arrays of values. $BROADCASTABLE_2
Returns
-------
diff --git a/numpy/core/defchararray.py b/numpy/core/defchararray.py
index 6d0a0add5..a941c5b81 100644
--- a/numpy/core/defchararray.py
+++ b/numpy/core/defchararray.py
@@ -17,16 +17,19 @@ The preferred alias for `defchararray` is `numpy.char`.
"""
from __future__ import division, absolute_import, print_function
+import functools
import sys
from .numerictypes import string_, unicode_, integer, object_, bool_, character
from .numeric import ndarray, compare_chararrays
from .numeric import array as narray
from numpy.core.multiarray import _vec_string
+from numpy.core.overrides import set_module
+from numpy.core import overrides
from numpy.compat import asbytes, long
import numpy
__all__ = [
- 'chararray', 'equal', 'not_equal', 'greater_equal', 'less_equal',
+ 'equal', 'not_equal', 'greater_equal', 'less_equal',
'greater', 'less', 'str_len', 'add', 'multiply', 'mod', 'capitalize',
'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs',
'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace',
@@ -47,6 +50,10 @@ else:
_bytes = str
_len = len
+array_function_dispatch = functools.partial(
+ overrides.array_function_dispatch, module='numpy.char')
+
+
def _use_unicode(*args):
"""
Helper function for determining the output type of some string
@@ -95,6 +102,11 @@ def _get_num_chars(a):
return a.itemsize
+def _binary_op_dispatcher(x1, x2):
+ return (x1, x2)
+
+
+@array_function_dispatch(_binary_op_dispatcher)
def equal(x1, x2):
"""
Return (x1 == x2) element-wise.
@@ -119,6 +131,8 @@ def equal(x1, x2):
"""
return compare_chararrays(x1, x2, '==', True)
+
+@array_function_dispatch(_binary_op_dispatcher)
def not_equal(x1, x2):
"""
Return (x1 != x2) element-wise.
@@ -143,6 +157,8 @@ def not_equal(x1, x2):
"""
return compare_chararrays(x1, x2, '!=', True)
+
+@array_function_dispatch(_binary_op_dispatcher)
def greater_equal(x1, x2):
"""
Return (x1 >= x2) element-wise.
@@ -168,6 +184,8 @@ def greater_equal(x1, x2):
"""
return compare_chararrays(x1, x2, '>=', True)
+
+@array_function_dispatch(_binary_op_dispatcher)
def less_equal(x1, x2):
"""
Return (x1 <= x2) element-wise.
@@ -192,6 +210,8 @@ def less_equal(x1, x2):
"""
return compare_chararrays(x1, x2, '<=', True)
+
+@array_function_dispatch(_binary_op_dispatcher)
def greater(x1, x2):
"""
Return (x1 > x2) element-wise.
@@ -216,6 +236,8 @@ def greater(x1, x2):
"""
return compare_chararrays(x1, x2, '>', True)
+
+@array_function_dispatch(_binary_op_dispatcher)
def less(x1, x2):
"""
Return (x1 < x2) element-wise.
@@ -240,6 +262,12 @@ def less(x1, x2):
"""
return compare_chararrays(x1, x2, '<', True)
+
+def _unary_op_dispatcher(a):
+ return (a,)
+
+
+@array_function_dispatch(_unary_op_dispatcher)
def str_len(a):
"""
Return len(a) element-wise.
@@ -259,6 +287,8 @@ def str_len(a):
"""
return _vec_string(a, integer, '__len__')
+
+@array_function_dispatch(_binary_op_dispatcher)
def add(x1, x2):
"""
Return element-wise string concatenation for two arrays of str or unicode.
@@ -285,6 +315,12 @@ def add(x1, x2):
dtype = _use_unicode(arr1, arr2)
return _vec_string(arr1, (dtype, out_size), '__add__', (arr2,))
+
+def _multiply_dispatcher(a, i):
+ return (a,)
+
+
+@array_function_dispatch(_multiply_dispatcher)
def multiply(a, i):
"""
Return (a * i), that is string multiple concatenation,
@@ -313,6 +349,12 @@ def multiply(a, i):
return _vec_string(
a_arr, (a_arr.dtype.type, out_size), '__mul__', (i_arr,))
+
+def _mod_dispatcher(a, values):
+ return (a, values)
+
+
+@array_function_dispatch(_mod_dispatcher)
def mod(a, values):
"""
Return (a % i), that is pre-Python 2.6 string formatting
@@ -339,6 +381,8 @@ def mod(a, values):
return _to_string_or_unicode_array(
_vec_string(a, object_, '__mod__', (values,)))
+
+@array_function_dispatch(_unary_op_dispatcher)
def capitalize(a):
"""
Return a copy of `a` with only the first character of each element
@@ -377,6 +421,11 @@ def capitalize(a):
return _vec_string(a_arr, a_arr.dtype, 'capitalize')
+def _center_dispatcher(a, width, fillchar=None):
+ return (a,)
+
+
+@array_function_dispatch(_center_dispatcher)
def center(a, width, fillchar=' '):
"""
Return a copy of `a` with its elements centered in a string of
@@ -413,6 +462,11 @@ def center(a, width, fillchar=' '):
a_arr, (a_arr.dtype.type, size), 'center', (width_arr, fillchar))
+def _count_dispatcher(a, sub, start=None, end=None):
+ return (a,)
+
+
+@array_function_dispatch(_count_dispatcher)
def count(a, sub, start=0, end=None):
"""
Returns an array with the number of non-overlapping occurrences of
@@ -444,8 +498,7 @@ def count(a, sub, start=0, end=None):
--------
>>> c = np.array(['aAaAaA', ' aA ', 'abBABba'])
>>> c
- array(['aAaAaA', ' aA ', 'abBABba'],
- dtype='|S7')
+ array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7')
>>> np.char.count(c, 'A')
array([3, 1, 1])
>>> np.char.count(c, 'aA')
@@ -459,6 +512,11 @@ def count(a, sub, start=0, end=None):
return _vec_string(a, integer, 'count', [sub, start] + _clean_args(end))
+def _code_dispatcher(a, encoding=None, errors=None):
+ return (a,)
+
+
+@array_function_dispatch(_code_dispatcher)
def decode(a, encoding=None, errors=None):
"""
Calls `str.decode` element-wise.
@@ -493,8 +551,7 @@ def decode(a, encoding=None, errors=None):
--------
>>> c = np.array(['aAaAaA', ' aA ', 'abBABba'])
>>> c
- array(['aAaAaA', ' aA ', 'abBABba'],
- dtype='|S7')
+ array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7')
>>> np.char.encode(c, encoding='cp037')
array(['\\x81\\xc1\\x81\\xc1\\x81\\xc1', '@@\\x81\\xc1@@',
'\\x81\\x82\\xc2\\xc1\\xc2\\x82\\x81'],
@@ -505,6 +562,7 @@ def decode(a, encoding=None, errors=None):
_vec_string(a, object_, 'decode', _clean_args(encoding, errors)))
+@array_function_dispatch(_code_dispatcher)
def encode(a, encoding=None, errors=None):
"""
Calls `str.encode` element-wise.
@@ -540,6 +598,11 @@ def encode(a, encoding=None, errors=None):
_vec_string(a, object_, 'encode', _clean_args(encoding, errors)))
+def _endswith_dispatcher(a, suffix, start=None, end=None):
+ return (a,)
+
+
+@array_function_dispatch(_endswith_dispatcher)
def endswith(a, suffix, start=0, end=None):
"""
Returns a boolean array which is `True` where the string element
@@ -572,8 +635,7 @@ def endswith(a, suffix, start=0, end=None):
>>> s[0] = 'foo'
>>> s[1] = 'bar'
>>> s
- array(['foo', 'bar'],
- dtype='|S3')
+ array(['foo', 'bar'], dtype='<U3')
>>> np.char.endswith(s, 'ar')
array([False, True])
>>> np.char.endswith(s, 'a', start=1, end=2)
@@ -584,6 +646,11 @@ def endswith(a, suffix, start=0, end=None):
a, bool_, 'endswith', [suffix, start] + _clean_args(end))
+def _expandtabs_dispatcher(a, tabsize=None):
+ return (a,)
+
+
+@array_function_dispatch(_expandtabs_dispatcher)
def expandtabs(a, tabsize=8):
"""
Return a copy of each string element where all tab characters are
@@ -619,6 +686,7 @@ def expandtabs(a, tabsize=8):
_vec_string(a, object_, 'expandtabs', (tabsize,)))
+@array_function_dispatch(_count_dispatcher)
def find(a, sub, start=0, end=None):
"""
For each element, return the lowest index in the string where
@@ -654,6 +722,7 @@ def find(a, sub, start=0, end=None):
a, integer, 'find', [sub, start] + _clean_args(end))
+@array_function_dispatch(_count_dispatcher)
def index(a, sub, start=0, end=None):
"""
Like `find`, but raises `ValueError` when the substring is not found.
@@ -681,6 +750,8 @@ def index(a, sub, start=0, end=None):
return _vec_string(
a, integer, 'index', [sub, start] + _clean_args(end))
+
+@array_function_dispatch(_unary_op_dispatcher)
def isalnum(a):
"""
Returns true for each element if all characters in the string are
@@ -705,6 +776,8 @@ def isalnum(a):
"""
return _vec_string(a, bool_, 'isalnum')
+
+@array_function_dispatch(_unary_op_dispatcher)
def isalpha(a):
"""
Returns true for each element if all characters in the string are
@@ -729,6 +802,8 @@ def isalpha(a):
"""
return _vec_string(a, bool_, 'isalpha')
+
+@array_function_dispatch(_unary_op_dispatcher)
def isdigit(a):
"""
Returns true for each element if all characters in the string are
@@ -753,6 +828,8 @@ def isdigit(a):
"""
return _vec_string(a, bool_, 'isdigit')
+
+@array_function_dispatch(_unary_op_dispatcher)
def islower(a):
"""
Returns true for each element if all cased characters in the
@@ -778,6 +855,8 @@ def islower(a):
"""
return _vec_string(a, bool_, 'islower')
+
+@array_function_dispatch(_unary_op_dispatcher)
def isspace(a):
"""
Returns true for each element if there are only whitespace
@@ -803,6 +882,8 @@ def isspace(a):
"""
return _vec_string(a, bool_, 'isspace')
+
+@array_function_dispatch(_unary_op_dispatcher)
def istitle(a):
"""
Returns true for each element if the element is a titlecased
@@ -827,6 +908,8 @@ def istitle(a):
"""
return _vec_string(a, bool_, 'istitle')
+
+@array_function_dispatch(_unary_op_dispatcher)
def isupper(a):
"""
Returns true for each element if all cased characters in the
@@ -852,6 +935,12 @@ def isupper(a):
"""
return _vec_string(a, bool_, 'isupper')
+
+def _join_dispatcher(sep, seq):
+ return (sep, seq)
+
+
+@array_function_dispatch(_join_dispatcher)
def join(sep, seq):
"""
Return a string which is the concatenation of the strings in the
@@ -877,6 +966,12 @@ def join(sep, seq):
_vec_string(sep, object_, 'join', (seq,)))
+
+def _just_dispatcher(a, width, fillchar=None):
+ return (a,)
+
+
+@array_function_dispatch(_just_dispatcher)
def ljust(a, width, fillchar=' '):
"""
Return an array with the elements of `a` left-justified in a
@@ -912,6 +1007,7 @@ def ljust(a, width, fillchar=' '):
a_arr, (a_arr.dtype.type, size), 'ljust', (width_arr, fillchar))
+@array_function_dispatch(_unary_op_dispatcher)
def lower(a):
"""
Return an array with the elements converted to lowercase.
@@ -937,17 +1033,20 @@ def lower(a):
Examples
--------
>>> c = np.array(['A1B C', '1BCA', 'BCA1']); c
- array(['A1B C', '1BCA', 'BCA1'],
- dtype='|S5')
+ array(['A1B C', '1BCA', 'BCA1'], dtype='<U5')
>>> np.char.lower(c)
- array(['a1b c', '1bca', 'bca1'],
- dtype='|S5')
+ array(['a1b c', '1bca', 'bca1'], dtype='<U5')
"""
a_arr = numpy.asarray(a)
return _vec_string(a_arr, a_arr.dtype, 'lower')
+def _strip_dispatcher(a, chars=None):
+ return (a,)
+
+
+@array_function_dispatch(_strip_dispatcher)
def lstrip(a, chars=None):
"""
For each element in `a`, return a copy with the leading characters
@@ -980,23 +1079,20 @@ def lstrip(a, chars=None):
--------
>>> c = np.array(['aAaAaA', ' aA ', 'abBABba'])
>>> c
- array(['aAaAaA', ' aA ', 'abBABba'],
- dtype='|S7')
+ array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7')
The 'a' variable is unstripped from c[1] because whitespace leading.
>>> np.char.lstrip(c, 'a')
- array(['AaAaA', ' aA ', 'bBABba'],
- dtype='|S7')
+ array(['AaAaA', ' aA ', 'bBABba'], dtype='<U7')
>>> np.char.lstrip(c, 'A') # leaves c unchanged
- array(['aAaAaA', ' aA ', 'abBABba'],
- dtype='|S7')
+ array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7')
>>> (np.char.lstrip(c, ' ') == np.char.lstrip(c, '')).all()
- ... # XXX: is this a regression? this line now returns False
+ ... # XXX: is this a regression? This used to return True
... # np.char.lstrip(c,'') does not modify c at all.
- True
+ False
>>> (np.char.lstrip(c, ' ') == np.char.lstrip(c, None)).all()
True
@@ -1005,6 +1101,11 @@ def lstrip(a, chars=None):
return _vec_string(a_arr, a_arr.dtype, 'lstrip', (chars,))
+def _partition_dispatcher(a, sep):
+ return (a,)
+
+
+@array_function_dispatch(_partition_dispatcher)
def partition(a, sep):
"""
Partition each element in `a` around `sep`.
@@ -1040,6 +1141,11 @@ def partition(a, sep):
_vec_string(a, object_, 'partition', (sep,)))
+def _replace_dispatcher(a, old, new, count=None):
+ return (a,)
+
+
+@array_function_dispatch(_replace_dispatcher)
def replace(a, old, new, count=None):
"""
For each element in `a`, return a copy of the string with all
@@ -1072,6 +1178,7 @@ def replace(a, old, new, count=None):
a, object_, 'replace', [old, new] + _clean_args(count)))
+@array_function_dispatch(_count_dispatcher)
def rfind(a, sub, start=0, end=None):
"""
For each element in `a`, return the highest index in the string
@@ -1104,6 +1211,7 @@ def rfind(a, sub, start=0, end=None):
a, integer, 'rfind', [sub, start] + _clean_args(end))
+@array_function_dispatch(_count_dispatcher)
def rindex(a, sub, start=0, end=None):
"""
Like `rfind`, but raises `ValueError` when the substring `sub` is
@@ -1133,6 +1241,7 @@ def rindex(a, sub, start=0, end=None):
a, integer, 'rindex', [sub, start] + _clean_args(end))
+@array_function_dispatch(_just_dispatcher)
def rjust(a, width, fillchar=' '):
"""
Return an array with the elements of `a` right-justified in a
@@ -1168,6 +1277,7 @@ def rjust(a, width, fillchar=' '):
a_arr, (a_arr.dtype.type, size), 'rjust', (width_arr, fillchar))
+@array_function_dispatch(_partition_dispatcher)
def rpartition(a, sep):
"""
Partition (split) each element around the right-most separator.
@@ -1203,6 +1313,11 @@ def rpartition(a, sep):
_vec_string(a, object_, 'rpartition', (sep,)))
+def _split_dispatcher(a, sep=None, maxsplit=None):
+ return (a,)
+
+
+@array_function_dispatch(_split_dispatcher)
def rsplit(a, sep=None, maxsplit=None):
"""
For each element in `a`, return a list of the words in the
@@ -1240,6 +1355,11 @@ def rsplit(a, sep=None, maxsplit=None):
a, object_, 'rsplit', [sep] + _clean_args(maxsplit))
+def _strip_dispatcher(a, chars=None):
+ return (a,)
+
+
+@array_function_dispatch(_strip_dispatcher)
def rstrip(a, chars=None):
"""
For each element in `a`, return a copy with the trailing
@@ -1272,10 +1392,10 @@ def rstrip(a, chars=None):
>>> c = np.array(['aAaAaA', 'abBABba'], dtype='S7'); c
array(['aAaAaA', 'abBABba'],
dtype='|S7')
- >>> np.char.rstrip(c, 'a')
+ >>> np.char.rstrip(c, b'a')
array(['aAaAaA', 'abBABb'],
dtype='|S7')
- >>> np.char.rstrip(c, 'A')
+ >>> np.char.rstrip(c, b'A')
array(['aAaAa', 'abBABba'],
dtype='|S7')
@@ -1284,6 +1404,7 @@ def rstrip(a, chars=None):
return _vec_string(a_arr, a_arr.dtype, 'rstrip', (chars,))
+@array_function_dispatch(_split_dispatcher)
def split(a, sep=None, maxsplit=None):
"""
For each element in `a`, return a list of the words in the
@@ -1318,6 +1439,11 @@ def split(a, sep=None, maxsplit=None):
a, object_, 'split', [sep] + _clean_args(maxsplit))
+def _splitlines_dispatcher(a, keepends=None):
+ return (a,)
+
+
+@array_function_dispatch(_splitlines_dispatcher)
def splitlines(a, keepends=None):
"""
For each element in `a`, return a list of the lines in the
@@ -1347,6 +1473,11 @@ def splitlines(a, keepends=None):
a, object_, 'splitlines', _clean_args(keepends))
+def _startswith_dispatcher(a, prefix, start=None, end=None):
+ return (a,)
+
+
+@array_function_dispatch(_startswith_dispatcher)
def startswith(a, prefix, start=0, end=None):
"""
Returns a boolean array which is `True` where the string element
@@ -1378,6 +1509,7 @@ def startswith(a, prefix, start=0, end=None):
a, bool_, 'startswith', [prefix, start] + _clean_args(end))
+@array_function_dispatch(_strip_dispatcher)
def strip(a, chars=None):
"""
For each element in `a`, return a copy with the leading and
@@ -1409,23 +1541,20 @@ def strip(a, chars=None):
--------
>>> c = np.array(['aAaAaA', ' aA ', 'abBABba'])
>>> c
- array(['aAaAaA', ' aA ', 'abBABba'],
- dtype='|S7')
+ array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7')
>>> np.char.strip(c)
- array(['aAaAaA', 'aA', 'abBABba'],
- dtype='|S7')
+ array(['aAaAaA', 'aA', 'abBABba'], dtype='<U7')
>>> np.char.strip(c, 'a') # 'a' unstripped from c[1] because whitespace leads
- array(['AaAaA', ' aA ', 'bBABb'],
- dtype='|S7')
+ array(['AaAaA', ' aA ', 'bBABb'], dtype='<U7')
>>> np.char.strip(c, 'A') # 'A' unstripped from c[1] because (unprinted) ws trails
- array(['aAaAa', ' aA ', 'abBABba'],
- dtype='|S7')
+ array(['aAaAa', ' aA ', 'abBABba'], dtype='<U7')
"""
a_arr = numpy.asarray(a)
return _vec_string(a_arr, a_arr.dtype, 'strip', _clean_args(chars))
+@array_function_dispatch(_unary_op_dispatcher)
def swapcase(a):
"""
Return element-wise a copy of the string with
@@ -1463,6 +1592,7 @@ def swapcase(a):
return _vec_string(a_arr, a_arr.dtype, 'swapcase')
+@array_function_dispatch(_unary_op_dispatcher)
def title(a):
"""
Return element-wise title cased version of string or unicode.
@@ -1502,6 +1632,11 @@ def title(a):
return _vec_string(a_arr, a_arr.dtype, 'title')
+def _translate_dispatcher(a, table, deletechars=None):
+ return (a,)
+
+
+@array_function_dispatch(_translate_dispatcher)
def translate(a, table, deletechars=None):
"""
For each element in `a`, return a copy of the string where all
@@ -1538,6 +1673,7 @@ def translate(a, table, deletechars=None):
a_arr, a_arr.dtype, 'translate', [table] + _clean_args(deletechars))
+@array_function_dispatch(_unary_op_dispatcher)
def upper(a):
"""
Return an array with the elements converted to uppercase.
@@ -1563,17 +1699,20 @@ def upper(a):
Examples
--------
>>> c = np.array(['a1b c', '1bca', 'bca1']); c
- array(['a1b c', '1bca', 'bca1'],
- dtype='|S5')
+ array(['a1b c', '1bca', 'bca1'], dtype='<U5')
>>> np.char.upper(c)
- array(['A1B C', '1BCA', 'BCA1'],
- dtype='|S5')
+ array(['A1B C', '1BCA', 'BCA1'], dtype='<U5')
"""
a_arr = numpy.asarray(a)
return _vec_string(a_arr, a_arr.dtype, 'upper')
+def _zfill_dispatcher(a, width):
+ return (a,)
+
+
+@array_function_dispatch(_zfill_dispatcher)
def zfill(a, width):
"""
Return the numeric string left-filled with zeros
@@ -1604,6 +1743,7 @@ def zfill(a, width):
a_arr, (a_arr.dtype.type, size), 'zfill', (width_arr,))
+@array_function_dispatch(_unary_op_dispatcher)
def isnumeric(a):
"""
For each element, return True if there are only numeric
@@ -1635,6 +1775,7 @@ def isnumeric(a):
return _vec_string(a, bool_, 'isnumeric')
+@array_function_dispatch(_unary_op_dispatcher)
def isdecimal(a):
"""
For each element, return True if there are only decimal
@@ -1666,6 +1807,7 @@ def isdecimal(a):
return _vec_string(a, bool_, 'isdecimal')
+@set_module('numpy')
class chararray(ndarray):
"""
chararray(shape, itemsize=1, unicode=False, buffer=None, offset=0,
@@ -1698,7 +1840,7 @@ class chararray(ndarray):
This constructor creates the array, using `buffer` (with `offset`
and `strides`) if it is not ``None``. If `buffer` is ``None``, then
constructs a new array with `strides` in "C order", unless both
- ``len(shape) >= 2`` and ``order='Fortran'``, in which case `strides`
+ ``len(shape) >= 2`` and ``order='F'``, in which case `strides`
is in "Fortran order".
Methods
@@ -1794,18 +1936,16 @@ class chararray(ndarray):
>>> charar = np.chararray((3, 3))
>>> charar[:] = 'a'
>>> charar
- chararray([['a', 'a', 'a'],
- ['a', 'a', 'a'],
- ['a', 'a', 'a']],
- dtype='|S1')
+ chararray([[b'a', b'a', b'a'],
+ [b'a', b'a', b'a'],
+ [b'a', b'a', b'a']], dtype='|S1')
>>> charar = np.chararray(charar.shape, itemsize=5)
>>> charar[:] = 'abc'
>>> charar
- chararray([['abc', 'abc', 'abc'],
- ['abc', 'abc', 'abc'],
- ['abc', 'abc', 'abc']],
- dtype='|S5')
+ chararray([[b'abc', b'abc', b'abc'],
+ [b'abc', b'abc', b'abc'],
+ [b'abc', b'abc', b'abc']], dtype='|S5')
"""
def __new__(subtype, shape, itemsize=1, unicode=False, buffer=None,
@@ -1984,7 +2124,7 @@ class chararray(ndarray):
def __rmod__(self, other):
return NotImplemented
- def argsort(self, axis=-1, kind='quicksort', order=None):
+ def argsort(self, axis=-1, kind=None, order=None):
"""
Return the indices that sort the array lexicographically.
diff --git a/numpy/core/einsumfunc.py b/numpy/core/einsumfunc.py
index 1281b3c98..3412c3fd5 100644
--- a/numpy/core/einsumfunc.py
+++ b/numpy/core/einsumfunc.py
@@ -9,6 +9,7 @@ import itertools
from numpy.compat import basestring
from numpy.core.multiarray import c_einsum
from numpy.core.numeric import asanyarray, tensordot
+from numpy.core.overrides import array_function_dispatch
__all__ = ['einsum', 'einsum_path']
@@ -40,10 +41,10 @@ def _flop_count(idx_contraction, inner, num_terms, size_dictionary):
--------
>>> _flop_count('abc', False, 1, {'a': 2, 'b':3, 'c':5})
- 90
+ 30
>>> _flop_count('abc', True, 2, {'a': 2, 'b':3, 'c':5})
- 270
+ 60
"""
@@ -168,9 +169,9 @@ def _optimal_path(input_sets, output_set, idx_dict, memory_limit):
Examples
--------
>>> isets = [set('abd'), set('ac'), set('bdc')]
- >>> oset = set('')
+ >>> oset = set()
>>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
- >>> _path__optimal_path(isets, oset, idx_sizes, 5000)
+ >>> _optimal_path(isets, oset, idx_sizes, 5000)
[(0, 2), (0, 1)]
"""
@@ -286,7 +287,7 @@ def _update_other_results(results, best):
Returns
-------
mod_results : list
- The list of modifed results, updated with outcome of ``best`` contraction.
+ The list of modified results, updated with outcome of ``best`` contraction.
"""
best_con = best[1]
@@ -339,9 +340,9 @@ def _greedy_path(input_sets, output_set, idx_dict, memory_limit):
Examples
--------
>>> isets = [set('abd'), set('ac'), set('bdc')]
- >>> oset = set('')
+ >>> oset = set()
>>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
- >>> _path__greedy_path(isets, oset, idx_sizes, 5000)
+ >>> _greedy_path(isets, oset, idx_sizes, 5000)
[(0, 2), (0, 1)]
"""
@@ -538,13 +539,14 @@ def _parse_einsum_input(operands):
--------
The operand list is simplified to reduce printing:
+ >>> np.random.seed(123)
>>> a = np.random.rand(4, 4)
>>> b = np.random.rand(4, 4, 4)
- >>> __parse_einsum_input(('...a,...a->...', a, b))
- ('za,xza', 'xz', [a, b])
+ >>> _parse_einsum_input(('...a,...a->...', a, b))
+ ('za,xza', 'xz', [a, b]) # may vary
- >>> __parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0]))
- ('za,xza', 'xz', [a, b])
+ >>> _parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0]))
+ ('za,xza', 'xz', [a, b]) # may vary
"""
if len(operands) == 0:
@@ -689,6 +691,17 @@ def _parse_einsum_input(operands):
return (input_subscripts, output_subscript, operands)
+def _einsum_path_dispatcher(*operands, **kwargs):
+ # NOTE: technically, we should only dispatch on array-like arguments, not
+ # subscripts (given as strings). But separating operands into
+ # arrays/subscripts is a little tricky/slow (given einsum's two supported
+ # signatures), so as a practical shortcut we dispatch on everything.
+ # Strings will be ignored for dispatching since they don't define
+ # __array_function__.
+ return operands
+
+
+@array_function_dispatch(_einsum_path_dispatcher, module='numpy')
def einsum_path(*operands, **kwargs):
"""
einsum_path(subscripts, *operands, optimize='greedy')
@@ -751,6 +764,7 @@ def einsum_path(*operands, **kwargs):
of the contraction and the remaining contraction ``(0, 1)`` is then
completed.
+ >>> np.random.seed(123)
>>> a = np.random.rand(2, 2)
>>> b = np.random.rand(2, 5)
>>> c = np.random.rand(5, 2)
@@ -758,7 +772,7 @@ def einsum_path(*operands, **kwargs):
>>> print(path_info[0])
['einsum_path', (1, 2), (0, 1)]
>>> print(path_info[1])
- Complete contraction: ij,jk,kl->il
+ Complete contraction: ij,jk,kl->il # may vary
Naive scaling: 4
Optimized scaling: 3
Naive FLOP count: 1.600e+02
@@ -777,12 +791,12 @@ def einsum_path(*operands, **kwargs):
>>> I = np.random.rand(10, 10, 10, 10)
>>> C = np.random.rand(10, 10)
>>> path_info = np.einsum_path('ea,fb,abcd,gc,hd->efgh', C, C, I, C, C,
- optimize='greedy')
+ ... optimize='greedy')
>>> print(path_info[0])
['einsum_path', (0, 2), (0, 3), (0, 2), (0, 1)]
- >>> print(path_info[1])
- Complete contraction: ea,fb,abcd,gc,hd->efgh
+ >>> print(path_info[1])
+ Complete contraction: ea,fb,abcd,gc,hd->efgh # may vary
Naive scaling: 8
Optimized scaling: 5
Naive FLOP count: 8.000e+08
@@ -837,7 +851,6 @@ def einsum_path(*operands, **kwargs):
# Python side parsing
input_subscripts, output_subscript, operands = _parse_einsum_input(operands)
- subscripts = input_subscripts + '->' + output_subscript
# Build a few useful list and sets
input_list = input_subscripts.split(',')
@@ -876,9 +889,8 @@ def einsum_path(*operands, **kwargs):
broadcast_indices = [set(x) for x in broadcast_indices]
# Compute size of each input array plus the output array
- size_list = []
- for term in input_list + [output_subscript]:
- size_list.append(_compute_size_by_dict(term, dimension_dict))
+ size_list = [_compute_size_by_dict(term, dimension_dict)
+ for term in input_list + [output_subscript]]
max_size = max(size_list)
if memory_limit is None:
@@ -980,7 +992,16 @@ def einsum_path(*operands, **kwargs):
return (path, path_print)
+def _einsum_dispatcher(*operands, **kwargs):
+ # Arguably we dispatch on more arguments that we really should; see note in
+ # _einsum_path_dispatcher for why.
+ for op in operands:
+ yield op
+ yield kwargs.get('out')
+
+
# Rewrite einsum to handle different cases
+@array_function_dispatch(_einsum_dispatcher, module='numpy')
def einsum(*operands, **kwargs):
"""
einsum(subscripts, *operands, out=None, dtype=None, order='K',
@@ -1255,32 +1276,32 @@ def einsum(*operands, **kwargs):
>>> a = np.arange(60.).reshape(3,4,5)
>>> b = np.arange(24.).reshape(4,3,2)
>>> np.einsum('ijk,jil->kl', a, b)
- array([[ 4400., 4730.],
- [ 4532., 4874.],
- [ 4664., 5018.],
- [ 4796., 5162.],
- [ 4928., 5306.]])
+ array([[4400., 4730.],
+ [4532., 4874.],
+ [4664., 5018.],
+ [4796., 5162.],
+ [4928., 5306.]])
>>> np.einsum(a, [0,1,2], b, [1,0,3], [2,3])
- array([[ 4400., 4730.],
- [ 4532., 4874.],
- [ 4664., 5018.],
- [ 4796., 5162.],
- [ 4928., 5306.]])
+ array([[4400., 4730.],
+ [4532., 4874.],
+ [4664., 5018.],
+ [4796., 5162.],
+ [4928., 5306.]])
>>> np.tensordot(a,b, axes=([1,0],[0,1]))
- array([[ 4400., 4730.],
- [ 4532., 4874.],
- [ 4664., 5018.],
- [ 4796., 5162.],
- [ 4928., 5306.]])
+ array([[4400., 4730.],
+ [4532., 4874.],
+ [4664., 5018.],
+ [4796., 5162.],
+ [4928., 5306.]])
Writeable returned arrays (since version 1.10.0):
>>> a = np.zeros((3, 3))
>>> np.einsum('ii->i', a)[:] = 1
>>> a
- array([[ 1., 0., 0.],
- [ 0., 1., 0.],
- [ 0., 0., 1.]])
+ array([[1., 0., 0.],
+ [0., 1., 0.],
+ [0., 0., 1.]])
Example of ellipsis use:
@@ -1303,19 +1324,27 @@ def einsum(*operands, **kwargs):
particularly significant with larger arrays:
>>> a = np.ones(64).reshape(2,4,8)
- # Basic `einsum`: ~1520ms (benchmarked on 3.1GHz Intel i5.)
+
+ Basic `einsum`: ~1520ms (benchmarked on 3.1GHz Intel i5.)
+
>>> for iteration in range(500):
- ... np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a)
- # Sub-optimal `einsum` (due to repeated path calculation time): ~330ms
+ ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a)
+
+ Sub-optimal `einsum` (due to repeated path calculation time): ~330ms
+
>>> for iteration in range(500):
- ... np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')
- # Greedy `einsum` (faster optimal path approximation): ~160ms
+ ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')
+
+ Greedy `einsum` (faster optimal path approximation): ~160ms
+
>>> for iteration in range(500):
- ... np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='greedy')
- # Optimal `einsum` (best usage pattern in some use cases): ~110ms
+ ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='greedy')
+
+ Optimal `einsum` (best usage pattern in some use cases): ~110ms
+
>>> path = np.einsum_path('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')[0]
>>> for iteration in range(500):
- ... np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize=path)
+ ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize=path)
"""
@@ -1354,9 +1383,7 @@ def einsum(*operands, **kwargs):
# Start contraction loop
for num, contraction in enumerate(contraction_list):
inds, idx_rm, einsum_str, remaining, blas = contraction
- tmp_operands = []
- for x in inds:
- tmp_operands.append(operands.pop(x))
+ tmp_operands = [operands.pop(x) for x in inds]
# Do we need to deal with the output?
handle_out = specified_out and ((num + 1) == len(contraction_list))
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
index b9cc98cae..6c0b9cde9 100644
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -3,15 +3,18 @@
"""
from __future__ import division, absolute_import, print_function
+import functools
import types
import warnings
import numpy as np
from .. import VisibleDeprecationWarning
from . import multiarray as mu
+from . import overrides
from . import umath as um
from . import numerictypes as nt
-from .numeric import asarray, array, asanyarray, concatenate
+from ._asarray import asarray, array, asanyarray
+from .multiarray import concatenate
from . import _methods
_dt_ = nt.sctype2char
@@ -22,7 +25,7 @@ __all__ = [
'argmin', 'argpartition', 'argsort', 'around', 'choose', 'clip',
'compress', 'cumprod', 'cumproduct', 'cumsum', 'diagonal', 'mean',
'ndim', 'nonzero', 'partition', 'prod', 'product', 'ptp', 'put',
- 'rank', 'ravel', 'repeat', 'reshape', 'resize', 'round_',
+ 'ravel', 'repeat', 'reshape', 'resize', 'round_',
'searchsorted', 'shape', 'size', 'sometrue', 'sort', 'squeeze',
'std', 'sum', 'swapaxes', 'take', 'trace', 'transpose', 'var',
]
@@ -31,6 +34,9 @@ _gentype = types.GeneratorType
# save away Python sum
_sum_ = sum
+array_function_dispatch = functools.partial(
+ overrides.array_function_dispatch, module='numpy')
+
# functions that are now methods
def _wrapit(obj, method, *args, **kwds):
@@ -47,25 +53,26 @@ def _wrapit(obj, method, *args, **kwds):
def _wrapfunc(obj, method, *args, **kwds):
- try:
- return getattr(obj, method)(*args, **kwds)
-
- # An AttributeError occurs if the object does not have
- # such a method in its class.
+ bound = getattr(obj, method, None)
+ if bound is None:
+ return _wrapit(obj, method, *args, **kwds)
- # A TypeError occurs if the object does have such a method
- # in its class, but its signature is not identical to that
- # of NumPy's. This situation has occurred in the case of
- # a downstream library like 'pandas'.
- except (AttributeError, TypeError):
+ try:
+ return bound(*args, **kwds)
+ except TypeError:
+ # A TypeError occurs if the object does have such a method in its
+ # class, but its signature is not identical to that of NumPy's. This
+ # situation has occurred in the case of a downstream library like
+ # 'pandas'.
+ #
+ # Call _wrapit from within the except clause to ensure a potential
+ # exception has a traceback chain.
return _wrapit(obj, method, *args, **kwds)
def _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs):
- passkwargs = {}
- for k, v in kwargs.items():
- if v is not np._NoValue:
- passkwargs[k] = v
+ passkwargs = {k: v for k, v in kwargs.items()
+ if v is not np._NoValue}
if type(obj) is not mu.ndarray:
try:
@@ -83,6 +90,11 @@ def _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs):
return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
+def _take_dispatcher(a, indices, axis=None, out=None, mode=None):
+ return (a, out)
+
+
+@array_function_dispatch(_take_dispatcher)
def take(a, indices, axis=None, out=None, mode='raise'):
"""
Take elements from an array along an axis.
@@ -119,7 +131,8 @@ def take(a, indices, axis=None, out=None, mode='raise'):
input array is used.
out : ndarray, optional (Ni..., Nj..., Nk...)
If provided, the result will be placed in this array. It should
- be of the appropriate shape and dtype.
+ be of the appropriate shape and dtype. Note that `out` is always
+ buffered if `mode='raise'`; use other modes for better performance.
mode : {'raise', 'wrap', 'clip'}, optional
Specifies how out-of-bounds indices will behave.
@@ -181,7 +194,12 @@ def take(a, indices, axis=None, out=None, mode='raise'):
return _wrapfunc(a, 'take', indices, axis=axis, out=out, mode=mode)
+def _reshape_dispatcher(a, newshape, order=None):
+ return (a,)
+
+
# not deprecated --- copy if necessary, view otherwise
+@array_function_dispatch(_reshape_dispatcher)
def reshape(a, newshape, order='C'):
"""
Gives a new shape to an array without changing its data.
@@ -227,12 +245,16 @@ def reshape(a, newshape, order='C'):
you should assign the new shape to the shape attribute of the array::
>>> a = np.zeros((10, 2))
+
# A transpose makes the array non-contiguous
>>> b = a.T
+
# Taking a view makes it possible to modify the shape without modifying
# the initial object.
>>> c = b.view()
>>> c.shape = (20)
+ Traceback (most recent call last):
+ ...
AttributeError: incompatible shape for a non-contiguous array
The `order` keyword gives the index ordering both for *fetching* the values
@@ -279,6 +301,14 @@ def reshape(a, newshape, order='C'):
return _wrapfunc(a, 'reshape', newshape, order=order)
+def _choose_dispatcher(a, choices, out=None, mode=None):
+ yield a
+ for c in choices:
+ yield c
+ yield out
+
+
+@array_function_dispatch(_choose_dispatcher)
def choose(a, choices, out=None, mode='raise'):
"""
Construct an array from an index array and a set of arrays to choose from.
@@ -327,7 +357,8 @@ def choose(a, choices, out=None, mode='raise'):
``choices.shape[0]``) is taken as defining the "sequence".
out : array, optional
If provided, the result will be inserted into this array. It should
- be of the appropriate shape and dtype.
+ be of the appropriate shape and dtype. Note that `out` is always
+ buffered if `mode='raise'`; use other modes for better performance.
mode : {'raise' (default), 'wrap', 'clip'}, optional
Specifies how indices outside `[0, n-1]` will be treated:
@@ -349,6 +380,7 @@ def choose(a, choices, out=None, mode='raise'):
See Also
--------
ndarray.choose : equivalent method
+ numpy.take_along_axis : Preferable if `choices` is an array
Notes
-----
@@ -401,6 +433,11 @@ def choose(a, choices, out=None, mode='raise'):
return _wrapfunc(a, 'choose', choices, out=out, mode=mode)
+def _repeat_dispatcher(a, repeats, axis=None):
+ return (a,)
+
+
+@array_function_dispatch(_repeat_dispatcher)
def repeat(a, repeats, axis=None):
"""
Repeat elements of an array.
@@ -445,6 +482,11 @@ def repeat(a, repeats, axis=None):
return _wrapfunc(a, 'repeat', repeats, axis=axis)
+def _put_dispatcher(a, ind, v, mode=None):
+ return (a, ind, v)
+
+
+@array_function_dispatch(_put_dispatcher)
def put(a, ind, v, mode='raise'):
"""
Replaces specified elements of an array with given values.
@@ -474,7 +516,8 @@ def put(a, ind, v, mode='raise'):
'clip' mode means that all indices that are too large are replaced
by the index that addresses the last element along that axis. Note
- that this disables indexing with negative numbers.
+ that this disables indexing with negative numbers. In 'raise' mode,
+ if an exception occurs the target array may still be modified.
See Also
--------
@@ -503,6 +546,11 @@ def put(a, ind, v, mode='raise'):
return put(ind, v, mode=mode)
+def _swapaxes_dispatcher(a, axis1, axis2):
+ return (a,)
+
+
+@array_function_dispatch(_swapaxes_dispatcher)
def swapaxes(a, axis1, axis2):
"""
Interchange two axes of an array.
@@ -549,6 +597,11 @@ def swapaxes(a, axis1, axis2):
return _wrapfunc(a, 'swapaxes', axis1, axis2)
+def _transpose_dispatcher(a, axes=None):
+ return (a,)
+
+
+@array_function_dispatch(_transpose_dispatcher)
def transpose(a, axes=None):
"""
Permute the dimensions of an array.
@@ -598,6 +651,11 @@ def transpose(a, axes=None):
return _wrapfunc(a, 'transpose', axes)
+def _partition_dispatcher(a, kth, axis=None, kind=None, order=None):
+ return (a,)
+
+
+@array_function_dispatch(_partition_dispatcher)
def partition(a, kth, axis=-1, kind='introselect', order=None):
"""
Return a partitioned copy of an array.
@@ -689,6 +747,11 @@ def partition(a, kth, axis=-1, kind='introselect', order=None):
return a
+def _argpartition_dispatcher(a, kth, axis=None, kind=None, order=None):
+ return (a,)
+
+
+@array_function_dispatch(_argpartition_dispatcher)
def argpartition(a, kth, axis=-1, kind='introselect', order=None):
"""
Perform an indirect partition along the given axis using the
@@ -757,7 +820,12 @@ def argpartition(a, kth, axis=-1, kind='introselect', order=None):
return _wrapfunc(a, 'argpartition', kth, axis=axis, kind=kind, order=order)
-def sort(a, axis=-1, kind='quicksort', order=None):
+def _sort_dispatcher(a, axis=None, kind=None, order=None):
+ return (a,)
+
+
+@array_function_dispatch(_sort_dispatcher)
+def sort(a, axis=-1, kind=None, order=None):
"""
Return a sorted copy of an array.
@@ -769,7 +837,14 @@ def sort(a, axis=-1, kind='quicksort', order=None):
Axis along which to sort. If None, the array is flattened before
sorting. The default is -1, which sorts along the last axis.
kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional
- Sorting algorithm. Default is 'quicksort'.
+ Sorting algorithm. The default is 'quicksort'. Note that both 'stable'
+ and 'mergesort' use timsort or radix sort under the covers and, in general,
+ the actual implementation will vary with data type. The 'mergesort' option
+ is retained for backwards compatibility.
+
+ .. versionchanged:: 1.15.0.
+ The 'stable' option was added.
+
order : str or list of str, optional
When `a` is an array with fields defined, this argument specifies
which fields to compare first, second, etc. A single field can
@@ -795,17 +870,22 @@ def sort(a, axis=-1, kind='quicksort', order=None):
The various sorting algorithms are characterized by their average speed,
worst case performance, work space size, and whether they are stable. A
stable sort keeps items with the same key in the same relative
- order. The three available algorithms have the following
+ order. The four algorithms implemented in NumPy have the following
properties:
=========== ======= ============= ============ ========
kind speed worst case work space stable
=========== ======= ============= ============ ========
'quicksort' 1 O(n^2) 0 no
- 'mergesort' 2 O(n*log(n)) ~n/2 yes
'heapsort' 3 O(n*log(n)) 0 no
+ 'mergesort' 2 O(n*log(n)) ~n/2 yes
+ 'timsort' 2 O(n*log(n)) ~n/2 yes
=========== ======= ============= ============ ========
+ .. note:: The datatype determines which of 'mergesort' or 'timsort'
+ is actually used, even if 'mergesort' is specified. User selection
+ at a finer scale is not currently available.
+
All the sort algorithms make temporary copies of the data when
sorting along any but the last axis. Consequently, sorting along
the last axis is faster and uses less space than sorting along
@@ -829,13 +909,30 @@ def sort(a, axis=-1, kind='quicksort', order=None):
.. versionadded:: 1.12.0
- quicksort has been changed to an introsort which will switch
- heapsort when it does not make enough progress. This makes its
- worst case O(n*log(n)).
-
- 'stable' automatically choses the best stable sorting algorithm
- for the data type being sorted. It is currently mapped to
- merge sort.
+ quicksort has been changed to `introsort <https://en.wikipedia.org/wiki/Introsort>`_.
+ When sorting does not make enough progress it switches to
+ `heapsort <https://en.wikipedia.org/wiki/Heapsort>`_.
+ This implementation makes quicksort O(n*log(n)) in the worst case.
+
+ 'stable' automatically chooses the best stable sorting algorithm
+ for the data type being sorted.
+ It, along with 'mergesort' is currently mapped to
+ `timsort <https://en.wikipedia.org/wiki/Timsort>`_
+ or `radix sort <https://en.wikipedia.org/wiki/Radix_sort>`_
+ depending on the data type.
+ API forward compatibility currently limits the
+ ability to select the implementation and it is hardwired for the different
+ data types.
+
+ .. versionadded:: 1.17.0
+
+ Timsort is added for better performance on already or nearly
+ sorted data. On random data timsort is almost identical to
+ mergesort. It is now used for stable sort while quicksort is still the
+ default sort if none is chosen. For timsort details, refer to
+ `CPython listsort.txt <https://github.com/python/cpython/blob/3.7/Objects/listsort.txt>`_.
+ 'mergesort' and 'stable' are mapped to radix sort for integer data types. Radix sort is an
+ O(n) sort instead of O(n log n).
Examples
--------
@@ -879,7 +976,12 @@ def sort(a, axis=-1, kind='quicksort', order=None):
return a
-def argsort(a, axis=-1, kind='quicksort', order=None):
+def _argsort_dispatcher(a, axis=None, kind=None, order=None):
+ return (a,)
+
+
+@array_function_dispatch(_argsort_dispatcher)
+def argsort(a, axis=-1, kind=None, order=None):
"""
Returns the indices that would sort an array.
@@ -895,7 +997,13 @@ def argsort(a, axis=-1, kind='quicksort', order=None):
Axis along which to sort. The default is -1 (the last axis). If None,
the flattened array is used.
kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional
- Sorting algorithm.
+ Sorting algorithm. The default is 'quicksort'. Note that both 'stable'
+ and 'mergesort' use timsort under the covers and, in general, the
+ actual implementation will vary with data type. The 'mergesort' option
+ is retained for backwards compatibility.
+
+ .. versionchanged:: 1.15.0.
+ The 'stable' option was added.
order : str or list of str, optional
When `a` is an array with fields defined, this argument specifies
which fields to compare first, second, etc. A single field can
@@ -906,10 +1014,10 @@ def argsort(a, axis=-1, kind='quicksort', order=None):
Returns
-------
index_array : ndarray, int
- Array of indices that sort `a` along the specified axis.
+ Array of indices that sort `a` along the specified `axis`.
If `a` is one-dimensional, ``a[index_array]`` yields a sorted `a`.
- More generally, ``np.take_along_axis(a, index_array, axis=a)`` always
- yields the sorted `a`, irrespective of dimensionality.
+ More generally, ``np.take_along_axis(a, index_array, axis=axis)``
+ always yields the sorted `a`, irrespective of dimensionality.
See Also
--------
@@ -940,13 +1048,21 @@ def argsort(a, axis=-1, kind='quicksort', order=None):
array([[0, 3],
[2, 2]])
- >>> np.argsort(x, axis=0) # sorts along first axis (down)
+ >>> ind = np.argsort(x, axis=0) # sorts along first axis (down)
+ >>> ind
array([[0, 1],
[1, 0]])
+ >>> np.take_along_axis(x, ind, axis=0) # same as np.sort(x, axis=0)
+ array([[0, 2],
+ [2, 3]])
- >>> np.argsort(x, axis=1) # sorts along last axis (across)
+ >>> ind = np.argsort(x, axis=1) # sorts along last axis (across)
+ >>> ind
array([[0, 1],
[0, 1]])
+ >>> np.take_along_axis(x, ind, axis=1) # same as np.sort(x, axis=1)
+ array([[0, 3],
+ [2, 2]])
Indices of the sorted elements of a N-dimensional array:
@@ -973,6 +1089,11 @@ def argsort(a, axis=-1, kind='quicksort', order=None):
return _wrapfunc(a, 'argsort', axis=axis, kind=kind, order=order)
+def _argmax_dispatcher(a, axis=None, out=None):
+ return (a, out)
+
+
+@array_function_dispatch(_argmax_dispatcher)
def argmax(a, axis=None, out=None):
"""
Returns the indices of the maximum values along an axis.
@@ -1007,10 +1128,10 @@ def argmax(a, axis=None, out=None):
Examples
--------
- >>> a = np.arange(6).reshape(2,3)
+ >>> a = np.arange(6).reshape(2,3) + 10
>>> a
- array([[0, 1, 2],
- [3, 4, 5]])
+ array([[10, 11, 12],
+ [13, 14, 15]])
>>> np.argmax(a)
5
>>> np.argmax(a, axis=0)
@@ -1024,7 +1145,7 @@ def argmax(a, axis=None, out=None):
>>> ind
(1, 2)
>>> a[ind]
- 5
+ 15
>>> b = np.arange(6)
>>> b[1] = 5
@@ -1037,6 +1158,11 @@ def argmax(a, axis=None, out=None):
return _wrapfunc(a, 'argmax', axis=axis, out=out)
+def _argmin_dispatcher(a, axis=None, out=None):
+ return (a, out)
+
+
+@array_function_dispatch(_argmin_dispatcher)
def argmin(a, axis=None, out=None):
"""
Returns the indices of the minimum values along an axis.
@@ -1071,10 +1197,10 @@ def argmin(a, axis=None, out=None):
Examples
--------
- >>> a = np.arange(6).reshape(2,3)
+ >>> a = np.arange(6).reshape(2,3) + 10
>>> a
- array([[0, 1, 2],
- [3, 4, 5]])
+ array([[10, 11, 12],
+ [13, 14, 15]])
>>> np.argmin(a)
0
>>> np.argmin(a, axis=0)
@@ -1088,12 +1214,12 @@ def argmin(a, axis=None, out=None):
>>> ind
(0, 0)
>>> a[ind]
- 0
+ 10
- >>> b = np.arange(6)
- >>> b[4] = 0
+ >>> b = np.arange(6) + 10
+ >>> b[4] = 10
>>> b
- array([0, 1, 2, 3, 0, 5])
+ array([10, 11, 12, 13, 10, 15])
>>> np.argmin(b) # Only the first occurrence is returned.
0
@@ -1101,6 +1227,11 @@ def argmin(a, axis=None, out=None):
return _wrapfunc(a, 'argmin', axis=axis, out=out)
+def _searchsorted_dispatcher(a, v, side=None, sorter=None):
+ return (a, v, sorter)
+
+
+@array_function_dispatch(_searchsorted_dispatcher)
def searchsorted(a, v, side='left', sorter=None):
"""
Find indices where elements should be inserted to maintain order.
@@ -1153,7 +1284,7 @@ def searchsorted(a, v, side='left', sorter=None):
As of NumPy 1.4.0 `searchsorted` works with real/complex arrays containing
`nan` values. The enhanced sort order is documented in `sort`.
- This function is a faster version of the builtin python `bisect.bisect_left`
+ This function uses the same algorithm as the builtin python `bisect.bisect_left`
(``side='left'``) and `bisect.bisect_right` (``side='right'``) functions,
which is also vectorized in the `v` argument.
@@ -1170,6 +1301,11 @@ def searchsorted(a, v, side='left', sorter=None):
return _wrapfunc(a, 'searchsorted', v, side=side, sorter=sorter)
+def _resize_dispatcher(a, new_shape):
+ return (a,)
+
+
+@array_function_dispatch(_resize_dispatcher)
def resize(a, new_shape):
"""
Return a new array with the specified shape.
@@ -1243,6 +1379,11 @@ def resize(a, new_shape):
return reshape(a, new_shape)
+def _squeeze_dispatcher(a, axis=None):
+ return (a,)
+
+
+@array_function_dispatch(_squeeze_dispatcher)
def squeeze(a, axis=None):
"""
Remove single-dimensional entries from the shape of an array.
@@ -1295,12 +1436,18 @@ def squeeze(a, axis=None):
try:
squeeze = a.squeeze
except AttributeError:
- return _wrapit(a, 'squeeze')
+ return _wrapit(a, 'squeeze', axis=axis)
if axis is None:
return squeeze()
else:
return squeeze(axis=axis)
+
+def _diagonal_dispatcher(a, offset=None, axis1=None, axis2=None):
+ return (a,)
+
+
+@array_function_dispatch(_diagonal_dispatcher)
def diagonal(a, offset=0, axis1=0, axis2=1):
"""
Return specified diagonals.
@@ -1356,7 +1503,7 @@ def diagonal(a, offset=0, axis1=0, axis2=1):
same type as `a` is returned unless `a` is a `matrix`, in which case
a 1-D array rather than a (2-D) `matrix` is returned in order to
maintain backward compatibility.
-
+
If ``a.ndim > 2``, then the dimensions specified by `axis1` and `axis2`
are removed, and a new axis inserted at the end corresponding to the
diagonal.
@@ -1390,9 +1537,9 @@ def diagonal(a, offset=0, axis1=0, axis2=1):
[2, 3]],
[[4, 5],
[6, 7]]])
- >>> a.diagonal(0, # Main diagonals of two arrays created by skipping
- ... 0, # across the outer(left)-most axis last and
- ... 1) # the "middle" (row) axis first.
+ >>> a.diagonal(0, # Main diagonals of two arrays created by skipping
+ ... 0, # across the outer(left)-most axis last and
+ ... 1) # the "middle" (row) axis first.
array([[0, 6],
[1, 7]])
@@ -1400,13 +1547,28 @@ def diagonal(a, offset=0, axis1=0, axis2=1):
corresponds to fixing the right-most (column) axis, and that the
diagonals are "packed" in rows.
- >>> a[:,:,0] # main diagonal is [0 6]
+ >>> a[:,:,0] # main diagonal is [0 6]
array([[0, 2],
[4, 6]])
- >>> a[:,:,1] # main diagonal is [1 7]
+ >>> a[:,:,1] # main diagonal is [1 7]
array([[1, 3],
[5, 7]])
+ The anti-diagonal can be obtained by reversing the order of elements
+ using either `numpy.flipud` or `numpy.fliplr`.
+
+ >>> a = np.arange(9).reshape(3, 3)
+ >>> a
+ array([[0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8]])
+ >>> np.fliplr(a).diagonal() # Horizontal flip
+ array([2, 4, 6])
+ >>> np.flipud(a).diagonal() # Vertical flip
+ array([6, 4, 2])
+
+ Note that the order in which the diagonal is retrieved varies depending
+ on the flip function.
"""
if isinstance(a, np.matrix):
# Make diagonal of matrix 1-D to preserve backward compatibility.
@@ -1415,6 +1577,12 @@ def diagonal(a, offset=0, axis1=0, axis2=1):
return asanyarray(a).diagonal(offset=offset, axis1=axis1, axis2=axis2)
+def _trace_dispatcher(
+ a, offset=None, axis1=None, axis2=None, dtype=None, out=None):
+ return (a, out)
+
+
+@array_function_dispatch(_trace_dispatcher)
def trace(a, offset=0, axis1=0, axis2=1, dtype=None, out=None):
"""
Return the sum along diagonals of the array.
@@ -1478,6 +1646,11 @@ def trace(a, offset=0, axis1=0, axis2=1, dtype=None, out=None):
return asanyarray(a).trace(offset=offset, axis1=axis1, axis2=axis2, dtype=dtype, out=out)
+def _ravel_dispatcher(a, order=None):
+ return (a,)
+
+
+@array_function_dispatch(_ravel_dispatcher)
def ravel(a, order='C'):
"""Return a contiguous flattened array.
@@ -1541,21 +1714,21 @@ def ravel(a, order='C'):
It is equivalent to ``reshape(-1, order=order)``.
>>> x = np.array([[1, 2, 3], [4, 5, 6]])
- >>> print(np.ravel(x))
- [1 2 3 4 5 6]
+ >>> np.ravel(x)
+ array([1, 2, 3, 4, 5, 6])
- >>> print(x.reshape(-1))
- [1 2 3 4 5 6]
+ >>> x.reshape(-1)
+ array([1, 2, 3, 4, 5, 6])
- >>> print(np.ravel(x, order='F'))
- [1 4 2 5 3 6]
+ >>> np.ravel(x, order='F')
+ array([1, 4, 2, 5, 3, 6])
When ``order`` is 'A', it will preserve the array's 'C' or 'F' ordering:
- >>> print(np.ravel(x.T))
- [1 4 2 5 3 6]
- >>> print(np.ravel(x.T, order='A'))
- [1 2 3 4 5 6]
+ >>> np.ravel(x.T)
+ array([1, 4, 2, 5, 3, 6])
+ >>> np.ravel(x.T, order='A')
+ array([1, 2, 3, 4, 5, 6])
When ``order`` is 'K', it will preserve orderings that are neither 'C'
nor 'F', but won't reverse axes:
@@ -1584,6 +1757,11 @@ def ravel(a, order='C'):
return asanyarray(a).ravel(order=order)
+def _nonzero_dispatcher(a):
+ return (a,)
+
+
+@array_function_dispatch(_nonzero_dispatcher)
def nonzero(a):
"""
Return the indices of the elements that are non-zero.
@@ -1591,17 +1769,19 @@ def nonzero(a):
Returns a tuple of arrays, one for each dimension of `a`,
containing the indices of the non-zero elements in that
dimension. The values in `a` are always tested and returned in
- row-major, C-style order. The corresponding non-zero
- values can be obtained with::
+ row-major, C-style order.
- a[nonzero(a)]
+ To group the indices by element, rather than dimension, use `argwhere`,
+ which returns a row for each non-zero element.
- To group the indices by element, rather than dimension, use::
+ .. note::
+
+ When called on a zero-d array or scalar, ``nonzero(a)`` is treated
+ as ``nonzero(atleast1d(a))``.
- transpose(nonzero(a))
+ .. deprecated:: 1.17.0
- The result of this is always a 2-D array, with a row for
- each non-zero element.
+ Use `atleast1d` explicitly if this behavior is deliberate.
Parameters
----------
@@ -1623,6 +1803,12 @@ def nonzero(a):
count_nonzero :
Counts the number of non-zero elements in the input array.
+ Notes
+ -----
+ While the nonzero values can be obtained with ``a[nonzero(a)]``, it is
+ recommended to use ``x[x.astype(bool)]`` or ``x[x != 0]`` instead, which
+ will correctly handle 0-d arrays.
+
Examples
--------
>>> x = np.array([[3, 0, 0], [0, 4, 0], [5, 6, 0]])
@@ -1639,7 +1825,7 @@ def nonzero(a):
array([[0, 0],
[1, 1],
[2, 0],
- [2, 1])
+ [2, 1]])
A common use for ``nonzero`` is to find the indices of an array, where
a condition is True. Given an array `a`, the condition `a` > 3 is a
@@ -1670,6 +1856,11 @@ def nonzero(a):
return _wrapfunc(a, 'nonzero')
+def _shape_dispatcher(a):
+ return (a,)
+
+
+@array_function_dispatch(_shape_dispatcher)
def shape(a):
"""
Return the shape of an array.
@@ -1715,6 +1906,11 @@ def shape(a):
return result
+def _compress_dispatcher(condition, a, axis=None, out=None):
+ return (condition, a, out)
+
+
+@array_function_dispatch(_compress_dispatcher)
def compress(condition, a, axis=None, out=None):
"""
Return selected slices of an array along given axis.
@@ -1778,7 +1974,12 @@ def compress(condition, a, axis=None, out=None):
return _wrapfunc(a, 'compress', condition, axis=axis, out=out)
-def clip(a, a_min, a_max, out=None):
+def _clip_dispatcher(a, a_min, a_max, out=None, **kwargs):
+ return (a, a_min, a_max)
+
+
+@array_function_dispatch(_clip_dispatcher)
+def clip(a, a_min, a_max, out=None, **kwargs):
"""
Clip (limit) the values in an array.
@@ -1787,6 +1988,9 @@ def clip(a, a_min, a_max, out=None):
is specified, values smaller than 0 become 0, and values larger
than 1 become 1.
+ Equivalent to but faster than ``np.maximum(a_min, np.minimum(a, a_max))``.
+ No check is performed to ensure ``a_min < a_max``.
+
Parameters
----------
a : array_like
@@ -1804,6 +2008,11 @@ def clip(a, a_min, a_max, out=None):
The results will be placed in this array. It may be the input
array for in-place clipping. `out` must be of the right shape
to hold the output. Its type is preserved.
+ **kwargs
+ For other keyword-only arguments, see the
+ :ref:`ufunc docs <ufuncs.kwargs>`.
+
+ .. versionadded:: 1.17.0
Returns
-------
@@ -1832,10 +2041,17 @@ def clip(a, a_min, a_max, out=None):
array([3, 4, 2, 3, 4, 5, 6, 7, 8, 8])
"""
- return _wrapfunc(a, 'clip', a_min, a_max, out=out)
+ return _wrapfunc(a, 'clip', a_min, a_max, out=out, **kwargs)
+
+def _sum_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None,
+ initial=None, where=None):
+ return (a, out)
-def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
+
+@array_function_dispatch(_sum_dispatcher)
+def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue,
+ initial=np._NoValue, where=np._NoValue):
"""
Sum of array elements over a given axis.
@@ -1879,6 +2095,11 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No
.. versionadded:: 1.15.0
+ where : array_like of bool, optional
+ Elements to include in the sum. See `~numpy.ufunc.reduce` for details.
+
+ .. versionadded:: 1.17.0
+
Returns
-------
sum_along_axis : ndarray
@@ -1891,6 +2112,8 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No
--------
ndarray.sum : Equivalent method.
+ add.reduce : Equivalent functionality of `add`.
+
cumsum : Cumulative sum of array elements.
trapz : Integration of array values using the composite trapezoidal rule.
@@ -1907,6 +2130,23 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No
>>> np.sum([])
0.0
+ For floating point numbers the numerical precision of sum (and
+ ``np.add.reduce``) is in general limited by directly adding each number
+ individually to the result causing rounding errors in every step.
+ However, often numpy will use a numerically better approach (partial
+ pairwise summation) leading to improved precision in many use-cases.
+ This improved precision is always provided when no ``axis`` is given.
+ When ``axis`` is given, it will depend on which axis is summed.
+ Technically, to provide the best speed possible, the improved precision
+ is only used when the summation is along the fast axis in memory.
+ Note that the exact precision may vary depending on other parameters.
+ In contrast to NumPy, Python's ``math.fsum`` function uses a slower but
+ more precise approach to summation.
+ Especially when summing a large number of lower precision floating point
+ numbers, such as ``float32``, numerical errors can become significant.
+ In such cases it can be advisable to use `dtype="float64"` to use a higher
+ precision for the output.
+
Examples
--------
>>> np.sum([0.5, 1.5])
@@ -1919,6 +2159,8 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No
array([0, 6])
>>> np.sum([[0, 1], [0, 5]], axis=1)
array([1, 5])
+ >>> np.sum([[0, 1], [np.nan, 5]], where=[False, True], axis=1)
+ array([1., 5.])
If the accumulator is too small, overflow occurs:
@@ -1934,8 +2176,8 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No
# 2018-02-25, 1.15.0
warnings.warn(
"Calling np.sum(generator) is deprecated, and in the future will give a different result. "
- "Use np.sum(np.from_iter(generator)) or the python sum builtin instead.",
- DeprecationWarning, stacklevel=2)
+ "Use np.sum(np.fromiter(generator)) or the python sum builtin instead.",
+ DeprecationWarning, stacklevel=3)
res = _sum_(a)
if out is not None:
@@ -1944,9 +2186,14 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No
return res
return _wrapreduction(a, np.add, 'sum', axis, dtype, out, keepdims=keepdims,
- initial=initial)
+ initial=initial, where=where)
+
+def _any_dispatcher(a, axis=None, out=None, keepdims=None):
+ return (a, out)
+
+@array_function_dispatch(_any_dispatcher)
def any(a, axis=None, out=None, keepdims=np._NoValue):
"""
Test whether any array element along a given axis evaluates to True.
@@ -2016,10 +2263,10 @@ def any(a, axis=None, out=None, keepdims=np._NoValue):
>>> np.any(np.nan)
True
- >>> o=np.array([False])
+ >>> o=np.array(False)
>>> z=np.any([-1, 4, 5], out=o)
>>> z, o
- (array([ True]), array([ True]))
+ (array(True), array(True))
>>> # Check now that z is a reference to o
>>> z is o
True
@@ -2030,6 +2277,11 @@ def any(a, axis=None, out=None, keepdims=np._NoValue):
return _wrapreduction(a, np.logical_or, 'any', axis, None, out, keepdims=keepdims)
+def _all_dispatcher(a, axis=None, out=None, keepdims=None):
+ return (a, out)
+
+
+@array_function_dispatch(_all_dispatcher)
def all(a, axis=None, out=None, keepdims=np._NoValue):
"""
Test whether all array elements along a given axis evaluate to True.
@@ -2097,15 +2349,20 @@ def all(a, axis=None, out=None, keepdims=np._NoValue):
>>> np.all([1.0, np.nan])
True
- >>> o=np.array([False])
+ >>> o=np.array(False)
>>> z=np.all([-1, 4, 5], out=o)
- >>> id(z), id(o), z # doctest: +SKIP
- (28293632, 28293632, array([ True]))
+ >>> id(z), id(o), z
+ (28293632, 28293632, array(True)) # may vary
"""
return _wrapreduction(a, np.logical_and, 'all', axis, None, out, keepdims=keepdims)
+def _cumsum_dispatcher(a, axis=None, dtype=None, out=None):
+ return (a, out)
+
+
+@array_function_dispatch(_cumsum_dispatcher)
def cumsum(a, axis=None, dtype=None, out=None):
"""
Return the cumulative sum of the elements along a given axis.
@@ -2173,6 +2430,11 @@ def cumsum(a, axis=None, dtype=None, out=None):
return _wrapfunc(a, 'cumsum', axis=axis, dtype=dtype, out=out)
+def _ptp_dispatcher(a, axis=None, out=None, keepdims=None):
+ return (a, out)
+
+
+@array_function_dispatch(_ptp_dispatcher)
def ptp(a, axis=None, out=None, keepdims=np._NoValue):
"""
Range of values (maximum - minimum) along an axis.
@@ -2241,7 +2503,14 @@ def ptp(a, axis=None, out=None, keepdims=np._NoValue):
return _methods._ptp(a, axis=axis, out=out, **kwargs)
-def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
+def _amax_dispatcher(a, axis=None, out=None, keepdims=None, initial=None,
+ where=None):
+ return (a, out)
+
+
+@array_function_dispatch(_amax_dispatcher)
+def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
+ where=np._NoValue):
"""
Return the maximum of an array or maximum along an axis.
@@ -2279,6 +2548,11 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
.. versionadded:: 1.15.0
+ where : array_like of bool, optional
+ Elements to compare for the maximum. See `~numpy.ufunc.reduce`
+ for details.
+
+ .. versionadded:: 1.17.0
Returns
-------
@@ -2324,11 +2598,14 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
array([2, 3])
>>> np.amax(a, axis=1) # Maxima along the second axis
array([1, 3])
-
+ >>> np.amax(a, where=[False, True], initial=-1, axis=0)
+ array([-1, 3])
>>> b = np.arange(5, dtype=float)
>>> b[2] = np.NaN
>>> np.amax(b)
nan
+ >>> np.amax(b, where=~np.isnan(b), initial=-1)
+ 4.0
>>> np.nanmax(b)
4.0
@@ -2347,11 +2624,18 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
>>> max([5], default=6)
5
"""
- return _wrapreduction(a, np.maximum, 'max', axis, None, out, keepdims=keepdims,
- initial=initial)
+ return _wrapreduction(a, np.maximum, 'max', axis, None, out,
+ keepdims=keepdims, initial=initial, where=where)
-def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
+def _amin_dispatcher(a, axis=None, out=None, keepdims=None, initial=None,
+ where=None):
+ return (a, out)
+
+
+@array_function_dispatch(_amin_dispatcher)
+def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
+ where=np._NoValue):
"""
Return the minimum of an array or minimum along an axis.
@@ -2389,6 +2673,12 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
.. versionadded:: 1.15.0
+ where : array_like of bool, optional
+ Elements to compare for the minimum. See `~numpy.ufunc.reduce`
+ for details.
+
+ .. versionadded:: 1.17.0
+
Returns
-------
amin : ndarray or scalar
@@ -2433,11 +2723,15 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
array([0, 1])
>>> np.amin(a, axis=1) # Minima along the second axis
array([0, 2])
+ >>> np.amin(a, where=[False, True], initial=10, axis=0)
+ array([10, 1])
>>> b = np.arange(5, dtype=float)
>>> b[2] = np.NaN
>>> np.amin(b)
nan
+ >>> np.amin(b, where=~np.isnan(b), initial=10)
+ 0.0
>>> np.nanmin(b)
0.0
@@ -2455,10 +2749,15 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
>>> min([6], default=5)
6
"""
- return _wrapreduction(a, np.minimum, 'min', axis, None, out, keepdims=keepdims,
- initial=initial)
+ return _wrapreduction(a, np.minimum, 'min', axis, None, out,
+ keepdims=keepdims, initial=initial, where=where)
+
+def _alen_dispathcer(a):
+ return (a,)
+
+@array_function_dispatch(_alen_dispathcer)
def alen(a):
"""
Return the length of the first dimension of the input array.
@@ -2486,13 +2785,24 @@ def alen(a):
7
"""
+ # NumPy 1.18.0, 2019-08-02
+ warnings.warn(
+ "`np.alen` is deprecated, use `len` instead",
+ DeprecationWarning, stacklevel=2)
try:
return len(a)
except TypeError:
return len(array(a, ndmin=1))
-def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._NoValue):
+def _prod_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None,
+ initial=None, where=None):
+ return (a, out)
+
+
+@array_function_dispatch(_prod_dispatcher)
+def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue,
+ initial=np._NoValue, where=np._NoValue):
"""
Return the product of array elements over a given axis.
@@ -2537,6 +2847,11 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N
.. versionadded:: 1.15.0
+ where : array_like of bool, optional
+ Elements to include in the product. See `~numpy.ufunc.reduce` for details.
+
+ .. versionadded:: 1.17.0
+
Returns
-------
product_along_axis : ndarray, see `dtype` parameter above.
@@ -2554,8 +2869,8 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N
raised on overflow. That means that, on a 32-bit platform:
>>> x = np.array([536870910, 536870910, 536870910, 536870910])
- >>> np.prod(x) # random
- 16
+ >>> np.prod(x)
+ 16 # may vary
The product of an empty array is the neutral element 1:
@@ -2579,6 +2894,11 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N
>>> np.prod([[1.,2.],[3.,4.]], axis=1)
array([ 2., 12.])
+ Or select specific elements to include:
+
+ >>> np.prod([1., np.nan, 3.], where=[True, False, True])
+ 3.0
+
If the type of `x` is unsigned, then the output type is
the unsigned platform integer:
@@ -2598,10 +2918,15 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N
>>> np.prod([1, 2], initial=5)
10
"""
- return _wrapreduction(a, np.multiply, 'prod', axis, dtype, out, keepdims=keepdims,
- initial=initial)
+ return _wrapreduction(a, np.multiply, 'prod', axis, dtype, out,
+ keepdims=keepdims, initial=initial, where=where)
+def _cumprod_dispatcher(a, axis=None, dtype=None, out=None):
+ return (a, out)
+
+
+@array_function_dispatch(_cumprod_dispatcher)
def cumprod(a, axis=None, dtype=None, out=None):
"""
Return the cumulative product of elements along a given axis.
@@ -2665,6 +2990,11 @@ def cumprod(a, axis=None, dtype=None, out=None):
return _wrapfunc(a, 'cumprod', axis=axis, dtype=dtype, out=out)
+def _ndim_dispatcher(a):
+ return (a,)
+
+
+@array_function_dispatch(_ndim_dispatcher)
def ndim(a):
"""
Return the number of dimensions of an array.
@@ -2702,6 +3032,11 @@ def ndim(a):
return asarray(a).ndim
+def _size_dispatcher(a, axis=None):
+ return (a,)
+
+
+@array_function_dispatch(_size_dispatcher)
def size(a, axis=None):
"""
Return the number of elements along a given axis.
@@ -2748,6 +3083,11 @@ def size(a, axis=None):
return asarray(a).shape[axis]
+def _around_dispatcher(a, decimals=None, out=None):
+ return (a, out)
+
+
+@array_function_dispatch(_around_dispatcher)
def around(a, decimals=0, out=None):
"""
Evenly round to the given number of decimals.
@@ -2787,10 +3127,37 @@ def around(a, decimals=0, out=None):
-----
For values exactly halfway between rounded decimal values, NumPy
rounds to the nearest even value. Thus 1.5 and 2.5 round to 2.0,
- -0.5 and 0.5 round to 0.0, etc. Results may also be surprising due
- to the inexact representation of decimal fractions in the IEEE
- floating point standard [1]_ and errors introduced when scaling
- by powers of ten.
+ -0.5 and 0.5 round to 0.0, etc.
+
+ ``np.around`` uses a fast but sometimes inexact algorithm to round
+ floating-point datatypes. For positive `decimals` it is equivalent to
+ ``np.true_divide(np.rint(a * 10**decimals), 10**decimals)``, which has
+ error due to the inexact representation of decimal fractions in the IEEE
+ floating point standard [1]_ and errors introduced when scaling by powers
+ of ten. For instance, note the extra "1" in the following:
+
+ >>> np.round(56294995342131.5, 3)
+ 56294995342131.51
+
+ If your goal is to print such values with a fixed number of decimals, it is
+ preferable to use numpy's float printing routines to limit the number of
+ printed decimals:
+
+ >>> np.format_float_positional(56294995342131.5, precision=3)
+ '56294995342131.5'
+
+ The float printing routines use an accurate but much more computationally
+ demanding algorithm to compute the number of digits after the decimal
+ point.
+
+ Alternatively, Python's builtin `round` function uses a more accurate
+ but slower algorithm for 64-bit floating point values:
+
+ >>> round(56294995342131.5, 3)
+ 56294995342131.5
+ >>> np.round(16.055, 2), round(16.055, 2) # equals 16.0549999999999997
+ (16.06, 16.05)
+
References
----------
@@ -2803,11 +3170,11 @@ def around(a, decimals=0, out=None):
Examples
--------
>>> np.around([0.37, 1.64])
- array([ 0., 2.])
+ array([0., 2.])
>>> np.around([0.37, 1.64], decimals=1)
- array([ 0.4, 1.6])
+ array([0.4, 1.6])
>>> np.around([.5, 1.5, 2.5, 3.5, 4.5]) # rounds to nearest even value
- array([ 0., 2., 2., 4., 4.])
+ array([0., 2., 2., 4., 4.])
>>> np.around([1,2,3,11], decimals=1) # ndarray of ints is returned
array([ 1, 2, 3, 11])
>>> np.around([1,2,3,11], decimals=-1)
@@ -2817,20 +3184,11 @@ def around(a, decimals=0, out=None):
return _wrapfunc(a, 'round', decimals=decimals, out=out)
-def round_(a, decimals=0, out=None):
- """
- Round an array to the given number of decimals.
-
- Refer to `around` for full documentation.
-
- See Also
- --------
- around : equivalent function
-
- """
- return around(a, decimals=decimals, out=out)
+def _mean_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None):
+ return (a, out)
+@array_function_dispatch(_mean_dispatcher)
def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue):
"""
Compute the arithmetic mean along the specified axis.
@@ -2904,9 +3262,9 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue):
>>> np.mean(a)
2.5
>>> np.mean(a, axis=0)
- array([ 2., 3.])
+ array([2., 3.])
>>> np.mean(a, axis=1)
- array([ 1.5, 3.5])
+ array([1.5, 3.5])
In single precision, `mean` can be inaccurate:
@@ -2919,7 +3277,7 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue):
Computing the mean in float64 is more accurate:
>>> np.mean(a, dtype=np.float64)
- 0.55000000074505806
+ 0.55000000074505806 # may vary
"""
kwargs = {}
@@ -2937,6 +3295,12 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue):
out=out, **kwargs)
+def _std_dispatcher(
+ a, axis=None, dtype=None, out=None, ddof=None, keepdims=None):
+ return (a, out)
+
+
+@array_function_dispatch(_std_dispatcher)
def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
"""
Compute the standard deviation along the specified axis.
@@ -3019,11 +3383,11 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
--------
>>> a = np.array([[1, 2], [3, 4]])
>>> np.std(a)
- 1.1180339887498949
+ 1.1180339887498949 # may vary
>>> np.std(a, axis=0)
- array([ 1., 1.])
+ array([1., 1.])
>>> np.std(a, axis=1)
- array([ 0.5, 0.5])
+ array([0.5, 0.5])
In single precision, std() can be inaccurate:
@@ -3036,7 +3400,7 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
Computing the standard deviation in float64 is more accurate:
>>> np.std(a, dtype=np.float64)
- 0.44999999925494177
+ 0.44999999925494177 # may vary
"""
kwargs = {}
@@ -3055,6 +3419,12 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
**kwargs)
+def _var_dispatcher(
+ a, axis=None, dtype=None, out=None, ddof=None, keepdims=None):
+ return (a, out)
+
+
+@array_function_dispatch(_var_dispatcher)
def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
"""
Compute the variance along the specified axis.
@@ -3078,7 +3448,7 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
instead of a single axis or all the axes as before.
dtype : data-type, optional
Type to use in computing the variance. For arrays of integer type
- the default is `float32`; for arrays of float types it is the same as
+ the default is `float64`; for arrays of float types it is the same as
the array type.
out : ndarray, optional
Alternate output array in which to place the result. It must have
@@ -3107,7 +3477,7 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
See Also
--------
- std , mean, nanmean, nanstd, nanvar
+ std, mean, nanmean, nanstd, nanvar
numpy.doc.ufuncs : Section "Output arguments"
Notes
@@ -3137,9 +3507,9 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
>>> np.var(a)
1.25
>>> np.var(a, axis=0)
- array([ 1., 1.])
+ array([1., 1.])
>>> np.var(a, axis=1)
- array([ 0.25, 0.25])
+ array([0.25, 0.25])
In single precision, var() can be inaccurate:
@@ -3152,7 +3522,7 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
Computing the variance in float64 is more accurate:
>>> np.var(a, dtype=np.float64)
- 0.20249999932944759
+ 0.20249999932944759 # may vary
>>> ((1-0.55)**2 + (0.1-0.55)**2)/2
0.2025
@@ -3177,6 +3547,19 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
# Aliases of other functions. These have their own definitions only so that
# they can have unique docstrings.
+@array_function_dispatch(_around_dispatcher)
+def round_(a, decimals=0, out=None):
+ """
+ Round an array to the given number of decimals.
+
+ See Also
+ --------
+ around : equivalent function; see for details.
+ """
+ return around(a, decimals=decimals, out=out)
+
+
+@array_function_dispatch(_prod_dispatcher, verify=False)
def product(*args, **kwargs):
"""
Return the product of array elements over a given axis.
@@ -3188,6 +3571,7 @@ def product(*args, **kwargs):
return prod(*args, **kwargs)
+@array_function_dispatch(_cumprod_dispatcher, verify=False)
def cumproduct(*args, **kwargs):
"""
Return the cumulative product over the given axis.
@@ -3199,6 +3583,7 @@ def cumproduct(*args, **kwargs):
return cumprod(*args, **kwargs)
+@array_function_dispatch(_any_dispatcher, verify=False)
def sometrue(*args, **kwargs):
"""
Check whether some values are true.
@@ -3212,6 +3597,7 @@ def sometrue(*args, **kwargs):
return any(*args, **kwargs)
+@array_function_dispatch(_all_dispatcher, verify=False)
def alltrue(*args, **kwargs):
"""
Check if all elements of input array are true.
@@ -3221,29 +3607,3 @@ def alltrue(*args, **kwargs):
numpy.all : Equivalent function; see for details.
"""
return all(*args, **kwargs)
-
-
-def rank(a):
- """
- Return the number of dimensions of an array.
-
- .. note::
- This function is deprecated in NumPy 1.9 to avoid confusion with
- `numpy.linalg.matrix_rank`. The ``ndim`` attribute or function
- should be used instead.
-
- See Also
- --------
- ndim : equivalent non-deprecated function
-
- Notes
- -----
- In the old Numeric package, `rank` was the term used for the number of
- dimensions, but in NumPy `ndim` is used instead.
- """
- # 2014-04-12, 1.9
- warnings.warn(
- "`rank` is deprecated; use the `ndim` attribute or function instead. "
- "To find the rank of a matrix see `numpy.linalg.matrix_rank`.",
- VisibleDeprecationWarning, stacklevel=2)
- return ndim(a)
diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py
index 799b1418d..42604ec3f 100644
--- a/numpy/core/function_base.py
+++ b/numpy/core/function_base.py
@@ -1,29 +1,31 @@
from __future__ import division, absolute_import, print_function
+import functools
import warnings
import operator
+import types
from . import numeric as _nx
from .numeric import (result_type, NaN, shares_memory, MAY_SHARE_BOUNDS,
- TooHardError,asanyarray)
+ TooHardError, asanyarray, ndim)
from numpy.core.multiarray import add_docstring
+from numpy.core import overrides
__all__ = ['logspace', 'linspace', 'geomspace']
-def _index_deprecate(i, stacklevel=2):
- try:
- i = operator.index(i)
- except TypeError:
- msg = ("object of type {} cannot be safely interpreted as "
- "an integer.".format(type(i)))
- i = int(i)
- stacklevel += 1
- warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel)
- return i
+array_function_dispatch = functools.partial(
+ overrides.array_function_dispatch, module='numpy')
+
+
+def _linspace_dispatcher(start, stop, num=None, endpoint=None, retstep=None,
+ dtype=None, axis=None):
+ return (start, stop)
-def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
+@array_function_dispatch(_linspace_dispatcher)
+def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None,
+ axis=0):
"""
Return evenly spaced numbers over a specified interval.
@@ -32,11 +34,14 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
The endpoint of the interval can optionally be excluded.
+ .. versionchanged:: 1.16.0
+ Non-scalar `start` and `stop` are now supported.
+
Parameters
----------
- start : scalar
+ start : array_like
The starting value of the sequence.
- stop : scalar
+ stop : array_like
The end value of the sequence, unless `endpoint` is set to False.
In that case, the sequence consists of all but the last of ``num + 1``
evenly spaced samples, so that `stop` is excluded. Note that the step
@@ -55,6 +60,13 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
.. versionadded:: 1.9.0
+ axis : int, optional
+ The axis in the result to store the samples. Relevant only if start
+ or stop are array-like. By default (0), the samples will be along a
+ new axis inserted at the beginning. Use -1 to get an axis at the end.
+
+ .. versionadded:: 1.16.0
+
Returns
-------
samples : ndarray
@@ -79,11 +91,11 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
Examples
--------
>>> np.linspace(2.0, 3.0, num=5)
- array([ 2. , 2.25, 2.5 , 2.75, 3. ])
+ array([2. , 2.25, 2.5 , 2.75, 3. ])
>>> np.linspace(2.0, 3.0, num=5, endpoint=False)
- array([ 2. , 2.2, 2.4, 2.6, 2.8])
+ array([2. , 2.2, 2.4, 2.6, 2.8])
>>> np.linspace(2.0, 3.0, num=5, retstep=True)
- (array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)
+ (array([2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)
Graphical illustration:
@@ -101,8 +113,13 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
>>> plt.show()
"""
- # 2016-02-25, 1.12
- num = _index_deprecate(num)
+ try:
+ num = operator.index(num)
+ except TypeError:
+ raise TypeError(
+ "object of type {} cannot be safely interpreted as an integer."
+ .format(type(num)))
+
if num < 0:
raise ValueError("Number of samples, %s, must be non-negative." % num)
div = (num - 1) if endpoint else num
@@ -116,16 +133,15 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
if dtype is None:
dtype = dt
- y = _nx.arange(0, num, dtype=dt)
-
delta = stop - start
+ y = _nx.arange(0, num, dtype=dt).reshape((-1,) + (1,) * ndim(delta))
# In-place multiplication y *= delta/div is faster, but prevents the multiplicant
# from overriding what class is produced, and thus prevents, e.g. use of Quantities,
# see gh-7142. Hence, we multiply in place only for standard scalar types.
- _mult_inplace = _nx.isscalar(delta)
+ _mult_inplace = _nx.isscalar(delta)
if num > 1:
step = delta / div
- if step == 0:
+ if _nx.any(step == 0):
# Special handling for denormal numbers, gh-5437
y /= div
if _mult_inplace:
@@ -148,13 +164,23 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
if endpoint and num > 1:
y[-1] = stop
+ if axis != 0:
+ y = _nx.moveaxis(y, 0, axis)
+
if retstep:
return y.astype(dtype, copy=False), step
else:
return y.astype(dtype, copy=False)
-def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):
+def _logspace_dispatcher(start, stop, num=None, endpoint=None, base=None,
+ dtype=None, axis=None):
+ return (start, stop)
+
+
+@array_function_dispatch(_logspace_dispatcher)
+def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None,
+ axis=0):
"""
Return numbers spaced evenly on a log scale.
@@ -162,11 +188,14 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):
(`base` to the power of `start`) and ends with ``base ** stop``
(see `endpoint` below).
+ .. versionchanged:: 1.16.0
+ Non-scalar `start` and `stop` are now supported.
+
Parameters
----------
- start : float
+ start : array_like
``base ** start`` is the starting value of the sequence.
- stop : float
+ stop : array_like
``base ** stop`` is the final value of the sequence, unless `endpoint`
is False. In that case, ``num + 1`` values are spaced over the
interval in log-space, of which all but the last (a sequence of
@@ -183,6 +212,13 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):
dtype : dtype
The type of the output array. If `dtype` is not given, infer the data
type from the other input arguments.
+ axis : int, optional
+ The axis in the result to store the samples. Relevant only if start
+ or stop are array-like. By default (0), the samples will be along a
+ new axis inserted at the beginning. Use -1 to get an axis at the end.
+
+ .. versionadded:: 1.16.0
+
Returns
-------
@@ -210,11 +246,11 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):
Examples
--------
>>> np.logspace(2.0, 3.0, num=4)
- array([ 100. , 215.443469 , 464.15888336, 1000. ])
+ array([ 100. , 215.443469 , 464.15888336, 1000. ])
>>> np.logspace(2.0, 3.0, num=4, endpoint=False)
- array([ 100. , 177.827941 , 316.22776602, 562.34132519])
+ array([100. , 177.827941 , 316.22776602, 562.34132519])
>>> np.logspace(2.0, 3.0, num=4, base=2.0)
- array([ 4. , 5.0396842 , 6.34960421, 8. ])
+ array([4. , 5.0396842 , 6.34960421, 8. ])
Graphical illustration:
@@ -232,24 +268,33 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):
>>> plt.show()
"""
- y = linspace(start, stop, num=num, endpoint=endpoint)
+ y = linspace(start, stop, num=num, endpoint=endpoint, axis=axis)
if dtype is None:
return _nx.power(base, y)
- return _nx.power(base, y).astype(dtype)
+ return _nx.power(base, y).astype(dtype, copy=False)
+
+
+def _geomspace_dispatcher(start, stop, num=None, endpoint=None, dtype=None,
+ axis=None):
+ return (start, stop)
-def geomspace(start, stop, num=50, endpoint=True, dtype=None):
+@array_function_dispatch(_geomspace_dispatcher)
+def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
"""
Return numbers spaced evenly on a log scale (a geometric progression).
This is similar to `logspace`, but with endpoints specified directly.
Each output sample is a constant multiple of the previous.
+ .. versionchanged:: 1.16.0
+ Non-scalar `start` and `stop` are now supported.
+
Parameters
----------
- start : scalar
+ start : array_like
The starting value of the sequence.
- stop : scalar
+ stop : array_like
The final value of the sequence, unless `endpoint` is False.
In that case, ``num + 1`` values are spaced over the
interval in log-space, of which all but the last (a sequence of
@@ -262,6 +307,12 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None):
dtype : dtype
The type of the output array. If `dtype` is not given, infer the data
type from the other input arguments.
+ axis : int, optional
+ The axis in the result to store the samples. Relevant only if start
+ or stop are array-like. By default (0), the samples will be along a
+ new axis inserted at the beginning. Use -1 to get an axis at the end.
+
+ .. versionadded:: 1.16.0
Returns
-------
@@ -304,15 +355,15 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None):
Negative, decreasing, and complex inputs are allowed:
>>> np.geomspace(1000, 1, num=4)
- array([ 1000., 100., 10., 1.])
+ array([1000., 100., 10., 1.])
>>> np.geomspace(-1000, -1, num=4)
array([-1000., -100., -10., -1.])
>>> np.geomspace(1j, 1000j, num=4) # Straight line
- array([ 0. +1.j, 0. +10.j, 0. +100.j, 0.+1000.j])
+ array([0. +1.j, 0. +10.j, 0. +100.j, 0.+1000.j])
>>> np.geomspace(-1+0j, 1+0j, num=5) # Circle
- array([-1.00000000+0.j , -0.70710678+0.70710678j,
- 0.00000000+1.j , 0.70710678+0.70710678j,
- 1.00000000+0.j ])
+ array([-1.00000000e+00+1.22464680e-16j, -7.07106781e-01+7.07106781e-01j,
+ 6.12323400e-17+1.00000000e+00j, 7.07106781e-01+7.07106781e-01j,
+ 1.00000000e+00+0.00000000e+00j])
Graphical illustration of ``endpoint`` parameter:
@@ -320,78 +371,143 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None):
>>> N = 10
>>> y = np.zeros(N)
>>> plt.semilogx(np.geomspace(1, 1000, N, endpoint=True), y + 1, 'o')
+ [<matplotlib.lines.Line2D object at 0x...>]
>>> plt.semilogx(np.geomspace(1, 1000, N, endpoint=False), y + 2, 'o')
+ [<matplotlib.lines.Line2D object at 0x...>]
>>> plt.axis([0.5, 2000, 0, 3])
+ [0.5, 2000, 0, 3]
>>> plt.grid(True, color='0.7', linestyle='-', which='both', axis='both')
>>> plt.show()
"""
- if start == 0 or stop == 0:
+ start = asanyarray(start)
+ stop = asanyarray(stop)
+ if _nx.any(start == 0) or _nx.any(stop == 0):
raise ValueError('Geometric sequence cannot include zero')
- dt = result_type(start, stop, float(num))
+ dt = result_type(start, stop, float(num), _nx.zeros((), dtype))
if dtype is None:
dtype = dt
else:
# complex to dtype('complex128'), for instance
dtype = _nx.dtype(dtype)
+ # Promote both arguments to the same dtype in case, for instance, one is
+ # complex and another is negative and log would produce NaN otherwise.
+ # Copy since we may change things in-place further down.
+ start = start.astype(dt, copy=True)
+ stop = stop.astype(dt, copy=True)
+
+ out_sign = _nx.ones(_nx.broadcast(start, stop).shape, dt)
# Avoid negligible real or imaginary parts in output by rotating to
# positive real, calculating, then undoing rotation
- out_sign = 1
- if start.real == stop.real == 0:
- start, stop = start.imag, stop.imag
- out_sign = 1j * out_sign
- if _nx.sign(start) == _nx.sign(stop) == -1:
- start, stop = -start, -stop
- out_sign = -out_sign
-
- # Promote both arguments to the same dtype in case, for instance, one is
- # complex and another is negative and log would produce NaN otherwise
- start = start + (stop - stop)
- stop = stop + (start - start)
- if _nx.issubdtype(dtype, _nx.complexfloating):
- start = start + 0j
- stop = stop + 0j
+ if _nx.issubdtype(dt, _nx.complexfloating):
+ all_imag = (start.real == 0.) & (stop.real == 0.)
+ if _nx.any(all_imag):
+ start[all_imag] = start[all_imag].imag
+ stop[all_imag] = stop[all_imag].imag
+ out_sign[all_imag] = 1j
+
+ both_negative = (_nx.sign(start) == -1) & (_nx.sign(stop) == -1)
+ if _nx.any(both_negative):
+ _nx.negative(start, out=start, where=both_negative)
+ _nx.negative(stop, out=stop, where=both_negative)
+ _nx.negative(out_sign, out=out_sign, where=both_negative)
log_start = _nx.log10(start)
log_stop = _nx.log10(stop)
result = out_sign * logspace(log_start, log_stop, num=num,
endpoint=endpoint, base=10.0, dtype=dtype)
+ if axis != 0:
+ result = _nx.moveaxis(result, 0, axis)
- return result.astype(dtype)
+ return result.astype(dtype, copy=False)
-#always succeed
-def add_newdoc(place, obj, doc):
+def _needs_add_docstring(obj):
"""
- Adds documentation to obj which is in module place.
+ Returns true if the only way to set the docstring of `obj` from python is
+ via add_docstring.
+
+ This function errs on the side of being overly conservative.
+ """
+ Py_TPFLAGS_HEAPTYPE = 1 << 9
+
+ if isinstance(obj, (types.FunctionType, types.MethodType, property)):
+ return False
+
+ if isinstance(obj, type) and obj.__flags__ & Py_TPFLAGS_HEAPTYPE:
+ return False
- If doc is a string add it to obj as a docstring
+ return True
- If doc is a tuple, then the first element is interpreted as
- an attribute of obj and the second as the docstring
- (method, docstring)
- If doc is a list, then each element of the list should be a
- sequence of length two --> [(method1, docstring1),
- (method2, docstring2), ...]
+def _add_docstring(obj, doc, warn_on_python):
+ if warn_on_python and not _needs_add_docstring(obj):
+ warnings.warn(
+ "add_newdoc was used on a pure-python object {}. "
+ "Prefer to attach it directly to the source."
+ .format(obj),
+ UserWarning,
+ stacklevel=3)
+ try:
+ add_docstring(obj, doc)
+ except Exception:
+ pass
+
+
+def add_newdoc(place, obj, doc, warn_on_python=True):
+ """
+ Add documentation to an existing object, typically one defined in C
- This routine never raises an error.
+ The purpose is to allow easier editing of the docstrings without requiring
+ a re-compile. This exists primarily for internal use within numpy itself.
+
+ Parameters
+ ----------
+ place : str
+ The absolute name of the module to import from
+ obj : str
+ The name of the object to add documentation to, typically a class or
+ function name
+ doc : {str, Tuple[str, str], List[Tuple[str, str]]}
+ If a string, the documentation to apply to `obj`
+
+ If a tuple, then the first element is interpreted as an attribute of
+ `obj` and the second as the docstring to apply - ``(method, docstring)``
+
+ If a list, then each element of the list should be a tuple of length
+ two - ``[(method1, docstring1), (method2, docstring2), ...]``
+ warn_on_python : bool
+ If True, the default, emit `UserWarning` if this is used to attach
+ documentation to a pure-python object.
+
+ Notes
+ -----
+ This routine never raises an error if the docstring can't be written, but
+ will raise an error if the object being documented does not exist.
This routine cannot modify read-only docstrings, as appear
in new-style classes or built-in functions. Because this
routine never raises an error the caller must check manually
that the docstrings were changed.
+
+ Since this function grabs the ``char *`` from a c-level str object and puts
+ it into the ``tp_doc`` slot of the type of `obj`, it violates a number of
+ C-API best-practices, by:
+
+ - modifying a `PyTypeObject` after calling `PyType_Ready`
+ - calling `Py_INCREF` on the str and losing the reference, so the str
+ will never be released
+
+ If possible it should be avoided.
"""
- try:
- new = getattr(__import__(place, globals(), {}, [obj]), obj)
- if isinstance(doc, str):
- add_docstring(new, doc.strip())
- elif isinstance(doc, tuple):
- add_docstring(getattr(new, doc[0]), doc[1].strip())
- elif isinstance(doc, list):
- for val in doc:
- add_docstring(getattr(new, val[0]), val[1].strip())
- except Exception:
- pass
+ new = getattr(__import__(place, globals(), {}, [obj]), obj)
+ if isinstance(doc, str):
+ _add_docstring(new, doc.strip(), warn_on_python)
+ elif isinstance(doc, tuple):
+ attr, docstring = doc
+ _add_docstring(getattr(new, attr), docstring.strip(), warn_on_python)
+ elif isinstance(doc, list):
+ for attr, docstring in doc:
+ _add_docstring(getattr(new, attr), docstring.strip(), warn_on_python)
diff --git a/numpy/core/getlimits.py b/numpy/core/getlimits.py
index 0e3c58793..31fa6b9bf 100644
--- a/numpy/core/getlimits.py
+++ b/numpy/core/getlimits.py
@@ -8,6 +8,7 @@ __all__ = ['finfo', 'iinfo']
import warnings
from .machar import MachAr
+from .overrides import set_module
from . import numeric
from . import numerictypes as ntypes
from .numeric import array, inf
@@ -30,6 +31,32 @@ def _fr1(a):
a.shape = ()
return a
+class MachArLike(object):
+ """ Object to simulate MachAr instance """
+
+ def __init__(self,
+ ftype,
+ **kwargs):
+ params = _MACHAR_PARAMS[ftype]
+ float_conv = lambda v: array([v], ftype)
+ float_to_float = lambda v : _fr1(float_conv(v))
+ float_to_str = lambda v: (params['fmt'] % array(_fr0(v)[0], ftype))
+
+ self.title = params['title']
+ # Parameter types same as for discovered MachAr object.
+ self.epsilon = self.eps = float_to_float(kwargs.pop('eps'))
+ self.epsneg = float_to_float(kwargs.pop('epsneg'))
+ self.xmax = self.huge = float_to_float(kwargs.pop('huge'))
+ self.xmin = self.tiny = float_to_float(kwargs.pop('tiny'))
+ self.ibeta = params['itype'](kwargs.pop('ibeta'))
+ self.__dict__.update(kwargs)
+ self.precision = int(-log10(self.eps))
+ self.resolution = float_to_float(float_conv(10) ** (-self.precision))
+ self._str_eps = float_to_str(self.eps)
+ self._str_epsneg = float_to_str(self.epsneg)
+ self._str_xmin = float_to_str(self.xmin)
+ self._str_xmax = float_to_str(self.xmax)
+ self._str_resolution = float_to_str(self.resolution)
_convert_to_float = {
ntypes.csingle: ntypes.single,
@@ -37,7 +64,6 @@ _convert_to_float = {
ntypes.clongfloat: ntypes.longfloat
}
-
# Parameters for creating MachAr / MachAr-like objects
_title_fmt = 'numpy {} precision floating point number'
_MACHAR_PARAMS = {
@@ -58,194 +84,156 @@ _MACHAR_PARAMS = {
fmt = '%12.5e',
title = _title_fmt.format('half'))}
-
-class MachArLike(object):
- """ Object to simulate MachAr instance """
-
- def __init__(self,
- ftype,
- **kwargs):
- params = _MACHAR_PARAMS[ftype]
- float_conv = lambda v: array([v], ftype)
- float_to_float = lambda v : _fr1(float_conv(v))
- self._float_to_str = lambda v: (params['fmt'] %
- array(_fr0(v)[0], ftype))
- self.title = params['title']
- # Parameter types same as for discovered MachAr object.
- self.epsilon = self.eps = float_to_float(kwargs.pop('eps'))
- self.epsneg = float_to_float(kwargs.pop('epsneg'))
- self.xmax = self.huge = float_to_float(kwargs.pop('huge'))
- self.xmin = self.tiny = float_to_float(kwargs.pop('tiny'))
- self.ibeta = params['itype'](kwargs.pop('ibeta'))
- self.__dict__.update(kwargs)
- self.precision = int(-log10(self.eps))
- self.resolution = float_to_float(float_conv(10) ** (-self.precision))
-
- # Properties below to delay need for float_to_str, and thus avoid circular
- # imports during early numpy module loading.
- # See: https://github.com/numpy/numpy/pull/8983#discussion_r115838683
-
- @property
- def _str_eps(self):
- return self._float_to_str(self.eps)
-
- @property
- def _str_epsneg(self):
- return self._float_to_str(self.epsneg)
-
- @property
- def _str_xmin(self):
- return self._float_to_str(self.xmin)
-
- @property
- def _str_xmax(self):
- return self._float_to_str(self.xmax)
-
- @property
- def _str_resolution(self):
- return self._float_to_str(self.resolution)
-
-
-# Known parameters for float16
-# See docstring of MachAr class for description of parameters.
-_f16 = ntypes.float16
-_float16_ma = MachArLike(_f16,
- machep=-10,
- negep=-11,
- minexp=-14,
- maxexp=16,
- it=10,
- iexp=5,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(_f16(-10)),
- epsneg=exp2(_f16(-11)),
- huge=_f16(65504),
- tiny=_f16(2 ** -14))
-
-# Known parameters for float32
-_f32 = ntypes.float32
-_float32_ma = MachArLike(_f32,
- machep=-23,
- negep=-24,
- minexp=-126,
- maxexp=128,
- it=23,
- iexp=8,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(_f32(-23)),
- epsneg=exp2(_f32(-24)),
- huge=_f32((1 - 2 ** -24) * 2**128),
- tiny=exp2(_f32(-126)))
-
-# Known parameters for float64
-_f64 = ntypes.float64
-_epsneg_f64 = 2.0 ** -53.0
-_tiny_f64 = 2.0 ** -1022.0
-_float64_ma = MachArLike(_f64,
- machep=-52,
- negep=-53,
- minexp=-1022,
- maxexp=1024,
- it=52,
- iexp=11,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=2.0 ** -52.0,
- epsneg=_epsneg_f64,
- huge=(1.0 - _epsneg_f64) / _tiny_f64 * _f64(4),
- tiny=_tiny_f64)
-
-# Known parameters for IEEE 754 128-bit binary float
-_ld = ntypes.longdouble
-_epsneg_f128 = exp2(_ld(-113))
-_tiny_f128 = exp2(_ld(-16382))
-# Ignore runtime error when this is not f128
-with numeric.errstate(all='ignore'):
- _huge_f128 = (_ld(1) - _epsneg_f128) / _tiny_f128 * _ld(4)
-_float128_ma = MachArLike(_ld,
- machep=-112,
- negep=-113,
- minexp=-16382,
- maxexp=16384,
- it=112,
- iexp=15,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(_ld(-112)),
- epsneg=_epsneg_f128,
- huge=_huge_f128,
- tiny=_tiny_f128)
-
-# Known parameters for float80 (Intel 80-bit extended precision)
-_epsneg_f80 = exp2(_ld(-64))
-_tiny_f80 = exp2(_ld(-16382))
-# Ignore runtime error when this is not f80
-with numeric.errstate(all='ignore'):
- _huge_f80 = (_ld(1) - _epsneg_f80) / _tiny_f80 * _ld(4)
-_float80_ma = MachArLike(_ld,
- machep=-63,
- negep=-64,
- minexp=-16382,
- maxexp=16384,
- it=63,
- iexp=15,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(_ld(-63)),
- epsneg=_epsneg_f80,
- huge=_huge_f80,
- tiny=_tiny_f80)
-
-# Guessed / known parameters for double double; see:
-# https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic
-# These numbers have the same exponent range as float64, but extended number of
-# digits in the significand.
-_huge_dd = (umath.nextafter(_ld(inf), _ld(0))
- if hasattr(umath, 'nextafter') # Missing on some platforms?
- else _float64_ma.huge)
-_float_dd_ma = MachArLike(_ld,
- machep=-105,
- negep=-106,
- minexp=-1022,
- maxexp=1024,
- it=105,
- iexp=11,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(_ld(-105)),
- epsneg= exp2(_ld(-106)),
- huge=_huge_dd,
- tiny=exp2(_ld(-1022)))
-
-
# Key to identify the floating point type. Key is result of
# ftype('-0.1').newbyteorder('<').tobytes()
# See:
# https://perl5.git.perl.org/perl.git/blob/3118d7d684b56cbeb702af874f4326683c45f045:/Configure
-_KNOWN_TYPES = {
- b'\x9a\x99\x99\x99\x99\x99\xb9\xbf' : _float64_ma,
- b'\xcd\xcc\xcc\xbd' : _float32_ma,
- b'f\xae' : _float16_ma,
+_KNOWN_TYPES = {}
+def _register_type(machar, bytepat):
+ _KNOWN_TYPES[bytepat] = machar
+_float_ma = {}
+
+def _register_known_types():
+ # Known parameters for float16
+ # See docstring of MachAr class for description of parameters.
+ f16 = ntypes.float16
+ float16_ma = MachArLike(f16,
+ machep=-10,
+ negep=-11,
+ minexp=-14,
+ maxexp=16,
+ it=10,
+ iexp=5,
+ ibeta=2,
+ irnd=5,
+ ngrd=0,
+ eps=exp2(f16(-10)),
+ epsneg=exp2(f16(-11)),
+ huge=f16(65504),
+ tiny=f16(2 ** -14))
+ _register_type(float16_ma, b'f\xae')
+ _float_ma[16] = float16_ma
+
+ # Known parameters for float32
+ f32 = ntypes.float32
+ float32_ma = MachArLike(f32,
+ machep=-23,
+ negep=-24,
+ minexp=-126,
+ maxexp=128,
+ it=23,
+ iexp=8,
+ ibeta=2,
+ irnd=5,
+ ngrd=0,
+ eps=exp2(f32(-23)),
+ epsneg=exp2(f32(-24)),
+ huge=f32((1 - 2 ** -24) * 2**128),
+ tiny=exp2(f32(-126)))
+ _register_type(float32_ma, b'\xcd\xcc\xcc\xbd')
+ _float_ma[32] = float32_ma
+
+ # Known parameters for float64
+ f64 = ntypes.float64
+ epsneg_f64 = 2.0 ** -53.0
+ tiny_f64 = 2.0 ** -1022.0
+ float64_ma = MachArLike(f64,
+ machep=-52,
+ negep=-53,
+ minexp=-1022,
+ maxexp=1024,
+ it=52,
+ iexp=11,
+ ibeta=2,
+ irnd=5,
+ ngrd=0,
+ eps=2.0 ** -52.0,
+ epsneg=epsneg_f64,
+ huge=(1.0 - epsneg_f64) / tiny_f64 * f64(4),
+ tiny=tiny_f64)
+ _register_type(float64_ma, b'\x9a\x99\x99\x99\x99\x99\xb9\xbf')
+ _float_ma[64] = float64_ma
+
+ # Known parameters for IEEE 754 128-bit binary float
+ ld = ntypes.longdouble
+ epsneg_f128 = exp2(ld(-113))
+ tiny_f128 = exp2(ld(-16382))
+ # Ignore runtime error when this is not f128
+ with numeric.errstate(all='ignore'):
+ huge_f128 = (ld(1) - epsneg_f128) / tiny_f128 * ld(4)
+ float128_ma = MachArLike(ld,
+ machep=-112,
+ negep=-113,
+ minexp=-16382,
+ maxexp=16384,
+ it=112,
+ iexp=15,
+ ibeta=2,
+ irnd=5,
+ ngrd=0,
+ eps=exp2(ld(-112)),
+ epsneg=epsneg_f128,
+ huge=huge_f128,
+ tiny=tiny_f128)
+ # IEEE 754 128-bit binary float
+ _register_type(float128_ma,
+ b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf')
+ _register_type(float128_ma,
+ b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf')
+ _float_ma[128] = float128_ma
+
+ # Known parameters for float80 (Intel 80-bit extended precision)
+ epsneg_f80 = exp2(ld(-64))
+ tiny_f80 = exp2(ld(-16382))
+ # Ignore runtime error when this is not f80
+ with numeric.errstate(all='ignore'):
+ huge_f80 = (ld(1) - epsneg_f80) / tiny_f80 * ld(4)
+ float80_ma = MachArLike(ld,
+ machep=-63,
+ negep=-64,
+ minexp=-16382,
+ maxexp=16384,
+ it=63,
+ iexp=15,
+ ibeta=2,
+ irnd=5,
+ ngrd=0,
+ eps=exp2(ld(-63)),
+ epsneg=epsneg_f80,
+ huge=huge_f80,
+ tiny=tiny_f80)
# float80, first 10 bytes containing actual storage
- b'\xcd\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf' : _float80_ma,
+ _register_type(float80_ma, b'\xcd\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf')
+ _float_ma[80] = float80_ma
+
+ # Guessed / known parameters for double double; see:
+ # https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic
+ # These numbers have the same exponent range as float64, but extended number of
+ # digits in the significand.
+ huge_dd = (umath.nextafter(ld(inf), ld(0))
+ if hasattr(umath, 'nextafter') # Missing on some platforms?
+ else float64_ma.huge)
+ float_dd_ma = MachArLike(ld,
+ machep=-105,
+ negep=-106,
+ minexp=-1022,
+ maxexp=1024,
+ it=105,
+ iexp=11,
+ ibeta=2,
+ irnd=5,
+ ngrd=0,
+ eps=exp2(ld(-105)),
+ epsneg= exp2(ld(-106)),
+ huge=huge_dd,
+ tiny=exp2(ld(-1022)))
# double double; low, high order (e.g. PPC 64)
- b'\x9a\x99\x99\x99\x99\x99Y<\x9a\x99\x99\x99\x99\x99\xb9\xbf' :
- _float_dd_ma,
+ _register_type(float_dd_ma,
+ b'\x9a\x99\x99\x99\x99\x99Y<\x9a\x99\x99\x99\x99\x99\xb9\xbf')
# double double; high, low order (e.g. PPC 64 le)
- b'\x9a\x99\x99\x99\x99\x99\xb9\xbf\x9a\x99\x99\x99\x99\x99Y<' :
- _float_dd_ma,
- # IEEE 754 128-bit binary float
- b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf' :
- _float128_ma,
-}
+ _register_type(float_dd_ma,
+ b'\x9a\x99\x99\x99\x99\x99\xb9\xbf\x9a\x99\x99\x99\x99\x99Y<')
+ _float_ma['dd'] = float_dd_ma
def _get_machar(ftype):
@@ -302,6 +290,7 @@ def _discovered_machar(ftype):
params['title'])
+@set_module('numpy')
class finfo(object):
"""
finfo(dtype)
@@ -452,6 +441,7 @@ class finfo(object):
" max=%(_str_max)s, dtype=%(dtype)s)") % d)
+@set_module('numpy')
class iinfo(object):
"""
iinfo(type)
@@ -515,6 +505,7 @@ class iinfo(object):
if self.kind not in 'iu':
raise ValueError("Invalid integer data type %r." % (self.kind,))
+ @property
def min(self):
"""Minimum value of given dtype."""
if self.kind == 'u':
@@ -527,8 +518,7 @@ class iinfo(object):
iinfo._min_vals[self.key] = val
return val
- min = property(min)
-
+ @property
def max(self):
"""Maximum value of given dtype."""
try:
@@ -541,8 +531,6 @@ class iinfo(object):
iinfo._max_vals[self.key] = val
return val
- max = property(max)
-
def __str__(self):
"""String representation."""
fmt = (
diff --git a/numpy/core/include/numpy/ndarrayobject.h b/numpy/core/include/numpy/ndarrayobject.h
index 12fc7098c..95e9cb060 100644
--- a/numpy/core/include/numpy/ndarrayobject.h
+++ b/numpy/core/include/numpy/ndarrayobject.h
@@ -5,13 +5,7 @@
#ifndef NPY_NDARRAYOBJECT_H
#define NPY_NDARRAYOBJECT_H
#ifdef __cplusplus
-#define CONFUSE_EMACS {
-#define CONFUSE_EMACS2 }
-extern "C" CONFUSE_EMACS
-#undef CONFUSE_EMACS
-#undef CONFUSE_EMACS2
-/* ... otherwise a semi-smart identer (like emacs) tries to indent
- everything when you're typing */
+extern "C" {
#endif
#include <Python.h>
@@ -29,7 +23,7 @@ extern "C" CONFUSE_EMACS
/* C-API that requires previous API to be defined */
-#define PyArray_DescrCheck(op) (((PyObject*)(op))->ob_type==&PyArrayDescr_Type)
+#define PyArray_DescrCheck(op) PyObject_TypeCheck(op, &PyArrayDescr_Type)
#define PyArray_Check(op) PyObject_TypeCheck(op, &PyArray_Type)
#define PyArray_CheckExact(op) (((PyObject*)(op))->ob_type == &PyArray_Type)
@@ -239,10 +233,10 @@ static NPY_INLINE int
NPY_TITLE_KEY_check(PyObject *key, PyObject *value)
{
PyObject *title;
- if (PyTuple_GET_SIZE(value) != 3) {
+ if (PyTuple_Size(value) != 3) {
return 0;
}
- title = PyTuple_GET_ITEM(value, 2);
+ title = PyTuple_GetItem(value, 2);
if (key == title) {
return 1;
}
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index ec2893b21..ad98d562b 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -156,12 +156,20 @@ enum NPY_TYPECHAR {
NPY_COMPLEXLTR = 'c'
};
+/*
+ * Changing this may break Numpy API compatibility
+ * due to changing offsets in PyArray_ArrFuncs, so be
+ * careful. Here we have reused the mergesort slot for
+ * any kind of stable sort, the actual implementation will
+ * depend on the data type.
+ */
typedef enum {
NPY_QUICKSORT=0,
NPY_HEAPSORT=1,
- NPY_MERGESORT=2
+ NPY_MERGESORT=2,
+ NPY_STABLESORT=2,
} NPY_SORTKIND;
-#define NPY_NSORTS (NPY_MERGESORT + 1)
+#define NPY_NSORTS (NPY_STABLESORT + 1)
typedef enum {
@@ -505,7 +513,8 @@ typedef struct {
PyArray_NonzeroFunc *nonzero;
/*
- * Used for arange.
+ * Used for arange. Should return 0 on success
+ * and -1 on failure.
* Can be NULL.
*/
PyArray_FillFunc *fill;
@@ -949,12 +958,12 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
*/
-#define PyArray_ISCONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS)
-#define PyArray_ISWRITEABLE(m) PyArray_CHKFLAGS(m, NPY_ARRAY_WRITEABLE)
-#define PyArray_ISALIGNED(m) PyArray_CHKFLAGS(m, NPY_ARRAY_ALIGNED)
+#define PyArray_ISCONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_C_CONTIGUOUS)
+#define PyArray_ISWRITEABLE(m) PyArray_CHKFLAGS((m), NPY_ARRAY_WRITEABLE)
+#define PyArray_ISALIGNED(m) PyArray_CHKFLAGS((m), NPY_ARRAY_ALIGNED)
-#define PyArray_IS_C_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS)
-#define PyArray_IS_F_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_F_CONTIGUOUS)
+#define PyArray_IS_C_CONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_C_CONTIGUOUS)
+#define PyArray_IS_F_CONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_F_CONTIGUOUS)
/* the variable is used in some places, so always define it */
#define NPY_BEGIN_THREADS_DEF PyThreadState *_save=NULL;
@@ -964,15 +973,15 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
#define NPY_BEGIN_THREADS do {_save = PyEval_SaveThread();} while (0);
#define NPY_END_THREADS do { if (_save) \
{ PyEval_RestoreThread(_save); _save = NULL;} } while (0);
-#define NPY_BEGIN_THREADS_THRESHOLDED(loop_size) do { if (loop_size > 500) \
+#define NPY_BEGIN_THREADS_THRESHOLDED(loop_size) do { if ((loop_size) > 500) \
{ _save = PyEval_SaveThread();} } while (0);
#define NPY_BEGIN_THREADS_DESCR(dtype) \
- do {if (!(PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI))) \
+ do {if (!(PyDataType_FLAGCHK((dtype), NPY_NEEDS_PYAPI))) \
NPY_BEGIN_THREADS;} while (0);
#define NPY_END_THREADS_DESCR(dtype) \
- do {if (!(PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI))) \
+ do {if (!(PyDataType_FLAGCHK((dtype), NPY_NEEDS_PYAPI))) \
NPY_END_THREADS; } while (0);
#define NPY_ALLOW_C_API_DEF PyGILState_STATE __save__;
@@ -1086,7 +1095,8 @@ typedef struct PyArrayIterObject_tag PyArrayIterObject;
* type of the function which translates a set of coordinates to a
* pointer to the data
*/
-typedef char* (*npy_iter_get_dataptr_t)(PyArrayIterObject* iter, npy_intp*);
+typedef char* (*npy_iter_get_dataptr_t)(
+ PyArrayIterObject* iter, const npy_intp*);
struct PyArrayIterObject_tag {
PyObject_HEAD
@@ -1109,7 +1119,7 @@ struct PyArrayIterObject_tag {
/* Iterator API */
-#define PyArrayIter_Check(op) PyObject_TypeCheck(op, &PyArrayIter_Type)
+#define PyArrayIter_Check(op) PyObject_TypeCheck((op), &PyArrayIter_Type)
#define _PyAIT(it) ((PyArrayIterObject *)(it))
#define PyArray_ITER_RESET(it) do { \
@@ -1187,7 +1197,7 @@ struct PyArrayIterObject_tag {
#define PyArray_ITER_GOTO1D(it, ind) do { \
int __npy_i; \
- npy_intp __npy_ind = (npy_intp) (ind); \
+ npy_intp __npy_ind = (npy_intp)(ind); \
if (__npy_ind < 0) __npy_ind += _PyAIT(it)->size; \
_PyAIT(it)->index = __npy_ind; \
if (_PyAIT(it)->nd_m1 == 0) { \
@@ -1670,7 +1680,7 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags)
#define PyTypeNum_ISOBJECT(type) ((type) == NPY_OBJECT)
-#define PyDataType_ISBOOL(obj) PyTypeNum_ISBOOL(_PyADt(obj))
+#define PyDataType_ISBOOL(obj) PyTypeNum_ISBOOL(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_ISUNSIGNED(obj) PyTypeNum_ISUNSIGNED(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_ISSIGNED(obj) PyTypeNum_ISSIGNED(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_ISINTEGER(obj) PyTypeNum_ISINTEGER(((PyArray_Descr*)(obj))->type_num )
@@ -1686,7 +1696,8 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags)
#define PyDataType_ISOBJECT(obj) PyTypeNum_ISOBJECT(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_HASFIELDS(obj) (((PyArray_Descr *)(obj))->names != NULL)
#define PyDataType_HASSUBARRAY(dtype) ((dtype)->subarray != NULL)
-#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0)
+#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0 && \
+ !PyDataType_HASFIELDS(dtype))
#define PyDataType_MAKEUNSIZED(dtype) ((dtype)->elsize = 0)
#define PyArray_ISBOOL(obj) PyTypeNum_ISBOOL(PyArray_TYPE(obj))
diff --git a/numpy/core/include/numpy/npy_1_7_deprecated_api.h b/numpy/core/include/numpy/npy_1_7_deprecated_api.h
index 76b57b748..a6ee21219 100644
--- a/numpy/core/include/numpy/npy_1_7_deprecated_api.h
+++ b/numpy/core/include/numpy/npy_1_7_deprecated_api.h
@@ -5,6 +5,8 @@
#error "Should never include npy_*_*_deprecated_api directly."
#endif
+/* Emit a warning if the user did not specifically request the old API */
+#ifndef NPY_NO_DEPRECATED_API
#if defined(_WIN32)
#define _WARN___STR2__(x) #x
#define _WARN___STR1__(x) _WARN___STR2__(x)
@@ -16,6 +18,7 @@
"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION"
#endif
/* TODO: How to do this warning message for other compilers? */
+#endif
/*
* This header exists to collect all dangerous/deprecated NumPy API
diff --git a/numpy/core/include/numpy/npy_3kcompat.h b/numpy/core/include/numpy/npy_3kcompat.h
index 808518266..832bc0599 100644
--- a/numpy/core/include/numpy/npy_3kcompat.h
+++ b/numpy/core/include/numpy/npy_3kcompat.h
@@ -69,6 +69,16 @@ static NPY_INLINE int PyInt_Check(PyObject *op) {
#define Npy_EnterRecursiveCall(x) Py_EnterRecursiveCall(x)
#endif
+/* Py_SETREF was added in 3.5.2, and only if Py_LIMITED_API is absent */
+#if PY_VERSION_HEX < 0x03050200
+ #define Py_SETREF(op, op2) \
+ do { \
+ PyObject *_py_tmp = (PyObject *)(op); \
+ (op) = (op2); \
+ Py_DECREF(_py_tmp); \
+ } while (0)
+#endif
+
/*
* PyString -> PyBytes
*/
@@ -141,20 +151,14 @@ static NPY_INLINE int PyInt_Check(PyObject *op) {
static NPY_INLINE void
PyUnicode_ConcatAndDel(PyObject **left, PyObject *right)
{
- PyObject *newobj;
- newobj = PyUnicode_Concat(*left, right);
- Py_DECREF(*left);
+ Py_SETREF(*left, PyUnicode_Concat(*left, right));
Py_DECREF(right);
- *left = newobj;
}
static NPY_INLINE void
PyUnicode_Concat2(PyObject **left, PyObject *right)
{
- PyObject *newobj;
- newobj = PyUnicode_Concat(*left, right);
- Py_DECREF(*left);
- *left = newobj;
+ Py_SETREF(*left, PyUnicode_Concat(*left, right));
}
/*
@@ -215,6 +219,7 @@ npy_PyFile_Dup2(PyObject *file, char *mode, npy_off_t *orig_pos)
if (handle == NULL) {
PyErr_SetString(PyExc_IOError,
"Getting a FILE* from a Python file object failed");
+ return NULL;
}
/* Record the original raw file handle position */
@@ -379,6 +384,36 @@ npy_PyFile_CloseFile(PyObject *file)
}
+/* This is a copy of _PyErr_ChainExceptions
+ */
+static NPY_INLINE void
+npy_PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb)
+{
+ if (exc == NULL)
+ return;
+
+ if (PyErr_Occurred()) {
+ /* only py3 supports this anyway */
+ #ifdef NPY_PY3K
+ PyObject *exc2, *val2, *tb2;
+ PyErr_Fetch(&exc2, &val2, &tb2);
+ PyErr_NormalizeException(&exc, &val, &tb);
+ if (tb != NULL) {
+ PyException_SetTraceback(val, tb);
+ Py_DECREF(tb);
+ }
+ Py_DECREF(exc);
+ PyErr_NormalizeException(&exc2, &val2, &tb2);
+ PyException_SetContext(val2, val);
+ PyErr_Restore(exc2, val2, tb2);
+ #endif
+ }
+ else {
+ PyErr_Restore(exc, val, tb);
+ }
+}
+
+
/* This is a copy of _PyErr_ChainExceptions, with:
* - a minimal implementation for python 2
* - __cause__ used instead of __context__
diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h
index 64aaaacff..27b83f7b5 100644
--- a/numpy/core/include/numpy/npy_common.h
+++ b/numpy/core/include/numpy/npy_common.h
@@ -44,12 +44,26 @@
#else
#define NPY_GCC_TARGET_AVX
#endif
+
+#if defined HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS
+#define HAVE_ATTRIBUTE_TARGET_FMA
+#define NPY_GCC_TARGET_FMA __attribute__((target("avx2,fma")))
+#endif
+
#if defined HAVE_ATTRIBUTE_TARGET_AVX2 && defined HAVE_LINK_AVX2
#define NPY_GCC_TARGET_AVX2 __attribute__((target("avx2")))
#else
#define NPY_GCC_TARGET_AVX2
#endif
+#if defined HAVE_ATTRIBUTE_TARGET_AVX512F && defined HAVE_LINK_AVX512F
+#define NPY_GCC_TARGET_AVX512F __attribute__((target("avx512f")))
+#elif defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS
+#define NPY_GCC_TARGET_AVX512F __attribute__((target("avx512f")))
+#else
+#define NPY_GCC_TARGET_AVX512F
+#endif
+
/*
* mark an argument (starting from 1) that must not be NULL and is not checked
* DO NOT USE IF FUNCTION CHECKS FOR NULL!! the compiler will remove the check
@@ -68,6 +82,13 @@
#define NPY_HAVE_SSE2_INTRINSICS
#endif
+#if defined HAVE_IMMINTRIN_H && defined HAVE_LINK_AVX2
+#define NPY_HAVE_AVX2_INTRINSICS
+#endif
+
+#if defined HAVE_IMMINTRIN_H && defined HAVE_LINK_AVX512F
+#define NPY_HAVE_AVX512F_INTRINSICS
+#endif
/*
* give a hint to the compiler which branch is more likely or unlikely
* to occur, e.g. rare error cases:
diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h
index 582390cdc..69e690f28 100644
--- a/numpy/core/include/numpy/npy_math.h
+++ b/numpy/core/include/numpy/npy_math.h
@@ -114,6 +114,101 @@ NPY_INLINE static float __npy_nzerof(void)
#define NPY_SQRT1_2l 0.707106781186547524400844362104849039L /* 1/sqrt(2) */
/*
+ * Constants used in vector implementation of exp(x)
+ */
+#define NPY_RINT_CVT_MAGICf 0x1.800000p+23f
+#define NPY_CODY_WAITE_LOGE_2_HIGHf -6.93145752e-1f
+#define NPY_CODY_WAITE_LOGE_2_LOWf -1.42860677e-6f
+#define NPY_COEFF_P0_EXPf 9.999999999980870924916e-01f
+#define NPY_COEFF_P1_EXPf 7.257664613233124478488e-01f
+#define NPY_COEFF_P2_EXPf 2.473615434895520810817e-01f
+#define NPY_COEFF_P3_EXPf 5.114512081637298353406e-02f
+#define NPY_COEFF_P4_EXPf 6.757896990527504603057e-03f
+#define NPY_COEFF_P5_EXPf 5.082762527590693718096e-04f
+#define NPY_COEFF_Q0_EXPf 1.000000000000000000000e+00f
+#define NPY_COEFF_Q1_EXPf -2.742335390411667452936e-01f
+#define NPY_COEFF_Q2_EXPf 2.159509375685829852307e-02f
+
+/*
+ * Constants used in vector implementation of log(x)
+ */
+#define NPY_COEFF_P0_LOGf 0.000000000000000000000e+00f
+#define NPY_COEFF_P1_LOGf 9.999999999999998702752e-01f
+#define NPY_COEFF_P2_LOGf 2.112677543073053063722e+00f
+#define NPY_COEFF_P3_LOGf 1.480000633576506585156e+00f
+#define NPY_COEFF_P4_LOGf 3.808837741388407920751e-01f
+#define NPY_COEFF_P5_LOGf 2.589979117907922693523e-02f
+#define NPY_COEFF_Q0_LOGf 1.000000000000000000000e+00f
+#define NPY_COEFF_Q1_LOGf 2.612677543073109236779e+00f
+#define NPY_COEFF_Q2_LOGf 2.453006071784736363091e+00f
+#define NPY_COEFF_Q3_LOGf 9.864942958519418960339e-01f
+#define NPY_COEFF_Q4_LOGf 1.546476374983906719538e-01f
+#define NPY_COEFF_Q5_LOGf 5.875095403124574342950e-03f
+/*
+ * Constants used in vector implementation of sinf/cosf(x)
+ */
+#define NPY_TWO_O_PIf 0x1.45f306p-1f
+#define NPY_CODY_WAITE_PI_O_2_HIGHf -0x1.921fb0p+00f
+#define NPY_CODY_WAITE_PI_O_2_MEDf -0x1.5110b4p-22f
+#define NPY_CODY_WAITE_PI_O_2_LOWf -0x1.846988p-48f
+#define NPY_COEFF_INVF0_COSINEf 0x1.000000p+00f
+#define NPY_COEFF_INVF2_COSINEf -0x1.000000p-01f
+#define NPY_COEFF_INVF4_COSINEf 0x1.55553cp-05f
+#define NPY_COEFF_INVF6_COSINEf -0x1.6c06dcp-10f
+#define NPY_COEFF_INVF8_COSINEf 0x1.98e616p-16f
+#define NPY_COEFF_INVF3_SINEf -0x1.555556p-03f
+#define NPY_COEFF_INVF5_SINEf 0x1.11119ap-07f
+#define NPY_COEFF_INVF7_SINEf -0x1.a06bbap-13f
+#define NPY_COEFF_INVF9_SINEf 0x1.7d3bbcp-19f
+/*
+ * Integer functions.
+ */
+NPY_INPLACE npy_uint npy_gcdu(npy_uint a, npy_uint b);
+NPY_INPLACE npy_uint npy_lcmu(npy_uint a, npy_uint b);
+NPY_INPLACE npy_ulong npy_gcdul(npy_ulong a, npy_ulong b);
+NPY_INPLACE npy_ulong npy_lcmul(npy_ulong a, npy_ulong b);
+NPY_INPLACE npy_ulonglong npy_gcdull(npy_ulonglong a, npy_ulonglong b);
+NPY_INPLACE npy_ulonglong npy_lcmull(npy_ulonglong a, npy_ulonglong b);
+
+NPY_INPLACE npy_int npy_gcd(npy_int a, npy_int b);
+NPY_INPLACE npy_int npy_lcm(npy_int a, npy_int b);
+NPY_INPLACE npy_long npy_gcdl(npy_long a, npy_long b);
+NPY_INPLACE npy_long npy_lcml(npy_long a, npy_long b);
+NPY_INPLACE npy_longlong npy_gcdll(npy_longlong a, npy_longlong b);
+NPY_INPLACE npy_longlong npy_lcmll(npy_longlong a, npy_longlong b);
+
+NPY_INPLACE npy_ubyte npy_rshiftuhh(npy_ubyte a, npy_ubyte b);
+NPY_INPLACE npy_ubyte npy_lshiftuhh(npy_ubyte a, npy_ubyte b);
+NPY_INPLACE npy_ushort npy_rshiftuh(npy_ushort a, npy_ushort b);
+NPY_INPLACE npy_ushort npy_lshiftuh(npy_ushort a, npy_ushort b);
+NPY_INPLACE npy_uint npy_rshiftu(npy_uint a, npy_uint b);
+NPY_INPLACE npy_uint npy_lshiftu(npy_uint a, npy_uint b);
+NPY_INPLACE npy_ulong npy_rshiftul(npy_ulong a, npy_ulong b);
+NPY_INPLACE npy_ulong npy_lshiftul(npy_ulong a, npy_ulong b);
+NPY_INPLACE npy_ulonglong npy_rshiftull(npy_ulonglong a, npy_ulonglong b);
+NPY_INPLACE npy_ulonglong npy_lshiftull(npy_ulonglong a, npy_ulonglong b);
+
+NPY_INPLACE npy_byte npy_rshifthh(npy_byte a, npy_byte b);
+NPY_INPLACE npy_byte npy_lshifthh(npy_byte a, npy_byte b);
+NPY_INPLACE npy_short npy_rshifth(npy_short a, npy_short b);
+NPY_INPLACE npy_short npy_lshifth(npy_short a, npy_short b);
+NPY_INPLACE npy_int npy_rshift(npy_int a, npy_int b);
+NPY_INPLACE npy_int npy_lshift(npy_int a, npy_int b);
+NPY_INPLACE npy_long npy_rshiftl(npy_long a, npy_long b);
+NPY_INPLACE npy_long npy_lshiftl(npy_long a, npy_long b);
+NPY_INPLACE npy_longlong npy_rshiftll(npy_longlong a, npy_longlong b);
+NPY_INPLACE npy_longlong npy_lshiftll(npy_longlong a, npy_longlong b);
+
+/*
+ * avx function has a common API for both sin & cos. This enum is used to
+ * distinguish between the two
+ */
+typedef enum {
+ npy_compute_sin,
+ npy_compute_cos
+} NPY_TRIG_OP;
+
+/*
* C99 double math funcs
*/
NPY_INPLACE double npy_sin(double x);
diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h
index 4b1b3d325..5ff4a0041 100644
--- a/numpy/core/include/numpy/ufuncobject.h
+++ b/numpy/core/include/numpy/ufuncobject.h
@@ -120,7 +120,11 @@ typedef struct _tagPyUFuncObject {
*/
int nin, nout, nargs;
- /* Identity for reduction, either PyUFunc_One or PyUFunc_Zero */
+ /*
+ * Identity for reduction, any of PyUFunc_One, PyUFunc_Zero
+ * PyUFunc_MinusOne, PyUFunc_None, PyUFunc_ReorderableNone,
+ * PyUFunc_IdentityValue.
+ */
int identity;
/* Array of one-dimensional core loops */
@@ -209,9 +213,33 @@ typedef struct _tagPyUFuncObject {
* set by nditer object.
*/
npy_uint32 iter_flags;
+
+ /* New in NPY_API_VERSION 0x0000000D and above */
+
+ /*
+ * for each core_num_dim_ix distinct dimension names,
+ * the possible "frozen" size (-1 if not frozen).
+ */
+ npy_intp *core_dim_sizes;
+
+ /*
+ * for each distinct core dimension, a set of UFUNC_CORE_DIM* flags
+ */
+ npy_uint32 *core_dim_flags;
+
+ /* Identity for reduction, when identity == PyUFunc_IdentityValue */
+ PyObject *identity_value;
+
} PyUFuncObject;
#include "arrayobject.h"
+/* Generalized ufunc; 0x0001 reserved for possible use as CORE_ENABLED */
+/* the core dimension's size will be determined by the operands. */
+#define UFUNC_CORE_DIM_SIZE_INFERRED 0x0002
+/* the core dimension may be absent */
+#define UFUNC_CORE_DIM_CAN_IGNORE 0x0004
+/* flags inferred during execution */
+#define UFUNC_CORE_DIM_MISSING 0x00040000
#define UFUNC_ERR_IGNORE 0
#define UFUNC_ERR_WARN 1
@@ -276,6 +304,12 @@ typedef struct _tagPyUFuncObject {
* This case allows reduction with multiple axes at once.
*/
#define PyUFunc_ReorderableNone -2
+/*
+ * UFunc unit is an identity_value, and the order of operations can be reordered
+ * This case allows reduction with multiple axes at once.
+ */
+#define PyUFunc_IdentityValue -3
+
#define UFUNC_REDUCE 0
#define UFUNC_ACCUMULATE 1
@@ -306,30 +340,6 @@ typedef struct _loop1d_info {
#define UFUNC_PYVALS_NAME "UFUNC_PYVALS"
-#define UFUNC_CHECK_ERROR(arg) \
- do {if ((((arg)->obj & UFUNC_OBJ_NEEDS_API) && PyErr_Occurred()) || \
- ((arg)->errormask && \
- PyUFunc_checkfperr((arg)->errormask, \
- (arg)->errobj, \
- &(arg)->first))) \
- goto fail;} while (0)
-
-
-/* keep in sync with ieee754.c.src */
-#if defined(sun) || defined(__BSD__) || defined(__OpenBSD__) || \
- (defined(__FreeBSD__) && (__FreeBSD_version < 502114)) || \
- defined(__NetBSD__) || \
- defined(__GLIBC__) || defined(__APPLE__) || \
- defined(__CYGWIN__) || defined(__MINGW32__) || \
- (defined(__FreeBSD__) && (__FreeBSD_version >= 502114)) || \
- defined(_AIX) || \
- defined(_MSC_VER) || \
- defined(__osf__) && defined(__alpha)
-#else
-#define NO_FLOATING_POINT_SUPPORT
-#endif
-
-
/*
* THESE MACROS ARE DEPRECATED.
* Use npy_set_floatstatus_* in the npymath library.
@@ -339,10 +349,6 @@ typedef struct _loop1d_info {
#define UFUNC_FPE_UNDERFLOW NPY_FPE_UNDERFLOW
#define UFUNC_FPE_INVALID NPY_FPE_INVALID
-#define UFUNC_CHECK_STATUS(ret) \
- { \
- ret = npy_clear_floatstatus(); \
- }
#define generate_divbyzero_error() npy_set_floatstatus_divbyzero()
#define generate_overflow_error() npy_set_floatstatus_overflow()
diff --git a/numpy/core/info.py b/numpy/core/info.py
deleted file mode 100644
index c6f7bbcf2..000000000
--- a/numpy/core/info.py
+++ /dev/null
@@ -1,87 +0,0 @@
-"""Defines a multi-dimensional array and useful procedures for Numerical computation.
-
-Functions
-
-- array - NumPy Array construction
-- zeros - Return an array of all zeros
-- empty - Return an uninitialized array
-- shape - Return shape of sequence or array
-- rank - Return number of dimensions
-- size - Return number of elements in entire array or a
- certain dimension
-- fromstring - Construct array from (byte) string
-- take - Select sub-arrays using sequence of indices
-- put - Set sub-arrays using sequence of 1-D indices
-- putmask - Set portion of arrays using a mask
-- reshape - Return array with new shape
-- repeat - Repeat elements of array
-- choose - Construct new array from indexed array tuple
-- correlate - Correlate two 1-d arrays
-- searchsorted - Search for element in 1-d array
-- sum - Total sum over a specified dimension
-- average - Average, possibly weighted, over axis or array.
-- cumsum - Cumulative sum over a specified dimension
-- product - Total product over a specified dimension
-- cumproduct - Cumulative product over a specified dimension
-- alltrue - Logical and over an entire axis
-- sometrue - Logical or over an entire axis
-- allclose - Tests if sequences are essentially equal
-
-More Functions:
-
-- arange - Return regularly spaced array
-- asarray - Guarantee NumPy array
-- convolve - Convolve two 1-d arrays
-- swapaxes - Exchange axes
-- concatenate - Join arrays together
-- transpose - Permute axes
-- sort - Sort elements of array
-- argsort - Indices of sorted array
-- argmax - Index of largest value
-- argmin - Index of smallest value
-- inner - Innerproduct of two arrays
-- dot - Dot product (matrix multiplication)
-- outer - Outerproduct of two arrays
-- resize - Return array with arbitrary new shape
-- indices - Tuple of indices
-- fromfunction - Construct array from universal function
-- diagonal - Return diagonal array
-- trace - Trace of array
-- dump - Dump array to file object (pickle)
-- dumps - Return pickled string representing data
-- load - Return array stored in file object
-- loads - Return array from pickled string
-- ravel - Return array as 1-D
-- nonzero - Indices of nonzero elements for 1-D array
-- shape - Shape of array
-- where - Construct array from binary result
-- compress - Elements of array where condition is true
-- clip - Clip array between two values
-- ones - Array of all ones
-- identity - 2-D identity array (matrix)
-
-(Universal) Math Functions
-
- add logical_or exp
- subtract logical_xor log
- multiply logical_not log10
- divide maximum sin
- divide_safe minimum sinh
- conjugate bitwise_and sqrt
- power bitwise_or tan
- absolute bitwise_xor tanh
- negative invert ceil
- greater left_shift fabs
- greater_equal right_shift floor
- less arccos arctan2
- less_equal arcsin fmod
- equal arctan hypot
- not_equal cos around
- logical_and cosh sign
- arccosh arcsinh arctanh
-
-"""
-from __future__ import division, absolute_import, print_function
-
-depends = ['testing']
-global_symbols = ['*']
diff --git a/numpy/core/machar.py b/numpy/core/machar.py
index 7578544fe..202580bdb 100644
--- a/numpy/core/machar.py
+++ b/numpy/core/machar.py
@@ -10,10 +10,12 @@ from __future__ import division, absolute_import, print_function
__all__ = ['MachAr']
from numpy.core.fromnumeric import any
-from numpy.core.numeric import errstate
+from numpy.core._ufunc_config import errstate
+from numpy.core.overrides import set_module
# Need to speed this up...especially for longfloat
+@set_module('numpy')
class MachAr(object):
"""
Diagnosing machine parameters.
diff --git a/numpy/core/memmap.py b/numpy/core/memmap.py
index 8269f537f..062645551 100644
--- a/numpy/core/memmap.py
+++ b/numpy/core/memmap.py
@@ -3,8 +3,9 @@ from __future__ import division, absolute_import, print_function
import numpy as np
from .numeric import uint8, ndarray, dtype
from numpy.compat import (
- long, basestring, is_pathlib_path, contextlib_nullcontext
+ long, basestring, os_fspath, contextlib_nullcontext, is_pathlib_path
)
+from numpy.core.overrides import set_module
__all__ = ['memmap']
@@ -19,6 +20,8 @@ mode_equivalents = {
"write":"w+"
}
+
+@set_module('numpy')
class memmap(ndarray):
"""Create a memory-map to an array stored in a *binary* file on disk.
@@ -132,9 +135,9 @@ class memmap(ndarray):
>>> fp = np.memmap(filename, dtype='float32', mode='w+', shape=(3,4))
>>> fp
- memmap([[ 0., 0., 0., 0.],
- [ 0., 0., 0., 0.],
- [ 0., 0., 0., 0.]], dtype=float32)
+ memmap([[0., 0., 0., 0.],
+ [0., 0., 0., 0.],
+ [0., 0., 0., 0.]], dtype=float32)
Write data to memmap array:
@@ -218,10 +221,8 @@ class memmap(ndarray):
if hasattr(filename, 'read'):
f_ctx = contextlib_nullcontext(filename)
- elif is_pathlib_path(filename):
- f_ctx = filename.open(('r' if mode == 'c' else mode)+'b')
else:
- f_ctx = open(filename, ('r' if mode == 'c' else mode)+'b')
+ f_ctx = open(os_fspath(filename), ('r' if mode == 'c' else mode)+'b')
with f_ctx as fid:
fid.seek(0, 2)
@@ -245,7 +246,7 @@ class memmap(ndarray):
bytes = long(offset + size*_dbytes)
- if mode == 'w+' or (mode == 'r+' and flen < bytes):
+ if mode in ('w+', 'r+') and flen < bytes:
fid.seek(bytes - 1, 0)
fid.write(b'\0')
fid.flush()
@@ -268,14 +269,13 @@ class memmap(ndarray):
self.offset = offset
self.mode = mode
- if isinstance(filename, basestring):
- self.filename = os.path.abspath(filename)
- elif is_pathlib_path(filename):
+ if is_pathlib_path(filename):
+ # special case - if we were constructed with a pathlib.path,
+ # then filename is a path object, not a string
self.filename = filename.resolve()
- # py3 returns int for TemporaryFile().name
- elif (hasattr(filename, "name") and
- isinstance(filename.name, basestring)):
- self.filename = os.path.abspath(filename.name)
+ elif hasattr(fid, "name") and isinstance(fid.name, basestring):
+ # py3 returns int for TemporaryFile().name
+ self.filename = os.path.abspath(fid.name)
# same as memmap copies (e.g. memmap + 1)
else:
self.filename = None
diff --git a/numpy/core/multiarray.py b/numpy/core/multiarray.py
index 673328397..c0fcc10ff 100644
--- a/numpy/core/multiarray.py
+++ b/numpy/core/multiarray.py
@@ -6,11 +6,18 @@ by importing from the extension module.
"""
+import functools
+import sys
+import warnings
+import sys
+
+from . import overrides
from . import _multiarray_umath
+import numpy as np
from numpy.core._multiarray_umath import *
from numpy.core._multiarray_umath import (
_fastCopyAndTranspose, _flagdict, _insert, _reconstruct, _vec_string,
- _ARRAY_API, _monotonicity
+ _ARRAY_API, _monotonicity, _get_ndarray_c_version
)
__all__ = [
@@ -25,13 +32,1600 @@ __all__ = [
'count_nonzero', 'c_einsum', 'datetime_as_string', 'datetime_data',
'digitize', 'dot', 'dragon4_positional', 'dragon4_scientific', 'dtype',
'empty', 'empty_like', 'error', 'flagsobj', 'flatiter', 'format_longfloat',
- 'frombuffer', 'fromfile', 'fromiter', 'fromstring', 'getbuffer', 'inner',
+ 'frombuffer', 'fromfile', 'fromiter', 'fromstring', 'inner',
'int_asbuffer', 'interp', 'interp_complex', 'is_busday', 'lexsort',
'matmul', 'may_share_memory', 'min_scalar_type', 'ndarray', 'nditer',
- 'nested_iters', 'newbuffer', 'normalize_axis_index', 'packbits',
+ 'nested_iters', 'normalize_axis_index', 'packbits',
'promote_types', 'putmask', 'ravel_multi_index', 'result_type', 'scalar',
'set_datetimeparse_function', 'set_legacy_print_mode', 'set_numeric_ops',
'set_string_function', 'set_typeDict', 'shares_memory', 'test_interrupt',
'tracemalloc_domain', 'typeinfo', 'unpackbits', 'unravel_index', 'vdot',
'where', 'zeros']
+if sys.version_info.major < 3:
+ __all__ += ['newbuffer', 'getbuffer']
+
+# For backward compatibility, make sure pickle imports these functions from here
+_reconstruct.__module__ = 'numpy.core.multiarray'
+scalar.__module__ = 'numpy.core.multiarray'
+
+
+arange.__module__ = 'numpy'
+array.__module__ = 'numpy'
+datetime_data.__module__ = 'numpy'
+empty.__module__ = 'numpy'
+frombuffer.__module__ = 'numpy'
+fromfile.__module__ = 'numpy'
+fromiter.__module__ = 'numpy'
+frompyfunc.__module__ = 'numpy'
+fromstring.__module__ = 'numpy'
+geterrobj.__module__ = 'numpy'
+may_share_memory.__module__ = 'numpy'
+nested_iters.__module__ = 'numpy'
+promote_types.__module__ = 'numpy'
+set_numeric_ops.__module__ = 'numpy'
+seterrobj.__module__ = 'numpy'
+zeros.__module__ = 'numpy'
+
+
+# We can't verify dispatcher signatures because NumPy's C functions don't
+# support introspection.
+array_function_from_c_func_and_dispatcher = functools.partial(
+ overrides.array_function_from_dispatcher,
+ module='numpy', docs_from_dispatcher=True, verify=False)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.empty_like)
+def empty_like(prototype, dtype=None, order=None, subok=None, shape=None):
+ """
+ empty_like(prototype, dtype=None, order='K', subok=True, shape=None)
+
+ Return a new array with the same shape and type as a given array.
+
+ Parameters
+ ----------
+ prototype : array_like
+ The shape and data-type of `prototype` define these same attributes
+ of the returned array.
+ dtype : data-type, optional
+ Overrides the data type of the result.
+
+ .. versionadded:: 1.6.0
+ order : {'C', 'F', 'A', or 'K'}, optional
+ Overrides the memory layout of the result. 'C' means C-order,
+ 'F' means F-order, 'A' means 'F' if ``prototype`` is Fortran
+ contiguous, 'C' otherwise. 'K' means match the layout of ``prototype``
+ as closely as possible.
+
+ .. versionadded:: 1.6.0
+ subok : bool, optional.
+ If True, then the newly created array will use the sub-class
+ type of 'a', otherwise it will be a base-class array. Defaults
+ to True.
+ shape : int or sequence of ints, optional.
+ Overrides the shape of the result. If order='K' and the number of
+ dimensions is unchanged, will try to keep order, otherwise,
+ order='C' is implied.
+
+ .. versionadded:: 1.17.0
+
+ Returns
+ -------
+ out : ndarray
+ Array of uninitialized (arbitrary) data with the same
+ shape and type as `prototype`.
+
+ See Also
+ --------
+ ones_like : Return an array of ones with shape and type of input.
+ zeros_like : Return an array of zeros with shape and type of input.
+ full_like : Return a new array with shape of input filled with value.
+ empty : Return a new uninitialized array.
+
+ Notes
+ -----
+ This function does *not* initialize the returned array; to do that use
+ `zeros_like` or `ones_like` instead. It may be marginally faster than
+ the functions that do set the array values.
+
+ Examples
+ --------
+ >>> a = ([1,2,3], [4,5,6]) # a is array-like
+ >>> np.empty_like(a)
+ array([[-1073741821, -1073741821, 3], # uninitialized
+ [ 0, 0, -1073741821]])
+ >>> a = np.array([[1., 2., 3.],[4.,5.,6.]])
+ >>> np.empty_like(a)
+ array([[ -2.00000715e+000, 1.48219694e-323, -2.00000572e+000], # uninitialized
+ [ 4.38791518e-305, -2.00000715e+000, 4.17269252e-309]])
+
+ """
+ return (prototype,)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.concatenate)
+def concatenate(arrays, axis=None, out=None):
+ """
+ concatenate((a1, a2, ...), axis=0, out=None)
+
+ Join a sequence of arrays along an existing axis.
+
+ Parameters
+ ----------
+ a1, a2, ... : sequence of array_like
+ The arrays must have the same shape, except in the dimension
+ corresponding to `axis` (the first, by default).
+ axis : int, optional
+ The axis along which the arrays will be joined. If axis is None,
+ arrays are flattened before use. Default is 0.
+ out : ndarray, optional
+ If provided, the destination to place the result. The shape must be
+ correct, matching that of what concatenate would have returned if no
+ out argument were specified.
+
+ Returns
+ -------
+ res : ndarray
+ The concatenated array.
+
+ See Also
+ --------
+ ma.concatenate : Concatenate function that preserves input masks.
+ array_split : Split an array into multiple sub-arrays of equal or
+ near-equal size.
+ split : Split array into a list of multiple sub-arrays of equal size.
+ hsplit : Split array into multiple sub-arrays horizontally (column wise)
+ vsplit : Split array into multiple sub-arrays vertically (row wise)
+ dsplit : Split array into multiple sub-arrays along the 3rd axis (depth).
+ stack : Stack a sequence of arrays along a new axis.
+ hstack : Stack arrays in sequence horizontally (column wise)
+ vstack : Stack arrays in sequence vertically (row wise)
+ dstack : Stack arrays in sequence depth wise (along third dimension)
+ block : Assemble arrays from blocks.
+
+ Notes
+ -----
+ When one or more of the arrays to be concatenated is a MaskedArray,
+ this function will return a MaskedArray object instead of an ndarray,
+ but the input masks are *not* preserved. In cases where a MaskedArray
+ is expected as input, use the ma.concatenate function from the masked
+ array module instead.
+
+ Examples
+ --------
+ >>> a = np.array([[1, 2], [3, 4]])
+ >>> b = np.array([[5, 6]])
+ >>> np.concatenate((a, b), axis=0)
+ array([[1, 2],
+ [3, 4],
+ [5, 6]])
+ >>> np.concatenate((a, b.T), axis=1)
+ array([[1, 2, 5],
+ [3, 4, 6]])
+ >>> np.concatenate((a, b), axis=None)
+ array([1, 2, 3, 4, 5, 6])
+
+ This function will not preserve masking of MaskedArray inputs.
+
+ >>> a = np.ma.arange(3)
+ >>> a[1] = np.ma.masked
+ >>> b = np.arange(2, 5)
+ >>> a
+ masked_array(data=[0, --, 2],
+ mask=[False, True, False],
+ fill_value=999999)
+ >>> b
+ array([2, 3, 4])
+ >>> np.concatenate([a, b])
+ masked_array(data=[0, 1, 2, 2, 3, 4],
+ mask=False,
+ fill_value=999999)
+ >>> np.ma.concatenate([a, b])
+ masked_array(data=[0, --, 2, 2, 3, 4],
+ mask=[False, True, False, False, False, False],
+ fill_value=999999)
+
+ """
+ if out is not None:
+ # optimize for the typical case where only arrays is provided
+ arrays = list(arrays)
+ arrays.append(out)
+ return arrays
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.inner)
+def inner(a, b):
+ """
+ inner(a, b)
+
+ Inner product of two arrays.
+
+ Ordinary inner product of vectors for 1-D arrays (without complex
+ conjugation), in higher dimensions a sum product over the last axes.
+
+ Parameters
+ ----------
+ a, b : array_like
+ If `a` and `b` are nonscalar, their last dimensions must match.
+
+ Returns
+ -------
+ out : ndarray
+ `out.shape = a.shape[:-1] + b.shape[:-1]`
+
+ Raises
+ ------
+ ValueError
+ If the last dimension of `a` and `b` has different size.
+
+ See Also
+ --------
+ tensordot : Sum products over arbitrary axes.
+ dot : Generalised matrix product, using second last dimension of `b`.
+ einsum : Einstein summation convention.
+
+ Notes
+ -----
+ For vectors (1-D arrays) it computes the ordinary inner-product::
+
+ np.inner(a, b) = sum(a[:]*b[:])
+
+ More generally, if `ndim(a) = r > 0` and `ndim(b) = s > 0`::
+
+ np.inner(a, b) = np.tensordot(a, b, axes=(-1,-1))
+
+ or explicitly::
+
+ np.inner(a, b)[i0,...,ir-1,j0,...,js-1]
+ = sum(a[i0,...,ir-1,:]*b[j0,...,js-1,:])
+
+ In addition `a` or `b` may be scalars, in which case::
+
+ np.inner(a,b) = a*b
+
+ Examples
+ --------
+ Ordinary inner product for vectors:
+
+ >>> a = np.array([1,2,3])
+ >>> b = np.array([0,1,0])
+ >>> np.inner(a, b)
+ 2
+
+ A multidimensional example:
+
+ >>> a = np.arange(24).reshape((2,3,4))
+ >>> b = np.arange(4)
+ >>> np.inner(a, b)
+ array([[ 14, 38, 62],
+ [ 86, 110, 134]])
+
+ An example where `b` is a scalar:
+
+ >>> np.inner(np.eye(2), 7)
+ array([[7., 0.],
+ [0., 7.]])
+
+ """
+ return (a, b)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.where)
+def where(condition, x=None, y=None):
+ """
+ where(condition, [x, y])
+
+ Return elements chosen from `x` or `y` depending on `condition`.
+
+ .. note::
+ When only `condition` is provided, this function is a shorthand for
+ ``np.asarray(condition).nonzero()``. Using `nonzero` directly should be
+ preferred, as it behaves correctly for subclasses. The rest of this
+ documentation covers only the case where all three arguments are
+ provided.
+
+ Parameters
+ ----------
+ condition : array_like, bool
+ Where True, yield `x`, otherwise yield `y`.
+ x, y : array_like
+ Values from which to choose. `x`, `y` and `condition` need to be
+ broadcastable to some shape.
+
+ Returns
+ -------
+ out : ndarray
+ An array with elements from `x` where `condition` is True, and elements
+ from `y` elsewhere.
+
+ See Also
+ --------
+ choose
+ nonzero : The function that is called when x and y are omitted
+
+ Notes
+ -----
+ If all the arrays are 1-D, `where` is equivalent to::
+
+ [xv if c else yv
+ for c, xv, yv in zip(condition, x, y)]
+
+ Examples
+ --------
+ >>> a = np.arange(10)
+ >>> a
+ array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+ >>> np.where(a < 5, a, 10*a)
+ array([ 0, 1, 2, 3, 4, 50, 60, 70, 80, 90])
+
+ This can be used on multidimensional arrays too:
+
+ >>> np.where([[True, False], [True, True]],
+ ... [[1, 2], [3, 4]],
+ ... [[9, 8], [7, 6]])
+ array([[1, 8],
+ [3, 4]])
+
+ The shapes of x, y, and the condition are broadcast together:
+
+ >>> x, y = np.ogrid[:3, :4]
+ >>> np.where(x < y, x, 10 + y) # both x and 10+y are broadcast
+ array([[10, 0, 0, 0],
+ [10, 11, 1, 1],
+ [10, 11, 12, 2]])
+
+ >>> a = np.array([[0, 1, 2],
+ ... [0, 2, 4],
+ ... [0, 3, 6]])
+ >>> np.where(a < 4, a, -1) # -1 is broadcast
+ array([[ 0, 1, 2],
+ [ 0, 2, -1],
+ [ 0, 3, -1]])
+ """
+ return (condition, x, y)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.lexsort)
+def lexsort(keys, axis=None):
+ """
+ lexsort(keys, axis=-1)
+
+ Perform an indirect stable sort using a sequence of keys.
+
+ Given multiple sorting keys, which can be interpreted as columns in a
+ spreadsheet, lexsort returns an array of integer indices that describes
+ the sort order by multiple columns. The last key in the sequence is used
+ for the primary sort order, the second-to-last key for the secondary sort
+ order, and so on. The keys argument must be a sequence of objects that
+ can be converted to arrays of the same shape. If a 2D array is provided
+ for the keys argument, it's rows are interpreted as the sorting keys and
+ sorting is according to the last row, second last row etc.
+
+ Parameters
+ ----------
+ keys : (k, N) array or tuple containing k (N,)-shaped sequences
+ The `k` different "columns" to be sorted. The last column (or row if
+ `keys` is a 2D array) is the primary sort key.
+ axis : int, optional
+ Axis to be indirectly sorted. By default, sort over the last axis.
+
+ Returns
+ -------
+ indices : (N,) ndarray of ints
+ Array of indices that sort the keys along the specified axis.
+
+ See Also
+ --------
+ argsort : Indirect sort.
+ ndarray.sort : In-place sort.
+ sort : Return a sorted copy of an array.
+
+ Examples
+ --------
+ Sort names: first by surname, then by name.
+
+ >>> surnames = ('Hertz', 'Galilei', 'Hertz')
+ >>> first_names = ('Heinrich', 'Galileo', 'Gustav')
+ >>> ind = np.lexsort((first_names, surnames))
+ >>> ind
+ array([1, 2, 0])
+
+ >>> [surnames[i] + ", " + first_names[i] for i in ind]
+ ['Galilei, Galileo', 'Hertz, Gustav', 'Hertz, Heinrich']
+
+ Sort two columns of numbers:
+
+ >>> a = [1,5,1,4,3,4,4] # First column
+ >>> b = [9,4,0,4,0,2,1] # Second column
+ >>> ind = np.lexsort((b,a)) # Sort by a, then by b
+ >>> ind
+ array([2, 0, 4, 6, 5, 3, 1])
+
+ >>> [(a[i],b[i]) for i in ind]
+ [(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)]
+
+ Note that sorting is first according to the elements of ``a``.
+ Secondary sorting is according to the elements of ``b``.
+
+ A normal ``argsort`` would have yielded:
+
+ >>> [(a[i],b[i]) for i in np.argsort(a)]
+ [(1, 9), (1, 0), (3, 0), (4, 4), (4, 2), (4, 1), (5, 4)]
+
+ Structured arrays are sorted lexically by ``argsort``:
+
+ >>> x = np.array([(1,9), (5,4), (1,0), (4,4), (3,0), (4,2), (4,1)],
+ ... dtype=np.dtype([('x', int), ('y', int)]))
+
+ >>> np.argsort(x) # or np.argsort(x, order=('x', 'y'))
+ array([2, 0, 4, 6, 5, 3, 1])
+
+ """
+ if isinstance(keys, tuple):
+ return keys
+ else:
+ return (keys,)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.can_cast)
+def can_cast(from_, to, casting=None):
+ """
+ can_cast(from_, to, casting='safe')
+
+ Returns True if cast between data types can occur according to the
+ casting rule. If from is a scalar or array scalar, also returns
+ True if the scalar value can be cast without overflow or truncation
+ to an integer.
+
+ Parameters
+ ----------
+ from_ : dtype, dtype specifier, scalar, or array
+ Data type, scalar, or array to cast from.
+ to : dtype or dtype specifier
+ Data type to cast to.
+ casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
+ Controls what kind of data casting may occur.
+
+ * 'no' means the data types should not be cast at all.
+ * 'equiv' means only byte-order changes are allowed.
+ * 'safe' means only casts which can preserve values are allowed.
+ * 'same_kind' means only safe casts or casts within a kind,
+ like float64 to float32, are allowed.
+ * 'unsafe' means any data conversions may be done.
+
+ Returns
+ -------
+ out : bool
+ True if cast can occur according to the casting rule.
+
+ Notes
+ -----
+ .. versionchanged:: 1.17.0
+ Casting between a simple data type and a structured one is possible only
+ for "unsafe" casting. Casting to multiple fields is allowed, but
+ casting from multiple fields is not.
+
+ .. versionchanged:: 1.9.0
+ Casting from numeric to string types in 'safe' casting mode requires
+ that the string dtype length is long enough to store the maximum
+ integer/float value converted.
+
+ See also
+ --------
+ dtype, result_type
+
+ Examples
+ --------
+ Basic examples
+
+ >>> np.can_cast(np.int32, np.int64)
+ True
+ >>> np.can_cast(np.float64, complex)
+ True
+ >>> np.can_cast(complex, float)
+ False
+
+ >>> np.can_cast('i8', 'f8')
+ True
+ >>> np.can_cast('i8', 'f4')
+ False
+ >>> np.can_cast('i4', 'S4')
+ False
+
+ Casting scalars
+
+ >>> np.can_cast(100, 'i1')
+ True
+ >>> np.can_cast(150, 'i1')
+ False
+ >>> np.can_cast(150, 'u1')
+ True
+
+ >>> np.can_cast(3.5e100, np.float32)
+ False
+ >>> np.can_cast(1000.0, np.float32)
+ True
+
+ Array scalar checks the value, array does not
+
+ >>> np.can_cast(np.array(1000.0), np.float32)
+ True
+ >>> np.can_cast(np.array([1000.0]), np.float32)
+ False
+
+ Using the casting rules
+
+ >>> np.can_cast('i8', 'i8', 'no')
+ True
+ >>> np.can_cast('<i8', '>i8', 'no')
+ False
+
+ >>> np.can_cast('<i8', '>i8', 'equiv')
+ True
+ >>> np.can_cast('<i4', '>i8', 'equiv')
+ False
+
+ >>> np.can_cast('<i4', '>i8', 'safe')
+ True
+ >>> np.can_cast('<i8', '>i4', 'safe')
+ False
+
+ >>> np.can_cast('<i8', '>i4', 'same_kind')
+ True
+ >>> np.can_cast('<i8', '>u4', 'same_kind')
+ False
+
+ >>> np.can_cast('<i8', '>u4', 'unsafe')
+ True
+
+ """
+ return (from_,)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.min_scalar_type)
+def min_scalar_type(a):
+ """
+ min_scalar_type(a)
+
+ For scalar ``a``, returns the data type with the smallest size
+ and smallest scalar kind which can hold its value. For non-scalar
+ array ``a``, returns the vector's dtype unmodified.
+
+ Floating point values are not demoted to integers,
+ and complex values are not demoted to floats.
+
+ Parameters
+ ----------
+ a : scalar or array_like
+ The value whose minimal data type is to be found.
+
+ Returns
+ -------
+ out : dtype
+ The minimal data type.
+
+ Notes
+ -----
+ .. versionadded:: 1.6.0
+
+ See Also
+ --------
+ result_type, promote_types, dtype, can_cast
+
+ Examples
+ --------
+ >>> np.min_scalar_type(10)
+ dtype('uint8')
+
+ >>> np.min_scalar_type(-260)
+ dtype('int16')
+
+ >>> np.min_scalar_type(3.1)
+ dtype('float16')
+
+ >>> np.min_scalar_type(1e50)
+ dtype('float64')
+
+ >>> np.min_scalar_type(np.arange(4,dtype='f8'))
+ dtype('float64')
+
+ """
+ return (a,)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.result_type)
+def result_type(*arrays_and_dtypes):
+ """
+ result_type(*arrays_and_dtypes)
+
+ Returns the type that results from applying the NumPy
+ type promotion rules to the arguments.
+
+ Type promotion in NumPy works similarly to the rules in languages
+ like C++, with some slight differences. When both scalars and
+ arrays are used, the array's type takes precedence and the actual value
+ of the scalar is taken into account.
+
+ For example, calculating 3*a, where a is an array of 32-bit floats,
+ intuitively should result in a 32-bit float output. If the 3 is a
+ 32-bit integer, the NumPy rules indicate it can't convert losslessly
+ into a 32-bit float, so a 64-bit float should be the result type.
+ By examining the value of the constant, '3', we see that it fits in
+ an 8-bit integer, which can be cast losslessly into the 32-bit float.
+
+ Parameters
+ ----------
+ arrays_and_dtypes : list of arrays and dtypes
+ The operands of some operation whose result type is needed.
+
+ Returns
+ -------
+ out : dtype
+ The result type.
+
+ See also
+ --------
+ dtype, promote_types, min_scalar_type, can_cast
+
+ Notes
+ -----
+ .. versionadded:: 1.6.0
+
+ The specific algorithm used is as follows.
+
+ Categories are determined by first checking which of boolean,
+ integer (int/uint), or floating point (float/complex) the maximum
+ kind of all the arrays and the scalars are.
+
+ If there are only scalars or the maximum category of the scalars
+ is higher than the maximum category of the arrays,
+ the data types are combined with :func:`promote_types`
+ to produce the return value.
+
+ Otherwise, `min_scalar_type` is called on each array, and
+ the resulting data types are all combined with :func:`promote_types`
+ to produce the return value.
+
+ The set of int values is not a subset of the uint values for types
+ with the same number of bits, something not reflected in
+ :func:`min_scalar_type`, but handled as a special case in `result_type`.
+
+ Examples
+ --------
+ >>> np.result_type(3, np.arange(7, dtype='i1'))
+ dtype('int8')
+
+ >>> np.result_type('i4', 'c8')
+ dtype('complex128')
+
+ >>> np.result_type(3.0, -2)
+ dtype('float64')
+
+ """
+ return arrays_and_dtypes
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.dot)
+def dot(a, b, out=None):
+ """
+ dot(a, b, out=None)
+
+ Dot product of two arrays. Specifically,
+
+ - If both `a` and `b` are 1-D arrays, it is inner product of vectors
+ (without complex conjugation).
+
+ - If both `a` and `b` are 2-D arrays, it is matrix multiplication,
+ but using :func:`matmul` or ``a @ b`` is preferred.
+
+ - If either `a` or `b` is 0-D (scalar), it is equivalent to :func:`multiply`
+ and using ``numpy.multiply(a, b)`` or ``a * b`` is preferred.
+
+ - If `a` is an N-D array and `b` is a 1-D array, it is a sum product over
+ the last axis of `a` and `b`.
+
+ - If `a` is an N-D array and `b` is an M-D array (where ``M>=2``), it is a
+ sum product over the last axis of `a` and the second-to-last axis of `b`::
+
+ dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
+
+ Parameters
+ ----------
+ a : array_like
+ First argument.
+ b : array_like
+ Second argument.
+ out : ndarray, optional
+ Output argument. This must have the exact kind that would be returned
+ if it was not used. In particular, it must have the right type, must be
+ C-contiguous, and its dtype must be the dtype that would be returned
+ for `dot(a,b)`. This is a performance feature. Therefore, if these
+ conditions are not met, an exception is raised, instead of attempting
+ to be flexible.
+
+ Returns
+ -------
+ output : ndarray
+ Returns the dot product of `a` and `b`. If `a` and `b` are both
+ scalars or both 1-D arrays then a scalar is returned; otherwise
+ an array is returned.
+ If `out` is given, then it is returned.
+
+ Raises
+ ------
+ ValueError
+ If the last dimension of `a` is not the same size as
+ the second-to-last dimension of `b`.
+
+ See Also
+ --------
+ vdot : Complex-conjugating dot product.
+ tensordot : Sum products over arbitrary axes.
+ einsum : Einstein summation convention.
+ matmul : '@' operator as method with out parameter.
+
+ Examples
+ --------
+ >>> np.dot(3, 4)
+ 12
+
+ Neither argument is complex-conjugated:
+
+ >>> np.dot([2j, 3j], [2j, 3j])
+ (-13+0j)
+
+ For 2-D arrays it is the matrix product:
+
+ >>> a = [[1, 0], [0, 1]]
+ >>> b = [[4, 1], [2, 2]]
+ >>> np.dot(a, b)
+ array([[4, 1],
+ [2, 2]])
+
+ >>> a = np.arange(3*4*5*6).reshape((3,4,5,6))
+ >>> b = np.arange(3*4*5*6)[::-1].reshape((5,4,6,3))
+ >>> np.dot(a, b)[2,3,2,1,2,2]
+ 499128
+ >>> sum(a[2,3,2,:] * b[1,2,:,2])
+ 499128
+
+ """
+ return (a, b, out)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.vdot)
+def vdot(a, b):
+ """
+ vdot(a, b)
+
+ Return the dot product of two vectors.
+
+ The vdot(`a`, `b`) function handles complex numbers differently than
+ dot(`a`, `b`). If the first argument is complex the complex conjugate
+ of the first argument is used for the calculation of the dot product.
+
+ Note that `vdot` handles multidimensional arrays differently than `dot`:
+ it does *not* perform a matrix product, but flattens input arguments
+ to 1-D vectors first. Consequently, it should only be used for vectors.
+
+ Parameters
+ ----------
+ a : array_like
+ If `a` is complex the complex conjugate is taken before calculation
+ of the dot product.
+ b : array_like
+ Second argument to the dot product.
+
+ Returns
+ -------
+ output : ndarray
+ Dot product of `a` and `b`. Can be an int, float, or
+ complex depending on the types of `a` and `b`.
+
+ See Also
+ --------
+ dot : Return the dot product without using the complex conjugate of the
+ first argument.
+
+ Examples
+ --------
+ >>> a = np.array([1+2j,3+4j])
+ >>> b = np.array([5+6j,7+8j])
+ >>> np.vdot(a, b)
+ (70-8j)
+ >>> np.vdot(b, a)
+ (70+8j)
+
+ Note that higher-dimensional arrays are flattened!
+
+ >>> a = np.array([[1, 4], [5, 6]])
+ >>> b = np.array([[4, 1], [2, 2]])
+ >>> np.vdot(a, b)
+ 30
+ >>> np.vdot(b, a)
+ 30
+ >>> 1*4 + 4*1 + 5*2 + 6*2
+ 30
+
+ """
+ return (a, b)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.bincount)
+def bincount(x, weights=None, minlength=None):
+ """
+ bincount(x, weights=None, minlength=0)
+
+ Count number of occurrences of each value in array of non-negative ints.
+
+ The number of bins (of size 1) is one larger than the largest value in
+ `x`. If `minlength` is specified, there will be at least this number
+ of bins in the output array (though it will be longer if necessary,
+ depending on the contents of `x`).
+ Each bin gives the number of occurrences of its index value in `x`.
+ If `weights` is specified the input array is weighted by it, i.e. if a
+ value ``n`` is found at position ``i``, ``out[n] += weight[i]`` instead
+ of ``out[n] += 1``.
+
+ Parameters
+ ----------
+ x : array_like, 1 dimension, nonnegative ints
+ Input array.
+ weights : array_like, optional
+ Weights, array of the same shape as `x`.
+ minlength : int, optional
+ A minimum number of bins for the output array.
+
+ .. versionadded:: 1.6.0
+
+ Returns
+ -------
+ out : ndarray of ints
+ The result of binning the input array.
+ The length of `out` is equal to ``np.amax(x)+1``.
+
+ Raises
+ ------
+ ValueError
+ If the input is not 1-dimensional, or contains elements with negative
+ values, or if `minlength` is negative.
+ TypeError
+ If the type of the input is float or complex.
+
+ See Also
+ --------
+ histogram, digitize, unique
+
+ Examples
+ --------
+ >>> np.bincount(np.arange(5))
+ array([1, 1, 1, 1, 1])
+ >>> np.bincount(np.array([0, 1, 1, 3, 2, 1, 7]))
+ array([1, 3, 1, 1, 0, 0, 0, 1])
+
+ >>> x = np.array([0, 1, 1, 3, 2, 1, 7, 23])
+ >>> np.bincount(x).size == np.amax(x)+1
+ True
+
+ The input array needs to be of integer dtype, otherwise a
+ TypeError is raised:
+
+ >>> np.bincount(np.arange(5, dtype=float))
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ TypeError: array cannot be safely cast to required type
+
+ A possible use of ``bincount`` is to perform sums over
+ variable-size chunks of an array, using the ``weights`` keyword.
+
+ >>> w = np.array([0.3, 0.5, 0.2, 0.7, 1., -0.6]) # weights
+ >>> x = np.array([0, 1, 1, 2, 2, 2])
+ >>> np.bincount(x, weights=w)
+ array([ 0.3, 0.7, 1.1])
+
+ """
+ return (x, weights)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.ravel_multi_index)
+def ravel_multi_index(multi_index, dims, mode=None, order=None):
+ """
+ ravel_multi_index(multi_index, dims, mode='raise', order='C')
+
+ Converts a tuple of index arrays into an array of flat
+ indices, applying boundary modes to the multi-index.
+
+ Parameters
+ ----------
+ multi_index : tuple of array_like
+ A tuple of integer arrays, one array for each dimension.
+ dims : tuple of ints
+ The shape of array into which the indices from ``multi_index`` apply.
+ mode : {'raise', 'wrap', 'clip'}, optional
+ Specifies how out-of-bounds indices are handled. Can specify
+ either one mode or a tuple of modes, one mode per index.
+
+ * 'raise' -- raise an error (default)
+ * 'wrap' -- wrap around
+ * 'clip' -- clip to the range
+
+ In 'clip' mode, a negative index which would normally
+ wrap will clip to 0 instead.
+ order : {'C', 'F'}, optional
+ Determines whether the multi-index should be viewed as
+ indexing in row-major (C-style) or column-major
+ (Fortran-style) order.
+
+ Returns
+ -------
+ raveled_indices : ndarray
+ An array of indices into the flattened version of an array
+ of dimensions ``dims``.
+
+ See Also
+ --------
+ unravel_index
+
+ Notes
+ -----
+ .. versionadded:: 1.6.0
+
+ Examples
+ --------
+ >>> arr = np.array([[3,6,6],[4,5,1]])
+ >>> np.ravel_multi_index(arr, (7,6))
+ array([22, 41, 37])
+ >>> np.ravel_multi_index(arr, (7,6), order='F')
+ array([31, 41, 13])
+ >>> np.ravel_multi_index(arr, (4,6), mode='clip')
+ array([22, 23, 19])
+ >>> np.ravel_multi_index(arr, (4,4), mode=('clip','wrap'))
+ array([12, 13, 13])
+
+ >>> np.ravel_multi_index((3,1,4,1), (6,7,8,9))
+ 1621
+ """
+ return multi_index
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.unravel_index)
+def unravel_index(indices, shape=None, order=None, dims=None):
+ """
+ unravel_index(indices, shape, order='C')
+
+ Converts a flat index or array of flat indices into a tuple
+ of coordinate arrays.
+
+ Parameters
+ ----------
+ indices : array_like
+ An integer array whose elements are indices into the flattened
+ version of an array of dimensions ``shape``. Before version 1.6.0,
+ this function accepted just one index value.
+ shape : tuple of ints
+ The shape of the array to use for unraveling ``indices``.
+
+ .. versionchanged:: 1.16.0
+ Renamed from ``dims`` to ``shape``.
+
+ order : {'C', 'F'}, optional
+ Determines whether the indices should be viewed as indexing in
+ row-major (C-style) or column-major (Fortran-style) order.
+
+ .. versionadded:: 1.6.0
+
+ Returns
+ -------
+ unraveled_coords : tuple of ndarray
+ Each array in the tuple has the same shape as the ``indices``
+ array.
+
+ See Also
+ --------
+ ravel_multi_index
+
+ Examples
+ --------
+ >>> np.unravel_index([22, 41, 37], (7,6))
+ (array([3, 6, 6]), array([4, 5, 1]))
+ >>> np.unravel_index([31, 41, 13], (7,6), order='F')
+ (array([3, 6, 6]), array([4, 5, 1]))
+
+ >>> np.unravel_index(1621, (6,7,8,9))
+ (3, 1, 4, 1)
+
+ """
+ if dims is not None:
+ warnings.warn("'shape' argument should be used instead of 'dims'",
+ DeprecationWarning, stacklevel=3)
+ return (indices,)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.copyto)
+def copyto(dst, src, casting=None, where=None):
+ """
+ copyto(dst, src, casting='same_kind', where=True)
+
+ Copies values from one array to another, broadcasting as necessary.
+
+ Raises a TypeError if the `casting` rule is violated, and if
+ `where` is provided, it selects which elements to copy.
+
+ .. versionadded:: 1.7.0
+
+ Parameters
+ ----------
+ dst : ndarray
+ The array into which values are copied.
+ src : array_like
+ The array from which values are copied.
+ casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
+ Controls what kind of data casting may occur when copying.
+
+ * 'no' means the data types should not be cast at all.
+ * 'equiv' means only byte-order changes are allowed.
+ * 'safe' means only casts which can preserve values are allowed.
+ * 'same_kind' means only safe casts or casts within a kind,
+ like float64 to float32, are allowed.
+ * 'unsafe' means any data conversions may be done.
+ where : array_like of bool, optional
+ A boolean array which is broadcasted to match the dimensions
+ of `dst`, and selects elements to copy from `src` to `dst`
+ wherever it contains the value True.
+ """
+ return (dst, src, where)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.putmask)
+def putmask(a, mask, values):
+ """
+ putmask(a, mask, values)
+
+ Changes elements of an array based on conditional and input values.
+
+ Sets ``a.flat[n] = values[n]`` for each n where ``mask.flat[n]==True``.
+
+ If `values` is not the same size as `a` and `mask` then it will repeat.
+ This gives behavior different from ``a[mask] = values``.
+
+ Parameters
+ ----------
+ a : array_like
+ Target array.
+ mask : array_like
+ Boolean mask array. It has to be the same shape as `a`.
+ values : array_like
+ Values to put into `a` where `mask` is True. If `values` is smaller
+ than `a` it will be repeated.
+
+ See Also
+ --------
+ place, put, take, copyto
+
+ Examples
+ --------
+ >>> x = np.arange(6).reshape(2, 3)
+ >>> np.putmask(x, x>2, x**2)
+ >>> x
+ array([[ 0, 1, 2],
+ [ 9, 16, 25]])
+
+ If `values` is smaller than `a` it is repeated:
+
+ >>> x = np.arange(5)
+ >>> np.putmask(x, x>1, [-33, -44])
+ >>> x
+ array([ 0, 1, -33, -44, -33])
+
+ """
+ return (a, mask, values)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.packbits)
+def packbits(a, axis=None, bitorder='big'):
+ """
+ packbits(a, axis=None, bitorder='big')
+
+ Packs the elements of a binary-valued array into bits in a uint8 array.
+
+ The result is padded to full bytes by inserting zero bits at the end.
+
+ Parameters
+ ----------
+ a : array_like
+ An array of integers or booleans whose elements should be packed to
+ bits.
+ axis : int, optional
+ The dimension over which bit-packing is done.
+ ``None`` implies packing the flattened array.
+ bitorder : {'big', 'little'}, optional
+ The order of the input bits. 'big' will mimic bin(val),
+ ``[0, 0, 0, 0, 0, 0, 1, 1] => 3 = 0b00000011 => ``, 'little' will
+ reverse the order so ``[1, 1, 0, 0, 0, 0, 0, 0] => 3``.
+ Defaults to 'big'.
+
+ .. versionadded:: 1.17.0
+
+ Returns
+ -------
+ packed : ndarray
+ Array of type uint8 whose elements represent bits corresponding to the
+ logical (0 or nonzero) value of the input elements. The shape of
+ `packed` has the same number of dimensions as the input (unless `axis`
+ is None, in which case the output is 1-D).
+
+ See Also
+ --------
+ unpackbits: Unpacks elements of a uint8 array into a binary-valued output
+ array.
+
+ Examples
+ --------
+ >>> a = np.array([[[1,0,1],
+ ... [0,1,0]],
+ ... [[1,1,0],
+ ... [0,0,1]]])
+ >>> b = np.packbits(a, axis=-1)
+ >>> b
+ array([[[160],
+ [ 64]],
+ [[192],
+ [ 32]]], dtype=uint8)
+
+ Note that in binary 160 = 1010 0000, 64 = 0100 0000, 192 = 1100 0000,
+ and 32 = 0010 0000.
+
+ """
+ return (a,)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.unpackbits)
+def unpackbits(a, axis=None, count=None, bitorder='big'):
+ """
+ unpackbits(a, axis=None, count=None, bitorder='big')
+
+ Unpacks elements of a uint8 array into a binary-valued output array.
+
+ Each element of `a` represents a bit-field that should be unpacked
+ into a binary-valued output array. The shape of the output array is
+ either 1-D (if `axis` is ``None``) or the same shape as the input
+ array with unpacking done along the axis specified.
+
+ Parameters
+ ----------
+ a : ndarray, uint8 type
+ Input array.
+ axis : int, optional
+ The dimension over which bit-unpacking is done.
+ ``None`` implies unpacking the flattened array.
+ count : int or None, optional
+ The number of elements to unpack along `axis`, provided as a way
+ of undoing the effect of packing a size that is not a multiple
+ of eight. A non-negative number means to only unpack `count`
+ bits. A negative number means to trim off that many bits from
+ the end. ``None`` means to unpack the entire array (the
+ default). Counts larger than the available number of bits will
+ add zero padding to the output. Negative counts must not
+ exceed the available number of bits.
+
+ .. versionadded:: 1.17.0
+
+ bitorder : {'big', 'little'}, optional
+ The order of the returned bits. 'big' will mimic bin(val),
+ ``3 = 0b00000011 => [0, 0, 0, 0, 0, 0, 1, 1]``, 'little' will reverse
+ the order to ``[1, 1, 0, 0, 0, 0, 0, 0]``.
+ Defaults to 'big'.
+
+ .. versionadded:: 1.17.0
+
+ Returns
+ -------
+ unpacked : ndarray, uint8 type
+ The elements are binary-valued (0 or 1).
+
+ See Also
+ --------
+ packbits : Packs the elements of a binary-valued array into bits in
+ a uint8 array.
+
+ Examples
+ --------
+ >>> a = np.array([[2], [7], [23]], dtype=np.uint8)
+ >>> a
+ array([[ 2],
+ [ 7],
+ [23]], dtype=uint8)
+ >>> b = np.unpackbits(a, axis=1)
+ >>> b
+ array([[0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 0, 1, 1, 1],
+ [0, 0, 0, 1, 0, 1, 1, 1]], dtype=uint8)
+ >>> c = np.unpackbits(a, axis=1, count=-3)
+ >>> c
+ array([[0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 1, 0]], dtype=uint8)
+
+ >>> p = np.packbits(b, axis=0)
+ >>> np.unpackbits(p, axis=0)
+ array([[0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 0, 1, 1, 1],
+ [0, 0, 0, 1, 0, 1, 1, 1],
+ [0, 0, 0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
+ >>> np.array_equal(b, np.unpackbits(p, axis=0, count=b.shape[0]))
+ True
+
+ """
+ return (a,)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.shares_memory)
+def shares_memory(a, b, max_work=None):
+ """
+ shares_memory(a, b, max_work=None)
+
+ Determine if two arrays share memory
+
+ Parameters
+ ----------
+ a, b : ndarray
+ Input arrays
+ max_work : int, optional
+ Effort to spend on solving the overlap problem (maximum number
+ of candidate solutions to consider). The following special
+ values are recognized:
+
+ max_work=MAY_SHARE_EXACT (default)
+ The problem is solved exactly. In this case, the function returns
+ True only if there is an element shared between the arrays.
+ max_work=MAY_SHARE_BOUNDS
+ Only the memory bounds of a and b are checked.
+
+ Raises
+ ------
+ numpy.TooHardError
+ Exceeded max_work.
+
+ Returns
+ -------
+ out : bool
+
+ See Also
+ --------
+ may_share_memory
+
+ Examples
+ --------
+ >>> np.may_share_memory(np.array([1,2]), np.array([5,8,9]))
+ False
+
+ """
+ return (a, b)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.may_share_memory)
+def may_share_memory(a, b, max_work=None):
+ """
+ may_share_memory(a, b, max_work=None)
+
+ Determine if two arrays might share memory
+
+ A return of True does not necessarily mean that the two arrays
+ share any element. It just means that they *might*.
+
+ Only the memory bounds of a and b are checked by default.
+
+ Parameters
+ ----------
+ a, b : ndarray
+ Input arrays
+ max_work : int, optional
+ Effort to spend on solving the overlap problem. See
+ `shares_memory` for details. Default for ``may_share_memory``
+ is to do a bounds check.
+
+ Returns
+ -------
+ out : bool
+
+ See Also
+ --------
+ shares_memory
+
+ Examples
+ --------
+ >>> np.may_share_memory(np.array([1,2]), np.array([5,8,9]))
+ False
+ >>> x = np.zeros([3, 4])
+ >>> np.may_share_memory(x[:,0], x[:,1])
+ True
+
+ """
+ return (a, b)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.is_busday)
+def is_busday(dates, weekmask=None, holidays=None, busdaycal=None, out=None):
+ """
+ is_busday(dates, weekmask='1111100', holidays=None, busdaycal=None, out=None)
+
+ Calculates which of the given dates are valid days, and which are not.
+
+ .. versionadded:: 1.7.0
+
+ Parameters
+ ----------
+ dates : array_like of datetime64[D]
+ The array of dates to process.
+ weekmask : str or array_like of bool, optional
+ A seven-element array indicating which of Monday through Sunday are
+ valid days. May be specified as a length-seven list or array, like
+ [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
+ like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
+ weekdays, optionally separated by white space. Valid abbreviations
+ are: Mon Tue Wed Thu Fri Sat Sun
+ holidays : array_like of datetime64[D], optional
+ An array of dates to consider as invalid dates. They may be
+ specified in any order, and NaT (not-a-time) dates are ignored.
+ This list is saved in a normalized form that is suited for
+ fast calculations of valid days.
+ busdaycal : busdaycalendar, optional
+ A `busdaycalendar` object which specifies the valid days. If this
+ parameter is provided, neither weekmask nor holidays may be
+ provided.
+ out : array of bool, optional
+ If provided, this array is filled with the result.
+
+ Returns
+ -------
+ out : array of bool
+ An array with the same shape as ``dates``, containing True for
+ each valid day, and False for each invalid day.
+
+ See Also
+ --------
+ busdaycalendar: An object that specifies a custom set of valid days.
+ busday_offset : Applies an offset counted in valid days.
+ busday_count : Counts how many valid days are in a half-open date range.
+
+ Examples
+ --------
+ >>> # The weekdays are Friday, Saturday, and Monday
+ ... np.is_busday(['2011-07-01', '2011-07-02', '2011-07-18'],
+ ... holidays=['2011-07-01', '2011-07-04', '2011-07-17'])
+ array([False, False, True])
+ """
+ return (dates, weekmask, holidays, out)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.busday_offset)
+def busday_offset(dates, offsets, roll=None, weekmask=None, holidays=None,
+ busdaycal=None, out=None):
+ """
+ busday_offset(dates, offsets, roll='raise', weekmask='1111100', holidays=None, busdaycal=None, out=None)
+
+ First adjusts the date to fall on a valid day according to
+ the ``roll`` rule, then applies offsets to the given dates
+ counted in valid days.
+
+ .. versionadded:: 1.7.0
+
+ Parameters
+ ----------
+ dates : array_like of datetime64[D]
+ The array of dates to process.
+ offsets : array_like of int
+ The array of offsets, which is broadcast with ``dates``.
+ roll : {'raise', 'nat', 'forward', 'following', 'backward', 'preceding', 'modifiedfollowing', 'modifiedpreceding'}, optional
+ How to treat dates that do not fall on a valid day. The default
+ is 'raise'.
+
+ * 'raise' means to raise an exception for an invalid day.
+ * 'nat' means to return a NaT (not-a-time) for an invalid day.
+ * 'forward' and 'following' mean to take the first valid day
+ later in time.
+ * 'backward' and 'preceding' mean to take the first valid day
+ earlier in time.
+ * 'modifiedfollowing' means to take the first valid day
+ later in time unless it is across a Month boundary, in which
+ case to take the first valid day earlier in time.
+ * 'modifiedpreceding' means to take the first valid day
+ earlier in time unless it is across a Month boundary, in which
+ case to take the first valid day later in time.
+ weekmask : str or array_like of bool, optional
+ A seven-element array indicating which of Monday through Sunday are
+ valid days. May be specified as a length-seven list or array, like
+ [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
+ like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
+ weekdays, optionally separated by white space. Valid abbreviations
+ are: Mon Tue Wed Thu Fri Sat Sun
+ holidays : array_like of datetime64[D], optional
+ An array of dates to consider as invalid dates. They may be
+ specified in any order, and NaT (not-a-time) dates are ignored.
+ This list is saved in a normalized form that is suited for
+ fast calculations of valid days.
+ busdaycal : busdaycalendar, optional
+ A `busdaycalendar` object which specifies the valid days. If this
+ parameter is provided, neither weekmask nor holidays may be
+ provided.
+ out : array of datetime64[D], optional
+ If provided, this array is filled with the result.
+
+ Returns
+ -------
+ out : array of datetime64[D]
+ An array with a shape from broadcasting ``dates`` and ``offsets``
+ together, containing the dates with offsets applied.
+
+ See Also
+ --------
+ busdaycalendar: An object that specifies a custom set of valid days.
+ is_busday : Returns a boolean array indicating valid days.
+ busday_count : Counts how many valid days are in a half-open date range.
+
+ Examples
+ --------
+ >>> # First business day in October 2011 (not accounting for holidays)
+ ... np.busday_offset('2011-10', 0, roll='forward')
+ numpy.datetime64('2011-10-03')
+ >>> # Last business day in February 2012 (not accounting for holidays)
+ ... np.busday_offset('2012-03', -1, roll='forward')
+ numpy.datetime64('2012-02-29')
+ >>> # Third Wednesday in January 2011
+ ... np.busday_offset('2011-01', 2, roll='forward', weekmask='Wed')
+ numpy.datetime64('2011-01-19')
+ >>> # 2012 Mother's Day in Canada and the U.S.
+ ... np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun')
+ numpy.datetime64('2012-05-13')
+
+ >>> # First business day on or after a date
+ ... np.busday_offset('2011-03-20', 0, roll='forward')
+ numpy.datetime64('2011-03-21')
+ >>> np.busday_offset('2011-03-22', 0, roll='forward')
+ numpy.datetime64('2011-03-22')
+ >>> # First business day after a date
+ ... np.busday_offset('2011-03-20', 1, roll='backward')
+ numpy.datetime64('2011-03-21')
+ >>> np.busday_offset('2011-03-22', 1, roll='backward')
+ numpy.datetime64('2011-03-23')
+ """
+ return (dates, offsets, weekmask, holidays, out)
+
+
+@array_function_from_c_func_and_dispatcher(_multiarray_umath.busday_count)
+def busday_count(begindates, enddates, weekmask=None, holidays=None,
+ busdaycal=None, out=None):
+ """
+ busday_count(begindates, enddates, weekmask='1111100', holidays=[], busdaycal=None, out=None)
+
+ Counts the number of valid days between `begindates` and
+ `enddates`, not including the day of `enddates`.
+
+ If ``enddates`` specifies a date value that is earlier than the
+ corresponding ``begindates`` date value, the count will be negative.
+
+ .. versionadded:: 1.7.0
+
+ Parameters
+ ----------
+ begindates : array_like of datetime64[D]
+ The array of the first dates for counting.
+ enddates : array_like of datetime64[D]
+ The array of the end dates for counting, which are excluded
+ from the count themselves.
+ weekmask : str or array_like of bool, optional
+ A seven-element array indicating which of Monday through Sunday are
+ valid days. May be specified as a length-seven list or array, like
+ [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
+ like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
+ weekdays, optionally separated by white space. Valid abbreviations
+ are: Mon Tue Wed Thu Fri Sat Sun
+ holidays : array_like of datetime64[D], optional
+ An array of dates to consider as invalid dates. They may be
+ specified in any order, and NaT (not-a-time) dates are ignored.
+ This list is saved in a normalized form that is suited for
+ fast calculations of valid days.
+ busdaycal : busdaycalendar, optional
+ A `busdaycalendar` object which specifies the valid days. If this
+ parameter is provided, neither weekmask nor holidays may be
+ provided.
+ out : array of int, optional
+ If provided, this array is filled with the result.
+
+ Returns
+ -------
+ out : array of int
+ An array with a shape from broadcasting ``begindates`` and ``enddates``
+ together, containing the number of valid days between
+ the begin and end dates.
+
+ See Also
+ --------
+ busdaycalendar: An object that specifies a custom set of valid days.
+ is_busday : Returns a boolean array indicating valid days.
+ busday_offset : Applies an offset counted in valid days.
+
+ Examples
+ --------
+ >>> # Number of weekdays in January 2011
+ ... np.busday_count('2011-01', '2011-02')
+ 21
+ >>> # Number of weekdays in 2011
+ >>> np.busday_count('2011', '2012')
+ 260
+ >>> # Number of Saturdays in 2011
+ ... np.busday_count('2011', '2012', weekmask='Sat')
+ 53
+ """
+ return (begindates, enddates, weekmask, holidays, out)
+
+
+@array_function_from_c_func_and_dispatcher(
+ _multiarray_umath.datetime_as_string)
+def datetime_as_string(arr, unit=None, timezone=None, casting=None):
+ """
+ datetime_as_string(arr, unit=None, timezone='naive', casting='same_kind')
+
+ Convert an array of datetimes into an array of strings.
+
+ Parameters
+ ----------
+ arr : array_like of datetime64
+ The array of UTC timestamps to format.
+ unit : str
+ One of None, 'auto', or a :ref:`datetime unit <arrays.dtypes.dateunits>`.
+ timezone : {'naive', 'UTC', 'local'} or tzinfo
+ Timezone information to use when displaying the datetime. If 'UTC', end
+ with a Z to indicate UTC time. If 'local', convert to the local timezone
+ first, and suffix with a +-#### timezone offset. If a tzinfo object,
+ then do as with 'local', but use the specified timezone.
+ casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}
+ Casting to allow when changing between datetime units.
+
+ Returns
+ -------
+ str_arr : ndarray
+ An array of strings the same shape as `arr`.
+
+ Examples
+ --------
+ >>> import pytz
+ >>> d = np.arange('2002-10-27T04:30', 4*60, 60, dtype='M8[m]')
+ >>> d
+ array(['2002-10-27T04:30', '2002-10-27T05:30', '2002-10-27T06:30',
+ '2002-10-27T07:30'], dtype='datetime64[m]')
+
+ Setting the timezone to UTC shows the same information, but with a Z suffix
+
+ >>> np.datetime_as_string(d, timezone='UTC')
+ array(['2002-10-27T04:30Z', '2002-10-27T05:30Z', '2002-10-27T06:30Z',
+ '2002-10-27T07:30Z'], dtype='<U35')
+
+ Note that we picked datetimes that cross a DST boundary. Passing in a
+ ``pytz`` timezone object will print the appropriate offset
+
+ >>> np.datetime_as_string(d, timezone=pytz.timezone('US/Eastern'))
+ array(['2002-10-27T00:30-0400', '2002-10-27T01:30-0400',
+ '2002-10-27T01:30-0500', '2002-10-27T02:30-0500'], dtype='<U39')
+
+ Passing in a unit will change the precision
+
+ >>> np.datetime_as_string(d, unit='h')
+ array(['2002-10-27T04', '2002-10-27T05', '2002-10-27T06', '2002-10-27T07'],
+ dtype='<U32')
+ >>> np.datetime_as_string(d, unit='s')
+ array(['2002-10-27T04:30:00', '2002-10-27T05:30:00', '2002-10-27T06:30:00',
+ '2002-10-27T07:30:00'], dtype='<U38')
+
+ 'casting' can be used to specify whether precision can be changed
+ >>> np.datetime_as_string(d, unit='h', casting='safe')
+ Traceback (most recent call last):
+ ...
+ TypeError: Cannot create a datetime string as units 'h' from a NumPy
+ datetime with units 'm' according to the rule 'safe'
+ """
+ return (arr,)
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py
index 52c796ade..b5568fd86 100644
--- a/numpy/core/numeric.py
+++ b/numpy/core/numeric.py
@@ -1,18 +1,15 @@
from __future__ import division, absolute_import, print_function
-try:
- # Accessing collections abstract classes from collections
- # has been deprecated since Python 3.3
- import collections.abc as collections_abc
-except ImportError:
- import collections as collections_abc
+import functools
import itertools
import operator
import sys
import warnings
import numbers
+import contextlib
import numpy as np
+from numpy.compat import pickle, basestring
from . import multiarray
from .multiarray import (
_fastCopyAndTranspose as fastCopyAndTranspose, ALLOW_THREADS,
@@ -27,33 +24,29 @@ from .multiarray import (
if sys.version_info[0] < 3:
from .multiarray import newbuffer, getbuffer
+from . import overrides
from . import umath
-from .umath import (multiply, invert, sin, UFUNC_BUFSIZE_DEFAULT,
- ERR_IGNORE, ERR_WARN, ERR_RAISE, ERR_CALL, ERR_PRINT,
- ERR_LOG, ERR_DEFAULT, PINF, NAN)
+from . import shape_base
+from .overrides import set_module
+from .umath import (multiply, invert, sin, PINF, NAN)
from . import numerictypes
from .numerictypes import longlong, intc, int_, float_, complex_, bool_
-from ._internal import TooHardError, AxisError
+from ._exceptions import TooHardError, AxisError
+from ._asarray import asarray, asanyarray
+from ._ufunc_config import errstate
bitwise_not = invert
ufunc = type(sin)
newaxis = None
if sys.version_info[0] >= 3:
- import pickle
- basestring = str
import builtins
else:
- import cPickle as pickle
import __builtin__ as builtins
-def loads(*args, **kwargs):
- # NumPy 1.15.0, 2017-12-10
- warnings.warn(
- "np.core.numeric.loads is deprecated, use pickle.loads instead",
- DeprecationWarning, stacklevel=2)
- return pickle.loads(*args, **kwargs)
+array_function_dispatch = functools.partial(
+ overrides.array_function_dispatch, module='numpy')
__all__ = [
@@ -62,15 +55,13 @@ __all__ = [
'fromstring', 'fromfile', 'frombuffer', 'int_asbuffer', 'where',
'argwhere', 'copyto', 'concatenate', 'fastCopyAndTranspose', 'lexsort',
'set_numeric_ops', 'can_cast', 'promote_types', 'min_scalar_type',
- 'result_type', 'asarray', 'asanyarray', 'ascontiguousarray',
- 'asfortranarray', 'isfortran', 'empty_like', 'zeros_like', 'ones_like',
+ 'result_type', 'isfortran', 'empty_like', 'zeros_like', 'ones_like',
'correlate', 'convolve', 'inner', 'dot', 'outer', 'vdot', 'roll',
- 'rollaxis', 'moveaxis', 'cross', 'tensordot', 'little_endian', 'require',
+ 'rollaxis', 'moveaxis', 'cross', 'tensordot', 'little_endian',
'fromiter', 'array_equal', 'array_equiv', 'indices', 'fromfunction',
- 'isclose', 'load', 'loads', 'isscalar', 'binary_repr', 'base_repr', 'ones',
- 'identity', 'allclose', 'compare_chararrays', 'putmask', 'seterr',
- 'geterr', 'setbufsize', 'getbufsize', 'seterrcall', 'geterrcall',
- 'errstate', 'flatnonzero', 'Inf', 'inf', 'infty', 'Infinity', 'nan', 'NaN',
+ 'isclose', 'isscalar', 'binary_repr', 'base_repr', 'ones',
+ 'identity', 'allclose', 'compare_chararrays', 'putmask',
+ 'flatnonzero', 'Inf', 'inf', 'infty', 'Infinity', 'nan', 'NaN',
'False_', 'True_', 'bitwise_not', 'CLIP', 'RAISE', 'WRAP', 'MAXDIMS',
'BUFSIZE', 'ALLOW_THREADS', 'ComplexWarning', 'full', 'full_like',
'matmul', 'shares_memory', 'may_share_memory', 'MAY_SHARE_BOUNDS',
@@ -80,6 +71,7 @@ if sys.version_info[0] < 3:
__all__.extend(['getbuffer', 'newbuffer'])
+@set_module('numpy')
class ComplexWarning(RuntimeWarning):
"""
The warning raised when casting a complex dtype to a real dtype.
@@ -91,7 +83,12 @@ class ComplexWarning(RuntimeWarning):
pass
-def zeros_like(a, dtype=None, order='K', subok=True):
+def _zeros_like_dispatcher(a, dtype=None, order=None, subok=None, shape=None):
+ return (a,)
+
+
+@array_function_dispatch(_zeros_like_dispatcher)
+def zeros_like(a, dtype=None, order='K', subok=True, shape=None):
"""
Return an array of zeros with the same shape and type as a given array.
@@ -115,6 +112,12 @@ def zeros_like(a, dtype=None, order='K', subok=True):
If True, then the newly created array will use the sub-class
type of 'a', otherwise it will be a base-class array. Defaults
to True.
+ shape : int or sequence of ints, optional.
+ Overrides the shape of the result. If order='K' and the number of
+ dimensions is unchanged, will try to keep order, otherwise,
+ order='C' is implied.
+
+ .. versionadded:: 1.17.0
Returns
-------
@@ -141,18 +144,19 @@ def zeros_like(a, dtype=None, order='K', subok=True):
>>> y = np.arange(3, dtype=float)
>>> y
- array([ 0., 1., 2.])
+ array([0., 1., 2.])
>>> np.zeros_like(y)
- array([ 0., 0., 0.])
+ array([0., 0., 0.])
"""
- res = empty_like(a, dtype=dtype, order=order, subok=subok)
+ res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape)
# needed instead of a 0 to get same result as zeros for for string dtypes
z = zeros(1, dtype=res.dtype)
multiarray.copyto(res, z, casting='unsafe')
return res
+@set_module('numpy')
def ones(shape, dtype=None, order='C'):
"""
Return a new array of given shape and type, filled with ones.
@@ -185,19 +189,19 @@ def ones(shape, dtype=None, order='C'):
Examples
--------
>>> np.ones(5)
- array([ 1., 1., 1., 1., 1.])
+ array([1., 1., 1., 1., 1.])
>>> np.ones((5,), dtype=int)
array([1, 1, 1, 1, 1])
>>> np.ones((2, 1))
- array([[ 1.],
- [ 1.]])
+ array([[1.],
+ [1.]])
>>> s = (2,2)
>>> np.ones(s)
- array([[ 1., 1.],
- [ 1., 1.]])
+ array([[1., 1.],
+ [1., 1.]])
"""
a = empty(shape, dtype, order)
@@ -205,7 +209,12 @@ def ones(shape, dtype=None, order='C'):
return a
-def ones_like(a, dtype=None, order='K', subok=True):
+def _ones_like_dispatcher(a, dtype=None, order=None, subok=None, shape=None):
+ return (a,)
+
+
+@array_function_dispatch(_ones_like_dispatcher)
+def ones_like(a, dtype=None, order='K', subok=True, shape=None):
"""
Return an array of ones with the same shape and type as a given array.
@@ -229,6 +238,12 @@ def ones_like(a, dtype=None, order='K', subok=True):
If True, then the newly created array will use the sub-class
type of 'a', otherwise it will be a base-class array. Defaults
to True.
+ shape : int or sequence of ints, optional.
+ Overrides the shape of the result. If order='K' and the number of
+ dimensions is unchanged, will try to keep order, otherwise,
+ order='C' is implied.
+
+ .. versionadded:: 1.17.0
Returns
-------
@@ -255,16 +270,17 @@ def ones_like(a, dtype=None, order='K', subok=True):
>>> y = np.arange(3, dtype=float)
>>> y
- array([ 0., 1., 2.])
+ array([0., 1., 2.])
>>> np.ones_like(y)
- array([ 1., 1., 1.])
+ array([1., 1., 1.])
"""
- res = empty_like(a, dtype=dtype, order=order, subok=subok)
+ res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape)
multiarray.copyto(res, 1, casting='unsafe')
return res
+@set_module('numpy')
def full(shape, fill_value, dtype=None, order='C'):
"""
Return a new array of given shape and type, filled with `fill_value`.
@@ -297,8 +313,8 @@ def full(shape, fill_value, dtype=None, order='C'):
Examples
--------
>>> np.full((2, 2), np.inf)
- array([[ inf, inf],
- [ inf, inf]])
+ array([[inf, inf],
+ [inf, inf]])
>>> np.full((2, 2), 10)
array([[10, 10],
[10, 10]])
@@ -311,7 +327,12 @@ def full(shape, fill_value, dtype=None, order='C'):
return a
-def full_like(a, fill_value, dtype=None, order='K', subok=True):
+def _full_like_dispatcher(a, fill_value, dtype=None, order=None, subok=None, shape=None):
+ return (a,)
+
+
+@array_function_dispatch(_full_like_dispatcher)
+def full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None):
"""
Return a full array with the same shape and type as a given array.
@@ -333,6 +354,12 @@ def full_like(a, fill_value, dtype=None, order='K', subok=True):
If True, then the newly created array will use the sub-class
type of 'a', otherwise it will be a base-class array. Defaults
to True.
+ shape : int or sequence of ints, optional.
+ Overrides the shape of the result. If order='K' and the number of
+ dimensions is unchanged, will try to keep order, otherwise,
+ order='C' is implied.
+
+ .. versionadded:: 1.17.0
Returns
-------
@@ -354,20 +381,25 @@ def full_like(a, fill_value, dtype=None, order='K', subok=True):
>>> np.full_like(x, 0.1)
array([0, 0, 0, 0, 0, 0])
>>> np.full_like(x, 0.1, dtype=np.double)
- array([ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
+ array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
>>> np.full_like(x, np.nan, dtype=np.double)
- array([ nan, nan, nan, nan, nan, nan])
+ array([nan, nan, nan, nan, nan, nan])
>>> y = np.arange(6, dtype=np.double)
>>> np.full_like(y, 0.1)
- array([ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
+ array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
"""
- res = empty_like(a, dtype=dtype, order=order, subok=subok)
+ res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape)
multiarray.copyto(res, fill_value, casting='unsafe')
return res
+def _count_nonzero_dispatcher(a, axis=None):
+ return (a,)
+
+
+@array_function_dispatch(_count_nonzero_dispatcher)
def count_nonzero(a, axis=None):
"""
Counts the number of non-zero values in the array ``a``.
@@ -430,304 +462,10 @@ def count_nonzero(a, axis=None):
return a_bool.sum(axis=axis, dtype=np.intp)
-def asarray(a, dtype=None, order=None):
- """Convert the input to an array.
-
- Parameters
- ----------
- a : array_like
- Input data, in any form that can be converted to an array. This
- includes lists, lists of tuples, tuples, tuples of tuples, tuples
- of lists and ndarrays.
- dtype : data-type, optional
- By default, the data-type is inferred from the input data.
- order : {'C', 'F'}, optional
- Whether to use row-major (C-style) or
- column-major (Fortran-style) memory representation.
- Defaults to 'C'.
-
- Returns
- -------
- out : ndarray
- Array interpretation of `a`. No copy is performed if the input
- is already an ndarray with matching dtype and order. If `a` is a
- subclass of ndarray, a base class ndarray is returned.
-
- See Also
- --------
- asanyarray : Similar function which passes through subclasses.
- ascontiguousarray : Convert input to a contiguous array.
- asfarray : Convert input to a floating point ndarray.
- asfortranarray : Convert input to an ndarray with column-major
- memory order.
- asarray_chkfinite : Similar function which checks input for NaNs and Infs.
- fromiter : Create an array from an iterator.
- fromfunction : Construct an array by executing a function on grid
- positions.
-
- Examples
- --------
- Convert a list into an array:
-
- >>> a = [1, 2]
- >>> np.asarray(a)
- array([1, 2])
-
- Existing arrays are not copied:
-
- >>> a = np.array([1, 2])
- >>> np.asarray(a) is a
- True
-
- If `dtype` is set, array is copied only if dtype does not match:
-
- >>> a = np.array([1, 2], dtype=np.float32)
- >>> np.asarray(a, dtype=np.float32) is a
- True
- >>> np.asarray(a, dtype=np.float64) is a
- False
-
- Contrary to `asanyarray`, ndarray subclasses are not passed through:
-
- >>> issubclass(np.recarray, np.ndarray)
- True
- >>> a = np.array([(1.0, 2), (3.0, 4)], dtype='f4,i4').view(np.recarray)
- >>> np.asarray(a) is a
- False
- >>> np.asanyarray(a) is a
- True
-
- """
- return array(a, dtype, copy=False, order=order)
-
-
-def asanyarray(a, dtype=None, order=None):
- """Convert the input to an ndarray, but pass ndarray subclasses through.
-
- Parameters
- ----------
- a : array_like
- Input data, in any form that can be converted to an array. This
- includes scalars, lists, lists of tuples, tuples, tuples of tuples,
- tuples of lists, and ndarrays.
- dtype : data-type, optional
- By default, the data-type is inferred from the input data.
- order : {'C', 'F'}, optional
- Whether to use row-major (C-style) or column-major
- (Fortran-style) memory representation. Defaults to 'C'.
-
- Returns
- -------
- out : ndarray or an ndarray subclass
- Array interpretation of `a`. If `a` is an ndarray or a subclass
- of ndarray, it is returned as-is and no copy is performed.
-
- See Also
- --------
- asarray : Similar function which always returns ndarrays.
- ascontiguousarray : Convert input to a contiguous array.
- asfarray : Convert input to a floating point ndarray.
- asfortranarray : Convert input to an ndarray with column-major
- memory order.
- asarray_chkfinite : Similar function which checks input for NaNs and
- Infs.
- fromiter : Create an array from an iterator.
- fromfunction : Construct an array by executing a function on grid
- positions.
-
- Examples
- --------
- Convert a list into an array:
-
- >>> a = [1, 2]
- >>> np.asanyarray(a)
- array([1, 2])
-
- Instances of `ndarray` subclasses are passed through as-is:
-
- >>> a = np.array([(1.0, 2), (3.0, 4)], dtype='f4,i4').view(np.recarray)
- >>> np.asanyarray(a) is a
- True
-
- """
- return array(a, dtype, copy=False, order=order, subok=True)
-
-
-def ascontiguousarray(a, dtype=None):
- """
- Return a contiguous array in memory (C order).
-
- Parameters
- ----------
- a : array_like
- Input array.
- dtype : str or dtype object, optional
- Data-type of returned array.
-
- Returns
- -------
- out : ndarray
- Contiguous array of same shape and content as `a`, with type `dtype`
- if specified.
-
- See Also
- --------
- asfortranarray : Convert input to an ndarray with column-major
- memory order.
- require : Return an ndarray that satisfies requirements.
- ndarray.flags : Information about the memory layout of the array.
-
- Examples
- --------
- >>> x = np.arange(6).reshape(2,3)
- >>> np.ascontiguousarray(x, dtype=np.float32)
- array([[ 0., 1., 2.],
- [ 3., 4., 5.]], dtype=float32)
- >>> x.flags['C_CONTIGUOUS']
- True
-
- """
- return array(a, dtype, copy=False, order='C', ndmin=1)
-
-
-def asfortranarray(a, dtype=None):
- """
- Return an array laid out in Fortran order in memory.
-
- Parameters
- ----------
- a : array_like
- Input array.
- dtype : str or dtype object, optional
- By default, the data-type is inferred from the input data.
-
- Returns
- -------
- out : ndarray
- The input `a` in Fortran, or column-major, order.
-
- See Also
- --------
- ascontiguousarray : Convert input to a contiguous (C order) array.
- asanyarray : Convert input to an ndarray with either row or
- column-major memory order.
- require : Return an ndarray that satisfies requirements.
- ndarray.flags : Information about the memory layout of the array.
-
- Examples
- --------
- >>> x = np.arange(6).reshape(2,3)
- >>> y = np.asfortranarray(x)
- >>> x.flags['F_CONTIGUOUS']
- False
- >>> y.flags['F_CONTIGUOUS']
- True
-
- """
- return array(a, dtype, copy=False, order='F', ndmin=1)
-
-
-def require(a, dtype=None, requirements=None):
- """
- Return an ndarray of the provided type that satisfies requirements.
-
- This function is useful to be sure that an array with the correct flags
- is returned for passing to compiled code (perhaps through ctypes).
-
- Parameters
- ----------
- a : array_like
- The object to be converted to a type-and-requirement-satisfying array.
- dtype : data-type
- The required data-type. If None preserve the current dtype. If your
- application requires the data to be in native byteorder, include
- a byteorder specification as a part of the dtype specification.
- requirements : str or list of str
- The requirements list can be any of the following
-
- * 'F_CONTIGUOUS' ('F') - ensure a Fortran-contiguous array
- * 'C_CONTIGUOUS' ('C') - ensure a C-contiguous array
- * 'ALIGNED' ('A') - ensure a data-type aligned array
- * 'WRITEABLE' ('W') - ensure a writable array
- * 'OWNDATA' ('O') - ensure an array that owns its own data
- * 'ENSUREARRAY', ('E') - ensure a base array, instead of a subclass
-
- See Also
- --------
- asarray : Convert input to an ndarray.
- asanyarray : Convert to an ndarray, but pass through ndarray subclasses.
- ascontiguousarray : Convert input to a contiguous array.
- asfortranarray : Convert input to an ndarray with column-major
- memory order.
- ndarray.flags : Information about the memory layout of the array.
-
- Notes
- -----
- The returned array will be guaranteed to have the listed requirements
- by making a copy if needed.
-
- Examples
- --------
- >>> x = np.arange(6).reshape(2,3)
- >>> x.flags
- C_CONTIGUOUS : True
- F_CONTIGUOUS : False
- OWNDATA : False
- WRITEABLE : True
- ALIGNED : True
- WRITEBACKIFCOPY : False
- UPDATEIFCOPY : False
-
- >>> y = np.require(x, dtype=np.float32, requirements=['A', 'O', 'W', 'F'])
- >>> y.flags
- C_CONTIGUOUS : False
- F_CONTIGUOUS : True
- OWNDATA : True
- WRITEABLE : True
- ALIGNED : True
- WRITEBACKIFCOPY : False
- UPDATEIFCOPY : False
-
- """
- possible_flags = {'C': 'C', 'C_CONTIGUOUS': 'C', 'CONTIGUOUS': 'C',
- 'F': 'F', 'F_CONTIGUOUS': 'F', 'FORTRAN': 'F',
- 'A': 'A', 'ALIGNED': 'A',
- 'W': 'W', 'WRITEABLE': 'W',
- 'O': 'O', 'OWNDATA': 'O',
- 'E': 'E', 'ENSUREARRAY': 'E'}
- if not requirements:
- return asanyarray(a, dtype=dtype)
- else:
- requirements = set(possible_flags[x.upper()] for x in requirements)
-
- if 'E' in requirements:
- requirements.remove('E')
- subok = False
- else:
- subok = True
-
- order = 'A'
- if requirements >= set(['C', 'F']):
- raise ValueError('Cannot specify both "C" and "F" order')
- elif 'F' in requirements:
- order = 'F'
- requirements.remove('F')
- elif 'C' in requirements:
- order = 'C'
- requirements.remove('C')
-
- arr = array(a, dtype=dtype, order=order, copy=False, subok=subok)
-
- for prop in requirements:
- if not arr.flags[prop]:
- arr = arr.copy(order)
- break
- return arr
-
-
+@set_module('numpy')
def isfortran(a):
"""
- Returns True if the array is Fortran contiguous but *not* C contiguous.
+ Check if the array is Fortran contiguous but *not* C contiguous.
This function is obsolete and, because of changes due to relaxed stride
checking, its return value for the same array may differ for versions
@@ -739,6 +477,11 @@ def isfortran(a):
a : ndarray
Input array.
+ Returns
+ -------
+ isfortran : bool
+ Returns True if the array is Fortran contiguous but *not* C contiguous.
+
Examples
--------
@@ -754,7 +497,7 @@ def isfortran(a):
>>> np.isfortran(a)
False
- >>> b = np.array([[1, 2, 3], [4, 5, 6]], order='FORTRAN')
+ >>> b = np.array([[1, 2, 3], [4, 5, 6]], order='F')
>>> b
array([[1, 2, 3],
[4, 5, 6]])
@@ -780,13 +523,18 @@ def isfortran(a):
C-ordered arrays evaluate as False even if they are also FORTRAN-ordered.
- >>> np.isfortran(np.array([1, 2], order='FORTRAN'))
+ >>> np.isfortran(np.array([1, 2], order='F'))
False
"""
return a.flags.fnc
+def _argwhere_dispatcher(a):
+ return (a,)
+
+
+@array_function_dispatch(_argwhere_dispatcher)
def argwhere(a):
"""
Find the indices of array elements that are non-zero, grouped by element.
@@ -798,8 +546,10 @@ def argwhere(a):
Returns
-------
- index_array : ndarray
+ index_array : (N, a.ndim) ndarray
Indices of elements that are non-zero. Indices are grouped by element.
+ This array will have shape ``(N, a.ndim)`` where ``N`` is the number of
+ non-zero items.
See Also
--------
@@ -807,7 +557,8 @@ def argwhere(a):
Notes
-----
- ``np.argwhere(a)`` is the same as ``np.transpose(np.nonzero(a))``.
+ ``np.argwhere(a)`` is almost the same as ``np.transpose(np.nonzero(a))``,
+ but produces a result of the correct shape for a 0D array.
The output of ``argwhere`` is not suitable for indexing arrays.
For this purpose use ``nonzero(a)`` instead.
@@ -825,9 +576,19 @@ def argwhere(a):
[1, 2]])
"""
+ # nonzero does not behave well on 0d, so promote to 1d
+ if np.ndim(a) == 0:
+ a = shape_base.atleast_1d(a)
+ # then remove the added dimension
+ return argwhere(a)[:,:0]
return transpose(nonzero(a))
+def _flatnonzero_dispatcher(a):
+ return (a,)
+
+
+@array_function_dispatch(_flatnonzero_dispatcher)
def flatnonzero(a):
"""
Return indices that are non-zero in the flattened version of a.
@@ -879,6 +640,11 @@ def _mode_from_name(mode):
return mode
+def _correlate_dispatcher(a, v, mode=None):
+ return (a, v)
+
+
+@array_function_dispatch(_correlate_dispatcher)
def correlate(a, v, mode='valid'):
"""
Cross-correlation of two 1-dimensional sequences.
@@ -924,11 +690,11 @@ def correlate(a, v, mode='valid'):
Examples
--------
>>> np.correlate([1, 2, 3], [0, 1, 0.5])
- array([ 3.5])
+ array([3.5])
>>> np.correlate([1, 2, 3], [0, 1, 0.5], "same")
- array([ 2. , 3.5, 3. ])
+ array([2. , 3.5, 3. ])
>>> np.correlate([1, 2, 3], [0, 1, 0.5], "full")
- array([ 0.5, 2. , 3.5, 3. , 0. ])
+ array([0.5, 2. , 3.5, 3. , 0. ])
Using complex sequences:
@@ -947,6 +713,11 @@ def correlate(a, v, mode='valid'):
return multiarray.correlate2(a, v, mode)
+def _convolve_dispatcher(a, v, mode=None):
+ return (a, v)
+
+
+@array_function_dispatch(_convolve_dispatcher)
def convolve(a, v, mode='full'):
"""
Returns the discrete, linear convolution of two one-dimensional sequences.
@@ -1019,20 +790,20 @@ def convolve(a, v, mode='full'):
before "sliding" the two across one another:
>>> np.convolve([1, 2, 3], [0, 1, 0.5])
- array([ 0. , 1. , 2.5, 4. , 1.5])
+ array([0. , 1. , 2.5, 4. , 1.5])
Only return the middle values of the convolution.
Contains boundary effects, where zeros are taken
into account:
>>> np.convolve([1,2,3],[0,1,0.5], 'same')
- array([ 1. , 2.5, 4. ])
+ array([1. , 2.5, 4. ])
The two arrays are of the same length, so there
is only one position where they completely overlap:
>>> np.convolve([1,2,3],[0,1,0.5], 'valid')
- array([ 2.5])
+ array([2.5])
"""
a, v = array(a, copy=False, ndmin=1), array(v, copy=False, ndmin=1)
@@ -1046,6 +817,11 @@ def convolve(a, v, mode='full'):
return multiarray.correlate(a, v[::-1], mode)
+def _outer_dispatcher(a, b, out=None):
+ return (a, b, out)
+
+
+@array_function_dispatch(_outer_dispatcher)
def outer(a, b, out=None):
"""
Compute the outer product of two vectors.
@@ -1103,11 +879,11 @@ def outer(a, b, out=None):
[-2., -1., 0., 1., 2.]])
>>> im = np.outer(1j*np.linspace(2, -2, 5), np.ones((5,)))
>>> im
- array([[ 0.+2.j, 0.+2.j, 0.+2.j, 0.+2.j, 0.+2.j],
- [ 0.+1.j, 0.+1.j, 0.+1.j, 0.+1.j, 0.+1.j],
- [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
- [ 0.-1.j, 0.-1.j, 0.-1.j, 0.-1.j, 0.-1.j],
- [ 0.-2.j, 0.-2.j, 0.-2.j, 0.-2.j, 0.-2.j]])
+ array([[0.+2.j, 0.+2.j, 0.+2.j, 0.+2.j, 0.+2.j],
+ [0.+1.j, 0.+1.j, 0.+1.j, 0.+1.j, 0.+1.j],
+ [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
+ [0.-1.j, 0.-1.j, 0.-1.j, 0.-1.j, 0.-1.j],
+ [0.-2.j, 0.-2.j, 0.-2.j, 0.-2.j, 0.-2.j]])
>>> grid = rl + im
>>> grid
array([[-2.+2.j, -1.+2.j, 0.+2.j, 1.+2.j, 2.+2.j],
@@ -1120,9 +896,9 @@ def outer(a, b, out=None):
>>> x = np.array(['a', 'b', 'c'], dtype=object)
>>> np.outer(x, [1, 2, 3])
- array([[a, aa, aaa],
- [b, bb, bbb],
- [c, cc, ccc]], dtype=object)
+ array([['a', 'aa', 'aaa'],
+ ['b', 'bb', 'bbb'],
+ ['c', 'cc', 'ccc']], dtype=object)
"""
a = asarray(a)
@@ -1130,21 +906,25 @@ def outer(a, b, out=None):
return multiply(a.ravel()[:, newaxis], b.ravel()[newaxis, :], out)
+def _tensordot_dispatcher(a, b, axes=None):
+ return (a, b)
+
+
+@array_function_dispatch(_tensordot_dispatcher)
def tensordot(a, b, axes=2):
- """Compute tensor dot product along specified axes for arrays >= 1-D.
+ """
+ Compute tensor dot product along specified axes.
- Given two tensors (arrays of dimension greater than or equal to one),
- `a` and `b`, and an array_like object containing two array_like
- objects, ``(a_axes, b_axes)``, sum the products of `a`'s and `b`'s
- elements (components) over the axes specified by ``a_axes`` and
- ``b_axes``. The third argument can be a single non-negative
- integer_like scalar, ``N``; if it is such, then the last ``N``
- dimensions of `a` and the first ``N`` dimensions of `b` are summed
- over.
+ Given two tensors, `a` and `b`, and an array_like object containing
+ two array_like objects, ``(a_axes, b_axes)``, sum the products of
+ `a`'s and `b`'s elements (components) over the axes specified by
+ ``a_axes`` and ``b_axes``. The third argument can be a single non-negative
+ integer_like scalar, ``N``; if it is such, then the last ``N`` dimensions
+ of `a` and the first ``N`` dimensions of `b` are summed over.
Parameters
----------
- a, b : array_like, len(shape) >= 1
+ a, b : array_like
Tensors to "dot".
axes : int or (2,) array_like
@@ -1155,6 +935,11 @@ def tensordot(a, b, axes=2):
Or, a list of axes to be summed over, first sequence applying to `a`,
second to `b`. Both elements array_like must be of the same length.
+ Returns
+ -------
+ output : ndarray
+ The tensor dot product of the input.
+
See Also
--------
dot, einsum
@@ -1188,11 +973,11 @@ def tensordot(a, b, axes=2):
>>> c.shape
(5, 2)
>>> c
- array([[ 4400., 4730.],
- [ 4532., 4874.],
- [ 4664., 5018.],
- [ 4796., 5162.],
- [ 4928., 5306.]])
+ array([[4400., 4730.],
+ [4532., 4874.],
+ [4664., 5018.],
+ [4796., 5162.],
+ [4928., 5306.]])
>>> # A slower but equivalent way of computing the same...
>>> d = np.zeros((5,2))
>>> for i in range(5):
@@ -1218,40 +1003,40 @@ def tensordot(a, b, axes=2):
[3, 4]],
[[5, 6],
[7, 8]]])
- array([[a, b],
- [c, d]], dtype=object)
+ array([['a', 'b'],
+ ['c', 'd']], dtype=object)
>>> np.tensordot(a, A) # third argument default is 2 for double-contraction
- array([abbcccdddd, aaaaabbbbbbcccccccdddddddd], dtype=object)
+ array(['abbcccdddd', 'aaaaabbbbbbcccccccdddddddd'], dtype=object)
>>> np.tensordot(a, A, 1)
- array([[[acc, bdd],
- [aaacccc, bbbdddd]],
- [[aaaaacccccc, bbbbbdddddd],
- [aaaaaaacccccccc, bbbbbbbdddddddd]]], dtype=object)
+ array([[['acc', 'bdd'],
+ ['aaacccc', 'bbbdddd']],
+ [['aaaaacccccc', 'bbbbbdddddd'],
+ ['aaaaaaacccccccc', 'bbbbbbbdddddddd']]], dtype=object)
>>> np.tensordot(a, A, 0) # tensor product (result too long to incl.)
- array([[[[[a, b],
- [c, d]],
+ array([[[[['a', 'b'],
+ ['c', 'd']],
...
>>> np.tensordot(a, A, (0, 1))
- array([[[abbbbb, cddddd],
- [aabbbbbb, ccdddddd]],
- [[aaabbbbbbb, cccddddddd],
- [aaaabbbbbbbb, ccccdddddddd]]], dtype=object)
+ array([[['abbbbb', 'cddddd'],
+ ['aabbbbbb', 'ccdddddd']],
+ [['aaabbbbbbb', 'cccddddddd'],
+ ['aaaabbbbbbbb', 'ccccdddddddd']]], dtype=object)
>>> np.tensordot(a, A, (2, 1))
- array([[[abb, cdd],
- [aaabbbb, cccdddd]],
- [[aaaaabbbbbb, cccccdddddd],
- [aaaaaaabbbbbbbb, cccccccdddddddd]]], dtype=object)
+ array([[['abb', 'cdd'],
+ ['aaabbbb', 'cccdddd']],
+ [['aaaaabbbbbb', 'cccccdddddd'],
+ ['aaaaaaabbbbbbbb', 'cccccccdddddddd']]], dtype=object)
>>> np.tensordot(a, A, ((0, 1), (0, 1)))
- array([abbbcccccddddddd, aabbbbccccccdddddddd], dtype=object)
+ array(['abbbcccccddddddd', 'aabbbbccccccdddddddd'], dtype=object)
>>> np.tensordot(a, A, ((2, 1), (1, 0)))
- array([acccbbdddd, aaaaacccccccbbbbbbdddddddd], dtype=object)
+ array(['acccbbdddd', 'aaaaacccccccbbbbbbdddddddd'], dtype=object)
"""
try:
@@ -1318,6 +1103,11 @@ def tensordot(a, b, axes=2):
return res.reshape(olda + oldb)
+def _roll_dispatcher(a, shift, axis=None):
+ return (a,)
+
+
+@array_function_dispatch(_roll_dispatcher)
def roll(a, shift, axis=None):
"""
Roll array elements along a given axis.
@@ -1361,6 +1151,8 @@ def roll(a, shift, axis=None):
>>> x = np.arange(10)
>>> np.roll(x, 2)
array([8, 9, 0, 1, 2, 3, 4, 5, 6, 7])
+ >>> np.roll(x, -2)
+ array([2, 3, 4, 5, 6, 7, 8, 9, 0, 1])
>>> x2 = np.reshape(x, (2,5))
>>> x2
@@ -1369,12 +1161,21 @@ def roll(a, shift, axis=None):
>>> np.roll(x2, 1)
array([[9, 0, 1, 2, 3],
[4, 5, 6, 7, 8]])
+ >>> np.roll(x2, -1)
+ array([[1, 2, 3, 4, 5],
+ [6, 7, 8, 9, 0]])
>>> np.roll(x2, 1, axis=0)
array([[5, 6, 7, 8, 9],
[0, 1, 2, 3, 4]])
+ >>> np.roll(x2, -1, axis=0)
+ array([[5, 6, 7, 8, 9],
+ [0, 1, 2, 3, 4]])
>>> np.roll(x2, 1, axis=1)
array([[4, 0, 1, 2, 3],
[9, 5, 6, 7, 8]])
+ >>> np.roll(x2, -1, axis=1)
+ array([[1, 2, 3, 4, 0],
+ [6, 7, 8, 9, 5]])
"""
a = asanyarray(a)
@@ -1407,6 +1208,11 @@ def roll(a, shift, axis=None):
return result
+def _rollaxis_dispatcher(a, axis, start=None):
+ return (a,)
+
+
+@array_function_dispatch(_rollaxis_dispatcher)
def rollaxis(a, axis, start=0):
"""
Roll the specified axis backwards, until it lies in a given position.
@@ -1527,6 +1333,11 @@ def normalize_axis_tuple(axis, ndim, argname=None, allow_duplicate=False):
return axis
+def _moveaxis_dispatcher(a, source, destination):
+ return (a,)
+
+
+@array_function_dispatch(_moveaxis_dispatcher)
def moveaxis(a, source, destination):
"""
Move axes of an array to new positions.
@@ -1603,6 +1414,11 @@ def _move_axis_to_0(a, axis):
return moveaxis(a, axis, 0)
+def _cross_dispatcher(a, b, axisa=None, axisb=None, axisc=None, axis=None):
+ return (a, b)
+
+
+@array_function_dispatch(_cross_dispatcher)
def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None):
"""
Return the cross product of two (arrays of) vectors.
@@ -1684,7 +1500,7 @@ def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None):
>>> x = [1,2]
>>> y = [4,5]
>>> np.cross(x, y)
- -3
+ array(-3)
Multiple vector cross-products. Note that the direction of the cross
product vector is defined by the `right-hand rule`.
@@ -1803,11 +1619,12 @@ def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None):
little_endian = (sys.byteorder == 'little')
-def indices(dimensions, dtype=int):
+@set_module('numpy')
+def indices(dimensions, dtype=int, sparse=False):
"""
Return an array representing the indices of a grid.
- Compute an array where the subarrays contain index values 0,1,...
+ Compute an array where the subarrays contain index values 0, 1, ...
varying only along the corresponding axis.
Parameters
@@ -1816,28 +1633,38 @@ def indices(dimensions, dtype=int):
The shape of the grid.
dtype : dtype, optional
Data type of the result.
+ sparse : boolean, optional
+ Return a sparse representation of the grid instead of a dense
+ representation. Default is False.
+
+ .. versionadded:: 1.17
Returns
-------
- grid : ndarray
- The array of grid indices,
- ``grid.shape = (len(dimensions),) + tuple(dimensions)``.
+ grid : one ndarray or tuple of ndarrays
+ If sparse is False:
+ Returns one array of grid indices,
+ ``grid.shape = (len(dimensions),) + tuple(dimensions)``.
+ If sparse is True:
+ Returns a tuple of arrays, with
+ ``grid[i].shape = (1, ..., 1, dimensions[i], 1, ..., 1)`` with
+ dimensions[i] in the ith place
See Also
--------
- mgrid, meshgrid
+ mgrid, ogrid, meshgrid
Notes
-----
- The output shape is obtained by prepending the number of dimensions
- in front of the tuple of dimensions, i.e. if `dimensions` is a tuple
- ``(r0, ..., rN-1)`` of length ``N``, the output shape is
- ``(N,r0,...,rN-1)``.
+ The output shape in the dense case is obtained by prepending the number
+ of dimensions in front of the tuple of dimensions, i.e. if `dimensions`
+ is a tuple ``(r0, ..., rN-1)`` of length ``N``, the output shape is
+ ``(N, r0, ..., rN-1)``.
The subarrays ``grid[k]`` contains the N-D array of indices along the
``k-th`` axis. Explicitly::
- grid[k,i0,i1,...,iN-1] = ik
+ grid[k, i0, i1, ..., iN-1] = ik
Examples
--------
@@ -1862,18 +1689,40 @@ def indices(dimensions, dtype=int):
Note that it would be more straightforward in the above example to
extract the required elements directly with ``x[:2, :3]``.
+ If sparse is set to true, the grid will be returned in a sparse
+ representation.
+
+ >>> i, j = np.indices((2, 3), sparse=True)
+ >>> i.shape
+ (2, 1)
+ >>> j.shape
+ (1, 3)
+ >>> i # row indices
+ array([[0],
+ [1]])
+ >>> j # column indices
+ array([[0, 1, 2]])
+
"""
dimensions = tuple(dimensions)
N = len(dimensions)
shape = (1,)*N
- res = empty((N,)+dimensions, dtype=dtype)
+ if sparse:
+ res = tuple()
+ else:
+ res = empty((N,)+dimensions, dtype=dtype)
for i, dim in enumerate(dimensions):
- res[i] = arange(dim, dtype=dtype).reshape(
+ idx = arange(dim, dtype=dtype).reshape(
shape[:i] + (dim,) + shape[i+1:]
)
+ if sparse:
+ res = res + (idx,)
+ else:
+ res[i] = idx
return res
+@set_module('numpy')
def fromfunction(function, shape, **kwargs):
"""
Construct an array by executing a function over each coordinate.
@@ -1930,6 +1779,11 @@ def fromfunction(function, shape, **kwargs):
return function(*args, **kwargs)
+def _frombuffer(buf, dtype, shape, order):
+ return frombuffer(buf, dtype=dtype).reshape(shape, order=order)
+
+
+@set_module('numpy')
def isscalar(num):
"""
Returns True if the type of `num` is a scalar type.
@@ -1994,10 +1848,10 @@ def isscalar(num):
NumPy supports PEP 3141 numbers:
>>> from fractions import Fraction
- >>> isscalar(Fraction(5, 17))
+ >>> np.isscalar(Fraction(5, 17))
True
>>> from numbers import Number
- >>> isscalar(Number())
+ >>> np.isscalar(Number())
True
"""
@@ -2006,6 +1860,7 @@ def isscalar(num):
or isinstance(num, numbers.Number))
+@set_module('numpy')
def binary_repr(num, width=None):
"""
Return the binary representation of the input number as a string.
@@ -2084,6 +1939,10 @@ def binary_repr(num, width=None):
"will raise an error in the future.", DeprecationWarning,
stacklevel=3)
+ # Ensure that num is a Python integer to avoid overflow or unwanted
+ # casts to floating point.
+ num = operator.index(num)
+
if num == 0:
return '0' * (width or 1)
@@ -2116,6 +1975,7 @@ def binary_repr(num, width=None):
return '1' * (outwidth - binwidth) + binary
+@set_module('numpy')
def base_repr(number, base=2, padding=0):
"""
Return a string representation of a number in the given base system.
@@ -2172,29 +2032,6 @@ def base_repr(number, base=2, padding=0):
return ''.join(reversed(res or '0'))
-def load(file):
- """
- Wrapper around cPickle.load which accepts either a file-like object or
- a filename.
-
- Note that the NumPy binary format is not based on pickle/cPickle anymore.
- For details on the preferred way of loading and saving files, see `load`
- and `save`.
-
- See Also
- --------
- load, save
-
- """
- # NumPy 1.15.0, 2017-12-10
- warnings.warn(
- "np.core.numeric.load is deprecated, use pickle.load instead",
- DeprecationWarning, stacklevel=2)
- if isinstance(file, type("")):
- file = open(file, "rb")
- return pickle.load(file)
-
-
# These are all essentially abbreviations
# These might wind up in a special abbreviations module
@@ -2210,6 +2047,7 @@ def _maketup(descr, val):
return tuple(res)
+@set_module('numpy')
def identity(n, dtype=None):
"""
Return the identity array.
@@ -2233,15 +2071,20 @@ def identity(n, dtype=None):
Examples
--------
>>> np.identity(3)
- array([[ 1., 0., 0.],
- [ 0., 1., 0.],
- [ 0., 0., 1.]])
+ array([[1., 0., 0.],
+ [0., 1., 0.],
+ [0., 0., 1.]])
"""
from numpy import eye
return eye(n, dtype=dtype)
+def _allclose_dispatcher(a, b, rtol=None, atol=None, equal_nan=None):
+ return (a, b)
+
+
+@array_function_dispatch(_allclose_dispatcher)
def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
"""
Returns True if two arrays are element-wise equal within a tolerance.
@@ -2265,7 +2108,7 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
The absolute tolerance parameter (see Notes).
equal_nan : bool
Whether to compare NaN's as equal. If True, NaN's in `a` will be
- considered equal to NaN's in `b` in the output array.
+ considered equal to NaN's in `b`.
.. versionadded:: 1.10.0
@@ -2313,6 +2156,11 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
return bool(res)
+def _isclose_dispatcher(a, b, rtol=None, atol=None, equal_nan=None):
+ return (a, b)
+
+
+@array_function_dispatch(_isclose_dispatcher)
def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
"""
Returns a boolean array where two arrays are element-wise equal within a
@@ -2371,23 +2219,23 @@ def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
Examples
--------
>>> np.isclose([1e10,1e-7], [1.00001e10,1e-8])
- array([True, False])
+ array([ True, False])
>>> np.isclose([1e10,1e-8], [1.00001e10,1e-9])
- array([True, True])
+ array([ True, True])
>>> np.isclose([1e10,1e-8], [1.0001e10,1e-9])
- array([False, True])
+ array([False, True])
>>> np.isclose([1.0, np.nan], [1.0, np.nan])
- array([True, False])
+ array([ True, False])
>>> np.isclose([1.0, np.nan], [1.0, np.nan], equal_nan=True)
- array([True, True])
+ array([ True, True])
>>> np.isclose([1e-8, 1e-7], [0.0, 0.0])
- array([ True, False], dtype=bool)
+ array([ True, False])
>>> np.isclose([1e-100, 1e-7], [0.0, 0.0], atol=0.0)
- array([False, False], dtype=bool)
+ array([False, False])
>>> np.isclose([1e-10, 1e-10], [1e-20, 0.0])
- array([ True, True], dtype=bool)
+ array([ True, True])
>>> np.isclose([1e-10, 1e-10], [1e-20, 0.999999e-10], atol=0.0)
- array([False, True], dtype=bool)
+ array([False, True])
"""
def within_tol(x, y, atol, rtol):
with errstate(invalid='ignore'):
@@ -2428,6 +2276,11 @@ def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
return cond[()] # Flatten 0d arrays to scalars
+def _array_equal_dispatcher(a1, a2):
+ return (a1, a2)
+
+
+@array_function_dispatch(_array_equal_dispatcher)
def array_equal(a1, a2):
"""
True if two arrays have the same shape and elements, False otherwise.
@@ -2470,6 +2323,11 @@ def array_equal(a1, a2):
return bool(asarray(a1 == a2).all())
+def _array_equiv_dispatcher(a1, a2):
+ return (a1, a2)
+
+
+@array_function_dispatch(_array_equiv_dispatcher)
def array_equiv(a1, a2):
"""
Returns True if input arrays are shape consistent and all elements equal.
@@ -2517,433 +2375,6 @@ def array_equiv(a1, a2):
return bool(asarray(a1 == a2).all())
-_errdict = {"ignore": ERR_IGNORE,
- "warn": ERR_WARN,
- "raise": ERR_RAISE,
- "call": ERR_CALL,
- "print": ERR_PRINT,
- "log": ERR_LOG}
-
-_errdict_rev = {}
-for key in _errdict.keys():
- _errdict_rev[_errdict[key]] = key
-del key
-
-
-def seterr(all=None, divide=None, over=None, under=None, invalid=None):
- """
- Set how floating-point errors are handled.
-
- Note that operations on integer scalar types (such as `int16`) are
- handled like floating point, and are affected by these settings.
-
- Parameters
- ----------
- all : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
- Set treatment for all types of floating-point errors at once:
-
- - ignore: Take no action when the exception occurs.
- - warn: Print a `RuntimeWarning` (via the Python `warnings` module).
- - raise: Raise a `FloatingPointError`.
- - call: Call a function specified using the `seterrcall` function.
- - print: Print a warning directly to ``stdout``.
- - log: Record error in a Log object specified by `seterrcall`.
-
- The default is not to change the current behavior.
- divide : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
- Treatment for division by zero.
- over : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
- Treatment for floating-point overflow.
- under : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
- Treatment for floating-point underflow.
- invalid : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional
- Treatment for invalid floating-point operation.
-
- Returns
- -------
- old_settings : dict
- Dictionary containing the old settings.
-
- See also
- --------
- seterrcall : Set a callback function for the 'call' mode.
- geterr, geterrcall, errstate
-
- Notes
- -----
- The floating-point exceptions are defined in the IEEE 754 standard [1]_:
-
- - Division by zero: infinite result obtained from finite numbers.
- - Overflow: result too large to be expressed.
- - Underflow: result so close to zero that some precision
- was lost.
- - Invalid operation: result is not an expressible number, typically
- indicates that a NaN was produced.
-
- .. [1] https://en.wikipedia.org/wiki/IEEE_754
-
- Examples
- --------
- >>> old_settings = np.seterr(all='ignore') #seterr to known value
- >>> np.seterr(over='raise')
- {'over': 'ignore', 'divide': 'ignore', 'invalid': 'ignore',
- 'under': 'ignore'}
- >>> np.seterr(**old_settings) # reset to default
- {'over': 'raise', 'divide': 'ignore', 'invalid': 'ignore',
- 'under': 'ignore'}
-
- >>> np.int16(32000) * np.int16(3)
- 30464
- >>> old_settings = np.seterr(all='warn', over='raise')
- >>> np.int16(32000) * np.int16(3)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- FloatingPointError: overflow encountered in short_scalars
-
- >>> old_settings = np.seterr(all='print')
- >>> np.geterr()
- {'over': 'print', 'divide': 'print', 'invalid': 'print', 'under': 'print'}
- >>> np.int16(32000) * np.int16(3)
- Warning: overflow encountered in short_scalars
- 30464
-
- """
-
- pyvals = umath.geterrobj()
- old = geterr()
-
- if divide is None:
- divide = all or old['divide']
- if over is None:
- over = all or old['over']
- if under is None:
- under = all or old['under']
- if invalid is None:
- invalid = all or old['invalid']
-
- maskvalue = ((_errdict[divide] << SHIFT_DIVIDEBYZERO) +
- (_errdict[over] << SHIFT_OVERFLOW) +
- (_errdict[under] << SHIFT_UNDERFLOW) +
- (_errdict[invalid] << SHIFT_INVALID))
-
- pyvals[1] = maskvalue
- umath.seterrobj(pyvals)
- return old
-
-
-def geterr():
- """
- Get the current way of handling floating-point errors.
-
- Returns
- -------
- res : dict
- A dictionary with keys "divide", "over", "under", and "invalid",
- whose values are from the strings "ignore", "print", "log", "warn",
- "raise", and "call". The keys represent possible floating-point
- exceptions, and the values define how these exceptions are handled.
-
- See Also
- --------
- geterrcall, seterr, seterrcall
-
- Notes
- -----
- For complete documentation of the types of floating-point exceptions and
- treatment options, see `seterr`.
-
- Examples
- --------
- >>> np.geterr()
- {'over': 'warn', 'divide': 'warn', 'invalid': 'warn',
- 'under': 'ignore'}
- >>> np.arange(3.) / np.arange(3.)
- array([ NaN, 1., 1.])
-
- >>> oldsettings = np.seterr(all='warn', over='raise')
- >>> np.geterr()
- {'over': 'raise', 'divide': 'warn', 'invalid': 'warn', 'under': 'warn'}
- >>> np.arange(3.) / np.arange(3.)
- __main__:1: RuntimeWarning: invalid value encountered in divide
- array([ NaN, 1., 1.])
-
- """
- maskvalue = umath.geterrobj()[1]
- mask = 7
- res = {}
- val = (maskvalue >> SHIFT_DIVIDEBYZERO) & mask
- res['divide'] = _errdict_rev[val]
- val = (maskvalue >> SHIFT_OVERFLOW) & mask
- res['over'] = _errdict_rev[val]
- val = (maskvalue >> SHIFT_UNDERFLOW) & mask
- res['under'] = _errdict_rev[val]
- val = (maskvalue >> SHIFT_INVALID) & mask
- res['invalid'] = _errdict_rev[val]
- return res
-
-
-def setbufsize(size):
- """
- Set the size of the buffer used in ufuncs.
-
- Parameters
- ----------
- size : int
- Size of buffer.
-
- """
- if size > 10e6:
- raise ValueError("Buffer size, %s, is too big." % size)
- if size < 5:
- raise ValueError("Buffer size, %s, is too small." % size)
- if size % 16 != 0:
- raise ValueError("Buffer size, %s, is not a multiple of 16." % size)
-
- pyvals = umath.geterrobj()
- old = getbufsize()
- pyvals[0] = size
- umath.seterrobj(pyvals)
- return old
-
-
-def getbufsize():
- """
- Return the size of the buffer used in ufuncs.
-
- Returns
- -------
- getbufsize : int
- Size of ufunc buffer in bytes.
-
- """
- return umath.geterrobj()[0]
-
-
-def seterrcall(func):
- """
- Set the floating-point error callback function or log object.
-
- There are two ways to capture floating-point error messages. The first
- is to set the error-handler to 'call', using `seterr`. Then, set
- the function to call using this function.
-
- The second is to set the error-handler to 'log', using `seterr`.
- Floating-point errors then trigger a call to the 'write' method of
- the provided object.
-
- Parameters
- ----------
- func : callable f(err, flag) or object with write method
- Function to call upon floating-point errors ('call'-mode) or
- object whose 'write' method is used to log such message ('log'-mode).
-
- The call function takes two arguments. The first is a string describing
- the type of error (such as "divide by zero", "overflow", "underflow",
- or "invalid value"), and the second is the status flag. The flag is a
- byte, whose four least-significant bits indicate the type of error, one
- of "divide", "over", "under", "invalid"::
-
- [0 0 0 0 divide over under invalid]
-
- In other words, ``flags = divide + 2*over + 4*under + 8*invalid``.
-
- If an object is provided, its write method should take one argument,
- a string.
-
- Returns
- -------
- h : callable, log instance or None
- The old error handler.
-
- See Also
- --------
- seterr, geterr, geterrcall
-
- Examples
- --------
- Callback upon error:
-
- >>> def err_handler(type, flag):
- ... print("Floating point error (%s), with flag %s" % (type, flag))
- ...
-
- >>> saved_handler = np.seterrcall(err_handler)
- >>> save_err = np.seterr(all='call')
-
- >>> np.array([1, 2, 3]) / 0.0
- Floating point error (divide by zero), with flag 1
- array([ Inf, Inf, Inf])
-
- >>> np.seterrcall(saved_handler)
- <function err_handler at 0x...>
- >>> np.seterr(**save_err)
- {'over': 'call', 'divide': 'call', 'invalid': 'call', 'under': 'call'}
-
- Log error message:
-
- >>> class Log(object):
- ... def write(self, msg):
- ... print("LOG: %s" % msg)
- ...
-
- >>> log = Log()
- >>> saved_handler = np.seterrcall(log)
- >>> save_err = np.seterr(all='log')
-
- >>> np.array([1, 2, 3]) / 0.0
- LOG: Warning: divide by zero encountered in divide
- <BLANKLINE>
- array([ Inf, Inf, Inf])
-
- >>> np.seterrcall(saved_handler)
- <__main__.Log object at 0x...>
- >>> np.seterr(**save_err)
- {'over': 'log', 'divide': 'log', 'invalid': 'log', 'under': 'log'}
-
- """
- if func is not None and not isinstance(func, collections_abc.Callable):
- if not hasattr(func, 'write') or not isinstance(func.write, collections_abc.Callable):
- raise ValueError("Only callable can be used as callback")
- pyvals = umath.geterrobj()
- old = geterrcall()
- pyvals[2] = func
- umath.seterrobj(pyvals)
- return old
-
-
-def geterrcall():
- """
- Return the current callback function used on floating-point errors.
-
- When the error handling for a floating-point error (one of "divide",
- "over", "under", or "invalid") is set to 'call' or 'log', the function
- that is called or the log instance that is written to is returned by
- `geterrcall`. This function or log instance has been set with
- `seterrcall`.
-
- Returns
- -------
- errobj : callable, log instance or None
- The current error handler. If no handler was set through `seterrcall`,
- ``None`` is returned.
-
- See Also
- --------
- seterrcall, seterr, geterr
-
- Notes
- -----
- For complete documentation of the types of floating-point exceptions and
- treatment options, see `seterr`.
-
- Examples
- --------
- >>> np.geterrcall() # we did not yet set a handler, returns None
-
- >>> oldsettings = np.seterr(all='call')
- >>> def err_handler(type, flag):
- ... print("Floating point error (%s), with flag %s" % (type, flag))
- >>> oldhandler = np.seterrcall(err_handler)
- >>> np.array([1, 2, 3]) / 0.0
- Floating point error (divide by zero), with flag 1
- array([ Inf, Inf, Inf])
-
- >>> cur_handler = np.geterrcall()
- >>> cur_handler is err_handler
- True
-
- """
- return umath.geterrobj()[2]
-
-
-class _unspecified(object):
- pass
-
-
-_Unspecified = _unspecified()
-
-
-class errstate(object):
- """
- errstate(**kwargs)
-
- Context manager for floating-point error handling.
-
- Using an instance of `errstate` as a context manager allows statements in
- that context to execute with a known error handling behavior. Upon entering
- the context the error handling is set with `seterr` and `seterrcall`, and
- upon exiting it is reset to what it was before.
-
- Parameters
- ----------
- kwargs : {divide, over, under, invalid}
- Keyword arguments. The valid keywords are the possible floating-point
- exceptions. Each keyword should have a string value that defines the
- treatment for the particular error. Possible values are
- {'ignore', 'warn', 'raise', 'call', 'print', 'log'}.
-
- See Also
- --------
- seterr, geterr, seterrcall, geterrcall
-
- Notes
- -----
- For complete documentation of the types of floating-point exceptions and
- treatment options, see `seterr`.
-
- Examples
- --------
- >>> olderr = np.seterr(all='ignore') # Set error handling to known state.
-
- >>> np.arange(3) / 0.
- array([ NaN, Inf, Inf])
- >>> with np.errstate(divide='warn'):
- ... np.arange(3) / 0.
- ...
- __main__:2: RuntimeWarning: divide by zero encountered in divide
- array([ NaN, Inf, Inf])
-
- >>> np.sqrt(-1)
- nan
- >>> with np.errstate(invalid='raise'):
- ... np.sqrt(-1)
- Traceback (most recent call last):
- File "<stdin>", line 2, in <module>
- FloatingPointError: invalid value encountered in sqrt
-
- Outside the context the error handling behavior has not changed:
-
- >>> np.geterr()
- {'over': 'warn', 'divide': 'warn', 'invalid': 'warn',
- 'under': 'ignore'}
-
- """
- # Note that we don't want to run the above doctests because they will fail
- # without a from __future__ import with_statement
-
- def __init__(self, **kwargs):
- self.call = kwargs.pop('call', _Unspecified)
- self.kwargs = kwargs
-
- def __enter__(self):
- self.oldstate = seterr(**self.kwargs)
- if self.call is not _Unspecified:
- self.oldcall = seterrcall(self.call)
-
- def __exit__(self, *exc_info):
- seterr(**self.oldstate)
- if self.call is not _Unspecified:
- seterrcall(self.oldcall)
-
-
-def _setdef():
- defval = [UFUNC_BUFSIZE_DEFAULT, ERR_DEFAULT, None]
- umath.seterrobj(defval)
-
-
-# set the default values
-_setdef()
-
Inf = inf = infty = Infinity = PINF
nan = NaN = NAN
False_ = bool_(False)
@@ -2964,7 +2395,13 @@ from . import fromnumeric
from .fromnumeric import *
from . import arrayprint
from .arrayprint import *
+from . import _asarray
+from ._asarray import *
+from . import _ufunc_config
+from ._ufunc_config import *
extend_all(fromnumeric)
extend_all(umath)
extend_all(numerictypes)
extend_all(arrayprint)
+extend_all(_asarray)
+extend_all(_ufunc_config)
diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py
index 3ff9ceef0..ab1ff65a4 100644
--- a/numpy/core/numerictypes.py
+++ b/numpy/core/numerictypes.py
@@ -92,6 +92,7 @@ from numpy.core.multiarray import (
datetime_as_string, busday_offset, busday_count, is_busday,
busdaycalendar
)
+from numpy.core.overrides import set_module
# we add more at the bottom
__all__ = ['sctypeDict', 'sctypeNA', 'typeDict', 'typeNA', 'sctypes',
@@ -116,8 +117,8 @@ from ._type_aliases import (
_concrete_types,
_concrete_typeinfo,
_bits_of,
- _kind_to_stem,
)
+from ._dtype import _kind_name
# we don't export these for import *, but we do want them accessible
# as numerictypes.bool, etc.
@@ -139,6 +140,7 @@ genericTypeRank = ['bool', 'int8', 'uint8', 'int16', 'uint16',
'complex32', 'complex64', 'complex128', 'complex160',
'complex192', 'complex256', 'complex512', 'object']
+@set_module('numpy')
def maximum_sctype(t):
"""
Return the scalar type of highest precision of the same kind as the input.
@@ -162,32 +164,33 @@ def maximum_sctype(t):
Examples
--------
>>> np.maximum_sctype(int)
- <type 'numpy.int64'>
+ <class 'numpy.int64'>
>>> np.maximum_sctype(np.uint8)
- <type 'numpy.uint64'>
+ <class 'numpy.uint64'>
>>> np.maximum_sctype(complex)
- <type 'numpy.complex192'>
+ <class 'numpy.complex256'> # may vary
>>> np.maximum_sctype(str)
- <type 'numpy.string_'>
+ <class 'numpy.str_'>
>>> np.maximum_sctype('i2')
- <type 'numpy.int64'>
+ <class 'numpy.int64'>
>>> np.maximum_sctype('f4')
- <type 'numpy.float96'>
+ <class 'numpy.float128'> # may vary
"""
g = obj2sctype(t)
if g is None:
return t
t = g
- bits = _bits_of(t)
- base = _kind_to_stem[dtype(t).kind]
+ base = _kind_name(dtype(t))
if base in sctypes:
return sctypes[base][-1]
else:
return t
+
+@set_module('numpy')
def issctype(rep):
"""
Determines whether the given object represents a scalar data-type.
@@ -232,6 +235,8 @@ def issctype(rep):
except Exception:
return False
+
+@set_module('numpy')
def obj2sctype(rep, default=None):
"""
Return the scalar dtype or NumPy equivalent of Python type of an object.
@@ -256,22 +261,21 @@ def obj2sctype(rep, default=None):
Examples
--------
>>> np.obj2sctype(np.int32)
- <type 'numpy.int32'>
+ <class 'numpy.int32'>
>>> np.obj2sctype(np.array([1., 2.]))
- <type 'numpy.float64'>
+ <class 'numpy.float64'>
>>> np.obj2sctype(np.array([1.j]))
- <type 'numpy.complex128'>
+ <class 'numpy.complex128'>
>>> np.obj2sctype(dict)
- <type 'numpy.object_'>
+ <class 'numpy.object_'>
>>> np.obj2sctype('string')
- <type 'numpy.string_'>
>>> np.obj2sctype(1, default=list)
- <type 'list'>
+ <class 'list'>
"""
- # prevent abtract classes being upcast
+ # prevent abstract classes being upcast
if isinstance(rep, type) and issubclass(rep, generic):
return rep
# extract dtype from arrays
@@ -286,6 +290,7 @@ def obj2sctype(rep, default=None):
return res.type
+@set_module('numpy')
def issubclass_(arg1, arg2):
"""
Determine if a class is a subclass of a second class.
@@ -314,7 +319,7 @@ def issubclass_(arg1, arg2):
Examples
--------
>>> np.issubclass_(np.int32, int)
- True
+ False # True on Python 2.7
>>> np.issubclass_(np.int32, float)
False
@@ -324,6 +329,8 @@ def issubclass_(arg1, arg2):
except TypeError:
return False
+
+@set_module('numpy')
def issubsctype(arg1, arg2):
"""
Determine if the first argument is a subclass of the second argument.
@@ -340,12 +347,12 @@ def issubsctype(arg1, arg2):
See Also
--------
- issctype, issubdtype,obj2sctype
+ issctype, issubdtype, obj2sctype
Examples
--------
>>> np.issubsctype('S8', str)
- True
+ False
>>> np.issubsctype(np.array([1]), int)
True
>>> np.issubsctype(np.array([1]), float)
@@ -354,6 +361,8 @@ def issubsctype(arg1, arg2):
"""
return issubclass(obj2sctype(arg1), obj2sctype(arg2))
+
+@set_module('numpy')
def issubdtype(arg1, arg2):
"""
Returns True if first argument is a typecode lower/equal in type hierarchy.
@@ -447,6 +456,8 @@ def _construct_lookups():
_construct_lookups()
+
+@set_module('numpy')
def sctype2char(sctype):
"""
Return the string representation of a scalar dtype.
@@ -474,9 +485,9 @@ def sctype2char(sctype):
Examples
--------
- >>> for sctype in [np.int32, float, complex, np.string_, np.ndarray]:
+ >>> for sctype in [np.int32, np.double, np.complex, np.string_, np.ndarray]:
... print(np.sctype2char(sctype))
- l
+ l # may vary
d
D
S
@@ -587,6 +598,8 @@ def _register_types():
_register_types()
+
+@set_module('numpy')
def find_common_type(array_types, scalar_types):
"""
Determine common type following standard coercion rules.
diff --git a/numpy/core/overrides.py b/numpy/core/overrides.py
index 906292613..55c7bd1ea 100644
--- a/numpy/core/overrides.py
+++ b/numpy/core/overrides.py
@@ -1,69 +1,24 @@
-"""Preliminary implementation of NEP-18
-
-TODO: rewrite this in C for performance.
-"""
+"""Implementation of __array_function__ overrides from NEP-18."""
import collections
import functools
+import os
+import textwrap
-from numpy.core.multiarray import ndarray
+from numpy.core._multiarray_umath import (
+ add_docstring, implement_array_function, _get_implementing_args)
from numpy.compat._inspect import getargspec
-_NDARRAY_ARRAY_FUNCTION = ndarray.__array_function__
-
-
-def get_overloaded_types_and_args(relevant_args):
- """Returns a list of arguments on which to call __array_function__.
+ARRAY_FUNCTION_ENABLED = bool(
+ int(os.environ.get('NUMPY_EXPERIMENTAL_ARRAY_FUNCTION', 1)))
- Parameters
- ----------
- relevant_args : iterable of array-like
- Iterable of array-like arguments to check for __array_function__
- methods.
- Returns
- -------
- overloaded_types : collection of types
- Types of arguments from relevant_args with __array_function__ methods.
- overloaded_args : list
- Arguments from relevant_args on which to call __array_function__
- methods, in the order in which they should be called.
+add_docstring(
+ implement_array_function,
"""
- # Runtime is O(num_arguments * num_unique_types)
- overloaded_types = []
- overloaded_args = []
- for arg in relevant_args:
- arg_type = type(arg)
- # We only collect arguments if they have a unique type, which ensures
- # reasonable performance even with a long list of possibly overloaded
- # arguments.
- if (arg_type not in overloaded_types and
- hasattr(arg_type, '__array_function__')):
-
- overloaded_types.append(arg_type)
-
- # By default, insert this argument at the end, but if it is
- # subclass of another argument, insert it before that argument.
- # This ensures "subclasses before superclasses".
- index = len(overloaded_args)
- for i, old_arg in enumerate(overloaded_args):
- if issubclass(arg_type, type(old_arg)):
- index = i
- break
- overloaded_args.insert(index, arg)
-
- # Special handling for ndarray.__array_function__
- overloaded_args = [
- arg for arg in overloaded_args
- if type(arg).__array_function__ is not _NDARRAY_ARRAY_FUNCTION
- ]
-
- return overloaded_types, overloaded_args
-
-
-def array_function_implementation_or_override(
- implementation, public_api, relevant_args, args, kwargs):
- """Implement a function with checks for __array_function__ overrides.
+ Implement a function with checks for __array_function__ overrides.
+
+ All arguments are required, and can only be passed by position.
Arguments
---------
@@ -71,43 +26,44 @@ def array_function_implementation_or_override(
Function that implements the operation on NumPy array without
overrides when called like ``implementation(*args, **kwargs)``.
public_api : function
- Function exposed by NumPy's public API riginally called like
- ``public_api(*args, **kwargs`` on which arguments are now being
+ Function exposed by NumPy's public API originally called like
+ ``public_api(*args, **kwargs)`` on which arguments are now being
checked.
relevant_args : iterable
Iterable of arguments to check for __array_function__ methods.
args : tuple
Arbitrary positional arguments originally passed into ``public_api``.
- kwargs : tuple
+ kwargs : dict
Arbitrary keyword arguments originally passed into ``public_api``.
Returns
-------
- Result from calling `implementation()` or an `__array_function__`
+ Result from calling ``implementation()`` or an ``__array_function__``
method, as appropriate.
Raises
------
TypeError : if no implementation is found.
- """
- # Check for __array_function__ methods.
- types, overloaded_args = get_overloaded_types_and_args(relevant_args)
- if not overloaded_args:
- return implementation(*args, **kwargs)
+ """)
+
- # Call overrides
- for overloaded_arg in overloaded_args:
- # Use `public_api` instead of `implemenation` so __array_function__
- # implementations can do equality/identity comparisons.
- result = overloaded_arg.__array_function__(
- public_api, types, args, kwargs)
+# exposed for testing purposes; used internally by implement_array_function
+add_docstring(
+ _get_implementing_args,
+ """
+ Collect arguments on which to call __array_function__.
- if result is not NotImplemented:
- return result
+ Parameters
+ ----------
+ relevant_args : iterable of array-like
+ Iterable of possibly array-like arguments to check for
+ __array_function__ methods.
- raise TypeError('no implementation found for {} on types that implement '
- '__array_function__: {}'
- .format(public_api, list(map(type, overloaded_args))))
+ Returns
+ -------
+ Sequence of arguments with __array_function__ methods, in the order in
+ which they should be called.
+ """)
ArgSpec = collections.namedtuple('ArgSpec', 'args varargs keywords defaults')
@@ -135,20 +91,120 @@ def verify_matching_signatures(implementation, dispatcher):
'default argument values')
-def array_function_dispatch(dispatcher, verify=True):
- """Decorator for adding dispatch with the __array_function__ protocol."""
+def set_module(module):
+ """Decorator for overriding __module__ on a function or class.
+
+ Example usage::
+
+ @set_module('numpy')
+ def example():
+ pass
+
+ assert example.__module__ == 'numpy'
+ """
+ def decorator(func):
+ if module is not None:
+ func.__module__ = module
+ return func
+ return decorator
+
+
+
+# Call textwrap.dedent here instead of in the function so as to avoid
+# calling dedent multiple times on the same text
+_wrapped_func_source = textwrap.dedent("""
+ @functools.wraps(implementation)
+ def {name}(*args, **kwargs):
+ relevant_args = dispatcher(*args, **kwargs)
+ return implement_array_function(
+ implementation, {name}, relevant_args, args, kwargs)
+ """)
+
+
+def array_function_dispatch(dispatcher, module=None, verify=True,
+ docs_from_dispatcher=False):
+ """Decorator for adding dispatch with the __array_function__ protocol.
+
+ See NEP-18 for example usage.
+
+ Parameters
+ ----------
+ dispatcher : callable
+ Function that when called like ``dispatcher(*args, **kwargs)`` with
+ arguments from the NumPy function call returns an iterable of
+ array-like arguments to check for ``__array_function__``.
+ module : str, optional
+ __module__ attribute to set on new function, e.g., ``module='numpy'``.
+ By default, module is copied from the decorated function.
+ verify : bool, optional
+ If True, verify the that the signature of the dispatcher and decorated
+ function signatures match exactly: all required and optional arguments
+ should appear in order with the same names, but the default values for
+ all optional arguments should be ``None``. Only disable verification
+ if the dispatcher's signature needs to deviate for some particular
+ reason, e.g., because the function has a signature like
+ ``func(*args, **kwargs)``.
+ docs_from_dispatcher : bool, optional
+ If True, copy docs from the dispatcher function onto the dispatched
+ function, rather than from the implementation. This is useful for
+ functions defined in C, which otherwise don't have docstrings.
+
+ Returns
+ -------
+ Function suitable for decorating the implementation of a NumPy function.
+ """
+
+ if not ARRAY_FUNCTION_ENABLED:
+ def decorator(implementation):
+ if docs_from_dispatcher:
+ add_docstring(implementation, dispatcher.__doc__)
+ if module is not None:
+ implementation.__module__ = module
+ return implementation
+ return decorator
+
def decorator(implementation):
- # TODO: only do this check when the appropriate flag is enabled or for
- # a dev install. We want this check for testing but don't want to
- # slow down all numpy imports.
if verify:
verify_matching_signatures(implementation, dispatcher)
- @functools.wraps(implementation)
- def public_api(*args, **kwargs):
- relevant_args = dispatcher(*args, **kwargs)
- return array_function_implementation_or_override(
- implementation, public_api, relevant_args, args, kwargs)
+ if docs_from_dispatcher:
+ add_docstring(implementation, dispatcher.__doc__)
+
+ # Equivalently, we could define this function directly instead of using
+ # exec. This version has the advantage of giving the helper function a
+ # more interpettable name. Otherwise, the original function does not
+ # show up at all in many cases, e.g., if it's written in C or if the
+ # dispatcher gets an invalid keyword argument.
+ source = _wrapped_func_source.format(name=implementation.__name__)
+
+ source_object = compile(
+ source, filename='<__array_function__ internals>', mode='exec')
+ scope = {
+ 'implementation': implementation,
+ 'dispatcher': dispatcher,
+ 'functools': functools,
+ 'implement_array_function': implement_array_function,
+ }
+ exec(source_object, scope)
+
+ public_api = scope[implementation.__name__]
+
+ if module is not None:
+ public_api.__module__ = module
+
+ public_api._implementation = implementation
+
return public_api
return decorator
+
+
+def array_function_from_dispatcher(
+ implementation, module=None, verify=True, docs_from_dispatcher=True):
+ """Like array_function_dispatcher, but with function arguments flipped."""
+
+ def decorator(dispatcher):
+ return array_function_dispatch(
+ dispatcher, module, verify=verify,
+ docs_from_dispatcher=docs_from_dispatcher)(implementation)
+ return decorator
diff --git a/numpy/core/records.py b/numpy/core/records.py
index a483871ba..a1439f9df 100644
--- a/numpy/core/records.py
+++ b/numpy/core/records.py
@@ -7,10 +7,9 @@ Most commonly, ndarrays contain elements of a single type, e.g. floats,
integers, bools etc. However, it is possible for elements to be combinations
of these using structured types, such as::
- >>> a = np.array([(1, 2.0), (1, 2.0)], dtype=[('x', int), ('y', float)])
+ >>> a = np.array([(1, 2.0), (1, 2.0)], dtype=[('x', np.int64), ('y', np.float64)])
>>> a
- array([(1, 2.0), (1, 2.0)],
- dtype=[('x', '<i4'), ('y', '<f8')])
+ array([(1, 2.), (1, 2.)], dtype=[('x', '<i8'), ('y', '<f8')])
Here, each element consists of two fields: x (and int), and y (a float).
This is known as a structured array. The different fields are analogous
@@ -21,7 +20,7 @@ one would a dictionary::
array([1, 1])
>>> a['y']
- array([ 2., 2.])
+ array([2., 2.])
Record arrays allow us to access fields as properties::
@@ -31,7 +30,7 @@ Record arrays allow us to access fields as properties::
array([1, 1])
>>> ar.y
- array([ 2., 2.])
+ array([2., 2.])
"""
from __future__ import division, absolute_import, print_function
@@ -39,10 +38,14 @@ from __future__ import division, absolute_import, print_function
import sys
import os
import warnings
+from collections import Counter, OrderedDict
from . import numeric as sb
from . import numerictypes as nt
-from numpy.compat import isfileobj, bytes, long, unicode
+from numpy.compat import (
+ isfileobj, bytes, long, unicode, os_fspath, contextlib_nullcontext
+)
+from numpy.core.overrides import set_module
from .arrayprint import get_printoptions
# All of the functions allow formats to be a dtype
@@ -73,15 +76,28 @@ _byteorderconv = {'b':'>',
numfmt = nt.typeDict
+# taken from OrderedDict recipes in the Python documentation
+# https://docs.python.org/3.3/library/collections.html#ordereddict-examples-and-recipes
+class _OrderedCounter(Counter, OrderedDict):
+ """Counter that remembers the order elements are first encountered"""
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
+
+ def __reduce__(self):
+ return self.__class__, (OrderedDict(self),)
+
+
def find_duplicate(list):
"""Find duplication in a list, return a list of duplicated elements"""
- dup = []
- for i in range(len(list)):
- if (list[i] in list[i + 1:]):
- if (list[i] not in dup):
- dup.append(list[i])
- return dup
+ return [
+ item
+ for item, counts in _OrderedCounter(list).items()
+ if counts > 1
+ ]
+
+@set_module('numpy')
class format_parser(object):
"""
Class to convert formats, names, titles description to a dtype.
@@ -125,10 +141,9 @@ class format_parser(object):
Examples
--------
- >>> np.format_parser(['f8', 'i4', 'a5'], ['col1', 'col2', 'col3'],
+ >>> np.format_parser(['<f8', '<i4', '<a5'], ['col1', 'col2', 'col3'],
... ['T1', 'T2', 'T3']).dtype
- dtype([(('T1', 'col1'), '<f8'), (('T2', 'col2'), '<i4'),
- (('T3', 'col3'), '|S5')])
+ dtype([(('T1', 'col1'), '<f8'), (('T2', 'col2'), '<i4'), (('T3', 'col3'), 'S5')])
`names` and/or `titles` can be empty lists. If `titles` is an empty list,
titles will simply not appear. If `names` is empty, default field names
@@ -136,9 +151,9 @@ class format_parser(object):
>>> np.format_parser(['f8', 'i4', 'a5'], ['col1', 'col2', 'col3'],
... []).dtype
- dtype([('col1', '<f8'), ('col2', '<i4'), ('col3', '|S5')])
- >>> np.format_parser(['f8', 'i4', 'a5'], [], []).dtype
- dtype([('f0', '<f8'), ('f1', '<i4'), ('f2', '|S5')])
+ dtype([('col1', '<f8'), ('col2', '<i4'), ('col3', '<S5')])
+ >>> np.format_parser(['<f8', '<i4', '<a5'], [], []).dtype
+ dtype([('f0', '<f8'), ('f1', '<i4'), ('f2', 'S5')])
"""
@@ -148,16 +163,18 @@ class format_parser(object):
self._createdescr(byteorder)
self.dtype = self._descr
- def _parseFormats(self, formats, aligned=0):
+ def _parseFormats(self, formats, aligned=False):
""" Parse the field formats """
if formats is None:
raise ValueError("Need formats argument")
if isinstance(formats, list):
- if len(formats) < 2:
- formats.append('')
- formats = ','.join(formats)
- dtype = sb.dtype(formats, aligned)
+ dtype = sb.dtype(
+ [('f{}'.format(i), format_) for i, format_ in enumerate(formats)],
+ aligned,
+ )
+ else:
+ dtype = sb.dtype(formats, aligned)
fields = dtype.fields
if fields is None:
dtype = sb.dtype([('f1', dtype)], aligned)
@@ -251,8 +268,8 @@ class record(nt.void):
except AttributeError:
#happens if field is Object type
return obj
- if dt.fields:
- return obj.view((self.__class__, obj.dtype.fields))
+ if dt.names is not None:
+ return obj.view((self.__class__, obj.dtype))
return obj
else:
raise AttributeError("'record' object has no "
@@ -276,8 +293,8 @@ class record(nt.void):
obj = nt.void.__getitem__(self, indx)
# copy behavior of record.__getattribute__,
- if isinstance(obj, nt.void) and obj.dtype.fields:
- return obj.view((self.__class__, obj.dtype.fields))
+ if isinstance(obj, nt.void) and obj.dtype.names is not None:
+ return obj.view((self.__class__, obj.dtype))
else:
# return a single element
return obj
@@ -287,10 +304,8 @@ class record(nt.void):
# pretty-print all fields
names = self.dtype.names
maxlen = max(len(name) for name in names)
- rows = []
fmt = '%% %ds: %%s' % maxlen
- for name in names:
- rows.append(fmt % (name, getattr(self, name)))
+ rows = [fmt % (name, getattr(self, name)) for name in names]
return "\n".join(rows)
# The recarray is almost identical to a standard array (which supports
@@ -379,20 +394,19 @@ class recarray(ndarray):
--------
Create an array with two fields, ``x`` and ``y``:
- >>> x = np.array([(1.0, 2), (3.0, 4)], dtype=[('x', float), ('y', int)])
+ >>> x = np.array([(1.0, 2), (3.0, 4)], dtype=[('x', '<f8'), ('y', '<i8')])
>>> x
- array([(1.0, 2), (3.0, 4)],
- dtype=[('x', '<f8'), ('y', '<i4')])
+ array([(1., 2), (3., 4)], dtype=[('x', '<f8'), ('y', '<i8')])
>>> x['x']
- array([ 1., 3.])
+ array([1., 3.])
View the array as a record array:
>>> x = x.view(np.recarray)
>>> x.x
- array([ 1., 3.])
+ array([1., 3.])
>>> x.y
array([2, 4])
@@ -430,7 +444,7 @@ class recarray(ndarray):
return self
def __array_finalize__(self, obj):
- if self.dtype.type is not record and self.dtype.fields:
+ if self.dtype.type is not record and self.dtype.names is not None:
# if self.dtype is not np.record, invoke __setattr__ which will
# convert it to a record if it is a void dtype.
self.dtype = self.dtype
@@ -458,7 +472,7 @@ class recarray(ndarray):
# with void type convert it to the same dtype.type (eg to preserve
# numpy.record type if present), since nested structured fields do not
# inherit type. Don't do this for non-void structures though.
- if obj.dtype.fields:
+ if obj.dtype.names is not None:
if issubclass(obj.dtype.type, nt.void):
return obj.view(dtype=(self.dtype.type, obj.dtype))
return obj
@@ -473,7 +487,7 @@ class recarray(ndarray):
# Automatically convert (void) structured types to records
# (but not non-void structures, subarrays, or non-structured voids)
- if attr == 'dtype' and issubclass(val.type, nt.void) and val.fields:
+ if attr == 'dtype' and issubclass(val.type, nt.void) and val.names is not None:
val = sb.dtype((record, val))
newattr = attr not in self.__dict__
@@ -507,7 +521,7 @@ class recarray(ndarray):
# copy behavior of getattr, except that here
# we might also be returning a single element
if isinstance(obj, ndarray):
- if obj.dtype.fields:
+ if obj.dtype.names is not None:
obj = obj.view(type(self))
if issubclass(obj.dtype.type, nt.void):
return obj.view(dtype=(self.dtype.type, obj.dtype))
@@ -563,7 +577,7 @@ class recarray(ndarray):
if val is None:
obj = self.getfield(*res)
- if obj.dtype.fields:
+ if obj.dtype.names is not None:
return obj
return obj.view(ndarray)
else:
@@ -579,7 +593,7 @@ def fromarrays(arrayList, dtype=None, shape=None, formats=None,
>>> x3=np.array([1.1,2,3,4])
>>> r = np.core.records.fromarrays([x1,x2,x3],names='a,b,c')
>>> print(r[1])
- (2, 'dd', 2.0)
+ (2, 'dd', 2.0) # may vary
>>> x1[1]=34
>>> r.a
array([1, 2, 3, 4])
@@ -598,10 +612,7 @@ def fromarrays(arrayList, dtype=None, shape=None, formats=None,
# and determine the formats.
formats = []
for obj in arrayList:
- if not isinstance(obj, ndarray):
- raise ValueError("item in the array list must be an ndarray.")
- formats.append(obj.dtype.str)
- formats = ','.join(formats)
+ formats.append(obj.dtype)
if dtype is not None:
descr = sb.dtype(dtype)
@@ -658,11 +669,11 @@ def fromrecords(recList, dtype=None, shape=None, formats=None, names=None,
>>> r.col1
array([456, 2])
>>> r.col2
- array(['dbe', 'de'],
- dtype='|S3')
+ array(['dbe', 'de'], dtype='<U3')
>>> import pickle
- >>> print(pickle.loads(pickle.dumps(r)))
- [(456, 'dbe', 1.2) (2, 'de', 1.3)]
+ >>> pickle.loads(pickle.dumps(r))
+ rec.array([(456, 'dbe', 1.2), ( 2, 'de', 1.3)],
+ dtype=[('col1', '<i8'), ('col2', '<U3'), ('col3', '<f8')])
"""
if formats is None and dtype is None: # slower
@@ -710,7 +721,7 @@ def fromstring(datastring, dtype=None, shape=None, offset=0, formats=None,
a string"""
if dtype is None and formats is None:
- raise ValueError("Must have dtype= or formats=")
+ raise TypeError("fromstring() needs a 'dtype' or 'formats' argument")
if dtype is not None:
descr = sb.dtype(dtype)
@@ -737,9 +748,9 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None,
names=None, titles=None, aligned=False, byteorder=None):
"""Create an array from binary file data
- If file is a string then that file is opened, else it is assumed
- to be a file object. The file object must support random access
- (i.e. it must have tell and seek methods).
+ If file is a string or a path-like object then that file is opened,
+ else it is assumed to be a file object. The file object must
+ support random access (i.e. it must have tell and seek methods).
>>> from tempfile import TemporaryFile
>>> a = np.empty(10,dtype='f8,i4,a5')
@@ -749,7 +760,7 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None,
>>> a = a.newbyteorder('<')
>>> a.tofile(fd)
>>>
- >>> fd.seek(0)
+ >>> _ = fd.seek(0)
>>> r=np.core.records.fromfile(fd, formats='f8,i4,a5', shape=10,
... byteorder='<')
>>> print(r[5])
@@ -758,47 +769,52 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None,
(10,)
"""
+ if dtype is None and formats is None:
+ raise TypeError("fromfile() needs a 'dtype' or 'formats' argument")
+
if (shape is None or shape == 0):
shape = (-1,)
elif isinstance(shape, (int, long)):
shape = (shape,)
- name = 0
- if isinstance(fd, str):
- name = 1
- fd = open(fd, 'rb')
- if (offset > 0):
- fd.seek(offset, 1)
- size = get_remaining_size(fd)
-
- if dtype is not None:
- descr = sb.dtype(dtype)
+ if isfileobj(fd):
+ # file already opened
+ ctx = contextlib_nullcontext(fd)
else:
- descr = format_parser(formats, names, titles, aligned, byteorder)._descr
+ # open file
+ ctx = open(os_fspath(fd), 'rb')
- itemsize = descr.itemsize
+ with ctx as fd:
+ if (offset > 0):
+ fd.seek(offset, 1)
+ size = get_remaining_size(fd)
- shapeprod = sb.array(shape).prod()
- shapesize = shapeprod * itemsize
- if shapesize < 0:
- shape = list(shape)
- shape[shape.index(-1)] = size / -shapesize
- shape = tuple(shape)
- shapeprod = sb.array(shape).prod()
+ if dtype is not None:
+ descr = sb.dtype(dtype)
+ else:
+ descr = format_parser(formats, names, titles, aligned, byteorder)._descr
- nbytes = shapeprod * itemsize
+ itemsize = descr.itemsize
- if nbytes > size:
- raise ValueError(
- "Not enough bytes left in file for specified shape and type")
+ shapeprod = sb.array(shape).prod(dtype=nt.intp)
+ shapesize = shapeprod * itemsize
+ if shapesize < 0:
+ shape = list(shape)
+ shape[shape.index(-1)] = size // -shapesize
+ shape = tuple(shape)
+ shapeprod = sb.array(shape).prod(dtype=nt.intp)
- # create the array
- _array = recarray(shape, descr)
- nbytesread = fd.readinto(_array.data)
- if nbytesread != nbytes:
- raise IOError("Didn't read as many bytes as expected")
- if name:
- fd.close()
+ nbytes = shapeprod * itemsize
+
+ if nbytes > size:
+ raise ValueError(
+ "Not enough bytes left in file for specified shape and type")
+
+ # create the array
+ _array = recarray(shape, descr)
+ nbytesread = fd.readinto(_array.data)
+ if nbytesread != nbytes:
+ raise IOError("Didn't read as many bytes as expected")
return _array
diff --git a/numpy/core/setup.py b/numpy/core/setup.py
index bea9ff392..5f2f4a7b2 100644
--- a/numpy/core/setup.py
+++ b/numpy/core/setup.py
@@ -6,7 +6,9 @@ import pickle
import copy
import warnings
import platform
+import textwrap
from os.path import join
+
from numpy.distutils import log
from distutils.dep_util import newer
from distutils.sysconfig import get_config_var
@@ -171,6 +173,11 @@ def check_math_capabilities(config, moredefs, mathlibs):
if config.check_gcc_function_attribute(dec, fn):
moredefs.append((fname2def(fn), 1))
+ for dec, fn, code, header in OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS:
+ if config.check_gcc_function_attribute_with_intrinsics(dec, fn, code,
+ header):
+ moredefs.append((fname2def(fn), 1))
+
for fn in OPTIONAL_VARIABLE_ATTRIBUTES:
if config.check_gcc_variable_attribute(fn):
m = fn.replace("(", "_").replace(")", "_")
@@ -379,8 +386,9 @@ def check_mathlib(config_cmd):
def visibility_define(config):
"""Return the define value to use for NPY_VISIBILITY_HIDDEN (may be empty
string)."""
- if config.check_compiler_gcc4():
- return '__attribute__((visibility("hidden")))'
+ hide = '__attribute__((visibility("hidden")))'
+ if config.check_gcc_function_attribute(hide, 'hideme'):
+ return hide
else:
return ''
@@ -455,50 +463,53 @@ def configuration(parent_package='',top_path=None):
rep = check_long_double_representation(config_cmd)
moredefs.append(('HAVE_LDOUBLE_%s' % rep, 1))
+ if check_for_right_shift_internal_compiler_error(config_cmd):
+ moredefs.append('NPY_DO_NOT_OPTIMIZE_LONG_right_shift')
+ moredefs.append('NPY_DO_NOT_OPTIMIZE_ULONG_right_shift')
+ moredefs.append('NPY_DO_NOT_OPTIMIZE_LONGLONG_right_shift')
+ moredefs.append('NPY_DO_NOT_OPTIMIZE_ULONGLONG_right_shift')
+
# Py3K check
- if sys.version_info[0] == 3:
+ if sys.version_info[0] >= 3:
moredefs.append(('NPY_PY3K', 1))
# Generate the config.h file from moredefs
- target_f = open(target, 'w')
- for d in moredefs:
- if isinstance(d, str):
- target_f.write('#define %s\n' % (d))
+ with open(target, 'w') as target_f:
+ for d in moredefs:
+ if isinstance(d, str):
+ target_f.write('#define %s\n' % (d))
+ else:
+ target_f.write('#define %s %s\n' % (d[0], d[1]))
+
+ # define inline to our keyword, or nothing
+ target_f.write('#ifndef __cplusplus\n')
+ if inline == 'inline':
+ target_f.write('/* #undef inline */\n')
else:
- target_f.write('#define %s %s\n' % (d[0], d[1]))
-
- # define inline to our keyword, or nothing
- target_f.write('#ifndef __cplusplus\n')
- if inline == 'inline':
- target_f.write('/* #undef inline */\n')
- else:
- target_f.write('#define inline %s\n' % inline)
- target_f.write('#endif\n')
-
- # add the guard to make sure config.h is never included directly,
- # but always through npy_config.h
- target_f.write("""
-#ifndef _NPY_NPY_CONFIG_H_
-#error config.h should never be included directly, include npy_config.h instead
-#endif
-""")
-
- target_f.close()
- print('File:', target)
- target_f = open(target)
- print(target_f.read())
- target_f.close()
- print('EOF')
+ target_f.write('#define inline %s\n' % inline)
+ target_f.write('#endif\n')
+
+ # add the guard to make sure config.h is never included directly,
+ # but always through npy_config.h
+ target_f.write(textwrap.dedent("""
+ #ifndef _NPY_NPY_CONFIG_H_
+ #error config.h should never be included directly, include npy_config.h instead
+ #endif
+ """))
+
+ log.info('File: %s' % target)
+ with open(target) as target_f:
+ log.info(target_f.read())
+ log.info('EOF')
else:
mathlibs = []
- target_f = open(target)
- for line in target_f:
- s = '#define MATHLIB'
- if line.startswith(s):
- value = line[len(s):].strip()
- if value:
- mathlibs.extend(value.split(','))
- target_f.close()
+ with open(target) as target_f:
+ for line in target_f:
+ s = '#define MATHLIB'
+ if line.startswith(s):
+ value = line[len(s):].strip()
+ if value:
+ mathlibs.extend(value.split(','))
# Ugly: this can be called within a library and not an extension,
# in which case there is no libraries attributes (and none is
@@ -561,27 +572,25 @@ def configuration(parent_package='',top_path=None):
moredefs.append(('NPY_API_VERSION', '0x%.8X' % C_API_VERSION))
# Add moredefs to header
- target_f = open(target, 'w')
- for d in moredefs:
- if isinstance(d, str):
- target_f.write('#define %s\n' % (d))
- else:
- target_f.write('#define %s %s\n' % (d[0], d[1]))
-
- # Define __STDC_FORMAT_MACROS
- target_f.write("""
-#ifndef __STDC_FORMAT_MACROS
-#define __STDC_FORMAT_MACROS 1
-#endif
-""")
- target_f.close()
+ with open(target, 'w') as target_f:
+ for d in moredefs:
+ if isinstance(d, str):
+ target_f.write('#define %s\n' % (d))
+ else:
+ target_f.write('#define %s %s\n' % (d[0], d[1]))
+
+ # Define __STDC_FORMAT_MACROS
+ target_f.write(textwrap.dedent("""
+ #ifndef __STDC_FORMAT_MACROS
+ #define __STDC_FORMAT_MACROS 1
+ #endif
+ """))
# Dump the numpyconfig.h header to stdout
- print('File: %s' % target)
- target_f = open(target)
- print(target_f.read())
- target_f.close()
- print('EOF')
+ log.info('File: %s' % target)
+ with open(target) as target_f:
+ log.info(target_f.read())
+ log.info('EOF')
config.add_data_files((header_dir, target))
return target
@@ -607,7 +616,7 @@ def configuration(parent_package='',top_path=None):
config.add_include_dirs(join(local_dir, "src"))
config.add_include_dirs(join(local_dir))
- config.add_data_files('include/numpy/*.h')
+ config.add_data_dir('include/numpy')
config.add_include_dirs(join('src', 'npymath'))
config.add_include_dirs(join('src', 'multiarray'))
config.add_include_dirs(join('src', 'umath'))
@@ -630,23 +639,6 @@ def configuration(parent_package='',top_path=None):
]
#######################################################################
- # dummy module #
- #######################################################################
-
- # npymath needs the config.h and numpyconfig.h files to be generated, but
- # build_clib cannot handle generate_config_h and generate_numpyconfig_h
- # (don't ask). Because clib are generated before extensions, we have to
- # explicitly add an extension which has generate_config_h and
- # generate_numpyconfig_h as sources *before* adding npymath.
-
- config.add_extension('_dummy',
- sources=[join('src', 'dummymodule.c'),
- generate_config_h,
- generate_numpyconfig_h,
- generate_numpy_api]
- )
-
- #######################################################################
# npymath library #
#######################################################################
@@ -677,9 +669,11 @@ def configuration(parent_package='',top_path=None):
join('src', 'npymath', 'npy_math_complex.c.src'),
join('src', 'npymath', 'halffloat.c')
]
-
+
# Must be true for CRT compilers but not MinGW/cygwin. See gh-9977.
- is_msvc = platform.system() == 'Windows'
+ # Intel and Clang also don't seem happy with /GL
+ is_msvc = (platform.platform().startswith('Windows') and
+ platform.python_compiler().startswith('MS'))
config.add_installed_library('npymath',
sources=npymath_sources + [get_mathlib_info],
install_dir='lib',
@@ -697,9 +691,12 @@ def configuration(parent_package='',top_path=None):
#######################################################################
# This library is created for the build but it is not installed
- npysort_sources = [join('src', 'npysort', 'quicksort.c.src'),
+ npysort_sources = [join('src', 'common', 'npy_sort.h.src'),
+ join('src', 'npysort', 'quicksort.c.src'),
join('src', 'npysort', 'mergesort.c.src'),
+ join('src', 'npysort', 'timsort.c.src'),
join('src', 'npysort', 'heapsort.c.src'),
+ join('src', 'npysort', 'radixsort.c.src'),
join('src', 'common', 'npy_partition.h.src'),
join('src', 'npysort', 'selection.c.src'),
join('src', 'common', 'npy_binsearch.h.src'),
@@ -730,13 +727,17 @@ def configuration(parent_package='',top_path=None):
join('src', 'common', 'cblasfuncs.h'),
join('src', 'common', 'lowlevel_strided_loops.h'),
join('src', 'common', 'mem_overlap.h'),
+ join('src', 'common', 'npy_cblas.h'),
join('src', 'common', 'npy_config.h'),
+ join('src', 'common', 'npy_ctypes.h'),
join('src', 'common', 'npy_extint128.h'),
+ join('src', 'common', 'npy_import.h'),
join('src', 'common', 'npy_longdouble.h'),
join('src', 'common', 'templ_common.h.src'),
join('src', 'common', 'ucsnarrow.h'),
join('src', 'common', 'ufunc_override.h'),
join('src', 'common', 'umathmodule.h'),
+ join('src', 'common', 'numpyos.h'),
]
common_src = [
@@ -746,6 +747,7 @@ def configuration(parent_package='',top_path=None):
join('src', 'common', 'templ_common.h.src'),
join('src', 'common', 'ucsnarrow.c'),
join('src', 'common', 'ufunc_override.c'),
+ join('src', 'common', 'numpyos.c'),
]
blas_info = get_info('blas_opt', 0)
@@ -768,6 +770,7 @@ def configuration(parent_package='',top_path=None):
multiarray_deps = [
join('src', 'multiarray', 'arrayobject.h'),
join('src', 'multiarray', 'arraytypes.h'),
+ join('src', 'multiarray', 'arrayfunction_override.h'),
join('src', 'multiarray', 'buffer.h'),
join('src', 'multiarray', 'calculation.h'),
join('src', 'multiarray', 'common.h'),
@@ -785,7 +788,6 @@ def configuration(parent_package='',top_path=None):
join('src', 'multiarray', 'multiarraymodule.h'),
join('src', 'multiarray', 'nditer_impl.h'),
join('src', 'multiarray', 'number.h'),
- join('src', 'multiarray', 'numpyos.h'),
join('src', 'multiarray', 'refcount.h'),
join('src', 'multiarray', 'scalartypes.h'),
join('src', 'multiarray', 'sequence.h'),
@@ -821,6 +823,7 @@ def configuration(parent_package='',top_path=None):
join('src', 'multiarray', 'arraytypes.c.src'),
join('src', 'multiarray', 'array_assign_scalar.c'),
join('src', 'multiarray', 'array_assign_array.c'),
+ join('src', 'multiarray', 'arrayfunction_override.c'),
join('src', 'multiarray', 'buffer.c'),
join('src', 'multiarray', 'calculation.c'),
join('src', 'multiarray', 'compiled_base.c'),
@@ -851,7 +854,6 @@ def configuration(parent_package='',top_path=None):
join('src', 'multiarray', 'nditer_constr.c'),
join('src', 'multiarray', 'nditer_pywrap.c'),
join('src', 'multiarray', 'number.c'),
- join('src', 'multiarray', 'numpyos.c'),
join('src', 'multiarray', 'refcount.c'),
join('src', 'multiarray', 'sequence.c'),
join('src', 'multiarray', 'shape.c'),
@@ -875,10 +877,9 @@ def configuration(parent_package='',top_path=None):
os.makedirs(dir)
script = generate_umath_py
if newer(script, target):
- f = open(target, 'w')
- f.write(generate_umath.make_code(generate_umath.defdict,
- generate_umath.__file__))
- f.close()
+ with open(target, 'w') as f:
+ f.write(generate_umath.make_code(generate_umath.defdict,
+ generate_umath.__file__))
return []
umath_src = [
@@ -888,6 +889,10 @@ def configuration(parent_package='',top_path=None):
join('src', 'umath', 'simd.inc.src'),
join('src', 'umath', 'loops.h.src'),
join('src', 'umath', 'loops.c.src'),
+ join('src', 'umath', 'matmul.h.src'),
+ join('src', 'umath', 'matmul.c.src'),
+ join('src', 'umath', 'clip.h.src'),
+ join('src', 'umath', 'clip.c.src'),
join('src', 'umath', 'ufunc_object.c'),
join('src', 'umath', 'extobj.c'),
join('src', 'umath', 'cpuid.c'),
@@ -901,14 +906,15 @@ def configuration(parent_package='',top_path=None):
join('include', 'numpy', 'npy_math.h'),
join('include', 'numpy', 'halffloat.h'),
join('src', 'multiarray', 'common.h'),
+ join('src', 'multiarray', 'number.h'),
join('src', 'common', 'templ_common.h.src'),
join('src', 'umath', 'simd.inc.src'),
join('src', 'umath', 'override.h'),
join(codegen_dir, 'generate_ufunc_api.py'),
- ]
+ ]
config.add_extension('_multiarray_umath',
- sources=multiarray_src + umath_src +
+ sources=multiarray_src + umath_src +
npymath_sources + common_src +
[generate_config_h,
generate_numpyconfig_h,
@@ -918,7 +924,7 @@ def configuration(parent_package='',top_path=None):
generate_umath_c,
generate_ufunc_api,
],
- depends=deps + multiarray_deps + umath_deps +
+ depends=deps + multiarray_deps + umath_deps +
common_deps,
libraries=['npymath', 'npysort'],
extra_info=extra_info)
diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py
index e637dbc20..84b78b585 100644
--- a/numpy/core/setup_common.py
+++ b/numpy/core/setup_common.py
@@ -5,6 +5,7 @@ import sys
import warnings
import copy
import binascii
+import textwrap
from numpy.distutils.misc_util import mingw32
@@ -14,7 +15,7 @@ from numpy.distutils.misc_util import mingw32
#-------------------
# How to change C_API_VERSION ?
# - increase C_API_VERSION value
-# - record the hash for the new C API with the script cversions.py
+# - record the hash for the new C API with the cversions.py script
# and add the hash to cversions.txt
# The hash values are used to remind developers when the C API number was not
# updated - generates a MismatchCAPIWarning warning which is turned into an
@@ -41,7 +42,8 @@ C_ABI_VERSION = 0x01000009
# 0x0000000b - 1.13.x
# 0x0000000c - 1.14.x
# 0x0000000c - 1.15.x
-C_API_VERSION = 0x0000000c
+# 0x0000000d - 1.16.x
+C_API_VERSION = 0x0000000d
class MismatchCAPIWarning(Warning):
pass
@@ -80,21 +82,20 @@ def get_api_versions(apiversion, codegen_dir):
return curapi_hash, apis_hash[apiversion]
def check_api_version(apiversion, codegen_dir):
- """Emits a MismacthCAPIWarning if the C API version needs updating."""
+ """Emits a MismatchCAPIWarning if the C API version needs updating."""
curapi_hash, api_hash = get_api_versions(apiversion, codegen_dir)
# If different hash, it means that the api .txt files in
# codegen_dir have been updated without the API version being
# updated. Any modification in those .txt files should be reflected
# in the api and eventually abi versions.
- # To compute the checksum of the current API, use
- # code_generators/cversions.py script
+ # To compute the checksum of the current API, use numpy/core/cversions.py
if not curapi_hash == api_hash:
msg = ("API mismatch detected, the C API version "
"numbers have to be updated. Current C api version is %d, "
- "with checksum %s, but recorded checksum for C API version %d in "
- "codegen_dir/cversions.txt is %s. If functions were added in the "
- "C API, you have to update C_API_VERSION in %s."
+ "with checksum %s, but recorded checksum for C API version %d "
+ "in core/codegen_dir/cversions.txt is %s. If functions were "
+ "added in the C API, you have to update C_API_VERSION in %s."
)
warnings.warn(msg % (apiversion, curapi_hash, apiversion, api_hash,
__file__),
@@ -117,6 +118,7 @@ OPTIONAL_HEADERS = [
# sse headers only enabled automatically on amd64/x32 builds
"xmmintrin.h", # SSE
"emmintrin.h", # SSE2
+ "immintrin.h", # AVX
"features.h", # for glibc version linux
"xlocale.h", # see GH#8367
"dlfcn.h", # dladdr
@@ -136,6 +138,8 @@ OPTIONAL_INTRINSICS = [("__builtin_isnan", '5.'),
# broken on OSX 10.11, make sure its not optimized away
("volatile int r = __builtin_cpu_supports", '"sse"',
"stdio.h", "__BUILTIN_CPU_SUPPORTS"),
+ ("volatile int r = __builtin_cpu_supports", '"avx512f"',
+ "stdio.h", "__BUILTIN_CPU_SUPPORTS_AVX512F"),
# MMX only needed for icc, but some clangs don't have it
("_m_from_int64", '0', "emmintrin.h"),
("_mm_load_ps", '(float*)0', "xmmintrin.h"), # SSE
@@ -148,6 +152,8 @@ OPTIONAL_INTRINSICS = [("__builtin_isnan", '5.'),
"stdio.h", "LINK_AVX"),
("__asm__ volatile", '"vpand %ymm1, %ymm2, %ymm3"',
"stdio.h", "LINK_AVX2"),
+ ("__asm__ volatile", '"vpaddd %zmm1, %zmm2, %zmm3"',
+ "stdio.h", "LINK_AVX512F"),
("__asm__ volatile", '"xgetbv"', "stdio.h", "XGETBV"),
]
@@ -164,6 +170,24 @@ OPTIONAL_FUNCTION_ATTRIBUTES = [('__attribute__((optimize("unroll-loops")))',
'attribute_target_avx'),
('__attribute__((target ("avx2")))',
'attribute_target_avx2'),
+ ('__attribute__((target ("avx512f")))',
+ 'attribute_target_avx512f'),
+ ]
+
+# function attributes with intrinsics
+# To ensure your compiler can compile avx intrinsics with just the attributes
+# gcc 4.8.4 support attributes but not with intrisics
+# tested via "#include<%s> int %s %s(void *){code; return 0;};" % (header, attribute, name, code)
+# function name will be converted to HAVE_<upper-case-name> preprocessor macro
+OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS = [('__attribute__((target("avx2,fma")))',
+ 'attribute_target_avx2_with_intrinsics',
+ '__m256 temp = _mm256_set1_ps(1.0); temp = \
+ _mm256_fmadd_ps(temp, temp, temp)',
+ 'immintrin.h'),
+ ('__attribute__((target("avx512f")))',
+ 'attribute_target_avx512f_with_intrinsics',
+ '__m512 temp = _mm512_set1_ps(1.0)',
+ 'immintrin.h'),
]
# variable attributes tested via "int %s a" % attribute
@@ -291,30 +315,24 @@ def pyod(filename):
def _pyod2():
out = []
- fid = open(filename, 'rb')
- try:
+ with open(filename, 'rb') as fid:
yo = [int(oct(int(binascii.b2a_hex(o), 16))) for o in fid.read()]
- for i in range(0, len(yo), 16):
- line = ['%07d' % int(oct(i))]
- line.extend(['%03d' % c for c in yo[i:i+16]])
- out.append(" ".join(line))
- return out
- finally:
- fid.close()
+ for i in range(0, len(yo), 16):
+ line = ['%07d' % int(oct(i))]
+ line.extend(['%03d' % c for c in yo[i:i+16]])
+ out.append(" ".join(line))
+ return out
def _pyod3():
out = []
- fid = open(filename, 'rb')
- try:
+ with open(filename, 'rb') as fid:
yo2 = [oct(o)[2:] for o in fid.read()]
- for i in range(0, len(yo2), 16):
- line = ['%07d' % int(oct(i)[2:])]
- line.extend(['%03d' % int(c) for c in yo2[i:i+16]])
- out.append(" ".join(line))
- return out
- finally:
- fid.close()
+ for i in range(0, len(yo2), 16):
+ line = ['%07d' % int(oct(i)[2:])]
+ line.extend(['%03d' % int(c) for c in yo2[i:i+16]])
+ out.append(" ".join(line))
+ return out
if sys.version_info[0] < 3:
return _pyod2()
@@ -398,3 +416,41 @@ def long_double_representation(lines):
else:
# We never detected the after_sequence
raise ValueError("Could not lock sequences (%s)" % saw)
+
+
+def check_for_right_shift_internal_compiler_error(cmd):
+ """
+ On our arm CI, this fails with an internal compilation error
+
+ The failure looks like the following, and can be reproduced on ARM64 GCC 5.4:
+
+ <source>: In function 'right_shift':
+ <source>:4:20: internal compiler error: in expand_shift_1, at expmed.c:2349
+ ip1[i] = ip1[i] >> in2;
+ ^
+ Please submit a full bug report,
+ with preprocessed source if appropriate.
+ See <http://gcc.gnu.org/bugs.html> for instructions.
+ Compiler returned: 1
+
+ This function returns True if this compiler bug is present, and we need to
+ turn off optimization for the function
+ """
+ cmd._check_compiler()
+ has_optimize = cmd.try_compile(textwrap.dedent("""\
+ __attribute__((optimize("O3"))) void right_shift() {}
+ """), None, None)
+ if not has_optimize:
+ return False
+
+ no_err = cmd.try_compile(textwrap.dedent("""\
+ typedef long the_type; /* fails also for unsigned and long long */
+ __attribute__((optimize("O3"))) void right_shift(the_type in2, the_type *ip1, int n) {
+ for (int i = 0; i < n; i++) {
+ if (in2 < (the_type)sizeof(the_type) * 8) {
+ ip1[i] = ip1[i] >> in2;
+ }
+ }
+ }
+ """), None, None)
+ return not no_err
diff --git a/numpy/core/shape_base.py b/numpy/core/shape_base.py
index feb1605bc..d7e769e62 100644
--- a/numpy/core/shape_base.py
+++ b/numpy/core/shape_base.py
@@ -3,11 +3,26 @@ from __future__ import division, absolute_import, print_function
__all__ = ['atleast_1d', 'atleast_2d', 'atleast_3d', 'block', 'hstack',
'stack', 'vstack']
+import functools
+import operator
+import warnings
from . import numeric as _nx
-from .numeric import array, asanyarray, newaxis
+from . import overrides
+from ._asarray import array, asanyarray
from .multiarray import normalize_axis_index
+from . import fromnumeric as _from_nx
+
+array_function_dispatch = functools.partial(
+ overrides.array_function_dispatch, module='numpy')
+
+
+def _atleast_1d_dispatcher(*arys):
+ return arys
+
+
+@array_function_dispatch(_atleast_1d_dispatcher)
def atleast_1d(*arys):
"""
Convert inputs to arrays with at least one dimension.
@@ -33,13 +48,13 @@ def atleast_1d(*arys):
Examples
--------
>>> np.atleast_1d(1.0)
- array([ 1.])
+ array([1.])
>>> x = np.arange(9.0).reshape(3,3)
>>> np.atleast_1d(x)
- array([[ 0., 1., 2.],
- [ 3., 4., 5.],
- [ 6., 7., 8.]])
+ array([[0., 1., 2.],
+ [3., 4., 5.],
+ [6., 7., 8.]])
>>> np.atleast_1d(x) is x
True
@@ -60,6 +75,12 @@ def atleast_1d(*arys):
else:
return res
+
+def _atleast_2d_dispatcher(*arys):
+ return arys
+
+
+@array_function_dispatch(_atleast_2d_dispatcher)
def atleast_2d(*arys):
"""
View inputs as arrays with at least two dimensions.
@@ -85,11 +106,11 @@ def atleast_2d(*arys):
Examples
--------
>>> np.atleast_2d(3.0)
- array([[ 3.]])
+ array([[3.]])
>>> x = np.arange(3.0)
>>> np.atleast_2d(x)
- array([[ 0., 1., 2.]])
+ array([[0., 1., 2.]])
>>> np.atleast_2d(x).base is x
True
@@ -103,7 +124,7 @@ def atleast_2d(*arys):
if ary.ndim == 0:
result = ary.reshape(1, 1)
elif ary.ndim == 1:
- result = ary[newaxis,:]
+ result = ary[_nx.newaxis, :]
else:
result = ary
res.append(result)
@@ -112,6 +133,12 @@ def atleast_2d(*arys):
else:
return res
+
+def _atleast_3d_dispatcher(*arys):
+ return arys
+
+
+@array_function_dispatch(_atleast_3d_dispatcher)
def atleast_3d(*arys):
"""
View inputs as arrays with at least three dimensions.
@@ -139,7 +166,7 @@ def atleast_3d(*arys):
Examples
--------
>>> np.atleast_3d(3.0)
- array([[[ 3.]]])
+ array([[[3.]]])
>>> x = np.arange(3.0)
>>> np.atleast_3d(x).shape
@@ -152,7 +179,7 @@ def atleast_3d(*arys):
True
>>> for arr in np.atleast_3d([1, 2], [[1, 2]], [[[1, 2]]]):
- ... print(arr, arr.shape)
+ ... print(arr, arr.shape) # doctest: +SKIP
...
[[[1]
[2]]] (1, 2, 1)
@@ -167,9 +194,9 @@ def atleast_3d(*arys):
if ary.ndim == 0:
result = ary.reshape(1, 1, 1)
elif ary.ndim == 1:
- result = ary[newaxis,:, newaxis]
+ result = ary[_nx.newaxis, :, _nx.newaxis]
elif ary.ndim == 2:
- result = ary[:,:, newaxis]
+ result = ary[:, :, _nx.newaxis]
else:
result = ary
res.append(result)
@@ -179,6 +206,22 @@ def atleast_3d(*arys):
return res
+def _arrays_for_stack_dispatcher(arrays, stacklevel=4):
+ if not hasattr(arrays, '__getitem__') and hasattr(arrays, '__iter__'):
+ warnings.warn('arrays to stack must be passed as a "sequence" type '
+ 'such as list or tuple. Support for non-sequence '
+ 'iterables such as generators is deprecated as of '
+ 'NumPy 1.16 and will raise an error in the future.',
+ FutureWarning, stacklevel=stacklevel)
+ return ()
+ return arrays
+
+
+def _vhstack_dispatcher(tup):
+ return _arrays_for_stack_dispatcher(tup)
+
+
+@array_function_dispatch(_vhstack_dispatcher)
def vstack(tup):
"""
Stack arrays in sequence vertically (row wise).
@@ -231,8 +274,16 @@ def vstack(tup):
[4]])
"""
- return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)
+ if not overrides.ARRAY_FUNCTION_ENABLED:
+ # raise warning if necessary
+ _arrays_for_stack_dispatcher(tup, stacklevel=2)
+ arrs = atleast_2d(*tup)
+ if not isinstance(arrs, list):
+ arrs = [arrs]
+ return _nx.concatenate(arrs, 0)
+
+@array_function_dispatch(_vhstack_dispatcher)
def hstack(tup):
"""
Stack arrays in sequence horizontally (column wise).
@@ -280,7 +331,13 @@ def hstack(tup):
[3, 4]])
"""
- arrs = [atleast_1d(_m) for _m in tup]
+ if not overrides.ARRAY_FUNCTION_ENABLED:
+ # raise warning if necessary
+ _arrays_for_stack_dispatcher(tup, stacklevel=2)
+
+ arrs = atleast_1d(*tup)
+ if not isinstance(arrs, list):
+ arrs = [arrs]
# As a special case, dimension 0 of 1-dimensional arrays is "horizontal"
if arrs and arrs[0].ndim == 1:
return _nx.concatenate(arrs, 0)
@@ -288,13 +345,23 @@ def hstack(tup):
return _nx.concatenate(arrs, 1)
+def _stack_dispatcher(arrays, axis=None, out=None):
+ arrays = _arrays_for_stack_dispatcher(arrays, stacklevel=6)
+ if out is not None:
+ # optimize for the typical case where only arrays is provided
+ arrays = list(arrays)
+ arrays.append(out)
+ return arrays
+
+
+@array_function_dispatch(_stack_dispatcher)
def stack(arrays, axis=0, out=None):
"""
Join a sequence of arrays along a new axis.
- The `axis` parameter specifies the index of the new axis in the dimensions
- of the result. For example, if ``axis=0`` it will be the first dimension
- and if ``axis=-1`` it will be the last dimension.
+ The ``axis`` parameter specifies the index of the new axis in the
+ dimensions of the result. For example, if ``axis=0`` it will be the first
+ dimension and if ``axis=-1`` it will be the last dimension.
.. versionadded:: 1.10.0
@@ -302,8 +369,10 @@ def stack(arrays, axis=0, out=None):
----------
arrays : sequence of array_like
Each array must have the same shape.
+
axis : int, optional
The axis in the result array along which the input arrays are stacked.
+
out : ndarray, optional
If provided, the destination to place the result. The shape must be
correct, matching that of what stack would have returned if no
@@ -344,11 +413,15 @@ def stack(arrays, axis=0, out=None):
[3, 4]])
"""
+ if not overrides.ARRAY_FUNCTION_ENABLED:
+ # raise warning if necessary
+ _arrays_for_stack_dispatcher(arrays, stacklevel=2)
+
arrays = [asanyarray(arr) for arr in arrays]
if not arrays:
raise ValueError('need at least one array to stack')
- shapes = set(arr.shape for arr in arrays)
+ shapes = {arr.shape for arr in arrays}
if len(shapes) != 1:
raise ValueError('all input arrays must have the same shape')
@@ -360,6 +433,14 @@ def stack(arrays, axis=0, out=None):
return _nx.concatenate(expanded_arrays, axis=axis, out=out)
+# Internal functions to eliminate the overhead of repeated dispatch in one of
+# the two possible paths inside np.block.
+# Use getattr to protect against __array_function__ being disabled.
+_size = getattr(_from_nx.size, '__wrapped__', _from_nx.size)
+_ndim = getattr(_from_nx.ndim, '__wrapped__', _from_nx.ndim)
+_concatenate = getattr(_from_nx.concatenate, '__wrapped__', _from_nx.concatenate)
+
+
def _block_format_index(index):
"""
Convert a list of indices ``[0, 1, 2]`` into ``"arrays[0][1][2]"``.
@@ -394,6 +475,10 @@ def _block_check_depths_match(arrays, parent_index=[]):
refer to it, and the last index along the empty axis will be `None`.
max_arr_ndim : int
The maximum of the ndims of the arrays nested in `arrays`.
+ final_size: int
+ The number of elements in the final array. This is used the motivate
+ the choice of algorithm used using benchmarking wisdom.
+
"""
if type(arrays) is tuple:
# not strictly necessary, but saves us from:
@@ -412,8 +497,9 @@ def _block_check_depths_match(arrays, parent_index=[]):
idxs_ndims = (_block_check_depths_match(arr, parent_index + [i])
for i, arr in enumerate(arrays))
- first_index, max_arr_ndim = next(idxs_ndims)
- for index, ndim in idxs_ndims:
+ first_index, max_arr_ndim, final_size = next(idxs_ndims)
+ for index, ndim, size in idxs_ndims:
+ final_size += size
if ndim > max_arr_ndim:
max_arr_ndim = ndim
if len(index) != len(first_index):
@@ -428,13 +514,15 @@ def _block_check_depths_match(arrays, parent_index=[]):
# propagate our flag that indicates an empty list at the bottom
if index[-1] is None:
first_index = index
- return first_index, max_arr_ndim
+
+ return first_index, max_arr_ndim, final_size
elif type(arrays) is list and len(arrays) == 0:
# We've 'bottomed out' on an empty list
- return parent_index + [None], 0
+ return parent_index + [None], 0, 0
else:
# We've 'bottomed out' - arrays is either a scalar or an array
- return parent_index, _nx.ndim(arrays)
+ size = _size(arrays)
+ return parent_index, _ndim(arrays), size
def _atleast_nd(a, ndim):
@@ -443,9 +531,132 @@ def _atleast_nd(a, ndim):
return array(a, ndmin=ndim, copy=False, subok=True)
+def _accumulate(values):
+ # Helper function because Python 2.7 doesn't have
+ # itertools.accumulate
+ value = 0
+ accumulated = []
+ for v in values:
+ value += v
+ accumulated.append(value)
+ return accumulated
+
+
+def _concatenate_shapes(shapes, axis):
+ """Given array shapes, return the resulting shape and slices prefixes.
+
+ These help in nested concatation.
+ Returns
+ -------
+ shape: tuple of int
+ This tuple satisfies:
+ ```
+ shape, _ = _concatenate_shapes([arr.shape for shape in arrs], axis)
+ shape == concatenate(arrs, axis).shape
+ ```
+
+ slice_prefixes: tuple of (slice(start, end), )
+ For a list of arrays being concatenated, this returns the slice
+ in the larger array at axis that needs to be sliced into.
+
+ For example, the following holds:
+ ```
+ ret = concatenate([a, b, c], axis)
+ _, (sl_a, sl_b, sl_c) = concatenate_slices([a, b, c], axis)
+
+ ret[(slice(None),) * axis + sl_a] == a
+ ret[(slice(None),) * axis + sl_b] == b
+ ret[(slice(None),) * axis + sl_c] == c
+ ```
+
+ These are called slice prefixes since they are used in the recursive
+ blocking algorithm to compute the left-most slices during the
+ recursion. Therefore, they must be prepended to rest of the slice
+ that was computed deeper in the recursion.
+
+ These are returned as tuples to ensure that they can quickly be added
+ to existing slice tuple without creating a new tuple everytime.
+
+ """
+ # Cache a result that will be reused.
+ shape_at_axis = [shape[axis] for shape in shapes]
+
+ # Take a shape, any shape
+ first_shape = shapes[0]
+ first_shape_pre = first_shape[:axis]
+ first_shape_post = first_shape[axis+1:]
+
+ if any(shape[:axis] != first_shape_pre or
+ shape[axis+1:] != first_shape_post for shape in shapes):
+ raise ValueError(
+ 'Mismatched array shapes in block along axis {}.'.format(axis))
+
+ shape = (first_shape_pre + (sum(shape_at_axis),) + first_shape[axis+1:])
+
+ offsets_at_axis = _accumulate(shape_at_axis)
+ slice_prefixes = [(slice(start, end),)
+ for start, end in zip([0] + offsets_at_axis,
+ offsets_at_axis)]
+ return shape, slice_prefixes
+
+
+def _block_info_recursion(arrays, max_depth, result_ndim, depth=0):
+ """
+ Returns the shape of the final array, along with a list
+ of slices and a list of arrays that can be used for assignment inside the
+ new array
+
+ Parameters
+ ----------
+ arrays : nested list of arrays
+ The arrays to check
+ max_depth : list of int
+ The number of nested lists
+ result_ndim: int
+ The number of dimensions in thefinal array.
+
+ Returns
+ -------
+ shape : tuple of int
+ The shape that the final array will take on.
+ slices: list of tuple of slices
+ The slices into the full array required for assignment. These are
+ required to be prepended with ``(Ellipsis, )`` to obtain to correct
+ final index.
+ arrays: list of ndarray
+ The data to assign to each slice of the full array
+
+ """
+ if depth < max_depth:
+ shapes, slices, arrays = zip(
+ *[_block_info_recursion(arr, max_depth, result_ndim, depth+1)
+ for arr in arrays])
+
+ axis = result_ndim - max_depth + depth
+ shape, slice_prefixes = _concatenate_shapes(shapes, axis)
+
+ # Prepend the slice prefix and flatten the slices
+ slices = [slice_prefix + the_slice
+ for slice_prefix, inner_slices in zip(slice_prefixes, slices)
+ for the_slice in inner_slices]
+
+ # Flatten the array list
+ arrays = functools.reduce(operator.add, arrays)
+
+ return shape, slices, arrays
+ else:
+ # We've 'bottomed out' - arrays is either a scalar or an array
+ # type(arrays) is not list
+ # Return the slice and the array inside a list to be consistent with
+ # the recursive case.
+ arr = _atleast_nd(arrays, result_ndim)
+ return arr.shape, [()], [arr]
+
+
def _block(arrays, max_depth, result_ndim, depth=0):
"""
- Internal implementation of block. `arrays` is the argument passed to
+ Internal implementation of block based on repeated concatenation.
+ `arrays` is the argument passed to
block. `max_depth` is the depth of nested lists within `arrays` and
`result_ndim` is the greatest of the dimensions of the arrays in
`arrays` and the depth of the lists in `arrays` (see block docstring
@@ -454,13 +665,26 @@ def _block(arrays, max_depth, result_ndim, depth=0):
if depth < max_depth:
arrs = [_block(arr, max_depth, result_ndim, depth+1)
for arr in arrays]
- return _nx.concatenate(arrs, axis=-(max_depth-depth))
+ return _concatenate(arrs, axis=-(max_depth-depth))
else:
# We've 'bottomed out' - arrays is either a scalar or an array
# type(arrays) is not list
return _atleast_nd(arrays, result_ndim)
+def _block_dispatcher(arrays):
+ # Use type(...) is list to match the behavior of np.block(), which special
+ # cases list specifically rather than allowing for generic iterables or
+ # tuple. Also, we know that list.__array_function__ will never exist.
+ if type(arrays) is list:
+ for subarrays in arrays:
+ for subarray in _block_dispatcher(subarrays):
+ yield subarray
+ else:
+ yield arrays
+
+
+@array_function_dispatch(_block_dispatcher)
def block(arrays):
"""
Assemble an nd-array from nested lists of blocks.
@@ -555,11 +779,11 @@ def block(arrays):
... [A, np.zeros((2, 3))],
... [np.ones((3, 2)), B ]
... ])
- array([[ 2., 0., 0., 0., 0.],
- [ 0., 2., 0., 0., 0.],
- [ 1., 1., 3., 0., 0.],
- [ 1., 1., 0., 3., 0.],
- [ 1., 1., 0., 0., 3.]])
+ array([[2., 0., 0., 0., 0.],
+ [0., 2., 0., 0., 0.],
+ [1., 1., 3., 0., 0.],
+ [1., 1., 0., 3., 0.],
+ [1., 1., 0., 0., 3.]])
With a list of depth 1, `block` can be used as `hstack`
@@ -569,7 +793,7 @@ def block(arrays):
>>> a = np.array([1, 2, 3])
>>> b = np.array([2, 3, 4])
>>> np.block([a, b, 10]) # hstack([a, b, 10])
- array([1, 2, 3, 2, 3, 4, 10])
+ array([ 1, 2, 3, 2, 3, 4, 10])
>>> A = np.ones((2, 2), int)
>>> B = 2 * A
@@ -609,7 +833,38 @@ def block(arrays):
"""
- bottom_index, arr_ndim = _block_check_depths_match(arrays)
+ arrays, list_ndim, result_ndim, final_size = _block_setup(arrays)
+
+ # It was found through benchmarking that making an array of final size
+ # around 256x256 was faster by straight concatenation on a
+ # i7-7700HQ processor and dual channel ram 2400MHz.
+ # It didn't seem to matter heavily on the dtype used.
+ #
+ # A 2D array using repeated concatenation requires 2 copies of the array.
+ #
+ # The fastest algorithm will depend on the ratio of CPU power to memory
+ # speed.
+ # One can monitor the results of the benchmark
+ # https://pv.github.io/numpy-bench/#bench_shape_base.Block2D.time_block2d
+ # to tune this parameter until a C version of the `_block_info_recursion`
+ # algorithm is implemented which would likely be faster than the python
+ # version.
+ if list_ndim * final_size > (2 * 512 * 512):
+ return _block_slicing(arrays, list_ndim, result_ndim)
+ else:
+ return _block_concatenate(arrays, list_ndim, result_ndim)
+
+
+# These helper functions are mostly used for testing.
+# They allow us to write tests that directly call `_block_slicing`
+# or `_block_concatenate` without blocking large arrays to force the wisdom
+# to trigger the desired path.
+def _block_setup(arrays):
+ """
+ Returns
+ (`arrays`, list_ndim, result_ndim, final_size)
+ """
+ bottom_index, arr_ndim, final_size = _block_check_depths_match(arrays)
list_ndim = len(bottom_index)
if bottom_index and bottom_index[-1] is None:
raise ValueError(
@@ -617,7 +872,31 @@ def block(arrays):
_block_format_index(bottom_index)
)
)
- result = _block(arrays, list_ndim, max(arr_ndim, list_ndim))
+ result_ndim = max(arr_ndim, list_ndim)
+ return arrays, list_ndim, result_ndim, final_size
+
+
+def _block_slicing(arrays, list_ndim, result_ndim):
+ shape, slices, arrays = _block_info_recursion(
+ arrays, list_ndim, result_ndim)
+ dtype = _nx.result_type(*[arr.dtype for arr in arrays])
+
+ # Test preferring F only in the case that all input arrays are F
+ F_order = all(arr.flags['F_CONTIGUOUS'] for arr in arrays)
+ C_order = all(arr.flags['C_CONTIGUOUS'] for arr in arrays)
+ order = 'F' if F_order and not C_order else 'C'
+ result = _nx.empty(shape=shape, dtype=dtype, order=order)
+ # Note: In a c implementation, the function
+ # PyArray_CreateMultiSortedStridePerm could be used for more advanced
+ # guessing of the desired order.
+
+ for the_slice, arr in zip(slices, arrays):
+ result[(Ellipsis,) + the_slice] = arr
+ return result
+
+
+def _block_concatenate(arrays, list_ndim, result_ndim):
+ result = _block(arrays, list_ndim, result_ndim)
if list_ndim == 0:
# Catch an edge case where _block returns a view because
# `arrays` is a single numpy array and not a list of numpy arrays.
diff --git a/numpy/core/src/common/array_assign.c b/numpy/core/src/common/array_assign.c
index ac3fdbef7..0ac1b01c6 100644
--- a/numpy/core/src/common/array_assign.c
+++ b/numpy/core/src/common/array_assign.c
@@ -79,7 +79,7 @@ broadcast_error: {
Py_DECREF(errmsg);
return -1;
- }
+ }
}
/* See array_assign.h for parameter documentation */
@@ -125,9 +125,13 @@ raw_array_is_aligned(int ndim, npy_intp *shape,
return npy_is_aligned((void *)align_check, alignment);
}
- else {
+ else if (alignment == 1) {
return 1;
}
+ else {
+ /* always return false for alignment == 0, which means cannot-be-aligned */
+ return 0;
+ }
}
NPY_NO_EXPORT int
diff --git a/numpy/core/src/common/array_assign.h b/numpy/core/src/common/array_assign.h
index 07438c5e8..69ef56bb4 100644
--- a/numpy/core/src/common/array_assign.h
+++ b/numpy/core/src/common/array_assign.h
@@ -87,8 +87,10 @@ broadcast_strides(int ndim, npy_intp *shape,
/*
* Checks whether a data pointer + set of strides refers to a raw
- * array whose elements are all aligned to a given alignment.
- * alignment should be a power of two.
+ * array whose elements are all aligned to a given alignment. Returns
+ * 1 if data is aligned to alignment or 0 if not.
+ * alignment should be a power of two, or may be the sentinel value 0 to mean
+ * cannot-be-aligned, in which case 0 (false) is always returned.
*/
NPY_NO_EXPORT int
raw_array_is_aligned(int ndim, npy_intp *shape,
diff --git a/numpy/core/src/common/cblasfuncs.c b/numpy/core/src/common/cblasfuncs.c
index 6460c5db1..39572fed4 100644
--- a/numpy/core/src/common/cblasfuncs.c
+++ b/numpy/core/src/common/cblasfuncs.c
@@ -182,12 +182,13 @@ _select_matrix_shape(PyArrayObject *array)
* This also makes sure that the data segment is aligned with
* an itemsize address as well by returning one if not true.
*/
-static int
+NPY_NO_EXPORT int
_bad_strides(PyArrayObject *ap)
{
int itemsize = PyArray_ITEMSIZE(ap);
int i, N=PyArray_NDIM(ap);
npy_intp *strides = PyArray_STRIDES(ap);
+ npy_intp *dims = PyArray_DIMS(ap);
if (((npy_intp)(PyArray_DATA(ap)) % itemsize) != 0) {
return 1;
@@ -196,12 +197,14 @@ _bad_strides(PyArrayObject *ap)
if ((strides[i] < 0) || (strides[i] % itemsize) != 0) {
return 1;
}
+ if ((strides[i] == 0 && dims[i] > 1)) {
+ return 1;
+ }
}
return 0;
}
-
/*
* dot(a,b)
* Returns the dot product of a and b for arrays of floating point types.
diff --git a/numpy/core/src/common/get_attr_string.h b/numpy/core/src/common/get_attr_string.h
index bec87c5ed..d458d9550 100644
--- a/numpy/core/src/common/get_attr_string.h
+++ b/numpy/core/src/common/get_attr_string.h
@@ -103,7 +103,6 @@ PyArray_LookupSpecial(PyObject *obj, char *name)
if (_is_basic_python_type(tp)) {
return NULL;
}
-
return maybe_get_attr((PyObject *)tp, name);
}
diff --git a/numpy/core/src/common/lowlevel_strided_loops.h b/numpy/core/src/common/lowlevel_strided_loops.h
index 5f139cffb..bacd27473 100644
--- a/numpy/core/src/common/lowlevel_strided_loops.h
+++ b/numpy/core/src/common/lowlevel_strided_loops.h
@@ -4,6 +4,9 @@
#include <npy_config.h>
#include "mem_overlap.h"
+/* For PyArray_ macros used below */
+#include "numpy/ndarrayobject.h"
+
/*
* NOTE: This API should remain private for the time being, to allow
* for further refinement. I think the 'aligned' mechanism
diff --git a/numpy/core/src/common/npy_ctypes.h b/numpy/core/src/common/npy_ctypes.h
new file mode 100644
index 000000000..c0cc4f1a1
--- /dev/null
+++ b/numpy/core/src/common/npy_ctypes.h
@@ -0,0 +1,50 @@
+#ifndef NPY_CTYPES_H
+#define NPY_CTYPES_H
+
+#include <Python.h>
+
+#include "npy_import.h"
+
+/*
+ * Check if a python type is a ctypes class.
+ *
+ * Works like the Py<type>_Check functions, returning true if the argument
+ * looks like a ctypes object.
+ *
+ * This entire function is just a wrapper around the Python function of the
+ * same name.
+ */
+NPY_INLINE static int
+npy_ctypes_check(PyTypeObject *obj)
+{
+ static PyObject *py_func = NULL;
+ PyObject *ret_obj;
+ int ret;
+
+ npy_cache_import("numpy.core._internal", "npy_ctypes_check", &py_func);
+ if (py_func == NULL) {
+ goto fail;
+ }
+
+ ret_obj = PyObject_CallFunctionObjArgs(py_func, (PyObject *)obj, NULL);
+ if (ret_obj == NULL) {
+ goto fail;
+ }
+
+ ret = PyObject_IsTrue(ret_obj);
+ Py_DECREF(ret_obj);
+ if (ret == -1) {
+ goto fail;
+ }
+
+ return ret;
+
+fail:
+ /* If the above fails, then we should just assume that the type is not from
+ * ctypes
+ */
+ PyErr_Clear();
+ return 0;
+}
+
+#endif
diff --git a/numpy/core/src/common/npy_longdouble.c b/numpy/core/src/common/npy_longdouble.c
index 508fbceac..c580e0cce 100644
--- a/numpy/core/src/common/npy_longdouble.c
+++ b/numpy/core/src/common/npy_longdouble.c
@@ -1,17 +1,12 @@
#include <Python.h>
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+#define _MULTIARRAYMODULE
+
#include "numpy/ndarraytypes.h"
#include "numpy/npy_math.h"
-
-/* This is a backport of Py_SETREF */
-#define NPY_SETREF(op, op2) \
- do { \
- PyObject *_py_tmp = (PyObject *)(op); \
- (op) = (op2); \
- Py_DECREF(_py_tmp); \
- } while (0)
-
+#include "npy_pycompat.h"
+#include "numpyos.h"
/*
* Heavily derived from PyLong_FromDouble
@@ -66,7 +61,7 @@ npy_longdouble_to_PyLong(npy_longdouble ldval)
npy_ulonglong chunk = (npy_ulonglong)frac;
PyObject *l_chunk;
/* v = v << chunk_size */
- NPY_SETREF(v, PyNumber_Lshift(v, l_chunk_size));
+ Py_SETREF(v, PyNumber_Lshift(v, l_chunk_size));
if (v == NULL) {
goto done;
}
@@ -77,7 +72,7 @@ npy_longdouble_to_PyLong(npy_longdouble ldval)
goto done;
}
/* v = v | chunk */
- NPY_SETREF(v, PyNumber_Or(v, l_chunk));
+ Py_SETREF(v, PyNumber_Or(v, l_chunk));
Py_DECREF(l_chunk);
if (v == NULL) {
goto done;
@@ -90,7 +85,7 @@ npy_longdouble_to_PyLong(npy_longdouble ldval)
/* v = -v */
if (neg) {
- NPY_SETREF(v, PyNumber_Negative(v));
+ Py_SETREF(v, PyNumber_Negative(v));
if (v == NULL) {
goto done;
}
@@ -100,3 +95,84 @@ done:
Py_DECREF(l_chunk_size);
return v;
}
+
+/* Helper function to get unicode(PyLong).encode('utf8') */
+static PyObject *
+_PyLong_Bytes(PyObject *long_obj) {
+ PyObject *bytes;
+#if defined(NPY_PY3K)
+ PyObject *unicode = PyObject_Str(long_obj);
+ if (unicode == NULL) {
+ return NULL;
+ }
+ bytes = PyUnicode_AsUTF8String(unicode);
+ Py_DECREF(unicode);
+#else
+ bytes = PyObject_Str(long_obj);
+#endif
+ return bytes;
+}
+
+
+/**
+ * TODO: currently a hack that converts the long through a string. This is
+ * correct, but slow.
+ *
+ * Another approach would be to do this numerically, in a similar way to
+ * PyLong_AsDouble.
+ * However, in order to respect rounding modes correctly, this needs to know
+ * the size of the mantissa, which is platform-dependent.
+ */
+NPY_VISIBILITY_HIDDEN npy_longdouble
+npy_longdouble_from_PyLong(PyObject *long_obj) {
+ npy_longdouble result = 1234;
+ char *end;
+ char *cstr;
+ PyObject *bytes;
+
+ /* convert the long to a string */
+ bytes = _PyLong_Bytes(long_obj);
+ if (bytes == NULL) {
+ return -1;
+ }
+
+ cstr = PyBytes_AsString(bytes);
+ if (cstr == NULL) {
+ goto fail;
+ }
+ end = NULL;
+
+ /* convert the string to a long double and capture errors */
+ errno = 0;
+ result = NumPyOS_ascii_strtold(cstr, &end);
+ if (errno == ERANGE) {
+ /* strtold returns INFINITY of the correct sign. */
+ if (PyErr_Warn(PyExc_RuntimeWarning,
+ "overflow encountered in conversion from python long") < 0) {
+ goto fail;
+ }
+ }
+ else if (errno) {
+ PyErr_Format(PyExc_RuntimeError,
+ "Could not parse python long as longdouble: %s (%s)",
+ cstr,
+ strerror(errno));
+ goto fail;
+ }
+
+ /* Extra characters at the end of the string, or nothing parsed */
+ if (end == cstr || *end != '\0') {
+ PyErr_Format(PyExc_RuntimeError,
+ "Could not parse long as longdouble: %s",
+ cstr);
+ goto fail;
+ }
+
+ /* finally safe to decref now that we're done with `end` */
+ Py_DECREF(bytes);
+ return result;
+
+fail:
+ Py_DECREF(bytes);
+ return -1;
+}
diff --git a/numpy/core/src/common/npy_longdouble.h b/numpy/core/src/common/npy_longdouble.h
index 036b53070..01db06de7 100644
--- a/numpy/core/src/common/npy_longdouble.h
+++ b/numpy/core/src/common/npy_longdouble.h
@@ -14,4 +14,14 @@
NPY_VISIBILITY_HIDDEN PyObject *
npy_longdouble_to_PyLong(npy_longdouble ldval);
+/* Convert a python `long` integer to a npy_longdouble
+ *
+ * This performs the same task as PyLong_AsDouble, but for long doubles
+ * which have a greater range.
+ *
+ * Returns -1 if an error occurs.
+ */
+NPY_VISIBILITY_HIDDEN npy_longdouble
+npy_longdouble_from_PyLong(PyObject *long_obj);
+
#endif
diff --git a/numpy/core/src/common/npy_partition.h.src b/numpy/core/src/common/npy_partition.h.src
index a22cf911c..97dc2536b 100644
--- a/numpy/core/src/common/npy_partition.h.src
+++ b/numpy/core/src/common/npy_partition.h.src
@@ -113,9 +113,6 @@ get_argpartition_func(int type, NPY_SELECTKIND which)
npy_intp i;
npy_intp ntypes = ARRAY_SIZE(_part_map);
- if (which >= NPY_NSELECTS) {
- return NULL;
- }
for (i = 0; i < ntypes; i++) {
if (type == _part_map[i].typenum) {
return _part_map[i].argpart[which];
diff --git a/numpy/core/src/common/npy_sort.h b/numpy/core/src/common/npy_sort.h
deleted file mode 100644
index 8c6f05623..000000000
--- a/numpy/core/src/common/npy_sort.h
+++ /dev/null
@@ -1,204 +0,0 @@
-#ifndef __NPY_SORT_H__
-#define __NPY_SORT_H__
-
-/* Python include is for future object sorts */
-#include <Python.h>
-#include <numpy/npy_common.h>
-#include <numpy/ndarraytypes.h>
-
-#define NPY_ENOMEM 1
-#define NPY_ECOMP 2
-
-static NPY_INLINE int npy_get_msb(npy_uintp unum)
-{
- int depth_limit = 0;
- while (unum >>= 1) {
- depth_limit++;
- }
- return depth_limit;
-}
-
-int quicksort_bool(void *vec, npy_intp cnt, void *null);
-int heapsort_bool(void *vec, npy_intp cnt, void *null);
-int mergesort_bool(void *vec, npy_intp cnt, void *null);
-int aquicksort_bool(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_bool(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_bool(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_byte(void *vec, npy_intp cnt, void *null);
-int heapsort_byte(void *vec, npy_intp cnt, void *null);
-int mergesort_byte(void *vec, npy_intp cnt, void *null);
-int aquicksort_byte(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_byte(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_byte(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_ubyte(void *vec, npy_intp cnt, void *null);
-int heapsort_ubyte(void *vec, npy_intp cnt, void *null);
-int mergesort_ubyte(void *vec, npy_intp cnt, void *null);
-int aquicksort_ubyte(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_ubyte(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_ubyte(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_short(void *vec, npy_intp cnt, void *null);
-int heapsort_short(void *vec, npy_intp cnt, void *null);
-int mergesort_short(void *vec, npy_intp cnt, void *null);
-int aquicksort_short(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_short(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_short(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_ushort(void *vec, npy_intp cnt, void *null);
-int heapsort_ushort(void *vec, npy_intp cnt, void *null);
-int mergesort_ushort(void *vec, npy_intp cnt, void *null);
-int aquicksort_ushort(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_ushort(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_ushort(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_int(void *vec, npy_intp cnt, void *null);
-int heapsort_int(void *vec, npy_intp cnt, void *null);
-int mergesort_int(void *vec, npy_intp cnt, void *null);
-int aquicksort_int(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_int(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_int(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_uint(void *vec, npy_intp cnt, void *null);
-int heapsort_uint(void *vec, npy_intp cnt, void *null);
-int mergesort_uint(void *vec, npy_intp cnt, void *null);
-int aquicksort_uint(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_uint(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_uint(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_long(void *vec, npy_intp cnt, void *null);
-int heapsort_long(void *vec, npy_intp cnt, void *null);
-int mergesort_long(void *vec, npy_intp cnt, void *null);
-int aquicksort_long(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_long(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_long(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_ulong(void *vec, npy_intp cnt, void *null);
-int heapsort_ulong(void *vec, npy_intp cnt, void *null);
-int mergesort_ulong(void *vec, npy_intp cnt, void *null);
-int aquicksort_ulong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_ulong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_ulong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_longlong(void *vec, npy_intp cnt, void *null);
-int heapsort_longlong(void *vec, npy_intp cnt, void *null);
-int mergesort_longlong(void *vec, npy_intp cnt, void *null);
-int aquicksort_longlong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_longlong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_longlong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_ulonglong(void *vec, npy_intp cnt, void *null);
-int heapsort_ulonglong(void *vec, npy_intp cnt, void *null);
-int mergesort_ulonglong(void *vec, npy_intp cnt, void *null);
-int aquicksort_ulonglong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_ulonglong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_ulonglong(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_half(void *vec, npy_intp cnt, void *null);
-int heapsort_half(void *vec, npy_intp cnt, void *null);
-int mergesort_half(void *vec, npy_intp cnt, void *null);
-int aquicksort_half(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_half(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_half(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_float(void *vec, npy_intp cnt, void *null);
-int heapsort_float(void *vec, npy_intp cnt, void *null);
-int mergesort_float(void *vec, npy_intp cnt, void *null);
-int aquicksort_float(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_float(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_float(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_double(void *vec, npy_intp cnt, void *null);
-int heapsort_double(void *vec, npy_intp cnt, void *null);
-int mergesort_double(void *vec, npy_intp cnt, void *null);
-int aquicksort_double(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_double(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_double(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_longdouble(void *vec, npy_intp cnt, void *null);
-int heapsort_longdouble(void *vec, npy_intp cnt, void *null);
-int mergesort_longdouble(void *vec, npy_intp cnt, void *null);
-int aquicksort_longdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_longdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_longdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_cfloat(void *vec, npy_intp cnt, void *null);
-int heapsort_cfloat(void *vec, npy_intp cnt, void *null);
-int mergesort_cfloat(void *vec, npy_intp cnt, void *null);
-int aquicksort_cfloat(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_cfloat(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_cfloat(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_cdouble(void *vec, npy_intp cnt, void *null);
-int heapsort_cdouble(void *vec, npy_intp cnt, void *null);
-int mergesort_cdouble(void *vec, npy_intp cnt, void *null);
-int aquicksort_cdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_cdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_cdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_clongdouble(void *vec, npy_intp cnt, void *null);
-int heapsort_clongdouble(void *vec, npy_intp cnt, void *null);
-int mergesort_clongdouble(void *vec, npy_intp cnt, void *null);
-int aquicksort_clongdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_clongdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_clongdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_string(void *vec, npy_intp cnt, void *arr);
-int heapsort_string(void *vec, npy_intp cnt, void *arr);
-int mergesort_string(void *vec, npy_intp cnt, void *arr);
-int aquicksort_string(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-int aheapsort_string(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-int amergesort_string(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-
-
-int quicksort_unicode(void *vec, npy_intp cnt, void *arr);
-int heapsort_unicode(void *vec, npy_intp cnt, void *arr);
-int mergesort_unicode(void *vec, npy_intp cnt, void *arr);
-int aquicksort_unicode(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-int aheapsort_unicode(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-int amergesort_unicode(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-
-
-int quicksort_datetime(void *vec, npy_intp cnt, void *null);
-int heapsort_datetime(void *vec, npy_intp cnt, void *null);
-int mergesort_datetime(void *vec, npy_intp cnt, void *null);
-int aquicksort_datetime(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_datetime(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_datetime(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int quicksort_timedelta(void *vec, npy_intp cnt, void *null);
-int heapsort_timedelta(void *vec, npy_intp cnt, void *null);
-int mergesort_timedelta(void *vec, npy_intp cnt, void *null);
-int aquicksort_timedelta(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int aheapsort_timedelta(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-int amergesort_timedelta(void *vec, npy_intp *ind, npy_intp cnt, void *null);
-
-
-int npy_quicksort(void *vec, npy_intp cnt, void *arr);
-int npy_heapsort(void *vec, npy_intp cnt, void *arr);
-int npy_mergesort(void *vec, npy_intp cnt, void *arr);
-int npy_aquicksort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-int npy_aheapsort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-int npy_amergesort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
-
-#endif
diff --git a/numpy/core/src/common/npy_sort.h.src b/numpy/core/src/common/npy_sort.h.src
new file mode 100644
index 000000000..16a105499
--- /dev/null
+++ b/numpy/core/src/common/npy_sort.h.src
@@ -0,0 +1,100 @@
+#ifndef __NPY_SORT_H__
+#define __NPY_SORT_H__
+
+/* Python include is for future object sorts */
+#include <Python.h>
+#include <numpy/npy_common.h>
+#include <numpy/ndarraytypes.h>
+
+#define NPY_ENOMEM 1
+#define NPY_ECOMP 2
+
+static NPY_INLINE int npy_get_msb(npy_uintp unum)
+{
+ int depth_limit = 0;
+ while (unum >>= 1) {
+ depth_limit++;
+ }
+ return depth_limit;
+}
+
+
+/*
+ *****************************************************************************
+ ** NUMERIC SORTS **
+ *****************************************************************************
+ */
+
+
+/**begin repeat
+ *
+ * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ * longlong, ulonglong, half, float, double, longdouble,
+ * cfloat, cdouble, clongdouble, datetime, timedelta#
+ */
+
+int quicksort_@suff@(void *vec, npy_intp cnt, void *null);
+int heapsort_@suff@(void *vec, npy_intp cnt, void *null);
+int mergesort_@suff@(void *vec, npy_intp cnt, void *null);
+int timsort_@suff@(void *vec, npy_intp cnt, void *null);
+int aquicksort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null);
+int aheapsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null);
+int amergesort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null);
+int atimsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null);
+
+/**end repeat**/
+
+/**begin repeat
+ *
+ * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ * longlong, ulonglong#
+ */
+
+int radixsort_@suff@(void *vec, npy_intp cnt, void *null);
+int aradixsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null);
+
+/**end repeat**/
+
+
+
+/*
+ *****************************************************************************
+ ** STRING SORTS **
+ *****************************************************************************
+ */
+
+
+/**begin repeat
+ *
+ * #suff = string, unicode#
+ */
+
+int quicksort_@suff@(void *vec, npy_intp cnt, void *arr);
+int heapsort_@suff@(void *vec, npy_intp cnt, void *arr);
+int mergesort_@suff@(void *vec, npy_intp cnt, void *arr);
+int timsort_@suff@(void *vec, npy_intp cnt, void *arr);
+int aquicksort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
+int aheapsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
+int amergesort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
+int atimsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
+
+/**end repeat**/
+
+
+/*
+ *****************************************************************************
+ ** GENERIC SORT **
+ *****************************************************************************
+ */
+
+
+int npy_quicksort(void *vec, npy_intp cnt, void *arr);
+int npy_heapsort(void *vec, npy_intp cnt, void *arr);
+int npy_mergesort(void *vec, npy_intp cnt, void *arr);
+int npy_timsort(void *vec, npy_intp cnt, void *arr);
+int npy_aquicksort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
+int npy_aheapsort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
+int npy_amergesort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
+int npy_atimsort(void *vec, npy_intp *ind, npy_intp cnt, void *arr);
+
+#endif
diff --git a/numpy/core/src/multiarray/numpyos.c b/numpy/core/src/common/numpyos.c
index 52dcbf3c8..d60b1ca17 100644
--- a/numpy/core/src/multiarray/numpyos.c
+++ b/numpy/core/src/common/numpyos.c
@@ -769,3 +769,31 @@ NumPyOS_ascii_ftoLf(FILE *fp, long double *value)
}
return r;
}
+
+NPY_NO_EXPORT npy_longlong
+NumPyOS_strtoll(const char *str, char **endptr, int base)
+{
+#if defined HAVE_STRTOLL
+ return strtoll(str, endptr, base);
+#elif defined _MSC_VER
+ return _strtoi64(str, endptr, base);
+#else
+ /* ok on 64 bit posix */
+ return PyOS_strtol(str, endptr, base);
+#endif
+}
+
+NPY_NO_EXPORT npy_ulonglong
+NumPyOS_strtoull(const char *str, char **endptr, int base)
+{
+#if defined HAVE_STRTOULL
+ return strtoull(str, endptr, base);
+#elif defined _MSC_VER
+ return _strtoui64(str, endptr, base);
+#else
+ /* ok on 64 bit posix */
+ return PyOS_strtoul(str, endptr, base);
+#endif
+}
+
+
diff --git a/numpy/core/src/multiarray/numpyos.h b/numpy/core/src/common/numpyos.h
index 7ca795a6f..4deed8400 100644
--- a/numpy/core/src/multiarray/numpyos.h
+++ b/numpy/core/src/common/numpyos.h
@@ -31,4 +31,11 @@ NumPyOS_ascii_ftoLf(FILE *fp, long double *value);
NPY_NO_EXPORT int
NumPyOS_ascii_isspace(int c);
+/* Convert a string to an int in an arbitrary base */
+NPY_NO_EXPORT npy_longlong
+NumPyOS_strtoll(const char *str, char **endptr, int base);
+
+/* Convert a string to an int in an arbitrary base */
+NPY_NO_EXPORT npy_ulonglong
+NumPyOS_strtoull(const char *str, char **endptr, int base);
#endif
diff --git a/numpy/core/src/common/ufunc_override.c b/numpy/core/src/common/ufunc_override.c
index 33b54c665..89f08a9cb 100644
--- a/numpy/core/src/common/ufunc_override.c
+++ b/numpy/core/src/common/ufunc_override.c
@@ -1,10 +1,9 @@
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
-#define NO_IMPORT_ARRAY
+#define _MULTIARRAYMODULE
#include "npy_pycompat.h"
#include "get_attr_string.h"
#include "npy_import.h"
-
#include "ufunc_override.h"
/*
@@ -12,45 +11,39 @@
* is not the default, i.e., the object is not an ndarray, and its
* __array_ufunc__ is not the same as that of ndarray.
*
- * Returns a new reference, the value of type(obj).__array_ufunc__
- *
- * If the __array_ufunc__ matches that of ndarray, or does not exist, return
- * NULL.
- *
- * Note that since this module is used with both multiarray and umath, we do
- * not have access to PyArray_Type and therewith neither to PyArray_CheckExact
- * nor to the default __array_ufunc__ method, so instead we import locally.
- * TODO: Can this really not be done more smartly?
+ * Returns a new reference, the value of type(obj).__array_ufunc__ if it
+ * exists and is different from that of ndarray, and NULL otherwise.
*/
NPY_NO_EXPORT PyObject *
-get_non_default_array_ufunc(PyObject *obj)
+PyUFuncOverride_GetNonDefaultArrayUfunc(PyObject *obj)
{
- static PyObject *ndarray = NULL;
static PyObject *ndarray_array_ufunc = NULL;
PyObject *cls_array_ufunc;
- /* on first entry, import and cache ndarray and its __array_ufunc__ */
- if (ndarray == NULL) {
- npy_cache_import("numpy.core.multiarray", "ndarray", &ndarray);
- ndarray_array_ufunc = PyObject_GetAttrString(ndarray,
+ /* On first entry, cache ndarray's __array_ufunc__ */
+ if (ndarray_array_ufunc == NULL) {
+ ndarray_array_ufunc = PyObject_GetAttrString((PyObject *)&PyArray_Type,
"__array_ufunc__");
}
/* Fast return for ndarray */
- if ((PyObject *)Py_TYPE(obj) == ndarray) {
+ if (PyArray_CheckExact(obj)) {
return NULL;
}
- /* does the class define __array_ufunc__? */
+ /*
+ * Does the class define __array_ufunc__? (Note that LookupSpecial has fast
+ * return for basic python types, so no need to worry about those here)
+ */
cls_array_ufunc = PyArray_LookupSpecial(obj, "__array_ufunc__");
if (cls_array_ufunc == NULL) {
return NULL;
}
- /* is it different from ndarray.__array_ufunc__? */
- if (cls_array_ufunc != ndarray_array_ufunc) {
- return cls_array_ufunc;
+ /* Ignore if the same as ndarray.__array_ufunc__ */
+ if (cls_array_ufunc == ndarray_array_ufunc) {
+ Py_DECREF(cls_array_ufunc);
+ return NULL;
}
- Py_DECREF(cls_array_ufunc);
- return NULL;
+ return cls_array_ufunc;
}
/*
@@ -62,9 +55,9 @@ get_non_default_array_ufunc(PyObject *obj)
*/
NPY_NO_EXPORT int
-has_non_default_array_ufunc(PyObject * obj)
+PyUFunc_HasOverride(PyObject * obj)
{
- PyObject *method = get_non_default_array_ufunc(obj);
+ PyObject *method = PyUFuncOverride_GetNonDefaultArrayUfunc(obj);
if (method) {
Py_DECREF(method);
return 1;
@@ -78,164 +71,51 @@ has_non_default_array_ufunc(PyObject * obj)
* Get possible out argument from kwds, and returns the number of outputs
* contained within it: if a tuple, the number of elements in it, 1 otherwise.
* The out argument itself is returned in out_kwd_obj, and the outputs
- * in the out_obj array (all as borrowed references).
+ * in the out_obj array (as borrowed references).
*
- * Returns -1 if kwds is not a dict, 0 if no outputs found.
+ * Returns 0 if no outputs found, -1 if kwds is not a dict (with an error set).
*/
-static int
-get_out_objects(PyObject *kwds, PyObject **out_kwd_obj, PyObject ***out_objs)
+NPY_NO_EXPORT int
+PyUFuncOverride_GetOutObjects(PyObject *kwds, PyObject **out_kwd_obj, PyObject ***out_objs)
{
if (kwds == NULL) {
+ Py_INCREF(Py_None);
+ *out_kwd_obj = Py_None;
return 0;
}
if (!PyDict_CheckExact(kwds)) {
PyErr_SetString(PyExc_TypeError,
- "Internal Numpy error: call to PyUFunc_WithOverride "
+ "Internal Numpy error: call to PyUFuncOverride_GetOutObjects "
"with non-dict kwds");
+ *out_kwd_obj = NULL;
return -1;
}
/* borrowed reference */
*out_kwd_obj = PyDict_GetItemString(kwds, "out");
if (*out_kwd_obj == NULL) {
+ Py_INCREF(Py_None);
+ *out_kwd_obj = Py_None;
return 0;
}
if (PyTuple_CheckExact(*out_kwd_obj)) {
- *out_objs = PySequence_Fast_ITEMS(*out_kwd_obj);
- return PySequence_Fast_GET_SIZE(*out_kwd_obj);
- }
- else {
- *out_objs = out_kwd_obj;
- return 1;
- }
-}
-
-/*
- * For each positional argument and each argument in a possible "out"
- * keyword, look for overrides of the standard ufunc behaviour, i.e.,
- * non-default __array_ufunc__ methods.
- *
- * Returns the number of overrides, setting corresponding objects
- * in PyObject array ``with_override`` and the corresponding
- * __array_ufunc__ methods in ``methods`` (both using new references).
- *
- * Only the first override for a given class is returned.
- *
- * returns -1 on failure.
- */
-NPY_NO_EXPORT int
-PyUFunc_WithOverride(PyObject *args, PyObject *kwds,
- PyObject **with_override, PyObject **methods)
-{
- int i;
- int num_override_args = 0;
- int narg, nout = 0;
- PyObject *out_kwd_obj;
- PyObject **arg_objs, **out_objs;
-
- narg = PyTuple_Size(args);
- if (narg < 0) {
- return -1;
- }
- arg_objs = PySequence_Fast_ITEMS(args);
-
- nout = get_out_objects(kwds, &out_kwd_obj, &out_objs);
- if (nout < 0) {
- return -1;
- }
-
- for (i = 0; i < narg + nout; ++i) {
- PyObject *obj;
- int j;
- int new_class = 1;
-
- if (i < narg) {
- obj = arg_objs[i];
- }
- else {
- obj = out_objs[i - narg];
- }
/*
- * Have we seen this class before? If so, ignore.
+ * The C-API recommends calling PySequence_Fast before any of the other
+ * PySequence_Fast* functions. This is required for PyPy
*/
- for (j = 0; j < num_override_args; j++) {
- new_class = (Py_TYPE(obj) != Py_TYPE(with_override[j]));
- if (!new_class) {
- break;
- }
- }
- if (new_class) {
- /*
- * Now see if the object provides an __array_ufunc__. However, we should
- * ignore the base ndarray.__ufunc__, so we skip any ndarray as well as
- * any ndarray subclass instances that did not override __array_ufunc__.
- */
- PyObject *method = get_non_default_array_ufunc(obj);
- if (method == NULL) {
- continue;
- }
- if (method == Py_None) {
- PyErr_Format(PyExc_TypeError,
- "operand '%.200s' does not support ufuncs "
- "(__array_ufunc__=None)",
- obj->ob_type->tp_name);
- Py_DECREF(method);
- goto fail;
- }
- Py_INCREF(obj);
- with_override[num_override_args] = obj;
- methods[num_override_args] = method;
- ++num_override_args;
+ PyObject *seq;
+ seq = PySequence_Fast(*out_kwd_obj,
+ "Could not convert object to sequence");
+ if (seq == NULL) {
+ *out_kwd_obj = NULL;
+ return -1;
}
+ *out_objs = PySequence_Fast_ITEMS(seq);
+ *out_kwd_obj = seq;
+ return PySequence_Fast_GET_SIZE(seq);
}
- return num_override_args;
-
-fail:
- for (i = 0; i < num_override_args; i++) {
- Py_DECREF(with_override[i]);
- Py_DECREF(methods[i]);
- }
- return -1;
-}
-
-/*
- * Check whether any of a set of input and output args have a non-default
- * __array_ufunc__ method. Return 1 if so, 0 if not.
- *
- * This function primarily exists to help ndarray.__array_ufunc__ determine
- * whether it can support a ufunc (which is the case only if none of the
- * operands have an override). Thus, unlike in PyUFunc_CheckOverride, the
- * actual overrides are not needed and one can stop looking once one is found.
- *
- * TODO: move this function and has_non_default_array_ufunc closer to ndarray.
- */
-NPY_NO_EXPORT int
-PyUFunc_HasOverride(PyObject *args, PyObject *kwds)
-{
- int i;
- int nin, nout;
- PyObject *out_kwd_obj;
- PyObject **in_objs, **out_objs;
-
- /* check inputs */
- nin = PyTuple_Size(args);
- if (nin < 0) {
- return -1;
- }
- in_objs = PySequence_Fast_ITEMS(args);
- for (i = 0; i < nin; ++i) {
- if (has_non_default_array_ufunc(in_objs[i])) {
- return 1;
- }
- }
- /* check outputs, if any */
- nout = get_out_objects(kwds, &out_kwd_obj, &out_objs);
- if (nout < 0) {
- return -1;
- }
- for (i = 0; i < nout; i++) {
- if (has_non_default_array_ufunc(out_objs[i])) {
- return 1;
- }
+ else {
+ Py_INCREF(*out_kwd_obj);
+ *out_objs = out_kwd_obj;
+ return 1;
}
- return 0;
}
diff --git a/numpy/core/src/common/ufunc_override.h b/numpy/core/src/common/ufunc_override.h
index 5b269d270..bf86865c9 100644
--- a/numpy/core/src/common/ufunc_override.h
+++ b/numpy/core/src/common/ufunc_override.h
@@ -8,18 +8,11 @@
* is not the default, i.e., the object is not an ndarray, and its
* __array_ufunc__ is not the same as that of ndarray.
*
- * Returns a new reference, the value of type(obj).__array_ufunc__
- *
- * If the __array_ufunc__ matches that of ndarray, or does not exist, return
- * NULL.
- *
- * Note that since this module is used with both multiarray and umath, we do
- * not have access to PyArray_Type and therewith neither to PyArray_CheckExact
- * nor to the default __array_ufunc__ method, so instead we import locally.
- * TODO: Can this really not be done more smartly?
+ * Returns a new reference, the value of type(obj).__array_ufunc__ if it
+ * exists and is different from that of ndarray, and NULL otherwise.
*/
NPY_NO_EXPORT PyObject *
-get_non_default_array_ufunc(PyObject *obj);
+PyUFuncOverride_GetNonDefaultArrayUfunc(PyObject *obj);
/*
* Check whether an object has __array_ufunc__ defined on its class and it
@@ -29,18 +22,16 @@ get_non_default_array_ufunc(PyObject *obj);
* Returns 1 if this is the case, 0 if not.
*/
NPY_NO_EXPORT int
-has_non_default_array_ufunc(PyObject * obj);
+PyUFunc_HasOverride(PyObject *obj);
/*
- * Check whether a set of input and output args have a non-default
- * `__array_ufunc__` method. Returns the number of overrides, setting
- * corresponding objects in PyObject array with_override (if not NULL).
- * returns -1 on failure.
+ * Get possible out argument from kwds, and returns the number of outputs
+ * contained within it: if a tuple, the number of elements in it, 1 otherwise.
+ * The out argument itself is returned in out_kwd_obj, and the outputs
+ * in the out_obj array (as borrowed references).
+ *
+ * Returns 0 if no outputs found, -1 if kwds is not a dict (with an error set).
*/
NPY_NO_EXPORT int
-PyUFunc_WithOverride(PyObject *args, PyObject *kwds,
- PyObject **with_override, PyObject **methods);
-
-NPY_NO_EXPORT int
-PyUFunc_HasOverride(PyObject *args, PyObject *kwds);
+PyUFuncOverride_GetOutObjects(PyObject *kwds, PyObject **out_kwd_obj, PyObject ***out_objs);
#endif
diff --git a/numpy/core/src/multiarray/_multiarray_tests.c.src b/numpy/core/src/multiarray/_multiarray_tests.c.src
index 6c4d49bd1..fa2efb428 100644
--- a/numpy/core/src/multiarray/_multiarray_tests.c.src
+++ b/numpy/core/src/multiarray/_multiarray_tests.c.src
@@ -11,6 +11,13 @@
#include "npy_extint128.h"
#include "common.h"
+
+#if defined(MS_WIN32) || defined(__CYGWIN__)
+#define EXPORT(x) __declspec(dllexport) x
+#else
+#define EXPORT(x) x
+#endif
+
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
/* test PyArray_IsPythonScalar, before including private py3 compat header */
@@ -31,6 +38,12 @@ IsPythonScalar(PyObject * dummy, PyObject *args)
#include "npy_pycompat.h"
+/** Function to test calling via ctypes */
+EXPORT(void*) forward_pointer(void *x)
+{
+ return x;
+}
+
/*
* TODO:
* - Handle mode
@@ -580,6 +593,25 @@ fail:
return NULL;
}
+/*
+ * Helper to test fromstring of 0 terminated strings, as the C-API supports
+ * the -1 length identifier.
+ */
+static PyObject *
+fromstring_null_term_c_api(PyObject *dummy, PyObject *byte_obj)
+{
+ char *string;
+ PyArray_Descr *descr;
+
+ string = PyBytes_AsString(byte_obj);
+ if (string == NULL) {
+ return NULL;
+ }
+ descr = PyArray_DescrNewFromType(NPY_FLOAT64);
+ return PyArray_FromString(string, -1, descr, -1, " ");
+}
+
+
/* check no elison for avoided increfs */
static PyObject *
incref_elide(PyObject *dummy, PyObject *args)
@@ -643,6 +675,43 @@ npy_updateifcopy_deprecation(PyObject* NPY_UNUSED(self), PyObject* args)
Py_RETURN_NONE;
}
+/* used to test PyArray_As1D usage emits not implemented error */
+static PyObject*
+npy_pyarrayas1d_deprecation(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args))
+{
+ PyObject *op = Py_BuildValue("i", 42);
+ PyObject *result = op;
+ int dim = 4;
+ double arg[2] = {1, 2};
+ int temp = PyArray_As1D(&result, (char **)&arg, &dim, NPY_DOUBLE);
+ if (temp < 0) {
+ Py_DECREF(op);
+ return NULL;
+ }
+ /* op != result */
+ Py_DECREF(op);
+ return result;
+}
+
+/* used to test PyArray_As2D usage emits not implemented error */
+static PyObject*
+npy_pyarrayas2d_deprecation(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args))
+{
+ PyObject *op = Py_BuildValue("i", 42);
+ PyObject *result = op;
+ int dim1 = 4;
+ int dim2 = 6;
+ double arg[2][2] = {{1, 2}, {3, 4}};
+ int temp = PyArray_As2D(&result, (char ***)&arg, &dim1, &dim2, NPY_DOUBLE);
+ if (temp < 0) {
+ Py_DECREF(op);
+ return NULL;
+ }
+ /* op != result */
+ Py_DECREF(op);
+ return result;
+}
+
/* used to create array with WRITEBACKIFCOPY flag */
static PyObject*
npy_create_writebackifcopy(PyObject* NPY_UNUSED(self), PyObject* args)
@@ -834,6 +903,31 @@ get_buffer_info(PyObject *NPY_UNUSED(self), PyObject *args)
#undef GET_PYBUF_FLAG
+/*
+ * Return a new array object wrapping existing C-allocated (dummy) data.
+ * Such an array does not own its data (must not free it), but because it
+ * wraps C data, it also has no base object. Used to test arr.flags.writeable
+ * setting behaviour.
+ */
+static PyObject*
+get_c_wrapping_array(PyObject* NPY_UNUSED(self), PyObject* arg)
+{
+ int writeable, flags;
+ PyArray_Descr *descr;
+ npy_intp zero = 0;
+
+ writeable = PyObject_IsTrue(arg);
+ if (error_converting(writeable)) {
+ return NULL;
+ }
+
+ flags = writeable ? NPY_ARRAY_WRITEABLE : 0;
+ /* Create an empty array (which points to a random place) */
+ descr = PyArray_DescrNewFromType(NPY_INTP);
+ return PyArray_NewFromDescr(&PyArray_Type, descr,
+ 1, &zero, NULL, &zero, flags, NULL);
+}
+
/*
* Test C-api level item getting.
@@ -892,6 +986,7 @@ test_as_c_array(PyObject *NPY_UNUSED(self), PyObject *args)
num_dims = PyArray_NDIM(array_obj);
descr = PyArray_DESCR(array_obj);
+ Py_INCREF(descr); /* PyArray_AsCArray steals a reference to this */
switch (num_dims) {
case 1:
@@ -934,6 +1029,7 @@ test_as_c_array(PyObject *NPY_UNUSED(self), PyObject *args)
PyArray_Free((PyObject *) array_obj, (void *) array3);
break;
default:
+ Py_DECREF(descr);
PyErr_SetString(PyExc_ValueError, "array.ndim not in [1, 3]");
return NULL;
}
@@ -1227,7 +1323,9 @@ pylong_from_int128(npy_extint128_t value)
}
Py_DECREF(val);
+ Py_DECREF(val_64);
val = tmp;
+ val_64 = NULL;
tmp = PyLong_FromUnsignedLongLong(value.lo);
if (tmp == NULL) {
@@ -1855,6 +1953,19 @@ printf_float_g(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
return PrintFloat_Printf_g(obj, precision);
}
+static PyObject *
+getset_numericops(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args))
+{
+ PyObject *ret;
+ PyObject *ops = PyArray_GetNumericOps();
+ if (ops == NULL) {
+ return NULL;
+ }
+ ret = PyLong_FromLong(PyArray_SetNumericOps(ops));
+ Py_DECREF(ops);
+ return ret;
+}
+
static PyMethodDef Multiarray_TestsMethods[] = {
{"IsPythonScalar",
IsPythonScalar,
@@ -1874,6 +1985,9 @@ static PyMethodDef Multiarray_TestsMethods[] = {
{"test_inplace_increment",
inplace_increment,
METH_VARARGS, NULL},
+ {"fromstring_null_term_c_api",
+ fromstring_null_term_c_api,
+ METH_O, NULL},
{"incref_elide",
incref_elide,
METH_VARARGS, NULL},
@@ -1886,6 +2000,12 @@ static PyMethodDef Multiarray_TestsMethods[] = {
{"npy_updateifcopy_deprecation",
npy_updateifcopy_deprecation,
METH_O, NULL},
+ {"npy_pyarrayas1d_deprecation",
+ npy_pyarrayas1d_deprecation,
+ METH_NOARGS, NULL},
+ {"npy_pyarrayas2d_deprecation",
+ npy_pyarrayas2d_deprecation,
+ METH_NOARGS, NULL},
{"npy_create_writebackifcopy",
npy_create_writebackifcopy,
METH_O, NULL},
@@ -1906,6 +2026,9 @@ static PyMethodDef Multiarray_TestsMethods[] = {
{"get_buffer_info",
get_buffer_info,
METH_VARARGS, NULL},
+ {"get_c_wrapping_array",
+ get_c_wrapping_array,
+ METH_O, NULL},
{"array_indexing",
array_indexing,
METH_VARARGS, NULL},
@@ -1963,6 +2086,9 @@ static PyMethodDef Multiarray_TestsMethods[] = {
{"get_fpu_mode",
get_fpu_mode,
METH_VARARGS, get_fpu_mode_doc},
+ {"getset_numericops",
+ getset_numericops,
+ METH_NOARGS, NULL},
/**begin repeat
* #name = cabs, carg#
*/
@@ -2040,3 +2166,9 @@ init_multiarray_tests(void)
}
return RETVAL;
}
+
+NPY_NO_EXPORT int
+test_not_exported(void)
+{
+ return 1;
+}
diff --git a/numpy/core/src/multiarray/alloc.c b/numpy/core/src/multiarray/alloc.c
index 6755095d7..a7f34cbe5 100644
--- a/numpy/core/src/multiarray/alloc.c
+++ b/numpy/core/src/multiarray/alloc.c
@@ -25,10 +25,14 @@
#include <assert.h>
-#ifdef HAVE_SYS_MMAN_H
+#ifdef NPY_OS_LINUX
#include <sys/mman.h>
-#if defined MADV_HUGEPAGE && defined HAVE_MADVISE
-#define HAVE_MADV_HUGEPAGE
+#ifndef MADV_HUGEPAGE
+/*
+ * Use code 14 (MADV_HUGEPAGE) if it isn't defined. This gives a chance of
+ * enabling huge pages even if built with linux kernel < 2.6.38
+ */
+#define MADV_HUGEPAGE 14
#endif
#endif
@@ -74,11 +78,15 @@ _npy_alloc_cache(npy_uintp nelem, npy_uintp esz, npy_uint msz,
#ifdef _PyPyGC_AddMemoryPressure
_PyPyPyGC_AddMemoryPressure(nelem * esz);
#endif
-#ifdef HAVE_MADV_HUGEPAGE
+#ifdef NPY_OS_LINUX
/* allow kernel allocating huge pages for large arrays */
if (NPY_UNLIKELY(nelem * esz >= ((1u<<22u)))) {
npy_uintp offset = 4096u - (npy_uintp)p % (4096u);
npy_uintp length = nelem * esz - offset;
+ /**
+ * Intentionally not checking for errors that may be returned by
+ * older kernel versions; optimistically tries enabling huge pages.
+ */
madvise((void*)((npy_uintp)p + offset), length, MADV_HUGEPAGE);
}
#endif
@@ -218,6 +226,7 @@ PyDataMem_NEW(size_t size)
{
void *result;
+ assert(size != 0);
result = malloc(size);
if (_PyDataMem_eventhook != NULL) {
NPY_ALLOW_C_API_DEF
@@ -281,6 +290,7 @@ PyDataMem_RENEW(void *ptr, size_t size)
{
void *result;
+ assert(size != 0);
result = realloc(ptr, size);
if (result != ptr) {
PyTraceMalloc_Untrack(NPY_TRACE_DOMAIN, (npy_uintp)ptr);
diff --git a/numpy/core/src/multiarray/array_assign_array.c b/numpy/core/src/multiarray/array_assign_array.c
index f692e0307..7ff33ebd7 100644
--- a/numpy/core/src/multiarray/array_assign_array.c
+++ b/numpy/core/src/multiarray/array_assign_array.c
@@ -25,6 +25,47 @@
#include "array_assign.h"
/*
+ * Check that array data is both uint-aligned and true-aligned for all array
+ * elements, as required by the copy/casting code in lowlevel_strided_loops.c
+ */
+NPY_NO_EXPORT int
+copycast_isaligned(int ndim, npy_intp *shape,
+ PyArray_Descr *dtype, char *data, npy_intp *strides)
+{
+ int aligned;
+ int big_aln, small_aln;
+
+ int uint_aln = npy_uint_alignment(dtype->elsize);
+ int true_aln = dtype->alignment;
+
+ /* uint alignment can be 0, meaning not uint alignable */
+ if (uint_aln == 0) {
+ return 0;
+ }
+
+ /*
+ * As an optimization, it is unnecessary to check the alignment to the
+ * smaller of (uint_aln, true_aln) if the data is aligned to the bigger of
+ * the two and the big is a multiple of the small aln. We check the bigger
+ * one first and only check the smaller if necessary.
+ */
+ if (true_aln >= uint_aln) {
+ big_aln = true_aln;
+ small_aln = uint_aln;
+ }
+ else {
+ big_aln = uint_aln;
+ small_aln = true_aln;
+ }
+
+ aligned = raw_array_is_aligned(ndim, shape, data, strides, big_aln);
+ if (aligned && big_aln % small_aln != 0) {
+ aligned = raw_array_is_aligned(ndim, shape, data, strides, small_aln);
+ }
+ return aligned;
+}
+
+/*
* Assigns the array from 'src' to 'dst'. The strides must already have
* been broadcast.
*
@@ -48,11 +89,9 @@ raw_array_assign_array(int ndim, npy_intp *shape,
NPY_BEGIN_THREADS_DEF;
- /* Check alignment */
- aligned = raw_array_is_aligned(ndim, shape, dst_data, dst_strides,
- npy_uint_alignment(dst_dtype->elsize)) &&
- raw_array_is_aligned(ndim, shape, src_data, src_strides,
- npy_uint_alignment(src_dtype->elsize));
+ aligned =
+ copycast_isaligned(ndim, shape, dst_dtype, dst_data, dst_strides) &&
+ copycast_isaligned(ndim, shape, src_dtype, src_data, src_strides);
/* Use raw iteration with no heap allocation */
if (PyArray_PrepareTwoRawArrayIter(
@@ -133,11 +172,9 @@ raw_array_wheremasked_assign_array(int ndim, npy_intp *shape,
NPY_BEGIN_THREADS_DEF;
- /* Check alignment */
- aligned = raw_array_is_aligned(ndim, shape, dst_data, dst_strides,
- npy_uint_alignment(dst_dtype->elsize)) &&
- raw_array_is_aligned(ndim, shape, src_data, src_strides,
- npy_uint_alignment(src_dtype->elsize));
+ aligned =
+ copycast_isaligned(ndim, shape, dst_dtype, dst_data, dst_strides) &&
+ copycast_isaligned(ndim, shape, src_dtype, src_data, src_strides);
/* Use raw iteration with no heap allocation */
if (PyArray_PrepareThreeRawArrayIter(
@@ -383,14 +420,14 @@ PyArray_AssignArray(PyArrayObject *dst, PyArrayObject *src,
/* A straightforward where-masked assignment */
/* Do the masked assignment with raw array iteration */
- if (raw_array_wheremasked_assign_array(
- PyArray_NDIM(dst), PyArray_DIMS(dst),
- PyArray_DESCR(dst), PyArray_DATA(dst), PyArray_STRIDES(dst),
- PyArray_DESCR(src), PyArray_DATA(src), src_strides,
- PyArray_DESCR(wheremask), PyArray_DATA(wheremask),
- wheremask_strides) < 0) {
- goto fail;
- }
+ if (raw_array_wheremasked_assign_array(
+ PyArray_NDIM(dst), PyArray_DIMS(dst),
+ PyArray_DESCR(dst), PyArray_DATA(dst), PyArray_STRIDES(dst),
+ PyArray_DESCR(src), PyArray_DATA(src), src_strides,
+ PyArray_DESCR(wheremask), PyArray_DATA(wheremask),
+ wheremask_strides) < 0) {
+ goto fail;
+ }
}
if (copied_src) {
diff --git a/numpy/core/src/multiarray/array_assign_scalar.c b/numpy/core/src/multiarray/array_assign_scalar.c
index 841a41850..ecb5be47b 100644
--- a/numpy/core/src/multiarray/array_assign_scalar.c
+++ b/numpy/core/src/multiarray/array_assign_scalar.c
@@ -45,10 +45,13 @@ raw_array_assign_scalar(int ndim, npy_intp *shape,
NPY_BEGIN_THREADS_DEF;
- /* Check alignment */
+ /* Check both uint and true alignment */
aligned = raw_array_is_aligned(ndim, shape, dst_data, dst_strides,
npy_uint_alignment(dst_dtype->elsize)) &&
- npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize));
+ raw_array_is_aligned(ndim, shape, dst_data, dst_strides,
+ dst_dtype->alignment) &&
+ npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize) &&
+ npy_is_aligned(src_data, src_dtype->alignment));
/* Use raw iteration with no heap allocation */
if (PyArray_PrepareOneRawArrayIter(
@@ -116,10 +119,13 @@ raw_array_wheremasked_assign_scalar(int ndim, npy_intp *shape,
NPY_BEGIN_THREADS_DEF;
- /* Check alignment */
+ /* Check both uint and true alignment */
aligned = raw_array_is_aligned(ndim, shape, dst_data, dst_strides,
npy_uint_alignment(dst_dtype->elsize)) &&
- npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize));
+ raw_array_is_aligned(ndim, shape, dst_data, dst_strides,
+ dst_dtype->alignment) &&
+ npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize) &&
+ npy_is_aligned(src_data, src_dtype->alignment));
/* Use raw iteration with no heap allocation */
if (PyArray_PrepareTwoRawArrayIter(
@@ -220,7 +226,8 @@ PyArray_AssignRawScalar(PyArrayObject *dst,
* we also skip this if 'dst' has an object dtype.
*/
if ((!PyArray_EquivTypes(PyArray_DESCR(dst), src_dtype) ||
- !npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize))) &&
+ !(npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize)) &&
+ npy_is_aligned(src_data, src_dtype->alignment))) &&
PyArray_SIZE(dst) > 1 &&
!PyDataType_REFCHK(PyArray_DESCR(dst))) {
char *tmp_src_data;
diff --git a/numpy/core/src/multiarray/arrayfunction_override.c b/numpy/core/src/multiarray/arrayfunction_override.c
new file mode 100644
index 000000000..62e597764
--- /dev/null
+++ b/numpy/core/src/multiarray/arrayfunction_override.c
@@ -0,0 +1,376 @@
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+#define _MULTIARRAYMODULE
+
+#include "npy_pycompat.h"
+#include "get_attr_string.h"
+#include "npy_import.h"
+#include "multiarraymodule.h"
+
+
+/* Return the ndarray.__array_function__ method. */
+static PyObject *
+get_ndarray_array_function(void)
+{
+ PyObject* method = PyObject_GetAttrString((PyObject *)&PyArray_Type,
+ "__array_function__");
+ assert(method != NULL);
+ return method;
+}
+
+
+/*
+ * Get an object's __array_function__ method in the fastest way possible.
+ * Never raises an exception. Returns NULL if the method doesn't exist.
+ */
+static PyObject *
+get_array_function(PyObject *obj)
+{
+ static PyObject *ndarray_array_function = NULL;
+
+ if (ndarray_array_function == NULL) {
+ ndarray_array_function = get_ndarray_array_function();
+ }
+
+ /* Fast return for ndarray */
+ if (PyArray_CheckExact(obj)) {
+ Py_INCREF(ndarray_array_function);
+ return ndarray_array_function;
+ }
+
+ return PyArray_LookupSpecial(obj, "__array_function__");
+}
+
+
+/*
+ * Like list.insert(), but for C arrays of PyObject*. Skips error checking.
+ */
+static void
+pyobject_array_insert(PyObject **array, int length, int index, PyObject *item)
+{
+ int j;
+
+ for (j = length; j > index; j--) {
+ array[j] = array[j - 1];
+ }
+ array[index] = item;
+}
+
+
+/*
+ * Collects arguments with __array_function__ and their corresponding methods
+ * in the order in which they should be tried (i.e., skipping redundant types).
+ * `relevant_args` is expected to have been produced by PySequence_Fast.
+ * Returns the number of arguments, or -1 on failure.
+ */
+static int
+get_implementing_args_and_methods(PyObject *relevant_args,
+ PyObject **implementing_args,
+ PyObject **methods)
+{
+ int num_implementing_args = 0;
+ Py_ssize_t i;
+ int j;
+
+ PyObject **items = PySequence_Fast_ITEMS(relevant_args);
+ Py_ssize_t length = PySequence_Fast_GET_SIZE(relevant_args);
+
+ for (i = 0; i < length; i++) {
+ int new_class = 1;
+ PyObject *argument = items[i];
+
+ /* Have we seen this type before? */
+ for (j = 0; j < num_implementing_args; j++) {
+ if (Py_TYPE(argument) == Py_TYPE(implementing_args[j])) {
+ new_class = 0;
+ break;
+ }
+ }
+ if (new_class) {
+ PyObject *method = get_array_function(argument);
+
+ if (method != NULL) {
+ int arg_index;
+
+ if (num_implementing_args >= NPY_MAXARGS) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "maximum number (%d) of distinct argument types " \
+ "implementing __array_function__ exceeded",
+ NPY_MAXARGS);
+ Py_DECREF(method);
+ goto fail;
+ }
+
+ /* "subclasses before superclasses, otherwise left to right" */
+ arg_index = num_implementing_args;
+ for (j = 0; j < num_implementing_args; j++) {
+ PyObject *other_type;
+ other_type = (PyObject *)Py_TYPE(implementing_args[j]);
+ if (PyObject_IsInstance(argument, other_type)) {
+ arg_index = j;
+ break;
+ }
+ }
+ Py_INCREF(argument);
+ pyobject_array_insert(implementing_args, num_implementing_args,
+ arg_index, argument);
+ pyobject_array_insert(methods, num_implementing_args,
+ arg_index, method);
+ ++num_implementing_args;
+ }
+ }
+ }
+ return num_implementing_args;
+
+fail:
+ for (j = 0; j < num_implementing_args; j++) {
+ Py_DECREF(implementing_args[j]);
+ Py_DECREF(methods[j]);
+ }
+ return -1;
+}
+
+
+/*
+ * Is this object ndarray.__array_function__?
+ */
+static int
+is_default_array_function(PyObject *obj)
+{
+ static PyObject *ndarray_array_function = NULL;
+
+ if (ndarray_array_function == NULL) {
+ ndarray_array_function = get_ndarray_array_function();
+ }
+ return obj == ndarray_array_function;
+}
+
+
+/*
+ * Core implementation of ndarray.__array_function__. This is exposed
+ * separately so we can avoid the overhead of a Python method call from
+ * within `implement_array_function`.
+ */
+NPY_NO_EXPORT PyObject *
+array_function_method_impl(PyObject *func, PyObject *types, PyObject *args,
+ PyObject *kwargs)
+{
+ Py_ssize_t j;
+ PyObject *implementation, *result;
+
+ PyObject **items = PySequence_Fast_ITEMS(types);
+ Py_ssize_t length = PySequence_Fast_GET_SIZE(types);
+
+ for (j = 0; j < length; j++) {
+ int is_subclass = PyObject_IsSubclass(
+ items[j], (PyObject *)&PyArray_Type);
+ if (is_subclass == -1) {
+ return NULL;
+ }
+ if (!is_subclass) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+ }
+
+ implementation = PyObject_GetAttr(func, npy_ma_str_implementation);
+ if (implementation == NULL) {
+ return NULL;
+ }
+ result = PyObject_Call(implementation, args, kwargs);
+ Py_DECREF(implementation);
+ return result;
+}
+
+
+/*
+ * Calls __array_function__ on the provided argument, with a fast-path for
+ * ndarray.
+ */
+static PyObject *
+call_array_function(PyObject* argument, PyObject* method,
+ PyObject* public_api, PyObject* types,
+ PyObject* args, PyObject* kwargs)
+{
+ if (is_default_array_function(method)) {
+ return array_function_method_impl(public_api, types, args, kwargs);
+ }
+ else {
+ return PyObject_CallFunctionObjArgs(
+ method, argument, public_api, types, args, kwargs, NULL);
+ }
+}
+
+
+/*
+ * Implements the __array_function__ protocol for a function, as described in
+ * in NEP-18. See numpy.core.overrides for a full docstring.
+ */
+NPY_NO_EXPORT PyObject *
+array_implement_array_function(
+ PyObject *NPY_UNUSED(dummy), PyObject *positional_args)
+{
+ PyObject *implementation, *public_api, *relevant_args, *args, *kwargs;
+
+ PyObject *types = NULL;
+ PyObject *implementing_args[NPY_MAXARGS];
+ PyObject *array_function_methods[NPY_MAXARGS];
+
+ int j, any_overrides;
+ int num_implementing_args = 0;
+ PyObject *result = NULL;
+
+ static PyObject *errmsg_formatter = NULL;
+
+ if (!PyArg_UnpackTuple(
+ positional_args, "implement_array_function", 5, 5,
+ &implementation, &public_api, &relevant_args, &args, &kwargs)) {
+ return NULL;
+ }
+
+ relevant_args = PySequence_Fast(
+ relevant_args,
+ "dispatcher for __array_function__ did not return an iterable");
+ if (relevant_args == NULL) {
+ return NULL;
+ }
+
+ /* Collect __array_function__ implementations */
+ num_implementing_args = get_implementing_args_and_methods(
+ relevant_args, implementing_args, array_function_methods);
+ if (num_implementing_args == -1) {
+ goto cleanup;
+ }
+
+ /*
+ * Handle the typical case of no overrides. This is merely an optimization
+ * if some arguments are ndarray objects, but is also necessary if no
+ * arguments implement __array_function__ at all (e.g., if they are all
+ * built-in types).
+ */
+ any_overrides = 0;
+ for (j = 0; j < num_implementing_args; j++) {
+ if (!is_default_array_function(array_function_methods[j])) {
+ any_overrides = 1;
+ break;
+ }
+ }
+ if (!any_overrides) {
+ result = PyObject_Call(implementation, args, kwargs);
+ goto cleanup;
+ }
+
+ /*
+ * Create a Python object for types.
+ * We use a tuple, because it's the fastest Python collection to create
+ * and has the bonus of being immutable.
+ */
+ types = PyTuple_New(num_implementing_args);
+ if (types == NULL) {
+ goto cleanup;
+ }
+ for (j = 0; j < num_implementing_args; j++) {
+ PyObject *arg_type = (PyObject *)Py_TYPE(implementing_args[j]);
+ Py_INCREF(arg_type);
+ PyTuple_SET_ITEM(types, j, arg_type);
+ }
+
+ /* Call __array_function__ methods */
+ for (j = 0; j < num_implementing_args; j++) {
+ PyObject *argument = implementing_args[j];
+ PyObject *method = array_function_methods[j];
+
+ /*
+ * We use `public_api` instead of `implementation` here so
+ * __array_function__ implementations can do equality/identity
+ * comparisons.
+ */
+ result = call_array_function(
+ argument, method, public_api, types, args, kwargs);
+
+ if (result == Py_NotImplemented) {
+ /* Try the next one */
+ Py_DECREF(result);
+ result = NULL;
+ }
+ else {
+ /* Either a good result, or an exception was raised. */
+ goto cleanup;
+ }
+ }
+
+ /* No acceptable override found, raise TypeError. */
+ npy_cache_import("numpy.core._internal",
+ "array_function_errmsg_formatter",
+ &errmsg_formatter);
+ if (errmsg_formatter != NULL) {
+ PyObject *errmsg = PyObject_CallFunctionObjArgs(
+ errmsg_formatter, public_api, types, NULL);
+ if (errmsg != NULL) {
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ Py_DECREF(errmsg);
+ }
+ }
+
+cleanup:
+ for (j = 0; j < num_implementing_args; j++) {
+ Py_DECREF(implementing_args[j]);
+ Py_DECREF(array_function_methods[j]);
+ }
+ Py_XDECREF(types);
+ Py_DECREF(relevant_args);
+ return result;
+}
+
+
+/*
+ * Python wrapper for get_implementing_args_and_methods, for testing purposes.
+ */
+NPY_NO_EXPORT PyObject *
+array__get_implementing_args(
+ PyObject *NPY_UNUSED(dummy), PyObject *positional_args)
+{
+ PyObject *relevant_args;
+ int j;
+ int num_implementing_args = 0;
+ PyObject *implementing_args[NPY_MAXARGS];
+ PyObject *array_function_methods[NPY_MAXARGS];
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTuple(positional_args, "O:array__get_implementing_args",
+ &relevant_args)) {
+ return NULL;
+ }
+
+ relevant_args = PySequence_Fast(
+ relevant_args,
+ "dispatcher for __array_function__ did not return an iterable");
+ if (relevant_args == NULL) {
+ return NULL;
+ }
+
+ num_implementing_args = get_implementing_args_and_methods(
+ relevant_args, implementing_args, array_function_methods);
+ if (num_implementing_args == -1) {
+ goto cleanup;
+ }
+
+ /* create a Python object for implementing_args */
+ result = PyList_New(num_implementing_args);
+ if (result == NULL) {
+ goto cleanup;
+ }
+ for (j = 0; j < num_implementing_args; j++) {
+ PyObject *argument = implementing_args[j];
+ Py_INCREF(argument);
+ PyList_SET_ITEM(result, j, argument);
+ }
+
+cleanup:
+ for (j = 0; j < num_implementing_args; j++) {
+ Py_DECREF(implementing_args[j]);
+ Py_DECREF(array_function_methods[j]);
+ }
+ Py_DECREF(relevant_args);
+ return result;
+}
diff --git a/numpy/core/src/multiarray/arrayfunction_override.h b/numpy/core/src/multiarray/arrayfunction_override.h
new file mode 100644
index 000000000..0d224e2b6
--- /dev/null
+++ b/numpy/core/src/multiarray/arrayfunction_override.h
@@ -0,0 +1,16 @@
+#ifndef _NPY_PRIVATE__ARRAYFUNCTION_OVERRIDE_H
+#define _NPY_PRIVATE__ARRAYFUNCTION_OVERRIDE_H
+
+NPY_NO_EXPORT PyObject *
+array_implement_array_function(
+ PyObject *NPY_UNUSED(dummy), PyObject *positional_args);
+
+NPY_NO_EXPORT PyObject *
+array__get_implementing_args(
+ PyObject *NPY_UNUSED(dummy), PyObject *positional_args);
+
+NPY_NO_EXPORT PyObject *
+array_function_method_impl(PyObject *func, PyObject *types, PyObject *args,
+ PyObject *kwargs);
+
+#endif
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index 341682588..4e229e321 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -462,7 +462,7 @@ WARN_IN_DEALLOC(PyObject* warning, const char * msg) {
PyErr_WriteUnraisable(Py_None);
}
}
-};
+}
/* array object functions */
@@ -471,7 +471,7 @@ array_dealloc(PyArrayObject *self)
{
PyArrayObject_fields *fa = (PyArrayObject_fields *)self;
- _array_dealloc_buffer_info(self);
+ _dealloc_cached_buffer_info((PyObject*)self);
if (fa->weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject *)self);
@@ -483,10 +483,11 @@ array_dealloc(PyArrayObject *self)
char const * msg = "WRITEBACKIFCOPY detected in array_dealloc. "
" Required call to PyArray_ResolveWritebackIfCopy or "
"PyArray_DiscardWritebackIfCopy is missing.";
- Py_INCREF(self); /* hold on to self in next call since if
- * refcount == 0 it will recurse back into
- *array_dealloc
- */
+ /*
+ * prevent reaching 0 twice and thus recursing into dealloc.
+ * Increasing sys.gettotalrefcount, but path should not be taken.
+ */
+ Py_INCREF(self);
WARN_IN_DEALLOC(PyExc_RuntimeWarning, msg);
retval = PyArray_ResolveWritebackIfCopy(self);
if (retval < 0)
@@ -500,10 +501,11 @@ array_dealloc(PyArrayObject *self)
char const * msg = "UPDATEIFCOPY detected in array_dealloc. "
" Required call to PyArray_ResolveWritebackIfCopy or "
"PyArray_DiscardWritebackIfCopy is missing";
- Py_INCREF(self); /* hold on to self in next call since if
- * refcount == 0 it will recurse back into
- *array_dealloc
- */
+ /*
+ * prevent reaching 0 twice and thus recursing into dealloc.
+ * Increasing sys.gettotalrefcount, but path should not be taken.
+ */
+ Py_INCREF(self);
/* 2017-Nov-10 1.14 */
WARN_IN_DEALLOC(PyExc_DeprecationWarning, msg);
retval = PyArray_ResolveWritebackIfCopy(self);
@@ -523,12 +525,7 @@ array_dealloc(PyArrayObject *self)
if ((fa->flags & NPY_ARRAY_OWNDATA) && fa->data) {
/* Free internal references if an Object array */
if (PyDataType_FLAGCHK(fa->descr, NPY_ITEM_REFCOUNT)) {
- Py_INCREF(self); /*hold on to self */
PyArray_XDECREF(self);
- /*
- * Don't need to DECREF -- because we are deleting
- * self already...
- */
}
npy_free_cache(fa->data, PyArray_NBYTES(self));
}
@@ -610,7 +607,7 @@ PyArray_DebugPrint(PyArrayObject *obj)
* TO BE REMOVED - NOT USED INTERNALLY.
*/
NPY_NO_EXPORT void
-PyArray_SetDatetimeParseFunction(PyObject *op)
+PyArray_SetDatetimeParseFunction(PyObject *NPY_UNUSED(op))
{
}
@@ -633,7 +630,7 @@ PyArray_CompareUCS4(npy_ucs4 *s1, npy_ucs4 *s2, size_t len)
/*NUMPY_API
*/
NPY_NO_EXPORT int
-PyArray_CompareString(char *s1, char *s2, size_t len)
+PyArray_CompareString(const char *s1, const char *s2, size_t len)
{
const unsigned char *c1 = (unsigned char *)s1;
const unsigned char *c2 = (unsigned char *)s2;
@@ -655,15 +652,11 @@ NPY_NO_EXPORT int
array_might_be_written(PyArrayObject *obj)
{
const char *msg =
- "Numpy has detected that you (may be) writing to an array returned\n"
- "by numpy.diagonal or by selecting multiple fields in a structured\n"
- "array. This code will likely break in a future numpy release --\n"
- "see numpy.diagonal or arrays.indexing reference docs for details.\n"
- "The quick fix is to make an explicit copy (e.g., do\n"
- "arr.diagonal().copy() or arr[['f0','f1']].copy()).";
+ "Numpy has detected that you (may be) writing to an array with\n"
+ "overlapping memory from np.broadcast_arrays. If this is intentional\n"
+ "set the WRITEABLE flag True or make a copy immediately before writing.";
if (PyArray_FLAGS(obj) & NPY_ARRAY_WARN_ON_WRITE) {
- /* 2012-07-17, 1.7 */
- if (DEPRECATE_FUTUREWARNING(msg) < 0) {
+ if (DEPRECATE(msg) < 0) {
return -1;
}
/* Only warn once per array */
@@ -1165,8 +1158,10 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op)
newdims.ptr = dimensions;
newdims.len = result_ndim+1;
- memcpy(dimensions, PyArray_DIMS((PyArrayObject *)temp),
- sizeof(npy_intp)*result_ndim);
+ if (result_ndim) {
+ memcpy(dimensions, PyArray_DIMS((PyArrayObject *)temp),
+ sizeof(npy_intp)*result_ndim);
+ }
dimensions[result_ndim] = -1;
temp2 = PyArray_Newshape((PyArrayObject *)temp,
&newdims, NPY_ANYORDER);
@@ -1205,15 +1200,28 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op)
}
}
if (res == NULL && !PyErr_Occurred()) {
- PyErr_SetString(PyExc_ValueError, "No fields found.");
+ /* these dtypes had no fields. Use a MultiIter to broadcast them
+ * to an output array, and fill with True (for EQ)*/
+ PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *)
+ PyArray_MultiIterNew(2, self, other);
+ if (mit == NULL) {
+ return NULL;
+ }
+
+ res = PyArray_NewFromDescr(&PyArray_Type,
+ PyArray_DescrFromType(NPY_BOOL),
+ mit->nd, mit->dimensions,
+ NULL, NULL, 0, NULL);
+ Py_DECREF(mit);
+ if (res) {
+ PyArray_FILLWBYTE((PyArrayObject *)res,
+ cmp_op == Py_EQ ? 1 : 0);
+ }
}
return res;
}
else {
- /*
- * compare as a string. Assumes self and
- * other have same descr->type
- */
+ /* compare as a string. Assumes self and other have same descr->type */
return _strings_richcompare(self, other, cmp_op, 0);
}
}
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index 0e69cfc07..5d9e990e8 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -2,7 +2,8 @@
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "structmember.h"
-
+#include <limits.h>
+#include <assert.h>
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
@@ -29,13 +30,13 @@
#include <emmintrin.h>
#endif
+#include "npy_longdouble.h"
#include "numpyos.h"
#include <string.h>
#include "cblasfuncs.h"
#include "npy_cblas.h"
-#include <limits.h>
-#include <assert.h>
+#include "buffer.h"
/* check for sequences, but ignore the types numpy considers scalars */
static NPY_INLINE npy_bool
@@ -150,32 +151,6 @@ MyPyLong_AsUnsigned@Type@ (PyObject *obj)
/**end repeat**/
-static npy_longlong
-npy_strtoll(const char *str, char **endptr, int base)
-{
-#if defined HAVE_STRTOLL
- return strtoll(str, endptr, base);
-#elif defined _MSC_VER
- return _strtoi64(str, endptr, base);
-#else
- /* ok on 64 bit posix */
- return PyOS_strtol(str, endptr, base);
-#endif
-}
-
-static npy_ulonglong
-npy_strtoull(const char *str, char **endptr, int base)
-{
-#if defined HAVE_STRTOULL
- return strtoull(str, endptr, base);
-#elif defined _MSC_VER
- return _strtoui64(str, endptr, base);
-#else
- /* ok on 64 bit posix */
- return PyOS_strtoul(str, endptr, base);
-#endif
-}
-
/*
*****************************************************************************
** GETITEM AND SETITEM **
@@ -246,9 +221,7 @@ static int
if (PySequence_NoString_Check(op)) {
PyErr_SetString(PyExc_ValueError,
"setting an array element with a sequence.");
- Py_DECREF(type);
- Py_XDECREF(value);
- Py_XDECREF(traceback);
+ npy_PyErr_ChainExceptionsCause(type, value, traceback);
}
else {
PyErr_Restore(type, value, traceback);
@@ -354,6 +327,17 @@ string_to_long_double(PyObject*op)
npy_longdouble temp;
PyObject* b;
+ /* Convert python long objects to a longdouble, without precision or range
+ * loss via a double.
+ */
+ if ((PyLong_Check(op) && !PyBool_Check(op))
+#if !defined(NPY_PY3K)
+ || (PyInt_Check(op) && !PyBool_Check(op))
+#endif
+ ) {
+ return npy_longdouble_from_PyLong(op);
+ }
+
if (PyUnicode_Check(op)) {
b = PyUnicode_AsUTF8String(op);
if (!b) {
@@ -947,6 +931,7 @@ VOID_setitem(PyObject *op, void *input, void *vap)
memset(ip + view.len, 0, itemsize - view.len);
}
PyBuffer_Release(&view);
+ _dealloc_cached_buffer_info(op);
}
#else
{
@@ -1796,8 +1781,8 @@ BOOL_scan(FILE *fp, npy_bool *ip, void *NPY_UNUSED(ignore),
* #type = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
* npy_long, npy_ulong, npy_longlong, npy_ulonglong,
* npy_datetime, npy_timedelta#
- * #func = (PyOS_strtol, PyOS_strtoul)*4, npy_strtoll, npy_strtoull,
- * npy_strtoll*2#
+ * #func = (PyOS_strtol, PyOS_strtoul)*4, NumPyOS_strtoll, NumPyOS_strtoull,
+ * NumPyOS_strtoll*2#
* #btype = (npy_long, npy_ulong)*4, npy_longlong, npy_ulonglong,
* npy_longlong*2#
*/
@@ -2230,15 +2215,19 @@ static void
VOID_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride,
npy_intp n, int swap, PyArrayObject *arr)
{
+ PyArray_Descr *descr;
+
if (arr == NULL) {
return;
}
+
+ descr = PyArray_DESCR(arr);
+
if (PyArray_HASFIELDS(arr)) {
PyObject *key, *value;
- PyArray_Descr *descr;
+
Py_ssize_t pos = 0;
- descr = PyArray_DESCR(arr);
while (PyDict_Next(descr->fields, &pos, &key, &value)) {
npy_intp offset;
PyArray_Descr * new;
@@ -2261,14 +2250,28 @@ VOID_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride,
((PyArrayObject_fields *)arr)->descr = descr;
return;
}
- if (swap && PyArray_DESCR(arr)->subarray != NULL) {
- PyArray_Descr *descr, *new;
+ if (PyDataType_HASSUBARRAY(descr)) {
+ PyArray_Descr *new;
npy_intp num;
npy_intp i;
int subitemsize;
char *dstptr, *srcptr;
+ /*
+ * In certain cases subarray copy can be optimized. This is when
+ * swapping is unnecessary and the subarrays data type can certainly
+ * be simply copied (no object, fields, subarray, and not a user dtype).
+ */
+ npy_bool can_optimize_subarray = (!swap &&
+ !PyDataType_HASFIELDS(descr->subarray->base) &&
+ !PyDataType_HASSUBARRAY(descr->subarray->base) &&
+ !PyDataType_REFCHK(descr->subarray->base) &&
+ (descr->subarray->base->type_num < NPY_NTYPES));
+
+ if (can_optimize_subarray) {
+ _basic_copyn(dst, dstride, src, sstride, n, descr->elsize);
+ return;
+ }
- descr = PyArray_DESCR(arr);
new = descr->subarray->base;
/*
* TODO: temporarily modifying the array like this
@@ -2278,6 +2281,10 @@ VOID_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride,
dstptr = dst;
srcptr = src;
subitemsize = new->elsize;
+ if (subitemsize == 0) {
+ /* There cannot be any elements, so return */
+ return;
+ }
num = descr->elsize / subitemsize;
for (i = 0; i < n; i++) {
new->f->copyswapn(dstptr, subitemsize, srcptr,
@@ -2290,22 +2297,26 @@ VOID_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride,
((PyArrayObject_fields *)arr)->descr = descr;
return;
}
- _basic_copyn(dst, dstride, src, sstride, n, PyArray_DESCR(arr)->elsize);
+ /* Must be a naive Void type (e.g. a "V8") so simple copy is sufficient. */
+ _basic_copyn(dst, dstride, src, sstride, n, descr->elsize);
return;
}
static void
VOID_copyswap (char *dst, char *src, int swap, PyArrayObject *arr)
{
+ PyArray_Descr *descr;
+
if (arr == NULL) {
return;
}
+
+ descr = PyArray_DESCR(arr);
+
if (PyArray_HASFIELDS(arr)) {
PyObject *key, *value;
- PyArray_Descr *descr;
Py_ssize_t pos = 0;
- descr = PyArray_DESCR(arr);
while (PyDict_Next(descr->fields, &pos, &key, &value)) {
npy_intp offset;
PyArray_Descr * new;
@@ -2328,28 +2339,45 @@ VOID_copyswap (char *dst, char *src, int swap, PyArrayObject *arr)
((PyArrayObject_fields *)arr)->descr = descr;
return;
}
- if (swap && PyArray_DESCR(arr)->subarray != NULL) {
- PyArray_Descr *descr, *new;
+ if (PyDataType_HASSUBARRAY(descr)) {
+ PyArray_Descr *new;
npy_intp num;
- int itemsize;
+ int subitemsize;
+ /*
+ * In certain cases subarray copy can be optimized. This is when
+ * swapping is unnecessary and the subarrays data type can certainly
+ * be simply copied (no object, fields, subarray, and not a user dtype).
+ */
+ npy_bool can_optimize_subarray = (!swap &&
+ !PyDataType_HASFIELDS(descr->subarray->base) &&
+ !PyDataType_HASSUBARRAY(descr->subarray->base) &&
+ !PyDataType_REFCHK(descr->subarray->base) &&
+ (descr->subarray->base->type_num < NPY_NTYPES));
+
+ if (can_optimize_subarray) {
+ _basic_copy(dst, src, descr->elsize);
+ return;
+ }
- descr = PyArray_DESCR(arr);
new = descr->subarray->base;
/*
* TODO: temporarily modifying the array like this
* is bad coding style, should be changed.
*/
((PyArrayObject_fields *)arr)->descr = new;
- itemsize = new->elsize;
- num = descr->elsize / itemsize;
- new->f->copyswapn(dst, itemsize, src,
- itemsize, num, swap, arr);
+ subitemsize = new->elsize;
+ if (subitemsize == 0) {
+ /* There cannot be any elements, so return */
+ return;
+ }
+ num = descr->elsize / subitemsize;
+ new->f->copyswapn(dst, subitemsize, src,
+ subitemsize, num, swap, arr);
((PyArrayObject_fields *)arr)->descr = descr;
return;
}
-
- /* copy first if needed */
- _basic_copy(dst, src, PyArray_DESCR(arr)->elsize);
+ /* Must be a naive Void type (e.g. a "V8") so simple copy is sufficient. */
+ _basic_copy(dst, src, descr->elsize);
return;
}
@@ -3619,9 +3647,10 @@ OBJECT_dot(char *ip1, npy_intp is1, char *ip2, npy_intp is2, char *op, npy_intp
#define BOOL_fill NULL
/* this requires buffer to be filled with objects or NULL */
-static void
+static int
OBJECT_fill(PyObject **buffer, npy_intp length, void *NPY_UNUSED(ignored))
{
+ int retval = 0;
npy_intp i;
PyObject *start = buffer[0];
PyObject *delta = buffer[1];
@@ -3629,27 +3658,31 @@ OBJECT_fill(PyObject **buffer, npy_intp length, void *NPY_UNUSED(ignored))
delta = PyNumber_Subtract(delta, start);
if (!delta) {
- return;
+ return -1;
}
second = start = PyNumber_Add(start, delta);
if (!start) {
- goto finish;
+ goto error;
}
buffer += 2;
for (i = 2; i < length; i++, buffer++) {
start = PyNumber_Add(start, delta);
if (!start) {
- goto finish;
+ goto error;
}
Py_XDECREF(*buffer);
*buffer = start;
}
+ goto finish;
+
+error:
+ retval = -1;
finish:
Py_XDECREF(second);
Py_DECREF(delta);
- return;
+ return retval;
}
/**begin repeat
@@ -3663,7 +3696,7 @@ finish:
* npy_float, npy_double, npy_longdouble,
* npy_datetime, npy_timedelta#
*/
-static void
+static int
@NAME@_fill(@type@ *buffer, npy_intp length, void *NPY_UNUSED(ignored))
{
npy_intp i;
@@ -3674,10 +3707,11 @@ static void
for (i = 2; i < length; ++i) {
buffer[i] = start + i*delta;
}
+ return 0;
}
/**end repeat**/
-static void
+static int
HALF_fill(npy_half *buffer, npy_intp length, void *NPY_UNUSED(ignored))
{
npy_intp i;
@@ -3688,6 +3722,7 @@ HALF_fill(npy_half *buffer, npy_intp length, void *NPY_UNUSED(ignored))
for (i = 2; i < length; ++i) {
buffer[i] = npy_float_to_half(start + i*delta);
}
+ return 0;
}
/**begin repeat
@@ -3695,7 +3730,7 @@ HALF_fill(npy_half *buffer, npy_intp length, void *NPY_UNUSED(ignored))
* #NAME = CFLOAT, CDOUBLE, CLONGDOUBLE#
* #type = npy_cfloat, npy_cdouble, npy_clongdouble#
*/
-static void
+static int
@NAME@_fill(@type@ *buffer, npy_intp length, void *NPY_UNUSED(ignore))
{
npy_intp i;
@@ -3713,6 +3748,7 @@ static void
buffer->real = start.real + i*delta.real;
buffer->imag = start.imag + i*delta.imag;
}
+ return 0;
}
/**end repeat**/
@@ -3772,176 +3808,6 @@ static void
/*
*****************************************************************************
- ** FASTCLIP **
- *****************************************************************************
- */
-
-#define _LESS_THAN(a, b) ((a) < (b))
-#define _GREATER_THAN(a, b) ((a) > (b))
-
-/*
- * In fastclip, 'b' was already checked for NaN, so the half comparison
- * only needs to check 'a' for NaN.
- */
-
-#define _HALF_LESS_THAN(a, b) (!npy_half_isnan(a) && npy_half_lt_nonan(a, b))
-#define _HALF_GREATER_THAN(a, b) (!npy_half_isnan(a) && npy_half_lt_nonan(b, a))
-
-/**begin repeat
- *
- * #name = BOOL,
- * BYTE, UBYTE, SHORT, USHORT, INT, UINT,
- * LONG, ULONG, LONGLONG, ULONGLONG,
- * HALF, FLOAT, DOUBLE, LONGDOUBLE,
- * DATETIME, TIMEDELTA#
- * #type = npy_bool,
- * npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
- * npy_long, npy_ulong, npy_longlong, npy_ulonglong,
- * npy_half, npy_float, npy_double, npy_longdouble,
- * npy_datetime, npy_timedelta#
- * #isfloat = 0*11, 1*4, 0*2#
- * #isnan = nop*11, npy_half_isnan, npy_isnan*3, nop*2#
- * #lt = _LESS_THAN*11, _HALF_LESS_THAN, _LESS_THAN*5#
- * #gt = _GREATER_THAN*11, _HALF_GREATER_THAN, _GREATER_THAN*5#
- */
-static void
-@name@_fastclip(@type@ *in, npy_intp ni, @type@ *min, @type@ *max, @type@ *out)
-{
- npy_intp i;
- @type@ max_val = 0, min_val = 0;
-
- if (max != NULL) {
- max_val = *max;
-#if @isfloat@
- /* NaNs result in no clipping, so optimize the case away */
- if (@isnan@(max_val)) {
- if (min == NULL) {
- memmove(out, in, ni * sizeof(@type@));
- return;
- }
- max = NULL;
- }
-#endif
- }
- if (min != NULL) {
- min_val = *min;
-#if @isfloat@
- if (@isnan@(min_val)) {
- if (max == NULL) {
- memmove(out, in, ni * sizeof(@type@));
- return;
- }
- min = NULL;
- }
-#endif
- }
- if (max == NULL) {
- for (i = 0; i < ni; i++) {
- if (@lt@(in[i], min_val)) {
- out[i] = min_val;
- }
- else {
- out[i] = in[i];
- }
- }
- }
- else if (min == NULL) {
- for (i = 0; i < ni; i++) {
- if (@gt@(in[i], max_val)) {
- out[i] = max_val;
- }
- else {
- out[i] = in[i];
- }
- }
- }
- else {
- /*
- * Visual Studio 2015 loop vectorizer handles NaN in an unexpected
- * manner, see: https://github.com/numpy/numpy/issues/7601
- */
- #if (_MSC_VER == 1900)
- #pragma loop( no_vector )
- #endif
- for (i = 0; i < ni; i++) {
- if (@lt@(in[i], min_val)) {
- out[i] = min_val;
- }
- else if (@gt@(in[i], max_val)) {
- out[i] = max_val;
- }
- else {
- out[i] = in[i];
- }
- }
- }
-}
-/**end repeat**/
-
-#undef _LESS_THAN
-#undef _GREATER_THAN
-#undef _HALF_LESS_THAN
-#undef _HALF_GREATER_THAN
-
-/**begin repeat
- *
- * #name = CFLOAT, CDOUBLE, CLONGDOUBLE#
- * #type = npy_cfloat, npy_cdouble, npy_clongdouble#
- */
-static void
-@name@_fastclip(@type@ *in, npy_intp ni, @type@ *min, @type@ *max, @type@ *out)
-{
- npy_intp i;
- @type@ max_val, min_val;
-
- if (max != NULL) {
- max_val = *max;
- }
- if (min != NULL) {
- min_val = *min;
- }
- if (max == NULL) {
- for (i = 0; i < ni; i++) {
- if (PyArray_CLT(in[i],min_val)) {
- out[i] = min_val;
- }
- else {
- out[i] = in[i];
- }
- }
- }
- else if (min == NULL) {
- for (i = 0; i < ni; i++) {
- if (PyArray_CGT(in[i], max_val)) {
- out[i] = max_val;
- }
- else {
- out[i] = in[i];
- }
- }
- }
- else {
- for (i = 0; i < ni; i++) {
- if (PyArray_CLT(in[i], min_val)) {
- out[i] = min_val;
- }
- else if (PyArray_CGT(in[i], max_val)) {
- out[i] = max_val;
- }
- else {
- out[i] = in[i];
- }
- }
- }
-}
-
-/**end repeat**/
-
-#define OBJECT_fastclip NULL
-
-
-/*
- *****************************************************************************
** FASTPUTMASK **
*****************************************************************************
*/
@@ -4192,6 +4058,53 @@ small_correlate(const char * d_, npy_intp dstride,
}
/*
+*/
+
+/* A clone function for the datetime dtype c_metadata */
+static NpyAuxData *
+_datetime_dtype_metadata_clone(NpyAuxData *data)
+{
+ PyArray_DatetimeDTypeMetaData *newdata =
+ (PyArray_DatetimeDTypeMetaData *)PyArray_malloc(
+ sizeof(*newdata));
+ if (newdata == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ memcpy(newdata, data, sizeof(*newdata));
+
+ return (NpyAuxData *)newdata;
+}
+
+/*
+ * Allcoate and initialize a PyArray_DatetimeDTypeMetaData object
+ */
+static NpyAuxData*
+_create_datetime_metadata(NPY_DATETIMEUNIT base, int num)
+{
+ PyArray_DatetimeDTypeMetaData *data;
+
+ /* Allocate memory for the metadata */
+ data = PyArray_malloc(sizeof(*data));
+ if (data == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ /* Initialize the base aux data */
+ memset(data, 0, sizeof(PyArray_DatetimeDTypeMetaData));
+ data->base.free = (NpyAuxData_FreeFunc *)PyArray_free;
+ data->base.clone = _datetime_dtype_metadata_clone;
+
+ data->meta.base = base;
+ data->meta.num = num;
+
+ return (NpyAuxData*)data;
+}
+
+
+/*
*****************************************************************************
** SETUP FUNCTION POINTERS **
*****************************************************************************
@@ -4247,12 +4160,12 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
{
quicksort_@suff@,
heapsort_@suff@,
- mergesort_@suff@
+ timsort_@suff@
},
{
aquicksort_@suff@,
aheapsort_@suff@,
- amergesort_@suff@
+ atimsort_@suff@
},
#else
{
@@ -4332,6 +4245,7 @@ static PyArray_Descr @from@_Descr = {
* npy_half, npy_float, npy_double, npy_longdouble,
* npy_cfloat, npy_cdouble, npy_clongdouble,
* PyObject *, npy_datetime, npy_timedelta#
+ * #rsort = 1*5, 0*16#
* #NAME = Bool,
* Byte, UByte, Short, UShort, Int, UInt,
* Long, ULong, LongLong, ULongLong,
@@ -4388,12 +4302,20 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
{
quicksort_@suff@,
heapsort_@suff@,
- mergesort_@suff@
+ #if @rsort@
+ radixsort_@suff@
+ #else
+ timsort_@suff@
+ #endif
},
{
aquicksort_@suff@,
aheapsort_@suff@,
- amergesort_@suff@
+ #if @rsort@
+ aradixsort_@suff@
+ #else
+ atimsort_@suff@
+ #endif
},
#else
{
@@ -4407,7 +4329,7 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
(PyArray_ScalarKindFunc*)NULL,
NULL,
NULL,
- (PyArray_FastClipFunc*)@from@_fastclip,
+ (PyArray_FastClipFunc*)NULL,
(PyArray_FastPutmaskFunc*)@from@_fastputmask,
(PyArray_FastTakeFunc*)@from@_fasttake,
(PyArray_ArgFunc*)@from@_argmin
@@ -4490,7 +4412,17 @@ PyArray_DescrFromType(int type)
{
PyArray_Descr *ret = NULL;
- if (type < NPY_NTYPES) {
+ if (type < 0) {
+ /*
+ * It's not valid for type to be less than 0.
+ * If that happens, then no other branch of
+ * this if/else chain should be followed.
+ * This is effectively a no-op that ensures
+ * the default error is raised.
+ */
+ ret = NULL;
+ }
+ else if (type < NPY_NTYPES) {
ret = _builtin_descrs[type];
}
else if (type == NPY_NOTYPE) {
@@ -4547,66 +4479,6 @@ PyArray_DescrFromType(int type)
return ret;
}
-/* A clone function for the datetime dtype metadata */
-static NpyAuxData *
-datetime_dtype_metadata_clone(NpyAuxData *data)
-{
- PyArray_DatetimeDTypeMetaData *newdata =
- (PyArray_DatetimeDTypeMetaData *)PyArray_malloc(
- sizeof(PyArray_DatetimeDTypeMetaData));
- if (newdata == NULL) {
- return NULL;
- }
-
- memcpy(newdata, data, sizeof(PyArray_DatetimeDTypeMetaData));
-
- return (NpyAuxData *)newdata;
-}
-
-/*
- * Initializes the c_metadata field for the _builtin_descrs DATETIME
- * and TIMEDELTA.
- *
- * must not be static, gcc 4.1.2 on redhat 5 then miscompiles this function
- * see gh-5163
- *
- */
-NPY_NO_EXPORT int
-initialize_builtin_datetime_metadata(void)
-{
- PyArray_DatetimeDTypeMetaData *data1, *data2;
-
- /* Allocate memory for the metadata */
- data1 = PyArray_malloc(sizeof(PyArray_DatetimeDTypeMetaData));
- if (data1 == NULL) {
- return -1;
- }
- data2 = PyArray_malloc(sizeof(PyArray_DatetimeDTypeMetaData));
- if (data2 == NULL) {
- PyArray_free(data1);
- return -1;
- }
-
- /* Initialize the base aux data */
- memset(data1, 0, sizeof(PyArray_DatetimeDTypeMetaData));
- memset(data2, 0, sizeof(PyArray_DatetimeDTypeMetaData));
- data1->base.free = (NpyAuxData_FreeFunc *)PyArray_free;
- data2->base.free = (NpyAuxData_FreeFunc *)PyArray_free;
- data1->base.clone = datetime_dtype_metadata_clone;
- data2->base.clone = datetime_dtype_metadata_clone;
-
- /* Set to the default metadata */
- data1->meta.base = NPY_DATETIME_DEFAULTUNIT;
- data1->meta.num = 1;
- data2->meta.base = NPY_DATETIME_DEFAULTUNIT;
- data2->meta.num = 1;
-
- _builtin_descrs[NPY_DATETIME]->c_metadata = (NpyAuxData *)data1;
- _builtin_descrs[NPY_TIMEDELTA]->c_metadata = (NpyAuxData *)data2;
-
- return 0;
-}
-
/*
*****************************************************************************
** SETUP TYPE INFO **
@@ -4675,7 +4547,14 @@ set_typeinfo(PyObject *dict)
/**end repeat**/
- if (initialize_builtin_datetime_metadata() < 0) {
+ _builtin_descrs[NPY_DATETIME]->c_metadata = _create_datetime_metadata(
+ NPY_DATETIME_DEFAULTUNIT, 1);
+ if (_builtin_descrs[NPY_DATETIME]->c_metadata == NULL) {
+ return -1;
+ }
+ _builtin_descrs[NPY_TIMEDELTA]->c_metadata = _create_datetime_metadata(
+ NPY_DATETIME_DEFAULTUNIT, 1);
+ if (_builtin_descrs[NPY_DATETIME]->c_metadata == NULL) {
return -1;
}
diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c
index 9a2750aea..b729027ad 100644
--- a/numpy/core/src/multiarray/buffer.c
+++ b/numpy/core/src/multiarray/buffer.c
@@ -509,6 +509,10 @@ _buffer_info_new(PyObject *obj)
PyArray_Descr *descr = NULL;
int err = 0;
+ /*
+ * Note that the buffer info is cached as pyints making them appear like
+ * unreachable lost memory to valgrind.
+ */
info = malloc(sizeof(_buffer_info_t));
if (info == NULL) {
PyErr_NoMemory();
@@ -579,9 +583,11 @@ _buffer_info_new(PyObject *obj)
err = _buffer_format_string(descr, &fmt, obj, NULL, NULL);
Py_DECREF(descr);
if (err != 0) {
+ free(info->shape);
goto fail;
}
if (_append_char(&fmt, '\0') < 0) {
+ free(info->shape);
goto fail;
}
info->format = fmt.s;
@@ -765,17 +771,6 @@ array_getbuffer(PyObject *obj, Py_buffer *view, int flags)
goto fail;
}
}
- /*
- * If a read-only buffer is requested on a read-write array, we return a
- * read-write buffer, which is dubious behavior. But that's why this call
- * is guarded by PyArray_ISWRITEABLE rather than (flags &
- * PyBUF_WRITEABLE).
- */
- if (PyArray_ISWRITEABLE(self)) {
- if (array_might_be_written(self) < 0) {
- goto fail;
- }
- }
if (view == NULL) {
PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer");
@@ -791,7 +786,17 @@ array_getbuffer(PyObject *obj, Py_buffer *view, int flags)
view->buf = PyArray_DATA(self);
view->suboffsets = NULL;
view->itemsize = PyArray_ITEMSIZE(self);
- view->readonly = !PyArray_ISWRITEABLE(self);
+ /*
+ * If a read-only buffer is requested on a read-write array, we return a
+ * read-write buffer as per buffer protocol.
+ * We set a requested buffer to readonly also if the array will be readonly
+ * after a deprecation. This jumps the deprecation, but avoiding the
+ * warning is not convenient here. A warning is given if a writeable
+ * buffer is requested since `PyArray_FailUnlessWriteable` is called above
+ * (and clears the `NPY_ARRAY_WARN_ON_WRITE` flag).
+ */
+ view->readonly = (!PyArray_ISWRITEABLE(self) ||
+ PyArray_CHKFLAGS(self, NPY_ARRAY_WARN_ON_WRITE));
view->internal = NULL;
view->len = PyArray_NBYTES(self);
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) {
@@ -922,7 +927,7 @@ fail:
*/
NPY_NO_EXPORT void
-_array_dealloc_buffer_info(PyArrayObject *self)
+_dealloc_cached_buffer_info(PyObject *self)
{
int reset_error_state = 0;
PyObject *ptype, *pvalue, *ptraceback;
@@ -936,7 +941,7 @@ _array_dealloc_buffer_info(PyArrayObject *self)
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
}
- _buffer_clear_info((PyObject*)self);
+ _buffer_clear_info(self);
if (reset_error_state) {
PyErr_Restore(ptype, pvalue, ptraceback);
diff --git a/numpy/core/src/multiarray/buffer.h b/numpy/core/src/multiarray/buffer.h
index d5da8f440..fae413c85 100644
--- a/numpy/core/src/multiarray/buffer.h
+++ b/numpy/core/src/multiarray/buffer.h
@@ -4,7 +4,7 @@
extern NPY_NO_EXPORT PyBufferProcs array_as_buffer;
NPY_NO_EXPORT void
-_array_dealloc_buffer_info(PyArrayObject *self);
+_dealloc_cached_buffer_info(PyObject *self);
NPY_NO_EXPORT PyArray_Descr*
_descriptor_from_pep3118_format(char *s);
diff --git a/numpy/core/src/multiarray/calculation.c b/numpy/core/src/multiarray/calculation.c
index 90ee2c5b2..1d72a5227 100644
--- a/numpy/core/src/multiarray/calculation.c
+++ b/numpy/core/src/multiarray/calculation.c
@@ -918,6 +918,27 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o
}
func = PyArray_DESCR(self)->f->fastclip;
+ if (func == NULL) {
+ if (min == NULL) {
+ return PyObject_CallFunctionObjArgs(n_ops.minimum, self, max, out, NULL);
+ }
+ else if (max == NULL) {
+ return PyObject_CallFunctionObjArgs(n_ops.maximum, self, min, out, NULL);
+ }
+ else {
+ return PyObject_CallFunctionObjArgs(n_ops.clip, self, min, max, out, NULL);
+ }
+ }
+
+ /* NumPy 1.17.0, 2019-02-24 */
+ if (DEPRECATE(
+ "->f->fastclip is deprecated. Use PyUFunc_RegisterLoopForDescr to "
+ "attach a custom loop to np.core.umath.clip, np.minimum, and "
+ "np.maximum") < 0) {
+ return NULL;
+ }
+ /* everything below can be removed once this deprecation completes */
+
if (func == NULL
|| (min != NULL && !PyArray_CheckAnyScalar(min))
|| (max != NULL && !PyArray_CheckAnyScalar(max))
diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c
index 5b4611e8a..3270bc20d 100644
--- a/numpy/core/src/multiarray/common.c
+++ b/numpy/core/src/multiarray/common.c
@@ -147,7 +147,6 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
if (dtype == NULL) {
goto fail;
}
- Py_INCREF(dtype);
goto promote_types;
}
/* Check if it's a NumPy scalar */
@@ -164,7 +163,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
if (string_type == NPY_STRING) {
if ((temp = PyObject_Str(obj)) == NULL) {
- return -1;
+ goto fail;
}
#if defined(NPY_PY3K)
#if PY_VERSION_HEX >= 0x03030000
@@ -182,7 +181,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
#else
if ((temp = PyObject_Unicode(obj)) == NULL) {
#endif
- return -1;
+ goto fail;
}
itemsize = PyUnicode_GET_DATA_SIZE(temp);
#ifndef Py_UNICODE_WIDE
@@ -214,9 +213,13 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
int itemsize;
PyObject *temp;
+ /* dtype is not used in this (string discovery) branch */
+ Py_DECREF(dtype);
+ dtype = NULL;
+
if (string_type == NPY_STRING) {
if ((temp = PyObject_Str(obj)) == NULL) {
- return -1;
+ goto fail;
}
#if defined(NPY_PY3K)
#if PY_VERSION_HEX >= 0x03030000
@@ -234,7 +237,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
#else
if ((temp = PyObject_Unicode(obj)) == NULL) {
#endif
- return -1;
+ goto fail;
}
itemsize = PyUnicode_GET_DATA_SIZE(temp);
#ifndef Py_UNICODE_WIDE
@@ -312,6 +315,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
PyErr_Clear();
dtype = _descriptor_from_pep3118_format(buffer_view.format);
PyBuffer_Release(&buffer_view);
+ _dealloc_cached_buffer_info(obj);
if (dtype) {
goto promote_types;
}
@@ -323,6 +327,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
dtype = PyArray_DescrNewFromType(NPY_VOID);
dtype->elsize = buffer_view.itemsize;
PyBuffer_Release(&buffer_view);
+ _dealloc_cached_buffer_info(obj);
goto promote_types;
}
else {
@@ -341,7 +346,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
typestr = PyDict_GetItemString(ip, "typestr");
#if defined(NPY_PY3K)
/* Allow unicode type strings */
- if (PyUnicode_Check(typestr)) {
+ if (typestr && PyUnicode_Check(typestr)) {
tmp = PyUnicode_AsASCIIString(typestr);
typestr = tmp;
}
@@ -438,12 +443,18 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
return 0;
}
- /* Recursive case, first check the sequence contains only one type */
+ /*
+ * The C-API recommends calling PySequence_Fast before any of the other
+ * PySequence_Fast* functions. This is required for PyPy
+ */
seq = PySequence_Fast(obj, "Could not convert object to sequence");
if (seq == NULL) {
goto fail;
}
+
+ /* Recursive case, first check the sequence contains only one type */
size = PySequence_Fast_GET_SIZE(seq);
+ /* objects is borrowed, do not release seq */
objects = PySequence_Fast_ITEMS(seq);
common_type = size > 0 ? Py_TYPE(objects[0]) : NULL;
for (i = 1; i < size; ++i) {
@@ -503,7 +514,7 @@ promote_types:
PyArray_Descr *res_dtype = PyArray_PromoteTypes(dtype, *out_dtype);
Py_DECREF(dtype);
if (res_dtype == NULL) {
- return -1;
+ goto fail;
}
if (!string_type &&
res_dtype->type_num == NPY_UNICODE &&
@@ -590,7 +601,7 @@ _zerofill(PyArrayObject *ret)
NPY_NO_EXPORT npy_bool
_IsWriteable(PyArrayObject *ap)
{
- PyObject *base=PyArray_BASE(ap);
+ PyObject *base = PyArray_BASE(ap);
#if defined(NPY_PY3K)
Py_buffer view;
#else
@@ -598,44 +609,62 @@ _IsWriteable(PyArrayObject *ap)
Py_ssize_t n;
#endif
- /* If we own our own data, then no-problem */
- if ((base == NULL) || (PyArray_FLAGS(ap) & NPY_ARRAY_OWNDATA)) {
+ /*
+ * C-data wrapping arrays may not own their data while not having a base;
+ * WRITEBACKIFCOPY arrays have a base, but do own their data.
+ */
+ if (base == NULL || PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA)) {
+ /*
+ * This is somewhat unsafe for directly wrapped non-writable C-arrays,
+ * which do not know whether the memory area is writable or not and
+ * do not own their data (but have no base).
+ * It would be better if this returned PyArray_ISWRITEABLE(ap).
+ * Since it is hard to deprecate, this is deprecated only on the Python
+ * side, but not on in PyArray_UpdateFlags.
+ */
return NPY_TRUE;
}
+
/*
- * Get to the final base object
- * If it is a writeable array, then return TRUE
- * If we can find an array object
- * or a writeable buffer object as the final base object
- * or a string object (for pickling support memory savings).
- * - this last could be removed if a proper pickleable
- * buffer was added to Python.
- *
- * MW: I think it would better to disallow switching from READONLY
- * to WRITEABLE like this...
+ * Get to the final base object.
+ * If it is a writeable array, then return True if we can
+ * find an array object or a writeable buffer object as
+ * the final base object.
*/
+ while (PyArray_Check(base)) {
+ ap = (PyArrayObject *)base;
+ base = PyArray_BASE(ap);
- while(PyArray_Check(base)) {
- if (PyArray_CHKFLAGS((PyArrayObject *)base, NPY_ARRAY_OWNDATA)) {
- return (npy_bool) (PyArray_ISWRITEABLE((PyArrayObject *)base));
+ if (PyArray_ISWRITEABLE(ap)) {
+ /*
+ * If any base is writeable, it must be OK to switch, note that
+ * bases are typically collapsed to always point to the most
+ * general one.
+ */
+ return NPY_TRUE;
}
- base = PyArray_BASE((PyArrayObject *)base);
- }
- /*
- * here so pickle support works seamlessly
- * and unpickled array can be set and reset writeable
- * -- could be abused --
- */
- if (PyString_Check(base)) {
- return NPY_TRUE;
+ if (base == NULL || PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA)) {
+ /* there is no further base to test the writeable flag for */
+ return NPY_FALSE;
+ }
+ assert(!PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA));
}
+
#if defined(NPY_PY3K)
if (PyObject_GetBuffer(base, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) {
PyErr_Clear();
return NPY_FALSE;
}
PyBuffer_Release(&view);
+ /*
+ * The first call to PyObject_GetBuffer stores a reference to a struct
+ * _buffer_info_t (from buffer.c, with format, ndim, strides and shape) in
+ * a static dictionary, with id(base) as the key. Usually we release it
+ * after the call to PyBuffer_Release, via a call to
+ * _dealloc_cached_buffer_info, but in this case leave it in the cache to
+ * speed up future calls to _IsWriteable.
+ */
#else
if (PyObject_AsWriteBuffer(base, &dummy, &n) < 0) {
PyErr_Clear();
diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h
index 2b8d3d3a4..487d530a1 100644
--- a/numpy/core/src/multiarray/common.h
+++ b/numpy/core/src/multiarray/common.h
@@ -149,7 +149,7 @@ check_and_adjust_axis_msg(int *axis, int ndim, PyObject *msg_prefix)
PyObject *exc;
if (AxisError_cls == NULL) {
- PyObject *mod = PyImport_ImportModule("numpy.core._internal");
+ PyObject *mod = PyImport_ImportModule("numpy.core._exceptions");
if (mod != NULL) {
AxisError_cls = PyObject_GetAttrString(mod, "AxisError");
@@ -182,6 +182,7 @@ check_and_adjust_axis(int *axis, int ndim)
/* used for some alignment checks */
#define _ALIGN(type) offsetof(struct {char c; type v;}, v)
+#define _UINT_ALIGN(type) npy_uint_alignment(sizeof(type))
/*
* Disable harmless compiler warning "4116: unnamed type definition in
* parentheses" which is caused by the _ALIGN macro.
@@ -201,6 +202,7 @@ npy_is_aligned(const void * p, const npy_uintp alignment)
* Assumes cast from pointer to uintp gives a sensible representation we
* can use bitwise & on (not required by C standard, but used by glibc).
* This test is faster than a direct modulo.
+ * Note alignment value of 0 is allowed and returns False.
*/
return ((npy_uintp)(p) & ((alignment) - 1)) == 0;
}
diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c
index 1c27f8394..055d3e60f 100644
--- a/numpy/core/src/multiarray/compiled_base.c
+++ b/numpy/core/src/multiarray/compiled_base.c
@@ -328,6 +328,7 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
} else {
Py_XDECREF(values);
Py_XDECREF(mask);
+ PyArray_ResolveWritebackIfCopy(array);
Py_XDECREF(array);
Py_RETURN_NONE;
}
@@ -358,6 +359,7 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
fail:
Py_XDECREF(mask);
+ PyArray_ResolveWritebackIfCopy(array);
Py_XDECREF(array);
Py_XDECREF(values);
return NULL;
@@ -365,6 +367,18 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
#define LIKELY_IN_CACHE_SIZE 8
+#ifdef __INTEL_COMPILER
+#pragma intel optimization_level 0
+#endif
+static NPY_INLINE npy_intp
+_linear_search(const npy_double key, const npy_double *arr, const npy_intp len, const npy_intp i0)
+{
+ npy_intp i;
+
+ for (i = i0; i < len && key >= arr[i]; i++);
+ return i - 1;
+}
+
/** @brief find index of a sorted array such that arr[i] <= key < arr[i + 1].
*
* If an starting index guess is in-range, the array values around this
@@ -404,10 +418,7 @@ binary_search_with_guess(const npy_double key, const npy_double *arr,
* From above we know key >= arr[0] when we start.
*/
if (len <= 4) {
- npy_intp i;
-
- for (i = 1; i < len && key >= arr[i]; ++i);
- return i - 1;
+ return _linear_search(key, arr, len, 1);
}
if (guess > len - 3) {
@@ -565,6 +576,7 @@ arr_interp(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
if (lenxp <= lenx) {
slopes = PyArray_malloc((lenxp - 1) * sizeof(npy_double));
if (slopes == NULL) {
+ PyErr_NoMemory();
goto fail;
}
}
@@ -600,9 +612,18 @@ arr_interp(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
dres[i] = dy[j];
}
else {
- const npy_double slope = (slopes != NULL) ? slopes[j] :
- (dy[j+1] - dy[j]) / (dx[j+1] - dx[j]);
+ const npy_double slope =
+ (slopes != NULL) ? slopes[j] :
+ (dy[j+1] - dy[j]) / (dx[j+1] - dx[j]);
+
+ /* If we get nan in one direction, try the other */
dres[i] = slope*(x_val - dx[j]) + dy[j];
+ if (NPY_UNLIKELY(npy_isnan(dres[i]))) {
+ dres[i] = slope*(x_val - dx[j+1]) + dy[j+1];
+ if (NPY_UNLIKELY(npy_isnan(dres[i])) && dy[j] == dy[j+1]) {
+ dres[i] = dy[j];
+ }
+ }
}
}
@@ -734,6 +755,7 @@ arr_interp_complex(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
if (lenxp <= lenx) {
slopes = PyArray_malloc((lenxp - 1) * sizeof(npy_cdouble));
if (slopes == NULL) {
+ PyErr_NoMemory();
goto fail;
}
}
@@ -772,16 +794,32 @@ arr_interp_complex(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
dres[i] = dy[j];
}
else {
- if (slopes!=NULL) {
- dres[i].real = slopes[j].real*(x_val - dx[j]) + dy[j].real;
- dres[i].imag = slopes[j].imag*(x_val - dx[j]) + dy[j].imag;
+ npy_cdouble slope;
+ if (slopes != NULL) {
+ slope = slopes[j];
}
else {
const npy_double inv_dx = 1.0 / (dx[j+1] - dx[j]);
- dres[i].real = (dy[j+1].real - dy[j].real)*(x_val - dx[j])*
- inv_dx + dy[j].real;
- dres[i].imag = (dy[j+1].imag - dy[j].imag)*(x_val - dx[j])*
- inv_dx + dy[j].imag;
+ slope.real = (dy[j+1].real - dy[j].real) * inv_dx;
+ slope.imag = (dy[j+1].imag - dy[j].imag) * inv_dx;
+ }
+
+ /* If we get nan in one direction, try the other */
+ dres[i].real = slope.real*(x_val - dx[j]) + dy[j].real;
+ if (NPY_UNLIKELY(npy_isnan(dres[i].real))) {
+ dres[i].real = slope.real*(x_val - dx[j+1]) + dy[j+1].real;
+ if (NPY_UNLIKELY(npy_isnan(dres[i].real)) &&
+ dy[j].real == dy[j+1].real) {
+ dres[i].real = dy[j].real;
+ }
+ }
+ dres[i].imag = slope.imag*(x_val - dx[j]) + dy[j].imag;
+ if (NPY_UNLIKELY(npy_isnan(dres[i].imag))) {
+ dres[i].imag = slope.imag*(x_val - dx[j+1]) + dy[j+1].imag;
+ if (NPY_UNLIKELY(npy_isnan(dres[i].imag)) &&
+ dy[j].imag == dy[j+1].imag) {
+ dres[i].imag = dy[j].imag;
+ }
}
}
}
@@ -803,17 +841,63 @@ fail:
return NULL;
}
+static const char *EMPTY_SEQUENCE_ERR_MSG = "indices must be integral: the provided " \
+ "empty sequence was inferred as float. Wrap it with " \
+ "'np.array(indices, dtype=np.intp)'";
+
+static const char *NON_INTEGRAL_ERROR_MSG = "only int indices permitted";
+
+/* Convert obj to an ndarray with integer dtype or fail */
+static PyArrayObject *
+astype_anyint(PyObject *obj) {
+ PyArrayObject *ret;
+
+ if (!PyArray_Check(obj)) {
+ /* prefer int dtype */
+ PyArray_Descr *dtype_guess = NULL;
+ if (PyArray_DTypeFromObject(obj, NPY_MAXDIMS, &dtype_guess) < 0) {
+ return NULL;
+ }
+ if (dtype_guess == NULL) {
+ if (PySequence_Check(obj) && PySequence_Size(obj) == 0) {
+ PyErr_SetString(PyExc_TypeError, EMPTY_SEQUENCE_ERR_MSG);
+ }
+ return NULL;
+ }
+ ret = (PyArrayObject*)PyArray_FromAny(obj, dtype_guess, 0, 0, 0, NULL);
+ if (ret == NULL) {
+ return NULL;
+ }
+ }
+ else {
+ ret = (PyArrayObject *)obj;
+ Py_INCREF(ret);
+ }
+
+ if (!(PyArray_ISINTEGER(ret) || PyArray_ISBOOL(ret))) {
+ /* ensure dtype is int-based */
+ PyErr_SetString(PyExc_TypeError, NON_INTEGRAL_ERROR_MSG);
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
/*
* Converts a Python sequence into 'count' PyArrayObjects
*
- * seq - Input Python object, usually a tuple but any sequence works.
- * op - Where the arrays are placed.
- * count - How many arrays there should be (errors if it doesn't match).
- * paramname - The name of the parameter that produced 'seq'.
+ * seq - Input Python object, usually a tuple but any sequence works.
+ * Must have integral content.
+ * paramname - The name of the parameter that produced 'seq'.
+ * count - How many arrays there should be (errors if it doesn't match).
+ * op - Where the arrays are placed.
*/
-static int sequence_to_arrays(PyObject *seq,
- PyArrayObject **op, int count,
- char *paramname)
+static int int_sequence_to_arrays(PyObject *seq,
+ char *paramname,
+ int count,
+ PyArrayObject **op
+ )
{
int i;
@@ -827,30 +911,26 @@ static int sequence_to_arrays(PyObject *seq,
for (i = 0; i < count; ++i) {
PyObject *item = PySequence_GetItem(seq, i);
if (item == NULL) {
- while (--i >= 0) {
- Py_DECREF(op[i]);
- op[i] = NULL;
- }
- return -1;
+ goto fail;
}
-
- op[i] = (PyArrayObject *)PyArray_FROM_O(item);
+ op[i] = astype_anyint(item);
+ Py_DECREF(item);
if (op[i] == NULL) {
- while (--i >= 0) {
- Py_DECREF(op[i]);
- op[i] = NULL;
- }
- Py_DECREF(item);
- return -1;
+ goto fail;
}
-
- Py_DECREF(item);
}
return 0;
+
+fail:
+ while (--i >= 0) {
+ Py_XDECREF(op[i]);
+ op[i] = NULL;
+ }
+ return -1;
}
-/* Inner loop for unravel_index */
+/* Inner loop for ravel_multi_index */
static int
ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims,
npy_intp *ravel_strides,
@@ -862,6 +942,20 @@ ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims,
char invalid;
npy_intp j, m;
+ /*
+ * Check for 0-dimensional axes unless there is nothing to do.
+ * An empty array/shape cannot be indexed at all.
+ */
+ if (count != 0) {
+ for (i = 0; i < ravel_ndim; ++i) {
+ if (ravel_dims[i] == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot unravel if shape has zero entries (is empty).");
+ return NPY_FAIL;
+ }
+ }
+ }
+
NPY_BEGIN_ALLOW_THREADS;
invalid = 0;
while (count--) {
@@ -994,11 +1088,10 @@ arr_ravel_multi_index(PyObject *self, PyObject *args, PyObject *kwds)
}
/* Get the multi_index into op */
- if (sequence_to_arrays(coords0, op, dimensions.len, "multi_index") < 0) {
+ if (int_sequence_to_arrays(coords0, "multi_index", dimensions.len, op) < 0) {
goto fail;
}
-
for (i = 0; i < dimensions.len; ++i) {
op_flags[i] = NPY_ITER_READONLY|
NPY_ITER_ALIGNED;
@@ -1065,67 +1158,44 @@ fail:
return NULL;
}
-/* C-order inner loop for unravel_index */
-static int
-unravel_index_loop_corder(int unravel_ndim, npy_intp *unravel_dims,
- npy_intp unravel_size, npy_intp count,
- char *indices, npy_intp indices_stride,
- npy_intp *coords)
-{
- int i;
- char invalid;
- npy_intp val;
- NPY_BEGIN_ALLOW_THREADS;
- invalid = 0;
- while (count--) {
- val = *(npy_intp *)indices;
- if (val < 0 || val >= unravel_size) {
- invalid = 1;
- break;
- }
- for (i = unravel_ndim-1; i >= 0; --i) {
- coords[i] = val % unravel_dims[i];
- val /= unravel_dims[i];
- }
- coords += unravel_ndim;
- indices += indices_stride;
- }
- NPY_END_ALLOW_THREADS;
- if (invalid) {
- PyErr_Format(PyExc_ValueError,
- "index %" NPY_INTP_FMT " is out of bounds for array with size "
- "%" NPY_INTP_FMT,
- val, unravel_size
- );
- return NPY_FAIL;
- }
- return NPY_SUCCEED;
-}
-
-/* Fortran-order inner loop for unravel_index */
+/*
+ * Inner loop for unravel_index
+ * order must be NPY_CORDER or NPY_FORTRANORDER
+ */
static int
-unravel_index_loop_forder(int unravel_ndim, npy_intp *unravel_dims,
- npy_intp unravel_size, npy_intp count,
- char *indices, npy_intp indices_stride,
- npy_intp *coords)
+unravel_index_loop(int unravel_ndim, npy_intp *unravel_dims,
+ npy_intp unravel_size, npy_intp count,
+ char *indices, npy_intp indices_stride,
+ npy_intp *coords, NPY_ORDER order)
{
- int i;
- char invalid;
- npy_intp val;
+ int i, idx;
+ int idx_start = (order == NPY_CORDER) ? unravel_ndim - 1: 0;
+ int idx_step = (order == NPY_CORDER) ? -1 : 1;
+ char invalid = 0;
+ npy_intp val = 0;
NPY_BEGIN_ALLOW_THREADS;
- invalid = 0;
+ /* NPY_KEEPORDER or NPY_ANYORDER have no meaning in this setting */
+ assert(order == NPY_CORDER || order == NPY_FORTRANORDER);
while (count--) {
val = *(npy_intp *)indices;
if (val < 0 || val >= unravel_size) {
invalid = 1;
break;
}
+ idx = idx_start;
for (i = 0; i < unravel_ndim; ++i) {
- *coords++ = val % unravel_dims[i];
- val /= unravel_dims[i];
+ /*
+ * Using a local seems to enable single-divide optimization
+ * but only if the / precedes the %
+ */
+ npy_intp tmp = val / unravel_dims[idx];
+ coords[idx] = val % unravel_dims[idx];
+ val = tmp;
+ idx += idx_step;
}
+ coords += unravel_ndim;
indices += indices_stride;
}
NPY_END_ALLOW_THREADS;
@@ -1144,11 +1214,12 @@ unravel_index_loop_forder(int unravel_ndim, npy_intp *unravel_dims,
NPY_NO_EXPORT PyObject *
arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds)
{
- PyObject *indices0 = NULL, *ret_tuple = NULL;
+ PyObject *indices0 = NULL;
+ PyObject *ret_tuple = NULL;
PyArrayObject *ret_arr = NULL;
PyArrayObject *indices = NULL;
PyArray_Descr *dtype = NULL;
- PyArray_Dims dimensions={0,0};
+ PyArray_Dims dimensions = {0, 0};
NPY_ORDER order = NPY_CORDER;
npy_intp unravel_size;
@@ -1156,7 +1227,32 @@ arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds)
int i, ret_ndim;
npy_intp ret_dims[NPY_MAXDIMS], ret_strides[NPY_MAXDIMS];
- char *kwlist[] = {"indices", "dims", "order", NULL};
+ char *kwlist[] = {"indices", "shape", "order", NULL};
+
+ /*
+ * TODO: remove this in favor of warning raised in the dispatcher when
+ * __array_function__ is enabled by default.
+ */
+
+ /*
+ * Continue to support the older "dims" argument in place
+ * of the "shape" argument. Issue an appropriate warning
+ * if "dims" is detected in keywords, then replace it with
+ * the new "shape" argument and continue processing as usual.
+ */
+ if (kwds) {
+ PyObject *dims_item, *shape_item;
+ dims_item = PyDict_GetItemString(kwds, "dims");
+ shape_item = PyDict_GetItemString(kwds, "shape");
+ if (dims_item != NULL && shape_item == NULL) {
+ if (DEPRECATE("'shape' argument should be"
+ " used instead of 'dims'") < 0) {
+ return NULL;
+ }
+ PyDict_SetItemString(kwds, "shape", dims_item);
+ PyDict_DelItemString(kwds, "dims");
+ }
+ }
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&|O&:unravel_index",
kwlist,
@@ -1166,17 +1262,17 @@ arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds)
goto fail;
}
- unravel_size = PyArray_MultiplyList(dimensions.ptr, dimensions.len);
-
- if (!PyArray_Check(indices0)) {
- indices = (PyArrayObject*)PyArray_FROM_O(indices0);
- if (indices == NULL) {
- goto fail;
- }
+ unravel_size = PyArray_OverflowMultiplyList(dimensions.ptr, dimensions.len);
+ if (unravel_size == -1) {
+ PyErr_SetString(PyExc_ValueError,
+ "dimensions are too large; arrays and shapes with "
+ "a total size greater than 'intp' are not supported.");
+ goto fail;
}
- else {
- indices = (PyArrayObject *)indices0;
- Py_INCREF(indices);
+
+ indices = astype_anyint(indices0);
+ if (indices == NULL) {
+ goto fail;
}
dtype = PyArray_DescrFromType(NPY_INTP);
@@ -1226,64 +1322,35 @@ arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds)
goto fail;
}
- if (order == NPY_CORDER) {
- if (NpyIter_GetIterSize(iter) != 0) {
- NpyIter_IterNextFunc *iternext;
- char **dataptr;
- npy_intp *strides;
- npy_intp *countptr, count;
- npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr);
+ if (order != NPY_CORDER && order != NPY_FORTRANORDER) {
+ PyErr_SetString(PyExc_ValueError,
+ "only 'C' or 'F' order is permitted");
+ goto fail;
+ }
+ if (NpyIter_GetIterSize(iter) != 0) {
+ NpyIter_IterNextFunc *iternext;
+ char **dataptr;
+ npy_intp *strides;
+ npy_intp *countptr, count;
+ npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr);
- iternext = NpyIter_GetIterNext(iter, NULL);
- if (iternext == NULL) {
- goto fail;
- }
- dataptr = NpyIter_GetDataPtrArray(iter);
- strides = NpyIter_GetInnerStrideArray(iter);
- countptr = NpyIter_GetInnerLoopSizePtr(iter);
-
- do {
- count = *countptr;
- if (unravel_index_loop_corder(dimensions.len, dimensions.ptr,
- unravel_size, count, *dataptr, *strides,
- coordsptr) != NPY_SUCCEED) {
- goto fail;
- }
- coordsptr += count*dimensions.len;
- } while(iternext(iter));
+ iternext = NpyIter_GetIterNext(iter, NULL);
+ if (iternext == NULL) {
+ goto fail;
}
- }
- else if (order == NPY_FORTRANORDER) {
- if (NpyIter_GetIterSize(iter) != 0) {
- NpyIter_IterNextFunc *iternext;
- char **dataptr;
- npy_intp *strides;
- npy_intp *countptr, count;
- npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr);
+ dataptr = NpyIter_GetDataPtrArray(iter);
+ strides = NpyIter_GetInnerStrideArray(iter);
+ countptr = NpyIter_GetInnerLoopSizePtr(iter);
- iternext = NpyIter_GetIterNext(iter, NULL);
- if (iternext == NULL) {
+ do {
+ count = *countptr;
+ if (unravel_index_loop(dimensions.len, dimensions.ptr,
+ unravel_size, count, *dataptr, *strides,
+ coordsptr, order) != NPY_SUCCEED) {
goto fail;
}
- dataptr = NpyIter_GetDataPtrArray(iter);
- strides = NpyIter_GetInnerStrideArray(iter);
- countptr = NpyIter_GetInnerLoopSizePtr(iter);
-
- do {
- count = *countptr;
- if (unravel_index_loop_forder(dimensions.len, dimensions.ptr,
- unravel_size, count, *dataptr, *strides,
- coordsptr) != NPY_SUCCEED) {
- goto fail;
- }
- coordsptr += count*dimensions.len;
- } while(iternext(iter));
- }
- }
- else {
- PyErr_SetString(PyExc_ValueError,
- "only 'C' or 'F' order is permitted");
- goto fail;
+ coordsptr += count * dimensions.len;
+ } while (iternext(iter));
}
@@ -1467,7 +1534,8 @@ pack_inner(const char *inptr,
npy_intp in_stride,
char *outptr,
npy_intp n_out,
- npy_intp out_stride)
+ npy_intp out_stride,
+ char order)
{
/*
* Loop through the elements of inptr.
@@ -1487,9 +1555,13 @@ pack_inner(const char *inptr,
vn_out -= (vn_out & 1);
for (index = 0; index < vn_out; index += 2) {
unsigned int r;
- /* swap as packbits is "big endian", note x86 can load unaligned */
- npy_uint64 a = npy_bswap8(*(npy_uint64*)inptr);
- npy_uint64 b = npy_bswap8(*(npy_uint64*)(inptr + 8));
+ npy_uint64 a = *(npy_uint64*)inptr;
+ npy_uint64 b = *(npy_uint64*)(inptr + 8);
+ if (order == 'b') {
+ a = npy_bswap8(a);
+ b = npy_bswap8(b);
+ }
+ /* note x86 can load unaligned */
__m128i v = _mm_set_epi64(_m_from_int64(b), _m_from_int64(a));
/* false -> 0x00 and true -> 0xFF (there is no cmpneq) */
v = _mm_cmpeq_epi8(v, zero);
@@ -1509,30 +1581,45 @@ pack_inner(const char *inptr,
if (remain == 0) { /* assumes n_in > 0 */
remain = 8;
}
- /* don't reset index to handle remainder of above block */
+ /* Don't reset index. Just handle remainder of above block */
for (; index < n_out; index++) {
- char build = 0;
+ unsigned char build = 0;
int i, maxi;
npy_intp j;
maxi = (index == n_out - 1) ? remain : 8;
- for (i = 0; i < maxi; i++) {
- build <<= 1;
- for (j = 0; j < element_size; j++) {
- build |= (inptr[j] != 0);
+ if (order == 'b') {
+ for (i = 0; i < maxi; i++) {
+ build <<= 1;
+ for (j = 0; j < element_size; j++) {
+ build |= (inptr[j] != 0);
+ }
+ inptr += in_stride;
+ }
+ if (index == n_out - 1) {
+ build <<= 8 - remain;
}
- inptr += in_stride;
}
- if (index == n_out - 1) {
- build <<= 8 - remain;
+ else
+ {
+ for (i = 0; i < maxi; i++) {
+ build >>= 1;
+ for (j = 0; j < element_size; j++) {
+ build |= (inptr[j] != 0) ? 128 : 0;
+ }
+ inptr += in_stride;
+ }
+ if (index == n_out - 1) {
+ build >>= 8 - remain;
+ }
}
- *outptr = build;
+ *outptr = (char)build;
outptr += out_stride;
}
}
static PyObject *
-pack_bits(PyObject *input, int axis)
+pack_bits(PyObject *input, int axis, char order)
{
PyArrayObject *inp;
PyArrayObject *new = NULL;
@@ -1550,6 +1637,7 @@ pack_bits(PyObject *input, int axis)
if (!PyArray_ISBOOL(inp) && !PyArray_ISINTEGER(inp)) {
PyErr_SetString(PyExc_TypeError,
"Expected an input array of integer or boolean data type");
+ Py_DECREF(inp);
goto fail;
}
@@ -1616,7 +1704,7 @@ pack_bits(PyObject *input, int axis)
pack_inner(PyArray_ITER_DATA(it), PyArray_ITEMSIZE(new),
PyArray_DIM(new, axis), PyArray_STRIDE(new, axis),
PyArray_ITER_DATA(ot), PyArray_DIM(out, axis),
- PyArray_STRIDE(out, axis));
+ PyArray_STRIDE(out, axis), order);
PyArray_ITER_NEXT(it);
PyArray_ITER_NEXT(ot);
}
@@ -1636,17 +1724,24 @@ fail:
}
static PyObject *
-unpack_bits(PyObject *input, int axis)
+unpack_bits(PyObject *input, int axis, PyObject *count_obj, char order)
{
static int unpack_init = 0;
- static char unpack_lookup[256][8];
+ /*
+ * lookuptable for bitorder big as it has been around longer
+ * bitorder little is handled via byteswapping in the loop
+ */
+ static union {
+ npy_uint8 bytes[8];
+ npy_uint64 uint64;
+ } unpack_lookup_big[256];
PyArrayObject *inp;
PyArrayObject *new = NULL;
PyArrayObject *out = NULL;
npy_intp outdims[NPY_MAXDIMS];
int i;
PyArrayIterObject *it, *ot;
- npy_intp n_in, in_stride, out_stride;
+ npy_intp count, in_n, in_tail, out_pad, in_stride, out_stride;
NPY_BEGIN_THREADS_DEF;
inp = (PyArrayObject *)PyArray_FROM_O(input);
@@ -1657,6 +1752,7 @@ unpack_bits(PyObject *input, int axis)
if (PyArray_TYPE(inp) != NPY_UBYTE) {
PyErr_SetString(PyExc_TypeError,
"Expected an input array of unsigned byte data type");
+ Py_DECREF(inp);
goto fail;
}
@@ -1674,20 +1770,37 @@ unpack_bits(PyObject *input, int axis)
newdim.ptr = &shape;
temp = (PyArrayObject *)PyArray_Newshape(new, &newdim, NPY_CORDER);
+ Py_DECREF(new);
if (temp == NULL) {
- goto fail;
+ return NULL;
}
- Py_DECREF(new);
new = temp;
}
/* Setup output shape */
- for (i=0; i<PyArray_NDIM(new); i++) {
+ for (i = 0; i < PyArray_NDIM(new); i++) {
outdims[i] = PyArray_DIM(new, i);
}
/* Multiply axis dimension by 8 */
- outdims[axis] <<= 3;
+ outdims[axis] *= 8;
+ if (count_obj != Py_None) {
+ count = PyArray_PyIntAsIntp(count_obj);
+ if (error_converting(count)) {
+ goto fail;
+ }
+ if (count < 0) {
+ outdims[axis] += count;
+ if (outdims[axis] < 0) {
+ PyErr_Format(PyExc_ValueError,
+ "-count larger than number of elements");
+ goto fail;
+ }
+ }
+ else {
+ outdims[axis] = count;
+ }
+ }
/* Create output array */
out = (PyArrayObject *)PyArray_NewFromDescr(
@@ -1697,6 +1810,7 @@ unpack_bits(PyObject *input, int axis)
if (out == NULL) {
goto fail;
}
+
/* Setup iterators to iterate over all but given axis */
it = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)new, &axis);
ot = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)out, &axis);
@@ -1706,34 +1820,39 @@ unpack_bits(PyObject *input, int axis)
goto fail;
}
- /* setup lookup table under GIL, big endian 0..256 as bytes */
+ /*
+ * setup lookup table under GIL, 256 8 byte blocks representing 8 bits
+ * expanded to 1/0 bytes
+ */
if (unpack_init == 0) {
- npy_uint64 j;
- npy_uint64 * unpack_lookup_64 = (npy_uint64 *)unpack_lookup;
+ npy_intp j;
for (j=0; j < 256; j++) {
- npy_uint64 v = 0;
- v |= (npy_uint64)((j & 1) == 1);
- v |= (npy_uint64)((j & 2) == 2) << 8;
- v |= (npy_uint64)((j & 4) == 4) << 16;
- v |= (npy_uint64)((j & 8) == 8) << 24;
- v |= (npy_uint64)((j & 16) == 16) << 32;
- v |= (npy_uint64)((j & 32) == 32) << 40;
- v |= (npy_uint64)((j & 64) == 64) << 48;
- v |= (npy_uint64)((j & 128) == 128) << 56;
-#if NPY_BYTE_ORDER == NPY_LITTLE_ENDIAN
- v = npy_bswap8(v);
-#endif
- unpack_lookup_64[j] = v;
+ npy_intp k;
+ for (k=0; k < 8; k++) {
+ npy_uint8 v = (j & (1 << k)) == (1 << k);
+ unpack_lookup_big[j].bytes[7 - k] = v;
+ }
}
unpack_init = 1;
}
- NPY_BEGIN_THREADS_THRESHOLDED(PyArray_DIM(new, axis));
+ count = PyArray_DIM(new, axis) * 8;
+ if (outdims[axis] > count) {
+ in_n = count / 8;
+ in_tail = 0;
+ out_pad = outdims[axis] - count;
+ }
+ else {
+ in_n = outdims[axis] / 8;
+ in_tail = outdims[axis] % 8;
+ out_pad = 0;
+ }
- n_in = PyArray_DIM(new, axis);
in_stride = PyArray_STRIDE(new, axis);
out_stride = PyArray_STRIDE(out, axis);
+ NPY_BEGIN_THREADS_THRESHOLDED(PyArray_Size((PyObject *)out) / 8);
+
while (PyArray_ITER_NOTDONE(it)) {
npy_intp index;
unsigned const char *inptr = PyArray_ITER_DATA(it);
@@ -1741,24 +1860,74 @@ unpack_bits(PyObject *input, int axis)
if (out_stride == 1) {
/* for unity stride we can just copy out of the lookup table */
- for (index = 0; index < n_in; index++) {
- memcpy(outptr, unpack_lookup[*inptr], 8);
- outptr += 8;
- inptr += in_stride;
+ if (order == 'b') {
+ for (index = 0; index < in_n; index++) {
+ npy_uint64 v = unpack_lookup_big[*inptr].uint64;
+ memcpy(outptr, &v, 8);
+ outptr += 8;
+ inptr += in_stride;
+ }
+ }
+ else {
+ for (index = 0; index < in_n; index++) {
+ npy_uint64 v = unpack_lookup_big[*inptr].uint64;
+ if (order != 'b') {
+ v = npy_bswap8(v);
+ }
+ memcpy(outptr, &v, 8);
+ outptr += 8;
+ inptr += in_stride;
+ }
+ }
+ /* Clean up the tail portion */
+ if (in_tail) {
+ npy_uint64 v = unpack_lookup_big[*inptr].uint64;
+ if (order != 'b') {
+ v = npy_bswap8(v);
+ }
+ memcpy(outptr, &v, in_tail);
+ }
+ /* Add padding */
+ else if (out_pad) {
+ memset(outptr, 0, out_pad);
}
}
else {
- for (index = 0; index < n_in; index++) {
- unsigned char mask = 128;
-
- for (i = 0; i < 8; i++) {
- *outptr = ((mask & (*inptr)) != 0);
+ if (order == 'b') {
+ for (index = 0; index < in_n; index++) {
+ for (i = 0; i < 8; i++) {
+ *outptr = ((*inptr & (128 >> i)) != 0);
+ outptr += out_stride;
+ }
+ inptr += in_stride;
+ }
+ /* Clean up the tail portion */
+ for (i = 0; i < in_tail; i++) {
+ *outptr = ((*inptr & (128 >> i)) != 0);
+ outptr += out_stride;
+ }
+ }
+ else {
+ for (index = 0; index < in_n; index++) {
+ for (i = 0; i < 8; i++) {
+ *outptr = ((*inptr & (1 << i)) != 0);
+ outptr += out_stride;
+ }
+ inptr += in_stride;
+ }
+ /* Clean up the tail portion */
+ for (i = 0; i < in_tail; i++) {
+ *outptr = ((*inptr & (1 << i)) != 0);
outptr += out_stride;
- mask >>= 1;
}
- inptr += in_stride;
+ }
+ /* Add padding */
+ for (index = 0; index < out_pad; index++) {
+ *outptr = 0;
+ outptr += out_stride;
}
}
+
PyArray_ITER_NEXT(it);
PyArray_ITER_NEXT(ot);
}
@@ -1782,25 +1951,49 @@ io_pack(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
PyObject *obj;
int axis = NPY_MAXDIMS;
- static char *kwlist[] = {"in", "axis", NULL};
+ static char *kwlist[] = {"in", "axis", "bitorder", NULL};
+ char c = 'b';
+ const char * order_str = NULL;
- if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&:pack" , kwlist,
- &obj, PyArray_AxisConverter, &axis)) {
+ if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&s:pack" , kwlist,
+ &obj, PyArray_AxisConverter, &axis, &order_str)) {
return NULL;
}
- return pack_bits(obj, axis);
+ if (order_str != NULL) {
+ if (strncmp(order_str, "little", 6) == 0)
+ c = 'l';
+ else if (strncmp(order_str, "big", 3) == 0)
+ c = 'b';
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "'order' must be either 'little' or 'big'");
+ return NULL;
+ }
+ }
+ return pack_bits(obj, axis, c);
}
+
NPY_NO_EXPORT PyObject *
io_unpack(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
PyObject *obj;
int axis = NPY_MAXDIMS;
- static char *kwlist[] = {"in", "axis", NULL};
+ PyObject *count = Py_None;
+ static char *kwlist[] = {"in", "axis", "count", "bitorder", NULL};
+ const char * c = NULL;
- if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&:unpack" , kwlist,
- &obj, PyArray_AxisConverter, &axis)) {
+ if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&Os:unpack" , kwlist,
+ &obj, PyArray_AxisConverter, &axis, &count, &c)) {
+ return NULL;
+ }
+ if (c == NULL) {
+ c = "b";
+ }
+ if (c[0] != 'l' && c[0] != 'b') {
+ PyErr_SetString(PyExc_ValueError,
+ "'order' must begin with 'l' or 'b'");
return NULL;
}
- return unpack_bits(obj, axis);
+ return unpack_bits(obj, axis, count, c[0]);
}
diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c
index 7e92e5991..4baa02052 100644
--- a/numpy/core/src/multiarray/conversion_utils.c
+++ b/numpy/core/src/multiarray/conversion_utils.c
@@ -16,6 +16,7 @@
#include "conversion_utils.h"
#include "alloc.h"
+#include "buffer.h"
static int
PyArray_PyIntAsInt_ErrMsg(PyObject *o, const char * msg) NPY_GCC_NONNULL(2);
@@ -115,8 +116,8 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq)
return NPY_FAIL;
}
if (len > NPY_MAXDIMS) {
- PyErr_Format(PyExc_ValueError, "sequence too large; "
- "cannot be greater than %d", NPY_MAXDIMS);
+ PyErr_Format(PyExc_ValueError, "maximum supported dimension for an ndarray is %d"
+ ", found %d", NPY_MAXDIMS, len);
return NPY_FAIL;
}
if (len > 0) {
@@ -185,6 +186,7 @@ PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf)
* sticks around after the release.
*/
PyBuffer_Release(&view);
+ _dealloc_cached_buffer_info(obj);
/* Point to the base of the buffer object if present */
if (PyMemoryView_Check(obj)) {
@@ -391,6 +393,11 @@ PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind)
char *str;
PyObject *tmp = NULL;
+ if (obj == Py_None) {
+ *sortkind = NPY_QUICKSORT;
+ return NPY_SUCCEED;
+ }
+
if (PyUnicode_Check(obj)) {
obj = tmp = PyUnicode_AsASCIIString(obj);
if (obj == NULL) {
@@ -399,6 +406,7 @@ PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind)
}
*sortkind = NPY_QUICKSORT;
+
str = PyBytes_AsString(obj);
if (!str) {
Py_XDECREF(tmp);
@@ -417,11 +425,23 @@ PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind)
*sortkind = NPY_HEAPSORT;
}
else if (str[0] == 'm' || str[0] == 'M') {
+ /*
+ * Mergesort is an alias for NPY_STABLESORT.
+ * That maintains backwards compatibility while
+ * allowing other types of stable sorts to be used.
+ */
*sortkind = NPY_MERGESORT;
}
else if (str[0] == 's' || str[0] == 'S') {
- /* mergesort is the only stable sorting method in numpy */
- *sortkind = NPY_MERGESORT;
+ /*
+ * NPY_STABLESORT is one of
+ *
+ * - mergesort
+ * - timsort
+ *
+ * Which one is used depends on the data type.
+ */
+ *sortkind = NPY_STABLESORT;
}
else {
PyErr_Format(PyExc_ValueError,
@@ -530,10 +550,9 @@ PyArray_OrderConverter(PyObject *object, NPY_ORDER *val)
int ret;
tmp = PyUnicode_AsASCIIString(object);
if (tmp == NULL) {
- PyErr_SetString(PyExc_ValueError, "Invalid unicode string passed in "
- "for the array ordering. "
- "Please pass in 'C', 'F', 'A' "
- "or 'K' instead");
+ PyErr_SetString(PyExc_ValueError,
+ "Invalid unicode string passed in for the array ordering. "
+ "Please pass in 'C', 'F', 'A' or 'K' instead");
return NPY_FAIL;
}
ret = PyArray_OrderConverter(tmp, val);
@@ -541,38 +560,18 @@ PyArray_OrderConverter(PyObject *object, NPY_ORDER *val)
return ret;
}
else if (!PyBytes_Check(object) || PyBytes_GET_SIZE(object) < 1) {
- /* 2015-12-14, 1.11 */
- int ret = DEPRECATE("Non-string object detected for "
- "the array ordering. Please pass "
- "in 'C', 'F', 'A', or 'K' instead");
-
- if (ret < 0) {
- return -1;
- }
-
- if (PyObject_IsTrue(object)) {
- *val = NPY_FORTRANORDER;
- }
- else {
- *val = NPY_CORDER;
- }
- if (PyErr_Occurred()) {
- return NPY_FAIL;
- }
- return NPY_SUCCEED;
+ PyErr_SetString(PyExc_ValueError,
+ "Non-string object detected for the array ordering. "
+ "Please pass in 'C', 'F', 'A', or 'K' instead");
+ return NPY_FAIL;
}
else {
str = PyBytes_AS_STRING(object);
if (strlen(str) != 1) {
- /* 2015-12-14, 1.11 */
- int ret = DEPRECATE("Non length-one string passed "
- "in for the array ordering. "
- "Please pass in 'C', 'F', 'A', "
- "or 'K' instead");
-
- if (ret < 0) {
- return -1;
- }
+ PyErr_SetString(PyExc_ValueError,
+ "Non-string object detected for the array ordering. "
+ "Please pass in 'C', 'F', 'A', or 'K' instead");
+ return NPY_FAIL;
}
if (str[0] == 'C' || str[0] == 'c') {
diff --git a/numpy/core/src/multiarray/convert.c b/numpy/core/src/multiarray/convert.c
index e88582a51..aa4e40e66 100644
--- a/numpy/core/src/multiarray/convert.c
+++ b/numpy/core/src/multiarray/convert.c
@@ -543,35 +543,6 @@ PyArray_AssignZero(PyArrayObject *dst,
return retcode;
}
-/*
- * Fills an array with ones.
- *
- * dst: The destination array.
- * wheremask: If non-NULL, a boolean mask specifying where to set the values.
- *
- * Returns 0 on success, -1 on failure.
- */
-NPY_NO_EXPORT int
-PyArray_AssignOne(PyArrayObject *dst,
- PyArrayObject *wheremask)
-{
- npy_bool value;
- PyArray_Descr *bool_dtype;
- int retcode;
-
- /* Create a raw bool scalar with the value True */
- bool_dtype = PyArray_DescrFromType(NPY_BOOL);
- if (bool_dtype == NULL) {
- return -1;
- }
- value = 1;
-
- retcode = PyArray_AssignRawScalar(dst, bool_dtype, (char *)&value,
- wheremask, NPY_SAFE_CASTING);
-
- Py_DECREF(bool_dtype);
- return retcode;
-}
/*NUMPY_API
* Copy an array.
@@ -614,22 +585,6 @@ PyArray_View(PyArrayObject *self, PyArray_Descr *type, PyTypeObject *pytype)
}
dtype = PyArray_DESCR(self);
-
- if (type != NULL && !PyArray_EquivTypes(dtype, type) &&
- (PyArray_FLAGS(self) & NPY_ARRAY_WARN_ON_WRITE)) {
- const char *msg =
- "Numpy has detected that you may be viewing or writing to an array "
- "returned by selecting multiple fields in a structured array. \n\n"
- "This code may break in numpy 1.16 because this will return a view "
- "instead of a copy -- see release notes for details.";
- /* 2016-09-19, 1.12 */
- if (DEPRECATE_FUTUREWARNING(msg) < 0) {
- return NULL;
- }
- /* Only warn once per array */
- PyArray_CLEARFLAGS(self, NPY_ARRAY_WARN_ON_WRITE);
- }
-
flags = PyArray_FLAGS(self);
Py_INCREF(dtype);
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index 33a706412..025c66013 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -47,11 +47,10 @@ PyArray_CastToType(PyArrayObject *arr, PyArray_Descr *dtype, int is_f_order)
PyObject *out;
/* If the requested dtype is flexible, adapt it */
- PyArray_AdaptFlexibleDType((PyObject *)arr, PyArray_DESCR(arr), &dtype);
+ dtype = PyArray_AdaptFlexibleDType((PyObject *)arr, PyArray_DESCR(arr), dtype);
if (dtype == NULL) {
return NULL;
}
-
out = PyArray_NewFromDescr(Py_TYPE(arr), dtype,
PyArray_NDIM(arr),
PyArray_DIMS(arr),
@@ -128,9 +127,9 @@ PyArray_GetCastFunc(PyArray_Descr *descr, int type_num)
}
/*
- * This function calls Py_DECREF on flex_dtype, and replaces it with
- * a new dtype that has been adapted based on the values in data_dtype
- * and data_obj. If the flex_dtype is not flexible, it leaves it as is.
+ * This function returns a dtype based on flex_dtype and the values in
+ * data_dtype and data_obj. It also calls Py_DECREF on the flex_dtype. If the
+ * flex_dtype is not flexible, it returns it as-is.
*
* Usually, if data_obj is not an array, dtype should be the result
* given by the PyArray_GetArrayParamsFromObject function.
@@ -138,40 +137,37 @@ PyArray_GetCastFunc(PyArray_Descr *descr, int type_num)
* The data_obj may be NULL if just a dtype is known for the source.
*
* If *flex_dtype is NULL, returns immediately, without setting an
- * exception. This basically assumes an error was already set previously.
+ * exception, leaving any previous error handling intact.
*
* The current flexible dtypes include NPY_STRING, NPY_UNICODE, NPY_VOID,
* and NPY_DATETIME with generic units.
*/
-NPY_NO_EXPORT void
+NPY_NO_EXPORT PyArray_Descr *
PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
- PyArray_Descr **flex_dtype)
+ PyArray_Descr *flex_dtype)
{
PyArray_DatetimeMetaData *meta;
+ PyArray_Descr *retval = NULL;
int flex_type_num;
- if (*flex_dtype == NULL) {
- if (!PyErr_Occurred()) {
- PyErr_SetString(PyExc_RuntimeError,
- "NumPy AdaptFlexibleDType was called with NULL flex_dtype "
- "but no error set");
- }
- return;
+ if (flex_dtype == NULL) {
+ return retval;
}
- flex_type_num = (*flex_dtype)->type_num;
+ flex_type_num = flex_dtype->type_num;
/* Flexible types with expandable size */
- if (PyDataType_ISUNSIZED(*flex_dtype)) {
+ if (PyDataType_ISUNSIZED(flex_dtype)) {
/* First replace the flex_dtype */
- PyArray_DESCR_REPLACE(*flex_dtype);
- if (*flex_dtype == NULL) {
- return;
+ retval = PyArray_DescrNew(flex_dtype);
+ Py_DECREF(flex_dtype);
+ if (retval == NULL) {
+ return retval;
}
if (data_dtype->type_num == flex_type_num ||
flex_type_num == NPY_VOID) {
- (*flex_dtype)->elsize = data_dtype->elsize;
+ (retval)->elsize = data_dtype->elsize;
}
else if (flex_type_num == NPY_STRING || flex_type_num == NPY_UNICODE) {
npy_intp size = 8;
@@ -199,7 +195,7 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
}
else if (data_dtype->elsize > 8 ||
data_dtype->elsize < 0) {
- /*
+ /*
* Element size should never be greater than 8 or
* less than 0 for integer type, but just in case...
*/
@@ -237,9 +233,8 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
PyObject *s = PyObject_Str(list);
if (s == NULL) {
Py_DECREF(list);
- Py_DECREF(*flex_dtype);
- *flex_dtype = NULL;
- return;
+ Py_DECREF(retval);
+ return NULL;
}
else {
size = PyObject_Length(s);
@@ -262,9 +257,16 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
list = PyArray_ToList((PyArrayObject *)data_obj);
result = PyArray_GetArrayParamsFromObject(
list,
- *flex_dtype,
+ retval,
0, &dtype,
&ndim, dims, &arr, NULL);
+ Py_DECREF(list);
+ Py_XDECREF(arr);
+ if (result < 0) {
+ Py_XDECREF(dtype);
+ Py_DECREF(retval);
+ return NULL;
+ }
if (result == 0 && dtype != NULL) {
if (flex_type_num == NPY_UNICODE) {
size = dtype->elsize / 4;
@@ -274,15 +276,12 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
}
}
Py_XDECREF(dtype);
- Py_XDECREF(arr);
- Py_DECREF(list);
}
else if (PyArray_IsPythonScalar(data_obj)) {
PyObject *s = PyObject_Str(data_obj);
if (s == NULL) {
- Py_DECREF(*flex_dtype);
- *flex_dtype = NULL;
- return;
+ Py_DECREF(retval);
+ return NULL;
}
else {
size = PyObject_Length(s);
@@ -301,9 +300,8 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
case NPY_DATETIME:
meta = get_datetime_metadata_from_dtype(data_dtype);
if (meta == NULL) {
- Py_DECREF(*flex_dtype);
- *flex_dtype = NULL;
- return;
+ Py_DECREF(retval);
+ return NULL;
}
size = get_datetime_iso_8601_strlen(0, meta->base);
break;
@@ -313,10 +311,10 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
}
if (flex_type_num == NPY_STRING) {
- (*flex_dtype)->elsize = size;
+ retval->elsize = size;
}
else if (flex_type_num == NPY_UNICODE) {
- (*flex_dtype)->elsize = size * 4;
+ retval->elsize = size * 4;
}
}
else {
@@ -326,18 +324,17 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
*/
PyErr_SetString(PyExc_TypeError,
"don't know how to adapt flex dtype");
- *flex_dtype = NULL;
- return;
+ Py_DECREF(retval);
+ return NULL;
}
}
/* Flexible type with generic time unit that adapts */
else if (flex_type_num == NPY_DATETIME ||
flex_type_num == NPY_TIMEDELTA) {
- meta = get_datetime_metadata_from_dtype(*flex_dtype);
+ meta = get_datetime_metadata_from_dtype(flex_dtype);
+ retval = flex_dtype;
if (meta == NULL) {
- Py_DECREF(*flex_dtype);
- *flex_dtype = NULL;
- return;
+ return NULL;
}
if (meta->base == NPY_FR_GENERIC) {
@@ -345,22 +342,24 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
data_dtype->type_num == NPY_TIMEDELTA) {
meta = get_datetime_metadata_from_dtype(data_dtype);
if (meta == NULL) {
- Py_DECREF(*flex_dtype);
- *flex_dtype = NULL;
- return;
+ return NULL;
}
- Py_DECREF(*flex_dtype);
- *flex_dtype = create_datetime_dtype(flex_type_num, meta);
+ retval = create_datetime_dtype(flex_type_num, meta);
+ Py_DECREF(flex_dtype);
}
else if (data_obj != NULL) {
/* Detect the unit from the input's data */
- Py_DECREF(*flex_dtype);
- *flex_dtype = find_object_datetime_type(data_obj,
+ retval = find_object_datetime_type(data_obj,
flex_type_num);
+ Py_DECREF(flex_dtype);
}
}
}
+ else {
+ retval = flex_dtype;
+ }
+ return retval;
}
/*
@@ -518,7 +517,7 @@ PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to)
* stringified value of the object.
*/
else if (to_type_num == NPY_STRING || to_type_num == NPY_UNICODE) {
- /*
+ /*
* Boolean value cast to string type is 5 characters max
* for string 'False'.
*/
@@ -531,7 +530,7 @@ PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to)
if (PyDataType_ISUNSIZED(to)) {
ret = 1;
}
- /*
+ /*
* Need at least 5 characters to convert from boolean
* to 'True' or 'False'.
*/
@@ -680,15 +679,82 @@ NPY_NO_EXPORT npy_bool
PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to,
NPY_CASTING casting)
{
- /* Fast path for unsafe casts or basic types */
- if (casting == NPY_UNSAFE_CASTING ||
- (NPY_LIKELY(from->type_num < NPY_OBJECT) &&
- NPY_LIKELY(from->type_num == to->type_num) &&
- NPY_LIKELY(from->byteorder == to->byteorder))) {
+ /*
+ * Fast paths for equality and for basic types.
+ */
+ if (from == to ||
+ ((NPY_LIKELY(PyDataType_ISNUMBER(from)) ||
+ PyDataType_ISOBJECT(from)) &&
+ NPY_LIKELY(from->type_num == to->type_num) &&
+ NPY_LIKELY(from->byteorder == to->byteorder))) {
+ return 1;
+ }
+ /*
+ * Cases with subarrays and fields need special treatment.
+ */
+ if (PyDataType_HASFIELDS(from)) {
+ /*
+ * If from is a structured data type, then it can be cast to a simple
+ * non-object one only for unsafe casting *and* if it has a single
+ * field; recurse just in case the single field is itself structured.
+ */
+ if (!PyDataType_HASFIELDS(to) && !PyDataType_ISOBJECT(to)) {
+ if (casting == NPY_UNSAFE_CASTING &&
+ PyDict_Size(from->fields) == 1) {
+ Py_ssize_t ppos = 0;
+ PyObject *tuple;
+ PyArray_Descr *field;
+ PyDict_Next(from->fields, &ppos, NULL, &tuple);
+ field = (PyArray_Descr *)PyTuple_GET_ITEM(tuple, 0);
+ /*
+ * For a subarray, we need to get the underlying type;
+ * since we already are casting unsafely, we can ignore
+ * the shape.
+ */
+ if (PyDataType_HASSUBARRAY(field)) {
+ field = field->subarray->base;
+ }
+ return PyArray_CanCastTypeTo(field, to, casting);
+ }
+ else {
+ return 0;
+ }
+ }
+ /*
+ * Casting from one structured data type to another depends on the fields;
+ * we pass that case on to the EquivTypenums case below.
+ *
+ * TODO: move that part up here? Need to check whether equivalent type
+ * numbers is an addition constraint that is needed.
+ *
+ * TODO/FIXME: For now, always allow structured to structured for unsafe
+ * casting; this is not correct, but needed since the treatment in can_cast
+ * below got out of sync with astype; see gh-13667.
+ */
+ if (casting == NPY_UNSAFE_CASTING) {
+ return 1;
+ }
+ }
+ else if (PyDataType_HASFIELDS(to)) {
+ /*
+ * If "from" is a simple data type and "to" has fields, then only
+ * unsafe casting works (and that works always, even to multiple fields).
+ */
+ return casting == NPY_UNSAFE_CASTING;
+ }
+ /*
+ * Everything else we consider castable for unsafe for now.
+ * FIXME: ensure what we do here is consistent with "astype",
+ * i.e., deal more correctly with subarrays and user-defined dtype.
+ */
+ else if (casting == NPY_UNSAFE_CASTING) {
return 1;
}
- /* Equivalent types can be cast with any value of 'casting' */
- else if (PyArray_EquivTypenums(from->type_num, to->type_num)) {
+ /*
+ * Equivalent simple types can be cast with any value of 'casting', but
+ * we need to be careful about structured to structured.
+ */
+ if (PyArray_EquivTypenums(from->type_num, to->type_num)) {
/* For complicated case, use EquivTypes (for now) */
if (PyTypeNum_ISUSERDEF(from->type_num) ||
from->subarray != NULL) {
@@ -1166,7 +1232,11 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
PyArray_Descr *ret = NULL;
PyArray_Descr *temp = PyArray_DescrNew(type1);
PyDataType_MAKEUNSIZED(temp);
- PyArray_AdaptFlexibleDType(NULL, type2, &temp);
+
+ temp = PyArray_AdaptFlexibleDType(NULL, type2, temp);
+ if (temp == NULL) {
+ return NULL;
+ }
if (temp->elsize > type1->elsize) {
ret = ensure_dtype_nbo(temp);
}
@@ -1204,7 +1274,10 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
PyArray_Descr *ret = NULL;
PyArray_Descr *temp = PyArray_DescrNew(type1);
PyDataType_MAKEUNSIZED(temp);
- PyArray_AdaptFlexibleDType(NULL, type2, &temp);
+ temp = PyArray_AdaptFlexibleDType(NULL, type2, temp);
+ if (temp == NULL) {
+ return NULL;
+ }
if (temp->elsize > type1->elsize) {
ret = ensure_dtype_nbo(temp);
}
@@ -1252,7 +1325,10 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
PyArray_Descr *ret = NULL;
PyArray_Descr *temp = PyArray_DescrNew(type2);
PyDataType_MAKEUNSIZED(temp);
- PyArray_AdaptFlexibleDType(NULL, type1, &temp);
+ temp = PyArray_AdaptFlexibleDType(NULL, type1, temp);
+ if (temp == NULL) {
+ return NULL;
+ }
if (temp->elsize > type2->elsize) {
ret = ensure_dtype_nbo(temp);
}
@@ -1269,7 +1345,10 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2)
PyArray_Descr *ret = NULL;
PyArray_Descr *temp = PyArray_DescrNew(type2);
PyDataType_MAKEUNSIZED(temp);
- PyArray_AdaptFlexibleDType(NULL, type1, &temp);
+ temp = PyArray_AdaptFlexibleDType(NULL, type1, temp);
+ if (temp == NULL) {
+ return NULL;
+ }
if (temp->elsize > type2->elsize) {
ret = ensure_dtype_nbo(temp);
}
@@ -1688,46 +1767,22 @@ dtype_kind_to_simplified_ordering(char kind)
}
}
-/*NUMPY_API
- * Produces the result type of a bunch of inputs, using the UFunc
- * type promotion rules. Use this function when you have a set of
- * input arrays, and need to determine an output array dtype.
- *
- * If all the inputs are scalars (have 0 dimensions) or the maximum "kind"
- * of the scalars is greater than the maximum "kind" of the arrays, does
- * a regular type promotion.
- *
- * Otherwise, does a type promotion on the MinScalarType
- * of all the inputs. Data types passed directly are treated as array
- * types.
- *
+
+/*
+ * Determine if there is a mix of scalars and arrays/dtypes.
+ * If this is the case, the scalars should be handled as the minimum type
+ * capable of holding the value when the maximum "category" of the scalars
+ * surpasses the maximum "category" of the arrays/dtypes.
+ * If the scalars are of a lower or same category as the arrays, they may be
+ * demoted to a lower type within their category (the lowest type they can
+ * be cast to safely according to scalar casting rules).
*/
-NPY_NO_EXPORT PyArray_Descr *
-PyArray_ResultType(npy_intp narrs, PyArrayObject **arr,
- npy_intp ndtypes, PyArray_Descr **dtypes)
+NPY_NO_EXPORT int
+should_use_min_scalar(npy_intp narrs, PyArrayObject **arr,
+ npy_intp ndtypes, PyArray_Descr **dtypes)
{
- npy_intp i;
- int use_min_scalar;
+ int use_min_scalar = 0;
- /* If there's just one type, pass it through */
- if (narrs + ndtypes == 1) {
- PyArray_Descr *ret = NULL;
- if (narrs == 1) {
- ret = PyArray_DESCR(arr[0]);
- }
- else {
- ret = dtypes[0];
- }
- Py_INCREF(ret);
- return ret;
- }
-
- /*
- * Determine if there are any scalars, and if so, whether
- * the maximum "kind" of the scalars surpasses the maximum
- * "kind" of the arrays
- */
- use_min_scalar = 0;
if (narrs > 0) {
int all_scalars;
int max_scalar_kind = -1;
@@ -1736,7 +1791,7 @@ PyArray_ResultType(npy_intp narrs, PyArrayObject **arr,
all_scalars = (ndtypes > 0) ? 0 : 1;
/* Compute the maximum "kinds" and whether everything is scalar */
- for (i = 0; i < narrs; ++i) {
+ for (npy_intp i = 0; i < narrs; ++i) {
if (PyArray_NDIM(arr[i]) == 0) {
int kind = dtype_kind_to_simplified_ordering(
PyArray_DESCR(arr[i])->kind);
@@ -1757,7 +1812,7 @@ PyArray_ResultType(npy_intp narrs, PyArrayObject **arr,
* If the max scalar kind is bigger than the max array kind,
* finish computing the max array kind
*/
- for (i = 0; i < ndtypes; ++i) {
+ for (npy_intp i = 0; i < ndtypes; ++i) {
int kind = dtype_kind_to_simplified_ordering(dtypes[i]->kind);
if (kind > max_array_kind) {
max_array_kind = kind;
@@ -1769,6 +1824,44 @@ PyArray_ResultType(npy_intp narrs, PyArrayObject **arr,
use_min_scalar = 1;
}
}
+ return use_min_scalar;
+}
+
+
+/*NUMPY_API
+ * Produces the result type of a bunch of inputs, using the UFunc
+ * type promotion rules. Use this function when you have a set of
+ * input arrays, and need to determine an output array dtype.
+ *
+ * If all the inputs are scalars (have 0 dimensions) or the maximum "kind"
+ * of the scalars is greater than the maximum "kind" of the arrays, does
+ * a regular type promotion.
+ *
+ * Otherwise, does a type promotion on the MinScalarType
+ * of all the inputs. Data types passed directly are treated as array
+ * types.
+ *
+ */
+NPY_NO_EXPORT PyArray_Descr *
+PyArray_ResultType(npy_intp narrs, PyArrayObject **arr,
+ npy_intp ndtypes, PyArray_Descr **dtypes)
+{
+ npy_intp i;
+
+ /* If there's just one type, pass it through */
+ if (narrs + ndtypes == 1) {
+ PyArray_Descr *ret = NULL;
+ if (narrs == 1) {
+ ret = PyArray_DESCR(arr[0]);
+ }
+ else {
+ ret = dtypes[0];
+ }
+ Py_INCREF(ret);
+ return ret;
+ }
+
+ int use_min_scalar = should_use_min_scalar(narrs, arr, ndtypes, dtypes);
/* Loop through all the types, promoting them */
if (!use_min_scalar) {
diff --git a/numpy/core/src/multiarray/convert_datatype.h b/numpy/core/src/multiarray/convert_datatype.h
index bf77d699a..72867ead8 100644
--- a/numpy/core/src/multiarray/convert_datatype.h
+++ b/numpy/core/src/multiarray/convert_datatype.h
@@ -18,16 +18,28 @@ NPY_NO_EXPORT npy_bool
can_cast_scalar_to(PyArray_Descr *scal_type, char *scal_data,
PyArray_Descr *to, NPY_CASTING casting);
+NPY_NO_EXPORT int
+should_use_min_scalar(npy_intp narrs, PyArrayObject **arr,
+ npy_intp ndtypes, PyArray_Descr **dtypes);
+
/*
* This function calls Py_DECREF on flex_dtype, and replaces it with
* a new dtype that has been adapted based on the values in data_dtype
- * and data_obj. If the flex_dtype is not flexible, it leaves it as is.
+ * and data_obj. If the flex_dtype is not flexible, it returns it as-is.
+ *
+ * Usually, if data_obj is not an array, dtype should be the result
+ * given by the PyArray_GetArrayParamsFromObject function.
+ *
+ * The data_obj may be NULL if just a dtype is known for the source.
+ *
+ * If *flex_dtype is NULL, returns immediately, without setting an
+ * exception, leaving any previous error handling intact.
*
* The current flexible dtypes include NPY_STRING, NPY_UNICODE, NPY_VOID,
* and NPY_DATETIME with generic units.
*/
-NPY_NO_EXPORT void
+NPY_NO_EXPORT PyArray_Descr *
PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
- PyArray_Descr **flex_dtype);
+ PyArray_Descr *flex_dtype);
#endif
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index aaaaeee82..5174bd889 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -11,7 +11,7 @@
#include "npy_config.h"
-#include "npy_import.h"
+#include "npy_ctypes.h"
#include "npy_pycompat.h"
#include "multiarraymodule.h"
@@ -40,9 +40,31 @@
* regards to the handling of text representations.
*/
+/*
+ * Scanning function for next element parsing and seperator skipping.
+ * These functions return:
+ * - 0 to indicate more data to read
+ * - -1 when reading stopped at the end of the string/file
+ * - -2 when reading stopped before the end was reached.
+ *
+ * The dtype specific parsing functions may set the python error state
+ * (they have to get the GIL first) additionally.
+ */
typedef int (*next_element)(void **, void *, PyArray_Descr *, void *);
typedef int (*skip_separator)(void **, const char *, void *);
+
+static npy_bool
+string_is_fully_read(char const* start, char const* end) {
+ if (end == NULL) {
+ return *start == '\0'; /* null terminated */
+ }
+ else {
+ return start >= end; /* fixed length */
+ }
+}
+
+
static int
fromstr_next_element(char **s, void *dptr, PyArray_Descr *dtype,
const char *end)
@@ -50,19 +72,23 @@ fromstr_next_element(char **s, void *dptr, PyArray_Descr *dtype,
char *e = *s;
int r = dtype->f->fromstr(*s, dptr, &e, dtype);
/*
- * fromstr always returns 0 for basic dtypes
- * s points to the end of the parsed string
- * if an error occurs s is not changed
+ * fromstr always returns 0 for basic dtypes; s points to the end of the
+ * parsed string. If s is not changed an error occurred or the end was
+ * reached.
*/
- if (*s == e) {
- /* Nothing read */
- return -1;
+ if (*s == e || r < 0) {
+ /* Nothing read, could be end of string or an error (or both) */
+ if (string_is_fully_read(*s, end)) {
+ return -1;
+ }
+ return -2;
}
*s = e;
if (end != NULL && *s > end) {
+ /* Stop the iteration if we read far enough */
return -1;
}
- return r;
+ return 0;
}
static int
@@ -75,9 +101,13 @@ fromfile_next_element(FILE **fp, void *dptr, PyArray_Descr *dtype,
if (r == 1) {
return 0;
}
- else {
+ else if (r == EOF) {
return -1;
}
+ else {
+ /* unable to read more, but EOF not reached indicating an error. */
+ return -2;
+ }
}
/*
@@ -143,9 +173,10 @@ fromstr_skip_separator(char **s, const char *sep, const char *end)
{
char *string = *s;
int result = 0;
+
while (1) {
char c = *string;
- if (c == '\0' || (end != NULL && string >= end)) {
+ if (string_is_fully_read(string, end)) {
result = -1;
break;
}
@@ -422,6 +453,10 @@ copy_and_swap(void *dst, void *src, int itemsize, npy_intp numitems,
}
}
+NPY_NO_EXPORT PyObject *
+_array_from_array_like(PyObject *op, PyArray_Descr *requested_dtype,
+ npy_bool writeable, PyObject *context);
+
/*
* adapted from Numarray,
* a: destination array
@@ -442,11 +477,6 @@ setArrayFromSequence(PyArrayObject *a, PyObject *s,
if (dst == NULL)
dst = a;
- /*
- * This code is to ensure that the sequence access below will
- * return a lower-dimensional sequence.
- */
-
/* INCREF on entry DECREF on exit */
Py_INCREF(s);
@@ -472,6 +502,11 @@ setArrayFromSequence(PyArrayObject *a, PyObject *s,
return 0;
}
+ /*
+ * This code is to ensure that the sequence access below will
+ * return a lower-dimensional sequence.
+ */
+
if (dim > PyArray_NDIM(a)) {
PyErr_Format(PyExc_ValueError,
"setArrayFromSequence: sequence/array dimensions mismatch.");
@@ -482,6 +517,27 @@ setArrayFromSequence(PyArrayObject *a, PyObject *s,
if (slen < 0) {
goto fail;
}
+ if (slen > 0) {
+ /* gh-13659: try __array__ before using s as a sequence */
+ PyObject *tmp = _array_from_array_like(s, /*dtype*/NULL, /*writeable*/0,
+ /*context*/NULL);
+ if (tmp == NULL) {
+ goto fail;
+ }
+ else if (tmp == Py_NotImplemented) {
+ Py_DECREF(tmp);
+ }
+ else {
+ int r = PyArray_CopyInto(dst, (PyArrayObject *)tmp);
+ Py_DECREF(tmp);
+ if (r < 0) {
+ goto fail;
+ }
+ Py_DECREF(s);
+ return 0;
+ }
+ }
+
/*
* Either the dimensions match, or the sequence has length 1 and can
* be broadcast to the destination.
@@ -743,16 +799,31 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
d[i] = buffer_view.shape[i];
}
PyBuffer_Release(&buffer_view);
+ _dealloc_cached_buffer_info(obj);
return 0;
}
+ else if (PyErr_Occurred()) {
+ if (PyErr_ExceptionMatches(PyExc_BufferError) ||
+ PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ } else {
+ return -1;
+ }
+ }
else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) {
d[0] = buffer_view.len;
*maxndim = 1;
PyBuffer_Release(&buffer_view);
+ _dealloc_cached_buffer_info(obj);
return 0;
}
- else {
- PyErr_Clear();
+ else if (PyErr_Occurred()) {
+ if (PyErr_ExceptionMatches(PyExc_BufferError) ||
+ PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ } else {
+ return -1;
+ }
}
}
@@ -896,6 +967,39 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
return 0;
}
+static PyObject *
+raise_memory_error(int nd, npy_intp *dims, PyArray_Descr *descr)
+{
+ static PyObject *exc_type = NULL;
+
+ npy_cache_import(
+ "numpy.core._exceptions", "_ArrayMemoryError",
+ &exc_type);
+ if (exc_type == NULL) {
+ goto fail;
+ }
+
+ PyObject *shape = PyArray_IntTupleFromIntp(nd, dims);
+ if (shape == NULL) {
+ goto fail;
+ }
+
+ /* produce an error object */
+ PyObject *exc_value = PyTuple_Pack(2, shape, (PyObject *)descr);
+ Py_DECREF(shape);
+ if (exc_value == NULL){
+ goto fail;
+ }
+ PyErr_SetObject(exc_type, exc_value);
+ Py_DECREF(exc_value);
+ return NULL;
+
+fail:
+ /* we couldn't raise the formatted exception for some reason */
+ PyErr_WriteUnraisable(NULL);
+ return PyErr_NoMemory();
+}
+
/*
* Generic new array creation routine.
* Internal variant with calloc argument for PyArray_Zeros.
@@ -904,13 +1008,14 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
* be decrefed.
*/
NPY_NO_EXPORT PyObject *
-PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
- npy_intp *dims, npy_intp *strides, void *data,
- int flags, PyObject *obj, PyObject *base, int zeroed,
- int allow_emptystring)
+PyArray_NewFromDescr_int(
+ PyTypeObject *subtype, PyArray_Descr *descr, int nd,
+ npy_intp const *dims, npy_intp const *strides, void *data,
+ int flags, PyObject *obj, PyObject *base, int zeroed,
+ int allow_emptystring)
{
PyArrayObject_fields *fa;
- int i, is_empty;
+ int i;
npy_intp nbytes;
if (descr->subarray) {
@@ -964,7 +1069,6 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
}
/* Check dimensions and multiply them to nbytes */
- is_empty = 0;
for (i = 0; i < nd; i++) {
npy_intp dim = dims[i];
@@ -973,7 +1077,6 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
* Compare to PyArray_OverflowMultiplyList that
* returns 0 in this case.
*/
- is_empty = 1;
continue;
}
@@ -1031,7 +1134,9 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
goto fail;
}
fa->strides = fa->dimensions + nd;
- memcpy(fa->dimensions, dims, sizeof(npy_intp)*nd);
+ if (nd) {
+ memcpy(fa->dimensions, dims, sizeof(npy_intp)*nd);
+ }
if (strides == NULL) { /* fill it in */
_array_fill_strides(fa->strides, dims, nd, descr->elsize,
flags, &(fa->flags));
@@ -1041,7 +1146,9 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
* we allow strides even when we create
* the memory, but be careful with this...
*/
- memcpy(fa->strides, strides, sizeof(npy_intp)*nd);
+ if (nd) {
+ memcpy(fa->strides, strides, sizeof(npy_intp)*nd);
+ }
}
}
else {
@@ -1056,8 +1163,8 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
* (a.data) doesn't work as it should.
* Could probably just allocate a few bytes here. -- Chuck
*/
- if (is_empty) {
- nbytes = descr->elsize;
+ if (nbytes == 0) {
+ nbytes = descr->elsize ? descr->elsize : 1;
}
/*
* It is bad to have uninitialized OBJECT pointers
@@ -1070,8 +1177,7 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
data = npy_alloc_cache(nbytes);
}
if (data == NULL) {
- PyErr_NoMemory();
- goto fail;
+ return raise_memory_error(fa->nd, fa->dimensions, descr);
}
fa->flags |= NPY_ARRAY_OWNDATA;
@@ -1157,9 +1263,10 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
* true, dtype will be decrefed.
*/
NPY_NO_EXPORT PyObject *
-PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr,
- int nd, npy_intp *dims, npy_intp *strides, void *data,
- int flags, PyObject *obj)
+PyArray_NewFromDescr(
+ PyTypeObject *subtype, PyArray_Descr *descr,
+ int nd, npy_intp const *dims, npy_intp const *strides, void *data,
+ int flags, PyObject *obj)
{
return PyArray_NewFromDescrAndBase(
subtype, descr,
@@ -1173,7 +1280,7 @@ PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr,
NPY_NO_EXPORT PyObject *
PyArray_NewFromDescrAndBase(
PyTypeObject *subtype, PyArray_Descr *descr,
- int nd, npy_intp *dims, npy_intp *strides, void *data,
+ int nd, npy_intp const *dims, npy_intp const *strides, void *data,
int flags, PyObject *obj, PyObject *base)
{
return PyArray_NewFromDescr_int(subtype, descr, nd,
@@ -1181,9 +1288,9 @@ PyArray_NewFromDescrAndBase(
flags, obj, base, 0, 0);
}
-/*NUMPY_API
+/*
* Creates a new array with the same shape as the provided one,
- * with possible memory layout order and data type changes.
+ * with possible memory layout order, data type and shape changes.
*
* prototype - The array the new one should be like.
* order - NPY_CORDER - C-contiguous result.
@@ -1191,6 +1298,8 @@ PyArray_NewFromDescrAndBase(
* NPY_ANYORDER - Fortran if prototype is Fortran, C otherwise.
* NPY_KEEPORDER - Keeps the axis ordering of prototype.
* dtype - If not NULL, overrides the data type of the result.
+ * ndim - If not 0 and dims not NULL, overrides the shape of the result.
+ * dims - If not NULL and ndim not 0, overrides the shape of the result.
* subok - If 1, use the prototype's array subtype, otherwise
* always create a base-class array.
*
@@ -1198,11 +1307,18 @@ PyArray_NewFromDescrAndBase(
* dtype->subarray is true, dtype will be decrefed.
*/
NPY_NO_EXPORT PyObject *
-PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
- PyArray_Descr *dtype, int subok)
+PyArray_NewLikeArrayWithShape(PyArrayObject *prototype, NPY_ORDER order,
+ PyArray_Descr *dtype, int ndim, npy_intp const *dims, int subok)
{
PyObject *ret = NULL;
- int ndim = PyArray_NDIM(prototype);
+
+ if (dims == NULL) {
+ ndim = PyArray_NDIM(prototype);
+ dims = PyArray_DIMS(prototype);
+ }
+ else if (order == NPY_KEEPORDER && (ndim != PyArray_NDIM(prototype))) {
+ order = NPY_CORDER;
+ }
/* If no override data type, use the one from the prototype */
if (dtype == NULL) {
@@ -1235,7 +1351,7 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
ret = PyArray_NewFromDescr(subok ? Py_TYPE(prototype) : &PyArray_Type,
dtype,
ndim,
- PyArray_DIMS(prototype),
+ dims,
NULL,
NULL,
order,
@@ -1244,11 +1360,10 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
/* KEEPORDER needs some analysis of the strides */
else {
npy_intp strides[NPY_MAXDIMS], stride;
- npy_intp *shape = PyArray_DIMS(prototype);
npy_stride_sort_item strideperm[NPY_MAXDIMS];
int idim;
- PyArray_CreateSortedStridePerm(PyArray_NDIM(prototype),
+ PyArray_CreateSortedStridePerm(ndim,
PyArray_STRIDES(prototype),
strideperm);
@@ -1257,14 +1372,14 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
for (idim = ndim-1; idim >= 0; --idim) {
npy_intp i_perm = strideperm[idim].perm;
strides[i_perm] = stride;
- stride *= shape[i_perm];
+ stride *= dims[i_perm];
}
/* Finally, allocate the array */
ret = PyArray_NewFromDescr(subok ? Py_TYPE(prototype) : &PyArray_Type,
dtype,
ndim,
- shape,
+ dims,
strides,
NULL,
0,
@@ -1275,12 +1390,36 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
}
/*NUMPY_API
+ * Creates a new array with the same shape as the provided one,
+ * with possible memory layout order and data type changes.
+ *
+ * prototype - The array the new one should be like.
+ * order - NPY_CORDER - C-contiguous result.
+ * NPY_FORTRANORDER - Fortran-contiguous result.
+ * NPY_ANYORDER - Fortran if prototype is Fortran, C otherwise.
+ * NPY_KEEPORDER - Keeps the axis ordering of prototype.
+ * dtype - If not NULL, overrides the data type of the result.
+ * subok - If 1, use the prototype's array subtype, otherwise
+ * always create a base-class array.
+ *
+ * NOTE: If dtype is not NULL, steals the dtype reference. On failure or when
+ * dtype->subarray is true, dtype will be decrefed.
+ */
+NPY_NO_EXPORT PyObject *
+PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
+ PyArray_Descr *dtype, int subok)
+{
+ return PyArray_NewLikeArrayWithShape(prototype, order, dtype, 0, NULL, subok);
+}
+
+/*NUMPY_API
* Generic new array creation routine.
*/
NPY_NO_EXPORT PyObject *
-PyArray_New(PyTypeObject *subtype, int nd, npy_intp *dims, int type_num,
- npy_intp *strides, void *data, int itemsize, int flags,
- PyObject *obj)
+PyArray_New(
+ PyTypeObject *subtype, int nd, npy_intp const *dims, int type_num,
+ npy_intp const *strides, void *data, int itemsize, int flags,
+ PyObject *obj)
{
PyArray_Descr *descr;
PyObject *new;
@@ -1328,28 +1467,6 @@ _dtype_from_buffer_3118(PyObject *memoryview)
}
-/*
- * Call the python _is_from_ctypes
- */
-NPY_NO_EXPORT int
-_is_from_ctypes(PyObject *obj) {
- PyObject *ret_obj;
- static PyObject *py_func = NULL;
-
- npy_cache_import("numpy.core._internal", "_is_from_ctypes", &py_func);
-
- if (py_func == NULL) {
- return -1;
- }
- ret_obj = PyObject_CallFunctionObjArgs(py_func, obj, NULL);
- if (ret_obj == NULL) {
- return -1;
- }
-
- return PyObject_IsTrue(ret_obj);
-}
-
-
NPY_NO_EXPORT PyObject *
_array_from_buffer_3118(PyObject *memoryview)
{
@@ -1381,15 +1498,7 @@ _array_from_buffer_3118(PyObject *memoryview)
* Note that even if the above are fixed in master, we have to drop the
* early patch versions of python to actually make use of the fixes.
*/
-
- int is_ctypes = _is_from_ctypes(view->obj);
- if (is_ctypes < 0) {
- /* This error is not useful */
- PyErr_WriteUnraisable(view->obj);
- is_ctypes = 0;
- }
-
- if (!is_ctypes) {
+ if (!npy_ctypes_check(Py_TYPE(view->obj))) {
/* This object has no excuse for a broken PEP3118 buffer */
PyErr_Format(
PyExc_RuntimeError,
@@ -1416,6 +1525,7 @@ _array_from_buffer_3118(PyObject *memoryview)
* dimensions, so the array is now 0d.
*/
nd = 0;
+ Py_DECREF(descr);
descr = (PyArray_Descr *)PyObject_CallFunctionObjArgs(
(PyObject *)&PyArrayDescr_Type, Py_TYPE(view->obj), NULL);
if (descr == NULL) {
@@ -1486,6 +1596,90 @@ fail:
}
+
+/*
+ * Attempts to extract an array from an array-like object.
+ *
+ * array-like is defined as either
+ *
+ * * an object implementing the PEP 3118 buffer interface;
+ * * an object with __array_struct__ or __array_interface__ attributes;
+ * * an object with an __array__ function.
+ *
+ * Returns Py_NotImplemented if a given object is not array-like;
+ * PyArrayObject* in case of success and NULL in case of failure.
+ */
+NPY_NO_EXPORT PyObject *
+_array_from_array_like(PyObject *op, PyArray_Descr *requested_dtype,
+ npy_bool writeable, PyObject *context) {
+ PyObject* tmp;
+
+ /* If op supports the PEP 3118 buffer interface */
+ if (!PyBytes_Check(op) && !PyUnicode_Check(op)) {
+ PyObject *memoryview = PyMemoryView_FromObject(op);
+ if (memoryview == NULL) {
+ PyErr_Clear();
+ }
+ else {
+ tmp = _array_from_buffer_3118(memoryview);
+ Py_DECREF(memoryview);
+ if (tmp == NULL) {
+ return NULL;
+ }
+
+ if (writeable
+ && PyArray_FailUnlessWriteable((PyArrayObject *) tmp, "PEP 3118 buffer") < 0) {
+ Py_DECREF(tmp);
+ return NULL;
+ }
+
+ return tmp;
+ }
+ }
+
+ /* If op supports the __array_struct__ or __array_interface__ interface */
+ tmp = PyArray_FromStructInterface(op);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ if (tmp == Py_NotImplemented) {
+ tmp = PyArray_FromInterface(op);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ }
+
+ /*
+ * If op supplies the __array__ function.
+ * The documentation says this should produce a copy, so
+ * we skip this method if writeable is true, because the intent
+ * of writeable is to modify the operand.
+ * XXX: If the implementation is wrong, and/or if actual
+ * usage requires this behave differently,
+ * this should be changed!
+ */
+ if (!writeable && tmp == Py_NotImplemented) {
+ tmp = PyArray_FromArrayAttr(op, requested_dtype, context);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ }
+
+ if (tmp != Py_NotImplemented) {
+ if (writeable
+ && PyArray_FailUnlessWriteable((PyArrayObject *) tmp,
+ "array interface object") < 0) {
+ Py_DECREF(tmp);
+ return NULL;
+ }
+ return tmp;
+ }
+
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
+
/*NUMPY_API
* Retrieves the array parameters for viewing/converting an arbitrary
* PyObject* to a NumPy array. This allows the "innate type and shape"
@@ -1593,69 +1787,20 @@ PyArray_GetArrayParamsFromObject(PyObject *op,
return 0;
}
- /* If op supports the PEP 3118 buffer interface */
- if (!PyBytes_Check(op) && !PyUnicode_Check(op)) {
-
- PyObject *memoryview = PyMemoryView_FromObject(op);
- if (memoryview == NULL) {
- PyErr_Clear();
- }
- else {
- PyObject *arr = _array_from_buffer_3118(memoryview);
- Py_DECREF(memoryview);
- if (arr == NULL) {
- return -1;
- }
- if (writeable
- && PyArray_FailUnlessWriteable((PyArrayObject *)arr, "PEP 3118 buffer") < 0) {
- Py_DECREF(arr);
- return -1;
- }
- *out_arr = (PyArrayObject *)arr;
- return 0;
- }
- }
-
- /* If op supports the __array_struct__ or __array_interface__ interface */
- tmp = PyArray_FromStructInterface(op);
+ /* If op is an array-like */
+ tmp = _array_from_array_like(op, requested_dtype, writeable, context);
if (tmp == NULL) {
return -1;
}
- if (tmp == Py_NotImplemented) {
- tmp = PyArray_FromInterface(op);
- if (tmp == NULL) {
- return -1;
- }
- }
- if (tmp != Py_NotImplemented) {
- if (writeable
- && PyArray_FailUnlessWriteable((PyArrayObject *)tmp,
- "array interface object") < 0) {
- Py_DECREF(tmp);
- return -1;
- }
- *out_arr = (PyArrayObject *)tmp;
- return (*out_arr) == NULL ? -1 : 0;
+ else if (tmp != Py_NotImplemented) {
+ *out_arr = (PyArrayObject*) tmp;
+ return 0;
}
-
- /*
- * If op supplies the __array__ function.
- * The documentation says this should produce a copy, so
- * we skip this method if writeable is true, because the intent
- * of writeable is to modify the operand.
- * XXX: If the implementation is wrong, and/or if actual
- * usage requires this behave differently,
- * this should be changed!
- */
- if (!writeable) {
- tmp = PyArray_FromArrayAttr(op, requested_dtype, context);
- if (tmp != Py_NotImplemented) {
- *out_arr = (PyArrayObject *)tmp;
- return (*out_arr) == NULL ? -1 : 0;
- }
+ else {
+ Py_DECREF(Py_NotImplemented);
}
- /* Try to treat op as a list of lists */
+ /* Try to treat op as a list of lists or array-like objects. */
if (!writeable && PySequence_Check(op)) {
int check_it, stop_at_string, stop_at_tuple, is_object;
int type_num, type;
@@ -1817,15 +1962,19 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
/* If the requested dtype is flexible, adapt it */
if (newtype != NULL) {
- PyArray_AdaptFlexibleDType(op,
+ newtype = PyArray_AdaptFlexibleDType(op,
(dtype == NULL) ? PyArray_DESCR(arr) : dtype,
- &newtype);
+ newtype);
+ if (newtype == NULL) {
+ return NULL;
+ }
}
/* If we got dimensions and dtype instead of an array */
if (arr == NULL) {
if ((flags & NPY_ARRAY_WRITEBACKIFCOPY) ||
(flags & NPY_ARRAY_UPDATEIFCOPY)) {
+ Py_DECREF(dtype);
Py_XDECREF(newtype);
PyErr_SetString(PyExc_TypeError,
"WRITEBACKIFCOPY used for non-array input.");
@@ -2030,7 +2179,7 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
newtype = oldtype;
Py_INCREF(oldtype);
}
- if (PyDataType_ISUNSIZED(newtype)) {
+ else if (PyDataType_ISUNSIZED(newtype)) {
PyArray_DESCR_REPLACE(newtype);
if (newtype == NULL) {
return NULL;
@@ -2134,12 +2283,15 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
*/
/* 2017-Nov-10 1.14 */
- if (DEPRECATE("NPY_ARRAY_UPDATEIFCOPY, NPY_ARRAY_INOUT_ARRAY, and "
- "NPY_ARRAY_INOUT_FARRAY are deprecated, use NPY_WRITEBACKIFCOPY, "
- "NPY_ARRAY_INOUT_ARRAY2, or NPY_ARRAY_INOUT_FARRAY2 respectively "
- "instead, and call PyArray_ResolveWritebackIfCopy before the "
- "array is deallocated, i.e. before the last call to Py_DECREF.") < 0)
+ if (DEPRECATE(
+ "NPY_ARRAY_UPDATEIFCOPY, NPY_ARRAY_INOUT_ARRAY, and "
+ "NPY_ARRAY_INOUT_FARRAY are deprecated, use NPY_WRITEBACKIFCOPY, "
+ "NPY_ARRAY_INOUT_ARRAY2, or NPY_ARRAY_INOUT_FARRAY2 respectively "
+ "instead, and call PyArray_ResolveWritebackIfCopy before the "
+ "array is deallocated, i.e. before the last call to Py_DECREF.") < 0) {
+ Py_DECREF(ret);
return NULL;
+ }
Py_INCREF(arr);
if (PyArray_SetWritebackIfCopyBase(ret, arr) < 0) {
Py_DECREF(ret);
@@ -2166,14 +2318,12 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
Py_DECREF(newtype);
if (needview) {
- PyArray_Descr *dtype = PyArray_DESCR(arr);
PyTypeObject *subtype = NULL;
if (flags & NPY_ARRAY_ENSUREARRAY) {
subtype = &PyArray_Type;
}
- Py_INCREF(dtype);
ret = (PyArrayObject *)PyArray_View(arr, NULL, subtype);
if (ret == NULL) {
return NULL;
@@ -2471,6 +2621,7 @@ PyArray_FromInterface(PyObject *origin)
* sticks around after the release.
*/
PyBuffer_Release(&view);
+ _dealloc_cached_buffer_info(base);
#else
res = PyObject_AsWriteBuffer(base, (void **)&data, &buffer_len);
if (res < 0) {
@@ -2484,7 +2635,7 @@ PyArray_FromInterface(PyObject *origin)
}
#endif
/* Get offset number from interface specification */
- attr = PyDict_GetItemString(origin, "offset");
+ attr = PyDict_GetItemString(iface, "offset");
if (attr) {
npy_longlong num = PyLong_AsLongLong(attr);
if (error_converting(num)) {
@@ -2500,6 +2651,11 @@ PyArray_FromInterface(PyObject *origin)
&PyArray_Type, dtype,
n, dims, NULL, data,
dataflags, NULL, base);
+ /*
+ * Ref to dtype was stolen by PyArray_NewFromDescrAndBase
+ * Prevent DECREFing dtype in fail codepath by setting to NULL
+ */
+ dtype = NULL;
if (ret == NULL) {
goto fail;
}
@@ -2537,7 +2693,9 @@ PyArray_FromInterface(PyObject *origin)
goto fail;
}
}
- memcpy(PyArray_STRIDES(ret), strides, n*sizeof(npy_intp));
+ if (n) {
+ memcpy(PyArray_STRIDES(ret), strides, n*sizeof(npy_intp));
+ }
}
PyArray_UpdateFlags(ret, NPY_ARRAY_UPDATE_ALL);
Py_DECREF(iface);
@@ -2626,61 +2784,30 @@ PyArray_DescrFromObject(PyObject *op, PyArray_Descr *mintype)
/* They all zero-out the memory as previously done */
/* steals reference to descr -- and enforces native byteorder on it.*/
+
/*NUMPY_API
- Like FromDimsAndData but uses the Descr structure instead of typecode
- as input.
+ Deprecated, use PyArray_NewFromDescr instead.
*/
NPY_NO_EXPORT PyObject *
-PyArray_FromDimsAndDataAndDescr(int nd, int *d,
+PyArray_FromDimsAndDataAndDescr(int NPY_UNUSED(nd), int *NPY_UNUSED(d),
PyArray_Descr *descr,
- char *data)
+ char *NPY_UNUSED(data))
{
- PyObject *ret;
- int i;
- npy_intp newd[NPY_MAXDIMS];
- char msg[] = "PyArray_FromDimsAndDataAndDescr: use PyArray_NewFromDescr.";
-
- if (DEPRECATE(msg) < 0) {
- /* 2009-04-30, 1.5 */
- return NULL;
- }
- if (!PyArray_ISNBO(descr->byteorder))
- descr->byteorder = '=';
- for (i = 0; i < nd; i++) {
- newd[i] = (npy_intp) d[i];
- }
- ret = PyArray_NewFromDescr(&PyArray_Type, descr,
- nd, newd,
- NULL, data,
- (data ? NPY_ARRAY_CARRAY : 0), NULL);
- return ret;
+ PyErr_SetString(PyExc_NotImplementedError,
+ "PyArray_FromDimsAndDataAndDescr: use PyArray_NewFromDescr.");
+ Py_DECREF(descr);
+ return NULL;
}
/*NUMPY_API
- Construct an empty array from dimensions and typenum
+ Deprecated, use PyArray_SimpleNew instead.
*/
NPY_NO_EXPORT PyObject *
-PyArray_FromDims(int nd, int *d, int type)
+PyArray_FromDims(int NPY_UNUSED(nd), int *NPY_UNUSED(d), int NPY_UNUSED(type))
{
- PyArrayObject *ret;
- char msg[] = "PyArray_FromDims: use PyArray_SimpleNew.";
-
- if (DEPRECATE(msg) < 0) {
- /* 2009-04-30, 1.5 */
- return NULL;
- }
- ret = (PyArrayObject *)PyArray_FromDimsAndDataAndDescr(nd, d,
- PyArray_DescrFromType(type),
- NULL);
- /*
- * Old FromDims set memory to zero --- some algorithms
- * relied on that. Better keep it the same. If
- * Object type, then it's already been set to zero, though.
- */
- if (ret && (PyArray_DESCR(ret)->type_num != NPY_OBJECT)) {
- memset(PyArray_DATA(ret), 0, PyArray_NBYTES(ret));
- }
- return (PyObject *)ret;
+ PyErr_SetString(PyExc_NotImplementedError,
+ "PyArray_FromDims: use PyArray_SimpleNew.");
+ return NULL;
}
/* end old calls */
@@ -2832,7 +2959,8 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order)
* contiguous strides, etc.
*/
if (PyArray_GetDTypeTransferFunction(
- IsUintAligned(src) && IsUintAligned(dst),
+ IsUintAligned(src) && IsAligned(src) &&
+ IsUintAligned(dst) && IsAligned(dst),
src_stride, dst_stride,
PyArray_DESCR(src), PyArray_DESCR(dst),
0,
@@ -2997,7 +3125,7 @@ PyArray_CheckAxis(PyArrayObject *arr, int *axis, int flags)
* accepts NULL type
*/
NPY_NO_EXPORT PyObject *
-PyArray_Zeros(int nd, npy_intp *dims, PyArray_Descr *type, int is_f_order)
+PyArray_Zeros(int nd, npy_intp const *dims, PyArray_Descr *type, int is_f_order)
{
PyArrayObject *ret;
@@ -3032,10 +3160,10 @@ PyArray_Zeros(int nd, npy_intp *dims, PyArray_Descr *type, int is_f_order)
* Empty
*
* accepts NULL type
- * steals referenct to type
+ * steals a reference to type
*/
NPY_NO_EXPORT PyObject *
-PyArray_Empty(int nd, npy_intp *dims, PyArray_Descr *type, int is_f_order)
+PyArray_Empty(int nd, npy_intp const *dims, PyArray_Descr *type, int is_f_order)
{
PyArrayObject *ret;
@@ -3446,11 +3574,13 @@ PyArray_ArangeObj(PyObject *start, PyObject *stop, PyObject *step, PyArray_Descr
return NULL;
}
+/* This array creation function steals the reference to dtype. */
static PyArrayObject *
array_fromfile_binary(FILE *fp, PyArray_Descr *dtype, npy_intp num, size_t *nread)
{
PyArrayObject *r;
npy_off_t start, numbytes;
+ int elsize;
if (num < 0) {
int fail = 0;
@@ -3477,27 +3607,29 @@ array_fromfile_binary(FILE *fp, PyArray_Descr *dtype, npy_intp num, size_t *nrea
}
num = numbytes / dtype->elsize;
}
+
/*
- * When dtype->subarray is true, PyArray_NewFromDescr will decref dtype
- * even on success, so make sure it stays around until exit.
+ * Array creation may move sub-array dimensions from the dtype to array
+ * dimensions, so we need to use the original element size when reading.
*/
- Py_INCREF(dtype);
+ elsize = dtype->elsize;
+
r = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &num,
NULL, NULL, 0, NULL);
if (r == NULL) {
- Py_DECREF(dtype);
return NULL;
}
+
NPY_BEGIN_ALLOW_THREADS;
- *nread = fread(PyArray_DATA(r), dtype->elsize, num, fp);
+ *nread = fread(PyArray_DATA(r), elsize, num, fp);
NPY_END_ALLOW_THREADS;
- Py_DECREF(dtype);
return r;
}
/*
* Create an array by reading from the given stream, using the passed
* next_element and skip_separator functions.
+ * As typical for array creation functions, it steals the reference to dtype.
*/
#define FROM_BUFFER_SIZE 4096
static PyArrayObject *
@@ -3509,6 +3641,7 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread,
npy_intp i;
char *dptr, *clean_sep, *tmp;
int err = 0;
+ int stop_reading_flag; /* -1 indicates end reached; -2 a parsing error */
npy_intp thisbuf = 0;
npy_intp size;
npy_intp bytes, totalbytes;
@@ -3516,10 +3649,11 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread,
size = (num >= 0) ? num : FROM_BUFFER_SIZE;
/*
- * When dtype->subarray is true, PyArray_NewFromDescr will decref dtype
- * even on success, so make sure it stays around until exit.
+ * Array creation may move sub-array dimensions from the dtype to array
+ * dimensions, so we need to use the original dtype when reading.
*/
Py_INCREF(dtype);
+
r = (PyArrayObject *)
PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &size,
NULL, NULL, 0, NULL);
@@ -3527,6 +3661,7 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread,
Py_DECREF(dtype);
return NULL;
}
+
clean_sep = swab_separator(sep);
if (clean_sep == NULL) {
err = 1;
@@ -3536,9 +3671,9 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread,
NPY_BEGIN_ALLOW_THREADS;
totalbytes = bytes = size * dtype->elsize;
dptr = PyArray_DATA(r);
- for (i= 0; num < 0 || i < num; i++) {
- if (next(&stream, dptr, dtype, stream_data) < 0) {
- /* EOF */
+ for (i = 0; num < 0 || i < num; i++) {
+ stop_reading_flag = next(&stream, dptr, dtype, stream_data);
+ if (stop_reading_flag < 0) {
break;
}
*nread += 1;
@@ -3555,23 +3690,48 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread,
dptr = tmp + (totalbytes - bytes);
thisbuf = 0;
}
- if (skip_sep(&stream, clean_sep, stream_data) < 0) {
+ stop_reading_flag = skip_sep(&stream, clean_sep, stream_data);
+ if (stop_reading_flag < 0) {
+ if (num == i + 1) {
+ /* if we read as much as requested sep is optional */
+ stop_reading_flag = -1;
+ }
break;
}
}
if (num < 0) {
- tmp = PyDataMem_RENEW(PyArray_DATA(r), PyArray_MAX(*nread,1)*dtype->elsize);
- if (tmp == NULL) {
- err = 1;
- }
- else {
- PyArray_DIMS(r)[0] = *nread;
- ((PyArrayObject_fields *)r)->data = tmp;
+ const size_t nsize = PyArray_MAX(*nread,1)*dtype->elsize;
+
+ if (nsize != 0) {
+ tmp = PyDataMem_RENEW(PyArray_DATA(r), nsize);
+ if (tmp == NULL) {
+ err = 1;
+ }
+ else {
+ PyArray_DIMS(r)[0] = *nread;
+ ((PyArrayObject_fields *)r)->data = tmp;
+ }
}
}
NPY_END_ALLOW_THREADS;
+
free(clean_sep);
+ if (stop_reading_flag == -2) {
+ if (PyErr_Occurred()) {
+ /* If an error is already set (unlikely), do not create new one */
+ Py_DECREF(r);
+ Py_DECREF(dtype);
+ return NULL;
+ }
+ /* 2019-09-12, NumPy 1.18 */
+ if (DEPRECATE(
+ "string or file could not be read to its end due to unmatched "
+ "data; this will raise a ValueError in the future.") < 0) {
+ goto fail;
+ }
+ }
+
fail:
Py_DECREF(dtype);
if (err == 1) {
@@ -3590,9 +3750,8 @@ fail:
* Given a ``FILE *`` pointer ``fp``, and a ``PyArray_Descr``, return an
* array corresponding to the data encoded in that file.
*
- * If the dtype is NULL, the default array type is used (double).
- * If non-null, the reference is stolen and if dtype->subarray is true dtype
- * will be decrefed even on success.
+ * The reference to `dtype` is stolen (it is possible that the passed in
+ * dtype is not held on to).
*
* The number of elements to read is given as ``num``; if it is < 0, then
* then as many as possible are read.
@@ -3640,7 +3799,6 @@ PyArray_FromFile(FILE *fp, PyArray_Descr *dtype, npy_intp num, char *sep)
(skip_separator) fromfile_skip_separator, NULL);
}
if (ret == NULL) {
- Py_DECREF(dtype);
return NULL;
}
if (((npy_intp) nread) < num) {
@@ -3687,32 +3845,12 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
Py_DECREF(type);
return NULL;
}
- if (Py_TYPE(buf)->tp_as_buffer == NULL
-#if defined(NPY_PY3K)
- || Py_TYPE(buf)->tp_as_buffer->bf_getbuffer == NULL
-#else
- || (Py_TYPE(buf)->tp_as_buffer->bf_getwritebuffer == NULL
- && Py_TYPE(buf)->tp_as_buffer->bf_getreadbuffer == NULL)
-#endif
- ) {
- PyObject *newbuf;
- newbuf = PyObject_GetAttr(buf, npy_ma_str_buffer);
- if (newbuf == NULL) {
- Py_DECREF(type);
- return NULL;
- }
- buf = newbuf;
- }
- else {
- Py_INCREF(buf);
- }
#if defined(NPY_PY3K)
if (PyObject_GetBuffer(buf, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) {
writeable = 0;
PyErr_Clear();
if (PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE) < 0) {
- Py_DECREF(buf);
Py_DECREF(type);
return NULL;
}
@@ -3726,12 +3864,12 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
* sticks around after the release.
*/
PyBuffer_Release(&view);
+ _dealloc_cached_buffer_info(buf);
#else
if (PyObject_AsWriteBuffer(buf, (void *)&data, &ts) == -1) {
writeable = 0;
PyErr_Clear();
if (PyObject_AsReadBuffer(buf, (void *)&data, &ts) == -1) {
- Py_DECREF(buf);
Py_DECREF(type);
return NULL;
}
@@ -3742,7 +3880,6 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
PyErr_Format(PyExc_ValueError,
"offset must be non-negative and no greater than buffer "\
"length (%" NPY_INTP_FMT ")", (npy_intp)ts);
- Py_DECREF(buf);
Py_DECREF(type);
return NULL;
}
@@ -3751,12 +3888,17 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
s = (npy_intp)ts - offset;
n = (npy_intp)count;
itemsize = type->elsize;
- if (n < 0 ) {
+ if (n < 0) {
+ if (itemsize == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot determine count if itemsize is 0");
+ Py_DECREF(type);
+ return NULL;
+ }
if (s % itemsize != 0) {
PyErr_SetString(PyExc_ValueError,
"buffer size must be a multiple"\
" of element size");
- Py_DECREF(buf);
Py_DECREF(type);
return NULL;
}
@@ -3767,7 +3909,6 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
PyErr_SetString(PyExc_ValueError,
"buffer is smaller than requested"\
" size");
- Py_DECREF(buf);
Py_DECREF(type);
return NULL;
}
@@ -3777,7 +3918,6 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
&PyArray_Type, type,
1, &n, NULL, data,
NPY_ARRAY_DEFAULT, NULL, buf);
- Py_DECREF(buf);
if (ret == NULL) {
return NULL;
}
@@ -3859,6 +3999,11 @@ PyArray_FromString(char *data, npy_intp slen, PyArray_Descr *dtype,
return NULL;
}
}
+ /*
+ * NewFromDescr may replace dtype to absorb subarray shape
+ * into the array, so get size beforehand.
+ */
+ npy_intp size_to_copy = num*dtype->elsize;
ret = (PyArrayObject *)
PyArray_NewFromDescr(&PyArray_Type, dtype,
1, &num, NULL, NULL,
@@ -3866,14 +4011,14 @@ PyArray_FromString(char *data, npy_intp slen, PyArray_Descr *dtype,
if (ret == NULL) {
return NULL;
}
- memcpy(PyArray_DATA(ret), data, num*dtype->elsize);
+ memcpy(PyArray_DATA(ret), data, size_to_copy);
}
else {
/* read from character-based string */
size_t nread = 0;
char *end;
- if (dtype->f->scanfunc == NULL) {
+ if (dtype->f->fromstr == NULL) {
PyErr_SetString(PyExc_ValueError,
"don't know how to read " \
"character strings with that " \
@@ -3917,7 +4062,16 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count)
"Must specify length when using variable-size data-type.");
goto done;
}
- elcount = (count < 0) ? 0 : count;
+ if (count < 0) {
+ elcount = PyObject_LengthHint(obj, 0);
+ if (elcount < 0) {
+ goto done;
+ }
+ }
+ else {
+ elcount = count;
+ }
+
elsize = dtype->elsize;
/*
@@ -3938,7 +4092,7 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count)
}
for (i = 0; (i < count || count == -1) &&
(value = PyIter_Next(iter)); i++) {
- if (i >= elcount) {
+ if (i >= elcount && elsize != 0) {
npy_intp nbytes;
/*
Grow PyArray_DATA(ret):
@@ -3984,9 +4138,9 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count)
* Realloc the data so that don't keep extra memory tied up
* (assuming realloc is reasonably good about reusing space...)
*/
- if (i == 0) {
+ if (i == 0 || elsize == 0) {
/* The size cannot be zero for PyDataMem_RENEW. */
- i = 1;
+ goto done;
}
new_data = PyDataMem_RENEW(PyArray_DATA(ret), i * elsize);
if (new_data == NULL) {
@@ -4026,7 +4180,7 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count)
*/
NPY_NO_EXPORT void
-_array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize,
+_array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t itemsize,
int inflag, int *objflags)
{
int i;
diff --git a/numpy/core/src/multiarray/ctors.h b/numpy/core/src/multiarray/ctors.h
index e9a2532da..4768e4efd 100644
--- a/numpy/core/src/multiarray/ctors.h
+++ b/numpy/core/src/multiarray/ctors.h
@@ -2,24 +2,33 @@
#define _NPY_ARRAY_CTORS_H_
NPY_NO_EXPORT PyObject *
-PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
- npy_intp *dims, npy_intp *strides, void *data,
- int flags, PyObject *obj);
+PyArray_NewFromDescr(
+ PyTypeObject *subtype, PyArray_Descr *descr, int nd,
+ npy_intp const *dims, npy_intp const *strides, void *data,
+ int flags, PyObject *obj);
NPY_NO_EXPORT PyObject *
PyArray_NewFromDescrAndBase(
- PyTypeObject *subtype, PyArray_Descr *descr,
- int nd, npy_intp *dims, npy_intp *strides, void *data,
+ PyTypeObject *subtype, PyArray_Descr *descr, int nd,
+ npy_intp const *dims, npy_intp const *strides, void *data,
int flags, PyObject *obj, PyObject *base);
NPY_NO_EXPORT PyObject *
-PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
- npy_intp *dims, npy_intp *strides, void *data,
- int flags, PyObject *obj, PyObject *base, int zeroed,
- int allow_emptystring);
+PyArray_NewFromDescr_int(
+ PyTypeObject *subtype, PyArray_Descr *descr, int nd,
+ npy_intp const *dims, npy_intp const *strides, void *data,
+ int flags, PyObject *obj, PyObject *base, int zeroed,
+ int allow_emptystring);
-NPY_NO_EXPORT PyObject *PyArray_New(PyTypeObject *, int nd, npy_intp *,
- int, npy_intp *, void *, int, int, PyObject *);
+NPY_NO_EXPORT PyObject *
+PyArray_NewLikeArrayWithShape(
+ PyArrayObject *prototype, NPY_ORDER order,
+ PyArray_Descr *dtype, int ndim, npy_intp const *dims, int subok);
+
+NPY_NO_EXPORT PyObject *
+PyArray_New(
+ PyTypeObject *, int nd, npy_intp const *,
+ int, npy_intp const*, void *, int, int, PyObject *);
NPY_NO_EXPORT PyObject *
PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
@@ -64,7 +73,7 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src,
/* FIXME: remove those from here */
NPY_NO_EXPORT void
-_array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize,
+_array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t itemsize,
int inflag, int *objflags);
NPY_NO_EXPORT void
diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c
index 7f837901c..d21bb9776 100644
--- a/numpy/core/src/multiarray/datetime.c
+++ b/numpy/core/src/multiarray/datetime.c
@@ -27,6 +27,40 @@
#include "datetime_strings.h"
/*
+ * Computes the python `ret, d = divmod(d, unit)`.
+ *
+ * Note that GCC is smart enough at -O2 to eliminate the `if(*d < 0)` branch
+ * for subsequent calls to this command - it is able to deduce that `*d >= 0`.
+ */
+static inline
+npy_int64 extract_unit_64(npy_int64 *d, npy_int64 unit) {
+ assert(unit > 0);
+ npy_int64 div = *d / unit;
+ npy_int64 mod = *d % unit;
+ if (mod < 0) {
+ mod += unit;
+ div -= 1;
+ }
+ assert(mod >= 0);
+ *d = mod;
+ return div;
+}
+
+static inline
+npy_int32 extract_unit_32(npy_int32 *d, npy_int32 unit) {
+ assert(unit > 0);
+ npy_int32 div = *d / unit;
+ npy_int32 mod = *d % unit;
+ if (mod < 0) {
+ mod += unit;
+ div -= 1;
+ }
+ assert(mod >= 0);
+ *d = mod;
+ return div;
+}
+
+/*
* Imports the PyDateTime functions so we can create these objects.
* This is called during module initialization
*/
@@ -160,17 +194,7 @@ days_to_yearsdays(npy_int64 *days_)
npy_int64 year;
/* Break down the 400 year cycle to get the year and day within the year */
- if (days >= 0) {
- year = 400 * (days / days_per_400years);
- days = days % days_per_400years;
- }
- else {
- year = 400 * ((days - (days_per_400years - 1)) / days_per_400years);
- days = days % days_per_400years;
- if (days < 0) {
- days += days_per_400years;
- }
- }
+ year = 400 * extract_unit_64(&days, days_per_400years);
/* Work out the year/day within the 400 year cycle */
if (days >= 366) {
@@ -386,7 +410,8 @@ convert_datetimestruct_to_datetime(PyArray_DatetimeMetaData *meta,
* TO BE REMOVED - NOT USED INTERNALLY.
*/
NPY_NO_EXPORT npy_datetime
-PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d)
+PyArray_DatetimeStructToDatetime(
+ NPY_DATETIMEUNIT NPY_UNUSED(fr), npy_datetimestruct *NPY_UNUSED(d))
{
PyErr_SetString(PyExc_RuntimeError,
"The NumPy PyArray_DatetimeStructToDatetime function has "
@@ -400,7 +425,8 @@ PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d)
* TO BE REMOVED - NOT USED INTERNALLY.
*/
NPY_NO_EXPORT npy_datetime
-PyArray_TimedeltaStructToTimedelta(NPY_DATETIMEUNIT fr, npy_timedeltastruct *d)
+PyArray_TimedeltaStructToTimedelta(
+ NPY_DATETIMEUNIT NPY_UNUSED(fr), npy_timedeltastruct *NPY_UNUSED(d))
{
PyErr_SetString(PyExc_RuntimeError,
"The NumPy PyArray_TimedeltaStructToTimedelta function has "
@@ -416,7 +442,7 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
npy_datetime dt,
npy_datetimestruct *out)
{
- npy_int64 perday;
+ npy_int64 days;
/* Initialize the output to all zeros */
memset(out, 0, sizeof(npy_datetimestruct));
@@ -451,14 +477,8 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
break;
case NPY_FR_M:
- if (dt >= 0) {
- out->year = 1970 + dt / 12;
- out->month = dt % 12 + 1;
- }
- else {
- out->year = 1969 + (dt + 1) / 12;
- out->month = 12 + (dt + 1)% 12;
- }
+ out->year = 1970 + extract_unit_64(&dt, 12);
+ out->month = dt + 1;
break;
case NPY_FR_W:
@@ -471,171 +491,96 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
break;
case NPY_FR_h:
- perday = 24LL;
-
- if (dt >= 0) {
- set_datetimestruct_days(dt / perday, out);
- dt = dt % perday;
- }
- else {
- set_datetimestruct_days((dt - (perday-1)) / perday, out);
- dt = (perday-1) + (dt + 1) % perday;
- }
+ days = extract_unit_64(&dt, 24LL);
+ set_datetimestruct_days(days, out);
out->hour = (int)dt;
break;
case NPY_FR_m:
- perday = 24LL * 60;
-
- if (dt >= 0) {
- set_datetimestruct_days(dt / perday, out);
- dt = dt % perday;
- }
- else {
- set_datetimestruct_days((dt - (perday-1)) / perday, out);
- dt = (perday-1) + (dt + 1) % perday;
- }
- out->hour = (int)(dt / 60);
- out->min = (int)(dt % 60);
+ days = extract_unit_64(&dt, 60LL*24);
+ set_datetimestruct_days(days, out);
+ out->hour = (int)extract_unit_64(&dt, 60LL);
+ out->min = (int)dt;
break;
case NPY_FR_s:
- perday = 24LL * 60 * 60;
-
- if (dt >= 0) {
- set_datetimestruct_days(dt / perday, out);
- dt = dt % perday;
- }
- else {
- set_datetimestruct_days((dt - (perday-1)) / perday, out);
- dt = (perday-1) + (dt + 1) % perday;
- }
- out->hour = (int)(dt / (60*60));
- out->min = (int)((dt / 60) % 60);
- out->sec = (int)(dt % 60);
+ days = extract_unit_64(&dt, 60LL*60*24);
+ set_datetimestruct_days(days, out);
+ out->hour = (int)extract_unit_64(&dt, 60LL*60);
+ out->min = (int)extract_unit_64(&dt, 60LL);
+ out->sec = (int)dt;
break;
case NPY_FR_ms:
- perday = 24LL * 60 * 60 * 1000;
-
- if (dt >= 0) {
- set_datetimestruct_days(dt / perday, out);
- dt = dt % perday;
- }
- else {
- set_datetimestruct_days((dt - (perday-1)) / perday, out);
- dt = (perday-1) + (dt + 1) % perday;
- }
- out->hour = (int)(dt / (60*60*1000LL));
- out->min = (int)((dt / (60*1000LL)) % 60);
- out->sec = (int)((dt / 1000LL) % 60);
- out->us = (int)((dt % 1000LL) * 1000);
+ days = extract_unit_64(&dt, 1000LL*60*60*24);
+ set_datetimestruct_days(days, out);
+ out->hour = (int)extract_unit_64(&dt, 1000LL*60*60);
+ out->min = (int)extract_unit_64(&dt, 1000LL*60);
+ out->sec = (int)extract_unit_64(&dt, 1000LL);
+ out->us = (int)(dt * 1000);
break;
case NPY_FR_us:
- perday = 24LL * 60LL * 60LL * 1000LL * 1000LL;
-
- if (dt >= 0) {
- set_datetimestruct_days(dt / perday, out);
- dt = dt % perday;
- }
- else {
- set_datetimestruct_days((dt - (perday-1)) / perday, out);
- dt = (perday-1) + (dt + 1) % perday;
- }
- out->hour = (int)(dt / (60*60*1000000LL));
- out->min = (int)((dt / (60*1000000LL)) % 60);
- out->sec = (int)((dt / 1000000LL) % 60);
- out->us = (int)(dt % 1000000LL);
+ days = extract_unit_64(&dt, 1000LL*1000*60*60*24);
+ set_datetimestruct_days(days, out);
+ out->hour = (int)extract_unit_64(&dt, 1000LL*1000*60*60);
+ out->min = (int)extract_unit_64(&dt, 1000LL*1000*60);
+ out->sec = (int)extract_unit_64(&dt, 1000LL*1000);
+ out->us = (int)dt;
break;
case NPY_FR_ns:
- perday = 24LL * 60LL * 60LL * 1000LL * 1000LL * 1000LL;
-
- if (dt >= 0) {
- set_datetimestruct_days(dt / perday, out);
- dt = dt % perday;
- }
- else {
- set_datetimestruct_days((dt - (perday-1)) / perday, out);
- dt = (perday-1) + (dt + 1) % perday;
- }
- out->hour = (int)(dt / (60*60*1000000000LL));
- out->min = (int)((dt / (60*1000000000LL)) % 60);
- out->sec = (int)((dt / 1000000000LL) % 60);
- out->us = (int)((dt / 1000LL) % 1000000LL);
- out->ps = (int)((dt % 1000LL) * 1000);
+ days = extract_unit_64(&dt, 1000LL*1000*1000*60*60*24);
+ set_datetimestruct_days(days, out);
+ out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*60*60);
+ out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*60);
+ out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000);
+ out->us = (int)extract_unit_64(&dt, 1000LL);
+ out->ps = (int)(dt * 1000);
break;
case NPY_FR_ps:
- perday = 24LL * 60 * 60 * 1000 * 1000 * 1000 * 1000;
-
- if (dt >= 0) {
- set_datetimestruct_days(dt / perday, out);
- dt = dt % perday;
- }
- else {
- set_datetimestruct_days((dt - (perday-1)) / perday, out);
- dt = (perday-1) + (dt + 1) % perday;
- }
- out->hour = (int)(dt / (60*60*1000000000000LL));
- out->min = (int)((dt / (60*1000000000000LL)) % 60);
- out->sec = (int)((dt / 1000000000000LL) % 60);
- out->us = (int)((dt / 1000000LL) % 1000000LL);
- out->ps = (int)(dt % 1000000LL);
+ days = extract_unit_64(&dt, 1000LL*1000*1000*1000*60*60*24);
+ set_datetimestruct_days(days, out);
+ out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*60*60);
+ out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*60);
+ out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000);
+ out->us = (int)extract_unit_64(&dt, 1000LL*1000);
+ out->ps = (int)(dt);
break;
case NPY_FR_fs:
/* entire range is only +- 2.6 hours */
- if (dt >= 0) {
- out->hour = (int)(dt / (60*60*1000000000000000LL));
- out->min = (int)((dt / (60*1000000000000000LL)) % 60);
- out->sec = (int)((dt / 1000000000000000LL) % 60);
- out->us = (int)((dt / 1000000000LL) % 1000000LL);
- out->ps = (int)((dt / 1000LL) % 1000000LL);
- out->as = (int)((dt % 1000LL) * 1000);
- }
- else {
- npy_datetime minutes;
-
- minutes = dt / (60*1000000000000000LL);
- dt = dt % (60*1000000000000000LL);
- if (dt < 0) {
- dt += (60*1000000000000000LL);
- --minutes;
- }
- /* Offset the negative minutes */
- add_minutes_to_datetimestruct(out, minutes);
- out->sec = (int)((dt / 1000000000000000LL) % 60);
- out->us = (int)((dt / 1000000000LL) % 1000000LL);
- out->ps = (int)((dt / 1000LL) % 1000000LL);
- out->as = (int)((dt % 1000LL) * 1000);
- }
+ out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*60*60);
+ if (out->hour < 0) {
+ out->year = 1969;
+ out->month = 12;
+ out->day = 31;
+ out->hour += 24;
+ assert(out->hour >= 0);
+ }
+ out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*60);
+ out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000);
+ out->us = (int)extract_unit_64(&dt, 1000LL*1000*1000);
+ out->ps = (int)extract_unit_64(&dt, 1000LL);
+ out->as = (int)(dt * 1000);
break;
case NPY_FR_as:
/* entire range is only +- 9.2 seconds */
- if (dt >= 0) {
- out->sec = (int)((dt / 1000000000000000000LL) % 60);
- out->us = (int)((dt / 1000000000000LL) % 1000000LL);
- out->ps = (int)((dt / 1000000LL) % 1000000LL);
- out->as = (int)(dt % 1000000LL);
- }
- else {
- npy_datetime seconds;
-
- seconds = dt / 1000000000000000000LL;
- dt = dt % 1000000000000000000LL;
- if (dt < 0) {
- dt += 1000000000000000000LL;
- --seconds;
- }
- /* Offset the negative seconds */
- add_seconds_to_datetimestruct(out, seconds);
- out->us = (int)((dt / 1000000000000LL) % 1000000LL);
- out->ps = (int)((dt / 1000000LL) % 1000000LL);
- out->as = (int)(dt % 1000000LL);
- }
+ out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*1000);
+ if (out->sec < 0) {
+ out->year = 1969;
+ out->month = 12;
+ out->day = 31;
+ out->hour = 23;
+ out->min = 59;
+ out->sec += 60;
+ assert(out->sec >= 0);
+ }
+ out->us = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000);
+ out->ps = (int)extract_unit_64(&dt, 1000LL*1000);
+ out->as = (int)dt;
break;
default:
@@ -655,8 +600,9 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta,
* TO BE REMOVED - NOT USED INTERNALLY.
*/
NPY_NO_EXPORT void
-PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr,
- npy_datetimestruct *result)
+PyArray_DatetimeToDatetimeStruct(
+ npy_datetime NPY_UNUSED(val), NPY_DATETIMEUNIT NPY_UNUSED(fr),
+ npy_datetimestruct *result)
{
PyErr_SetString(PyExc_RuntimeError,
"The NumPy PyArray_DatetimeToDatetimeStruct function has "
@@ -676,8 +622,9 @@ PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr,
* TO BE REMOVED - NOT USED INTERNALLY.
*/
NPY_NO_EXPORT void
-PyArray_TimedeltaToTimedeltaStruct(npy_timedelta val, NPY_DATETIMEUNIT fr,
- npy_timedeltastruct *result)
+PyArray_TimedeltaToTimedeltaStruct(
+ npy_timedelta NPY_UNUSED(val), NPY_DATETIMEUNIT NPY_UNUSED(fr),
+ npy_timedeltastruct *result)
{
PyErr_SetString(PyExc_RuntimeError,
"The NumPy PyArray_TimedeltaToTimedeltaStruct function has "
@@ -1887,6 +1834,7 @@ convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple,
return -1;
}
equal_one = PyObject_RichCompareBool(event, one, Py_EQ);
+ Py_DECREF(one);
if (equal_one == -1) {
return -1;
}
@@ -2067,20 +2015,8 @@ add_seconds_to_datetimestruct(npy_datetimestruct *dts, int seconds)
int minutes;
dts->sec += seconds;
- if (dts->sec < 0) {
- minutes = dts->sec / 60;
- dts->sec = dts->sec % 60;
- if (dts->sec < 0) {
- --minutes;
- dts->sec += 60;
- }
- add_minutes_to_datetimestruct(dts, minutes);
- }
- else if (dts->sec >= 60) {
- minutes = dts->sec / 60;
- dts->sec = dts->sec % 60;
- add_minutes_to_datetimestruct(dts, minutes);
- }
+ minutes = extract_unit_32(&dts->sec, 60);
+ add_minutes_to_datetimestruct(dts, minutes);
}
/*
@@ -2092,28 +2028,13 @@ add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes)
{
int isleap;
- /* MINUTES */
dts->min += minutes;
- while (dts->min < 0) {
- dts->min += 60;
- dts->hour--;
- }
- while (dts->min >= 60) {
- dts->min -= 60;
- dts->hour++;
- }
- /* HOURS */
- while (dts->hour < 0) {
- dts->hour += 24;
- dts->day--;
- }
- while (dts->hour >= 24) {
- dts->hour -= 24;
- dts->day++;
- }
+ /* propagate invalid minutes into hour and day changes */
+ dts->hour += extract_unit_32(&dts->min, 60);
+ dts->day += extract_unit_32(&dts->hour, 24);
- /* DAYS */
+ /* propagate invalid days into month and year changes */
if (dts->day < 1) {
dts->month--;
if (dts->month < 1) {
@@ -2305,6 +2226,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out,
if (DEPRECATE(
"parsing timezone aware datetimes is deprecated; "
"this will raise an error in the future") < 0) {
+ Py_DECREF(tmp);
return -1;
}
@@ -2321,10 +2243,14 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out,
* which contains the value we want.
*/
tmp = PyObject_CallMethod(offset, "total_seconds", "");
+ Py_DECREF(offset);
if (tmp == NULL) {
return -1;
}
- seconds_offset = PyInt_AsLong(tmp);
+ /* Rounding here is no worse than the integer division below.
+ * Only whole minute offsets are supported by numpy anyway.
+ */
+ seconds_offset = (int)PyFloat_AsDouble(tmp);
if (error_converting(seconds_offset)) {
Py_DECREF(tmp);
return -1;
@@ -2468,6 +2394,9 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj,
return -1;
}
*out = PyLong_AsLongLong(obj);
+ if (error_converting(*out)) {
+ return -1;
+ }
return 0;
}
/* Datetime scalar */
@@ -2666,6 +2595,9 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj,
}
*out = PyLong_AsLongLong(obj);
+ if (error_converting(*out)) {
+ return -1;
+ }
return 0;
}
/* Timedelta scalar */
@@ -2845,6 +2777,19 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj,
*out = NPY_DATETIME_NAT;
return 0;
}
+ else if (PyArray_IsScalar(obj, Integer)) {
+ /* Use the default unit if none was specified */
+ if (meta->base == NPY_FR_ERROR) {
+ meta->base = NPY_DATETIME_DEFAULTUNIT;
+ meta->num = 1;
+ }
+
+ *out = PyLong_AsLongLong(obj);
+ if (error_converting(*out)) {
+ return -1;
+ }
+ return 0;
+ }
else {
PyErr_SetString(PyExc_ValueError,
"Could not convert object to NumPy timedelta");
@@ -2916,7 +2861,6 @@ convert_datetime_to_pyobject(npy_datetime dt, PyArray_DatetimeMetaData *meta)
NPY_NO_EXPORT PyObject *
convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta)
{
- PyObject *ret = NULL;
npy_timedelta value;
int days = 0, seconds = 0, useconds = 0;
@@ -2946,54 +2890,47 @@ convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta)
/* Convert to days/seconds/useconds */
switch (meta->base) {
case NPY_FR_W:
- value *= 7;
+ days = value * 7;
break;
case NPY_FR_D:
+ days = value;
break;
case NPY_FR_h:
- seconds = (int)((value % 24) * (60*60));
- value = value / 24;
+ days = extract_unit_64(&value, 24ULL);
+ seconds = value*60*60;
break;
case NPY_FR_m:
- seconds = (int)(value % (24*60)) * 60;
- value = value / (24*60);
+ days = extract_unit_64(&value, 60ULL*24);
+ seconds = value*60;
break;
case NPY_FR_s:
- seconds = (int)(value % (24*60*60));
- value = value / (24*60*60);
+ days = extract_unit_64(&value, 60ULL*60*24);
+ seconds = value;
break;
case NPY_FR_ms:
- useconds = (int)(value % 1000) * 1000;
- value = value / 1000;
- seconds = (int)(value % (24*60*60));
- value = value / (24*60*60);
+ days = extract_unit_64(&value, 1000ULL*60*60*24);
+ seconds = extract_unit_64(&value, 1000ULL);
+ useconds = value*1000;
break;
case NPY_FR_us:
- useconds = (int)(value % (1000*1000));
- value = value / (1000*1000);
- seconds = (int)(value % (24*60*60));
- value = value / (24*60*60);
+ days = extract_unit_64(&value, 1000ULL*1000*60*60*24);
+ seconds = extract_unit_64(&value, 1000ULL*1000);
+ useconds = value;
break;
default:
+ // unreachable, handled by the `if` above
+ assert(NPY_FALSE);
break;
}
/*
- * 'value' represents days, and seconds/useconds are filled.
- *
* If it would overflow the datetime.timedelta days, return a raw int
*/
- if (value < -999999999 || value > 999999999) {
+ if (days < -999999999 || days > 999999999) {
return PyLong_FromLongLong(td);
}
else {
- days = (int)value;
- ret = PyDelta_FromDSU(days, seconds, useconds);
- if (ret == NULL) {
- return NULL;
- }
+ return PyDelta_FromDSU(days, seconds, useconds);
}
-
- return ret;
}
/*
@@ -3158,7 +3095,7 @@ is_any_numpy_datetime_or_timedelta(PyObject *obj)
*/
NPY_NO_EXPORT int
convert_pyobjects_to_datetimes(int count,
- PyObject **objs, int *type_nums,
+ PyObject **objs, const int *type_nums,
NPY_CASTING casting,
npy_int64 *out_values,
PyArray_DatetimeMetaData *inout_meta)
@@ -3812,18 +3749,26 @@ recursive_find_object_timedelta64_type(PyObject *obj,
* single object using [()], but not by using
* __getitem__(integer) approaches
*/
- PyObject *item, *meth, *args;
+ PyObject *item, *args;
- meth = PyObject_GetAttrString(obj, "__getitem__");
- args = Py_BuildValue("(())");
- item = PyObject_CallObject(meth, args);
+ args = PyTuple_New(0);
+ if (args == NULL) {
+ return 0;
+ }
+ item = PyObject_GetItem(obj, args);
+ Py_DECREF(args);
+ if (item == NULL) {
+ return 0;
+ }
/*
* NOTE: may need other type checks here in the future
* for expanded 0 D datetime array conversions?
*/
if (PyDelta_Check(item)) {
+ Py_DECREF(item);
return delta_checker(meta);
}
+ Py_DECREF(item);
}
}
}
diff --git a/numpy/core/src/multiarray/datetime_busday.c b/numpy/core/src/multiarray/datetime_busday.c
index c04a6c125..cdeb65d0e 100644
--- a/numpy/core/src/multiarray/datetime_busday.c
+++ b/numpy/core/src/multiarray/datetime_busday.c
@@ -48,7 +48,7 @@ get_day_of_week(npy_datetime date)
*/
static int
is_holiday(npy_datetime date,
- npy_datetime *holidays_begin, npy_datetime *holidays_end)
+ npy_datetime *holidays_begin, const npy_datetime *holidays_end)
{
npy_datetime *trial;
@@ -88,7 +88,7 @@ is_holiday(npy_datetime date,
*/
static npy_datetime *
find_earliest_holiday_on_or_after(npy_datetime date,
- npy_datetime *holidays_begin, npy_datetime *holidays_end)
+ npy_datetime *holidays_begin, const npy_datetime *holidays_end)
{
npy_datetime *trial;
@@ -127,7 +127,7 @@ find_earliest_holiday_on_or_after(npy_datetime date,
*/
static npy_datetime *
find_earliest_holiday_after(npy_datetime date,
- npy_datetime *holidays_begin, npy_datetime *holidays_end)
+ npy_datetime *holidays_begin, const npy_datetime *holidays_end)
{
npy_datetime *trial;
@@ -159,7 +159,7 @@ static int
apply_business_day_roll(npy_datetime date, npy_datetime *out,
int *out_day_of_week,
NPY_BUSDAY_ROLL roll,
- npy_bool *weekmask,
+ const npy_bool *weekmask,
npy_datetime *holidays_begin, npy_datetime *holidays_end)
{
int day_of_week;
@@ -361,7 +361,7 @@ apply_business_day_offset(npy_datetime date, npy_int64 offset,
static int
apply_business_day_count(npy_datetime date_begin, npy_datetime date_end,
npy_int64 *out,
- npy_bool *weekmask, int busdays_in_weekmask,
+ const npy_bool *weekmask, int busdays_in_weekmask,
npy_datetime *holidays_begin, npy_datetime *holidays_end)
{
npy_int64 count, whole_weeks;
@@ -722,7 +722,7 @@ finish:
*/
NPY_NO_EXPORT PyArrayObject *
is_business_day(PyArrayObject *dates, PyArrayObject *out,
- npy_bool *weekmask, int busdays_in_weekmask,
+ const npy_bool *weekmask, int busdays_in_weekmask,
npy_datetime *holidays_begin, npy_datetime *holidays_end)
{
PyArray_DatetimeMetaData temp_meta;
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index 439980877..23d140cf6 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -10,7 +10,7 @@
#include "numpy/arrayscalars.h"
#include "npy_config.h"
-
+#include "npy_ctypes.h"
#include "npy_pycompat.h"
#include "_datetime.h"
@@ -19,6 +19,7 @@
#include "descriptor.h"
#include "alloc.h"
#include "assert.h"
+#include "buffer.h"
/*
* offset: A starting offset.
@@ -41,109 +42,87 @@ static PyObject *typeDict = NULL; /* Must be explicitly loaded */
static PyArray_Descr *
_use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag);
-
-/*
- * Returns value of PyMapping_GetItemString but as a borrowed reference instead
- * of a new reference.
- */
-static PyObject *
-Borrowed_PyMapping_GetItemString(PyObject *o, char *key)
-{
- PyObject *ret = PyMapping_GetItemString(o, key);
- Py_XDECREF(ret);
- return ret;
-}
-
-/*
- * Creates a dtype object from ctypes inputs.
- *
- * Returns a new reference to a dtype object, or NULL
- * if this is not possible. When it returns NULL, it does
- * not set a Python exception.
- */
static PyArray_Descr *
-_arraydescr_fromctypes(PyObject *obj)
+_arraydescr_from_ctypes_type(PyTypeObject *type)
{
- PyObject *dtypedescr;
- PyArray_Descr *newdescr;
- int ret;
+ PyObject *_numpy_dtype_ctypes;
+ PyObject *res;
- /* Understand basic ctypes */
- dtypedescr = PyObject_GetAttrString(obj, "_type_");
- PyErr_Clear();
- if (dtypedescr) {
- ret = PyArray_DescrConverter(dtypedescr, &newdescr);
- Py_DECREF(dtypedescr);
- if (ret == NPY_SUCCEED) {
- PyObject *length;
- /* Check for ctypes arrays */
- length = PyObject_GetAttrString(obj, "_length_");
- PyErr_Clear();
- if (length) {
- /* derived type */
- PyObject *newtup;
- PyArray_Descr *derived;
- newtup = Py_BuildValue("N(N)", newdescr, length);
- ret = PyArray_DescrConverter(newtup, &derived);
- Py_DECREF(newtup);
- if (ret == NPY_SUCCEED) {
- return derived;
- }
- PyErr_Clear();
- return NULL;
- }
- return newdescr;
- }
- PyErr_Clear();
+ /* Call the python function of the same name. */
+ _numpy_dtype_ctypes = PyImport_ImportModule("numpy.core._dtype_ctypes");
+ if (_numpy_dtype_ctypes == NULL) {
return NULL;
}
- /* Understand ctypes structures --
- bit-fields are not supported
- automatically aligns */
- dtypedescr = PyObject_GetAttrString(obj, "_fields_");
- PyErr_Clear();
- if (dtypedescr) {
- ret = PyArray_DescrAlignConverter(dtypedescr, &newdescr);
- Py_DECREF(dtypedescr);
- if (ret == NPY_SUCCEED) {
- return newdescr;
- }
- PyErr_Clear();
+ res = PyObject_CallMethod(_numpy_dtype_ctypes, "dtype_from_ctypes_type", "O", (PyObject *)type);
+ Py_DECREF(_numpy_dtype_ctypes);
+ if (res == NULL) {
+ return NULL;
}
- return NULL;
+ /*
+ * sanity check that dtype_from_ctypes_type returned the right type,
+ * since getting it wrong would give segfaults.
+ */
+ if (!PyObject_TypeCheck(res, &PyArrayDescr_Type)) {
+ Py_DECREF(res);
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ return (PyArray_Descr *)res;
}
/*
- * This function creates a dtype object when:
- * - The object has a "dtype" attribute, and it can be converted
- * to a dtype object.
- * - The object is a ctypes type object, including array
- * and structure types.
+ * This function creates a dtype object when the object has a "dtype" attribute,
+ * and it can be converted to a dtype object.
*
* Returns a new reference to a dtype object, or NULL
- * if this is not possible. When it returns NULL, it does
- * not set a Python exception.
+ * if this is not possible.
+ * When the return value is true, the dtype attribute should have been used
+ * and parsed. Currently the only failure mode for a 1 return is a
+ * RecursionError and the descriptor is set to NULL.
+ * When the return value is false, no error will be set.
*/
-NPY_NO_EXPORT PyArray_Descr *
-_arraydescr_fromobj(PyObject *obj)
+int
+_arraydescr_from_dtype_attr(PyObject *obj, PyArray_Descr **newdescr)
{
PyObject *dtypedescr;
- PyArray_Descr *newdescr = NULL;
int ret;
/* For arbitrary objects that have a "dtype" attribute */
dtypedescr = PyObject_GetAttrString(obj, "dtype");
- PyErr_Clear();
- if (dtypedescr != NULL) {
- ret = PyArray_DescrConverter(dtypedescr, &newdescr);
+ if (dtypedescr == NULL) {
+ /*
+ * This can be reached due to recursion limit being hit while fetching
+ * the attribute (tested for py3.7). This removes the custom message.
+ */
+ goto fail;
+ }
+
+ if (Py_EnterRecursiveCall(
+ " while trying to convert the given data type from its "
+ "`.dtype` attribute.") != 0) {
Py_DECREF(dtypedescr);
- if (ret == NPY_SUCCEED) {
- return newdescr;
- }
+ return 1;
+ }
+
+ ret = PyArray_DescrConverter(dtypedescr, newdescr);
+
+ Py_DECREF(dtypedescr);
+ Py_LeaveRecursiveCall();
+ if (ret != NPY_SUCCEED) {
+ goto fail;
+ }
+
+ return 1;
+
+ fail:
+ /* Ignore all but recursion errors, to give ctypes a full try. */
+ if (!PyErr_ExceptionMatches(PyExc_RecursionError)) {
PyErr_Clear();
+ return 0;
}
- return _arraydescr_fromctypes(obj);
+ return 1;
}
/*
@@ -170,7 +149,7 @@ array_set_typeDict(PyObject *NPY_UNUSED(ignored), PyObject *args)
arg == '|' || arg == '=')
static int
-_check_for_commastring(char *type, Py_ssize_t len)
+_check_for_commastring(const char *type, Py_ssize_t len)
{
Py_ssize_t i;
int sqbracket;
@@ -286,6 +265,9 @@ _convert_from_tuple(PyObject *obj, int align)
return NULL;
}
PyArray_DESCR_REPLACE(type);
+ if (type == NULL) {
+ return NULL;
+ }
if (type->type_num == NPY_UNICODE) {
type->elsize = itemsize << 2;
}
@@ -319,15 +301,22 @@ _convert_from_tuple(PyObject *obj, int align)
"invalid shape in fixed-type tuple.");
goto fail;
}
- /*
- * If (type, 1) was given, it is equivalent to type...
- * or (type, ()) was given it is equivalent to type...
- */
- if ((shape.len == 1
- && shape.ptr[0] == 1
- && PyNumber_Check(val))
- || (shape.len == 0
- && PyTuple_Check(val))) {
+ /* if (type, ()) was given it is equivalent to type... */
+ if (shape.len == 0 && PyTuple_Check(val)) {
+ npy_free_cache_dim_obj(shape);
+ return type;
+ }
+ /* (type, 1) use to be equivalent to type, but is deprecated */
+ if (shape.len == 1
+ && shape.ptr[0] == 1
+ && PyNumber_Check(val)) {
+ /* 2019-05-20, 1.17 */
+ if (DEPRECATE_FUTUREWARNING(
+ "Passing (type, 1) or '1type' as a synonym of type is "
+ "deprecated; in a future version of numpy, it will be "
+ "understood as (type, (1,)) / '(1,)type'.") < 0) {
+ goto fail;
+ }
npy_free_cache_dim_obj(shape);
return type;
}
@@ -509,9 +498,6 @@ _convert_from_array_descr(PyObject *obj, int align)
else {
ret = PyArray_DescrConverter(PyTuple_GET_ITEM(item, 1), &conv);
}
- if (ret == NPY_FAIL) {
- PyObject_Print(PyTuple_GET_ITEM(item, 1), stderr, 0);
- }
}
else if (PyTuple_GET_SIZE(item) == 3) {
newobj = PyTuple_GetSlice(item, 1, 3);
@@ -529,6 +515,7 @@ _convert_from_array_descr(PyObject *obj, int align)
if (ret == NPY_FAIL) {
goto fail;
}
+
if ((PyDict_GetItem(fields, name) != NULL)
|| (title
&& PyBaseString_Check(title)
@@ -541,6 +528,7 @@ _convert_from_array_descr(PyObject *obj, int align)
#if defined(NPY_PY3K)
Py_DECREF(name);
#endif
+ Py_DECREF(conv);
goto fail;
}
dtypeflags |= (conv->flags & NPY_FROM_FIELDS);
@@ -863,9 +851,11 @@ _use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag)
else if (new->elsize != conv->elsize) {
PyErr_SetString(PyExc_ValueError,
"mismatch in size of old and new data-descriptor");
+ Py_DECREF(new);
goto fail;
}
else if (invalid_union_object_dtype(new, conv)) {
+ Py_DECREF(new);
goto fail;
}
@@ -1024,8 +1014,11 @@ _convert_from_dict(PyObject *obj, int align)
{
PyArray_Descr *new;
PyObject *fields = NULL;
- PyObject *names, *offsets, *descrs, *titles, *tmp;
- PyObject *metadata;
+ PyObject *names = NULL;
+ PyObject *offsets= NULL;
+ PyObject *descrs = NULL;
+ PyObject *titles = NULL;
+ PyObject *metadata, *tmp;
int n, i;
int totalsize, itemsize;
int maxalign = 0;
@@ -1040,19 +1033,27 @@ _convert_from_dict(PyObject *obj, int align)
/*
* Use PyMapping_GetItemString to support dictproxy objects as well.
*/
- names = Borrowed_PyMapping_GetItemString(obj, "names");
- descrs = Borrowed_PyMapping_GetItemString(obj, "formats");
- if (!names || !descrs) {
+ names = PyMapping_GetItemString(obj, "names");
+ if (names == NULL) {
+ Py_DECREF(fields);
+ /* XXX should check this is a KeyError */
+ PyErr_Clear();
+ return _use_fields_dict(obj, align);
+ }
+ descrs = PyMapping_GetItemString(obj, "formats");
+ if (descrs == NULL) {
Py_DECREF(fields);
+ /* XXX should check this is a KeyError */
PyErr_Clear();
+ Py_DECREF(names);
return _use_fields_dict(obj, align);
}
n = PyObject_Length(names);
- offsets = Borrowed_PyMapping_GetItemString(obj, "offsets");
+ offsets = PyMapping_GetItemString(obj, "offsets");
if (!offsets) {
PyErr_Clear();
}
- titles = Borrowed_PyMapping_GetItemString(obj, "titles");
+ titles = PyMapping_GetItemString(obj, "titles");
if (!titles) {
PyErr_Clear();
}
@@ -1070,7 +1071,7 @@ _convert_from_dict(PyObject *obj, int align)
* If a property 'aligned' is in the dict, it overrides the align flag
* to be True if it not already true.
*/
- tmp = Borrowed_PyMapping_GetItemString(obj, "aligned");
+ tmp = PyMapping_GetItemString(obj, "aligned");
if (tmp == NULL) {
PyErr_Clear();
} else {
@@ -1078,11 +1079,13 @@ _convert_from_dict(PyObject *obj, int align)
align = 1;
}
else if (tmp != Py_False) {
+ Py_DECREF(tmp);
PyErr_SetString(PyExc_ValueError,
"NumPy dtype descriptor includes 'aligned' entry, "
"but its value is neither True nor False");
- return NULL;
+ goto fail;
}
+ Py_DECREF(tmp);
}
totalsize = 0;
@@ -1238,14 +1241,18 @@ _convert_from_dict(PyObject *obj, int align)
}
new->elsize = totalsize;
if (!PyTuple_Check(names)) {
- names = PySequence_Tuple(names);
- }
- else {
- Py_INCREF(names);
+ Py_SETREF(names, PySequence_Tuple(names));
+ if (names == NULL) {
+ Py_DECREF(new);
+ goto fail;
+ }
}
new->names = names;
new->fields = fields;
new->flags = dtypeflags;
+ /* new takes responsibility for DECREFing names, fields */
+ names = NULL;
+ fields = NULL;
/*
* If the fields weren't in order, and there was an OBJECT type,
@@ -1254,7 +1261,7 @@ _convert_from_dict(PyObject *obj, int align)
if (has_out_of_order_fields && PyDataType_REFCHK(new)) {
if (validate_object_field_overlap(new) < 0) {
Py_DECREF(new);
- return NULL;
+ goto fail;
}
}
@@ -1264,14 +1271,15 @@ _convert_from_dict(PyObject *obj, int align)
}
/* Override the itemsize if provided */
- tmp = Borrowed_PyMapping_GetItemString(obj, "itemsize");
+ tmp = PyMapping_GetItemString(obj, "itemsize");
if (tmp == NULL) {
PyErr_Clear();
} else {
itemsize = (int)PyArray_PyIntAsInt(tmp);
+ Py_DECREF(tmp);
if (error_converting(itemsize)) {
Py_DECREF(new);
- return NULL;
+ goto fail;
}
/* Make sure the itemsize isn't made too small */
if (itemsize < new->elsize) {
@@ -1280,7 +1288,7 @@ _convert_from_dict(PyObject *obj, int align)
"cannot override to smaller itemsize of %d",
(int)new->elsize, (int)itemsize);
Py_DECREF(new);
- return NULL;
+ goto fail;
}
/* If align is set, make sure the alignment divides into the size */
if (align && itemsize % new->alignment != 0) {
@@ -1289,30 +1297,43 @@ _convert_from_dict(PyObject *obj, int align)
"which is not divisible into the specified itemsize %d",
(int)new->alignment, (int)itemsize);
Py_DECREF(new);
- return NULL;
+ goto fail;
}
/* Set the itemsize */
new->elsize = itemsize;
}
/* Add the metadata if provided */
- metadata = Borrowed_PyMapping_GetItemString(obj, "metadata");
+ metadata = PyMapping_GetItemString(obj, "metadata");
if (metadata == NULL) {
PyErr_Clear();
}
else if (new->metadata == NULL) {
new->metadata = metadata;
- Py_XINCREF(new->metadata);
}
- else if (PyDict_Merge(new->metadata, metadata, 0) == -1) {
- Py_DECREF(new);
- return NULL;
+ else {
+ int ret = PyDict_Merge(new->metadata, metadata, 0);
+ Py_DECREF(metadata);
+ if (ret < 0) {
+ Py_DECREF(new);
+ goto fail;
+ }
}
+
+ Py_XDECREF(fields);
+ Py_XDECREF(names);
+ Py_XDECREF(descrs);
+ Py_XDECREF(offsets);
+ Py_XDECREF(titles);
return new;
fail:
Py_XDECREF(fields);
+ Py_XDECREF(names);
+ Py_XDECREF(descrs);
+ Py_XDECREF(offsets);
+ Py_XDECREF(titles);
return NULL;
}
@@ -1364,7 +1385,6 @@ NPY_NO_EXPORT int
PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at)
{
int check_num = NPY_NOTYPE + 10;
- PyObject *item;
int elsize = 0;
char endian = '=';
@@ -1423,10 +1443,25 @@ PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at)
check_num = NPY_VOID;
}
else {
- *at = _arraydescr_fromobj(obj);
- if (*at) {
+ if (_arraydescr_from_dtype_attr(obj, at)) {
+ /*
+ * Using dtype attribute, *at may be NULL if a
+ * RecursionError occurred.
+ */
+ if (*at == NULL) {
+ goto error;
+ }
return NPY_SUCCEED;
}
+ /*
+ * Note: this comes after _arraydescr_from_dtype_attr because the ctypes
+ * type might override the dtype if numpy does not otherwise
+ * support it.
+ */
+ if (npy_ctypes_check((PyTypeObject *)obj)) {
+ *at = _arraydescr_from_ctypes_type((PyTypeObject *)obj);
+ return *at ? NPY_SUCCEED : NPY_FAIL;
+ }
}
goto finish;
}
@@ -1509,7 +1544,8 @@ PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at)
/* A typecode like 'd' */
if (len == 1) {
- check_num = type[0];
+ /* Python byte string characters are unsigned */
+ check_num = (unsigned char) type[0];
}
/* A kind + size like 'f8' */
else {
@@ -1596,12 +1632,24 @@ PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at)
goto fail;
}
else {
- *at = _arraydescr_fromobj(obj);
- if (*at) {
+ if (_arraydescr_from_dtype_attr(obj, at)) {
+ /*
+ * Using dtype attribute, *at may be NULL if a
+ * RecursionError occurred.
+ */
+ if (*at == NULL) {
+ goto error;
+ }
return NPY_SUCCEED;
}
- if (PyErr_Occurred()) {
- return NPY_FAIL;
+ /*
+ * Note: this comes after _arraydescr_from_dtype_attr because the ctypes
+ * type might override the dtype if numpy does not otherwise
+ * support it.
+ */
+ if (npy_ctypes_check(Py_TYPE(obj))) {
+ *at = _arraydescr_from_ctypes_type(Py_TYPE(obj));
+ return *at ? NPY_SUCCEED : NPY_FAIL;
}
goto fail;
}
@@ -1615,16 +1663,22 @@ finish:
PyErr_Clear();
/* Now check to see if the object is registered in typeDict */
if (typeDict != NULL) {
- item = PyDict_GetItem(typeDict, obj);
+ PyObject *item = NULL;
#if defined(NPY_PY3K)
- if (!item && PyBytes_Check(obj)) {
+ if (PyBytes_Check(obj)) {
PyObject *tmp;
tmp = PyUnicode_FromEncodedObject(obj, "ascii", "strict");
- if (tmp != NULL) {
- item = PyDict_GetItem(typeDict, tmp);
- Py_DECREF(tmp);
+ if (tmp == NULL) {
+ goto fail;
}
+ item = PyDict_GetItem(typeDict, tmp);
+ Py_DECREF(tmp);
}
+ else {
+ item = PyDict_GetItem(typeDict, obj);
+ }
+#else
+ item = PyDict_GetItem(typeDict, obj);
#endif
if (item) {
/* Check for a deprecated Numeric-style typecode */
@@ -1660,6 +1714,9 @@ finish:
if (PyDataType_ISUNSIZED(*at) && (*at)->elsize != elsize) {
PyArray_DESCR_REPLACE(*at);
+ if (*at == NULL) {
+ goto error;
+ }
(*at)->elsize = elsize;
}
if (endian != '=' && PyArray_ISNBO(endian)) {
@@ -1668,6 +1725,9 @@ finish:
if (endian != '=' && (*at)->byteorder != '|'
&& (*at)->byteorder != endian) {
PyArray_DESCR_REPLACE(*at);
+ if (*at == NULL) {
+ goto error;
+ }
(*at)->byteorder = endian;
}
return NPY_SUCCEED;
@@ -1728,6 +1788,7 @@ PyArray_DescrNew(PyArray_Descr *base)
newdescr->c_metadata = NPY_AUXDATA_CLONE(base->c_metadata);
if (newdescr->c_metadata == NULL) {
PyErr_NoMemory();
+ /* TODO: This seems wrong, as the old fields get decref'd? */
Py_DECREF(newdescr);
return NULL;
}
@@ -1770,6 +1831,7 @@ arraydescr_dealloc(PyArray_Descr *self)
Py_INCREF(self);
return;
}
+ _dealloc_cached_buffer_info((PyObject*)self);
Py_XDECREF(self->typeobj);
Py_XDECREF(self->names);
Py_XDECREF(self->fields);
@@ -3220,7 +3282,7 @@ arraydescr_richcompare(PyArray_Descr *self, PyObject *other, int cmp_op)
}
static int
-descr_nonzero(PyObject *self)
+descr_nonzero(PyObject *NPY_UNUSED(self))
{
/* `bool(np.dtype(...)) == True` for all dtypes. Needed to override default
* nonzero implementation, which checks if `len(object) > 0`. */
@@ -3335,12 +3397,126 @@ static PyObject *
_subscript_by_index(PyArray_Descr *self, Py_ssize_t i)
{
PyObject *name = PySequence_GetItem(self->names, i);
+ PyObject *ret;
if (name == NULL) {
PyErr_Format(PyExc_IndexError,
"Field index %zd out of range.", i);
return NULL;
}
- return _subscript_by_name(self, name);
+ ret = _subscript_by_name(self, name);
+ Py_DECREF(name);
+ return ret;
+}
+
+static npy_bool
+_is_list_of_strings(PyObject *obj)
+{
+ int seqlen, i;
+ if (!PyList_CheckExact(obj)) {
+ return NPY_FALSE;
+ }
+ seqlen = PyList_GET_SIZE(obj);
+ for (i = 0; i < seqlen; i++) {
+ PyObject *item = PyList_GET_ITEM(obj, i);
+ if (!PyBaseString_Check(item)) {
+ return NPY_FALSE;
+ }
+ }
+
+ return NPY_TRUE;
+}
+
+NPY_NO_EXPORT PyArray_Descr *
+arraydescr_field_subset_view(PyArray_Descr *self, PyObject *ind)
+{
+ int seqlen, i;
+ PyObject *fields = NULL;
+ PyObject *names = NULL;
+ PyArray_Descr *view_dtype;
+
+ seqlen = PySequence_Size(ind);
+ if (seqlen == -1) {
+ return NULL;
+ }
+
+ fields = PyDict_New();
+ if (fields == NULL) {
+ goto fail;
+ }
+ names = PyTuple_New(seqlen);
+ if (names == NULL) {
+ goto fail;
+ }
+
+ for (i = 0; i < seqlen; i++) {
+ PyObject *name;
+ PyObject *tup;
+
+ name = PySequence_GetItem(ind, i);
+ if (name == NULL) {
+ goto fail;
+ }
+
+ /* Let the names tuple steal a reference now, so we don't need to
+ * decref name if an error occurs further on.
+ */
+ PyTuple_SET_ITEM(names, i, name);
+
+ tup = PyDict_GetItem(self->fields, name);
+ if (tup == NULL) {
+ PyErr_SetObject(PyExc_KeyError, name);
+ goto fail;
+ }
+
+ /* disallow use of titles as index */
+ if (PyTuple_Size(tup) == 3) {
+ PyObject *title = PyTuple_GET_ITEM(tup, 2);
+ int titlecmp = PyObject_RichCompareBool(title, name, Py_EQ);
+ if (titlecmp < 0) {
+ goto fail;
+ }
+ if (titlecmp == 1) {
+ /* if title == name, we were given a title, not a field name */
+ PyErr_SetString(PyExc_KeyError,
+ "cannot use field titles in multi-field index");
+ goto fail;
+ }
+ if (PyDict_SetItem(fields, title, tup) < 0) {
+ goto fail;
+ }
+ }
+ /* disallow duplicate field indices */
+ if (PyDict_Contains(fields, name)) {
+ PyObject *msg = NULL;
+ PyObject *fmt = PyUString_FromString(
+ "duplicate field of name {!r}");
+ if (fmt != NULL) {
+ msg = PyObject_CallMethod(fmt, "format", "O", name);
+ Py_DECREF(fmt);
+ }
+ PyErr_SetObject(PyExc_ValueError, msg);
+ Py_XDECREF(msg);
+ goto fail;
+ }
+ if (PyDict_SetItem(fields, name, tup) < 0) {
+ goto fail;
+ }
+ }
+
+ view_dtype = PyArray_DescrNewFromType(NPY_VOID);
+ if (view_dtype == NULL) {
+ goto fail;
+ }
+ view_dtype->elsize = self->elsize;
+ view_dtype->names = names;
+ view_dtype->fields = fields;
+ view_dtype->flags = self->flags;
+ return view_dtype;
+
+fail:
+ Py_XDECREF(fields);
+ Py_XDECREF(names);
+ return NULL;
}
static PyObject *
@@ -3353,6 +3529,9 @@ descr_subscript(PyArray_Descr *self, PyObject *op)
if (PyBaseString_Check(op)) {
return _subscript_by_name(self, op);
}
+ else if (_is_list_of_strings(op)) {
+ return (PyObject *)arraydescr_field_subset_view(self, op);
+ }
else {
Py_ssize_t i = PyArray_PyIntAsIntp(op);
if (error_converting(i)) {
@@ -3360,7 +3539,8 @@ descr_subscript(PyArray_Descr *self, PyObject *op)
PyObject *err = PyErr_Occurred();
if (PyErr_GivenExceptionMatches(err, PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
- "Field key must be an integer, string, or unicode.");
+ "Field key must be an integer field offset, "
+ "single field name, or list of field names.");
}
return NULL;
}
diff --git a/numpy/core/src/multiarray/descriptor.h b/numpy/core/src/multiarray/descriptor.h
index 5a3e4b15f..6024c5e77 100644
--- a/numpy/core/src/multiarray/descriptor.h
+++ b/numpy/core/src/multiarray/descriptor.h
@@ -7,13 +7,25 @@ NPY_NO_EXPORT PyObject *arraydescr_protocol_descr_get(PyArray_Descr *self);
NPY_NO_EXPORT PyObject *
array_set_typeDict(PyObject *NPY_UNUSED(ignored), PyObject *args);
-NPY_NO_EXPORT PyArray_Descr *
-_arraydescr_fromobj(PyObject *obj);
+int
+_arraydescr_from_dtype_attr(PyObject *obj, PyArray_Descr **newdescr);
NPY_NO_EXPORT int
is_dtype_struct_simple_unaligned_layout(PyArray_Descr *dtype);
+/*
+ * Filter the fields of a dtype to only those in the list of strings, ind.
+ *
+ * No type checking is performed on the input.
+ *
+ * Raises:
+ * ValueError - if a field is repeated
+ * KeyError - if an invalid field name (or any field title) is used
+ */
+NPY_NO_EXPORT PyArray_Descr *
+arraydescr_field_subset_view(PyArray_Descr *self, PyObject *ind);
+
extern NPY_NO_EXPORT char *_datetime_strings[];
#endif
diff --git a/numpy/core/src/multiarray/dragon4.c b/numpy/core/src/multiarray/dragon4.c
index 14dfa71c2..1694596e9 100644
--- a/numpy/core/src/multiarray/dragon4.c
+++ b/numpy/core/src/multiarray/dragon4.c
@@ -1,31 +1,33 @@
/*
* Copyright (c) 2014 Ryan Juckett
- * http://www.ryanjuckett.com/
*
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
*
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
*
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- *
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- *
- * 3. This notice may not be removed or altered from any source
- * distribution.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
*/
/*
* This file contains a modified version of Ryan Juckett's Dragon4
- * implementation, which has been ported from C++ to C and which has
+ * implementation, obtained from http://www.ryanjuckett.com,
+ * which has been ported from C++ to C and which has
* modifications specific to printing floats in numpy.
+ *
+ * Ryan Juckett's original code was under the Zlib license; he gave numpy
+ * permission to include it under the MIT license instead.
*/
#include "dragon4.h"
@@ -874,7 +876,7 @@ BigInt_Pow2(BigInt *result, npy_uint32 exponent)
result->length = blockIdx + 1;
bitIdx = (exponent % 32);
- result->blocks[blockIdx] |= (1 << bitIdx);
+ result->blocks[blockIdx] |= ((npy_uint32)1 << bitIdx);
}
/*
diff --git a/numpy/core/src/multiarray/dragon4.h b/numpy/core/src/multiarray/dragon4.h
index 2b8b4cef4..3a99bde6c 100644
--- a/numpy/core/src/multiarray/dragon4.h
+++ b/numpy/core/src/multiarray/dragon4.h
@@ -1,31 +1,33 @@
/*
* Copyright (c) 2014 Ryan Juckett
- * http://www.ryanjuckett.com/
*
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
*
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
*
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- *
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- *
- * 3. This notice may not be removed or altered from any source
- * distribution.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
*/
/*
* This file contains a modified version of Ryan Juckett's Dragon4
- * implementation, which has been ported from C++ to C and which has
+ * implementation, obtained from http://www.ryanjuckett.com,
+ * which has been ported from C++ to C and which has
* modifications specific to printing floats in numpy.
+ *
+ * Ryan Juckett's original code was under the Zlib license; he gave numpy
+ * permission to include it under the MIT license instead.
*/
#ifndef _NPY_DRAGON4_H_
diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c
index 97d899ce0..ef0dd4a01 100644
--- a/numpy/core/src/multiarray/dtype_transfer.c
+++ b/numpy/core/src/multiarray/dtype_transfer.c
@@ -26,6 +26,7 @@
#include "_datetime.h"
#include "datetime_strings.h"
#include "descriptor.h"
+#include "array_assign.h"
#include "shape.h"
#include "lowlevel_strided_loops.h"
@@ -51,6 +52,20 @@
#endif
/**********************************************/
+#if NPY_DT_DBG_TRACING
+/*
+ * Thin wrapper around print that ignores exceptions
+ */
+static void
+_safe_print(PyObject *obj)
+{
+ if (PyObject_Print(obj, stdout, 0) < 0) {
+ PyErr_Clear();
+ printf("<error during print>");
+ }
+}
+#endif
+
/*
* Returns a transfer function which DECREFs any references in src_type.
*
@@ -1042,9 +1057,9 @@ get_nbo_cast_datetime_transfer_function(int aligned,
#if NPY_DT_DBG_TRACING
printf("Dtype transfer from ");
- PyObject_Print((PyObject *)src_dtype, stdout, 0);
+ _safe_print((PyObject *)src_dtype);
printf(" to ");
- PyObject_Print((PyObject *)dst_dtype, stdout, 0);
+ _safe_print((PyObject *)dst_dtype);
printf("\n");
printf("has conversion fraction %lld/%lld\n", num, denom);
#endif
@@ -1089,9 +1104,9 @@ get_nbo_datetime_to_string_transfer_function(int aligned,
#if NPY_DT_DBG_TRACING
printf("Dtype transfer from ");
- PyObject_Print((PyObject *)src_dtype, stdout, 0);
+ _safe_print((PyObject *)src_dtype);
printf(" to ");
- PyObject_Print((PyObject *)dst_dtype, stdout, 0);
+ _safe_print((PyObject *)dst_dtype);
printf("\n");
#endif
@@ -1112,7 +1127,7 @@ get_datetime_to_unicode_transfer_function(int aligned,
/* Get an ASCII string data type, adapted to match the UNICODE one */
str_dtype = PyArray_DescrFromType(NPY_STRING);
- PyArray_AdaptFlexibleDType(NULL, dst_dtype, &str_dtype);
+ str_dtype = PyArray_AdaptFlexibleDType(NULL, dst_dtype, str_dtype);
if (str_dtype == NULL) {
return NPY_FAIL;
}
@@ -1211,9 +1226,9 @@ get_nbo_string_to_datetime_transfer_function(int aligned,
#if NPY_DT_DBG_TRACING
printf("Dtype transfer from ");
- PyObject_Print((PyObject *)src_dtype, stdout, 0);
+ _safe_print((PyObject *)src_dtype);
printf(" to ");
- PyObject_Print((PyObject *)dst_dtype, stdout, 0);
+ _safe_print((PyObject *)dst_dtype);
printf("\n");
#endif
@@ -1234,7 +1249,7 @@ get_unicode_to_datetime_transfer_function(int aligned,
/* Get an ASCII string data type, adapted to match the UNICODE one */
str_dtype = PyArray_DescrFromType(NPY_STRING);
- PyArray_AdaptFlexibleDType(NULL, src_dtype, &str_dtype);
+ str_dtype = PyArray_AdaptFlexibleDType(NULL, src_dtype, str_dtype);
if (str_dtype == NULL) {
return NPY_FAIL;
}
@@ -1557,12 +1572,30 @@ get_cast_transfer_function(int aligned,
src_dtype,
&tobuffer, &todata);
-
- /* Get the copy/swap operation to dst */
- PyArray_GetDTypeCopySwapFn(aligned,
- dst_itemsize, dst_stride,
- dst_dtype,
- &frombuffer, &fromdata);
+ if (!PyDataType_REFCHK(dst_dtype)) {
+ /* Copying from buffer is a simple copy/swap operation */
+ PyArray_GetDTypeCopySwapFn(aligned,
+ dst_itemsize, dst_stride,
+ dst_dtype,
+ &frombuffer, &fromdata);
+ }
+ else {
+ /*
+ * Since the buffer is initialized to NULL, need to move the
+ * references in order to DECREF the existing data.
+ */
+ /* Object types cannot be byte swapped */
+ assert(PyDataType_ISNOTSWAPPED(dst_dtype));
+ /* The loop already needs the python api if this is reached */
+ assert(*out_needs_api);
+
+ if (PyArray_GetDTypeTransferFunction(
+ aligned, dst_itemsize, dst_stride,
+ dst_dtype, dst_dtype, 1,
+ &frombuffer, &fromdata, out_needs_api) != NPY_SUCCEED) {
+ return NPY_FAIL;
+ }
+ }
if (frombuffer == NULL || tobuffer == NULL) {
NPY_AUXDATA_FREE(castdata);
@@ -1986,6 +2019,7 @@ typedef struct {
_subarray_broadcast_offsetrun offsetruns;
} _subarray_broadcast_data;
+
/* transfer data free function */
static void _subarray_broadcast_data_free(NpyAuxData *data)
{
@@ -2341,7 +2375,7 @@ get_subarray_transfer_function(int aligned,
/* Get the subarray shapes and sizes */
if (PyDataType_HASSUBARRAY(src_dtype)) {
- if (!(PyArray_IntpConverter(src_dtype->subarray->shape,
+ if (!(PyArray_IntpConverter(src_dtype->subarray->shape,
&src_shape))) {
PyErr_SetString(PyExc_ValueError,
"invalid subarray shape");
@@ -2351,7 +2385,7 @@ get_subarray_transfer_function(int aligned,
src_dtype = src_dtype->subarray->base;
}
if (PyDataType_HASSUBARRAY(dst_dtype)) {
- if (!(PyArray_IntpConverter(dst_dtype->subarray->shape,
+ if (!(PyArray_IntpConverter(dst_dtype->subarray->shape,
&dst_shape))) {
npy_free_cache_dim_obj(src_shape);
PyErr_SetString(PyExc_ValueError,
@@ -3303,7 +3337,7 @@ get_decsrcref_transfer_function(int aligned,
/* If there are subarrays, need to wrap it */
else if (PyDataType_HASSUBARRAY(src_dtype)) {
PyArray_Dims src_shape = {NULL, -1};
- npy_intp src_size = 1;
+ npy_intp src_size;
PyArray_StridedUnaryOp *stransfer;
NpyAuxData *data;
@@ -3421,9 +3455,13 @@ PyArray_GetDTypeTransferFunction(int aligned,
#if NPY_DT_DBG_TRACING
printf("Calculating dtype transfer from ");
- PyObject_Print((PyObject *)src_dtype, stdout, 0);
+ if (PyObject_Print((PyObject *)src_dtype, stdout, 0) < 0) {
+ return NPY_FAIL;
+ }
printf(" to ");
- PyObject_Print((PyObject *)dst_dtype, stdout, 0);
+ if (PyObject_Print((PyObject *)dst_dtype, stdout, 0) < 0) {
+ return NPY_FAIL;
+ }
printf("\n");
#endif
@@ -3747,11 +3785,15 @@ PyArray_CastRawArrays(npy_intp count,
return NPY_SUCCEED;
}
- /* Check data alignment */
- aligned = (((npy_intp)src | src_stride) &
- (src_dtype->alignment - 1)) == 0 &&
- (((npy_intp)dst | dst_stride) &
- (dst_dtype->alignment - 1)) == 0;
+ /* Check data alignment, both uint and true */
+ aligned = raw_array_is_aligned(1, &count, dst, &dst_stride,
+ npy_uint_alignment(dst_dtype->elsize)) &&
+ raw_array_is_aligned(1, &count, dst, &dst_stride,
+ dst_dtype->alignment) &&
+ raw_array_is_aligned(1, &count, src, &src_stride,
+ npy_uint_alignment(src_dtype->elsize)) &&
+ raw_array_is_aligned(1, &count, src, &src_stride,
+ src_dtype->alignment);
/* Get the function to do the casting */
if (PyArray_GetDTypeTransferFunction(aligned,
diff --git a/numpy/core/src/multiarray/einsum.c.src b/numpy/core/src/multiarray/einsum.c.src
index 1765982a0..e7bbc3d0b 100644
--- a/numpy/core/src/multiarray/einsum.c.src
+++ b/numpy/core/src/multiarray/einsum.c.src
@@ -1992,12 +1992,13 @@ parse_output_subscripts(char *subscripts, int length,
/*
- * When there's just one operand and no reduction, we
- * can return a view into op. This calculates the view
- * if possible.
+ * When there's just one operand and no reduction we can return a view
+ * into 'op'. This calculates the view and stores it in 'ret', if
+ * possible. Returns -1 on error, 0 otherwise. Note that a 0 return
+ * does not mean that a view was successfully created.
*/
static int
-get_single_op_view(PyArrayObject *op, int iop, char *labels,
+get_single_op_view(PyArrayObject *op, char *labels,
int ndim_output, char *output_labels,
PyArrayObject **ret)
{
@@ -2052,13 +2053,11 @@ get_single_op_view(PyArrayObject *op, int iop, char *labels,
}
/* Update the dimensions and strides of the output */
i = out_label - output_labels;
- if (new_dims[i] != 0 &&
- new_dims[i] != PyArray_DIM(op, idim)) {
+ if (new_dims[i] != 0 && new_dims[i] != PyArray_DIM(op, idim)) {
PyErr_Format(PyExc_ValueError,
- "dimensions in operand %d for collapsing "
+ "dimensions in single operand for collapsing "
"index '%c' don't match (%d != %d)",
- iop, label, (int)new_dims[i],
- (int)PyArray_DIM(op, idim));
+ label, (int)new_dims[i], (int)PyArray_DIM(op, idim));
return -1;
}
new_dims[i] = PyArray_DIM(op, idim);
@@ -2086,80 +2085,108 @@ get_single_op_view(PyArrayObject *op, int iop, char *labels,
return 0;
}
+
+/*
+ * The char type may be either signed or unsigned, we need it to be
+ * signed here.
+ */
+static int
+_any_labels_are_negative(signed char *labels, int ndim)
+{
+ int idim;
+
+ for (idim = 0; idim < ndim; ++idim) {
+ if (labels[idim] < 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Given the labels for an operand array, returns a view of the array
+ * with all repeated labels collapsed into a single dimension along
+ * the corresponding diagonal. The labels are also updated to match
+ * the dimensions of the new array. If no label is repeated, the
+ * original array is reference increased and returned unchanged.
+ */
static PyArrayObject *
get_combined_dims_view(PyArrayObject *op, int iop, char *labels)
{
npy_intp new_strides[NPY_MAXDIMS];
npy_intp new_dims[NPY_MAXDIMS];
- int idim, ndim, icombine, combineoffset;
+ int idim, icombine;
int icombinemap[NPY_MAXDIMS];
-
+ int ndim = PyArray_NDIM(op);
PyArrayObject *ret = NULL;
- ndim = PyArray_NDIM(op);
+ /* A fast path to avoid unnecessary calculations. */
+ if (!_any_labels_are_negative((signed char *)labels, ndim)) {
+ Py_INCREF(op);
- /* Initialize the dimensions and strides to zero */
- for (idim = 0; idim < ndim; ++idim) {
- new_dims[idim] = 0;
- new_strides[idim] = 0;
+ return op;
}
- /* Copy the dimensions and strides, except when collapsing */
+ /* Combine repeated labels. */
icombine = 0;
- for (idim = 0; idim < ndim; ++idim) {
+ for(idim = 0; idim < ndim; ++idim) {
/*
* The char type may be either signed or unsigned, we
* need it to be signed here.
*/
int label = (signed char)labels[idim];
- /* If this label says to merge axes, get the actual label */
- if (label < 0) {
- combineoffset = label;
- label = labels[idim+label];
- }
- else {
- combineoffset = 0;
- if (icombine != idim) {
- labels[icombine] = labels[idim];
- }
+ npy_intp dim = PyArray_DIM(op, idim);
+ npy_intp stride = PyArray_STRIDE(op, idim);
+
+ /* A label seen for the first time, add it to the op view. */
+ if (label >= 0) {
+ /*
+ * icombinemap maps dimensions in the original array to
+ * their position in the combined dimensions view.
+ */
icombinemap[idim] = icombine;
+ new_dims[icombine] = dim;
+ new_strides[icombine] = stride;
+ ++icombine;
}
- /* If the label is 0, it's an unlabeled broadcast dimension */
- if (label == 0) {
- new_dims[icombine] = PyArray_DIM(op, idim);
- new_strides[icombine] = PyArray_STRIDE(op, idim);
- }
+ /* A repeated label, find the original one and merge them. */
else {
- /* Update the combined axis dimensions and strides */
- int i = icombinemap[idim + combineoffset];
- if (combineoffset < 0 && new_dims[i] != 0 &&
- new_dims[i] != PyArray_DIM(op, idim)) {
+ int i = icombinemap[idim + label];
+
+ icombinemap[idim] = -1;
+ if (new_dims[i] != dim) {
+ char orig_label = labels[idim + label];
PyErr_Format(PyExc_ValueError,
- "dimensions in operand %d for collapsing "
- "index '%c' don't match (%d != %d)",
- iop, label, (int)new_dims[i],
- (int)PyArray_DIM(op, idim));
+ "dimensions in operand %d for collapsing "
+ "index '%c' don't match (%d != %d)",
+ iop, orig_label, (int)new_dims[i], (int)dim);
return NULL;
}
- new_dims[i] = PyArray_DIM(op, idim);
- new_strides[i] += PyArray_STRIDE(op, idim);
+ new_strides[i] += stride;
}
+ }
- /* If the label didn't say to combine axes, increment dest i */
- if (combineoffset == 0) {
- icombine++;
+ /* Overwrite labels to match the new operand view. */
+ for (idim = 0; idim < ndim; ++idim) {
+ int i = icombinemap[idim];
+
+ if (i >= 0) {
+ labels[i] = labels[idim];
}
}
- /* The compressed number of dimensions */
+ /* The number of dimensions of the combined view. */
ndim = icombine;
+ /* Create a view of the operand with the compressed dimensions. */
Py_INCREF(PyArray_DESCR(op));
ret = (PyArrayObject *)PyArray_NewFromDescrAndBase(
Py_TYPE(op), PyArray_DESCR(op),
ndim, new_dims, new_strides, PyArray_DATA(op),
PyArray_ISWRITEABLE(op) ? NPY_ARRAY_WRITEABLE : 0,
(PyObject *)op, (PyObject *)op);
+
return ret;
}
@@ -2620,6 +2647,24 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop,
return NULL;
}
+ /*
+ * If there's just one operand and no output parameter,
+ * first try remapping the axes to the output to return
+ * a view instead of a copy.
+ */
+ if (nop == 1 && out == NULL) {
+ ret = NULL;
+
+ if (get_single_op_view(op_in[0], op_labels[0], ndim_output,
+ output_labels, &ret) < 0) {
+ return NULL;
+ }
+
+ if (ret != NULL) {
+ return ret;
+ }
+ }
+
/* Set all the op references to NULL */
for (iop = 0; iop < nop; ++iop) {
op[iop] = NULL;
@@ -2631,53 +2676,10 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop,
*/
for (iop = 0; iop < nop; ++iop) {
char *labels = op_labels[iop];
- int combine, ndim;
-
- ndim = PyArray_NDIM(op_in[iop]);
- /*
- * If there's just one operand and no output parameter,
- * first try remapping the axes to the output to return
- * a view instead of a copy.
- */
- if (iop == 0 && nop == 1 && out == NULL) {
- ret = NULL;
-
- if (get_single_op_view(op_in[iop], iop, labels,
- ndim_output, output_labels,
- &ret) < 0) {
- return NULL;
- }
-
- if (ret != NULL) {
- return ret;
- }
- }
-
- /*
- * Check whether any dimensions need to be combined
- *
- * The char type may be either signed or unsigned, we
- * need it to be signed here.
- */
- combine = 0;
- for (idim = 0; idim < ndim; ++idim) {
- if ((signed char)labels[idim] < 0) {
- combine = 1;
- }
- }
-
- /* If any dimensions are combined, create a view which combines them */
- if (combine) {
- op[iop] = get_combined_dims_view(op_in[iop], iop, labels);
- if (op[iop] == NULL) {
- goto fail;
- }
- }
- /* No combining needed */
- else {
- Py_INCREF(op_in[iop]);
- op[iop] = op_in[iop];
+ op[iop] = get_combined_dims_view(op_in[iop], iop, labels);
+ if (op[iop] == NULL) {
+ goto fail;
}
}
diff --git a/numpy/core/src/multiarray/flagsobject.c b/numpy/core/src/multiarray/flagsobject.c
index 85ea49fb4..a66b9d40d 100644
--- a/numpy/core/src/multiarray/flagsobject.c
+++ b/numpy/core/src/multiarray/flagsobject.c
@@ -7,6 +7,7 @@
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
#include "numpy/arrayobject.h"
+#include "arrayobject.h"
#include "numpy/arrayscalars.h"
#include "npy_config.h"
@@ -147,7 +148,7 @@ _UpdateContiguousFlags(PyArrayObject *ap)
if (PyArray_STRIDES(ap)[i] != sd) {
is_c_contig = 0;
break;
- }
+ }
/* contiguous, if it got this far */
if (dim == 0) {
break;
@@ -201,21 +202,36 @@ arrayflags_dealloc(PyArrayFlagsObject *self)
static PyObject * \
arrayflags_ ## lower ## _get(PyArrayFlagsObject *self) \
{ \
- PyObject *item; \
- item = ((self->flags & (UPPER)) == (UPPER)) ? Py_True : Py_False; \
- Py_INCREF(item); \
- return item; \
+ return PyBool_FromLong((self->flags & (UPPER)) == (UPPER)); \
+ }
+
+static char *msg = "future versions will not create a writeable "
+ "array from broadcast_array. Set the writable flag explicitly to "
+ "avoid this warning.";
+
+#define _define_get_warn(UPPER, lower) \
+ static PyObject * \
+ arrayflags_ ## lower ## _get(PyArrayFlagsObject *self) \
+ { \
+ if (self->flags & NPY_ARRAY_WARN_ON_WRITE) { \
+ if (PyErr_Warn(PyExc_FutureWarning, msg) < 0) {\
+ return NULL; \
+ } \
+ }\
+ return PyBool_FromLong((self->flags & (UPPER)) == (UPPER)); \
}
+
_define_get(NPY_ARRAY_C_CONTIGUOUS, contiguous)
_define_get(NPY_ARRAY_F_CONTIGUOUS, fortran)
_define_get(NPY_ARRAY_WRITEBACKIFCOPY, writebackifcopy)
_define_get(NPY_ARRAY_OWNDATA, owndata)
_define_get(NPY_ARRAY_ALIGNED, aligned)
-_define_get(NPY_ARRAY_WRITEABLE, writeable)
-_define_get(NPY_ARRAY_ALIGNED|
+_define_get(NPY_ARRAY_WRITEABLE, writeable_no_warn)
+_define_get_warn(NPY_ARRAY_WRITEABLE, writeable)
+_define_get_warn(NPY_ARRAY_ALIGNED|
NPY_ARRAY_WRITEABLE, behaved)
-_define_get(NPY_ARRAY_ALIGNED|
+_define_get_warn(NPY_ARRAY_ALIGNED|
NPY_ARRAY_WRITEABLE|
NPY_ARRAY_C_CONTIGUOUS, carray)
@@ -398,6 +414,40 @@ arrayflags_writeable_set(PyArrayFlagsObject *self, PyObject *obj)
return 0;
}
+static int
+arrayflags_warn_on_write_set(PyArrayFlagsObject *self, PyObject *obj)
+{
+ /*
+ * This code should go away in a future release, so do not mangle the
+ * array_setflags function with an extra kwarg
+ */
+ int ret;
+ if (obj == NULL) {
+ PyErr_SetString(PyExc_AttributeError,
+ "Cannot delete flags _warn_on_write attribute");
+ return -1;
+ }
+ ret = PyObject_IsTrue(obj);
+ if (ret > 0) {
+ if (!(PyArray_FLAGS((PyArrayObject*)self->arr) & NPY_ARRAY_WRITEABLE)) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot set '_warn_on_write' flag when 'writable' is "
+ "False");
+ return -1;
+ }
+ PyArray_ENABLEFLAGS((PyArrayObject*)self->arr, NPY_ARRAY_WARN_ON_WRITE);
+ }
+ else if (ret < 0) {
+ return -1;
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot clear '_warn_on_write', set "
+ "writeable True to clear this private flag");
+ return -1;
+ }
+ return 0;
+}
static PyGetSetDef arrayflags_getsets[] = {
{"contiguous",
@@ -436,6 +486,14 @@ static PyGetSetDef arrayflags_getsets[] = {
(getter)arrayflags_writeable_get,
(setter)arrayflags_writeable_set,
NULL, NULL},
+ {"_writeable_no_warn",
+ (getter)arrayflags_writeable_no_warn_get,
+ (setter)NULL,
+ NULL, NULL},
+ {"_warn_on_write",
+ (getter)NULL,
+ (setter)arrayflags_warn_on_write_set,
+ NULL, NULL},
{"fnc",
(getter)arrayflags_fnc_get,
NULL,
@@ -623,7 +681,7 @@ arrayflags_setitem(PyArrayFlagsObject *self, PyObject *ind, PyObject *item)
((n==1) && (strncmp(key, "U", n) == 0))) {
return arrayflags_updateifcopy_set(self, item);
}
- else if (((n==14) && (strncmp(key, "WRITEBACKIFCOPY", n) == 0)) ||
+ else if (((n==15) && (strncmp(key, "WRITEBACKIFCOPY", n) == 0)) ||
((n==1) && (strncmp(key, "X", n) == 0))) {
return arrayflags_writebackifcopy_set(self, item);
}
@@ -648,19 +706,25 @@ static PyObject *
arrayflags_print(PyArrayFlagsObject *self)
{
int fl = self->flags;
+ const char *_warn_on_write = "";
+ if (fl & NPY_ARRAY_WARN_ON_WRITE) {
+ _warn_on_write = " (with WARN_ON_WRITE=True)";
+ }
return PyUString_FromFormat(
" %s : %s\n %s : %s\n"
+ " %s : %s\n %s : %s%s\n"
" %s : %s\n %s : %s\n"
- " %s : %s\n %s : %s\n"
- " %s : %s",
+ " %s : %s\n",
"C_CONTIGUOUS", _torf_(fl, NPY_ARRAY_C_CONTIGUOUS),
"F_CONTIGUOUS", _torf_(fl, NPY_ARRAY_F_CONTIGUOUS),
"OWNDATA", _torf_(fl, NPY_ARRAY_OWNDATA),
"WRITEABLE", _torf_(fl, NPY_ARRAY_WRITEABLE),
+ _warn_on_write,
"ALIGNED", _torf_(fl, NPY_ARRAY_ALIGNED),
"WRITEBACKIFCOPY", _torf_(fl, NPY_ARRAY_WRITEBACKIFCOPY),
- "UPDATEIFCOPY", _torf_(fl, NPY_ARRAY_UPDATEIFCOPY));
+ "UPDATEIFCOPY", _torf_(fl, NPY_ARRAY_UPDATEIFCOPY)
+ );
}
static int
diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c
index cae4273ff..116e37ce5 100644
--- a/numpy/core/src/multiarray/getset.c
+++ b/numpy/core/src/multiarray/getset.c
@@ -20,6 +20,7 @@
#include "arrayobject.h"
#include "mem_overlap.h"
#include "alloc.h"
+#include "buffer.h"
/******************* array attribute get and set routines ******************/
@@ -72,15 +73,17 @@ array_shape_set(PyArrayObject *self, PyObject *val)
((PyArrayObject_fields *)self)->nd = nd;
if (nd > 0) {
/* create new dimensions and strides */
- ((PyArrayObject_fields *)self)->dimensions = npy_alloc_cache_dim(3*nd);
+ ((PyArrayObject_fields *)self)->dimensions = npy_alloc_cache_dim(2 * nd);
if (PyArray_DIMS(self) == NULL) {
Py_DECREF(ret);
PyErr_SetString(PyExc_MemoryError,"");
return -1;
}
((PyArrayObject_fields *)self)->strides = PyArray_DIMS(self) + nd;
- memcpy(PyArray_DIMS(self), PyArray_DIMS(ret), nd*sizeof(npy_intp));
- memcpy(PyArray_STRIDES(self), PyArray_STRIDES(ret), nd*sizeof(npy_intp));
+ if (nd) {
+ memcpy(PyArray_DIMS(self), PyArray_DIMS(ret), nd*sizeof(npy_intp));
+ memcpy(PyArray_STRIDES(self), PyArray_STRIDES(ret), nd*sizeof(npy_intp));
+ }
}
else {
((PyArrayObject_fields *)self)->dimensions = NULL;
@@ -143,6 +146,7 @@ array_strides_set(PyArrayObject *self, PyObject *obj)
offset = PyArray_BYTES(self) - (char *)view.buf;
numbytes = view.len + offset;
PyBuffer_Release(&view);
+ _dealloc_cached_buffer_info((PyObject*)new);
}
#else
if (PyArray_BASE(new) &&
@@ -170,7 +174,9 @@ array_strides_set(PyArrayObject *self, PyObject *obj)
"compatible with available memory");
goto fail;
}
- memcpy(PyArray_STRIDES(self), newstrides.ptr, sizeof(npy_intp)*newstrides.len);
+ if (newstrides.len) {
+ memcpy(PyArray_STRIDES(self), newstrides.ptr, sizeof(npy_intp)*newstrides.len);
+ }
PyArray_UpdateFlags(self, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS |
NPY_ARRAY_ALIGNED);
npy_free_cache_dim_obj(newstrides);
@@ -184,14 +190,9 @@ array_strides_set(PyArrayObject *self, PyObject *obj)
static PyObject *
-array_priority_get(PyArrayObject *self)
+array_priority_get(PyArrayObject *NPY_UNUSED(self))
{
- if (PyArray_CheckExact(self)) {
- return PyFloat_FromDouble(NPY_PRIORITY);
- }
- else {
- return PyFloat_FromDouble(NPY_PRIORITY);
- }
+ return PyFloat_FromDouble(NPY_PRIORITY);
}
static PyObject *
@@ -376,6 +377,7 @@ array_data_set(PyArrayObject *self, PyObject *op)
* sticks around after the release.
*/
PyBuffer_Release(&view);
+ _dealloc_cached_buffer_info(op);
#else
if (PyObject_AsWriteBuffer(op, &buf, &buf_len) < 0) {
PyErr_Clear();
@@ -666,8 +668,10 @@ array_struct_get(PyArrayObject *self)
return PyErr_NoMemory();
}
inter->strides = inter->shape + PyArray_NDIM(self);
- memcpy(inter->shape, PyArray_DIMS(self), sizeof(npy_intp)*PyArray_NDIM(self));
- memcpy(inter->strides, PyArray_STRIDES(self), sizeof(npy_intp)*PyArray_NDIM(self));
+ if (PyArray_NDIM(self)) {
+ memcpy(inter->shape, PyArray_DIMS(self), sizeof(npy_intp)*PyArray_NDIM(self));
+ memcpy(inter->strides, PyArray_STRIDES(self), sizeof(npy_intp)*PyArray_NDIM(self));
+ }
}
else {
inter->shape = NULL;
diff --git a/numpy/core/src/multiarray/hashdescr.c b/numpy/core/src/multiarray/hashdescr.c
index 8465093b9..0b23b6c21 100644
--- a/numpy/core/src/multiarray/hashdescr.c
+++ b/numpy/core/src/multiarray/hashdescr.c
@@ -36,17 +36,17 @@ static int _array_descr_builtin(PyArray_Descr* descr, PyObject *l);
*/
static char _normalize_byteorder(char byteorder)
{
- switch(byteorder) {
- case '=':
- if (PyArray_GetEndianness() == NPY_CPU_BIG) {
- return '>';
- }
- else {
- return '<';
- }
- default:
- return byteorder;
- }
+ switch(byteorder) {
+ case '=':
+ if (PyArray_GetEndianness() == NPY_CPU_BIG) {
+ return '>';
+ }
+ else {
+ return '<';
+ }
+ default:
+ return byteorder;
+ }
}
/*
diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c
index de54ca1b3..a6ac902d3 100644
--- a/numpy/core/src/multiarray/item_selection.c
+++ b/numpy/core/src/multiarray/item_selection.c
@@ -45,7 +45,7 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis,
indices = NULL;
self = (PyArrayObject *)PyArray_CheckAxis(self0, &axis,
- NPY_ARRAY_CARRAY);
+ NPY_ARRAY_CARRAY_RO);
if (self == NULL) {
return NULL;
}
@@ -98,6 +98,10 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis,
goto fail;
}
+ if (arrays_overlap(out, self)) {
+ flags |= NPY_ARRAY_ENSURECOPY;
+ }
+
if (clipmode == NPY_RAISE) {
/*
* we need to make sure and get a copy
@@ -261,6 +265,7 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
npy_intp i, chunk, ni, max_item, nv, tmp;
char *src, *dest;
int copied = 0;
+ int overlap = 0;
indices = NULL;
values = NULL;
@@ -274,24 +279,6 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
return NULL;
}
- if (!PyArray_ISCONTIGUOUS(self)) {
- PyArrayObject *obj;
- int flags = NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY;
-
- if (clipmode == NPY_RAISE) {
- flags |= NPY_ARRAY_ENSURECOPY;
- }
- Py_INCREF(PyArray_DESCR(self));
- obj = (PyArrayObject *)PyArray_FromArray(self,
- PyArray_DESCR(self), flags);
- if (obj != self) {
- copied = 1;
- }
- self = obj;
- }
- max_item = PyArray_SIZE(self);
- dest = PyArray_DATA(self);
- chunk = PyArray_DESCR(self)->elsize;
indices = (PyArrayObject *)PyArray_ContiguousFromAny(indices0,
NPY_INTP, 0, 0);
if (indices == NULL) {
@@ -308,6 +295,25 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
if (nv <= 0) {
goto finish;
}
+
+ overlap = arrays_overlap(self, values) || arrays_overlap(self, indices);
+ if (overlap || !PyArray_ISCONTIGUOUS(self)) {
+ PyArrayObject *obj;
+ int flags = NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY |
+ NPY_ARRAY_ENSURECOPY;
+
+ Py_INCREF(PyArray_DESCR(self));
+ obj = (PyArrayObject *)PyArray_FromArray(self,
+ PyArray_DESCR(self), flags);
+ if (obj != self) {
+ copied = 1;
+ }
+ self = obj;
+ }
+ max_item = PyArray_SIZE(self);
+ dest = PyArray_DATA(self);
+ chunk = PyArray_DESCR(self)->elsize;
+
if (PyDataType_REFCHK(PyArray_DESCR(self))) {
switch(clipmode) {
case NPY_RAISE:
@@ -434,10 +440,11 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0)
PyArray_FastPutmaskFunc *func;
PyArrayObject *mask, *values;
PyArray_Descr *dtype;
- npy_intp i, j, chunk, ni, max_item, nv;
+ npy_intp i, j, chunk, ni, nv;
char *src, *dest;
npy_bool *mask_data;
int copied = 0;
+ int overlap = 0;
mask = NULL;
values = NULL;
@@ -447,29 +454,14 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0)
"be an array");
return NULL;
}
- if (!PyArray_ISCONTIGUOUS(self)) {
- PyArrayObject *obj;
- dtype = PyArray_DESCR(self);
- Py_INCREF(dtype);
- obj = (PyArrayObject *)PyArray_FromArray(self, dtype,
- NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY);
- if (obj != self) {
- copied = 1;
- }
- self = obj;
- }
-
- max_item = PyArray_SIZE(self);
- dest = PyArray_DATA(self);
- chunk = PyArray_DESCR(self)->elsize;
mask = (PyArrayObject *)PyArray_FROM_OTF(mask0, NPY_BOOL,
NPY_ARRAY_CARRAY | NPY_ARRAY_FORCECAST);
if (mask == NULL) {
goto fail;
}
ni = PyArray_SIZE(mask);
- if (ni != max_item) {
+ if (ni != PyArray_SIZE(self)) {
PyErr_SetString(PyExc_ValueError,
"putmask: mask and data must be "
"the same size");
@@ -491,6 +483,27 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0)
}
src = PyArray_DATA(values);
+ overlap = arrays_overlap(self, values) || arrays_overlap(self, mask);
+ if (overlap || !PyArray_ISCONTIGUOUS(self)) {
+ int flags = NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY;
+ PyArrayObject *obj;
+
+ if (overlap) {
+ flags |= NPY_ARRAY_ENSURECOPY;
+ }
+
+ dtype = PyArray_DESCR(self);
+ Py_INCREF(dtype);
+ obj = (PyArrayObject *)PyArray_FromArray(self, dtype, flags);
+ if (obj != self) {
+ copied = 1;
+ }
+ self = obj;
+ }
+
+ chunk = PyArray_DESCR(self)->elsize;
+ dest = PyArray_DATA(self);
+
if (PyDataType_REFCHK(PyArray_DESCR(self))) {
for (i = 0, j = 0; i < ni; i++, j++) {
if (j >= nv) {
@@ -594,7 +607,8 @@ PyArray_Repeat(PyArrayObject *aop, PyObject *op, int axis)
else {
for (j = 0; j < n; j++) {
if (counts[j] < 0) {
- PyErr_SetString(PyExc_ValueError, "count < 0");
+ PyErr_SetString(PyExc_ValueError,
+ "repeats may not contain negative values.");
goto fail;
}
total += counts[j];
@@ -712,6 +726,13 @@ PyArray_Choose(PyArrayObject *ip, PyObject *op, PyArrayObject *out,
"choose: invalid shape for output array.");
goto fail;
}
+
+ for (i = 0; i < n; i++) {
+ if (arrays_overlap(out, mps[i])) {
+ flags |= NPY_ARRAY_ENSURECOPY;
+ }
+ }
+
if (clipmode == NPY_RAISE) {
/*
* we need to make sure and get a copy
@@ -1105,7 +1126,7 @@ fail:
NPY_NO_EXPORT int
PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which)
{
- PyArray_SortFunc *sort;
+ PyArray_SortFunc *sort = NULL;
int n = PyArray_NDIM(op);
if (check_and_adjust_axis(&axis, n) < 0) {
@@ -1122,6 +1143,7 @@ PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which)
}
sort = PyArray_DESCR(op)->f->sort[which];
+
if (sort == NULL) {
if (PyArray_DESCR(op)->f->compare) {
switch (which) {
@@ -1132,8 +1154,8 @@ PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which)
case NPY_HEAPSORT:
sort = npy_heapsort;
break;
- case NPY_MERGESORT:
- sort = npy_mergesort;
+ case NPY_STABLESORT:
+ sort = npy_timsort;
break;
}
}
@@ -1263,16 +1285,11 @@ NPY_NO_EXPORT PyObject *
PyArray_ArgSort(PyArrayObject *op, int axis, NPY_SORTKIND which)
{
PyArrayObject *op2;
- PyArray_ArgSortFunc *argsort;
+ PyArray_ArgSortFunc *argsort = NULL;
PyObject *ret;
- if (which < 0 || which >= NPY_NSORTS) {
- PyErr_SetString(PyExc_ValueError,
- "not a valid sort kind");
- return NULL;
- }
-
argsort = PyArray_DESCR(op)->f->argsort[which];
+
if (argsort == NULL) {
if (PyArray_DESCR(op)->f->compare) {
switch (which) {
@@ -1283,8 +1300,8 @@ PyArray_ArgSort(PyArrayObject *op, int axis, NPY_SORTKIND which)
case NPY_HEAPSORT:
argsort = npy_aheapsort;
break;
- case NPY_MERGESORT:
- argsort = npy_amergesort;
+ case NPY_STABLESORT:
+ argsort = npy_atimsort;
break;
}
}
@@ -1319,7 +1336,11 @@ PyArray_ArgPartition(PyArrayObject *op, PyArrayObject *ktharray, int axis,
PyArray_ArgSortFunc *argsort;
PyObject *ret;
- if (which < 0 || which >= NPY_NSELECTS) {
+ /*
+ * As a C-exported function, enum NPY_SELECTKIND loses its enum property
+ * Check the values to make sure they are in range
+ */
+ if ((int)which < 0 || (int)which >= NPY_NSELECTS) {
PyErr_SetString(PyExc_ValueError,
"not a valid partition kind");
return NULL;
@@ -1425,7 +1446,7 @@ PyArray_LexSort(PyObject *sort_keys, int axis)
goto fail;
}
}
- if (!PyArray_DESCR(mps[i])->f->argsort[NPY_MERGESORT]
+ if (!PyArray_DESCR(mps[i])->f->argsort[NPY_STABLESORT]
&& !PyArray_DESCR(mps[i])->f->compare) {
PyErr_Format(PyExc_TypeError,
"item %zd type does not have compare function", i);
@@ -1439,8 +1460,8 @@ PyArray_LexSort(PyObject *sort_keys, int axis)
/* Now we can check the axis */
nd = PyArray_NDIM(mps[0]);
- if ((nd == 0) || (PyArray_SIZE(mps[0]) == 1)) {
- /* single element case */
+ if ((nd == 0) || (PyArray_SIZE(mps[0]) <= 1)) {
+ /* empty/single element case */
ret = (PyArrayObject *)PyArray_NewFromDescr(
&PyArray_Type, PyArray_DescrFromType(NPY_INTP),
PyArray_NDIM(mps[0]), PyArray_DIMS(mps[0]), NULL, NULL,
@@ -1449,7 +1470,9 @@ PyArray_LexSort(PyObject *sort_keys, int axis)
if (ret == NULL) {
goto fail;
}
- *((npy_intp *)(PyArray_DATA(ret))) = 0;
+ if (PyArray_SIZE(mps[0]) > 0) {
+ *((npy_intp *)(PyArray_DATA(ret))) = 0;
+ }
goto finish;
}
if (check_and_adjust_axis(&axis, nd) < 0) {
@@ -1499,16 +1522,28 @@ PyArray_LexSort(PyObject *sort_keys, int axis)
char *valbuffer, *indbuffer;
int *swaps;
- valbuffer = PyDataMem_NEW(N * maxelsize);
+ assert(N > 0); /* Guaranteed and assumed by indbuffer */
+ npy_intp valbufsize = N * maxelsize;
+ if (NPY_UNLIKELY(valbufsize) == 0) {
+ valbufsize = 1; /* Ensure allocation is not empty */
+ }
+
+ valbuffer = PyDataMem_NEW(valbufsize);
if (valbuffer == NULL) {
goto fail;
}
indbuffer = PyDataMem_NEW(N * sizeof(npy_intp));
if (indbuffer == NULL) {
+ PyDataMem_FREE(valbuffer);
+ goto fail;
+ }
+ swaps = malloc(NPY_LIKELY(n > 0) ? n * sizeof(int) : 1);
+ if (swaps == NULL) {
+ PyDataMem_FREE(valbuffer);
PyDataMem_FREE(indbuffer);
goto fail;
}
- swaps = malloc(n*sizeof(int));
+
for (j = 0; j < n; j++) {
swaps[j] = PyArray_ISBYTESWAPPED(mps[j]);
}
@@ -1521,9 +1556,9 @@ PyArray_LexSort(PyObject *sort_keys, int axis)
int rcode;
elsize = PyArray_DESCR(mps[j])->elsize;
astride = PyArray_STRIDES(mps[j])[axis];
- argsort = PyArray_DESCR(mps[j])->f->argsort[NPY_MERGESORT];
+ argsort = PyArray_DESCR(mps[j])->f->argsort[NPY_STABLESORT];
if(argsort == NULL) {
- argsort = npy_amergesort;
+ argsort = npy_atimsort;
}
_unaligned_strided_byte_copy(valbuffer, (npy_intp) elsize,
its[j]->dataptr, astride, N, elsize);
@@ -1537,8 +1572,8 @@ PyArray_LexSort(PyObject *sort_keys, int axis)
#else
if (rcode < 0) {
#endif
- npy_free_cache(valbuffer, N * maxelsize);
- npy_free_cache(indbuffer, N * sizeof(npy_intp));
+ PyDataMem_FREE(valbuffer);
+ PyDataMem_FREE(indbuffer);
free(swaps);
goto fail;
}
@@ -1560,9 +1595,9 @@ PyArray_LexSort(PyObject *sort_keys, int axis)
}
for (j = 0; j < n; j++) {
int rcode;
- argsort = PyArray_DESCR(mps[j])->f->argsort[NPY_MERGESORT];
+ argsort = PyArray_DESCR(mps[j])->f->argsort[NPY_STABLESORT];
if(argsort == NULL) {
- argsort = npy_amergesort;
+ argsort = npy_atimsort;
}
rcode = argsort(its[j]->dataptr,
(npy_intp *)rit->dataptr, N, mps[j]);
@@ -2183,14 +2218,59 @@ PyArray_Nonzero(PyArrayObject *self)
PyArrayObject *ret = NULL;
PyObject *ret_tuple;
npy_intp ret_dims[2];
- PyArray_NonzeroFunc *nonzero = PyArray_DESCR(self)->f->nonzero;
+
+ PyArray_NonzeroFunc *nonzero;
+ PyArray_Descr *dtype;
+
npy_intp nonzero_count;
+ npy_intp added_count = 0;
+ int needs_api;
+ int is_bool;
NpyIter *iter;
NpyIter_IterNextFunc *iternext;
NpyIter_GetMultiIndexFunc *get_multi_index;
char **dataptr;
- int is_empty = 0;
+
+ dtype = PyArray_DESCR(self);
+ nonzero = dtype->f->nonzero;
+ needs_api = PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI);
+
+ /* Special case - nonzero(zero_d) is nonzero(atleast_1d(zero_d)) */
+ if (ndim == 0) {
+ char const* msg;
+ if (PyArray_ISBOOL(self)) {
+ msg =
+ "Calling nonzero on 0d arrays is deprecated, as it behaves "
+ "surprisingly. Use `atleast_1d(cond).nonzero()` if the old "
+ "behavior was intended. If the context of this warning is of "
+ "the form `arr[nonzero(cond)]`, just use `arr[cond]`.";
+ }
+ else {
+ msg =
+ "Calling nonzero on 0d arrays is deprecated, as it behaves "
+ "surprisingly. Use `atleast_1d(arr).nonzero()` if the old "
+ "behavior was intended.";
+ }
+ if (DEPRECATE(msg) < 0) {
+ return NULL;
+ }
+
+ static npy_intp const zero_dim_shape[1] = {1};
+ static npy_intp const zero_dim_strides[1] = {0};
+
+ Py_INCREF(PyArray_DESCR(self)); /* array creation steals reference */
+ PyArrayObject *self_1d = (PyArrayObject *)PyArray_NewFromDescrAndBase(
+ Py_TYPE(self), PyArray_DESCR(self),
+ 1, zero_dim_shape, zero_dim_strides, PyArray_BYTES(self),
+ PyArray_FLAGS(self), (PyObject *)self, (PyObject *)self);
+ if (self_1d == NULL) {
+ return NULL;
+ }
+ ret_tuple = PyArray_Nonzero(self_1d);
+ Py_DECREF(self_1d);
+ return ret_tuple;
+ }
/*
* First count the number of non-zeros in 'self'.
@@ -2200,9 +2280,11 @@ PyArray_Nonzero(PyArrayObject *self)
return NULL;
}
+ is_bool = PyArray_ISBOOL(self);
+
/* Allocate the result as a 2D array */
ret_dims[0] = nonzero_count;
- ret_dims[1] = (ndim == 0) ? 1 : ndim;
+ ret_dims[1] = ndim;
ret = (PyArrayObject *)PyArray_NewFromDescr(
&PyArray_Type, PyArray_DescrFromType(NPY_INTP),
2, ret_dims, NULL, NULL,
@@ -2212,11 +2294,11 @@ PyArray_Nonzero(PyArrayObject *self)
}
/* If it's a one-dimensional result, don't use an iterator */
- if (ndim <= 1) {
+ if (ndim == 1) {
npy_intp * multi_index = (npy_intp *)PyArray_DATA(ret);
char * data = PyArray_BYTES(self);
- npy_intp stride = (ndim == 0) ? 0 : PyArray_STRIDE(self, 0);
- npy_intp count = (ndim == 0) ? 1 : PyArray_DIM(self, 0);
+ npy_intp stride = PyArray_STRIDE(self, 0);
+ npy_intp count = PyArray_DIM(self, 0);
NPY_BEGIN_THREADS_DEF;
/* nothing to do */
@@ -2224,10 +2306,12 @@ PyArray_Nonzero(PyArrayObject *self)
goto finish;
}
- NPY_BEGIN_THREADS_THRESHOLDED(count);
+ if (!needs_api) {
+ NPY_BEGIN_THREADS_THRESHOLDED(count);
+ }
/* avoid function call for bool */
- if (PyArray_ISBOOL(self)) {
+ if (is_bool) {
/*
* use fast memchr variant for sparse data, see gh-4370
* the fast bool count is followed by this sparse path is faster
@@ -2260,8 +2344,14 @@ PyArray_Nonzero(PyArrayObject *self)
npy_intp j;
for (j = 0; j < count; ++j) {
if (nonzero(data, self)) {
+ if (++added_count > nonzero_count) {
+ break;
+ }
*multi_index++ = j;
}
+ if (needs_api && PyErr_Occurred()) {
+ break;
+ }
data += stride;
}
}
@@ -2302,6 +2392,8 @@ PyArray_Nonzero(PyArrayObject *self)
Py_DECREF(ret);
return NULL;
}
+
+ needs_api = NpyIter_IterationNeedsAPI(iter);
NPY_BEGIN_THREADS_NDITER(iter);
@@ -2310,7 +2402,7 @@ PyArray_Nonzero(PyArrayObject *self)
multi_index = (npy_intp *)PyArray_DATA(ret);
/* Get the multi-index for each non-zero element */
- if (PyArray_ISBOOL(self)) {
+ if (is_bool) {
/* avoid function call for bool */
do {
if (**dataptr != 0) {
@@ -2322,9 +2414,15 @@ PyArray_Nonzero(PyArrayObject *self)
else {
do {
if (nonzero(*dataptr, self)) {
+ if (++added_count > nonzero_count) {
+ break;
+ }
get_multi_index(iter, multi_index);
multi_index += ndim;
}
+ if (needs_api && PyErr_Occurred()) {
+ break;
+ }
} while(iternext(iter));
}
@@ -2334,9 +2432,18 @@ PyArray_Nonzero(PyArrayObject *self)
NpyIter_Deallocate(iter);
finish:
- /* Treat zero-dimensional as shape (1,) */
- if (ndim == 0) {
- ndim = 1;
+ if (PyErr_Occurred()) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ /* if executed `nonzero()` check for miscount due to side-effect */
+ if (!is_bool && added_count != nonzero_count) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "number of non-zero array elements "
+ "changed during function execution.");
+ Py_DECREF(ret);
+ return NULL;
}
ret_tuple = PyTuple_New(ndim);
@@ -2345,18 +2452,11 @@ finish:
return NULL;
}
- for (i = 0; i < PyArray_NDIM(ret); ++i) {
- if (PyArray_DIMS(ret)[i] == 0) {
- is_empty = 1;
- break;
- }
- }
-
/* Create views into ret, one for each dimension */
for (i = 0; i < ndim; ++i) {
npy_intp stride = ndim * NPY_SIZEOF_INTP;
/* the result is an empty array, the view must point to valid memory */
- npy_intp data_offset = is_empty ? 0 : i * NPY_SIZEOF_INTP;
+ npy_intp data_offset = nonzero_count == 0 ? 0 : i * NPY_SIZEOF_INTP;
PyArrayObject *view = (PyArrayObject *)PyArray_NewFromDescrAndBase(
Py_TYPE(ret), PyArray_DescrFromType(NPY_INTP),
@@ -2379,7 +2479,7 @@ finish:
* array of values, which must be of length PyArray_NDIM(self).
*/
NPY_NO_EXPORT PyObject *
-PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index)
+PyArray_MultiIndexGetItem(PyArrayObject *self, const npy_intp *multi_index)
{
int idim, ndim = PyArray_NDIM(self);
char *data = PyArray_DATA(self);
@@ -2392,7 +2492,7 @@ PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index)
npy_intp ind = multi_index[idim];
if (check_and_adjust_index(&ind, shapevalue, idim, NULL) < 0) {
- return NULL;
+ return NULL;
}
data += ind * strides[idim];
}
@@ -2407,7 +2507,7 @@ PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index)
* Returns 0 on success, -1 on failure.
*/
NPY_NO_EXPORT int
-PyArray_MultiIndexSetItem(PyArrayObject *self, npy_intp *multi_index,
+PyArray_MultiIndexSetItem(PyArrayObject *self, const npy_intp *multi_index,
PyObject *obj)
{
int idim, ndim = PyArray_NDIM(self);
diff --git a/numpy/core/src/multiarray/item_selection.h b/numpy/core/src/multiarray/item_selection.h
index 90bb5100d..2276b4db7 100644
--- a/numpy/core/src/multiarray/item_selection.h
+++ b/numpy/core/src/multiarray/item_selection.h
@@ -15,7 +15,7 @@ count_boolean_trues(int ndim, char *data, npy_intp *ashape, npy_intp *astrides);
* array of values, which must be of length PyArray_NDIM(self).
*/
NPY_NO_EXPORT PyObject *
-PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index);
+PyArray_MultiIndexGetItem(PyArrayObject *self, const npy_intp *multi_index);
/*
* Sets a single item in the array, based on a single multi-index
@@ -24,7 +24,7 @@ PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index);
* Returns 0 on success, -1 on failure.
*/
NPY_NO_EXPORT int
-PyArray_MultiIndexSetItem(PyArrayObject *self, npy_intp *multi_index,
+PyArray_MultiIndexSetItem(PyArrayObject *self, const npy_intp *multi_index,
PyObject *obj);
#endif
diff --git a/numpy/core/src/multiarray/iterators.c b/numpy/core/src/multiarray/iterators.c
index 3e3248f53..e66bb36aa 100644
--- a/numpy/core/src/multiarray/iterators.c
+++ b/numpy/core/src/multiarray/iterators.c
@@ -92,121 +92,13 @@ parse_index_entry(PyObject *op, npy_intp *step_size,
}
-/*
- * Parses an index that has no fancy indexing. Populates
- * out_dimensions, out_strides, and out_offset.
- */
-NPY_NO_EXPORT int
-parse_index(PyArrayObject *self, PyObject *op,
- npy_intp *out_dimensions,
- npy_intp *out_strides,
- npy_intp *out_offset,
- int check_index)
-{
- int i, j, n;
- int nd_old, nd_new, n_add, n_ellipsis;
- npy_intp n_steps, start, offset, step_size;
- PyObject *op1 = NULL;
- int is_slice;
-
- if (PySlice_Check(op) || op == Py_Ellipsis || op == Py_None) {
- n = 1;
- op1 = op;
- Py_INCREF(op);
- /* this relies on the fact that n==1 for loop below */
- is_slice = 1;
- }
- else {
- if (!PySequence_Check(op)) {
- PyErr_SetString(PyExc_IndexError,
- "index must be either an int "
- "or a sequence");
- return -1;
- }
- n = PySequence_Length(op);
- is_slice = 0;
- }
-
- nd_old = nd_new = 0;
-
- offset = 0;
- for (i = 0; i < n; i++) {
- if (!is_slice) {
- op1 = PySequence_GetItem(op, i);
- if (op1 == NULL) {
- return -1;
- }
- }
- start = parse_index_entry(op1, &step_size, &n_steps,
- nd_old < PyArray_NDIM(self) ?
- PyArray_DIMS(self)[nd_old] : 0,
- nd_old, check_index ?
- nd_old < PyArray_NDIM(self) : 0);
- Py_DECREF(op1);
- if (start == -1) {
- break;
- }
- if (n_steps == NEWAXIS_INDEX) {
- out_dimensions[nd_new] = 1;
- out_strides[nd_new] = 0;
- nd_new++;
- }
- else if (n_steps == ELLIPSIS_INDEX) {
- for (j = i + 1, n_ellipsis = 0; j < n; j++) {
- op1 = PySequence_GetItem(op, j);
- if (op1 == Py_None) {
- n_ellipsis++;
- }
- Py_DECREF(op1);
- }
- n_add = PyArray_NDIM(self)-(n-i-n_ellipsis-1+nd_old);
- if (n_add < 0) {
- PyErr_SetString(PyExc_IndexError, "too many indices");
- return -1;
- }
- for (j = 0; j < n_add; j++) {
- out_dimensions[nd_new] = PyArray_DIMS(self)[nd_old];
- out_strides[nd_new] = PyArray_STRIDES(self)[nd_old];
- nd_new++; nd_old++;
- }
- }
- else {
- if (nd_old >= PyArray_NDIM(self)) {
- PyErr_SetString(PyExc_IndexError, "too many indices");
- return -1;
- }
- offset += PyArray_STRIDES(self)[nd_old]*start;
- nd_old++;
- if (n_steps != SINGLE_INDEX) {
- out_dimensions[nd_new] = n_steps;
- out_strides[nd_new] = step_size *
- PyArray_STRIDES(self)[nd_old-1];
- nd_new++;
- }
- }
- }
- if (i < n) {
- return -1;
- }
- n_add = PyArray_NDIM(self)-nd_old;
- for (j = 0; j < n_add; j++) {
- out_dimensions[nd_new] = PyArray_DIMS(self)[nd_old];
- out_strides[nd_new] = PyArray_STRIDES(self)[nd_old];
- nd_new++;
- nd_old++;
- }
- *out_offset = offset;
- return nd_new;
-}
-
-
/*********************** Element-wise Array Iterator ***********************/
/* Aided by Peter J. Verveer's nd_image package and numpy's arraymap ****/
/* and Python's array iterator ***/
/* get the dataptr from its current coordinates for simple iterator */
static char*
-get_ptr_simple(PyArrayIterObject* iter, npy_intp *coordinates)
+get_ptr_simple(PyArrayIterObject* iter, const npy_intp *coordinates)
{
npy_intp i;
char *ret;
@@ -224,10 +116,12 @@ get_ptr_simple(PyArrayIterObject* iter, npy_intp *coordinates)
* This is common initialization code between PyArrayIterObject and
* PyArrayNeighborhoodIterObject
*
- * Increase ao refcount
+ * Steals a reference to the array object which gets removed at deallocation,
+ * if the iterator is allocated statically and its dealloc not called, it
+ * can be thought of as borrowing the reference.
*/
-static PyObject *
-array_iter_base_init(PyArrayIterObject *it, PyArrayObject *ao)
+NPY_NO_EXPORT void
+PyArray_RawIterBaseInit(PyArrayIterObject *it, PyArrayObject *ao)
{
int nd, i;
@@ -239,7 +133,6 @@ array_iter_base_init(PyArrayIterObject *it, PyArrayObject *ao)
else {
it->contiguous = 0;
}
- Py_INCREF(ao);
it->ao = ao;
it->size = PyArray_SIZE(ao);
it->nd_m1 = nd - 1;
@@ -263,7 +156,7 @@ array_iter_base_init(PyArrayIterObject *it, PyArrayObject *ao)
it->translate = &get_ptr_simple;
PyArray_ITER_RESET(it);
- return (PyObject *)it;
+ return;
}
static void
@@ -278,6 +171,10 @@ array_iter_base_dealloc(PyArrayIterObject *it)
NPY_NO_EXPORT PyObject *
PyArray_IterNew(PyObject *obj)
{
+ /*
+ * Note that internall PyArray_RawIterBaseInit may be called directly on a
+ * statically allocated PyArrayIterObject.
+ */
PyArrayIterObject *it;
PyArrayObject *ao;
@@ -294,7 +191,8 @@ PyArray_IterNew(PyObject *obj)
return NULL;
}
- array_iter_base_init(it, ao);
+ Py_INCREF(ao); /* PyArray_RawIterBaseInit steals a reference */
+ PyArray_RawIterBaseInit(it, ao);
return (PyObject *)it;
}
@@ -498,6 +396,10 @@ arrayiter_next(PyArrayIterObject *it)
static void
arrayiter_dealloc(PyArrayIterObject *it)
{
+ /*
+ * Note that it is possible to statically allocate a PyArrayIterObject,
+ * which does not call this function.
+ */
array_iter_base_dealloc(it);
PyArray_free(it);
}
@@ -647,6 +549,7 @@ iter_subscript(PyArrayIterObject *self, PyObject *ind)
char *dptr;
int size;
PyObject *obj = NULL;
+ PyObject *new;
PyArray_CopySwapFunc *copyswap;
if (ind == Py_Ellipsis) {
@@ -748,36 +651,36 @@ iter_subscript(PyArrayIterObject *self, PyObject *ind)
obj = ind;
}
- if (PyArray_Check(obj)) {
- /* Check for Boolean object */
- if (PyArray_TYPE((PyArrayObject *)obj) == NPY_BOOL) {
- ret = iter_subscript_Bool(self, (PyArrayObject *)obj);
- Py_DECREF(indtype);
- }
- /* Check for integer array */
- else if (PyArray_ISINTEGER((PyArrayObject *)obj)) {
- PyObject *new;
- new = PyArray_FromAny(obj, indtype, 0, 0,
- NPY_ARRAY_FORCECAST | NPY_ARRAY_ALIGNED, NULL);
- if (new == NULL) {
- goto fail;
- }
- Py_DECREF(obj);
- obj = new;
- new = iter_subscript_int(self, (PyArrayObject *)obj);
- Py_DECREF(obj);
- return new;
- }
- else {
- goto fail;
- }
+ /* Any remaining valid input is an array or has been turned into one */
+ if (!PyArray_Check(obj)) {
+ goto fail;
+ }
+
+ /* Check for Boolean array */
+ if (PyArray_TYPE((PyArrayObject *)obj) == NPY_BOOL) {
+ ret = iter_subscript_Bool(self, (PyArrayObject *)obj);
+ Py_DECREF(indtype);
Py_DECREF(obj);
return (PyObject *)ret;
}
- else {
- Py_DECREF(indtype);
+
+ /* Only integer arrays left */
+ if (!PyArray_ISINTEGER((PyArrayObject *)obj)) {
+ goto fail;
}
+ Py_INCREF(indtype);
+ new = PyArray_FromAny(obj, indtype, 0, 0,
+ NPY_ARRAY_FORCECAST | NPY_ARRAY_ALIGNED, NULL);
+ if (new == NULL) {
+ goto fail;
+ }
+ Py_DECREF(indtype);
+ Py_DECREF(obj);
+ ret = (PyArrayObject *)iter_subscript_int(self, (PyArrayObject *)new);
+ Py_DECREF(new);
+ return (PyObject *)ret;
+
fail:
if (!PyErr_Occurred()) {
@@ -937,7 +840,6 @@ iter_ass_subscript(PyArrayIterObject *self, PyObject *ind, PyObject *val)
if (check_and_adjust_index(&start, self->size, -1, NULL) < 0) {
goto finish;
}
- retval = 0;
PyArray_ITER_GOTO1D(self, start);
retval = type->f->setitem(val, self->dataptr, self->ao);
PyArray_ITER_RESET(self);
@@ -1349,232 +1251,169 @@ PyArray_Broadcast(PyArrayMultiIterObject *mit)
return 0;
}
-/*NUMPY_API
- * Get MultiIterator from array of Python objects and any additional
- *
- * PyObject **mps -- array of PyObjects
- * int n - number of PyObjects in the array
- * int nadd - number of additional arrays to include in the iterator.
- *
- * Returns a multi-iterator object.
+static NPY_INLINE PyObject*
+multiiter_wrong_number_of_args(void)
+{
+ return PyErr_Format(PyExc_ValueError,
+ "Need at least 0 and at most %d "
+ "array objects.", NPY_MAXARGS);
+}
+
+/*
+ * Common implementation for all PyArrayMultiIterObject constructors.
*/
-NPY_NO_EXPORT PyObject *
-PyArray_MultiIterFromObjects(PyObject **mps, int n, int nadd, ...)
+static PyObject*
+multiiter_new_impl(int n_args, PyObject **args)
{
- va_list va;
PyArrayMultiIterObject *multi;
- PyObject *current;
- PyObject *arr;
-
- int i, ntot, err=0;
+ int i;
- ntot = n + nadd;
- if (ntot < 1 || ntot > NPY_MAXARGS) {
- PyErr_Format(PyExc_ValueError,
- "Need at least 1 and at most %d "
- "array objects.", NPY_MAXARGS);
- return NULL;
- }
multi = PyArray_malloc(sizeof(PyArrayMultiIterObject));
if (multi == NULL) {
return PyErr_NoMemory();
}
PyObject_Init((PyObject *)multi, &PyArrayMultiIter_Type);
+ multi->numiter = 0;
- for (i = 0; i < ntot; i++) {
- multi->iters[i] = NULL;
- }
- multi->numiter = ntot;
- multi->index = 0;
+ for (i = 0; i < n_args; ++i) {
+ PyObject *obj = args[i];
+ PyObject *arr;
+ PyArrayIterObject *it;
- va_start(va, nadd);
- for (i = 0; i < ntot; i++) {
- if (i < n) {
- current = mps[i];
- }
- else {
- current = va_arg(va, PyObject *);
- }
- arr = PyArray_FROM_O(current);
- if (arr == NULL) {
- err = 1;
- break;
+ if (PyObject_IsInstance(obj, (PyObject *)&PyArrayMultiIter_Type)) {
+ PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *)obj;
+ int j;
+
+ if (multi->numiter + mit->numiter > NPY_MAXARGS) {
+ multiiter_wrong_number_of_args();
+ goto fail;
+ }
+ for (j = 0; j < mit->numiter; ++j) {
+ arr = (PyObject *)mit->iters[j]->ao;
+ it = (PyArrayIterObject *)PyArray_IterNew(arr);
+ if (it == NULL) {
+ goto fail;
+ }
+ multi->iters[multi->numiter++] = it;
+ }
}
- else {
- multi->iters[i] = (PyArrayIterObject *)PyArray_IterNew(arr);
- if (multi->iters[i] == NULL) {
- err = 1;
- break;
+ else if (multi->numiter < NPY_MAXARGS) {
+ arr = PyArray_FromAny(obj, NULL, 0, 0, 0, NULL);
+ if (arr == NULL) {
+ goto fail;
}
+ it = (PyArrayIterObject *)PyArray_IterNew(arr);
Py_DECREF(arr);
+ if (it == NULL) {
+ goto fail;
+ }
+ multi->iters[multi->numiter++] = it;
+ }
+ else {
+ multiiter_wrong_number_of_args();
+ goto fail;
}
}
- va_end(va);
- if (!err && PyArray_Broadcast(multi) < 0) {
- err = 1;
+ if (multi->numiter < 0) {
+ multiiter_wrong_number_of_args();
+ goto fail;
}
- if (err) {
- Py_DECREF(multi);
- return NULL;
+ if (PyArray_Broadcast(multi) < 0) {
+ goto fail;
}
PyArray_MultiIter_RESET(multi);
+
return (PyObject *)multi;
+
+fail:
+ Py_DECREF(multi);
+
+ return NULL;
}
/*NUMPY_API
- * Get MultiIterator,
+ * Get MultiIterator from array of Python objects and any additional
+ *
+ * PyObject **mps - array of PyObjects
+ * int n - number of PyObjects in the array
+ * int nadd - number of additional arrays to include in the iterator.
+ *
+ * Returns a multi-iterator object.
*/
-NPY_NO_EXPORT PyObject *
-PyArray_MultiIterNew(int n, ...)
+NPY_NO_EXPORT PyObject*
+PyArray_MultiIterFromObjects(PyObject **mps, int n, int nadd, ...)
{
+ PyObject *args_impl[NPY_MAXARGS];
+ int ntot = n + nadd;
+ int i;
va_list va;
- PyArrayMultiIterObject *multi;
- PyObject *current;
- PyObject *arr;
- int i, err = 0;
-
- if (n < 1 || n > NPY_MAXARGS) {
- PyErr_Format(PyExc_ValueError,
- "Need at least 1 and at most %d "
- "array objects.", NPY_MAXARGS);
- return NULL;
+ if ((ntot > NPY_MAXARGS) || (ntot < 0)) {
+ return multiiter_wrong_number_of_args();
}
- /* fprintf(stderr, "multi new...");*/
+ for (i = 0; i < n; ++i) {
+ args_impl[i] = mps[i];
+ }
- multi = PyArray_malloc(sizeof(PyArrayMultiIterObject));
- if (multi == NULL) {
- return PyErr_NoMemory();
+ va_start(va, nadd);
+ for (; i < ntot; ++i) {
+ args_impl[i] = va_arg(va, PyObject *);
}
- PyObject_Init((PyObject *)multi, &PyArrayMultiIter_Type);
+ va_end(va);
- for (i = 0; i < n; i++) {
- multi->iters[i] = NULL;
+ return multiiter_new_impl(ntot, args_impl);
+}
+
+/*NUMPY_API
+ * Get MultiIterator,
+ */
+NPY_NO_EXPORT PyObject*
+PyArray_MultiIterNew(int n, ...)
+{
+ PyObject *args_impl[NPY_MAXARGS];
+ int i;
+ va_list va;
+
+ if ((n > NPY_MAXARGS) || (n < 0)) {
+ return multiiter_wrong_number_of_args();
}
- multi->numiter = n;
- multi->index = 0;
va_start(va, n);
- for (i = 0; i < n; i++) {
- current = va_arg(va, PyObject *);
- arr = PyArray_FROM_O(current);
- if (arr == NULL) {
- err = 1;
- break;
- }
- else {
- multi->iters[i] = (PyArrayIterObject *)PyArray_IterNew(arr);
- if (multi->iters[i] == NULL) {
- err = 1;
- break;
- }
- Py_DECREF(arr);
- }
+ for (i = 0; i < n; ++i) {
+ args_impl[i] = va_arg(va, PyObject *);
}
va_end(va);
- if (!err && PyArray_Broadcast(multi) < 0) {
- err = 1;
- }
- if (err) {
- Py_DECREF(multi);
- return NULL;
- }
- PyArray_MultiIter_RESET(multi);
- return (PyObject *)multi;
+ return multiiter_new_impl(n, args_impl);
}
-static PyObject *
-arraymultiter_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args, PyObject *kwds)
+static PyObject*
+arraymultiter_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args,
+ PyObject *kwds)
{
+ PyObject *ret, *fast_seq;
+ Py_ssize_t n;
- Py_ssize_t n = 0;
- Py_ssize_t i, j, k;
- PyArrayMultiIterObject *multi;
- PyObject *arr;
-
- if (kwds != NULL) {
+ if (kwds != NULL && PyDict_Size(kwds) > 0) {
PyErr_SetString(PyExc_ValueError,
"keyword arguments not accepted.");
return NULL;
}
- for (j = 0; j < PyTuple_Size(args); ++j) {
- PyObject *obj = PyTuple_GET_ITEM(args, j);
-
- if (PyObject_IsInstance(obj, (PyObject *)&PyArrayMultiIter_Type)) {
- /*
- * If obj is a multi-iterator, all its arrays will be added
- * to the new multi-iterator.
- */
- n += ((PyArrayMultiIterObject *)obj)->numiter;
- }
- else {
- /* If not, will try to convert it to a single array */
- ++n;
- }
- }
- if (n < 1 || n > NPY_MAXARGS) {
- if (PyErr_Occurred()) {
- return NULL;
- }
- PyErr_Format(PyExc_ValueError,
- "Need at least 1 and at most %d "
- "array objects.", NPY_MAXARGS);
+ fast_seq = PySequence_Fast(args, ""); // needed for pypy
+ if (fast_seq == NULL) {
return NULL;
}
-
- multi = PyArray_malloc(sizeof(PyArrayMultiIterObject));
- if (multi == NULL) {
- return PyErr_NoMemory();
- }
- PyObject_Init((PyObject *)multi, &PyArrayMultiIter_Type);
-
- multi->numiter = n;
- multi->index = 0;
- i = 0;
- for (j = 0; j < PyTuple_GET_SIZE(args); ++j) {
- PyObject *obj = PyTuple_GET_ITEM(args, j);
- PyArrayIterObject *it;
-
- if (PyObject_IsInstance(obj, (PyObject *)&PyArrayMultiIter_Type)) {
- PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *)obj;
-
- for (k = 0; k < mit->numiter; ++k) {
- arr = (PyObject *)mit->iters[k]->ao;
- assert (arr != NULL);
- it = (PyArrayIterObject *)PyArray_IterNew(arr);
- if (it == NULL) {
- goto fail;
- }
- multi->iters[i++] = it;
- }
- }
- else {
- arr = PyArray_FROM_O(obj);
- if (arr == NULL) {
- goto fail;
- }
- it = (PyArrayIterObject *)PyArray_IterNew(arr);
- if (it == NULL) {
- goto fail;
- }
- multi->iters[i++] = it;
- Py_DECREF(arr);
- }
- }
- assert (i == n);
- if (PyArray_Broadcast(multi) < 0) {
- goto fail;
+ n = PySequence_Fast_GET_SIZE(fast_seq);
+ if (n > NPY_MAXARGS) {
+ Py_DECREF(fast_seq);
+ return multiiter_wrong_number_of_args();
}
- PyArray_MultiIter_RESET(multi);
- return (PyObject *)multi;
-
- fail:
- Py_DECREF(multi);
- return NULL;
+ ret = multiiter_new_impl(n, PySequence_Fast_ITEMS(fast_seq));
+ Py_DECREF(fast_seq);
+ return ret;
}
static PyObject *
@@ -1826,7 +1665,7 @@ static char* _set_constant(PyArrayNeighborhoodIterObject* iter,
/* set the dataptr from its current coordinates */
static char*
-get_ptr_constant(PyArrayIterObject* _iter, npy_intp *coordinates)
+get_ptr_constant(PyArrayIterObject* _iter, const npy_intp *coordinates)
{
int i;
npy_intp bd, _coordinates[NPY_MAXDIMS];
@@ -1881,7 +1720,7 @@ __npy_pos_remainder(npy_intp i, npy_intp n)
/* set the dataptr from its current coordinates */
static char*
-get_ptr_mirror(PyArrayIterObject* _iter, npy_intp *coordinates)
+get_ptr_mirror(PyArrayIterObject* _iter, const npy_intp *coordinates)
{
int i;
npy_intp bd, _coordinates[NPY_MAXDIMS], lb;
@@ -1915,7 +1754,7 @@ __npy_euclidean_division(npy_intp i, npy_intp n)
_coordinates[c] = lb + __npy_euclidean_division(bd, p->limits_sizes[c]);
static char*
-get_ptr_circular(PyArrayIterObject* _iter, npy_intp *coordinates)
+get_ptr_circular(PyArrayIterObject* _iter, const npy_intp *coordinates)
{
int i;
npy_intp bd, _coordinates[NPY_MAXDIMS], lb;
@@ -1937,7 +1776,7 @@ get_ptr_circular(PyArrayIterObject* _iter, npy_intp *coordinates)
* A Neighborhood Iterator object.
*/
NPY_NO_EXPORT PyObject*
-PyArray_NeighborhoodIterNew(PyArrayIterObject *x, npy_intp *bounds,
+PyArray_NeighborhoodIterNew(PyArrayIterObject *x, const npy_intp *bounds,
int mode, PyArrayObject* fill)
{
int i;
@@ -1949,7 +1788,8 @@ PyArray_NeighborhoodIterNew(PyArrayIterObject *x, npy_intp *bounds,
}
PyObject_Init((PyObject *)ret, &PyArrayNeighborhoodIter_Type);
- array_iter_base_init((PyArrayIterObject*)ret, x->ao);
+ Py_INCREF(x->ao); /* PyArray_RawIterBaseInit steals a reference */
+ PyArray_RawIterBaseInit((PyArrayIterObject*)ret, x->ao);
Py_INCREF(x);
ret->_internal_iter = x;
diff --git a/numpy/core/src/multiarray/iterators.h b/numpy/core/src/multiarray/iterators.h
index 04f57c885..d942f45b8 100644
--- a/numpy/core/src/multiarray/iterators.h
+++ b/numpy/core/src/multiarray/iterators.h
@@ -1,21 +1,13 @@
#ifndef _NPY_ARRAYITERATORS_H_
#define _NPY_ARRAYITERATORS_H_
-/*
- * Parses an index that has no fancy indexing. Populates
- * out_dimensions, out_strides, and out_offset.
- */
-NPY_NO_EXPORT int
-parse_index(PyArrayObject *self, PyObject *op,
- npy_intp *out_dimensions,
- npy_intp *out_strides,
- npy_intp *out_offset,
- int check_index);
-
NPY_NO_EXPORT PyObject
*iter_subscript(PyArrayIterObject *, PyObject *);
NPY_NO_EXPORT int
iter_ass_subscript(PyArrayIterObject *, PyObject *, PyObject *);
+NPY_NO_EXPORT void
+PyArray_RawIterBaseInit(PyArrayIterObject *it, PyArrayObject *ao);
+
#endif
diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
index 159bb4103..63b2a8842 100644
--- a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
+++ b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
@@ -82,7 +82,7 @@
/**begin repeat
* #elsize = 1, 2, 4, 8, 16#
* #elsize_half = 0, 1, 2, 4, 8#
- * #type = npy_uint8, npy_uint16, npy_uint32, npy_uint64, npy_uint128#
+ * #type = npy_uint8, npy_uint16, npy_uint32, npy_uint64, npy_uint64#
*/
/**begin repeat1
* #oper = strided_to_strided, strided_to_contig,
@@ -119,10 +119,10 @@ static void
npy_intp N, npy_intp NPY_UNUSED(src_itemsize),
NpyAuxData *NPY_UNUSED(data))
{
-#if @is_aligned@ && @elsize@ != 16
+#if @is_aligned@
/* sanity check */
- assert(npy_is_aligned(dst, _ALIGN(@type@)));
- assert(npy_is_aligned(src, _ALIGN(@type@)));
+ assert(N == 0 || npy_is_aligned(dst, _UINT_ALIGN(@type@)));
+ assert(N == 0 || npy_is_aligned(src, _UINT_ALIGN(@type@)));
#endif
/*printf("fn @prefix@_@oper@_size@elsize@\n");*/
while (N > 0) {
@@ -201,8 +201,8 @@ static NPY_GCC_OPT_3 void
}
#if @is_aligned@ && @elsize@ != 16
/* sanity check */
- assert(npy_is_aligned(dst, _ALIGN(@type@)));
- assert(npy_is_aligned(src, _ALIGN(@type@)));
+ assert(N == 0 || npy_is_aligned(dst, _UINT_ALIGN(@type@)));
+ assert(N == 0 || npy_is_aligned(src, _UINT_ALIGN(@type@)));
#endif
#if @elsize@ == 1 && @dst_contig@
memset(dst, *src, N);
@@ -417,32 +417,31 @@ PyArray_GetStridedCopyFn(int aligned, npy_intp src_stride,
#if !NPY_USE_UNALIGNED_ACCESS
}
else {
- /* contiguous dst */
- if (itemsize != 0 && dst_stride == itemsize) {
- /* contiguous src */
- if (itemsize != 0 && src_stride == itemsize) {
- return &_contig_to_contig;
- }
- /* general src */
- else {
- switch (itemsize) {
- case 1:
- return &_aligned_strided_to_contig_size1;
+ if (itemsize != 0) {
+ if (dst_stride == itemsize) {
+ /* contiguous dst */
+ if (src_stride == itemsize) {
+ /* contiguous src, dst */
+ return &_contig_to_contig;
+ }
+ else {
+ /* general src */
+ switch (itemsize) {
+ case 1:
+ return &_aligned_strided_to_contig_size1;
/**begin repeat
* #elsize = 2, 4, 8, 16#
*/
- case @elsize@:
- return &_strided_to_contig_size@elsize@;
+ case @elsize@:
+ return &_strided_to_contig_size@elsize@;
/**end repeat**/
+ }
}
- }
- return &_strided_to_strided;
- }
- /* general dst */
- else {
- /* contiguous src */
- if (itemsize != 0 && src_stride == itemsize) {
+ return &_strided_to_strided;
+ }
+ else if (src_stride == itemsize) {
+ /* contiguous src, general dst */
switch (itemsize) {
case 1:
return &_aligned_contig_to_strided_size1;
@@ -456,18 +455,18 @@ PyArray_GetStridedCopyFn(int aligned, npy_intp src_stride,
return &_strided_to_strided;
}
- /* general src */
- else {
- switch (itemsize) {
- case 1:
- return &_aligned_strided_to_strided_size1;
+ }
+ else {
+ /* general src, dst */
+ switch (itemsize) {
+ case 1:
+ return &_aligned_strided_to_strided_size1;
/**begin repeat
* #elsize = 2, 4, 8, 16#
*/
- case @elsize@:
- return &_strided_to_strided_size@elsize@;
+ case @elsize@:
+ return &_strided_to_strided_size@elsize@;
/**end repeat**/
- }
}
}
}
@@ -592,7 +591,7 @@ NPY_NO_EXPORT PyArray_StridedUnaryOp *
/* contiguous dst */
if (itemsize != 0 && dst_stride == itemsize) {
/* contiguous src */
- if (itemsize != 0 && src_stride == itemsize) {
+ if (src_stride == itemsize) {
switch (itemsize) {
/**begin repeat1
* #elsize = 2, 4, 8, 16#
@@ -808,12 +807,8 @@ static NPY_GCC_OPT_3 void
#if @aligned@
/* sanity check */
-# if !@is_complex1@
- assert(npy_is_aligned(src, _ALIGN(_TYPE1)));
-# endif
-# if !@is_complex2@
- assert(npy_is_aligned(dst, _ALIGN(_TYPE2)));
-# endif
+ assert(N == 0 || npy_is_aligned(src, _ALIGN(_TYPE1)));
+ assert(N == 0 || npy_is_aligned(dst, _ALIGN(_TYPE2)));
#endif
/*printf("@prefix@_cast_@name1@_to_@name2@\n");*/
@@ -1425,7 +1420,7 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind,
while (itersize--) {
char * self_ptr;
npy_intp indval = *((npy_intp*)ind_ptr);
- assert(npy_is_aligned(ind_ptr, _ALIGN(npy_intp)));
+ assert(npy_is_aligned(ind_ptr, _UINT_ALIGN(npy_intp)));
#if @isget@
if (check_and_adjust_index(&indval, fancy_dim, 0, _save) < 0 ) {
return -1;
@@ -1439,8 +1434,8 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind,
#if @isget@
#if @elsize@
- assert(npy_is_aligned(result_ptr, _ALIGN(@copytype@)));
- assert(npy_is_aligned(self_ptr, _ALIGN(@copytype@)));
+ assert(npy_is_aligned(result_ptr, _UINT_ALIGN(@copytype@)));
+ assert(npy_is_aligned(self_ptr, _UINT_ALIGN(@copytype@)));
*(@copytype@ *)result_ptr = *(@copytype@ *)self_ptr;
#else
copyswap(result_ptr, self_ptr, 0, self);
@@ -1448,8 +1443,8 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind,
#else /* !@isget@ */
#if @elsize@
- assert(npy_is_aligned(result_ptr, _ALIGN(@copytype@)));
- assert(npy_is_aligned(self_ptr, _ALIGN(@copytype@)));
+ assert(npy_is_aligned(result_ptr, _UINT_ALIGN(@copytype@)));
+ assert(npy_is_aligned(self_ptr, _UINT_ALIGN(@copytype@)));
*(@copytype@ *)self_ptr = *(@copytype@ *)result_ptr;
#else
copyswap(self_ptr, result_ptr, 0, self);
@@ -1571,7 +1566,7 @@ mapiter_@name@(PyArrayMapIterObject *mit)
for (i=0; i < @numiter@; i++) {
npy_intp indval = *((npy_intp*)outer_ptrs[i]);
assert(npy_is_aligned(outer_ptrs[i],
- _ALIGN(npy_intp)));
+ _UINT_ALIGN(npy_intp)));
#if @isget@ && @one_iter@
if (check_and_adjust_index(&indval, fancy_dims[i],
@@ -1591,16 +1586,20 @@ mapiter_@name@(PyArrayMapIterObject *mit)
#if @isget@
#if @elsize@
- assert(npy_is_aligned(outer_ptrs[i], _ALIGN(@copytype@)));
- assert(npy_is_aligned(self_ptr, _ALIGN(@copytype@)));
+ assert(npy_is_aligned(outer_ptrs[i],
+ _UINT_ALIGN(@copytype@)));
+ assert(npy_is_aligned(self_ptr,
+ _UINT_ALIGN(@copytype@)));
*(@copytype@ *)(outer_ptrs[i]) = *(@copytype@ *)self_ptr;
#else
copyswap(outer_ptrs[i], self_ptr, 0, array);
#endif
#else /* !@isget@ */
#if @elsize@
- assert(npy_is_aligned(outer_ptrs[i], _ALIGN(@copytype@)));
- assert(npy_is_aligned(self_ptr, _ALIGN(@copytype@)));
+ assert(npy_is_aligned(outer_ptrs[i],
+ _UINT_ALIGN(@copytype@)));
+ assert(npy_is_aligned(self_ptr,
+ _UINT_ALIGN(@copytype@)));
*(@copytype@ *)self_ptr = *(@copytype@ *)(outer_ptrs[i]);
#else
copyswap(self_ptr, outer_ptrs[i], 0, array);
diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c
index 038c21c92..247864775 100644
--- a/numpy/core/src/multiarray/mapping.c
+++ b/numpy/core/src/multiarray/mapping.c
@@ -15,6 +15,7 @@
#include "common.h"
#include "ctors.h"
+#include "descriptor.h"
#include "iterators.h"
#include "mapping.h"
#include "lowlevel_strided_loops.h"
@@ -175,7 +176,7 @@ unpack_tuple(PyTupleObject *index, PyObject **result, npy_intp result_n)
/* Unpack a single scalar index, taking a new reference to match unpack_tuple */
static NPY_INLINE npy_intp
-unpack_scalar(PyObject *index, PyObject **result, npy_intp result_n)
+unpack_scalar(PyObject *index, PyObject **result, npy_intp NPY_UNUSED(result_n))
{
Py_INCREF(index);
result[0] = index;
@@ -611,9 +612,9 @@ prepare_index(PyArrayObject *self, PyObject *index,
/* Convert the boolean array into multiple integer ones */
n = _nonzero_indices((PyObject *)arr, nonzero_result);
- Py_DECREF(arr);
if (n < 0) {
+ Py_DECREF(arr);
goto failed_building_indices;
}
@@ -624,6 +625,7 @@ prepare_index(PyArrayObject *self, PyObject *index,
for (i=0; i < n; i++) {
Py_DECREF(nonzero_result[i]);
}
+ Py_DECREF(arr);
goto failed_building_indices;
}
@@ -637,6 +639,7 @@ prepare_index(PyArrayObject *self, PyObject *index,
used_ndim += 1;
curr_idx += 1;
}
+ Py_DECREF(arr);
/* All added indices have 1 dimension */
if (fancy_ndim < 1) {
@@ -710,26 +713,26 @@ prepare_index(PyArrayObject *self, PyObject *index,
* to find the ellipsis value or append an ellipsis if necessary.
*/
if (used_ndim < PyArray_NDIM(self)) {
- if (index_type & HAS_ELLIPSIS) {
- indices[ellipsis_pos].value = PyArray_NDIM(self) - used_ndim;
- used_ndim = PyArray_NDIM(self);
- new_ndim += indices[ellipsis_pos].value;
- }
- else {
- /*
- * There is no ellipsis yet, but it is not a full index
- * so we append an ellipsis to the end.
- */
- index_type |= HAS_ELLIPSIS;
- indices[curr_idx].object = NULL;
- indices[curr_idx].type = HAS_ELLIPSIS;
- indices[curr_idx].value = PyArray_NDIM(self) - used_ndim;
- ellipsis_pos = curr_idx;
-
- used_ndim = PyArray_NDIM(self);
- new_ndim += indices[curr_idx].value;
- curr_idx += 1;
- }
+ if (index_type & HAS_ELLIPSIS) {
+ indices[ellipsis_pos].value = PyArray_NDIM(self) - used_ndim;
+ used_ndim = PyArray_NDIM(self);
+ new_ndim += indices[ellipsis_pos].value;
+ }
+ else {
+ /*
+ * There is no ellipsis yet, but it is not a full index
+ * so we append an ellipsis to the end.
+ */
+ index_type |= HAS_ELLIPSIS;
+ indices[curr_idx].object = NULL;
+ indices[curr_idx].type = HAS_ELLIPSIS;
+ indices[curr_idx].value = PyArray_NDIM(self) - used_ndim;
+ ellipsis_pos = curr_idx;
+
+ used_ndim = PyArray_NDIM(self);
+ new_ndim += indices[curr_idx].value;
+ curr_idx += 1;
+ }
}
else if (used_ndim > PyArray_NDIM(self)) {
PyErr_SetString(PyExc_IndexError,
@@ -1064,7 +1067,8 @@ array_boolean_subscript(PyArrayObject *self,
/* Get a dtype transfer function */
NpyIter_GetInnerFixedStrideArray(iter, fixed_strides);
- if (PyArray_GetDTypeTransferFunction(IsUintAligned(self),
+ if (PyArray_GetDTypeTransferFunction(
+ IsUintAligned(self) && IsAligned(self),
fixed_strides[0], itemsize,
dtype, dtype,
0,
@@ -1128,8 +1132,8 @@ array_boolean_subscript(PyArrayObject *self,
1, &size, PyArray_STRIDES(ret), PyArray_BYTES(ret),
PyArray_FLAGS(self), (PyObject *)self, (PyObject *)tmp);
+ Py_DECREF(tmp);
if (ret == NULL) {
- Py_DECREF(tmp);
return NULL;
}
}
@@ -1253,7 +1257,8 @@ array_assign_boolean_subscript(PyArrayObject *self,
/* Get a dtype transfer function */
NpyIter_GetInnerFixedStrideArray(iter, fixed_strides);
if (PyArray_GetDTypeTransferFunction(
- IsUintAligned(self) && IsUintAligned(v),
+ IsUintAligned(self) && IsAligned(self) &&
+ IsUintAligned(v) && IsAligned(v),
v_stride, fixed_strides[0],
PyArray_DESCR(v), PyArray_DESCR(self),
0,
@@ -1389,54 +1394,14 @@ array_subscript_asarray(PyArrayObject *self, PyObject *op)
}
/*
- * Helper function for _get_field_view which turns a multifield
- * view into a "packed" copy, as done in numpy 1.15 and before.
- * In numpy 1.16 this function should be removed.
- */
-NPY_NO_EXPORT int
-_multifield_view_to_copy(PyArrayObject **view) {
- static PyObject *copyfunc = NULL;
- PyObject *viewcopy;
-
- /* return a repacked copy of the view */
- npy_cache_import("numpy.lib.recfunctions", "repack_fields", &copyfunc);
- if (copyfunc == NULL) {
- goto view_fail;
- }
-
- PyArray_CLEARFLAGS(*view, NPY_ARRAY_WARN_ON_WRITE);
- viewcopy = PyObject_CallFunction(copyfunc, "O", *view);
- if (viewcopy == NULL) {
- goto view_fail;
- }
- Py_DECREF(*view);
- *view = (PyArrayObject*)viewcopy;
-
- /* warn when writing to the copy */
- PyArray_ENABLEFLAGS(*view, NPY_ARRAY_WARN_ON_WRITE);
- return 0;
-
-view_fail:
- Py_DECREF(*view);
- *view = NULL;
- return 0;
-}
-
-/*
* Attempts to subscript an array using a field name or list of field names.
*
- * If an error occurred, return 0 and set view to NULL. If the subscript is not
- * a string or list of strings, return -1 and set view to NULL. Otherwise
- * return 0 and set view to point to a new view into arr for the given fields.
- *
- * In numpy 1.15 and before, in the case of a list of field names the returned
- * view will actually be a copy by default, with fields packed together.
- * The `force_view` argument causes a view to be returned. This argument can be
- * removed in 1.16 when we plan to return a view always.
+ * ret = 0, view != NULL: view points to the requested fields of arr
+ * ret = 0, view == NULL: an error occurred
+ * ret = -1, view == NULL: unrecognized input, this is not a field index.
*/
NPY_NO_EXPORT int
-_get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view,
- int force_view)
+_get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view)
{
*view = NULL;
@@ -1476,111 +1441,44 @@ _get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view,
}
return 0;
}
+
/* next check for a list of field names */
else if (PySequence_Check(ind) && !PyTuple_Check(ind)) {
- int seqlen, i;
- PyObject *name = NULL, *tup;
- PyObject *fields, *names;
+ npy_intp seqlen, i;
PyArray_Descr *view_dtype;
seqlen = PySequence_Size(ind);
- /* quit if have a 0-d array (seqlen==-1) or a 0-len array */
+ /* quit if have a fake sequence-like, which errors on len()*/
if (seqlen == -1) {
PyErr_Clear();
return -1;
}
+ /* 0-len list is handled elsewhere as an integer index */
if (seqlen == 0) {
return -1;
}
- fields = PyDict_New();
- if (fields == NULL) {
- return 0;
- }
- names = PyTuple_New(seqlen);
- if (names == NULL) {
- Py_DECREF(fields);
- return 0;
- }
-
+ /* check the items are strings */
for (i = 0; i < seqlen; i++) {
- name = PySequence_GetItem(ind, i);
- if (name == NULL) {
- /* only happens for strange sequence objects */
+ npy_bool is_string;
+ PyObject *item = PySequence_GetItem(ind, i);
+ if (item == NULL) {
PyErr_Clear();
- Py_DECREF(fields);
- Py_DECREF(names);
return -1;
}
-
- if (!PyBaseString_Check(name)) {
- Py_DECREF(name);
- Py_DECREF(fields);
- Py_DECREF(names);
+ is_string = PyBaseString_Check(item);
+ Py_DECREF(item);
+ if (!is_string) {
return -1;
}
-
- tup = PyDict_GetItem(PyArray_DESCR(arr)->fields, name);
- if (tup == NULL){
- PyObject *errmsg = PyUString_FromString("no field of name ");
- PyUString_ConcatAndDel(&errmsg, name);
- PyErr_SetObject(PyExc_ValueError, errmsg);
- Py_DECREF(errmsg);
- Py_DECREF(fields);
- Py_DECREF(names);
- return 0;
- }
- /* disallow use of titles as index */
- if (PyTuple_Size(tup) == 3) {
- PyObject *title = PyTuple_GET_ITEM(tup, 2);
- int titlecmp = PyObject_RichCompareBool(title, name, Py_EQ);
- if (titlecmp == 1) {
- /* if title == name, we got a title, not a field name */
- PyErr_SetString(PyExc_KeyError,
- "cannot use field titles in multi-field index");
- }
- if (titlecmp != 0 || PyDict_SetItem(fields, title, tup) < 0) {
- Py_DECREF(name);
- Py_DECREF(fields);
- Py_DECREF(names);
- return 0;
- }
- }
- /* disallow duplicate field indices */
- if (PyDict_Contains(fields, name)) {
- PyObject *errmsg = PyUString_FromString(
- "duplicate field of name ");
- PyUString_ConcatAndDel(&errmsg, name);
- PyErr_SetObject(PyExc_ValueError, errmsg);
- Py_DECREF(errmsg);
- Py_DECREF(fields);
- Py_DECREF(names);
- return 0;
- }
- if (PyDict_SetItem(fields, name, tup) < 0) {
- Py_DECREF(name);
- Py_DECREF(fields);
- Py_DECREF(names);
- return 0;
- }
- if (PyTuple_SetItem(names, i, name) < 0) {
- Py_DECREF(fields);
- Py_DECREF(names);
- return 0;
- }
}
- view_dtype = PyArray_DescrNewFromType(NPY_VOID);
+ /* Call into the dtype subscript */
+ view_dtype = arraydescr_field_subset_view(PyArray_DESCR(arr), ind);
if (view_dtype == NULL) {
- Py_DECREF(fields);
- Py_DECREF(names);
return 0;
}
- view_dtype->elsize = PyArray_DESCR(arr)->elsize;
- view_dtype->names = names;
- view_dtype->fields = fields;
- view_dtype->flags = PyArray_DESCR(arr)->flags;
*view = (PyArrayObject*)PyArray_NewFromDescr_int(
Py_TYPE(arr),
@@ -1597,11 +1495,7 @@ _get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view,
return 0;
}
- /* the code below can be replaced by "return 0" in 1.16 */
- if (force_view) {
- return 0;
- }
- return _multifield_view_to_copy(view);
+ return 0;
}
return -1;
}
@@ -1629,7 +1523,7 @@ array_subscript(PyArrayObject *self, PyObject *op)
/* return fields if op is a string index */
if (PyDataType_HASFIELDS(PyArray_DESCR(self))) {
PyArrayObject *view;
- int ret = _get_field_view(self, op, &view, 0);
+ int ret = _get_field_view(self, op, &view);
if (ret == 0){
if (view == NULL) {
return NULL;
@@ -1805,7 +1699,7 @@ array_subscript(PyArrayObject *self, PyObject *op)
PyArray_SHAPE(tmp_arr),
PyArray_STRIDES(tmp_arr),
PyArray_BYTES(tmp_arr),
- PyArray_FLAGS(self),
+ PyArray_FLAGS(tmp_arr),
(PyObject *)self, (PyObject *)tmp_arr);
Py_DECREF(tmp_arr);
if (result == NULL) {
@@ -1911,7 +1805,7 @@ array_assign_subscript(PyArrayObject *self, PyObject *ind, PyObject *op)
/* field access */
if (PyDataType_HASFIELDS(PyArray_DESCR(self))){
PyArrayObject *view;
- int ret = _get_field_view(self, ind, &view, 1);
+ int ret = _get_field_view(self, ind, &view);
if (ret == 0){
if (view == NULL) {
return -1;
@@ -2622,6 +2516,7 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit)
indval = *((npy_intp*)data);
if (check_and_adjust_index(&indval,
outer_dim, outer_axis, _save) < 0) {
+ Py_DECREF(intp_type);
return -1;
}
data += stride;
@@ -2722,7 +2617,8 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
PyArrayObject *original_extra_op = extra_op;
PyArrayObject *index_arrays[NPY_MAXDIMS];
- PyArray_Descr *dtypes[NPY_MAXDIMS];
+ PyArray_Descr *intp_descr;
+ PyArray_Descr *dtypes[NPY_MAXDIMS]; /* borrowed references */
npy_uint32 op_flags[NPY_MAXDIMS];
npy_uint32 outer_flags;
@@ -2735,9 +2631,15 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
int nops;
int uses_subspace;
+ intp_descr = PyArray_DescrFromType(NPY_INTP);
+ if (intp_descr == NULL) {
+ return NULL;
+ }
+
/* create new MapIter object */
mit = (PyArrayMapIterObject *)PyArray_malloc(sizeof(PyArrayMapIterObject));
if (mit == NULL) {
+ Py_DECREF(intp_descr);
return NULL;
}
/* set all attributes of mapiter to zero */
@@ -2767,6 +2669,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
mit->nd_fancy = fancy_ndim;
if (mapiter_fill_info(mit, indices, index_num, arr) < 0) {
Py_DECREF(mit);
+ Py_DECREF(intp_descr);
return NULL;
}
@@ -2776,7 +2679,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
for (i=0; i < index_num; i++) {
if (indices[i].type & HAS_FANCY) {
index_arrays[mit->numiter] = (PyArrayObject *)indices[i].object;
- dtypes[mit->numiter] = PyArray_DescrFromType(NPY_INTP);
+ dtypes[mit->numiter] = intp_descr;
op_flags[mit->numiter] = (NPY_ITER_NBO |
NPY_ITER_ALIGNED |
@@ -2799,9 +2702,10 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
PyArray_DescrFromType(NPY_INTP), 0);
if (index_arrays[0] == NULL) {
Py_DECREF(mit);
+ Py_DECREF(intp_descr);
return NULL;
}
- dtypes[0] = PyArray_DescrFromType(NPY_INTP);
+ dtypes[0] = intp_descr;
op_flags[0] = NPY_ITER_NBO | NPY_ITER_ALIGNED | NPY_ITER_READONLY;
mit->fancy_dims[0] = 1;
@@ -3031,7 +2935,6 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
nops += 1;
index_arrays[mit->numiter] = extra_op;
- Py_INCREF(extra_op_dtype);
dtypes[mit->numiter] = extra_op_dtype;
op_flags[mit->numiter] = (extra_op_flags |
NPY_ITER_ALLOCATE |
@@ -3057,9 +2960,6 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
}
/* NpyIter cleanup and information: */
- for (i=0; i < nops; i++) {
- Py_DECREF(dtypes[i]);
- }
if (dummy_array) {
Py_DECREF(index_arrays[0]);
}
@@ -3145,6 +3045,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
/* Can now return early if no subspace is being used */
if (!uses_subspace) {
Py_XDECREF(extra_op);
+ Py_DECREF(intp_descr);
return (PyObject *)mit;
}
@@ -3214,6 +3115,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
}
Py_XDECREF(extra_op);
+ Py_DECREF(intp_descr);
return (PyObject *)mit;
fail:
@@ -3282,6 +3184,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
finish:
Py_XDECREF(extra_op);
+ Py_DECREF(intp_descr);
Py_DECREF(mit);
return NULL;
}
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index cdbd0d6ae..e5845f2f6 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -6,8 +6,10 @@
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
#include "numpy/arrayobject.h"
+#include "arrayobject.h"
#include "numpy/arrayscalars.h"
+#include "arrayfunction_override.h"
#include "npy_config.h"
#include "npy_pycompat.h"
#include "npy_import.h"
@@ -187,7 +189,7 @@ array_reshape(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
if (n <= 1) {
- if (PyTuple_GET_ITEM(args, 0) == Py_None) {
+ if (n != 0 && PyTuple_GET_ITEM(args, 0) == Py_None) {
return PyArray_View(self, NULL, NULL);
}
if (!PyArg_ParseTuple(args, "O&:reshape", PyArray_IntpConverter,
@@ -363,6 +365,7 @@ PyArray_GetField(PyArrayObject *self, PyArray_Descr *typed, int offset)
npy_cache_import("numpy.core._internal", "_getfield_is_safe",
&checkfunc);
if (checkfunc == NULL) {
+ Py_DECREF(typed);
return NULL;
}
@@ -370,6 +373,7 @@ PyArray_GetField(PyArrayObject *self, PyArray_Descr *typed, int offset)
safe = PyObject_CallFunction(checkfunc, "OOi", PyArray_DESCR(self),
typed, offset);
if (safe == NULL) {
+ Py_DECREF(typed);
return NULL;
}
Py_DECREF(safe);
@@ -380,14 +384,17 @@ PyArray_GetField(PyArrayObject *self, PyArray_Descr *typed, int offset)
/* check that values are valid */
if (typed_elsize > self_elsize) {
PyErr_SetString(PyExc_ValueError, "new type is larger than original type");
+ Py_DECREF(typed);
return NULL;
}
if (offset < 0) {
PyErr_SetString(PyExc_ValueError, "offset is negative");
+ Py_DECREF(typed);
return NULL;
}
if (offset > self_elsize - typed_elsize) {
PyErr_SetString(PyExc_ValueError, "new type plus offset is larger than original type");
+ Py_DECREF(typed);
return NULL;
}
@@ -432,6 +439,7 @@ PyArray_SetField(PyArrayObject *self, PyArray_Descr *dtype,
int retval = 0;
if (PyArray_FailUnlessWriteable(self, "assignment destination") < 0) {
+ Py_DECREF(dtype);
return -1;
}
@@ -576,15 +584,18 @@ array_tofile(PyArrayObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
+ file = NpyPath_PathlikeToFspath(file);
+ if (file == NULL) {
+ return NULL;
+ }
if (PyBytes_Check(file) || PyUnicode_Check(file)) {
- file = npy_PyFile_OpenFile(file, "wb");
+ Py_SETREF(file, npy_PyFile_OpenFile(file, "wb"));
if (file == NULL) {
return NULL;
}
own = 1;
}
else {
- Py_INCREF(file);
own = 0;
}
@@ -823,8 +834,8 @@ array_astype(PyArrayObject *self, PyObject *args, PyObject *kwds)
PyArrayObject *ret;
/* If the requested dtype is flexible, adapt it */
- PyArray_AdaptFlexibleDType((PyObject *)self, PyArray_DESCR(self),
- &dtype);
+ dtype = PyArray_AdaptFlexibleDType((PyObject *)self,
+ PyArray_DESCR(self), dtype);
if (dtype == NULL) {
return NULL;
}
@@ -988,9 +999,59 @@ array_getarray(PyArrayObject *self, PyObject *args)
}
}
+/*
+ * Check whether any of a set of input and output args have a non-default
+ * __array_ufunc__ method. Return 1 if so, 0 if not, and -1 on error.
+ *
+ * This function primarily exists to help ndarray.__array_ufunc__ determine
+ * whether it can support a ufunc (which is the case only if none of the
+ * operands have an override). Thus, unlike in umath/override.c, the
+ * actual overrides are not needed and one can stop looking once one is found.
+ */
+static int
+any_array_ufunc_overrides(PyObject *args, PyObject *kwds)
+{
+ int i;
+ int nin, nout;
+ PyObject *out_kwd_obj;
+ PyObject *fast;
+ PyObject **in_objs, **out_objs;
-static PyObject *
-array_ufunc(PyArrayObject *self, PyObject *args, PyObject *kwds)
+ /* check inputs */
+ nin = PyTuple_Size(args);
+ if (nin < 0) {
+ return -1;
+ }
+ fast = PySequence_Fast(args, "Could not convert object to sequence");
+ if (fast == NULL) {
+ return -1;
+ }
+ in_objs = PySequence_Fast_ITEMS(fast);
+ for (i = 0; i < nin; ++i) {
+ if (PyUFunc_HasOverride(in_objs[i])) {
+ Py_DECREF(fast);
+ return 1;
+ }
+ }
+ Py_DECREF(fast);
+ /* check outputs, if any */
+ nout = PyUFuncOverride_GetOutObjects(kwds, &out_kwd_obj, &out_objs);
+ if (nout < 0) {
+ return -1;
+ }
+ for (i = 0; i < nout; i++) {
+ if (PyUFunc_HasOverride(out_objs[i])) {
+ Py_DECREF(out_kwd_obj);
+ return 1;
+ }
+ }
+ Py_DECREF(out_kwd_obj);
+ return 0;
+}
+
+
+NPY_NO_EXPORT PyObject *
+array_ufunc(PyArrayObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
PyObject *ufunc, *method_name, *normal_args, *ufunc_method;
PyObject *result = NULL;
@@ -1009,7 +1070,7 @@ array_ufunc(PyArrayObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
/* ndarray cannot handle overrides itself */
- has_override = PyUFunc_HasOverride(normal_args, kwds);
+ has_override = any_array_ufunc_overrides(normal_args, kwds);
if (has_override < 0) {
goto cleanup;
}
@@ -1038,13 +1099,29 @@ cleanup:
return result;
}
-
static PyObject *
-array_function(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_function(PyArrayObject *NPY_UNUSED(self), PyObject *c_args, PyObject *c_kwds)
{
- NPY_FORWARD_NDARRAY_METHOD("_array_function");
-}
+ PyObject *func, *types, *args, *kwargs, *result;
+ static char *kwlist[] = {"func", "types", "args", "kwargs", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(
+ c_args, c_kwds, "OOOO:__array_function__", kwlist,
+ &func, &types, &args, &kwargs)) {
+ return NULL;
+ }
+ types = PySequence_Fast(
+ types,
+ "types argument to ndarray.__array_function__ must be iterable");
+ if (types == NULL) {
+ return NULL;
+ }
+
+ result = array_function_method_impl(func, types, args, kwargs);
+ Py_DECREF(types);
+ return result;
+}
static PyObject *
array_copy(PyArrayObject *self, PyObject *args, PyObject *kwds)
@@ -1102,7 +1179,7 @@ array_resize(PyArrayObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
- ret = PyArray_Resize(self, &newshape, refcheck, NPY_CORDER);
+ ret = PyArray_Resize(self, &newshape, refcheck, NPY_ANYORDER);
npy_free_cache_dim_obj(newshape);
if (ret == NULL) {
return NULL;
@@ -1314,6 +1391,7 @@ array_argsort(PyArrayObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
newd = PyArray_DescrNew(saved);
+ Py_DECREF(newd->names);
newd->names = new_name;
((PyArrayObject_fields *)self)->descr = newd;
}
@@ -1368,6 +1446,7 @@ array_argpartition(PyArrayObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
newd = PyArray_DescrNew(saved);
+ Py_DECREF(newd->names);
newd->names = new_name;
((PyArrayObject_fields *)self)->descr = newd;
}
@@ -1471,7 +1550,6 @@ array_deepcopy(PyArrayObject *self, PyObject *args)
copy = PyImport_ImportModule("copy");
if (copy == NULL) {
Py_DECREF(copied_array);
- Py_DECREF(copy);
return NULL;
}
deepcopy = PyObject_GetAttrString(copy, "deepcopy");
@@ -1619,6 +1697,8 @@ array_reduce(PyArrayObject *self, PyObject *NPY_UNUSED(args))
Notice because Python does not describe a mechanism to write
raw data to the pickle, this performs a copy to a string first
+ This issue is now addressed in protocol 5, where a buffer is serialized
+ instead of a string,
*/
state = PyTuple_New(5);
@@ -1652,6 +1732,153 @@ array_reduce(PyArrayObject *self, PyObject *NPY_UNUSED(args))
}
static PyObject *
+array_reduce_ex_regular(PyArrayObject *self, int NPY_UNUSED(protocol))
+{
+ PyObject *subclass_array_reduce = NULL;
+ PyObject *ret;
+
+ /* We do not call array_reduce directly but instead lookup and call
+ * the __reduce__ method to make sure that it's possible to customize
+ * pickling in sub-classes. */
+ subclass_array_reduce = PyObject_GetAttrString((PyObject *)self,
+ "__reduce__");
+ if (subclass_array_reduce == NULL) {
+ return NULL;
+ }
+ ret = PyObject_CallObject(subclass_array_reduce, NULL);
+ Py_DECREF(subclass_array_reduce);
+ return ret;
+}
+
+static PyObject *
+array_reduce_ex_picklebuffer(PyArrayObject *self, int protocol)
+{
+ PyObject *numeric_mod = NULL, *from_buffer_func = NULL;
+ PyObject *pickle_module = NULL, *picklebuf_class = NULL;
+ PyObject *picklebuf_args = NULL;
+ PyObject *buffer = NULL, *transposed_array = NULL;
+ PyArray_Descr *descr = NULL;
+ char order;
+
+ descr = PyArray_DESCR(self);
+
+ /* if the python version is below 3.8, the pickle module does not provide
+ * built-in support for protocol 5. We try importing the pickle5
+ * backport instead */
+#if PY_VERSION_HEX >= 0x03080000
+ /* we expect protocol 5 to be available in Python 3.8 */
+ pickle_module = PyImport_ImportModule("pickle");
+#elif PY_VERSION_HEX >= 0x03060000
+ pickle_module = PyImport_ImportModule("pickle5");
+ if (pickle_module == NULL) {
+ /* for protocol 5, raise a clear ImportError if pickle5 is not found
+ */
+ PyErr_SetString(PyExc_ImportError, "Using pickle protocol 5 "
+ "requires the pickle5 module for Python >=3.6 and <3.8");
+ return NULL;
+ }
+#else
+ PyErr_SetString(PyExc_ValueError, "pickle protocol 5 is not available "
+ "for Python < 3.6");
+ return NULL;
+#endif
+ if (pickle_module == NULL){
+ return NULL;
+ }
+ picklebuf_class = PyObject_GetAttrString(pickle_module, "PickleBuffer");
+ Py_DECREF(pickle_module);
+ if (picklebuf_class == NULL) {
+ return NULL;
+ }
+
+ /* Construct a PickleBuffer of the array */
+
+ if (!PyArray_IS_C_CONTIGUOUS((PyArrayObject*) self) &&
+ PyArray_IS_F_CONTIGUOUS((PyArrayObject*) self)) {
+ /* if the array if Fortran-contiguous and not C-contiguous,
+ * the PickleBuffer instance will hold a view on the transpose
+ * of the initial array, that is C-contiguous. */
+ order = 'F';
+ transposed_array = PyArray_Transpose((PyArrayObject*)self, NULL);
+ picklebuf_args = Py_BuildValue("(N)", transposed_array);
+ }
+ else {
+ order = 'C';
+ picklebuf_args = Py_BuildValue("(O)", self);
+ }
+ if (picklebuf_args == NULL) {
+ Py_DECREF(picklebuf_class);
+ return NULL;
+ }
+
+ buffer = PyObject_CallObject(picklebuf_class, picklebuf_args);
+ Py_DECREF(picklebuf_class);
+ Py_DECREF(picklebuf_args);
+ if (buffer == NULL) {
+ /* Some arrays may refuse to export a buffer, in which case
+ * just fall back on regular __reduce_ex__ implementation
+ * (gh-12745).
+ */
+ PyErr_Clear();
+ return array_reduce_ex_regular(self, protocol);
+ }
+
+ /* Get the _frombuffer() function for reconstruction */
+
+ numeric_mod = PyImport_ImportModule("numpy.core.numeric");
+ if (numeric_mod == NULL) {
+ Py_DECREF(buffer);
+ return NULL;
+ }
+ from_buffer_func = PyObject_GetAttrString(numeric_mod,
+ "_frombuffer");
+ Py_DECREF(numeric_mod);
+ if (from_buffer_func == NULL) {
+ Py_DECREF(buffer);
+ return NULL;
+ }
+
+ return Py_BuildValue("N(NONN)",
+ from_buffer_func, buffer, (PyObject *)descr,
+ PyObject_GetAttrString((PyObject *)self, "shape"),
+ PyUnicode_FromStringAndSize(&order, 1));
+}
+
+static PyObject *
+array_reduce_ex(PyArrayObject *self, PyObject *args)
+{
+ int protocol;
+ PyArray_Descr *descr = NULL;
+
+ if (!PyArg_ParseTuple(args, "i", &protocol)) {
+ return NULL;
+ }
+
+ descr = PyArray_DESCR(self);
+ if ((protocol < 5) ||
+ (!PyArray_IS_C_CONTIGUOUS((PyArrayObject*)self) &&
+ !PyArray_IS_F_CONTIGUOUS((PyArrayObject*)self)) ||
+ PyDataType_FLAGCHK(descr, NPY_ITEM_HASOBJECT) ||
+ (PyType_IsSubtype(((PyObject*)self)->ob_type, &PyArray_Type) &&
+ ((PyObject*)self)->ob_type != &PyArray_Type) ||
+ descr->elsize == 0) {
+ /* The PickleBuffer class from version 5 of the pickle protocol
+ * can only be used for arrays backed by a contiguous data buffer.
+ * For all other cases we fallback to the generic array_reduce
+ * method that involves using a temporary bytes allocation. */
+ return array_reduce_ex_regular(self, protocol);
+ }
+ else if (protocol == 5) {
+ return array_reduce_ex_picklebuffer(self, protocol);
+ }
+ else {
+ PyErr_Format(PyExc_ValueError,
+ "__reduce_ex__ called with protocol > 5");
+ return NULL;
+ }
+}
+
+static PyObject *
array_setstate(PyArrayObject *self, PyObject *args)
{
PyObject *shape;
@@ -1785,12 +2012,14 @@ array_setstate(PyArrayObject *self, PyObject *args)
fa->nd = nd;
if (nd > 0) {
- fa->dimensions = npy_alloc_cache_dim(3*nd);
+ fa->dimensions = npy_alloc_cache_dim(2 * nd);
if (fa->dimensions == NULL) {
return PyErr_NoMemory();
}
fa->strides = PyArray_DIMS(self) + nd;
- memcpy(PyArray_DIMS(self), dimensions, sizeof(npy_intp)*nd);
+ if (nd) {
+ memcpy(PyArray_DIMS(self), dimensions, sizeof(npy_intp)*nd);
+ }
_array_fill_strides(PyArray_STRIDES(self), dimensions, nd,
PyArray_DESCR(self)->elsize,
(is_f_order ? NPY_ARRAY_F_CONTIGUOUS :
@@ -1810,10 +2039,12 @@ array_setstate(PyArrayObject *self, PyObject *args)
if (!IsAligned(self) || swap || (len <= 1000)) {
#endif
npy_intp num = PyArray_NBYTES(self);
+ if (num == 0) {
+ Py_DECREF(rawdata);
+ Py_RETURN_NONE;
+ }
fa->data = PyDataMem_NEW(num);
if (PyArray_DATA(self) == NULL) {
- fa->nd = 0;
- npy_free_cache_dim_array(self);
Py_DECREF(rawdata);
return PyErr_NoMemory();
}
@@ -1824,7 +2055,9 @@ array_setstate(PyArrayObject *self, PyObject *args)
PyArray_DESCR(self)->elsize,
datastr, PyArray_DESCR(self)->elsize,
numels, 1, self);
- if (!PyArray_ISEXTENDED(self)) {
+ if (!(PyArray_ISEXTENDED(self) ||
+ PyArray_DESCR(self)->metadata ||
+ PyArray_DESCR(self)->c_metadata)) {
fa->descr = PyArray_DescrFromType(
PyArray_DESCR(self)->type_num);
}
@@ -1853,11 +2086,13 @@ array_setstate(PyArrayObject *self, PyObject *args)
}
}
else {
- fa->data = PyDataMem_NEW(PyArray_NBYTES(self));
+ npy_intp num = PyArray_NBYTES(self);
+ int elsize = PyArray_DESCR(self)->elsize;
+ if (num == 0 || elsize == 0) {
+ Py_RETURN_NONE;
+ }
+ fa->data = PyDataMem_NEW(num);
if (PyArray_DATA(self) == NULL) {
- fa->nd = 0;
- fa->data = PyDataMem_NEW(PyArray_DESCR(self)->elsize);
- npy_free_cache_dim_array(self);
return PyErr_NoMemory();
}
if (PyDataType_FLAGCHK(PyArray_DESCR(self), NPY_NEEDS_INIT)) {
@@ -1879,37 +2114,22 @@ array_setstate(PyArrayObject *self, PyObject *args)
NPY_NO_EXPORT int
PyArray_Dump(PyObject *self, PyObject *file, int protocol)
{
- PyObject *cpick = NULL;
+ static PyObject *method = NULL;
PyObject *ret;
- if (protocol < 0) {
- protocol = 2;
- }
-
-#if defined(NPY_PY3K)
- cpick = PyImport_ImportModule("pickle");
-#else
- cpick = PyImport_ImportModule("cPickle");
-#endif
- if (cpick == NULL) {
+ npy_cache_import("numpy.core._methods", "_dump", &method);
+ if (method == NULL) {
return -1;
}
- if (PyBytes_Check(file) || PyUnicode_Check(file)) {
- file = npy_PyFile_OpenFile(file, "wb");
- if (file == NULL) {
- Py_DECREF(cpick);
- return -1;
- }
+ if (protocol < 0) {
+ ret = PyObject_CallFunction(method, "OO", self, file);
}
else {
- Py_INCREF(file);
+ ret = PyObject_CallFunction(method, "OOi", self, file, protocol);
}
- ret = PyObject_CallMethod(cpick, "dump", "OOi", self, file, protocol);
- Py_XDECREF(ret);
- Py_DECREF(file);
- Py_DECREF(cpick);
- if (PyErr_Occurred()) {
+ if (ret == NULL) {
return -1;
}
+ Py_DECREF(ret);
return 0;
}
@@ -1917,49 +2137,31 @@ PyArray_Dump(PyObject *self, PyObject *file, int protocol)
NPY_NO_EXPORT PyObject *
PyArray_Dumps(PyObject *self, int protocol)
{
- PyObject *cpick = NULL;
- PyObject *ret;
+ static PyObject *method = NULL;
+ npy_cache_import("numpy.core._methods", "_dumps", &method);
+ if (method == NULL) {
+ return NULL;
+ }
if (protocol < 0) {
- protocol = 2;
+ return PyObject_CallFunction(method, "O", self);
}
-#if defined(NPY_PY3K)
- cpick = PyImport_ImportModule("pickle");
-#else
- cpick = PyImport_ImportModule("cPickle");
-#endif
- if (cpick == NULL) {
- return NULL;
+ else {
+ return PyObject_CallFunction(method, "Oi", self, protocol);
}
- ret = PyObject_CallMethod(cpick, "dumps", "Oi", self, protocol);
- Py_DECREF(cpick);
- return ret;
}
static PyObject *
-array_dump(PyArrayObject *self, PyObject *args)
+array_dump(PyArrayObject *self, PyObject *args, PyObject *kwds)
{
- PyObject *file = NULL;
- int ret;
-
- if (!PyArg_ParseTuple(args, "O:dump", &file)) {
- return NULL;
- }
- ret = PyArray_Dump((PyObject *)self, file, 2);
- if (ret < 0) {
- return NULL;
- }
- Py_RETURN_NONE;
+ NPY_FORWARD_NDARRAY_METHOD("_dump");
}
static PyObject *
-array_dumps(PyArrayObject *self, PyObject *args)
+array_dumps(PyArrayObject *self, PyObject *args, PyObject *kwds)
{
- if (!PyArg_ParseTuple(args, "")) {
- return NULL;
- }
- return PyArray_Dumps((PyObject *)self, 2);
+ NPY_FORWARD_NDARRAY_METHOD("_dumps");
}
@@ -2182,28 +2384,13 @@ array_trace(PyArrayObject *self, PyObject *args, PyObject *kwds)
static PyObject *
array_clip(PyArrayObject *self, PyObject *args, PyObject *kwds)
{
- PyObject *min = NULL, *max = NULL;
- PyArrayObject *out = NULL;
- static char *kwlist[] = {"min", "max", "out", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO&:clip", kwlist,
- &min,
- &max,
- PyArray_OutputConverter, &out)) {
- return NULL;
- }
- if (max == NULL && min == NULL) {
- PyErr_SetString(PyExc_ValueError, "One of max or min must be given.");
- return NULL;
- }
- return PyArray_Return((PyArrayObject *)PyArray_Clip(self, min, max, out));
+ NPY_FORWARD_NDARRAY_METHOD("_clip");
}
static PyObject *
array_conjugate(PyArrayObject *self, PyObject *args)
{
-
PyArrayObject *out = NULL;
if (!PyArg_ParseTuple(args, "|O&:conjugate",
PyArray_OutputConverter,
@@ -2329,7 +2516,24 @@ array_setflags(PyArrayObject *self, PyObject *args, PyObject *kwds)
if (write_flag != Py_None) {
if (PyObject_IsTrue(write_flag)) {
if (_IsWriteable(self)) {
+ /*
+ * _IsWritable (and PyArray_UpdateFlags) allows flipping this,
+ * although the C-Api user who created the array may have
+ * chosen to make it non-writable for a good reason, so
+ * deprecate.
+ */
+ if ((PyArray_BASE(self) == NULL) &&
+ !PyArray_CHKFLAGS(self, NPY_ARRAY_OWNDATA) &&
+ !PyArray_CHKFLAGS(self, NPY_ARRAY_WRITEABLE)) {
+ /* 2017-05-03, NumPy 1.17.0 */
+ if (DEPRECATE("making a non-writeable array writeable "
+ "is deprecated for arrays without a base "
+ "which do not own their data.") < 0) {
+ return NULL;
+ }
+ }
PyArray_ENABLEFLAGS(self, NPY_ARRAY_WRITEABLE);
+ PyArray_CLEARFLAGS(self, NPY_ARRAY_WARN_ON_WRITE);
}
else {
fa->flags = flagback;
@@ -2342,9 +2546,9 @@ array_setflags(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
else {
PyArray_CLEARFLAGS(self, NPY_ARRAY_WRITEABLE);
+ PyArray_CLEARFLAGS(self, NPY_ARRAY_WARN_ON_WRITE);
}
}
-
Py_RETURN_NONE;
}
@@ -2524,15 +2728,18 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
{"__reduce__",
(PyCFunction) array_reduce,
METH_VARARGS, NULL},
+ {"__reduce_ex__",
+ (PyCFunction) array_reduce_ex,
+ METH_VARARGS, NULL},
{"__setstate__",
(PyCFunction) array_setstate,
METH_VARARGS, NULL},
{"dumps",
(PyCFunction) array_dumps,
- METH_VARARGS, NULL},
+ METH_VARARGS | METH_KEYWORDS, NULL},
{"dump",
(PyCFunction) array_dump,
- METH_VARARGS, NULL},
+ METH_VARARGS | METH_KEYWORDS, NULL},
{"__complex__",
(PyCFunction) array_complex,
diff --git a/numpy/core/src/multiarray/methods.h b/numpy/core/src/multiarray/methods.h
index 7bf87f42d..7a9a24a00 100644
--- a/numpy/core/src/multiarray/methods.h
+++ b/numpy/core/src/multiarray/methods.h
@@ -1,9 +1,36 @@
#ifndef _NPY_ARRAY_METHODS_H_
#define _NPY_ARRAY_METHODS_H_
+#include "npy_import.h"
+
extern NPY_NO_EXPORT PyMethodDef array_methods[];
NPY_NO_EXPORT const char *
npy_casting_to_string(NPY_CASTING casting);
+/*
+ * Pathlib support, takes a borrowed reference and returns a new one.
+ * The new object may be the same as the old.
+ */
+static inline PyObject *
+NpyPath_PathlikeToFspath(PyObject *file)
+{
+ static PyObject *os_PathLike = NULL;
+ static PyObject *os_fspath = NULL;
+ npy_cache_import("numpy.compat", "os_PathLike", &os_PathLike);
+ if (os_PathLike == NULL) {
+ return NULL;
+ }
+ npy_cache_import("numpy.compat", "os_fspath", &os_fspath);
+ if (os_fspath == NULL) {
+ return NULL;
+ }
+
+ if (!PyObject_IsInstance(file, os_PathLike)) {
+ Py_INCREF(file);
+ return file;
+ }
+ return PyObject_CallFunctionObjArgs(os_fspath, file, NULL);
+}
+
#endif
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index 8f782cff6..441567049 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -34,6 +34,7 @@
NPY_NO_EXPORT int NPY_NUMUSERTYPES = 0;
/* Internal APIs */
+#include "arrayfunction_override.h"
#include "arraytypes.h"
#include "arrayobject.h"
#include "hashdescr.h"
@@ -72,10 +73,10 @@ NPY_NO_EXPORT int NPY_NUMUSERTYPES = 0;
*****************************************************************************
*/
#include "funcs.inc"
-#include "loops.h"
#include "umathmodule.h"
NPY_NO_EXPORT int initscalarmath(PyObject *);
+NPY_NO_EXPORT int set_matmul_flags(PyObject *d); /* in ufunc_object.c */
/*
* global variable to determine if legacy printing is enabled, accessible from
@@ -129,7 +130,7 @@ PyArray_GetPriority(PyObject *obj, double default_)
* Multiply a List of ints
*/
NPY_NO_EXPORT int
-PyArray_MultiplyIntList(int *l1, int n)
+PyArray_MultiplyIntList(int const *l1, int n)
{
int s = 1;
@@ -143,7 +144,7 @@ PyArray_MultiplyIntList(int *l1, int n)
* Multiply a List
*/
NPY_NO_EXPORT npy_intp
-PyArray_MultiplyList(npy_intp *l1, int n)
+PyArray_MultiplyList(npy_intp const *l1, int n)
{
npy_intp s = 1;
@@ -179,7 +180,7 @@ PyArray_OverflowMultiplyList(npy_intp *l1, int n)
* Produce a pointer into array
*/
NPY_NO_EXPORT void *
-PyArray_GetPtr(PyArrayObject *obj, npy_intp* ind)
+PyArray_GetPtr(PyArrayObject *obj, npy_intp const* ind)
{
int n = PyArray_NDIM(obj);
npy_intp *strides = PyArray_STRIDES(obj);
@@ -195,7 +196,7 @@ PyArray_GetPtr(PyArrayObject *obj, npy_intp* ind)
* Compare Lists
*/
NPY_NO_EXPORT int
-PyArray_CompareLists(npy_intp *l1, npy_intp *l2, int n)
+PyArray_CompareLists(npy_intp const *l1, npy_intp const *l2, int n)
{
int i;
@@ -272,7 +273,9 @@ PyArray_AsCArray(PyObject **op, void *ptr, npy_intp *dims, int nd,
}
*((char ****)ptr) = ptr3;
}
- memcpy(dims, PyArray_DIMS(ap), nd*sizeof(npy_intp));
+ if (nd) {
+ memcpy(dims, PyArray_DIMS(ap), nd*sizeof(npy_intp));
+ }
*op = (PyObject *)ap;
return 0;
}
@@ -283,45 +286,26 @@ PyArray_AsCArray(PyObject **op, void *ptr, npy_intp *dims, int nd,
* Convert to a 1D C-array
*/
NPY_NO_EXPORT int
-PyArray_As1D(PyObject **op, char **ptr, int *d1, int typecode)
+PyArray_As1D(PyObject **NPY_UNUSED(op), char **NPY_UNUSED(ptr),
+ int *NPY_UNUSED(d1), int NPY_UNUSED(typecode))
{
- npy_intp newd1;
- PyArray_Descr *descr;
- static const char msg[] = "PyArray_As1D: use PyArray_AsCArray.";
-
/* 2008-07-14, 1.5 */
- if (DEPRECATE(msg) < 0) {
- return -1;
- }
- descr = PyArray_DescrFromType(typecode);
- if (PyArray_AsCArray(op, (void *)ptr, &newd1, 1, descr) == -1) {
- return -1;
- }
- *d1 = (int) newd1;
- return 0;
+ PyErr_SetString(PyExc_NotImplementedError,
+ "PyArray_As1D: use PyArray_AsCArray.");
+ return -1;
}
/*NUMPY_API
* Convert to a 2D C-array
*/
NPY_NO_EXPORT int
-PyArray_As2D(PyObject **op, char ***ptr, int *d1, int *d2, int typecode)
+PyArray_As2D(PyObject **NPY_UNUSED(op), char ***NPY_UNUSED(ptr),
+ int *NPY_UNUSED(d1), int *NPY_UNUSED(d2), int NPY_UNUSED(typecode))
{
- npy_intp newdims[2];
- PyArray_Descr *descr;
- static const char msg[] = "PyArray_As1D: use PyArray_AsCArray.";
-
/* 2008-07-14, 1.5 */
- if (DEPRECATE(msg) < 0) {
- return -1;
- }
- descr = PyArray_DescrFromType(typecode);
- if (PyArray_AsCArray(op, (void *)ptr, newdims, 2, descr) == -1) {
- return -1;
- }
- *d1 = (int ) newdims[0];
- *d2 = (int ) newdims[1];
- return 0;
+ PyErr_SetString(PyExc_NotImplementedError,
+ "PyArray_As2D: use PyArray_AsCArray.");
+ return -1;
}
/* End Deprecated */
@@ -408,9 +392,12 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis,
npy_intp *arr_shape;
if (PyArray_NDIM(arrays[iarrays]) != ndim) {
- PyErr_SetString(PyExc_ValueError,
- "all the input arrays must have same "
- "number of dimensions");
+ PyErr_Format(PyExc_ValueError,
+ "all the input arrays must have same number of "
+ "dimensions, but the array at index %d has %d "
+ "dimension(s) and the array at index %d has %d "
+ "dimension(s)",
+ 0, ndim, iarrays, PyArray_NDIM(arrays[iarrays]));
return NULL;
}
arr_shape = PyArray_SHAPE(arrays[iarrays]);
@@ -422,10 +409,12 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis,
}
/* Validate that the rest of the dimensions match */
else if (shape[idim] != arr_shape[idim]) {
- PyErr_SetString(PyExc_ValueError,
- "all the input array dimensions "
- "except for the concatenation axis "
- "must match exactly");
+ PyErr_Format(PyExc_ValueError,
+ "all the input array dimensions for the "
+ "concatenation axis must match exactly, but "
+ "along dimension %d, the array at index %d has "
+ "size %d and the array at index %d has size %d",
+ idim, 0, shape[idim], iarrays, arr_shape[idim]);
return NULL;
}
}
@@ -833,7 +822,10 @@ PyArray_InnerProduct(PyObject *op1, PyObject *op2)
typenum = PyArray_ObjectType(op2, typenum);
typec = PyArray_DescrFromType(typenum);
if (typec == NULL) {
- PyErr_SetString(PyExc_TypeError, "Cannot find a common data type.");
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError,
+ "Cannot find a common data type.");
+ }
goto fail;
}
@@ -919,7 +911,10 @@ PyArray_MatrixProduct2(PyObject *op1, PyObject *op2, PyArrayObject* out)
typenum = PyArray_ObjectType(op2, typenum);
typec = PyArray_DescrFromType(typenum);
if (typec == NULL) {
- PyErr_SetString(PyExc_TypeError, "Cannot find a common data type.");
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError,
+ "Cannot find a common data type.");
+ }
return NULL;
}
@@ -976,7 +971,7 @@ PyArray_MatrixProduct2(PyObject *op1, PyObject *op2, PyArrayObject* out)
for (i = 0; i < PyArray_NDIM(ap2) - 2; i++) {
dimensions[j++] = PyArray_DIMS(ap2)[i];
}
- if(PyArray_NDIM(ap2) > 1) {
+ if (PyArray_NDIM(ap2) > 1) {
dimensions[j++] = PyArray_DIMS(ap2)[PyArray_NDIM(ap2)-1];
}
@@ -1312,7 +1307,7 @@ PyArray_Correlate2(PyObject *op1, PyObject *op2, int mode)
*/
if (inverted) {
st = _pyarray_revert(ret);
- if(st) {
+ if (st) {
goto clean_ret;
}
}
@@ -1359,7 +1354,7 @@ PyArray_Correlate(PyObject *op1, PyObject *op2, int mode)
}
ret = _pyarray_correlate(ap1, ap2, typenum, mode, &unused);
- if(ret == NULL) {
+ if (ret == NULL) {
goto fail;
}
Py_DECREF(ap1);
@@ -1567,7 +1562,8 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
PyArrayObject *oparr = NULL, *ret = NULL;
npy_bool subok = NPY_FALSE;
npy_bool copy = NPY_TRUE;
- int ndmin = 0, nd;
+ int nd;
+ npy_intp ndmin = 0;
PyArray_Descr *type = NULL;
PyArray_Descr *oldtype = NULL;
NPY_ORDER order = NPY_KEEPORDER;
@@ -1638,17 +1634,15 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
}
}
- /* copy=False with default dtype, order and ndim */
- if (STRIDING_OK(oparr, order)) {
- ret = oparr;
- Py_INCREF(ret);
- goto finish;
- }
+ /* copy=False with default dtype, order (any is OK) and ndim */
+ ret = oparr;
+ Py_INCREF(ret);
+ goto finish;
}
}
full_path:
- if(!PyArg_ParseTupleAndKeywords(args, kws, "O|O&O&O&O&i:array", kwd,
+ if (!PyArg_ParseTupleAndKeywords(args, kws, "O|O&O&O&O&i:array", kwd,
&op,
PyArray_DescrConverter2, &type,
PyArray_BoolConverter, &copy,
@@ -1746,7 +1740,7 @@ static PyObject *
array_copyto(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"dst","src","casting","where",NULL};
+ static char *kwlist[] = {"dst", "src", "casting", "where", NULL};
PyObject *wheremask_in = NULL;
PyArrayObject *dst = NULL, *src = NULL, *wheremask = NULL;
NPY_CASTING casting = NPY_SAME_KIND_CASTING;
@@ -1791,7 +1785,7 @@ static PyObject *
array_empty(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"shape","dtype","order",NULL};
+ static char *kwlist[] = {"shape", "dtype", "order", NULL};
PyArray_Descr *typecode = NULL;
PyArray_Dims shape = {NULL, 0};
NPY_ORDER order = NPY_CORDER;
@@ -1834,23 +1828,28 @@ static PyObject *
array_empty_like(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"prototype","dtype","order","subok",NULL};
+ static char *kwlist[] = {"prototype", "dtype", "order", "subok", "shape", NULL};
PyArrayObject *prototype = NULL;
PyArray_Descr *dtype = NULL;
NPY_ORDER order = NPY_KEEPORDER;
PyArrayObject *ret = NULL;
int subok = 1;
+ PyArray_Dims shape = {NULL, 0};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&i:empty_like", kwlist,
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&iO&:empty_like", kwlist,
&PyArray_Converter, &prototype,
&PyArray_DescrConverter2, &dtype,
&PyArray_OrderConverter, &order,
- &subok)) {
+ &subok,
+ &PyArray_IntpConverter, &shape)) {
goto fail;
}
/* steals the reference to dtype if it's not NULL */
- ret = (PyArrayObject *)PyArray_NewLikeArray(prototype,
- order, dtype, subok);
+ ret = (PyArrayObject *)PyArray_NewLikeArrayWithShape(prototype, order, dtype,
+ shape.len, shape.ptr, subok);
+ if (!ret) {
+ goto fail;
+ }
Py_DECREF(prototype);
return (PyObject *)ret;
@@ -1869,7 +1868,7 @@ static PyObject *
array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"dtype","obj", NULL};
+ static char *kwlist[] = {"dtype", "obj", NULL};
PyArray_Descr *typecode;
PyObject *obj = NULL, *tmpobj = NULL;
int alloc = 0;
@@ -1945,7 +1944,7 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
static PyObject *
array_zeros(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"shape","dtype","order",NULL};
+ static char *kwlist[] = {"shape", "dtype", "order", NULL};
PyArray_Descr *typecode = NULL;
PyArray_Dims shape = {NULL, 0};
NPY_ORDER order = NPY_CORDER;
@@ -2043,54 +2042,81 @@ array_fromstring(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds
static PyObject *
array_fromfile(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds)
{
- PyObject *file = NULL, *ret;
+ PyObject *file = NULL, *ret = NULL;
+ PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL;
char *sep = "";
Py_ssize_t nin = -1;
- static char *kwlist[] = {"file", "dtype", "count", "sep", NULL};
+ static char *kwlist[] = {"file", "dtype", "count", "sep", "offset", NULL};
PyArray_Descr *type = NULL;
int own;
- npy_off_t orig_pos = 0;
+ npy_off_t orig_pos = 0, offset = 0;
FILE *fp;
if (!PyArg_ParseTupleAndKeywords(args, keywds,
- "O|O&" NPY_SSIZE_T_PYFMT "s:fromfile", kwlist,
- &file, PyArray_DescrConverter, &type, &nin, &sep)) {
+ "O|O&" NPY_SSIZE_T_PYFMT "s" NPY_OFF_T_PYFMT ":fromfile", kwlist,
+ &file, PyArray_DescrConverter, &type, &nin, &sep, &offset)) {
+ Py_XDECREF(type);
+ return NULL;
+ }
+
+ file = NpyPath_PathlikeToFspath(file);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ if (offset != 0 && strcmp(sep, "") != 0) {
+ PyErr_SetString(PyExc_TypeError, "'offset' argument only permitted for binary files");
Py_XDECREF(type);
+ Py_DECREF(file);
return NULL;
}
if (PyString_Check(file) || PyUnicode_Check(file)) {
- file = npy_PyFile_OpenFile(file, "rb");
+ Py_SETREF(file, npy_PyFile_OpenFile(file, "rb"));
if (file == NULL) {
+ Py_XDECREF(type);
return NULL;
}
own = 1;
}
else {
- Py_INCREF(file);
own = 0;
}
fp = npy_PyFile_Dup2(file, "rb", &orig_pos);
if (fp == NULL) {
Py_DECREF(file);
+ Py_XDECREF(type);
return NULL;
}
+ if (npy_fseek(fp, offset, SEEK_CUR) != 0) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ goto cleanup;
+ }
if (type == NULL) {
type = PyArray_DescrFromType(NPY_DEFAULT_TYPE);
}
ret = PyArray_FromFile(fp, type, (npy_intp) nin, sep);
+ /* If an exception is thrown in the call to PyArray_FromFile
+ * we need to clear it, and restore it later to ensure that
+ * we can cleanup the duplicated file descriptor properly.
+ */
+cleanup:
+ PyErr_Fetch(&err_type, &err_value, &err_traceback);
if (npy_PyFile_DupClose2(file, fp, orig_pos) < 0) {
+ npy_PyErr_ChainExceptions(err_type, err_value, err_traceback);
goto fail;
}
if (own && npy_PyFile_CloseFile(file) < 0) {
+ npy_PyErr_ChainExceptions(err_type, err_value, err_traceback);
goto fail;
}
+ PyErr_Restore(err_type, err_value, err_traceback);
Py_DECREF(file);
return ret;
fail:
Py_DECREF(file);
- Py_DECREF(ret);
+ Py_XDECREF(ret);
return NULL;
}
@@ -2303,154 +2329,6 @@ fail:
return NULL;
}
-
-
-/*
- * matmul
- *
- * Implements the protocol used by the '@' operator defined in PEP 364.
- * Not in the NUMPY API at this time, maybe later.
- *
- *
- * in1: Left hand side operand
- * in2: Right hand side operand
- * out: Either NULL, or an array into which the output should be placed.
- *
- * Returns NULL on error.
- */
-static PyObject *
-array_matmul(PyObject *NPY_UNUSED(m), PyObject *args, PyObject* kwds)
-{
- PyObject *in1, *in2, *out = NULL;
- char* kwlist[] = {"a", "b", "out", NULL };
- PyArrayObject *ap1, *ap2, *ret = NULL;
- NPY_ORDER order = NPY_KEEPORDER;
- NPY_CASTING casting = NPY_SAFE_CASTING;
- PyArray_Descr *dtype;
- int nd1, nd2, typenum;
- char *subscripts;
- PyArrayObject *ops[2];
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O:matmul", kwlist,
- &in1, &in2, &out)) {
- return NULL;
- }
-
- if (out != NULL) {
- if (out == Py_None) {
- out = NULL;
- }
- else if (!PyArray_Check(out)) {
- PyErr_SetString(PyExc_TypeError, "'out' must be an array");
- return NULL;
- }
- }
-
- dtype = PyArray_DescrFromObject(in1, NULL);
- dtype = PyArray_DescrFromObject(in2, dtype);
- if (dtype == NULL) {
- PyErr_SetString(PyExc_ValueError, "Cannot find a common data type.");
- return NULL;
- }
- typenum = dtype->type_num;
-
- if (typenum == NPY_OBJECT) {
- /* matmul is not currently implemented for object arrays */
- PyErr_SetString(PyExc_TypeError,
- "Object arrays are not currently supported");
- Py_DECREF(dtype);
- return NULL;
- }
-
- ap1 = (PyArrayObject *)PyArray_FromAny(in1, dtype, 0, 0,
- NPY_ARRAY_ALIGNED, NULL);
- if (ap1 == NULL) {
- return NULL;
- }
-
- Py_INCREF(dtype);
- ap2 = (PyArrayObject *)PyArray_FromAny(in2, dtype, 0, 0,
- NPY_ARRAY_ALIGNED, NULL);
- if (ap2 == NULL) {
- Py_DECREF(ap1);
- return NULL;
- }
-
- if (PyArray_NDIM(ap1) == 0 || PyArray_NDIM(ap2) == 0) {
- /* Scalars are rejected */
- PyErr_SetString(PyExc_ValueError,
- "Scalar operands are not allowed, use '*' instead");
- return NULL;
- }
-
- nd1 = PyArray_NDIM(ap1);
- nd2 = PyArray_NDIM(ap2);
-
-#if defined(HAVE_CBLAS)
- if (nd1 <= 2 && nd2 <= 2 &&
- (NPY_DOUBLE == typenum || NPY_CDOUBLE == typenum ||
- NPY_FLOAT == typenum || NPY_CFLOAT == typenum)) {
- return cblas_matrixproduct(typenum, ap1, ap2, (PyArrayObject *)out);
- }
-#endif
-
- /*
- * Use einsum for the stacked cases. This is a quick implementation
- * to avoid setting up the proper iterators. Einsum broadcasts, so
- * we need to check dimensions before the call.
- */
- if (nd1 == 1 && nd2 == 1) {
- /* vector vector */
- if (PyArray_DIM(ap1, 0) != PyArray_DIM(ap2, 0)) {
- dot_alignment_error(ap1, 0, ap2, 0);
- goto fail;
- }
- subscripts = "i, i";
- }
- else if (nd1 == 1) {
- /* vector matrix */
- if (PyArray_DIM(ap1, 0) != PyArray_DIM(ap2, nd2 - 2)) {
- dot_alignment_error(ap1, 0, ap2, nd2 - 2);
- goto fail;
- }
- subscripts = "i, ...ij";
- }
- else if (nd2 == 1) {
- /* matrix vector */
- if (PyArray_DIM(ap1, nd1 - 1) != PyArray_DIM(ap2, 0)) {
- dot_alignment_error(ap1, nd1 - 1, ap2, 0);
- goto fail;
- }
- subscripts = "...i, i";
- }
- else {
- /* matrix * matrix */
- if (PyArray_DIM(ap1, nd1 - 1) != PyArray_DIM(ap2, nd2 - 2)) {
- dot_alignment_error(ap1, nd1 - 1, ap2, nd2 - 2);
- goto fail;
- }
- subscripts = "...ij, ...jk";
- }
- ops[0] = ap1;
- ops[1] = ap2;
- ret = PyArray_EinsteinSum(subscripts, 2, ops, NULL, order, casting,
- (PyArrayObject *)out);
- Py_DECREF(ap1);
- Py_DECREF(ap2);
-
- /* If no output was supplied, possibly convert to a scalar */
- if (ret != NULL && out == NULL) {
- return PyArray_Return((PyArrayObject *)ret);
- }
- return (PyObject *)ret;
-
-fail:
- Py_XDECREF(ap1);
- Py_XDECREF(ap2);
- return NULL;
-}
-
-
static int
einsum_sub_op_from_str(PyObject *args, PyObject **str_obj, char **subscripts,
PyArrayObject **op)
@@ -2622,7 +2500,7 @@ einsum_sub_op_from_lists(PyObject *args,
"operand and a subscripts list to einsum");
return -1;
}
- else if(nop >= NPY_MAXARGS) {
+ else if (nop >= NPY_MAXARGS) {
PyErr_SetString(PyExc_ValueError, "too many operands");
return -1;
}
@@ -2857,7 +2735,7 @@ array_arange(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws) {
static char *kwd[]= {"start", "stop", "step", "dtype", NULL};
PyArray_Descr *typecode = NULL;
- if(!PyArg_ParseTupleAndKeywords(args, kws, "O|OOO&:arange", kwd,
+ if (!PyArg_ParseTupleAndKeywords(args, kws, "O|OOO&:arange", kwd,
&o_start,
&o_stop,
&o_step,
@@ -2895,7 +2773,7 @@ array__get_ndarray_c_version(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObje
{
static char *kwlist[] = {NULL};
- if(!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist )) {
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist )) {
return NULL;
}
return PyInt_FromLong( (long) PyArray_GetNDArrayCVersion() );
@@ -2968,7 +2846,7 @@ array_set_string_function(PyObject *NPY_UNUSED(self), PyObject *args,
int repr = 1;
static char *kwlist[] = {"f", "repr", NULL};
- if(!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:set_string_function", kwlist, &op, &repr)) {
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:set_string_function", kwlist, &op, &repr)) {
return NULL;
}
/* reset the array_repr function to built-in */
@@ -2990,7 +2868,7 @@ array_set_ops_function(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args),
{
PyObject *oldops = NULL;
- if ((oldops = PyArray_GetNumericOps()) == NULL) {
+ if ((oldops = _PyArray_GetNumericOps()) == NULL) {
return NULL;
}
/*
@@ -3000,8 +2878,10 @@ array_set_ops_function(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args),
*/
if (kwds && PyArray_SetNumericOps(kwds) == -1) {
Py_DECREF(oldops);
- PyErr_SetString(PyExc_ValueError,
+ if (PyErr_Occurred() == NULL) {
+ PyErr_SetString(PyExc_ValueError,
"one or more objects not callable");
+ }
return NULL;
}
return oldops;
@@ -3276,7 +3156,7 @@ array_promote_types(PyObject *NPY_UNUSED(dummy), PyObject *args)
PyArray_Descr *d1 = NULL;
PyArray_Descr *d2 = NULL;
PyObject *ret = NULL;
- if(!PyArg_ParseTuple(args, "O&O&:promote_types",
+ if (!PyArg_ParseTuple(args, "O&O&:promote_types",
PyArray_DescrConverter2, &d1, PyArray_DescrConverter2, &d2)) {
goto finish;
}
@@ -3302,7 +3182,7 @@ array_min_scalar_type(PyObject *NPY_UNUSED(dummy), PyObject *args)
PyArrayObject *array;
PyObject *ret = NULL;
- if(!PyArg_ParseTuple(args, "O:min_scalar_type", &array_in)) {
+ if (!PyArg_ParseTuple(args, "O:min_scalar_type", &array_in)) {
return NULL;
}
@@ -3379,12 +3259,13 @@ array_datetime_data(PyObject *NPY_UNUSED(dummy), PyObject *args)
PyArray_Descr *dtype;
PyArray_DatetimeMetaData *meta;
- if(!PyArg_ParseTuple(args, "O&:datetime_data",
+ if (!PyArg_ParseTuple(args, "O&:datetime_data",
PyArray_DescrConverter, &dtype)) {
return NULL;
}
meta = get_datetime_metadata_from_dtype(dtype);
+ Py_DECREF(dtype);
if (meta == NULL) {
return NULL;
}
@@ -3398,7 +3279,7 @@ new_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args)
{
int size;
- if(!PyArg_ParseTuple(args, "i:buffer", &size)) {
+ if (!PyArg_ParseTuple(args, "i:buffer", &size)) {
return NULL;
}
return PyBuffer_New(size);
@@ -3749,6 +3630,7 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type,
if (nargs == -1 || nargs > NPY_MAXARGS) {
PyErr_Format(PyExc_ValueError,
"len(args) must be < %d", NPY_MAXARGS - 1);
+ Py_DECREF(type);
goto err;
}
@@ -3756,6 +3638,7 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type,
for (i = 1; i < nargs; i++) {
PyObject* item = PySequence_GetItem(args, i-1);
if (item == NULL) {
+ Py_DECREF(type);
goto err;
}
broadcast_args[i] = item;
@@ -3764,6 +3647,7 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type,
in_iter = (PyArrayMultiIterObject*)PyArray_MultiIterFromObjects
(broadcast_args, nargs, 0);
if (in_iter == NULL) {
+ Py_DECREF(type);
goto err;
}
n = in_iter->numiter;
@@ -3844,6 +3728,7 @@ _vec_string_no_args(PyArrayObject* char_array,
in_iter = (PyArrayIterObject*)PyArray_IterNew((PyObject*)char_array);
if (in_iter == NULL) {
+ Py_DECREF(type);
goto err;
}
@@ -3897,10 +3782,10 @@ _vec_string_no_args(PyArrayObject* char_array,
}
static PyObject *
-_vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
+_vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUSED(kwds))
{
PyArrayObject* char_array = NULL;
- PyArray_Descr *type = NULL;
+ PyArray_Descr *type;
PyObject* method_name;
PyObject* args_seq = NULL;
@@ -3923,9 +3808,11 @@ _vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
else {
PyErr_SetString(PyExc_TypeError,
"string operation on non-string array");
+ Py_DECREF(type);
goto err;
}
if (method == NULL) {
+ Py_DECREF(type);
goto err;
}
@@ -3937,6 +3824,7 @@ _vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
result = _vec_string_with_args(char_array, type, method, args_seq);
}
else {
+ Py_DECREF(type);
PyErr_SetString(PyExc_TypeError,
"'args' must be a sequence of arguments");
goto err;
@@ -4134,7 +4022,7 @@ array_shares_memory_impl(PyObject *args, PyObject *kwds, Py_ssize_t default_max_
}
else if (result == MEM_OVERLAP_TOO_HARD) {
if (raise_exceptions) {
- npy_cache_import("numpy.core._internal", "TooHardError",
+ npy_cache_import("numpy.core._exceptions", "TooHardError",
&too_hard_cls);
if (too_hard_cls) {
PyErr_SetString(too_hard_cls, "Exceeded max_work");
@@ -4193,6 +4081,9 @@ normalize_axis_index(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
}
static struct PyMethodDef array_module_methods[] = {
+ {"_get_implementing_args",
+ (PyCFunction)array__get_implementing_args,
+ METH_VARARGS, NULL},
{"_get_ndarray_c_version",
(PyCFunction)array__get_ndarray_c_version,
METH_VARARGS|METH_KEYWORDS, NULL},
@@ -4265,9 +4156,6 @@ static struct PyMethodDef array_module_methods[] = {
{"vdot",
(PyCFunction)array_vdot,
METH_VARARGS | METH_KEYWORDS, NULL},
- {"matmul",
- (PyCFunction)array_matmul,
- METH_VARARGS | METH_KEYWORDS, NULL},
{"c_einsum",
(PyCFunction)array_einsum,
METH_VARARGS|METH_KEYWORDS, NULL},
@@ -4358,6 +4246,9 @@ static struct PyMethodDef array_module_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"_monotonicity", (PyCFunction)arr__monotonicity,
METH_VARARGS | METH_KEYWORDS, NULL},
+ {"implement_array_function",
+ (PyCFunction)array_implement_array_function,
+ METH_VARARGS, NULL},
{"interp", (PyCFunction)arr_interp,
METH_VARARGS | METH_KEYWORDS, NULL},
{"interp_complex", (PyCFunction)arr_interp_complex,
@@ -4608,8 +4499,8 @@ NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_prepare = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_wrap = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_finalize = NULL;
-NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_buffer = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_ufunc = NULL;
+NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_implementation = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_order = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_copy = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_dtype = NULL;
@@ -4624,8 +4515,8 @@ intern_strings(void)
npy_ma_str_array_prepare = PyUString_InternFromString("__array_prepare__");
npy_ma_str_array_wrap = PyUString_InternFromString("__array_wrap__");
npy_ma_str_array_finalize = PyUString_InternFromString("__array_finalize__");
- npy_ma_str_buffer = PyUString_InternFromString("__buffer__");
npy_ma_str_ufunc = PyUString_InternFromString("__array_ufunc__");
+ npy_ma_str_implementation = PyUString_InternFromString("_implementation");
npy_ma_str_order = PyUString_InternFromString("order");
npy_ma_str_copy = PyUString_InternFromString("copy");
npy_ma_str_dtype = PyUString_InternFromString("dtype");
@@ -4635,12 +4526,11 @@ intern_strings(void)
return npy_ma_str_array && npy_ma_str_array_prepare &&
npy_ma_str_array_wrap && npy_ma_str_array_finalize &&
- npy_ma_str_buffer && npy_ma_str_ufunc &&
+ npy_ma_str_ufunc && npy_ma_str_implementation &&
npy_ma_str_order && npy_ma_str_copy && npy_ma_str_dtype &&
npy_ma_str_ndmin && npy_ma_str_axis1 && npy_ma_str_axis2;
}
-
#if defined(NPY_PY3K)
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
@@ -4705,15 +4595,23 @@ PyMODINIT_FUNC init_multiarray_umath(void) {
*/
PyArray_Type.tp_hash = PyObject_HashNotImplemented;
+ if (PyType_Ready(&PyUFunc_Type) < 0) {
+ goto err;
+ }
+
/* Load the ufunc operators into the array module's namespace */
if (InitOperators(d) < 0) {
goto err;
}
+ if (set_matmul_flags(d) < 0) {
+ goto err;
+ }
initialize_casting_tables();
initialize_numeric_types();
- if(initscalarmath(m) < 0)
+ if (initscalarmath(m) < 0) {
goto err;
+ }
if (PyType_Ready(&PyArray_Type) < 0) {
goto err;
@@ -4834,11 +4732,9 @@ PyMODINIT_FUNC init_multiarray_umath(void) {
set_flaginfo(d);
/* Create the typeinfo types */
- typeinfo_init_structsequences();
- PyDict_SetItemString(d,
- "typeinfo", (PyObject *)&PyArray_typeinfoType);
- PyDict_SetItemString(d,
- "typeinforanged", (PyObject *)&PyArray_typeinforangedType);
+ if (typeinfo_init_structsequences(d) < 0) {
+ goto err;
+ }
if (!intern_strings()) {
goto err;
diff --git a/numpy/core/src/multiarray/multiarraymodule.h b/numpy/core/src/multiarray/multiarraymodule.h
index 3de68c549..dd437e091 100644
--- a/numpy/core/src/multiarray/multiarraymodule.h
+++ b/numpy/core/src/multiarray/multiarraymodule.h
@@ -5,8 +5,8 @@ NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_prepare;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_wrap;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_finalize;
-NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_buffer;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_ufunc;
+NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_implementation;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_order;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_copy;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_dtype;
diff --git a/numpy/core/src/multiarray/nditer_api.c b/numpy/core/src/multiarray/nditer_api.c
index 7a33ac05e..db0bfcece 100644
--- a/numpy/core/src/multiarray/nditer_api.c
+++ b/numpy/core/src/multiarray/nditer_api.c
@@ -406,7 +406,7 @@ NpyIter_ResetToIterIndexRange(NpyIter *iter,
* Returns NPY_SUCCEED on success, NPY_FAIL on failure.
*/
NPY_NO_EXPORT int
-NpyIter_GotoMultiIndex(NpyIter *iter, npy_intp *multi_index)
+NpyIter_GotoMultiIndex(NpyIter *iter, npy_intp const *multi_index)
{
npy_uint32 itflags = NIT_ITFLAGS(iter);
int idim, ndim = NIT_NDIM(iter);
@@ -1628,15 +1628,12 @@ npyiter_coalesce_axes(NpyIter *iter)
npy_intp istrides, nstrides = NAD_NSTRIDES();
NpyIter_AxisData *axisdata = NIT_AXISDATA(iter);
npy_intp sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, nop);
- NpyIter_AxisData *ad_compress;
+ NpyIter_AxisData *ad_compress = axisdata;
npy_intp new_ndim = 1;
/* The HASMULTIINDEX or IDENTPERM flags do not apply after coalescing */
NIT_ITFLAGS(iter) &= ~(NPY_ITFLAG_IDENTPERM|NPY_ITFLAG_HASMULTIINDEX);
- axisdata = NIT_AXISDATA(iter);
- ad_compress = axisdata;
-
for (idim = 0; idim < ndim-1; ++idim) {
int can_coalesce = 1;
npy_intp shape0 = NAD_SHAPE(ad_compress);
diff --git a/numpy/core/src/multiarray/nditer_constr.c b/numpy/core/src/multiarray/nditer_constr.c
index dbb24f26b..d40836dc2 100644
--- a/numpy/core/src/multiarray/nditer_constr.c
+++ b/numpy/core/src/multiarray/nditer_constr.c
@@ -24,7 +24,7 @@ static int
npyiter_check_global_flags(npy_uint32 flags, npy_uint32* itflags);
static int
npyiter_check_op_axes(int nop, int oa_ndim, int **op_axes,
- npy_intp *itershape);
+ const npy_intp *itershape);
static int
npyiter_calculate_ndim(int nop, PyArrayObject **op_in,
int oa_ndim);
@@ -55,7 +55,7 @@ npyiter_check_casting(int nop, PyArrayObject **op,
static int
npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itflags,
char **op_dataptr,
- npy_uint32 *op_flags, int **op_axes,
+ const npy_uint32 *op_flags, int **op_axes,
npy_intp *itershape);
static void
npyiter_replace_axisdata(NpyIter *iter, int iop,
@@ -74,23 +74,23 @@ static void
npyiter_find_best_axis_ordering(NpyIter *iter);
static PyArray_Descr *
npyiter_get_common_dtype(int nop, PyArrayObject **op,
- npyiter_opitflags *op_itflags, PyArray_Descr **op_dtype,
+ const npyiter_opitflags *op_itflags, PyArray_Descr **op_dtype,
PyArray_Descr **op_request_dtypes,
int only_inputs);
static PyArrayObject *
npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype,
npy_uint32 flags, npyiter_opitflags *op_itflags,
int op_ndim, npy_intp *shape,
- PyArray_Descr *op_dtype, int *op_axes);
+ PyArray_Descr *op_dtype, const int *op_axes);
static int
npyiter_allocate_arrays(NpyIter *iter,
npy_uint32 flags,
PyArray_Descr **op_dtype, PyTypeObject *subtype,
- npy_uint32 *op_flags, npyiter_opitflags *op_itflags,
+ const npy_uint32 *op_flags, npyiter_opitflags *op_itflags,
int **op_axes);
static void
npyiter_get_priority_subtype(int nop, PyArrayObject **op,
- npyiter_opitflags *op_itflags,
+ const npyiter_opitflags *op_itflags,
double *subtype_priority, PyTypeObject **subtype);
static int
npyiter_allocate_transfer_functions(NpyIter *iter);
@@ -787,7 +787,7 @@ npyiter_check_global_flags(npy_uint32 flags, npy_uint32* itflags)
static int
npyiter_check_op_axes(int nop, int oa_ndim, int **op_axes,
- npy_intp *itershape)
+ const npy_intp *itershape)
{
char axes_dupcheck[NPY_MAXDIMS];
int iop, idim;
@@ -1101,8 +1101,8 @@ npyiter_prepare_one_operand(PyArrayObject **op,
/* We just have a borrowed reference to op_request_dtype */
Py_INCREF(op_request_dtype);
/* If the requested dtype is flexible, adapt it */
- PyArray_AdaptFlexibleDType((PyObject *)(*op), PyArray_DESCR(*op),
- &op_request_dtype);
+ op_request_dtype = PyArray_AdaptFlexibleDType((PyObject *)(*op), PyArray_DESCR(*op),
+ op_request_dtype);
if (op_request_dtype == NULL) {
return 0;
}
@@ -1132,7 +1132,7 @@ npyiter_prepare_one_operand(PyArrayObject **op,
/* Check if the operand is aligned */
if (op_flags & NPY_ITER_ALIGNED) {
/* Check alignment */
- if (!IsUintAligned(*op)) {
+ if (!IsAligned(*op)) {
NPY_IT_DBG_PRINT("Iterator: Setting NPY_OP_ITFLAG_CAST "
"because of NPY_ITER_ALIGNED\n");
*op_itflags |= NPY_OP_ITFLAG_CAST;
@@ -1248,9 +1248,9 @@ npyiter_prepare_operands(int nop, PyArrayObject **op_in,
return 1;
fail_nop:
- iop = nop;
+ iop = nop - 1;
fail_iop:
- for (i = 0; i < iop; ++i) {
+ for (i = 0; i < iop+1; ++i) {
Py_XDECREF(op[i]);
Py_XDECREF(op_dtype[i]);
}
@@ -1423,7 +1423,7 @@ check_mask_for_writemasked_reduction(NpyIter *iter, int iop)
static int
npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itflags,
char **op_dataptr,
- npy_uint32 *op_flags, int **op_axes,
+ const npy_uint32 *op_flags, int **op_axes,
npy_intp *itershape)
{
npy_uint32 itflags = NIT_ITFLAGS(iter);
@@ -2120,8 +2120,8 @@ npyiter_apply_forced_iteration_order(NpyIter *iter, NPY_ORDER order)
/* Check that all the array inputs are fortran order */
for (iop = 0; iop < nop; ++iop, ++op) {
if (*op && !PyArray_CHKFLAGS(*op, NPY_ARRAY_F_CONTIGUOUS)) {
- forder = 0;
- break;
+ forder = 0;
+ break;
}
}
@@ -2409,7 +2409,7 @@ npyiter_find_best_axis_ordering(NpyIter *iter)
*/
static PyArray_Descr *
npyiter_get_common_dtype(int nop, PyArrayObject **op,
- npyiter_opitflags *op_itflags, PyArray_Descr **op_dtype,
+ const npyiter_opitflags *op_itflags, PyArray_Descr **op_dtype,
PyArray_Descr **op_request_dtypes,
int only_inputs)
{
@@ -2477,7 +2477,7 @@ static PyArrayObject *
npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype,
npy_uint32 flags, npyiter_opitflags *op_itflags,
int op_ndim, npy_intp *shape,
- PyArray_Descr *op_dtype, int *op_axes)
+ PyArray_Descr *op_dtype, const int *op_axes)
{
npy_uint32 itflags = NIT_ITFLAGS(iter);
int idim, ndim = NIT_NDIM(iter);
@@ -2706,7 +2706,7 @@ static int
npyiter_allocate_arrays(NpyIter *iter,
npy_uint32 flags,
PyArray_Descr **op_dtype, PyTypeObject *subtype,
- npy_uint32 *op_flags, npyiter_opitflags *op_itflags,
+ const npy_uint32 *op_flags, npyiter_opitflags *op_itflags,
int **op_axes)
{
npy_uint32 itflags = NIT_ITFLAGS(iter);
@@ -2851,8 +2851,14 @@ npyiter_allocate_arrays(NpyIter *iter,
npyiter_replace_axisdata(iter, iop, op[iop], ondim,
PyArray_DATA(op[iop]), op_axes ? op_axes[iop] : NULL);
- /* New arrays are aligned and need no cast */
- op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED;
+ /*
+ * New arrays are guaranteed true-aligned, but copy/cast code
+ * needs uint-alignment in addition.
+ */
+ if (IsUintAligned(out)) {
+ op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED;
+ }
+ /* New arrays need no cast */
op_itflags[iop] &= ~NPY_OP_ITFLAG_CAST;
}
/*
@@ -2888,11 +2894,17 @@ npyiter_allocate_arrays(NpyIter *iter,
PyArray_DATA(op[iop]), NULL);
/*
- * New arrays are aligned need no cast, and in the case
+ * New arrays are guaranteed true-aligned, but copy/cast code
+ * needs uint-alignment in addition.
+ */
+ if (IsUintAligned(temp)) {
+ op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED;
+ }
+ /*
+ * New arrays need no cast, and in the case
* of scalars, always have stride 0 so never need buffering
*/
- op_itflags[iop] |= (NPY_OP_ITFLAG_ALIGNED |
- NPY_OP_ITFLAG_BUFNEVER);
+ op_itflags[iop] |= NPY_OP_ITFLAG_BUFNEVER;
op_itflags[iop] &= ~NPY_OP_ITFLAG_CAST;
if (itflags & NPY_ITFLAG_BUFFER) {
NBF_STRIDES(bufferdata)[iop] = 0;
@@ -2953,8 +2965,14 @@ npyiter_allocate_arrays(NpyIter *iter,
npyiter_replace_axisdata(iter, iop, op[iop], ondim,
PyArray_DATA(op[iop]), op_axes ? op_axes[iop] : NULL);
- /* The temporary copy is aligned and needs no cast */
- op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED;
+ /*
+ * New arrays are guaranteed true-aligned, but copy/cast code
+ * additionally needs uint-alignment in addition.
+ */
+ if (IsUintAligned(temp)) {
+ op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED;
+ }
+ /* The temporary copy needs no cast */
op_itflags[iop] &= ~NPY_OP_ITFLAG_CAST;
}
else {
@@ -3091,7 +3109,7 @@ npyiter_allocate_arrays(NpyIter *iter,
*/
static void
npyiter_get_priority_subtype(int nop, PyArrayObject **op,
- npyiter_opitflags *op_itflags,
+ const npyiter_opitflags *op_itflags,
double *subtype_priority,
PyTypeObject **subtype)
{
@@ -3157,6 +3175,7 @@ npyiter_allocate_transfer_functions(NpyIter *iter)
&stransfer,
&transferdata,
&needs_api) != NPY_SUCCEED) {
+ iop -= 1; /* This one cannot be cleaned up yet. */
goto fail;
}
readtransferfn[iop] = stransfer;
@@ -3250,7 +3269,7 @@ npyiter_allocate_transfer_functions(NpyIter *iter)
return 1;
fail:
- for (i = 0; i < iop; ++i) {
+ for (i = 0; i < iop+1; ++i) {
if (readtransferdata[iop] != NULL) {
NPY_AUXDATA_FREE(readtransferdata[iop]);
readtransferdata[iop] = NULL;
diff --git a/numpy/core/src/multiarray/nditer_pywrap.c b/numpy/core/src/multiarray/nditer_pywrap.c
index 5a9f3c5fa..4b9d41aa4 100644
--- a/numpy/core/src/multiarray/nditer_pywrap.c
+++ b/numpy/core/src/multiarray/nditer_pywrap.c
@@ -82,7 +82,8 @@ static int npyiter_cache_values(NewNpyArrayIterObject *self)
}
static PyObject *
-npyiter_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
+npyiter_new(PyTypeObject *subtype, PyObject *NPY_UNUSED(args),
+ PyObject *NPY_UNUSED(kwds))
{
NewNpyArrayIterObject *self;
@@ -535,7 +536,7 @@ try_single_dtype:
}
static int
-npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp nop,
+npyiter_convert_op_axes(PyObject *op_axes_in, int nop,
int **op_axes, int *oa_ndim)
{
PyObject *a;
@@ -572,6 +573,7 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp nop,
if (*oa_ndim > NPY_MAXDIMS) {
PyErr_SetString(PyExc_ValueError,
"Too many dimensions in op_axes");
+ Py_DECREF(a);
return 0;
}
}
@@ -602,8 +604,8 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp nop,
}
Py_DECREF(v);
}
- Py_DECREF(a);
}
+ Py_DECREF(a);
}
if (*oa_ndim == -1) {
@@ -2355,6 +2357,8 @@ npyiter_close(NewNpyArrayIterObject *self)
}
ret = NpyIter_Deallocate(iter);
self->iter = NULL;
+ Py_XDECREF(self->nested_child);
+ self->nested_child = NULL;
if (ret < 0) {
return NULL;
}
@@ -2362,7 +2366,7 @@ npyiter_close(NewNpyArrayIterObject *self)
}
static PyObject *
-npyiter_exit(NewNpyArrayIterObject *self, PyObject *args)
+npyiter_exit(NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args))
{
/* even if called via exception handling, writeback any data */
return npyiter_close(self);
diff --git a/numpy/core/src/multiarray/number.c b/numpy/core/src/multiarray/number.c
index dabbae064..dabc866ff 100644
--- a/numpy/core/src/multiarray/number.c
+++ b/numpy/core/src/multiarray/number.c
@@ -71,12 +71,8 @@ array_inplace_power(PyArrayObject *a1, PyObject *o2, PyObject *NPY_UNUSED(modulo
n_ops.op = temp; \
}
-
-/*NUMPY_API
- *Set internal structure with number functions that all arrays will use
- */
NPY_NO_EXPORT int
-PyArray_SetNumericOps(PyObject *dict)
+_PyArray_SetNumericOps(PyObject *dict)
{
PyObject *temp = NULL;
SET(add);
@@ -116,19 +112,33 @@ PyArray_SetNumericOps(PyObject *dict)
SET(minimum);
SET(rint);
SET(conjugate);
+ SET(matmul);
+ SET(clip);
return 0;
}
+/*NUMPY_API
+ *Set internal structure with number functions that all arrays will use
+ */
+NPY_NO_EXPORT int
+PyArray_SetNumericOps(PyObject *dict)
+{
+ /* 2018-09-09, 1.16 */
+ if (DEPRECATE("PyArray_SetNumericOps is deprecated. Use "
+ "PyUFunc_ReplaceLoopBySignature to replace ufunc inner loop functions "
+ "instead.") < 0) {
+ return -1;
+ }
+ return _PyArray_SetNumericOps(dict);
+}
+
/* Note - macro contains goto */
#define GET(op) if (n_ops.op && \
(PyDict_SetItemString(dict, #op, n_ops.op)==-1)) \
goto fail;
-/*NUMPY_API
- Get dictionary showing number functions that all arrays will use
-*/
NPY_NO_EXPORT PyObject *
-PyArray_GetNumericOps(void)
+_PyArray_GetNumericOps(void)
{
PyObject *dict;
if ((dict = PyDict_New())==NULL)
@@ -169,6 +179,8 @@ PyArray_GetNumericOps(void)
GET(minimum);
GET(rint);
GET(conjugate);
+ GET(matmul);
+ GET(clip);
return dict;
fail:
@@ -176,6 +188,19 @@ PyArray_GetNumericOps(void)
return NULL;
}
+/*NUMPY_API
+ Get dictionary showing number functions that all arrays will use
+*/
+NPY_NO_EXPORT PyObject *
+PyArray_GetNumericOps(void)
+{
+ /* 2018-09-09, 1.16 */
+ if (DEPRECATE("PyArray_GetNumericOps is deprecated.") < 0) {
+ return NULL;
+ }
+ return _PyArray_GetNumericOps();
+}
+
static PyObject *
_get_keywords(int rtype, PyArrayObject *out)
{
@@ -361,18 +386,13 @@ array_divmod(PyArrayObject *m1, PyObject *m2)
static PyObject *
array_matrix_multiply(PyArrayObject *m1, PyObject *m2)
{
- static PyObject *matmul = NULL;
-
- npy_cache_import("numpy.core.multiarray", "matmul", &matmul);
- if (matmul == NULL) {
- return NULL;
- }
BINOP_GIVE_UP_IF_NEEDED(m1, m2, nb_matrix_multiply, array_matrix_multiply);
- return PyArray_GenericBinaryFunction(m1, m2, matmul);
+ return PyArray_GenericBinaryFunction(m1, m2, n_ops.matmul);
}
static PyObject *
-array_inplace_matrix_multiply(PyArrayObject *m1, PyObject *m2)
+array_inplace_matrix_multiply(
+ PyArrayObject *NPY_UNUSED(m1), PyObject *NPY_UNUSED(m2))
{
PyErr_SetString(PyExc_TypeError,
"In-place matrix multiplication is not (yet) supported. "
@@ -578,19 +598,20 @@ array_positive(PyArrayObject *m1)
*/
PyObject *exc, *val, *tb;
PyErr_Fetch(&exc, &val, &tb);
- if (has_non_default_array_ufunc((PyObject *)m1)) {
+ if (PyUFunc_HasOverride((PyObject *)m1)) {
PyErr_Restore(exc, val, tb);
return NULL;
}
+ Py_XDECREF(exc);
+ Py_XDECREF(val);
+ Py_XDECREF(tb);
+
/* 2018-06-28, 1.16.0 */
if (DEPRECATE("Applying '+' to a non-numerical array is "
"ill-defined. Returning a copy, but in the future "
"this will error.") < 0) {
return NULL;
}
- Py_XDECREF(exc);
- Py_XDECREF(val);
- Py_XDECREF(tb);
value = PyArray_Return((PyArrayObject *)PyArray_Copy(m1));
}
return value;
diff --git a/numpy/core/src/multiarray/number.h b/numpy/core/src/multiarray/number.h
index 99a2a722b..643241b3d 100644
--- a/numpy/core/src/multiarray/number.h
+++ b/numpy/core/src/multiarray/number.h
@@ -39,6 +39,8 @@ typedef struct {
PyObject *minimum;
PyObject *rint;
PyObject *conjugate;
+ PyObject *matmul;
+ PyObject *clip;
} NumericOps;
extern NPY_NO_EXPORT NumericOps n_ops;
@@ -48,10 +50,10 @@ NPY_NO_EXPORT PyObject *
array_int(PyArrayObject *v);
NPY_NO_EXPORT int
-PyArray_SetNumericOps(PyObject *dict);
+_PyArray_SetNumericOps(PyObject *dict);
NPY_NO_EXPORT PyObject *
-PyArray_GetNumericOps(void);
+_PyArray_GetNumericOps(void);
NPY_NO_EXPORT PyObject *
PyArray_GenericBinaryFunction(PyArrayObject *m1, PyObject *m2, PyObject *op);
diff --git a/numpy/core/src/multiarray/refcount.c b/numpy/core/src/multiarray/refcount.c
index 4b018b056..6033929d9 100644
--- a/numpy/core/src/multiarray/refcount.c
+++ b/numpy/core/src/multiarray/refcount.c
@@ -11,6 +11,7 @@
#define _MULTIARRAYMODULE
#include "numpy/arrayobject.h"
#include "numpy/arrayscalars.h"
+#include "iterators.h"
#include "npy_config.h"
@@ -19,8 +20,12 @@
static void
_fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype);
-/* Incref all objects found at this record */
+
/*NUMPY_API
+ * XINCREF all objects in a single array item. This is complicated for
+ * structured datatypes where the position of objects needs to be extracted.
+ * The function is execute recursively for each nested field or subarrays dtype
+ * such as as `np.dtype([("field1", "O"), ("field2", "f,O", (3,2))])`
*/
NPY_NO_EXPORT void
PyArray_Item_INCREF(char *data, PyArray_Descr *descr)
@@ -51,11 +56,37 @@ PyArray_Item_INCREF(char *data, PyArray_Descr *descr)
PyArray_Item_INCREF(data + offset, new);
}
}
+ else if (PyDataType_HASSUBARRAY(descr)) {
+ int size, i, inner_elsize;
+
+ inner_elsize = descr->subarray->base->elsize;
+ if (inner_elsize == 0) {
+ /* There cannot be any elements, so return */
+ return;
+ }
+ /* Subarrays are always contiguous in memory */
+ size = descr->elsize / inner_elsize;
+
+ for (i = 0; i < size; i++){
+ /* Recursively increment the reference count of subarray elements */
+ PyArray_Item_INCREF(data + i * inner_elsize,
+ descr->subarray->base);
+ }
+ }
+ else {
+ /* This path should not be reachable. */
+ assert(0);
+ }
return;
}
-/* XDECREF all objects found at this record */
+
/*NUMPY_API
+ *
+ * XDECREF all objects in a single array item. This is complicated for
+ * structured datatypes where the position of objects needs to be extracted.
+ * The function is execute recursively for each nested field or subarrays dtype
+ * such as as `np.dtype([("field1", "O"), ("field2", "f,O", (3,2))])`
*/
NPY_NO_EXPORT void
PyArray_Item_XDECREF(char *data, PyArray_Descr *descr)
@@ -87,6 +118,27 @@ PyArray_Item_XDECREF(char *data, PyArray_Descr *descr)
PyArray_Item_XDECREF(data + offset, new);
}
}
+ else if (PyDataType_HASSUBARRAY(descr)) {
+ int size, i, inner_elsize;
+
+ inner_elsize = descr->subarray->base->elsize;
+ if (inner_elsize == 0) {
+ /* There cannot be any elements, so return */
+ return;
+ }
+ /* Subarrays are always contiguous in memory */
+ size = descr->elsize / inner_elsize;
+
+ for (i = 0; i < size; i++){
+ /* Recursively decrement the reference count of subarray elements */
+ PyArray_Item_XDECREF(data + i * inner_elsize,
+ descr->subarray->base);
+ }
+ }
+ else {
+ /* This path should not be reachable. */
+ assert(0);
+ }
return;
}
@@ -159,21 +211,22 @@ PyArray_XDECREF(PyArrayObject *mp)
npy_intp i, n;
PyObject **data;
PyObject *temp;
- PyArrayIterObject *it;
+ /*
+ * statically allocating it allows this function to not modify the
+ * reference count of the array for use during dealloc.
+ * (statically is not necessary as such)
+ */
+ PyArrayIterObject it;
if (!PyDataType_REFCHK(PyArray_DESCR(mp))) {
return 0;
}
if (PyArray_DESCR(mp)->type_num != NPY_OBJECT) {
- it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)mp);
- if (it == NULL) {
- return -1;
+ PyArray_RawIterBaseInit(&it, mp);
+ while(it.index < it.size) {
+ PyArray_Item_XDECREF(it.dataptr, PyArray_DESCR(mp));
+ PyArray_ITER_NEXT(&it);
}
- while(it->index < it->size) {
- PyArray_Item_XDECREF(it->dataptr, PyArray_DESCR(mp));
- PyArray_ITER_NEXT(it);
- }
- Py_DECREF(it);
return 0;
}
@@ -191,16 +244,12 @@ PyArray_XDECREF(PyArrayObject *mp)
}
}
else { /* handles misaligned data too */
- it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)mp);
- if (it == NULL) {
- return -1;
- }
- while(it->index < it->size) {
- NPY_COPY_PYOBJECT_PTR(&temp, it->dataptr);
+ PyArray_RawIterBaseInit(&it, mp);
+ while(it.index < it.size) {
+ NPY_COPY_PYOBJECT_PTR(&temp, it.dataptr);
Py_XDECREF(temp);
- PyArray_ITER_NEXT(it);
+ PyArray_ITER_NEXT(&it);
}
- Py_DECREF(it);
}
return 0;
}
@@ -258,6 +307,10 @@ _fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype)
Py_XDECREF(arr);
}
}
+ if (dtype->type_num == NPY_OBJECT) {
+ Py_XINCREF(obj);
+ NPY_COPY_PYOBJECT_PTR(optr, &obj);
+ }
else if (PyDataType_HASFIELDS(dtype)) {
PyObject *key, *value, *title = NULL;
PyArray_Descr *new;
@@ -274,15 +327,26 @@ _fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype)
_fillobject(optr + offset, obj, new);
}
}
- else {
- npy_intp i;
- npy_intp nsize = dtype->elsize / sizeof(obj);
+ else if (PyDataType_HASSUBARRAY(dtype)) {
+ int size, i, inner_elsize;
- for (i = 0; i < nsize; i++) {
- Py_XINCREF(obj);
- NPY_COPY_PYOBJECT_PTR(optr, &obj);
- optr += sizeof(obj);
+ inner_elsize = dtype->subarray->base->elsize;
+ if (inner_elsize == 0) {
+ /* There cannot be any elements, so return */
+ return;
+ }
+ /* Subarrays are always contiguous in memory */
+ size = dtype->elsize / inner_elsize;
+
+ /* Call _fillobject on each item recursively. */
+ for (i = 0; i < size; i++){
+ _fillobject(optr, obj, dtype->subarray->base);
+ optr += inner_elsize;
}
- return;
}
+ else {
+ /* This path should not be reachable. */
+ assert(0);
+ }
+ return;
}
diff --git a/numpy/core/src/multiarray/scalarapi.c b/numpy/core/src/multiarray/scalarapi.c
index 5ef6c0bbf..b669a3e76 100644
--- a/numpy/core/src/multiarray/scalarapi.c
+++ b/numpy/core/src/multiarray/scalarapi.c
@@ -471,12 +471,18 @@ PyArray_DescrFromTypeObject(PyObject *type)
/* Do special thing for VOID sub-types */
if (PyType_IsSubtype((PyTypeObject *)type, &PyVoidArrType_Type)) {
new = PyArray_DescrNewFromType(NPY_VOID);
- conv = _arraydescr_fromobj(type);
- if (conv) {
+ if (new == NULL) {
+ return NULL;
+ }
+ if (_arraydescr_from_dtype_attr(type, &conv)) {
+ if (conv == NULL) {
+ Py_DECREF(new);
+ return NULL;
+ }
new->fields = conv->fields;
- Py_INCREF(new->fields);
+ Py_XINCREF(new->fields);
new->names = conv->names;
- Py_INCREF(new->names);
+ Py_XINCREF(new->names);
new->elsize = conv->elsize;
new->subarray = conv->subarray;
conv->subarray = NULL;
@@ -802,6 +808,9 @@ PyArray_Scalar(void *data, PyArray_Descr *descr, PyObject *base)
return obj;
}
}
+ if (itemsize == 0) {
+ return obj;
+ }
destptr = PyDataMem_NEW(itemsize);
if (destptr == NULL) {
Py_DECREF(obj);
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
index 6dd8b1a29..9adca6773 100644
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -139,6 +139,7 @@ gentype_alloc(PyTypeObject *type, Py_ssize_t nitems)
static void
gentype_dealloc(PyObject *v)
{
+ _dealloc_cached_buffer_info(v);
Py_TYPE(v)->tp_free(v);
}
@@ -1103,8 +1104,7 @@ static PyNumberMethods gentype_as_number = {
(binaryfunc)gentype_add, /*nb_add*/
(binaryfunc)gentype_subtract, /*nb_subtract*/
(binaryfunc)gentype_multiply, /*nb_multiply*/
-#if defined(NPY_PY3K)
-#else
+#if !defined(NPY_PY3K)
(binaryfunc)gentype_divide, /*nb_divide*/
#endif
(binaryfunc)gentype_remainder, /*nb_remainder*/
@@ -1120,8 +1120,7 @@ static PyNumberMethods gentype_as_number = {
(binaryfunc)gentype_and, /*nb_and*/
(binaryfunc)gentype_xor, /*nb_xor*/
(binaryfunc)gentype_or, /*nb_or*/
-#if defined(NPY_PY3K)
-#else
+#if !defined(NPY_PY3K)
0, /*nb_coerce*/
#endif
(unaryfunc)gentype_int, /*nb_int*/
@@ -1131,16 +1130,14 @@ static PyNumberMethods gentype_as_number = {
(unaryfunc)gentype_long, /*nb_long*/
#endif
(unaryfunc)gentype_float, /*nb_float*/
-#if defined(NPY_PY3K)
-#else
+#if !defined(NPY_PY3K)
(unaryfunc)gentype_oct, /*nb_oct*/
(unaryfunc)gentype_hex, /*nb_hex*/
#endif
0, /*inplace_add*/
0, /*inplace_subtract*/
0, /*inplace_multiply*/
-#if defined(NPY_PY3K)
-#else
+#if !defined(NPY_PY3K)
0, /*inplace_divide*/
#endif
0, /*inplace_remainder*/
@@ -1155,6 +1152,10 @@ static PyNumberMethods gentype_as_number = {
0, /*nb_inplace_floor_divide*/
0, /*nb_inplace_true_divide*/
(unaryfunc)NULL, /*nb_index*/
+#if PY_VERSION_HEX >= 0x03050000
+ 0, /*np_matmul*/
+ 0, /*np_inplace_matmul*/
+#endif
};
@@ -1858,6 +1859,7 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args))
* sticks around after the release.
*/
PyBuffer_Release(&view);
+ _dealloc_cached_buffer_info(self);
}
else {
Py_DECREF(ret);
@@ -1991,6 +1993,92 @@ static PyObject *
}
/**end repeat**/
+/**begin repeat
+ * #name = half, float, double, longdouble#
+ * #Name = Half, Float, Double, LongDouble#
+ * #is_half = 1,0,0,0#
+ * #c = f, f, , l#
+ * #convert = PyLong_FromDouble, PyLong_FromDouble, PyLong_FromDouble,
+ * npy_longdouble_to_PyLong#
+ * #
+ */
+/* Heavily copied from the builtin float.as_integer_ratio */
+static PyObject *
+@name@_as_integer_ratio(PyObject *self)
+{
+#if @is_half@
+ npy_double val = npy_half_to_double(PyArrayScalar_VAL(self, @Name@));
+ npy_double frac;
+#else
+ npy_@name@ val = PyArrayScalar_VAL(self, @Name@);
+ npy_@name@ frac;
+#endif
+ int exponent;
+ int i;
+
+ PyObject *py_exponent = NULL;
+ PyObject *numerator = NULL;
+ PyObject *denominator = NULL;
+ PyObject *result_pair = NULL;
+ PyNumberMethods *long_methods = PyLong_Type.tp_as_number;
+
+ if (npy_isnan(val)) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot convert NaN to integer ratio");
+ return NULL;
+ }
+ if (!npy_isfinite(val)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "cannot convert Infinity to integer ratio");
+ return NULL;
+ }
+
+ frac = npy_frexp@c@(val, &exponent); /* val == frac * 2**exponent exactly */
+
+ /* This relies on the floating point type being base 2 to converge */
+ for (i = 0; frac != npy_floor@c@(frac); i++) {
+ frac *= 2.0;
+ exponent--;
+ }
+
+ /* self == frac * 2**exponent exactly and frac is integral. */
+ numerator = @convert@(frac);
+ if (numerator == NULL)
+ goto error;
+ denominator = PyLong_FromLong(1);
+ if (denominator == NULL)
+ goto error;
+ py_exponent = PyLong_FromLong(exponent < 0 ? -exponent : exponent);
+ if (py_exponent == NULL)
+ goto error;
+
+ /* fold in 2**exponent */
+ if (exponent > 0) {
+ PyObject *temp = long_methods->nb_lshift(numerator, py_exponent);
+ if (temp == NULL)
+ goto error;
+ Py_DECREF(numerator);
+ numerator = temp;
+ }
+ else {
+ PyObject *temp = long_methods->nb_lshift(denominator, py_exponent);
+ if (temp == NULL)
+ goto error;
+ Py_DECREF(denominator);
+ denominator = temp;
+ }
+
+ result_pair = PyTuple_Pack(2, numerator, denominator);
+
+error:
+ Py_XDECREF(py_exponent);
+ Py_XDECREF(denominator);
+ Py_XDECREF(numerator);
+ return result_pair;
+}
+/**end repeat**/
+
+
/*
* need to fill in doc-strings for these methods on import -- copy from
* array docstrings
@@ -2254,6 +2342,17 @@ static PyMethodDef @name@type_methods[] = {
};
/**end repeat**/
+/**begin repeat
+ * #name = half,float,double,longdouble#
+ */
+static PyMethodDef @name@type_methods[] = {
+ {"as_integer_ratio",
+ (PyCFunction)@name@_as_integer_ratio,
+ METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL}
+};
+/**end repeat**/
+
/************* As_mapping functions for void array scalar ************/
static Py_ssize_t
@@ -2597,6 +2696,8 @@ NPY_NO_EXPORT PyTypeObject PyGenericArrType_Type = {
static void
void_dealloc(PyVoidScalarObject *v)
{
+ _dealloc_cached_buffer_info((PyObject *)v);
+
if (v->flags & NPY_ARRAY_OWNDATA) {
npy_free_cache(v->obval, Py_SIZE(v));
}
@@ -3923,7 +4024,6 @@ initialize_casting_tables(void)
}
_npy_can_cast_safely_table[NPY_STRING][NPY_UNICODE] = 1;
- _npy_can_cast_safely_table[NPY_BOOL][NPY_TIMEDELTA] = 1;
#ifndef NPY_SIZEOF_BYTE
#define NPY_SIZEOF_BYTE 1
@@ -4307,6 +4407,15 @@ initialize_numeric_types(void)
/**end repeat**/
+ /**begin repeat
+ * #name = half, float, double, longdouble#
+ * #Name = Half, Float, Double, LongDouble#
+ */
+
+ Py@Name@ArrType_Type.tp_methods = @name@type_methods;
+
+ /**end repeat**/
+
#if (NPY_SIZEOF_INT != NPY_SIZEOF_LONG) || defined(NPY_PY3K)
/* We won't be inheriting from Python Int type. */
PyIntArrType_Type.tp_hash = int_arrtype_hash;
@@ -4383,6 +4492,36 @@ initialize_numeric_types(void)
PyArrayIter_Type.tp_iter = PyObject_SelfIter;
PyArrayMapIter_Type.tp_iter = PyObject_SelfIter;
+
+ /*
+ * Give types different names when they are the same size (gh-9799).
+ * `np.intX` always refers to the first int of that size in the sequence
+ * `['LONG', 'LONGLONG', 'INT', 'SHORT', 'BYTE']`.
+ */
+#if (NPY_SIZEOF_BYTE == NPY_SIZEOF_SHORT)
+ PyByteArrType_Type.tp_name = "numpy.byte";
+ PyUByteArrType_Type.tp_name = "numpy.ubyte";
+#endif
+#if (NPY_SIZEOF_SHORT == NPY_SIZEOF_INT)
+ PyShortArrType_Type.tp_name = "numpy.short";
+ PyUShortArrType_Type.tp_name = "numpy.ushort";
+#endif
+#if (NPY_SIZEOF_INT == NPY_SIZEOF_LONG)
+ PyIntArrType_Type.tp_name = "numpy.intc";
+ PyUIntArrType_Type.tp_name = "numpy.uintc";
+#endif
+#if (NPY_SIZEOF_LONGLONG == NPY_SIZEOF_LONG)
+ PyLongLongArrType_Type.tp_name = "numpy.longlong";
+ PyULongLongArrType_Type.tp_name = "numpy.ulonglong";
+#endif
+
+ /*
+ Do the same for longdouble
+ */
+#if (NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE)
+ PyLongDoubleArrType_Type.tp_name = "numpy.longdouble";
+ PyCLongDoubleArrType_Type.tp_name = "numpy.clongdouble";
+#endif
}
typedef struct {
diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c
index 3ac71e285..4e31f003b 100644
--- a/numpy/core/src/multiarray/shape.c
+++ b/numpy/core/src/multiarray/shape.c
@@ -26,7 +26,7 @@ static int
_fix_unknown_dimension(PyArray_Dims *newshape, PyArrayObject *arr);
static int
-_attempt_nocopy_reshape(PyArrayObject *self, int newnd, npy_intp* newdims,
+_attempt_nocopy_reshape(PyArrayObject *self, int newnd, const npy_intp *newdims,
npy_intp *newstrides, int is_f_order);
static void
@@ -40,11 +40,11 @@ _putzero(char *optr, PyObject *zero, PyArray_Descr *dtype);
*/
NPY_NO_EXPORT PyObject *
PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
- NPY_ORDER order)
+ NPY_ORDER NPY_UNUSED(order))
{
npy_intp oldnbytes, newnbytes;
npy_intp oldsize, newsize;
- int new_nd=newshape->len, k, n, elsize;
+ int new_nd=newshape->len, k, elsize;
int refcnt;
npy_intp* new_dimensions=newshape->ptr;
npy_intp new_strides[NPY_MAXDIMS];
@@ -89,11 +89,19 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
return NULL;
}
+ if (PyArray_BASE(self) != NULL
+ || (((PyArrayObject_fields *)self)->weakreflist != NULL)) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot resize an array that "
+ "references or is referenced\n"
+ "by another array in this way. Use the np.resize function.");
+ return NULL;
+ }
if (refcheck) {
#ifdef PYPY_VERSION
PyErr_SetString(PyExc_ValueError,
"cannot resize an array with refcheck=True on PyPy.\n"
- "Use the resize function or refcheck=False");
+ "Use the np.resize function or refcheck=False");
return NULL;
#else
refcnt = PyArray_REFCOUNT(self);
@@ -102,13 +110,12 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
else {
refcnt = 1;
}
- if ((refcnt > 2)
- || (PyArray_BASE(self) != NULL)
- || (((PyArrayObject_fields *)self)->weakreflist != NULL)) {
+ if (refcnt > 2) {
PyErr_SetString(PyExc_ValueError,
"cannot resize an array that "
"references or is referenced\n"
- "by another array in this way. Use the resize function");
+ "by another array in this way.\n"
+ "Use the np.resize function or refcheck=False");
return NULL;
}
@@ -129,8 +136,8 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
PyObject *zero = PyInt_FromLong(0);
char *optr;
optr = PyArray_BYTES(self) + oldnbytes;
- n = newsize - oldsize;
- for (k = 0; k < n; k++) {
+ npy_intp n_new = newsize - oldsize;
+ for (npy_intp i = 0; i < n_new; i++) {
_putzero((char *)optr, zero, PyArray_DESCR(self));
optr += elsize;
}
@@ -354,7 +361,7 @@ _putzero(char *optr, PyObject *zero, PyArray_Descr *dtype)
* stride of the next-fastest index.
*/
static int
-_attempt_nocopy_reshape(PyArrayObject *self, int newnd, npy_intp* newdims,
+_attempt_nocopy_reshape(PyArrayObject *self, int newnd, const npy_intp *newdims,
npy_intp *newstrides, int is_f_order)
{
int oldnd;
@@ -759,7 +766,7 @@ static int _npy_stride_sort_item_comparator(const void *a, const void *b)
* [(2, 12), (0, 4), (1, -2)].
*/
NPY_NO_EXPORT void
-PyArray_CreateSortedStridePerm(int ndim, npy_intp *strides,
+PyArray_CreateSortedStridePerm(int ndim, npy_intp const *strides,
npy_stride_sort_item *out_strideperm)
{
int i;
@@ -1041,7 +1048,7 @@ build_shape_string(npy_intp n, npy_intp *vals)
* from a reduction result once its computation is complete.
*/
NPY_NO_EXPORT void
-PyArray_RemoveAxesInPlace(PyArrayObject *arr, npy_bool *flags)
+PyArray_RemoveAxesInPlace(PyArrayObject *arr, const npy_bool *flags)
{
PyArrayObject_fields *fa = (PyArrayObject_fields *)arr;
npy_intp *shape = fa->dimensions, *strides = fa->strides;
diff --git a/numpy/core/src/multiarray/temp_elide.c b/numpy/core/src/multiarray/temp_elide.c
index 3d2f976f2..09b948218 100644
--- a/numpy/core/src/multiarray/temp_elide.c
+++ b/numpy/core/src/multiarray/temp_elide.c
@@ -166,7 +166,7 @@ check_callers(int * cannot)
return 0;
}
/* get multiarray base address */
- if (dladdr(&PyArray_SetNumericOps, &info)) {
+ if (dladdr(&PyArray_INCREF, &info)) {
pos_ma_start = info.dli_fbase;
pos_ma_end = info.dli_fbase;
}
diff --git a/numpy/core/src/multiarray/typeinfo.c b/numpy/core/src/multiarray/typeinfo.c
index f0af76809..14c4f27cb 100644
--- a/numpy/core/src/multiarray/typeinfo.c
+++ b/numpy/core/src/multiarray/typeinfo.c
@@ -3,8 +3,7 @@
* Unfortunately, we need two different types to cover the cases where min/max
* do and do not appear in the tuple.
*/
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
+#include "typeinfo.h"
/* In python 2, this is not exported from Python.h */
#include <structseq.h>
@@ -14,8 +13,8 @@
#include "npy_pycompat.h"
-PyTypeObject PyArray_typeinfoType;
-PyTypeObject PyArray_typeinforangedType;
+static PyTypeObject PyArray_typeinfoType;
+static PyTypeObject PyArray_typeinforangedType;
static PyStructSequence_Field typeinfo_fields[] = {
{"char", "The character used to represent the type"},
@@ -51,7 +50,7 @@ static PyStructSequence_Desc typeinforanged_desc = {
7, /* n_in_sequence */
};
-PyObject *
+NPY_NO_EXPORT PyObject *
PyArray_typeinfo(
char typechar, int typenum, int nbits, int align,
PyTypeObject *type_obj)
@@ -77,7 +76,7 @@ PyArray_typeinfo(
return entry;
}
-PyObject *
+NPY_NO_EXPORT PyObject *
PyArray_typeinforanged(
char typechar, int typenum, int nbits, int align,
PyObject *max, PyObject *min, PyTypeObject *type_obj)
@@ -105,10 +104,38 @@ PyArray_typeinforanged(
return entry;
}
-void typeinfo_init_structsequences(void)
+/* Python version only needed for backport to 2.7 */
+#if (PY_VERSION_HEX < 0x03040000) \
+ || (defined(PYPY_VERSION_NUM) && (PYPY_VERSION_NUM < 0x07020000))
+
+ static int
+ PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc) {
+ PyStructSequence_InitType(type, desc);
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+ return 0;
+ }
+#endif
+
+NPY_NO_EXPORT int
+typeinfo_init_structsequences(PyObject *multiarray_dict)
{
- PyStructSequence_InitType(
- &PyArray_typeinfoType, &typeinfo_desc);
- PyStructSequence_InitType(
- &PyArray_typeinforangedType, &typeinforanged_desc);
+ if (PyStructSequence_InitType2(
+ &PyArray_typeinfoType, &typeinfo_desc) < 0) {
+ return -1;
+ }
+ if (PyStructSequence_InitType2(
+ &PyArray_typeinforangedType, &typeinforanged_desc) < 0) {
+ return -1;
+ }
+ if (PyDict_SetItemString(multiarray_dict,
+ "typeinfo", (PyObject *)&PyArray_typeinfoType) < 0) {
+ return -1;
+ }
+ if (PyDict_SetItemString(multiarray_dict,
+ "typeinforanged", (PyObject *)&PyArray_typeinforangedType) < 0) {
+ return -1;
+ }
+ return 0;
}
diff --git a/numpy/core/src/multiarray/typeinfo.h b/numpy/core/src/multiarray/typeinfo.h
index 5899c2093..28afa4120 100644
--- a/numpy/core/src/multiarray/typeinfo.h
+++ b/numpy/core/src/multiarray/typeinfo.h
@@ -1,17 +1,19 @@
#ifndef _NPY_PRIVATE_TYPEINFO_H_
#define _NPY_PRIVATE_TYPEINFO_H_
-void typeinfo_init_structsequences(void);
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "npy_config.h"
-extern PyTypeObject PyArray_typeinfoType;
-extern PyTypeObject PyArray_typeinforangedType;
+NPY_VISIBILITY_HIDDEN int
+typeinfo_init_structsequences(PyObject *multiarray_dict);
-PyObject *
+NPY_VISIBILITY_HIDDEN PyObject *
PyArray_typeinfo(
char typechar, int typenum, int nbits, int align,
PyTypeObject *type_obj);
-PyObject *
+NPY_VISIBILITY_HIDDEN PyObject *
PyArray_typeinforanged(
char typechar, int typenum, int nbits, int align,
PyObject *max, PyObject *min, PyTypeObject *type_obj);
diff --git a/numpy/core/src/multiarray/usertypes.c b/numpy/core/src/multiarray/usertypes.c
index 8e8090002..2e8fb514f 100644
--- a/numpy/core/src/multiarray/usertypes.c
+++ b/numpy/core/src/multiarray/usertypes.c
@@ -40,19 +40,27 @@ maintainer email: oliphant.travis@ieee.org
NPY_NO_EXPORT PyArray_Descr **userdescrs=NULL;
-static int *
-_append_new(int *types, int insert)
+static int
+_append_new(int **p_types, int insert)
{
int n = 0;
int *newtypes;
+ int *types = *p_types;
while (types[n] != NPY_NOTYPE) {
n++;
}
newtypes = (int *)realloc(types, (n + 2)*sizeof(int));
+ if (newtypes == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
newtypes[n] = insert;
newtypes[n + 1] = NPY_NOTYPE;
- return newtypes;
+
+ /* Replace the passed-in pointer */
+ *p_types = newtypes;
+ return 0;
}
static npy_bool
@@ -247,10 +255,13 @@ PyArray_RegisterCanCast(PyArray_Descr *descr, int totype,
*/
if (descr->f->cancastto == NULL) {
descr->f->cancastto = (int *)malloc(1*sizeof(int));
+ if (descr->f->cancastto == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
descr->f->cancastto[0] = NPY_NOTYPE;
}
- descr->f->cancastto = _append_new(descr->f->cancastto,
- totype);
+ return _append_new(&descr->f->cancastto, totype);
}
else {
/* register with cancastscalarkindto */
@@ -258,6 +269,10 @@ PyArray_RegisterCanCast(PyArray_Descr *descr, int totype,
int i;
descr->f->cancastscalarkindto =
(int **)malloc(NPY_NSCALARKINDS* sizeof(int*));
+ if (descr->f->cancastscalarkindto == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
for (i = 0; i < NPY_NSCALARKINDS; i++) {
descr->f->cancastscalarkindto[i] = NULL;
}
@@ -265,11 +280,13 @@ PyArray_RegisterCanCast(PyArray_Descr *descr, int totype,
if (descr->f->cancastscalarkindto[scalar] == NULL) {
descr->f->cancastscalarkindto[scalar] =
(int *)malloc(1*sizeof(int));
+ if (descr->f->cancastscalarkindto[scalar] == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
descr->f->cancastscalarkindto[scalar][0] =
NPY_NOTYPE;
}
- descr->f->cancastscalarkindto[scalar] =
- _append_new(descr->f->cancastscalarkindto[scalar], totype);
+ return _append_new(&descr->f->cancastscalarkindto[scalar], totype);
}
- return 0;
}
diff --git a/numpy/core/src/npymath/halffloat.c b/numpy/core/src/npymath/halffloat.c
index c2bd28d60..84af86009 100644
--- a/numpy/core/src/npymath/halffloat.c
+++ b/numpy/core/src/npymath/halffloat.c
@@ -301,15 +301,23 @@ npy_uint16 npy_floatbits_to_halfbits(npy_uint32 f)
npy_set_floatstatus_underflow();
}
#endif
+ /*
+ * Usually the significand is shifted by 13. For subnormals an
+ * additional shift needs to occur. This shift is one for the largest
+ * exponent giving a subnormal `f_exp = 0x38000000 >> 23 = 112`, which
+ * offsets the new first bit. At most the shift can be 1+10 bits.
+ */
f_sig >>= (113 - f_exp);
/* Handle rounding by adding 1 to the bit beyond half precision */
#if NPY_HALF_ROUND_TIES_TO_EVEN
/*
* If the last bit in the half significand is 0 (already even), and
* the remaining bit pattern is 1000...0, then we do not add one
- * to the bit after the half significand. In all other cases, we do.
+ * to the bit after the half significand. However, the (113 - f_exp)
+ * shift can lose up to 11 bits, so the || checks them in the original.
+ * In all other cases, we can just add one.
*/
- if ((f_sig&0x00003fffu) != 0x00001000u) {
+ if (((f_sig&0x00003fffu) != 0x00001000u) || (f&0x000007ffu)) {
f_sig += 0x00001000u;
}
#else
@@ -416,7 +424,16 @@ npy_uint16 npy_doublebits_to_halfbits(npy_uint64 d)
npy_set_floatstatus_underflow();
}
#endif
- d_sig >>= (1009 - d_exp);
+ /*
+ * Unlike floats, doubles have enough room to shift left to align
+ * the subnormal significand leading to no loss of the last bits.
+ * The smallest possible exponent giving a subnormal is:
+ * `d_exp = 0x3e60000000000000 >> 52 = 998`. All larger subnormals are
+ * shifted with respect to it. This adds a shift of 10+1 bits the final
+ * right shift when comparing it to the one in the normal branch.
+ */
+ assert(d_exp - 998 >= 0);
+ d_sig <<= (d_exp - 998);
/* Handle rounding by adding 1 to the bit beyond half precision */
#if NPY_HALF_ROUND_TIES_TO_EVEN
/*
@@ -424,13 +441,13 @@ npy_uint16 npy_doublebits_to_halfbits(npy_uint64 d)
* the remaining bit pattern is 1000...0, then we do not add one
* to the bit after the half significand. In all other cases, we do.
*/
- if ((d_sig&0x000007ffffffffffULL) != 0x0000020000000000ULL) {
- d_sig += 0x0000020000000000ULL;
+ if ((d_sig&0x003fffffffffffffULL) != 0x0010000000000000ULL) {
+ d_sig += 0x0010000000000000ULL;
}
#else
- d_sig += 0x0000020000000000ULL;
+ d_sig += 0x0010000000000000ULL;
#endif
- h_sig = (npy_uint16) (d_sig >> 42);
+ h_sig = (npy_uint16) (d_sig >> 53);
/*
* If the rounding causes a bit to spill into h_exp, it will
* increment h_exp from zero to one and h_sig will be zero.
diff --git a/numpy/core/src/npymath/ieee754.c.src b/numpy/core/src/npymath/ieee754.c.src
index 8b5eef87a..3f66b24a4 100644
--- a/numpy/core/src/npymath/ieee754.c.src
+++ b/numpy/core/src/npymath/ieee754.c.src
@@ -568,13 +568,21 @@ int npy_get_floatstatus() {
/*
* Functions to set the floating point status word.
- * keep in sync with NO_FLOATING_POINT_SUPPORT in ufuncobject.h
*/
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif
+
+/*
+ * Define floating point status functions. We must define
+ * npy_get_floatstatus_barrier, npy_clear_floatstatus_barrier,
+ * npy_set_floatstatus_{divbyzero, overflow, underflow, invalid}
+ * for all supported platforms.
+ */
+
+
/* Solaris --------------------------------------------------------*/
/* --------ignoring SunOS ieee_flags approach, someone else can
** deal with that! */
@@ -626,117 +634,95 @@ void npy_set_floatstatus_invalid(void)
fpsetsticky(FP_X_INV);
}
+#elif defined(_AIX)
+#include <float.h>
+#include <fpxcp.h>
-#elif defined(__GLIBC__) || defined(__APPLE__) || \
- defined(__CYGWIN__) || defined(__MINGW32__) || \
- (defined(__FreeBSD__) && (__FreeBSD_version >= 502114))
-# include <fenv.h>
-
-int npy_get_floatstatus_barrier(char* param)
+int npy_get_floatstatus_barrier(char *param)
{
- int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW |
- FE_UNDERFLOW | FE_INVALID);
+ int fpstatus = fp_read_flag();
/*
* By using a volatile, the compiler cannot reorder this call
*/
if (param != NULL) {
volatile char NPY_UNUSED(c) = *(char*)param;
}
-
- return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) |
- ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) |
- ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) |
- ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0);
+ return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) |
+ ((FP_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) |
+ ((FP_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) |
+ ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0);
}
int npy_clear_floatstatus_barrier(char * param)
{
- /* testing float status is 50-100 times faster than clearing on x86 */
int fpstatus = npy_get_floatstatus_barrier(param);
- if (fpstatus != 0) {
- feclearexcept(FE_DIVBYZERO | FE_OVERFLOW |
- FE_UNDERFLOW | FE_INVALID);
- }
+ fp_swap_flag(0);
return fpstatus;
}
-
void npy_set_floatstatus_divbyzero(void)
{
- feraiseexcept(FE_DIVBYZERO);
+ fp_raise_xcp(FP_DIV_BY_ZERO);
}
void npy_set_floatstatus_overflow(void)
{
- feraiseexcept(FE_OVERFLOW);
+ fp_raise_xcp(FP_OVERFLOW);
}
void npy_set_floatstatus_underflow(void)
{
- feraiseexcept(FE_UNDERFLOW);
+ fp_raise_xcp(FP_UNDERFLOW);
}
void npy_set_floatstatus_invalid(void)
{
- feraiseexcept(FE_INVALID);
-}
-
-#elif defined(_AIX)
-#include <float.h>
-#include <fpxcp.h>
-
-int npy_get_floatstatus_barrier(char *param)
-{
- int fpstatus = fp_read_flag();
- /*
- * By using a volatile, the compiler cannot reorder this call
- */
- if (param != NULL) {
- volatile char NPY_UNUSED(c) = *(char*)param;
- }
- return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) |
- ((FP_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) |
- ((FP_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) |
- ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0);
+ fp_raise_xcp(FP_INVALID);
}
-int npy_clear_floatstatus_barrier(char * param)
-{
- int fpstatus = npy_get_floatstatus_barrier(param);
- fp_swap_flag(0);
+#elif defined(_MSC_VER) || (defined(__osf__) && defined(__alpha)) || \
+ defined (__UCLIBC__) || (defined(__arc__) && defined(__GLIBC__))
- return fpstatus;
-}
+/*
+ * By using a volatile floating point value,
+ * the compiler is forced to actually do the requested
+ * operations because of potential concurrency.
+ *
+ * We shouldn't write multiple values to a single
+ * global here, because that would cause
+ * a race condition.
+ */
+static volatile double _npy_floatstatus_x,
+ _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300,
+ _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf;
void npy_set_floatstatus_divbyzero(void)
{
- fp_raise_xcp(FP_DIV_BY_ZERO);
+ _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero;
}
void npy_set_floatstatus_overflow(void)
{
- fp_raise_xcp(FP_OVERFLOW);
+ _npy_floatstatus_x = _npy_floatstatus_big * 1e300;
}
void npy_set_floatstatus_underflow(void)
{
- fp_raise_xcp(FP_UNDERFLOW);
+ _npy_floatstatus_x = _npy_floatstatus_small * 1e-300;
}
void npy_set_floatstatus_invalid(void)
{
- fp_raise_xcp(FP_INVALID);
+ _npy_floatstatus_inf = NPY_INFINITY;
+ _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY;
}
-#else
-
/* MS Windows -----------------------------------------------------*/
#if defined(_MSC_VER)
#include <float.h>
-
int npy_get_floatstatus_barrier(char *param)
{
/*
@@ -796,53 +782,61 @@ int npy_clear_floatstatus_barrier(char *param)
return fpstatus;
}
+#endif
+/* End of defined(_MSC_VER) || (defined(__osf__) && defined(__alpha)) */
+
#else
+/* General GCC code, should work on most platforms */
+# include <fenv.h>
-int npy_get_floatstatus_barrier(char *NPY_UNUSED(param))
+int npy_get_floatstatus_barrier(char* param)
{
- return 0;
+ int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW |
+ FE_UNDERFLOW | FE_INVALID);
+ /*
+ * By using a volatile, the compiler cannot reorder this call
+ */
+ if (param != NULL) {
+ volatile char NPY_UNUSED(c) = *(char*)param;
+ }
+
+ return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) |
+ ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) |
+ ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) |
+ ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0);
}
-int npy_clear_floatstatus_barrier(char *param)
+int npy_clear_floatstatus_barrier(char * param)
{
+ /* testing float status is 50-100 times faster than clearing on x86 */
int fpstatus = npy_get_floatstatus_barrier(param);
- return 0;
-}
+ if (fpstatus != 0) {
+ feclearexcept(FE_DIVBYZERO | FE_OVERFLOW |
+ FE_UNDERFLOW | FE_INVALID);
+ }
-#endif
+ return fpstatus;
+}
-/*
- * By using a volatile floating point value,
- * the compiler is forced to actually do the requested
- * operations because of potential concurrency.
- *
- * We shouldn't write multiple values to a single
- * global here, because that would cause
- * a race condition.
- */
-static volatile double _npy_floatstatus_x,
- _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300,
- _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf;
void npy_set_floatstatus_divbyzero(void)
{
- _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero;
+ feraiseexcept(FE_DIVBYZERO);
}
void npy_set_floatstatus_overflow(void)
{
- _npy_floatstatus_x = _npy_floatstatus_big * 1e300;
+ feraiseexcept(FE_OVERFLOW);
}
void npy_set_floatstatus_underflow(void)
{
- _npy_floatstatus_x = _npy_floatstatus_small * 1e-300;
+ feraiseexcept(FE_UNDERFLOW);
}
void npy_set_floatstatus_invalid(void)
{
- _npy_floatstatus_inf = NPY_INFINITY;
- _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY;
+ feraiseexcept(FE_INVALID);
}
#endif
diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src
index cf427dad8..8c432e483 100644
--- a/numpy/core/src/npymath/npy_math_complex.c.src
+++ b/numpy/core/src/npymath/npy_math_complex.c.src
@@ -40,13 +40,14 @@
* flag in an efficient way. The flag is IEEE specific. See
* https://github.com/freebsd/freebsd/blob/4c6378299/lib/msun/src/catrig.c#L42
*/
+#if !defined(HAVE_CACOSF) || !defined(HAVE_CACOSL) || !defined(HAVE_CASINHF) || !defined(HAVE_CASINHL)
#define raise_inexact() do { \
volatile npy_float NPY_UNUSED(junk) = 1 + tiny; \
} while (0)
static const volatile npy_float tiny = 3.9443045e-31f;
-
+#endif
/**begin repeat
* #type = npy_float, npy_double, npy_longdouble#
@@ -64,9 +65,6 @@ static const volatile npy_float tiny = 3.9443045e-31f;
* Constants
*=========================================================*/
static const @ctype@ c_1@c@ = {1.0@C@, 0.0};
-static const @ctype@ c_half@c@ = {0.5@C@, 0.0};
-static const @ctype@ c_i@c@ = {0.0, 1.0@C@};
-static const @ctype@ c_ihalf@c@ = {0.0, 0.5@C@};
/*==========================================================
* Helper functions
@@ -76,22 +74,6 @@ static const @ctype@ c_ihalf@c@ = {0.0, 0.5@C@};
*=========================================================*/
static NPY_INLINE
@ctype@
-cadd@c@(@ctype@ a, @ctype@ b)
-{
- return npy_cpack@c@(npy_creal@c@(a) + npy_creal@c@(b),
- npy_cimag@c@(a) + npy_cimag@c@(b));
-}
-
-static NPY_INLINE
-@ctype@
-csub@c@(@ctype@ a, @ctype@ b)
-{
- return npy_cpack@c@(npy_creal@c@(a) - npy_creal@c@(b),
- npy_cimag@c@(a) - npy_cimag@c@(b));
-}
-
-static NPY_INLINE
-@ctype@
cmul@c@(@ctype@ a, @ctype@ b)
{
@type@ ar, ai, br, bi;
@@ -132,20 +114,6 @@ cdiv@c@(@ctype@ a, @ctype@ b)
}
}
-static NPY_INLINE
-@ctype@
-cneg@c@(@ctype@ a)
-{
- return npy_cpack@c@(-npy_creal@c@(a), -npy_cimag@c@(a));
-}
-
-static NPY_INLINE
-@ctype@
-cmuli@c@(@ctype@ a)
-{
- return npy_cpack@c@(-npy_cimag@c@(a), npy_creal@c@(a));
-}
-
/*==========================================================
* Custom implementation of missing complex C99 functions
*=========================================================*/
@@ -1246,7 +1214,7 @@ _clog_for_large_values@c@(@type@ x, @type@ y,
* Divide x and y by E, and then add 1 to the logarithm. This depends
* on E being larger than sqrt(2).
* Dividing by E causes an insignificant loss of accuracy; however
- * this method is still poor since it is uneccessarily slow.
+ * this method is still poor since it is unnecessarily slow.
*/
if (ax > @TMAX@ / 2) {
*rr = npy_log@c@(npy_hypot@c@(x / NPY_E@c@, y / NPY_E@c@)) + 1;
@@ -1430,19 +1398,14 @@ npy_casinh@c@(@ctype@ z)
#if @precision@ == 1
/* this is sqrt(6*EPS) */
const npy_float SQRT_6_EPSILON = 8.4572793338e-4f;
- /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */
- const volatile npy_float pio2_lo = 7.5497899549e-9f;
#endif
#if @precision@ == 2
const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08;
- const volatile npy_double pio2_lo = 6.1232339957367659e-17;
#endif
#if @precision@ == 3
const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l;
- const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l;
#endif
const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@;
- const @type@ pio2_hi = NPY_PI_2@c@;
@type@ x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2my2, new_y;
npy_int B_is_usable;
diff --git a/numpy/core/src/npymath/npy_math_internal.h.src b/numpy/core/src/npymath/npy_math_internal.h.src
index f2e5229b0..18b6d1434 100644
--- a/numpy/core/src/npymath/npy_math_internal.h.src
+++ b/numpy/core/src/npymath/npy_math_internal.h.src
@@ -654,7 +654,7 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
}
else {
/* if mod is zero ensure correct sign */
- mod = (b > 0) ? 0.0@c@ : -0.0@c@;
+ mod = npy_copysign@c@(0, b);
}
/* snap quotient to nearest integral value */
@@ -665,7 +665,7 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
}
else {
/* if div is zero ensure correct sign */
- floordiv = (a / b > 0) ? 0.0@c@ : -0.0@c@;
+ floordiv = npy_copysign@c@(0, a/b);
}
*modulus = mod;
@@ -716,3 +716,44 @@ npy_@func@@c@(@type@ a, @type@ b)
return npy_@func@u@c@(a < 0 ? -a : a, b < 0 ? -b : b);
}
/**end repeat**/
+
+/* Unlike LCM and GCD, we need byte and short variants for the shift operators,
+ * since the result is dependent on the width of the type
+ */
+/**begin repeat
+ *
+ * #type = byte, short, int, long, longlong#
+ * #c = hh,h,,l,ll#
+ */
+/**begin repeat1
+ *
+ * #u = u,#
+ * #is_signed = 0,1#
+ */
+NPY_INPLACE npy_@u@@type@
+npy_lshift@u@@c@(npy_@u@@type@ a, npy_@u@@type@ b)
+{
+ if (NPY_LIKELY((size_t)b < sizeof(a) * CHAR_BIT)) {
+ return a << b;
+ }
+ else {
+ return 0;
+ }
+}
+NPY_INPLACE npy_@u@@type@
+npy_rshift@u@@c@(npy_@u@@type@ a, npy_@u@@type@ b)
+{
+ if (NPY_LIKELY((size_t)b < sizeof(a) * CHAR_BIT)) {
+ return a >> b;
+ }
+#if @is_signed@
+ else if (a < 0) {
+ return (npy_@u@@type@)-1; /* preserve the sign bit */
+ }
+#endif
+ else {
+ return 0;
+ }
+}
+/**end repeat1**/
+/**end repeat**/
diff --git a/numpy/core/src/npysort/npysort_common.h b/numpy/core/src/npysort/npysort_common.h
index a22045b41..5fd03b96f 100644
--- a/numpy/core/src/npysort/npysort_common.h
+++ b/numpy/core/src/npysort/npysort_common.h
@@ -273,10 +273,10 @@ STRING_SWAP(char *s1, char *s2, size_t len)
NPY_INLINE static int
-STRING_LT(char *s1, char *s2, size_t len)
+STRING_LT(const char *s1, const char *s2, size_t len)
{
- const unsigned char *c1 = (unsigned char *)s1;
- const unsigned char *c2 = (unsigned char *)s2;
+ const unsigned char *c1 = (const unsigned char *)s1;
+ const unsigned char *c2 = (const unsigned char *)s2;
size_t i;
int ret = 0;
@@ -311,7 +311,7 @@ UNICODE_SWAP(npy_ucs4 *s1, npy_ucs4 *s2, size_t len)
NPY_INLINE static int
-UNICODE_LT(npy_ucs4 *s1, npy_ucs4 *s2, size_t len)
+UNICODE_LT(const npy_ucs4 *s1, const npy_ucs4 *s2, size_t len)
{
size_t i;
int ret = 0;
diff --git a/numpy/core/src/npysort/radixsort.c.src b/numpy/core/src/npysort/radixsort.c.src
new file mode 100644
index 000000000..72887d7e4
--- /dev/null
+++ b/numpy/core/src/npysort/radixsort.c.src
@@ -0,0 +1,231 @@
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+
+#include "npy_sort.h"
+#include "npysort_common.h"
+#include <stdlib.h>
+
+/*
+ *****************************************************************************
+ ** INTEGER SORTS **
+ *****************************************************************************
+ */
+
+
+/**begin repeat
+ *
+ * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
+ * LONGLONG, ULONGLONG#
+ * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ * longlong, ulonglong#
+ * #type = npy_ubyte, npy_ubyte, npy_ubyte, npy_ushort, npy_ushort, npy_uint,
+ * npy_uint, npy_ulong, npy_ulong, npy_ulonglong, npy_ulonglong#
+ * #sign = 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0#
+ * #floating = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0#
+ */
+
+// Reference: https://github.com/eloj/radix-sorting#-key-derivation
+#if @sign@
+ // Floating-point is currently disabled.
+ // Floating-point tests succeed for double and float on macOS but not on Windows/Linux.
+ // Basic sorting tests succeed but others relying on sort fail.
+ // Possibly related to floating-point normalisation or multiple NaN reprs? Not sure.
+ #if @floating@
+ // For floats, we invert the key if the sign bit is set, else we invert the sign bit.
+ #define KEY_OF(x) ((x) ^ (-((x) >> (sizeof(@type@) * 8 - 1)) | ((@type@)1 << (sizeof(@type@) * 8 - 1))))
+ #else
+ // For signed ints, we flip the sign bit so the negatives are below the positives.
+ #define KEY_OF(x) ((x) ^ ((@type@)1 << (sizeof(@type@) * 8 - 1)))
+ #endif
+#else
+ // For unsigned ints, the key is as-is
+ #define KEY_OF(x) (x)
+#endif
+
+static inline npy_ubyte
+nth_byte_@suff@(@type@ key, npy_intp l) {
+ return (key >> (l << 3)) & 0xFF;
+}
+
+@type@*
+radixsort0_@suff@(@type@ *arr, @type@ *aux, npy_intp num)
+{
+ npy_intp cnt[sizeof(@type@)][1 << 8] = { { 0 } };
+ npy_intp i;
+ size_t l;
+ @type@ key0 = KEY_OF(arr[0]);
+ size_t ncols = 0;
+ npy_ubyte cols[sizeof(@type@)];
+
+ for (i = 0; i < num; i++) {
+ @type@ k = KEY_OF(arr[i]);
+
+ for (l = 0; l < sizeof(@type@); l++) {
+ cnt[l][nth_byte_@suff@(k, l)]++;
+ }
+ }
+
+ for (l = 0; l < sizeof(@type@); l++) {
+ if (cnt[l][nth_byte_@suff@(key0, l)] != num) {
+ cols[ncols++] = l;
+ }
+ }
+
+ for (l = 0; l < ncols; l++) {
+ npy_intp a = 0;
+ for (i = 0; i < 256; i++) {
+ npy_intp b = cnt[cols[l]][i];
+ cnt[cols[l]][i] = a;
+ a += b;
+ }
+ }
+
+ for (l = 0; l < ncols; l++) {
+ @type@* temp;
+ for (i = 0; i < num; i++) {
+ @type@ k = KEY_OF(arr[i]);
+ npy_intp dst = cnt[cols[l]][nth_byte_@suff@(k, cols[l])]++;
+ aux[dst] = arr[i];
+ }
+
+ temp = aux;
+ aux = arr;
+ arr = temp;
+ }
+
+ return arr;
+}
+
+int
+radixsort_@suff@(void *start, npy_intp num, void *NPY_UNUSED(varr))
+{
+ void *sorted;
+ @type@ *aux;
+ @type@ *arr = start;
+ @type@ k1, k2;
+ npy_bool all_sorted = 1;
+
+ if (num < 2) {
+ return 0;
+ }
+
+ k1 = KEY_OF(arr[0]);
+ for (npy_intp i = 1; i < num; i++) {
+ k2 = KEY_OF(arr[i]);
+ if (k1 > k2) {
+ all_sorted = 0;
+ break;
+ }
+ k1 = k2;
+ }
+
+ if (all_sorted) {
+ return 0;
+ }
+
+ aux = malloc(num * sizeof(@type@));
+ if (aux == NULL) {
+ return -NPY_ENOMEM;
+ }
+
+ sorted = radixsort0_@suff@(start, aux, num);
+ if (sorted != start) {
+ memcpy(start, sorted, num * sizeof(@type@));
+ }
+
+ free(aux);
+ return 0;
+}
+
+npy_intp*
+aradixsort0_@suff@(@type@ *arr, npy_intp *aux, npy_intp *tosort, npy_intp num)
+{
+ npy_intp cnt[sizeof(@type@)][1 << 8] = { { 0 } };
+ npy_intp i;
+ size_t l;
+ @type@ key0 = KEY_OF(arr[0]);
+ size_t ncols = 0;
+ npy_ubyte cols[sizeof(@type@)];
+
+ for (i = 0; i < num; i++) {
+ @type@ k = KEY_OF(arr[i]);
+
+ for (l = 0; l < sizeof(@type@); l++) {
+ cnt[l][nth_byte_@suff@(k, l)]++;
+ }
+ }
+
+ for (l = 0; l < sizeof(@type@); l++) {
+ if (cnt[l][nth_byte_@suff@(key0, l)] != num) {
+ cols[ncols++] = l;
+ }
+ }
+
+ for (l = 0; l < ncols; l++) {
+ npy_intp a = 0;
+ for (i = 0; i < 256; i++) {
+ npy_intp b = cnt[cols[l]][i];
+ cnt[cols[l]][i] = a;
+ a += b;
+ }
+ }
+
+ for (l = 0; l < ncols; l++) {
+ npy_intp* temp;
+ for (i = 0; i < num; i++) {
+ @type@ k = KEY_OF(arr[tosort[i]]);
+ npy_intp dst = cnt[cols[l]][nth_byte_@suff@(k, cols[l])]++;
+ aux[dst] = tosort[i];
+ }
+
+ temp = aux;
+ aux = tosort;
+ tosort = temp;
+ }
+
+ return tosort;
+}
+
+int
+aradixsort_@suff@(void *start, npy_intp* tosort, npy_intp num, void *NPY_UNUSED(varr))
+{
+ npy_intp *sorted;
+ npy_intp *aux;
+ @type@ *arr = start;
+ @type@ k1, k2;
+ npy_bool all_sorted = 1;
+
+ if (num < 2) {
+ return 0;
+ }
+
+ k1 = KEY_OF(arr[tosort[0]]);
+ for (npy_intp i = 1; i < num; i++) {
+ k2 = KEY_OF(arr[tosort[i]]);
+ if (k1 > k2) {
+ all_sorted = 0;
+ break;
+ }
+ k1 = k2;
+ }
+
+ if (all_sorted) {
+ return 0;
+ }
+
+ aux = malloc(num * sizeof(npy_intp));
+ if (aux == NULL) {
+ return -NPY_ENOMEM;
+ }
+
+ sorted = aradixsort0_@suff@(start, aux, tosort, num);
+ if (sorted != tosort) {
+ memcpy(tosort, sorted, num * sizeof(npy_intp));
+ }
+
+ free(aux);
+ return 0;
+}
+
+#undef KEY_OF
+
+/**end repeat**/
diff --git a/numpy/core/src/npysort/selection.c.src b/numpy/core/src/npysort/selection.c.src
index 1e0934558..be645450f 100644
--- a/numpy/core/src/npysort/selection.c.src
+++ b/numpy/core/src/npysort/selection.c.src
@@ -40,7 +40,7 @@ static NPY_INLINE void store_pivot(npy_intp pivot, npy_intp kth,
}
/*
- * If pivot is the requested kth store it, overwritting other pivots if
+ * If pivot is the requested kth store it, overwriting other pivots if
* required. This must be done so iterative partition can work without
* manually shifting lower data offset by kth each time
*/
diff --git a/numpy/core/src/npysort/timsort.c.src b/numpy/core/src/npysort/timsort.c.src
new file mode 100644
index 000000000..26313ca5b
--- /dev/null
+++ b/numpy/core/src/npysort/timsort.c.src
@@ -0,0 +1,2574 @@
+/* -*- c -*- */
+
+/*
+ * The purpose of this module is to add faster sort functions
+ * that are type-specific. This is done by altering the
+ * function table for the builtin descriptors.
+ *
+ * These sorting functions are copied almost directly from numarray
+ * with a few modifications (complex comparisons compare the imaginary
+ * part if the real parts are equal, for example), and the names
+ * are changed.
+ *
+ * The original sorting code is due to Charles R. Harris who wrote
+ * it for numarray.
+ */
+
+/*
+ * Quick sort is usually the fastest, but the worst case scenario can
+ * be slower than the merge and heap sorts. The merge sort requires
+ * extra memory and so for large arrays may not be useful.
+ *
+ * The merge sort is *stable*, meaning that equal components
+ * are unmoved from their entry versions, so it can be used to
+ * implement lexigraphic sorting on multiple keys.
+ *
+ * The heap sort is included for completeness.
+ */
+
+
+/* For details of Timsort, refer to
+ * https://github.com/python/cpython/blob/3.7/Objects/listsort.txt
+ */
+
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+
+#include "npy_sort.h"
+#include "npysort_common.h"
+#include <stdlib.h>
+
+/* enough for 32 * 1.618 ** 128 elements */
+#define TIMSORT_STACK_SIZE 128
+
+
+
+npy_intp compute_min_run(npy_intp num)
+{
+ npy_intp r = 0;
+
+ while (64 < num) {
+ r |= num & 1;
+ num >>= 1;
+ }
+
+ return num + r;
+}
+
+typedef struct {
+ npy_intp s; /* start pointer */
+ npy_intp l; /* length */
+} run;
+
+
+/* buffer for argsort. Declared here to avoid multiple declarations. */
+typedef struct {
+ npy_intp *pw;
+ npy_intp size;
+} buffer_intp;
+
+
+/* buffer method */
+static NPY_INLINE int
+resize_buffer_intp(buffer_intp *buffer, npy_intp new_size)
+{
+ if (new_size <= buffer->size) {
+ return 0;
+ }
+
+ if (NPY_UNLIKELY(buffer->pw == NULL)) {
+ buffer->pw = malloc(new_size * sizeof(npy_intp));
+ } else {
+ buffer->pw = realloc(buffer->pw, new_size * sizeof(npy_intp));
+ }
+
+ buffer->size = new_size;
+
+ if (NPY_UNLIKELY(buffer->pw == NULL)) {
+ return -NPY_ENOMEM;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ *****************************************************************************
+ ** NUMERIC SORTS **
+ *****************************************************************************
+ */
+
+
+/**begin repeat
+ *
+ * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
+ * LONGLONG, ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE,
+ * CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA#
+ * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ * longlong, ulonglong, half, float, double, longdouble,
+ * cfloat, cdouble, clongdouble, datetime, timedelta#
+ * #type = npy_bool, npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int,
+ * npy_uint, npy_long, npy_ulong, npy_longlong, npy_ulonglong,
+ * npy_ushort, npy_float, npy_double, npy_longdouble, npy_cfloat,
+ * npy_cdouble, npy_clongdouble, npy_datetime, npy_timedelta#
+ */
+
+
+typedef struct {
+ @type@ * pw;
+ npy_intp size;
+} buffer_@suff@;
+
+
+static NPY_INLINE int
+resize_buffer_@suff@(buffer_@suff@ *buffer, npy_intp new_size)
+{
+ if (new_size <= buffer->size) {
+ return 0;
+ }
+
+ if (NPY_UNLIKELY(buffer->pw == NULL)) {
+ buffer->pw = malloc(new_size * sizeof(@type@));
+ } else {
+ buffer->pw = realloc(buffer->pw, new_size * sizeof(@type@));
+ }
+
+ buffer->size = new_size;
+
+ if (NPY_UNLIKELY(buffer->pw == NULL)) {
+ return -NPY_ENOMEM;
+ } else {
+ return 0;
+ }
+}
+
+
+static npy_intp
+count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun)
+{
+ npy_intp sz;
+ @type@ vc, *pl, *pi, *pj, *pr;
+
+ if (NPY_UNLIKELY(num - l == 1)) {
+ return 1;
+ }
+
+ pl = arr + l;
+
+ /* (not strictly) ascending sequence */
+ if (!@TYPE@_LT(*(pl + 1), *pl)) {
+ for (pi = pl + 1; pi < arr + num - 1 && !@TYPE@_LT(*(pi + 1), *pi); ++pi) {
+ }
+ } else { /* (strictly) descending sequence */
+ for (pi = pl + 1; pi < arr + num - 1 && @TYPE@_LT(*(pi + 1), *pi); ++pi) {
+ }
+
+ for (pj = pl, pr = pi; pj < pr; ++pj, --pr) {
+ @TYPE@_SWAP(*pj, *pr);
+ }
+ }
+
+ ++pi;
+ sz = pi - pl;
+
+ if (sz < minrun) {
+ if (l + minrun < num) {
+ sz = minrun;
+ } else {
+ sz = num - l;
+ }
+
+ pr = pl + sz;
+
+ /* insertion sort */
+ for (; pi < pr; ++pi) {
+ vc = *pi;
+ pj = pi;
+
+ while (pl < pj && @TYPE@_LT(vc, *(pj - 1))) {
+ *pj = *(pj - 1);
+ --pj;
+ }
+
+ *pj = vc;
+ }
+ }
+
+ return sz;
+}
+
+
+/* when the left part of the array (p1) is smaller, copy p1 to buffer
+ * and merge from left to right
+ */
+static void
+merge_left_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2,
+ @type@ *p3)
+{
+ @type@ *end = p2 + l2;
+ memcpy(p3, p1, sizeof(@type@) * l1);
+ /* first element must be in p2 otherwise skipped in the caller */
+ *p1++ = *p2++;
+
+ while (p1 < p2 && p2 < end) {
+ if (@TYPE@_LT(*p2, *p3)) {
+ *p1++ = *p2++;
+ } else {
+ *p1++ = *p3++;
+ }
+ }
+
+ if (p1 != p2) {
+ memcpy(p1, p3, sizeof(@type@) * (p2 - p1));
+ }
+}
+
+
+/* when the right part of the array (p2) is smaller, copy p2 to buffer
+ * and merge from right to left
+ */
+static void
+merge_right_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2,
+ @type@ *p3)
+{
+ npy_intp ofs;
+ @type@ *start = p1 - 1;
+ memcpy(p3, p2, sizeof(@type@) * l2);
+ p1 += l1 - 1;
+ p2 += l2 - 1;
+ p3 += l2 - 1;
+ /* first element must be in p1 otherwise skipped in the caller */
+ *p2-- = *p1--;
+
+ while (p1 < p2 && start < p1) {
+ if (@TYPE@_LT(*p3, *p1)) {
+ *p2-- = *p1--;
+ } else {
+ *p2-- = *p3--;
+ }
+ }
+
+ if (p1 != p2) {
+ ofs = p2 - start;
+ memcpy(start + 1, p3 - ofs + 1, sizeof(@type@) * ofs);
+ }
+}
+
+
+/* Note: the naming convention of gallop functions are different from that of
+ * CPython. For example, here gallop_right means gallop from left toward right,
+ * whereas in CPython gallop_right means gallop
+ * and find the right most element among equal elements
+ */
+static npy_intp
+gallop_right_@suff@(const @type@ *arr, const npy_intp size, const @type@ key)
+{
+ npy_intp last_ofs, ofs, m;
+
+ if (@TYPE@_LT(key, arr[0])) {
+ return 0;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size; /* arr[ofs] is never accessed */
+ break;
+ }
+
+ if (@TYPE@_LT(key, arr[ofs])) {
+ break;
+ } else {
+ last_ofs = ofs;
+ /* ofs = 1, 3, 7, 15... */
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[last_ofs] <= key < arr[ofs] */
+ while (last_ofs + 1 < ofs) {
+ m = last_ofs + ((ofs - last_ofs) >> 1);
+
+ if (@TYPE@_LT(key, arr[m])) {
+ ofs = m;
+ } else {
+ last_ofs = m;
+ }
+ }
+
+ /* now that arr[ofs-1] <= key < arr[ofs] */
+ return ofs;
+}
+
+
+static npy_intp
+gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ key)
+{
+ npy_intp last_ofs, ofs, l, m, r;
+
+ if (@TYPE@_LT(arr[size - 1], key)) {
+ return size;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size;
+ break;
+ }
+
+ if (@TYPE@_LT(arr[size - ofs - 1], key)) {
+ break;
+ } else {
+ last_ofs = ofs;
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[size-ofs-1] < key <= arr[size-last_ofs-1] */
+ l = size - ofs - 1;
+ r = size - last_ofs - 1;
+
+ while (l + 1 < r) {
+ m = l + ((r - l) >> 1);
+
+ if (@TYPE@_LT(arr[m], key)) {
+ l = m;
+ } else {
+ r = m;
+ }
+ }
+
+ /* now that arr[r-1] < key <= arr[r] */
+ return r;
+}
+
+
+static int
+merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at,
+ buffer_@suff@ *buffer)
+{
+ int ret;
+ npy_intp s1, l1, s2, l2, k;
+ @type@ *p1, *p2;
+ s1 = stack[at].s;
+ l1 = stack[at].l;
+ s2 = stack[at + 1].s;
+ l2 = stack[at + 1].l;
+ /* arr[s2] belongs to arr[s1+k].
+ * if try to comment this out for debugging purpose, remember
+ * in the merging process the first element is skipped
+ */
+ k = gallop_right_@suff@(arr + s1, l1, arr[s2]);
+
+ if (l1 == k) {
+ /* already sorted */
+ return 0;
+ }
+
+ p1 = arr + s1 + k;
+ l1 -= k;
+ p2 = arr + s2;
+ /* arr[s2-1] belongs to arr[s2+l2] */
+ l2 = gallop_left_@suff@(arr + s2, l2, arr[s2 - 1]);
+
+ if (l2 < l1) {
+ ret = resize_buffer_@suff@(buffer, l2);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ merge_right_@suff@(p1, l1, p2, l2, buffer->pw);
+ } else {
+ ret = resize_buffer_@suff@(buffer, l1);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ merge_left_@suff@(p1, l1, p2, l2, buffer->pw);
+ }
+
+ return 0;
+}
+
+
+static int
+try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr,
+ buffer_@suff@ *buffer)
+{
+ int ret;
+ npy_intp A, B, C, top;
+ top = *stack_ptr;
+
+ while (1 < top) {
+ B = stack[top - 2].l;
+ C = stack[top - 1].l;
+
+ if ((2 < top && stack[top - 3].l <= B + C) ||
+ (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) {
+ A = stack[top - 3].l;
+
+ if (A <= C) {
+ ret = merge_at_@suff@(arr, stack, top - 3, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += B;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = merge_at_@suff@(arr, stack, top - 2, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ }
+ } else if (1 < top && B <= C) {
+ ret = merge_at_@suff@(arr, stack, top - 2, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ } else {
+ break;
+ }
+ }
+
+ *stack_ptr = top;
+ return 0;
+}
+
+static int
+force_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr,
+ buffer_@suff@ *buffer)
+{
+ int ret;
+ npy_intp top = *stack_ptr;
+
+ while (2 < top) {
+ if (stack[top - 3].l <= stack[top - 1].l) {
+ ret = merge_at_@suff@(arr, stack, top - 3, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += stack[top - 2].l;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = merge_at_@suff@(arr, stack, top - 2, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += stack[top - 1].l;
+ --top;
+ }
+ }
+
+ if (1 < top) {
+ ret = merge_at_@suff@(arr, stack, top - 2, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+ }
+
+ return 0;
+}
+
+
+int
+timsort_@suff@(void *start, npy_intp num, void *NPY_UNUSED(varr))
+{
+ int ret;
+ npy_intp l, n, stack_ptr, minrun;
+ buffer_@suff@ buffer;
+ run stack[TIMSORT_STACK_SIZE];
+ buffer.pw = NULL;
+ buffer.size = 0;
+ stack_ptr = 0;
+ minrun = compute_min_run(num);
+
+ for (l = 0; l < num;) {
+ n = count_run_@suff@(start, l, num, minrun);
+ stack[stack_ptr].s = l;
+ stack[stack_ptr].l = n;
+ ++stack_ptr;
+ ret = try_collapse_@suff@(start, stack, &stack_ptr, &buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ l += n;
+ }
+
+ ret = force_collapse_@suff@(start, stack, &stack_ptr, &buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ ret = 0;
+cleanup:
+
+ if (buffer.pw != NULL) {
+ free(buffer.pw);
+ }
+
+ return ret;
+}
+
+
+/* argsort */
+
+
+static npy_intp
+acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num,
+ npy_intp minrun)
+{
+ npy_intp sz;
+ @type@ vc;
+ npy_intp vi;
+ npy_intp *pl, *pi, *pj, *pr;
+
+ if (NPY_UNLIKELY(num - l == 1)) {
+ return 1;
+ }
+
+ pl = tosort + l;
+
+ /* (not strictly) ascending sequence */
+ if (!@TYPE@_LT(arr[*(pl + 1)], arr[*pl])) {
+ for (pi = pl + 1; pi < tosort + num - 1
+ && !@TYPE@_LT(arr[*(pi + 1)], arr[*pi]); ++pi) {
+ }
+ } else { /* (strictly) descending sequence */
+ for (pi = pl + 1; pi < tosort + num - 1
+ && @TYPE@_LT(arr[*(pi + 1)], arr[*pi]); ++pi) {
+ }
+
+ for (pj = pl, pr = pi; pj < pr; ++pj, --pr) {
+ INTP_SWAP(*pj, *pr);
+ }
+ }
+
+ ++pi;
+ sz = pi - pl;
+
+ if (sz < minrun) {
+ if (l + minrun < num) {
+ sz = minrun;
+ } else {
+ sz = num - l;
+ }
+
+ pr = pl + sz;
+
+ /* insertion sort */
+ for (; pi < pr; ++pi) {
+ vi = *pi;
+ vc = arr[*pi];
+ pj = pi;
+
+ while (pl < pj && @TYPE@_LT(vc, arr[*(pj - 1)])) {
+ *pj = *(pj - 1);
+ --pj;
+ }
+
+ *pj = vi;
+ }
+ }
+
+ return sz;
+}
+
+
+static npy_intp
+agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort,
+ const npy_intp size, const @type@ key)
+{
+ npy_intp last_ofs, ofs, m;
+
+ if (@TYPE@_LT(key, arr[tosort[0]])) {
+ return 0;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size; /* arr[ofs] is never accessed */
+ break;
+ }
+
+ if (@TYPE@_LT(key, arr[tosort[ofs]])) {
+ break;
+ } else {
+ last_ofs = ofs;
+ /* ofs = 1, 3, 7, 15... */
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[tosort[last_ofs]] <= key < arr[tosort[ofs]] */
+ while (last_ofs + 1 < ofs) {
+ m = last_ofs + ((ofs - last_ofs) >> 1);
+
+ if (@TYPE@_LT(key, arr[tosort[m]])) {
+ ofs = m;
+ } else {
+ last_ofs = m;
+ }
+ }
+
+ /* now that arr[tosort[ofs-1]] <= key < arr[tosort[ofs]] */
+ return ofs;
+}
+
+
+
+static npy_intp
+agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort,
+ const npy_intp size, const @type@ key)
+{
+ npy_intp last_ofs, ofs, l, m, r;
+
+ if (@TYPE@_LT(arr[tosort[size - 1]], key)) {
+ return size;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size;
+ break;
+ }
+
+ if (@TYPE@_LT(arr[tosort[size - ofs - 1]], key)) {
+ break;
+ } else {
+ last_ofs = ofs;
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[tosort[size-ofs-1]] < key <= arr[tosort[size-last_ofs-1]] */
+ l = size - ofs - 1;
+ r = size - last_ofs - 1;
+
+ while (l + 1 < r) {
+ m = l + ((r - l) >> 1);
+
+ if (@TYPE@_LT(arr[tosort[m]], key)) {
+ l = m;
+ } else {
+ r = m;
+ }
+ }
+
+ /* now that arr[tosort[r-1]] < key <= arr[tosort[r]] */
+ return r;
+}
+
+
+static void
+amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2,
+ npy_intp l2,
+ npy_intp *p3)
+{
+ npy_intp *end = p2 + l2;
+ memcpy(p3, p1, sizeof(npy_intp) * l1);
+ /* first element must be in p2 otherwise skipped in the caller */
+ *p1++ = *p2++;
+
+ while (p1 < p2 && p2 < end) {
+ if (@TYPE@_LT(arr[*p2], arr[*p3])) {
+ *p1++ = *p2++;
+ } else {
+ *p1++ = *p3++;
+ }
+ }
+
+ if (p1 != p2) {
+ memcpy(p1, p3, sizeof(npy_intp) * (p2 - p1));
+ }
+}
+
+
+static void
+amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2,
+ npy_intp l2,
+ npy_intp *p3)
+{
+ npy_intp ofs;
+ npy_intp *start = p1 - 1;
+ memcpy(p3, p2, sizeof(npy_intp) * l2);
+ p1 += l1 - 1;
+ p2 += l2 - 1;
+ p3 += l2 - 1;
+ /* first element must be in p1 otherwise skipped in the caller */
+ *p2-- = *p1--;
+
+ while (p1 < p2 && start < p1) {
+ if (@TYPE@_LT(arr[*p3], arr[*p1])) {
+ *p2-- = *p1--;
+ } else {
+ *p2-- = *p3--;
+ }
+ }
+
+ if (p1 != p2) {
+ ofs = p2 - start;
+ memcpy(start + 1, p3 - ofs + 1, sizeof(npy_intp) * ofs);
+ }
+}
+
+
+static int
+amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack,
+ const npy_intp at,
+ buffer_intp *buffer)
+{
+ int ret;
+ npy_intp s1, l1, s2, l2, k;
+ npy_intp *p1, *p2;
+ s1 = stack[at].s;
+ l1 = stack[at].l;
+ s2 = stack[at + 1].s;
+ l2 = stack[at + 1].l;
+ /* tosort[s2] belongs to tosort[s1+k] */
+ k = agallop_right_@suff@(arr, tosort + s1, l1, arr[tosort[s2]]);
+
+ if (l1 == k) {
+ /* already sorted */
+ return 0;
+ }
+
+ p1 = tosort + s1 + k;
+ l1 -= k;
+ p2 = tosort + s2;
+ /* tosort[s2-1] belongs to tosort[s2+l2] */
+ l2 = agallop_left_@suff@(arr, tosort + s2, l2, arr[tosort[s2 - 1]]);
+
+ if (l2 < l1) {
+ ret = resize_buffer_intp(buffer, l2);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ amerge_right_@suff@(arr, p1, l1, p2, l2, buffer->pw);
+ } else {
+ ret = resize_buffer_intp(buffer, l1);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ amerge_left_@suff@(arr, p1, l1, p2, l2, buffer->pw);
+ }
+
+ return 0;
+}
+
+
+static int
+atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack,
+ npy_intp *stack_ptr,
+ buffer_intp *buffer)
+{
+ int ret;
+ npy_intp A, B, C, top;
+ top = *stack_ptr;
+
+ while (1 < top) {
+ B = stack[top - 2].l;
+ C = stack[top - 1].l;
+
+ if ((2 < top && stack[top - 3].l <= B + C) ||
+ (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) {
+ A = stack[top - 3].l;
+
+ if (A <= C) {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += B;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ }
+ } else if (1 < top && B <= C) {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ } else {
+ break;
+ }
+ }
+
+ *stack_ptr = top;
+ return 0;
+}
+
+
+static int
+aforce_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack,
+ npy_intp *stack_ptr,
+ buffer_intp *buffer)
+{
+ int ret;
+ npy_intp top = *stack_ptr;
+
+ while (2 < top) {
+ if (stack[top - 3].l <= stack[top - 1].l) {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += stack[top - 2].l;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += stack[top - 1].l;
+ --top;
+ }
+ }
+
+ if (1 < top) {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+ }
+
+ return 0;
+}
+
+
+int
+atimsort_@suff@(void *v, npy_intp *tosort, npy_intp num,
+ void *NPY_UNUSED(varr))
+{
+ int ret;
+ npy_intp l, n, stack_ptr, minrun;
+ buffer_intp buffer;
+ run stack[TIMSORT_STACK_SIZE];
+ buffer.pw = NULL;
+ buffer.size = 0;
+ stack_ptr = 0;
+ minrun = compute_min_run(num);
+
+ for (l = 0; l < num;) {
+ n = acount_run_@suff@(v, tosort, l, num, minrun);
+ stack[stack_ptr].s = l;
+ stack[stack_ptr].l = n;
+ ++stack_ptr;
+ ret = atry_collapse_@suff@(v, tosort, stack, &stack_ptr, &buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ l += n;
+ }
+
+ ret = aforce_collapse_@suff@(v, tosort, stack, &stack_ptr, &buffer);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ ret = 0;
+cleanup:
+
+ if (buffer.pw != NULL) {
+ free(buffer.pw);
+ }
+
+ return ret;
+}
+
+/**end repeat**/
+
+
+
+/* For string sorts and generic sort, element comparisons are very expensive,
+ * and the time cost of insertion sort (involves N**2 comparison) clearly hurts.
+ * Implementing binary insertion sort and probably gallop mode during merging process
+ * can hopefully boost the performance. Here as a temporary workaround we use shorter
+ * run length to reduce the cost of insertion sort.
+ */
+
+npy_intp compute_min_run_short(npy_intp num)
+{
+ npy_intp r = 0;
+
+ while (16 < num) {
+ r |= num & 1;
+ num >>= 1;
+ }
+
+ return num + r;
+}
+
+/*
+ *****************************************************************************
+ ** STRING SORTS **
+ *****************************************************************************
+ */
+
+
+/**begin repeat
+ *
+ * #TYPE = STRING, UNICODE#
+ * #suff = string, unicode#
+ * #type = npy_char, npy_ucs4#
+ */
+
+
+typedef struct {
+ @type@ * pw;
+ npy_intp size;
+ size_t len;
+} buffer_@suff@;
+
+
+static NPY_INLINE int
+resize_buffer_@suff@(buffer_@suff@ *buffer, npy_intp new_size)
+{
+ if (new_size <= buffer->size) {
+ return 0;
+ }
+
+ if (NPY_UNLIKELY(buffer->pw == NULL)) {
+ buffer->pw = malloc(sizeof(@type@) * new_size * buffer->len);
+ } else {
+ buffer->pw = realloc(buffer->pw, sizeof(@type@) * new_size * buffer->len);
+ }
+
+ buffer->size = new_size;
+
+ if (NPY_UNLIKELY(buffer->pw == NULL)) {
+ return -NPY_ENOMEM;
+ } else {
+ return 0;
+ }
+}
+
+
+static npy_intp
+count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun,
+ @type@ *vp, size_t len)
+{
+ npy_intp sz;
+ @type@ *pl, *pi, *pj, *pr;
+
+ if (NPY_UNLIKELY(num - l == 1)) {
+ return 1;
+ }
+
+ pl = arr + l * len;
+
+ /* (not strictly) ascending sequence */
+ if (!@TYPE@_LT(pl + len, pl, len)) {
+ for (pi = pl + len; pi < arr + (num - 1) * len
+ && !@TYPE@_LT(pi + len, pi, len); pi += len) {
+ }
+ } else { /* (strictly) descending sequence */
+ for (pi = pl + len; pi < arr + (num - 1) * len
+ && @TYPE@_LT(pi + len, pi, len); pi += len) {
+ }
+
+ for (pj = pl, pr = pi; pj < pr; pj += len, pr -= len) {
+ @TYPE@_SWAP(pj, pr, len);
+ }
+ }
+
+ pi += len;
+ sz = (pi - pl) / len;
+
+ if (sz < minrun) {
+ if (l + minrun < num) {
+ sz = minrun;
+ } else {
+ sz = num - l;
+ }
+
+ pr = pl + sz * len;
+
+ /* insertion sort */
+ for (; pi < pr; pi += len) {
+ @TYPE@_COPY(vp, pi, len);
+ pj = pi;
+
+ while (pl < pj && @TYPE@_LT(vp, pj - len, len)) {
+ @TYPE@_COPY(pj, pj - len, len);
+ pj -= len;
+ }
+
+ @TYPE@_COPY(pj, vp, len);
+ }
+ }
+
+ return sz;
+}
+
+
+static npy_intp
+gallop_right_@suff@(const @type@ *arr, const npy_intp size,
+ const @type@ *key, size_t len)
+{
+ npy_intp last_ofs, ofs, m;
+
+ if (@TYPE@_LT(key, arr, len)) {
+ return 0;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size; /* arr[ofs] is never accessed */
+ break;
+ }
+
+ if (@TYPE@_LT(key, arr + ofs * len, len)) {
+ break;
+ } else {
+ last_ofs = ofs;
+ /* ofs = 1, 3, 7, 15... */
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[last_ofs*len] <= key < arr[ofs*len] */
+ while (last_ofs + 1 < ofs) {
+ m = last_ofs + ((ofs - last_ofs) >> 1);
+
+ if (@TYPE@_LT(key, arr + m * len, len)) {
+ ofs = m;
+ } else {
+ last_ofs = m;
+ }
+ }
+
+ /* now that arr[(ofs-1)*len] <= key < arr[ofs*len] */
+ return ofs;
+}
+
+
+
+static npy_intp
+gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ *key,
+ size_t len)
+{
+ npy_intp last_ofs, ofs, l, m, r;
+
+ if (@TYPE@_LT(arr + (size - 1) * len, key, len)) {
+ return size;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size;
+ break;
+ }
+
+ if (@TYPE@_LT(arr + (size - ofs - 1) * len, key, len)) {
+ break;
+ } else {
+ last_ofs = ofs;
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[(size-ofs-1)*len] < key <= arr[(size-last_ofs-1)*len] */
+ l = size - ofs - 1;
+ r = size - last_ofs - 1;
+
+ while (l + 1 < r) {
+ m = l + ((r - l) >> 1);
+
+ if (@TYPE@_LT(arr + m * len, key, len)) {
+ l = m;
+ } else {
+ r = m;
+ }
+ }
+
+ /* now that arr[(r-1)*len] < key <= arr[r*len] */
+ return r;
+}
+
+
+static void
+merge_left_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2,
+ @type@ *p3, size_t len)
+{
+ @type@ *end = p2 + l2 * len;
+ memcpy(p3, p1, sizeof(@type@) * l1 * len);
+ /* first element must be in p2 otherwise skipped in the caller */
+ @TYPE@_COPY(p1, p2, len);
+ p1 += len;
+ p2 += len;
+
+ while (p1 < p2 && p2 < end) {
+ if (@TYPE@_LT(p2, p3, len)) {
+ @TYPE@_COPY(p1, p2, len);
+ p1 += len;
+ p2 += len;
+ } else {
+ @TYPE@_COPY(p1, p3, len);
+ p1 += len;
+ p3 += len;
+ }
+ }
+
+ if (p1 != p2) {
+ memcpy(p1, p3, sizeof(@type@) * (p2 - p1));
+ }
+}
+
+
+static void
+merge_right_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2,
+ @type@ *p3, size_t len)
+{
+ npy_intp ofs;
+ @type@ *start = p1 - len;
+ memcpy(p3, p2, sizeof(@type@) * l2 * len);
+ p1 += (l1 - 1) * len;
+ p2 += (l2 - 1) * len;
+ p3 += (l2 - 1) * len;
+ /* first element must be in p1 otherwise skipped in the caller */
+ @TYPE@_COPY(p2, p1, len);
+ p2 -= len;
+ p1 -= len;
+
+ while (p1 < p2 && start < p1) {
+ if (@TYPE@_LT(p3, p1, len)) {
+ @TYPE@_COPY(p2, p1, len);
+ p2 -= len;
+ p1 -= len;
+ } else {
+ @TYPE@_COPY(p2, p3, len);
+ p2 -= len;
+ p3 -= len;
+ }
+ }
+
+ if (p1 != p2) {
+ ofs = p2 - start;
+ memcpy(start + len, p3 - ofs + len, sizeof(@type@) * ofs);
+ }
+}
+
+
+static int
+merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at,
+ buffer_@suff@ *buffer, size_t len)
+{
+ int ret;
+ npy_intp s1, l1, s2, l2, k;
+ @type@ *p1, *p2;
+ s1 = stack[at].s;
+ l1 = stack[at].l;
+ s2 = stack[at + 1].s;
+ l2 = stack[at + 1].l;
+ /* arr[s2] belongs to arr[s1+k] */
+ @TYPE@_COPY(buffer->pw, arr + s2 * len, len);
+ k = gallop_right_@suff@(arr + s1 * len, l1, buffer->pw, len);
+
+ if (l1 == k) {
+ /* already sorted */
+ return 0;
+ }
+
+ p1 = arr + (s1 + k) * len;
+ l1 -= k;
+ p2 = arr + s2 * len;
+ /* arr[s2-1] belongs to arr[s2+l2] */
+ @TYPE@_COPY(buffer->pw, arr + (s2 - 1) * len, len);
+ l2 = gallop_left_@suff@(arr + s2 * len, l2, buffer->pw, len);
+
+ if (l2 < l1) {
+ ret = resize_buffer_@suff@(buffer, l2);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ merge_right_@suff@(p1, l1, p2, l2, buffer->pw, len);
+ } else {
+ ret = resize_buffer_@suff@(buffer, l1);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ merge_left_@suff@(p1, l1, p2, l2, buffer->pw, len);
+ }
+
+ return 0;
+}
+
+
+static int
+try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr,
+ buffer_@suff@ *buffer, size_t len)
+{
+ int ret;
+ npy_intp A, B, C, top;
+ top = *stack_ptr;
+
+ while (1 < top) {
+ B = stack[top - 2].l;
+ C = stack[top - 1].l;
+
+ if ((2 < top && stack[top - 3].l <= B + C) ||
+ (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) {
+ A = stack[top - 3].l;
+
+ if (A <= C) {
+ ret = merge_at_@suff@(arr, stack, top - 3, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += B;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = merge_at_@suff@(arr, stack, top - 2, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ }
+ } else if (1 < top && B <= C) {
+ ret = merge_at_@suff@(arr, stack, top - 2, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ } else {
+ break;
+ }
+ }
+
+ *stack_ptr = top;
+ return 0;
+}
+
+
+static int
+force_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr,
+ buffer_@suff@ *buffer, size_t len)
+{
+ int ret;
+ npy_intp top = *stack_ptr;
+
+ while (2 < top) {
+ if (stack[top - 3].l <= stack[top - 1].l) {
+ ret = merge_at_@suff@(arr, stack, top - 3, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += stack[top - 2].l;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = merge_at_@suff@(arr, stack, top - 2, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += stack[top - 1].l;
+ --top;
+ }
+ }
+
+ if (1 < top) {
+ ret = merge_at_@suff@(arr, stack, top - 2, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+ }
+
+ return 0;
+}
+
+
+int
+timsort_@suff@(void *start, npy_intp num, void *varr)
+{
+ PyArrayObject *arr = varr;
+ size_t elsize = PyArray_ITEMSIZE(arr);
+ size_t len = elsize / sizeof(@type@);
+ int ret;
+ npy_intp l, n, stack_ptr, minrun;
+ run stack[TIMSORT_STACK_SIZE];
+ buffer_@suff@ buffer;
+
+ /* Items that have zero size don't make sense to sort */
+ if (len == 0) {
+ return 0;
+ }
+
+ buffer.pw = NULL;
+ buffer.size = 0;
+ buffer.len = len;
+ stack_ptr = 0;
+ minrun = compute_min_run_short(num);
+ /* used for insertion sort and gallop key */
+ ret = resize_buffer_@suff@(&buffer, 1);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ for (l = 0; l < num;) {
+ n = count_run_@suff@(start, l, num, minrun, buffer.pw, len);
+ /* both s and l are scaled by len */
+ stack[stack_ptr].s = l;
+ stack[stack_ptr].l = n;
+ ++stack_ptr;
+ ret = try_collapse_@suff@(start, stack, &stack_ptr, &buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ l += n;
+ }
+
+ ret = force_collapse_@suff@(start, stack, &stack_ptr, &buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ ret = 0;
+
+cleanup:
+ if (buffer.pw != NULL) {
+ free(buffer.pw);
+ }
+ return ret;
+}
+
+
+/* argsort */
+
+
+static npy_intp
+acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num,
+ npy_intp minrun, size_t len)
+{
+ npy_intp sz;
+ npy_intp vi;
+ npy_intp *pl, *pi, *pj, *pr;
+
+ if (NPY_UNLIKELY(num - l == 1)) {
+ return 1;
+ }
+
+ pl = tosort + l;
+
+ /* (not strictly) ascending sequence */
+ if (!@TYPE@_LT(arr + (*(pl + 1)) * len, arr + (*pl) * len, len)) {
+ for (pi = pl + 1; pi < tosort + num - 1
+ && !@TYPE@_LT(arr + (*(pi + 1)) * len, arr + (*pi) * len, len); ++pi) {
+ }
+ } else { /* (strictly) descending sequence */
+ for (pi = pl + 1; pi < tosort + num - 1
+ && @TYPE@_LT(arr + (*(pi + 1)) * len, arr + (*pi) * len, len); ++pi) {
+ }
+
+ for (pj = pl, pr = pi; pj < pr; ++pj, --pr) {
+ INTP_SWAP(*pj, *pr);
+ }
+ }
+
+ ++pi;
+ sz = pi - pl;
+
+ if (sz < minrun) {
+ if (l + minrun < num) {
+ sz = minrun;
+ } else {
+ sz = num - l;
+ }
+
+ pr = pl + sz;
+
+ /* insertion sort */
+ for (; pi < pr; ++pi) {
+ vi = *pi;
+ pj = pi;
+
+ while (pl < pj && @TYPE@_LT(arr + vi * len, arr + (*(pj - 1)) * len, len)) {
+ *pj = *(pj - 1);
+ --pj;
+ }
+
+ *pj = vi;
+ }
+ }
+
+ return sz;
+}
+
+
+static npy_intp
+agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort,
+ const npy_intp size, const @type@ *key, size_t len)
+{
+ npy_intp last_ofs, ofs, l, m, r;
+
+ if (@TYPE@_LT(arr + tosort[size - 1] * len, key, len)) {
+ return size;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size;
+ break;
+ }
+
+ if (@TYPE@_LT(arr + tosort[size - ofs - 1] * len, key, len)) {
+ break;
+ } else {
+ last_ofs = ofs;
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[tosort[size-ofs-1]*len] < key <= arr[tosort[size-last_ofs-1]*len] */
+ l = size - ofs - 1;
+ r = size - last_ofs - 1;
+
+ while (l + 1 < r) {
+ m = l + ((r - l) >> 1);
+
+ if (@TYPE@_LT(arr + tosort[m] * len, key, len)) {
+ l = m;
+ } else {
+ r = m;
+ }
+ }
+
+ /* now that arr[tosort[r-1]*len] < key <= arr[tosort[r]*len] */
+ return r;
+}
+
+
+static npy_intp
+agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort,
+ const npy_intp size, const @type@ *key, size_t len)
+{
+ npy_intp last_ofs, ofs, m;
+
+ if (@TYPE@_LT(key, arr + tosort[0] * len, len)) {
+ return 0;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size; /* arr[ofs] is never accessed */
+ break;
+ }
+
+ if (@TYPE@_LT(key, arr + tosort[ofs] * len, len)) {
+ break;
+ } else {
+ last_ofs = ofs;
+ /* ofs = 1, 3, 7, 15... */
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[tosort[last_ofs]*len] <= key < arr[tosort[ofs]*len] */
+ while (last_ofs + 1 < ofs) {
+ m = last_ofs + ((ofs - last_ofs) >> 1);
+
+ if (@TYPE@_LT(key, arr + tosort[m] * len, len)) {
+ ofs = m;
+ } else {
+ last_ofs = m;
+ }
+ }
+
+ /* now that arr[tosort[ofs-1]*len] <= key < arr[tosort[ofs]*len] */
+ return ofs;
+}
+
+
+
+static void
+amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2,
+ npy_intp l2, npy_intp *p3, size_t len)
+{
+ npy_intp *end = p2 + l2;
+ memcpy(p3, p1, sizeof(npy_intp) * l1);
+ /* first element must be in p2 otherwise skipped in the caller */
+ *p1++ = *p2++;
+
+ while (p1 < p2 && p2 < end) {
+ if (@TYPE@_LT(arr + (*p2) * len, arr + (*p3) * len, len)) {
+ *p1++ = *p2++;
+ } else {
+ *p1++ = *p3++;
+ }
+ }
+
+ if (p1 != p2) {
+ memcpy(p1, p3, sizeof(npy_intp) * (p2 - p1));
+ }
+}
+
+
+static void
+amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2,
+ npy_intp l2, npy_intp *p3, size_t len)
+{
+ npy_intp ofs;
+ npy_intp *start = p1 - 1;
+ memcpy(p3, p2, sizeof(npy_intp) * l2);
+ p1 += l1 - 1;
+ p2 += l2 - 1;
+ p3 += l2 - 1;
+ /* first element must be in p1 otherwise skipped in the caller */
+ *p2-- = *p1--;
+
+ while (p1 < p2 && start < p1) {
+ if (@TYPE@_LT(arr + (*p3) * len, arr + (*p1) * len, len)) {
+ *p2-- = *p1--;
+ } else {
+ *p2-- = *p3--;
+ }
+ }
+
+ if (p1 != p2) {
+ ofs = p2 - start;
+ memcpy(start + 1, p3 - ofs + 1, sizeof(npy_intp) * ofs);
+ }
+}
+
+
+
+static int
+amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack,
+ const npy_intp at, buffer_intp *buffer, size_t len)
+{
+ int ret;
+ npy_intp s1, l1, s2, l2, k;
+ npy_intp *p1, *p2;
+ s1 = stack[at].s;
+ l1 = stack[at].l;
+ s2 = stack[at + 1].s;
+ l2 = stack[at + 1].l;
+ /* tosort[s2] belongs to tosort[s1+k] */
+ k = agallop_right_@suff@(arr, tosort + s1, l1, arr + tosort[s2] * len, len);
+
+ if (l1 == k) {
+ /* already sorted */
+ return 0;
+ }
+
+ p1 = tosort + s1 + k;
+ l1 -= k;
+ p2 = tosort + s2;
+ /* tosort[s2-1] belongs to tosort[s2+l2] */
+ l2 = agallop_left_@suff@(arr, tosort + s2, l2, arr + tosort[s2 - 1] * len,
+ len);
+
+ if (l2 < l1) {
+ ret = resize_buffer_intp(buffer, l2);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ amerge_right_@suff@(arr, p1, l1, p2, l2, buffer->pw, len);
+ } else {
+ ret = resize_buffer_intp(buffer, l1);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ amerge_left_@suff@(arr, p1, l1, p2, l2, buffer->pw, len);
+ }
+
+ return 0;
+}
+
+
+static int
+atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack,
+ npy_intp *stack_ptr, buffer_intp *buffer, size_t len)
+{
+ int ret;
+ npy_intp A, B, C, top;
+ top = *stack_ptr;
+
+ while (1 < top) {
+ B = stack[top - 2].l;
+ C = stack[top - 1].l;
+
+ if ((2 < top && stack[top - 3].l <= B + C) ||
+ (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) {
+ A = stack[top - 3].l;
+
+ if (A <= C) {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += B;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ }
+ } else if (1 < top && B <= C) {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ } else {
+ break;
+ }
+ }
+
+ *stack_ptr = top;
+ return 0;
+}
+
+
+
+static int
+aforce_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack,
+ npy_intp *stack_ptr, buffer_intp *buffer, size_t len)
+{
+ int ret;
+ npy_intp top = *stack_ptr;
+
+ while (2 < top) {
+ if (stack[top - 3].l <= stack[top - 1].l) {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += stack[top - 2].l;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += stack[top - 1].l;
+ --top;
+ }
+ }
+
+ if (1 < top) {
+ ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+ }
+
+ return 0;
+}
+
+
+int
+atimsort_@suff@(void *start, npy_intp *tosort, npy_intp num, void *varr)
+{
+ PyArrayObject *arr = varr;
+ size_t elsize = PyArray_ITEMSIZE(arr);
+ size_t len = elsize / sizeof(@type@);
+ int ret;
+ npy_intp l, n, stack_ptr, minrun;
+ run stack[TIMSORT_STACK_SIZE];
+ buffer_intp buffer;
+
+ /* Items that have zero size don't make sense to sort */
+ if (len == 0) {
+ return 0;
+ }
+
+ buffer.pw = NULL;
+ buffer.size = 0;
+ stack_ptr = 0;
+ minrun = compute_min_run_short(num);
+
+ for (l = 0; l < num;) {
+ n = acount_run_@suff@(start, tosort, l, num, minrun, len);
+ /* both s and l are scaled by len */
+ stack[stack_ptr].s = l;
+ stack[stack_ptr].l = n;
+ ++stack_ptr;
+ ret = atry_collapse_@suff@(start, tosort, stack, &stack_ptr, &buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ l += n;
+ }
+
+ ret = aforce_collapse_@suff@(start, tosort, stack, &stack_ptr, &buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ ret = 0;
+
+cleanup:
+ if (buffer.pw != NULL) {
+ free(buffer.pw);
+ }
+ return ret;
+}
+
+
+/**end repeat**/
+
+
+
+/*
+ *****************************************************************************
+ ** GENERIC SORT **
+ *****************************************************************************
+ */
+
+
+typedef struct {
+ char *pw;
+ npy_intp size;
+ size_t len;
+} buffer_char;
+
+
+static NPY_INLINE int
+resize_buffer_char(buffer_char *buffer, npy_intp new_size)
+{
+ if (new_size <= buffer->size) {
+ return 0;
+ }
+
+ if (NPY_UNLIKELY(buffer->pw == NULL)) {
+ buffer->pw = malloc(sizeof(char) * new_size * buffer->len);
+ } else {
+ buffer->pw = realloc(buffer->pw, sizeof(char) * new_size * buffer->len);
+ }
+
+ buffer->size = new_size;
+
+ if (NPY_UNLIKELY(buffer->pw == NULL)) {
+ return -NPY_ENOMEM;
+ } else {
+ return 0;
+ }
+}
+
+
+static npy_intp
+npy_count_run(char *arr, npy_intp l, npy_intp num, npy_intp minrun,
+ char *vp, size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp sz;
+ char *pl, *pi, *pj, *pr;
+
+ if (NPY_UNLIKELY(num - l == 1)) {
+ return 1;
+ }
+
+ pl = arr + l * len;
+
+ /* (not strictly) ascending sequence */
+ if (cmp(pl, pl + len, py_arr) <= 0) {
+ for (pi = pl + len; pi < arr + (num - 1) * len
+ && cmp(pi, pi + len, py_arr) <= 0; pi += len) {
+ }
+ } else { /* (strictly) descending sequence */
+ for (pi = pl + len; pi < arr + (num - 1) * len
+ && cmp(pi + len, pi, py_arr) < 0; pi += len) {
+ }
+
+ for (pj = pl, pr = pi; pj < pr; pj += len, pr -= len) {
+ GENERIC_SWAP(pj, pr, len);
+ }
+ }
+
+ pi += len;
+ sz = (pi - pl) / len;
+
+ if (sz < minrun) {
+ if (l + minrun < num) {
+ sz = minrun;
+ } else {
+ sz = num - l;
+ }
+
+ pr = pl + sz * len;
+
+ /* insertion sort */
+ for (; pi < pr; pi += len) {
+ GENERIC_COPY(vp, pi, len);
+ pj = pi;
+
+ while (pl < pj && cmp(vp, pj - len, py_arr) < 0) {
+ GENERIC_COPY(pj, pj - len, len);
+ pj -= len;
+ }
+
+ GENERIC_COPY(pj, vp, len);
+ }
+ }
+
+ return sz;
+}
+
+
+static npy_intp
+npy_gallop_right(const char *arr, const npy_intp size, const char *key,
+ size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp last_ofs, ofs, m;
+
+ if (cmp(key, arr, py_arr) < 0) {
+ return 0;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size; /* arr[ofs] is never accessed */
+ break;
+ }
+
+ if (cmp(key, arr + ofs * len, py_arr) < 0) {
+ break;
+ } else {
+ last_ofs = ofs;
+ /* ofs = 1, 3, 7, 15... */
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[last_ofs*len] <= key < arr[ofs*len] */
+ while (last_ofs + 1 < ofs) {
+ m = last_ofs + ((ofs - last_ofs) >> 1);
+
+ if (cmp(key, arr + m * len, py_arr) < 0) {
+ ofs = m;
+ } else {
+ last_ofs = m;
+ }
+ }
+
+ /* now that arr[(ofs-1)*len] <= key < arr[ofs*len] */
+ return ofs;
+}
+
+
+
+static npy_intp
+npy_gallop_left(const char *arr, const npy_intp size, const char *key,
+ size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp last_ofs, ofs, l, m, r;
+
+ if (cmp(arr + (size - 1) * len, key, py_arr) < 0) {
+ return size;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size;
+ break;
+ }
+
+ if (cmp(arr + (size - ofs - 1) * len, key, py_arr) < 0) {
+ break;
+ } else {
+ last_ofs = ofs;
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[(size-ofs-1)*len] < key <= arr[(size-last_ofs-1)*len] */
+ l = size - ofs - 1;
+ r = size - last_ofs - 1;
+
+ while (l + 1 < r) {
+ m = l + ((r - l) >> 1);
+
+ if (cmp(arr + m * len, key, py_arr) < 0) {
+ l = m;
+ } else {
+ r = m;
+ }
+ }
+
+ /* now that arr[(r-1)*len] < key <= arr[r*len] */
+ return r;
+}
+
+
+static void
+npy_merge_left(char *p1, npy_intp l1, char *p2, npy_intp l2,
+ char *p3, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ char *end = p2 + l2 * len;
+ memcpy(p3, p1, sizeof(char) * l1 * len);
+ /* first element must be in p2 otherwise skipped in the caller */
+ GENERIC_COPY(p1, p2, len);
+ p1 += len;
+ p2 += len;
+
+ while (p1 < p2 && p2 < end) {
+ if (cmp(p2, p3, py_arr) < 0) {
+ GENERIC_COPY(p1, p2, len);
+ p1 += len;
+ p2 += len;
+ } else {
+ GENERIC_COPY(p1, p3, len);
+ p1 += len;
+ p3 += len;
+ }
+ }
+
+ if (p1 != p2) {
+ memcpy(p1, p3, sizeof(char) * (p2 - p1));
+ }
+}
+
+
+static void
+npy_merge_right(char *p1, npy_intp l1, char *p2, npy_intp l2,
+ char *p3, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp ofs;
+ char *start = p1 - len;
+ memcpy(p3, p2, sizeof(char) * l2 * len);
+ p1 += (l1 - 1) * len;
+ p2 += (l2 - 1) * len;
+ p3 += (l2 - 1) * len;
+ /* first element must be in p1 otherwise skipped in the caller */
+ GENERIC_COPY(p2, p1, len);
+ p2 -= len;
+ p1 -= len;
+
+ while (p1 < p2 && start < p1) {
+ if (cmp(p3, p1, py_arr) < 0) {
+ GENERIC_COPY(p2, p1, len);
+ p2 -= len;
+ p1 -= len;
+ } else {
+ GENERIC_COPY(p2, p3, len);
+ p2 -= len;
+ p3 -= len;
+ }
+ }
+
+ if (p1 != p2) {
+ ofs = p2 - start;
+ memcpy(start + len, p3 - ofs + len, sizeof(char) * ofs);
+ }
+}
+
+
+
+static int
+npy_merge_at(char *arr, const run *stack, const npy_intp at,
+ buffer_char *buffer, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ int ret;
+ npy_intp s1, l1, s2, l2, k;
+ char *p1, *p2;
+ s1 = stack[at].s;
+ l1 = stack[at].l;
+ s2 = stack[at + 1].s;
+ l2 = stack[at + 1].l;
+ /* arr[s2] belongs to arr[s1+k] */
+ GENERIC_COPY(buffer->pw, arr + s2 * len, len);
+ k = npy_gallop_right(arr + s1 * len, l1, buffer->pw, len, cmp, py_arr);
+
+ if (l1 == k) {
+ /* already sorted */
+ return 0;
+ }
+
+ p1 = arr + (s1 + k) * len;
+ l1 -= k;
+ p2 = arr + s2 * len;
+ /* arr[s2-1] belongs to arr[s2+l2] */
+ GENERIC_COPY(buffer->pw, arr + (s2 - 1) * len, len);
+ l2 = npy_gallop_left(arr + s2 * len, l2, buffer->pw, len, cmp, py_arr);
+
+ if (l2 < l1) {
+ ret = resize_buffer_char(buffer, l2);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ npy_merge_right(p1, l1, p2, l2, buffer->pw, len, cmp, py_arr);
+ } else {
+ ret = resize_buffer_char(buffer, l1);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ npy_merge_left(p1, l1, p2, l2, buffer->pw, len, cmp, py_arr);
+ }
+
+ return 0;
+}
+
+
+static int
+npy_try_collapse(char *arr, run *stack, npy_intp *stack_ptr,
+ buffer_char *buffer, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ int ret;
+ npy_intp A, B, C, top;
+ top = *stack_ptr;
+
+ while (1 < top) {
+ B = stack[top - 2].l;
+ C = stack[top - 1].l;
+
+ if ((2 < top && stack[top - 3].l <= B + C) ||
+ (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) {
+ A = stack[top - 3].l;
+
+ if (A <= C) {
+ ret = npy_merge_at(arr, stack, top - 3, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += B;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ }
+ } else if (1 < top && B <= C) {
+ ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ } else {
+ break;
+ }
+ }
+
+ *stack_ptr = top;
+ return 0;
+}
+
+
+static int
+npy_force_collapse(char *arr, run *stack, npy_intp *stack_ptr,
+ buffer_char *buffer, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ int ret;
+ npy_intp top = *stack_ptr;
+
+ while (2 < top) {
+ if (stack[top - 3].l <= stack[top - 1].l) {
+ ret = npy_merge_at(arr, stack, top - 3, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += stack[top - 2].l;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += stack[top - 1].l;
+ --top;
+ }
+ }
+
+ if (1 < top) {
+ ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+ }
+
+ return 0;
+}
+
+
+int
+npy_timsort(void *start, npy_intp num, void *varr)
+{
+ PyArrayObject *arr = varr;
+ size_t len = PyArray_ITEMSIZE(arr);
+ PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
+ int ret;
+ npy_intp l, n, stack_ptr, minrun;
+ run stack[TIMSORT_STACK_SIZE];
+ buffer_char buffer;
+
+ /* Items that have zero size don't make sense to sort */
+ if (len == 0) {
+ return 0;
+ }
+
+ buffer.pw = NULL;
+ buffer.size = 0;
+ buffer.len = len;
+ stack_ptr = 0;
+ minrun = compute_min_run_short(num);
+
+ /* used for insertion sort and gallop key */
+ ret = resize_buffer_char(&buffer, len);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ for (l = 0; l < num;) {
+ n = npy_count_run(start, l, num, minrun, buffer.pw, len, cmp, arr);
+
+ /* both s and l are scaled by len */
+ stack[stack_ptr].s = l;
+ stack[stack_ptr].l = n;
+ ++stack_ptr;
+ ret = npy_try_collapse(start, stack, &stack_ptr, &buffer, len, cmp, arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ l += n;
+ }
+
+ ret = npy_force_collapse(start, stack, &stack_ptr, &buffer, len, cmp, arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ ret = 0;
+
+cleanup:
+ if (buffer.pw != NULL) {
+ free(buffer.pw);
+ }
+ return ret;
+}
+
+
+/* argsort */
+
+static npy_intp
+npy_acount_run(char *arr, npy_intp *tosort, npy_intp l, npy_intp num,
+ npy_intp minrun, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp sz;
+ npy_intp vi;
+ npy_intp *pl, *pi, *pj, *pr;
+
+ if (NPY_UNLIKELY(num - l == 1)) {
+ return 1;
+ }
+
+ pl = tosort + l;
+
+ /* (not strictly) ascending sequence */
+ if (cmp(arr + (*pl) * len, arr + (*(pl + 1)) * len, py_arr) <= 0) {
+ for (pi = pl + 1; pi < tosort + num - 1
+ && cmp(arr + (*pi) * len, arr + (*(pi + 1)) * len, py_arr) <= 0; ++pi) {
+ }
+ } else { /* (strictly) descending sequence */
+ for (pi = pl + 1; pi < tosort + num - 1
+ && cmp(arr + (*(pi + 1)) * len, arr + (*pi) * len, py_arr) < 0; ++pi) {
+ }
+
+ for (pj = pl, pr = pi; pj < pr; ++pj, --pr) {
+ INTP_SWAP(*pj, *pr);
+ }
+ }
+
+ ++pi;
+ sz = pi - pl;
+
+ if (sz < minrun) {
+ if (l + minrun < num) {
+ sz = minrun;
+ } else {
+ sz = num - l;
+ }
+
+ pr = pl + sz;
+
+ /* insertion sort */
+ for (; pi < pr; ++pi) {
+ vi = *pi;
+ pj = pi;
+
+ while (pl < pj && cmp(arr + vi * len, arr + (*(pj - 1)) * len, py_arr) < 0) {
+ *pj = *(pj - 1);
+ --pj;
+ }
+
+ *pj = vi;
+ }
+ }
+
+ return sz;
+}
+
+
+static npy_intp
+npy_agallop_left(const char *arr, const npy_intp *tosort,
+ const npy_intp size, const char *key, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp last_ofs, ofs, l, m, r;
+
+ if (cmp(arr + tosort[size - 1] * len, key, py_arr) < 0) {
+ return size;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size;
+ break;
+ }
+
+ if (cmp(arr + tosort[size - ofs - 1] * len, key, py_arr) < 0) {
+ break;
+ } else {
+ last_ofs = ofs;
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[tosort[size-ofs-1]*len] < key <= arr[tosort[size-last_ofs-1]*len] */
+ l = size - ofs - 1;
+ r = size - last_ofs - 1;
+
+ while (l + 1 < r) {
+ m = l + ((r - l) >> 1);
+
+ if (cmp(arr + tosort[m] * len, key, py_arr) < 0) {
+ l = m;
+ } else {
+ r = m;
+ }
+ }
+
+ /* now that arr[tosort[r-1]*len] < key <= arr[tosort[r]*len] */
+ return r;
+}
+
+
+static npy_intp
+npy_agallop_right(const char *arr, const npy_intp *tosort,
+ const npy_intp size, const char *key, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp last_ofs, ofs, m;
+
+ if (cmp(key, arr + tosort[0] * len, py_arr) < 0) {
+ return 0;
+ }
+
+ last_ofs = 0;
+ ofs = 1;
+
+ for (;;) {
+ if (size <= ofs || ofs < 0) {
+ ofs = size; /* arr[ofs] is never accessed */
+ break;
+ }
+
+ if (cmp(key, arr + tosort[ofs] * len, py_arr) < 0) {
+ break;
+ } else {
+ last_ofs = ofs;
+ /* ofs = 1, 3, 7, 15... */
+ ofs = (ofs << 1) + 1;
+ }
+ }
+
+ /* now that arr[tosort[last_ofs]*len] <= key < arr[tosort[ofs]*len] */
+ while (last_ofs + 1 < ofs) {
+ m = last_ofs + ((ofs - last_ofs) >> 1);
+
+ if (cmp(key, arr + tosort[m] * len, py_arr) < 0) {
+ ofs = m;
+ } else {
+ last_ofs = m;
+ }
+ }
+
+ /* now that arr[tosort[ofs-1]*len] <= key < arr[tosort[ofs]*len] */
+ return ofs;
+}
+
+
+static void
+npy_amerge_left(char *arr, npy_intp *p1, npy_intp l1, npy_intp *p2,
+ npy_intp l2, npy_intp *p3, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp *end = p2 + l2;
+ memcpy(p3, p1, sizeof(npy_intp) * l1);
+ /* first element must be in p2 otherwise skipped in the caller */
+ *p1++ = *p2++;
+
+ while (p1 < p2 && p2 < end) {
+ if (cmp(arr + (*p2) * len, arr + (*p3) * len, py_arr) < 0) {
+ *p1++ = *p2++;
+ } else {
+ *p1++ = *p3++;
+ }
+ }
+
+ if (p1 != p2) {
+ memcpy(p1, p3, sizeof(npy_intp) * (p2 - p1));
+ }
+}
+
+
+static void
+npy_amerge_right(char *arr, npy_intp* p1, npy_intp l1, npy_intp *p2,
+ npy_intp l2, npy_intp *p3, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ npy_intp ofs;
+ npy_intp *start = p1 - 1;
+ memcpy(p3, p2, sizeof(npy_intp) * l2);
+ p1 += l1 - 1;
+ p2 += l2 - 1;
+ p3 += l2 - 1;
+ /* first element must be in p1 otherwise skipped in the caller */
+ *p2-- = *p1--;
+
+ while (p1 < p2 && start < p1) {
+ if (cmp(arr + (*p3) * len, arr + (*p1) * len, py_arr) < 0) {
+ *p2-- = *p1--;
+ } else {
+ *p2-- = *p3--;
+ }
+ }
+
+ if (p1 != p2) {
+ ofs = p2 - start;
+ memcpy(start + 1, p3 - ofs + 1, sizeof(npy_intp) * ofs);
+ }
+}
+
+
+
+static int
+npy_amerge_at(char *arr, npy_intp *tosort, const run *stack,
+ const npy_intp at, buffer_intp *buffer, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ int ret;
+ npy_intp s1, l1, s2, l2, k;
+ npy_intp *p1, *p2;
+ s1 = stack[at].s;
+ l1 = stack[at].l;
+ s2 = stack[at + 1].s;
+ l2 = stack[at + 1].l;
+ /* tosort[s2] belongs to tosort[s1+k] */
+ k = npy_agallop_right(arr, tosort + s1, l1, arr + tosort[s2] * len, len, cmp,
+ py_arr);
+
+ if (l1 == k) {
+ /* already sorted */
+ return 0;
+ }
+
+ p1 = tosort + s1 + k;
+ l1 -= k;
+ p2 = tosort + s2;
+ /* tosort[s2-1] belongs to tosort[s2+l2] */
+ l2 = npy_agallop_left(arr, tosort + s2, l2, arr + tosort[s2 - 1] * len,
+ len, cmp, py_arr);
+
+ if (l2 < l1) {
+ ret = resize_buffer_intp(buffer, l2);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ npy_amerge_right(arr, p1, l1, p2, l2, buffer->pw, len, cmp, py_arr);
+ } else {
+ ret = resize_buffer_intp(buffer, l1);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ npy_amerge_left(arr, p1, l1, p2, l2, buffer->pw, len, cmp, py_arr);
+ }
+
+ return 0;
+}
+
+
+static int
+npy_atry_collapse(char *arr, npy_intp *tosort, run *stack,
+ npy_intp *stack_ptr, buffer_intp *buffer, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ int ret;
+ npy_intp A, B, C, top;
+ top = *stack_ptr;
+
+ while (1 < top) {
+ B = stack[top - 2].l;
+ C = stack[top - 1].l;
+
+ if ((2 < top && stack[top - 3].l <= B + C) ||
+ (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) {
+ A = stack[top - 3].l;
+
+ if (A <= C) {
+ ret = npy_amerge_at(arr, tosort, stack, top - 3, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += B;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ }
+ } else if (1 < top && B <= C) {
+ ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += C;
+ --top;
+ } else {
+ break;
+ }
+ }
+
+ *stack_ptr = top;
+ return 0;
+}
+
+
+static int
+npy_aforce_collapse(char *arr, npy_intp *tosort, run *stack,
+ npy_intp *stack_ptr, buffer_intp *buffer, size_t len,
+ PyArray_CompareFunc *cmp, PyArrayObject *py_arr)
+{
+ int ret;
+ npy_intp top = *stack_ptr;
+
+ while (2 < top) {
+ if (stack[top - 3].l <= stack[top - 1].l) {
+ ret = npy_amerge_at(arr, tosort, stack, top - 3, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 3].l += stack[top - 2].l;
+ stack[top - 2] = stack[top - 1];
+ --top;
+ } else {
+ ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+
+ stack[top - 2].l += stack[top - 1].l;
+ --top;
+ }
+ }
+
+ if (1 < top) {
+ ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { return ret; }
+ }
+
+ return 0;
+}
+
+
+int
+npy_atimsort(void *start, npy_intp *tosort, npy_intp num, void *varr)
+{
+ PyArrayObject *arr = varr;
+ size_t len = PyArray_ITEMSIZE(arr);
+ PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
+ int ret;
+ npy_intp l, n, stack_ptr, minrun;
+ run stack[TIMSORT_STACK_SIZE];
+ buffer_intp buffer;
+
+ /* Items that have zero size don't make sense to sort */
+ if (len == 0) {
+ return 0;
+ }
+
+ buffer.pw = NULL;
+ buffer.size = 0;
+ stack_ptr = 0;
+ minrun = compute_min_run_short(num);
+
+ for (l = 0; l < num;) {
+ n = npy_acount_run(start, tosort, l, num, minrun, len, cmp, arr);
+ /* both s and l are scaled by len */
+ stack[stack_ptr].s = l;
+ stack[stack_ptr].l = n;
+ ++stack_ptr;
+ ret = npy_atry_collapse(start, tosort, stack, &stack_ptr, &buffer, len, cmp,
+ arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ l += n;
+ }
+
+ ret = npy_aforce_collapse(start, tosort, stack, &stack_ptr, &buffer, len,
+ cmp, arr);
+
+ if (NPY_UNLIKELY(ret < 0)) { goto cleanup; }
+
+ ret = 0;
+
+cleanup:
+ if (buffer.pw != NULL) {
+ free(buffer.pw);
+ }
+ return ret;
+}
diff --git a/numpy/core/src/umath/_rational_tests.c.src b/numpy/core/src/umath/_rational_tests.c.src
index 9e74845df..615e395c7 100644
--- a/numpy/core/src/umath/_rational_tests.c.src
+++ b/numpy/core/src/umath/_rational_tests.c.src
@@ -539,11 +539,11 @@ static PyObject*
pyrational_str(PyObject* self) {
rational x = ((PyRational*)self)->r;
if (d(x)!=1) {
- return PyString_FromFormat(
+ return PyUString_FromFormat(
"%ld/%ld",(long)x.n,(long)d(x));
}
else {
- return PyString_FromFormat(
+ return PyUString_FromFormat(
"%ld",(long)x.n);
}
}
diff --git a/numpy/core/src/umath/_struct_ufunc_tests.c.src b/numpy/core/src/umath/_struct_ufunc_tests.c.src
index b831d5c2a..3eaac73e1 100644
--- a/numpy/core/src/umath/_struct_ufunc_tests.c.src
+++ b/numpy/core/src/umath/_struct_ufunc_tests.c.src
@@ -17,12 +17,6 @@
* docs.python.org .
*/
-static PyMethodDef StructUfuncTestMethods[] = {
- {NULL, NULL, 0, NULL}
-};
-
-/* The loop definition must precede the PyMODINIT_FUNC. */
-
static void add_uint64_triplet(char **args, npy_intp *dimensions,
npy_intp* steps, void* data)
{
@@ -53,6 +47,59 @@ static void add_uint64_triplet(char **args, npy_intp *dimensions,
}
}
+static PyObject*
+register_fail(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args))
+{
+ PyObject *add_triplet;
+ PyObject *dtype_dict;
+ PyArray_Descr *dtype;
+ PyArray_Descr *dtypes[3];
+ int retval;
+
+ add_triplet = PyUFunc_FromFuncAndData(NULL, NULL, NULL, 0, 2, 1,
+ PyUFunc_None, "add_triplet",
+ "add_triplet_docstring", 0);
+
+ dtype_dict = Py_BuildValue("[(s, s), (s, s), (s, s)]",
+ "f0", "u8", "f1", "u8", "f2", "u8");
+ PyArray_DescrConverter(dtype_dict, &dtype);
+ Py_DECREF(dtype_dict);
+
+ dtypes[0] = dtype;
+ dtypes[1] = dtype;
+ dtypes[2] = dtype;
+
+ retval = PyUFunc_RegisterLoopForDescr((PyUFuncObject *)add_triplet,
+ dtype,
+ &add_uint64_triplet,
+ dtypes,
+ NULL);
+
+ if (retval < 0) {
+ Py_DECREF(add_triplet);
+ Py_DECREF(dtype);
+ return NULL;
+ }
+ retval = PyUFunc_RegisterLoopForDescr((PyUFuncObject *)add_triplet,
+ dtype,
+ &add_uint64_triplet,
+ dtypes,
+ NULL);
+ Py_DECREF(add_triplet);
+ Py_DECREF(dtype);
+ if (retval < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef StructUfuncTestMethods[] = {
+ {"register_fail",
+ register_fail,
+ METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL}
+};
+
#if defined(NPY_PY3K)
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
@@ -100,7 +147,7 @@ PyMODINIT_FUNC init_struct_ufunc_tests(void)
"add_triplet_docstring", 0);
dtype_dict = Py_BuildValue("[(s, s), (s, s), (s, s)]",
- "f0", "u8", "f1", "u8", "f2", "u8");
+ "f0", "u8", "f1", "u8", "f2", "u8");
PyArray_DescrConverter(dtype_dict, &dtype);
Py_DECREF(dtype_dict);
@@ -114,6 +161,7 @@ PyMODINIT_FUNC init_struct_ufunc_tests(void)
dtypes,
NULL);
+ Py_DECREF(dtype);
d = PyModule_GetDict(m);
PyDict_SetItemString(d, "add_triplet", add_triplet);
diff --git a/numpy/core/src/umath/_umath_tests.c.src b/numpy/core/src/umath/_umath_tests.c.src
index fcbdbe330..6c3bcce71 100644
--- a/numpy/core/src/umath/_umath_tests.c.src
+++ b/numpy/core/src/umath/_umath_tests.c.src
@@ -128,6 +128,8 @@ static void
/**end repeat**/
char *matrix_multiply_signature = "(m,n),(n,p)->(m,p)";
+/* for use with matrix_multiply code, but different signature */
+char *matmul_signature = "(m?,n),(n,p?)->(m?,p?)";
/**begin repeat
@@ -195,6 +197,45 @@ static void
/**end repeat**/
+char *cross1d_signature = "(3),(3)->(3)";
+
+/**begin repeat
+
+ #TYPE=LONG,DOUBLE#
+ #typ=npy_long, npy_double#
+*/
+
+/*
+ * This implements the cross product:
+ * out[n, 0] = in1[n, 1]*in2[n, 2] - in1[n, 2]*in2[n, 1]
+ * out[n, 1] = in1[n, 2]*in2[n, 0] - in1[n, 0]*in2[n, 2]
+ * out[n, 2] = in1[n, 0]*in2[n, 1] - in1[n, 1]*in2[n, 0]
+ */
+static void
+@TYPE@_cross1d(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ INIT_OUTER_LOOP_3
+ npy_intp is1=steps[0], is2=steps[1], os = steps[2];
+ BEGIN_OUTER_LOOP_3
+ @typ@ i1_x = *(@typ@ *)(args[0] + 0*is1);
+ @typ@ i1_y = *(@typ@ *)(args[0] + 1*is1);
+ @typ@ i1_z = *(@typ@ *)(args[0] + 2*is1);
+
+ @typ@ i2_x = *(@typ@ *)(args[1] + 0*is2);
+ @typ@ i2_y = *(@typ@ *)(args[1] + 1*is2);
+ @typ@ i2_z = *(@typ@ *)(args[1] + 2*is2);
+ char *op = args[2];
+
+ *(@typ@ *)op = i1_y * i2_z - i1_z * i2_y;
+ op += os;
+ *(@typ@ *)op = i1_z * i2_x - i1_x * i2_z;
+ op += os;
+ *(@typ@ *)op = i1_x * i2_y - i1_y * i2_x;
+ END_OUTER_LOOP
+}
+
+/**end repeat**/
+
char *euclidean_pdist_signature = "(n,d)->(p)";
/**begin repeat
@@ -285,17 +326,39 @@ static void
/**end repeat**/
+/* The following lines were generated using a slightly modified
+ version of code_generators/generate_umath.py and adding these
+ lines to defdict:
+
+defdict = {
+'inner1d' :
+ Ufunc(2, 1, None_,
+ r'''inner on the last dimension and broadcast on the rest \n"
+ " \"(i),(i)->()\" \n''',
+ TD('ld'),
+ ),
+'innerwt' :
+ Ufunc(3, 1, None_,
+ r'''inner1d with a weight argument \n"
+ " \"(i),(i),(i)->()\" \n''',
+ TD('ld'),
+ ),
+}
+
+*/
static PyUFuncGenericFunction inner1d_functions[] = { LONG_inner1d, DOUBLE_inner1d };
-static void * inner1d_data[] = { (void *)NULL, (void *)NULL };
+static void *inner1d_data[] = { (void *)NULL, (void *)NULL };
static char inner1d_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE };
static PyUFuncGenericFunction innerwt_functions[] = { LONG_innerwt, DOUBLE_innerwt };
-static void * innerwt_data[] = { (void *)NULL, (void *)NULL };
+static void *innerwt_data[] = { (void *)NULL, (void *)NULL };
static char innerwt_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE };
static PyUFuncGenericFunction matrix_multiply_functions[] = { LONG_matrix_multiply, FLOAT_matrix_multiply, DOUBLE_matrix_multiply };
static void *matrix_multiply_data[] = { (void *)NULL, (void *)NULL, (void *)NULL };
static char matrix_multiply_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE };
-
+static PyUFuncGenericFunction cross1d_functions[] = { LONG_cross1d, DOUBLE_cross1d };
+static void *cross1d_data[] = { (void *)NULL, (void *)NULL };
+static char cross1d_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE };
static PyUFuncGenericFunction euclidean_pdist_functions[] =
{ FLOAT_euclidean_pdist, DOUBLE_euclidean_pdist };
static void *eucldiean_pdist_data[] = { (void *)NULL, (void *)NULL };
@@ -303,7 +366,7 @@ static char euclidean_pdist_signatures[] = { NPY_FLOAT, NPY_FLOAT,
NPY_DOUBLE, NPY_DOUBLE };
static PyUFuncGenericFunction cumsum_functions[] = { LONG_cumsum, DOUBLE_cumsum };
-static void * cumsum_data[] = { (void *)NULL, (void *)NULL };
+static void *cumsum_data[] = { (void *)NULL, (void *)NULL };
static char cumsum_signatures[] = { NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE };
@@ -346,6 +409,17 @@ addUfuncs(PyObject *dictionary) {
}
PyDict_SetItemString(dictionary, "matrix_multiply", f);
Py_DECREF(f);
+ f = PyUFunc_FromFuncAndDataAndSignature(matrix_multiply_functions,
+ matrix_multiply_data, matrix_multiply_signatures,
+ 3, 2, 1, PyUFunc_None, "matmul",
+ "matmul on last two dimensions, with some being optional\n"
+ " \"(m?,n),(n,p?)->(m?,p?)\" \n",
+ 0, matmul_signature);
+ if (f == NULL) {
+ return -1;
+ }
+ PyDict_SetItemString(dictionary, "matmul", f);
+ Py_DECREF(f);
f = PyUFunc_FromFuncAndDataAndSignature(euclidean_pdist_functions,
eucldiean_pdist_data, euclidean_pdist_signatures,
2, 1, 1, PyUFunc_None, "euclidean_pdist",
@@ -376,6 +450,16 @@ addUfuncs(PyObject *dictionary) {
}
PyDict_SetItemString(dictionary, "inner1d_no_doc", f);
Py_DECREF(f);
+ f = PyUFunc_FromFuncAndDataAndSignature(cross1d_functions, cross1d_data,
+ cross1d_signatures, 2, 2, 1, PyUFunc_None, "cross1d",
+ "cross product on the last dimension and broadcast on the rest \n"\
+ " \"(3),(3)->(3)\" \n",
+ 0, cross1d_signature);
+ if (f == NULL) {
+ return -1;
+ }
+ PyDict_SetItemString(dictionary, "cross1d", f);
+ Py_DECREF(f);
return 0;
}
@@ -385,9 +469,10 @@ static PyObject *
UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args)
{
int nin, nout, i;
- PyObject *signature, *sig_str;
- PyUFuncObject *f = NULL;
- PyObject *core_num_dims = NULL, *core_dim_ixs = NULL;
+ PyObject *signature=NULL, *sig_str=NULL;
+ PyUFuncObject *f=NULL;
+ PyObject *core_num_dims=NULL, *core_dim_ixs=NULL;
+ PyObject *core_dim_flags=NULL, *core_dim_sizes=NULL;
int core_enabled;
int core_num_ixs = 0;
@@ -442,7 +527,7 @@ UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args)
goto fail;
}
for (i = 0; i < core_num_ixs; i++) {
- PyObject * val = PyLong_FromLong(f->core_dim_ixs[i]);
+ PyObject *val = PyLong_FromLong(f->core_dim_ixs[i]);
PyTuple_SET_ITEM(core_dim_ixs, i, val);
}
}
@@ -450,13 +535,44 @@ UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args)
Py_INCREF(Py_None);
core_dim_ixs = Py_None;
}
+ if (f->core_dim_flags != NULL) {
+ core_dim_flags = PyTuple_New(f->core_num_dim_ix);
+ if (core_dim_flags == NULL) {
+ goto fail;
+ }
+ for (i = 0; i < f->core_num_dim_ix; i++) {
+ PyObject *val = PyLong_FromLong(f->core_dim_flags[i]);
+ PyTuple_SET_ITEM(core_dim_flags, i, val);
+ }
+ }
+ else {
+ Py_INCREF(Py_None);
+ core_dim_flags = Py_None;
+ }
+ if (f->core_dim_sizes != NULL) {
+ core_dim_sizes = PyTuple_New(f->core_num_dim_ix);
+ if (core_dim_sizes == NULL) {
+ goto fail;
+ }
+ for (i = 0; i < f->core_num_dim_ix; i++) {
+ PyObject *val = PyLong_FromLong(f->core_dim_sizes[i]);
+ PyTuple_SET_ITEM(core_dim_sizes, i, val);
+ }
+ }
+ else {
+ Py_INCREF(Py_None);
+ core_dim_sizes = Py_None;
+ }
Py_DECREF(f);
- return Py_BuildValue("iOO", core_enabled, core_num_dims, core_dim_ixs);
+ return Py_BuildValue("iNNNN", core_enabled, core_num_dims,
+ core_dim_ixs, core_dim_flags, core_dim_sizes);
fail:
Py_XDECREF(f);
Py_XDECREF(core_num_dims);
Py_XDECREF(core_dim_ixs);
+ Py_XDECREF(core_dim_flags);
+ Py_XDECREF(core_dim_sizes);
return NULL;
}
@@ -464,8 +580,8 @@ static PyMethodDef UMath_TestsMethods[] = {
{"test_signature", UMath_Tests_test_signature, METH_VARARGS,
"Test signature parsing of ufunc. \n"
"Arguments: nin nout signature \n"
- "If fails, it returns NULL. Otherwise it will returns 0 for scalar ufunc "
- "and 1 for generalized ufunc. \n",
+ "If fails, it returns NULL. Otherwise it returns a tuple of ufunc "
+ "internals. \n",
},
{NULL, NULL, 0, NULL} /* Sentinel */
};
@@ -504,6 +620,7 @@ PyMODINIT_FUNC init_umath_tests(void) {
if (m == NULL) {
return RETVAL(NULL);
}
+
import_array();
import_ufunc();
diff --git a/numpy/core/src/umath/clip.c.src b/numpy/core/src/umath/clip.c.src
new file mode 100644
index 000000000..30fa3d2b3
--- /dev/null
+++ b/numpy/core/src/umath/clip.c.src
@@ -0,0 +1,119 @@
+/**
+ * This module provides the inner loops for the clip ufunc
+ */
+#define _UMATHMODULE
+#define _MULTIARRAYMODULE
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+
+#include "Python.h"
+
+#include "numpy/halffloat.h"
+#include "numpy/npy_math.h"
+#include "numpy/ndarraytypes.h"
+#include "numpy/npy_common.h"
+#include "numpy/utils.h"
+#include "fast_loop_macros.h"
+
+/*
+ * Produce macros that perform nan/nat-propagating min and max
+ */
+
+/**begin repeat
+ * #name = BOOL,
+ * BYTE, UBYTE, SHORT, USHORT, INT, UINT,
+ * LONG, ULONG, LONGLONG, ULONGLONG#
+ */
+#define _NPY_@name@_MIN(a, b) PyArray_MIN(a, b)
+#define _NPY_@name@_MAX(a, b) PyArray_MAX(a, b)
+/**end repeat**/
+
+#define _NPY_HALF_MIN(a, b) (npy_half_isnan(a) || npy_half_le(a, b) ? (a) : (b))
+#define _NPY_HALF_MAX(a, b) (npy_half_isnan(a) || npy_half_ge(a, b) ? (a) : (b))
+
+/**begin repeat
+ * #name = FLOAT, DOUBLE, LONGDOUBLE#
+ */
+#define _NPY_@name@_MIN(a, b) (npy_isnan(a) ? (a) : PyArray_MIN(a, b))
+#define _NPY_@name@_MAX(a, b) (npy_isnan(a) ? (a) : PyArray_MAX(a, b))
+/**end repeat**/
+
+/**begin repeat
+ * #name = CFLOAT, CDOUBLE, CLONGDOUBLE#
+ */
+#define _NPY_@name@_MIN(a, b) (npy_isnan((a).real) || npy_isnan((a).imag) || PyArray_CLT(a, b) ? (a) : (b))
+#define _NPY_@name@_MAX(a, b) (npy_isnan((a).real) || npy_isnan((a).imag) || PyArray_CGT(a, b) ? (a) : (b))
+/**end repeat**/
+
+/**begin repeat
+ * #name = DATETIME, TIMEDELTA#
+ */
+#define _NPY_@name@_MIN(a, b) ( \
+ (a) == NPY_DATETIME_NAT ? (a) : \
+ (b) == NPY_DATETIME_NAT ? (b) : \
+ (a) < (b) ? (a) : (b) \
+)
+#define _NPY_@name@_MAX(a, b) ( \
+ (a) == NPY_DATETIME_NAT ? (a) : \
+ (b) == NPY_DATETIME_NAT ? (b) : \
+ (a) > (b) ? (a) : (b) \
+)
+/**end repeat**/
+
+/**begin repeat
+ *
+ * #name = BOOL,
+ * BYTE, UBYTE, SHORT, USHORT, INT, UINT,
+ * LONG, ULONG, LONGLONG, ULONGLONG,
+ * HALF, FLOAT, DOUBLE, LONGDOUBLE,
+ * CFLOAT, CDOUBLE, CLONGDOUBLE,
+ * DATETIME, TIMEDELTA#
+ * #type = npy_bool,
+ * npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
+ * npy_long, npy_ulong, npy_longlong, npy_ulonglong,
+ * npy_half, npy_float, npy_double, npy_longdouble,
+ * npy_cfloat, npy_cdouble, npy_clongdouble,
+ * npy_datetime, npy_timedelta#
+ */
+
+#define _NPY_CLIP(x, min, max) \
+ _NPY_@name@_MIN(_NPY_@name@_MAX((x), (min)), (max))
+
+NPY_NO_EXPORT void
+@name@_clip(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ if (steps[1] == 0 && steps[2] == 0) {
+ /* min and max are constant throughout the loop, the most common case */
+ /* NOTE: it may be possible to optimize these checks for nan */
+ @type@ min_val = *(@type@ *)args[1];
+ @type@ max_val = *(@type@ *)args[2];
+
+ char *ip1 = args[0], *op1 = args[3];
+ npy_intp is1 = steps[0], os1 = steps[3];
+ npy_intp n = dimensions[0];
+
+ /* contiguous, branch to let the compiler optimize */
+ if (is1 == sizeof(@type@) && os1 == sizeof(@type@)) {
+ for(npy_intp i = 0; i < n; i++, ip1 += is1, op1 += os1) {
+ *(@type@ *)op1 = _NPY_CLIP(*(@type@ *)ip1, min_val, max_val);
+ }
+ }
+ else {
+ for(npy_intp i = 0; i < n; i++, ip1 += is1, op1 += os1) {
+ *(@type@ *)op1 = _NPY_CLIP(*(@type@ *)ip1, min_val, max_val);
+ }
+ }
+ }
+ else {
+ TERNARY_LOOP {
+ *(@type@ *)op1 = _NPY_CLIP(*(@type@ *)ip1, *(@type@ *)ip2, *(@type@ *)ip3);
+ }
+ }
+ npy_clear_floatstatus_barrier((char*)dimensions);
+}
+
+// clean up the macros we defined above
+#undef _NPY_CLIP
+#undef _NPY_@name@_MAX
+#undef _NPY_@name@_MIN
+
+/**end repeat**/
diff --git a/numpy/core/src/umath/clip.h.src b/numpy/core/src/umath/clip.h.src
new file mode 100644
index 000000000..d77971ad7
--- /dev/null
+++ b/numpy/core/src/umath/clip.h.src
@@ -0,0 +1,18 @@
+#ifndef _NPY_UMATH_CLIP_H_
+#define _NPY_UMATH_CLIP_H_
+
+
+/**begin repeat
+ *
+ * #name = BOOL,
+ * BYTE, UBYTE, SHORT, USHORT, INT, UINT,
+ * LONG, ULONG, LONGLONG, ULONGLONG,
+ * HALF, FLOAT, DOUBLE, LONGDOUBLE,
+ * CFLOAT, CDOUBLE, CLONGDOUBLE,
+ * DATETIME, TIMEDELTA#
+ */
+NPY_NO_EXPORT void
+@name@_clip(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+/**end repeat**/
+
+#endif
diff --git a/numpy/core/src/umath/cpuid.c b/numpy/core/src/umath/cpuid.c
index 6744ceb05..72c6493e8 100644
--- a/numpy/core/src/umath/cpuid.c
+++ b/numpy/core/src/umath/cpuid.c
@@ -11,6 +11,7 @@
#define XCR_XFEATURE_ENABLED_MASK 0x0
#define XSTATE_SSE 0x2
#define XSTATE_YMM 0x4
+#define XSTATE_ZMM 0x70
/*
* verify the OS supports avx instructions
@@ -33,6 +34,38 @@ int os_avx_support(void)
#endif
}
+static NPY_INLINE
+int os_avx512_support(void)
+{
+#if HAVE_XGETBV
+ unsigned int eax, edx;
+ unsigned int ecx = XCR_XFEATURE_ENABLED_MASK;
+ unsigned int xcr0 = XSTATE_ZMM | XSTATE_YMM | XSTATE_SSE;
+ __asm__("xgetbv" : "=a" (eax), "=d" (edx) : "c" (ecx));
+ return (eax & xcr0) == xcr0;
+#else
+ return 0;
+#endif
+}
+
+static NPY_INLINE
+int cpu_supports_fma(void)
+{
+#ifdef __x86_64__
+ unsigned int feature = 0x01;
+ unsigned int a, b, c, d;
+ __asm__ volatile (
+ "cpuid" "\n\t"
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d)
+ : "a" (feature));
+ /*
+ * FMA is the 12th bit of ECX
+ */
+ return (c >> 12) & 1;
+#else
+ return 0;
+#endif
+}
/*
* Primitive cpu feature detect function
@@ -42,7 +75,17 @@ NPY_NO_EXPORT int
npy_cpu_supports(const char * feature)
{
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
- if (strcmp(feature, "avx2") == 0) {
+ if (strcmp(feature, "avx512f") == 0) {
+#ifdef HAVE___BUILTIN_CPU_SUPPORTS_AVX512F
+ return __builtin_cpu_supports("avx512f") && os_avx512_support();
+#else
+ return 0;
+#endif
+ }
+ else if (strcmp(feature, "fma") == 0) {
+ return cpu_supports_fma() && __builtin_cpu_supports("avx2") && os_avx_support();
+ }
+ else if (strcmp(feature, "avx2") == 0) {
return __builtin_cpu_supports("avx2") && os_avx_support();
}
else if (strcmp(feature, "avx") == 0) {
diff --git a/numpy/core/src/umath/fast_loop_macros.h b/numpy/core/src/umath/fast_loop_macros.h
new file mode 100644
index 000000000..ae6d69a3e
--- /dev/null
+++ b/numpy/core/src/umath/fast_loop_macros.h
@@ -0,0 +1,234 @@
+/**
+ * Macros to help build fast ufunc inner loops.
+ *
+ * These expect to have access to the arguments of a typical ufunc loop,
+ *
+ * char **args
+ * npy_intp *dimensions
+ * npy_intp *steps
+ */
+#ifndef _NPY_UMATH_FAST_LOOP_MACROS_H_
+#define _NPY_UMATH_FAST_LOOP_MACROS_H_
+
+#include "simd.inc"
+
+/**
+ * Simple unoptimized loop macros that iterate over the ufunc arguments in
+ * parallel.
+ * @{
+ */
+
+/** (<ignored>) -> (op1) */
+#define OUTPUT_LOOP\
+ char *op1 = args[1];\
+ npy_intp os1 = steps[1];\
+ npy_intp n = dimensions[0];\
+ npy_intp i;\
+ for(i = 0; i < n; i++, op1 += os1)
+
+/** (ip1) -> (op1) */
+#define UNARY_LOOP\
+ char *ip1 = args[0], *op1 = args[1];\
+ npy_intp is1 = steps[0], os1 = steps[1];\
+ npy_intp n = dimensions[0];\
+ npy_intp i;\
+ for(i = 0; i < n; i++, ip1 += is1, op1 += os1)
+
+/** (ip1) -> (op1, op2) */
+#define UNARY_LOOP_TWO_OUT\
+ char *ip1 = args[0], *op1 = args[1], *op2 = args[2];\
+ npy_intp is1 = steps[0], os1 = steps[1], os2 = steps[2];\
+ npy_intp n = dimensions[0];\
+ npy_intp i;\
+ for(i = 0; i < n; i++, ip1 += is1, op1 += os1, op2 += os2)
+
+/** (ip1, ip2) -> (op1) */
+#define BINARY_LOOP\
+ char *ip1 = args[0], *ip2 = args[1], *op1 = args[2];\
+ npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2];\
+ npy_intp n = dimensions[0];\
+ npy_intp i;\
+ for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1)
+
+/** (ip1, ip2) -> (op1, op2) */
+#define BINARY_LOOP_TWO_OUT\
+ char *ip1 = args[0], *ip2 = args[1], *op1 = args[2], *op2 = args[3];\
+ npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2], os2 = steps[3];\
+ npy_intp n = dimensions[0];\
+ npy_intp i;\
+ for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1, op2 += os2)
+
+/** (ip1, ip2, ip3) -> (op1) */
+#define TERNARY_LOOP\
+ char *ip1 = args[0], *ip2 = args[1], *ip3 = args[2], *op1 = args[3];\
+ npy_intp is1 = steps[0], is2 = steps[1], is3 = steps[2], os1 = steps[3];\
+ npy_intp n = dimensions[0];\
+ npy_intp i;\
+ for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, ip3 += is3, op1 += os1)
+
+/** @} */
+
+/* unary loop input and output contiguous */
+#define IS_UNARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \
+ steps[1] == sizeof(tout))
+
+#define IS_OUTPUT_CONT(tout) (steps[1] == sizeof(tout))
+
+#define IS_BINARY_REDUCE ((args[0] == args[2])\
+ && (steps[0] == steps[2])\
+ && (steps[0] == 0))
+
+/* binary loop input and output contiguous */
+#define IS_BINARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \
+ steps[1] == sizeof(tin) && \
+ steps[2] == sizeof(tout))
+
+/* binary loop input and output contiguous with first scalar */
+#define IS_BINARY_CONT_S1(tin, tout) (steps[0] == 0 && \
+ steps[1] == sizeof(tin) && \
+ steps[2] == sizeof(tout))
+
+/* binary loop input and output contiguous with second scalar */
+#define IS_BINARY_CONT_S2(tin, tout) (steps[0] == sizeof(tin) && \
+ steps[1] == 0 && \
+ steps[2] == sizeof(tout))
+
+/*
+ * loop with contiguous specialization
+ * op should be the code working on `tin in` and
+ * storing the result in `tout *out`
+ * combine with NPY_GCC_OPT_3 to allow autovectorization
+ * should only be used where its worthwhile to avoid code bloat
+ */
+#define BASE_UNARY_LOOP(tin, tout, op) \
+ UNARY_LOOP { \
+ const tin in = *(tin *)ip1; \
+ tout *out = (tout *)op1; \
+ op; \
+ }
+
+#define UNARY_LOOP_FAST(tin, tout, op) \
+ do { \
+ /* condition allows compiler to optimize the generic macro */ \
+ if (IS_UNARY_CONT(tin, tout)) { \
+ if (args[0] == args[1]) { \
+ BASE_UNARY_LOOP(tin, tout, op) \
+ } \
+ else { \
+ BASE_UNARY_LOOP(tin, tout, op) \
+ } \
+ } \
+ else { \
+ BASE_UNARY_LOOP(tin, tout, op) \
+ } \
+ } \
+ while (0)
+
+/*
+ * loop with contiguous specialization
+ * op should be the code working on `tin in1`, `tin in2` and
+ * storing the result in `tout *out`
+ * combine with NPY_GCC_OPT_3 to allow autovectorization
+ * should only be used where its worthwhile to avoid code bloat
+ */
+#define BASE_BINARY_LOOP(tin, tout, op) \
+ BINARY_LOOP { \
+ const tin in1 = *(tin *)ip1; \
+ const tin in2 = *(tin *)ip2; \
+ tout *out = (tout *)op1; \
+ op; \
+ }
+
+/*
+ * unfortunately gcc 6/7 regressed and we need to give it additional hints to
+ * vectorize inplace operations (PR80198)
+ * must only be used after op1 == ip1 or ip2 has been checked
+ * TODO: using ivdep might allow other compilers to vectorize too
+ */
+#if __GNUC__ >= 6
+#define IVDEP_LOOP _Pragma("GCC ivdep")
+#else
+#define IVDEP_LOOP
+#endif
+#define BASE_BINARY_LOOP_INP(tin, tout, op) \
+ char *ip1 = args[0], *ip2 = args[1], *op1 = args[2];\
+ npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2];\
+ npy_intp n = dimensions[0];\
+ npy_intp i;\
+ IVDEP_LOOP \
+ for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1) { \
+ const tin in1 = *(tin *)ip1; \
+ const tin in2 = *(tin *)ip2; \
+ tout *out = (tout *)op1; \
+ op; \
+ }
+
+#define BASE_BINARY_LOOP_S(tin, tout, cin, cinp, vin, vinp, op) \
+ const tin cin = *(tin *)cinp; \
+ BINARY_LOOP { \
+ const tin vin = *(tin *)vinp; \
+ tout *out = (tout *)op1; \
+ op; \
+ }
+
+/* PR80198 again, scalar works without the pragma */
+#define BASE_BINARY_LOOP_S_INP(tin, tout, cin, cinp, vin, vinp, op) \
+ const tin cin = *(tin *)cinp; \
+ BINARY_LOOP { \
+ const tin vin = *(tin *)vinp; \
+ tout *out = (tout *)vinp; \
+ op; \
+ }
+
+#define BINARY_LOOP_FAST(tin, tout, op) \
+ do { \
+ /* condition allows compiler to optimize the generic macro */ \
+ if (IS_BINARY_CONT(tin, tout)) { \
+ if (abs_ptrdiff(args[2], args[0]) == 0 && \
+ abs_ptrdiff(args[2], args[1]) >= NPY_MAX_SIMD_SIZE) { \
+ BASE_BINARY_LOOP_INP(tin, tout, op) \
+ } \
+ else if (abs_ptrdiff(args[2], args[1]) == 0 && \
+ abs_ptrdiff(args[2], args[0]) >= NPY_MAX_SIMD_SIZE) { \
+ BASE_BINARY_LOOP_INP(tin, tout, op) \
+ } \
+ else { \
+ BASE_BINARY_LOOP(tin, tout, op) \
+ } \
+ } \
+ else if (IS_BINARY_CONT_S1(tin, tout)) { \
+ if (abs_ptrdiff(args[2], args[1]) == 0) { \
+ BASE_BINARY_LOOP_S_INP(tin, tout, in1, args[0], in2, ip2, op) \
+ } \
+ else { \
+ BASE_BINARY_LOOP_S(tin, tout, in1, args[0], in2, ip2, op) \
+ } \
+ } \
+ else if (IS_BINARY_CONT_S2(tin, tout)) { \
+ if (abs_ptrdiff(args[2], args[0]) == 0) { \
+ BASE_BINARY_LOOP_S_INP(tin, tout, in2, args[1], in1, ip1, op) \
+ } \
+ else { \
+ BASE_BINARY_LOOP_S(tin, tout, in2, args[1], in1, ip1, op) \
+ }\
+ } \
+ else { \
+ BASE_BINARY_LOOP(tin, tout, op) \
+ } \
+ } \
+ while (0)
+
+#define BINARY_REDUCE_LOOP_INNER\
+ char *ip2 = args[1]; \
+ npy_intp is2 = steps[1]; \
+ npy_intp n = dimensions[0]; \
+ npy_intp i; \
+ for(i = 0; i < n; i++, ip2 += is2)
+
+#define BINARY_REDUCE_LOOP(TYPE)\
+ char *iop1 = args[0]; \
+ TYPE io1 = *(TYPE *)iop1; \
+ BINARY_REDUCE_LOOP_INNER
+
+
+#endif /* _NPY_UMATH_FAST_LOOP_MACROS_H_ */
diff --git a/numpy/core/src/umath/funcs.inc.src b/numpy/core/src/umath/funcs.inc.src
index da2ab07f8..10ed66e50 100644
--- a/numpy/core/src/umath/funcs.inc.src
+++ b/numpy/core/src/umath/funcs.inc.src
@@ -160,6 +160,39 @@ npy_ObjectLogicalNot(PyObject *i1)
}
static PyObject *
+npy_ObjectFloor(PyObject *obj) {
+ static PyObject *math_floor_func = NULL;
+
+ npy_cache_import("math", "floor", &math_floor_func);
+ if (math_floor_func == NULL) {
+ return NULL;
+ }
+ return PyObject_CallFunction(math_floor_func, "O", obj);
+}
+
+static PyObject *
+npy_ObjectCeil(PyObject *obj) {
+ static PyObject *math_ceil_func = NULL;
+
+ npy_cache_import("math", "ceil", &math_ceil_func);
+ if (math_ceil_func == NULL) {
+ return NULL;
+ }
+ return PyObject_CallFunction(math_ceil_func, "O", obj);
+}
+
+static PyObject *
+npy_ObjectTrunc(PyObject *obj) {
+ static PyObject *math_trunc_func = NULL;
+
+ npy_cache_import("math", "trunc", &math_trunc_func);
+ if (math_trunc_func == NULL) {
+ return NULL;
+ }
+ return PyObject_CallFunction(math_trunc_func, "O", obj);
+}
+
+static PyObject *
npy_ObjectGCD(PyObject *i1, PyObject *i2)
{
PyObject *gcd = NULL;
@@ -195,7 +228,8 @@ npy_ObjectGCD(PyObject *i1, PyObject *i2)
return NULL;
}
/* _gcd has some unusual behaviour regarding sign */
- return PyNumber_Absolute(gcd);
+ Py_SETREF(gcd, PyNumber_Absolute(gcd));
+ return gcd;
}
}
@@ -213,17 +247,30 @@ npy_ObjectLCM(PyObject *i1, PyObject *i2)
* no remainder
*/
tmp = PyNumber_FloorDivide(i1, gcd);
+ Py_DECREF(gcd);
if(tmp == NULL) {
return NULL;
}
- tmp = PyNumber_Multiply(tmp, i2);
+ Py_SETREF(tmp, PyNumber_Multiply(tmp, i2));
if(tmp == NULL) {
return NULL;
}
/* even though we fix gcd to be positive, we need to do it again here */
- return PyNumber_Absolute(tmp);
+ Py_SETREF(tmp, PyNumber_Absolute(tmp));
+ return tmp;
+}
+
+
+static PyObject *
+npy_ObjectClip(PyObject *arr, PyObject *min, PyObject *max) {
+ PyObject *o = npy_ObjectMax(arr, min);
+ if (o == NULL) {
+ return NULL;
+ }
+ Py_SETREF(o, npy_ObjectMin(o, max));
+ return o;
}
/*
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index e62942efd..d948e25bb 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -46,200 +46,9 @@
*/
#include "simd.inc"
+/** Provides the various *_LOOP macros */
+#include "fast_loop_macros.h"
-/*
- *****************************************************************************
- ** UFUNC LOOPS **
- *****************************************************************************
- */
-
-/* unary loop input and output contiguous */
-#define IS_UNARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \
- steps[1] == sizeof(tout))
-
-#define IS_BINARY_REDUCE ((args[0] == args[2])\
- && (steps[0] == steps[2])\
- && (steps[0] == 0))
-
-/* binary loop input and output contiguous */
-#define IS_BINARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \
- steps[1] == sizeof(tin) && \
- steps[2] == sizeof(tout))
-/* binary loop input and output contiguous with first scalar */
-#define IS_BINARY_CONT_S1(tin, tout) (steps[0] == 0 && \
- steps[1] == sizeof(tin) && \
- steps[2] == sizeof(tout))
-/* binary loop input and output contiguous with second scalar */
-#define IS_BINARY_CONT_S2(tin, tout) (steps[0] == sizeof(tin) && \
- steps[1] == 0 && \
- steps[2] == sizeof(tout))
-
-#define OUTPUT_LOOP\
- char *op1 = args[1];\
- npy_intp os1 = steps[1];\
- npy_intp n = dimensions[0];\
- npy_intp i;\
- for(i = 0; i < n; i++, op1 += os1)
-
-#define UNARY_LOOP\
- char *ip1 = args[0], *op1 = args[1];\
- npy_intp is1 = steps[0], os1 = steps[1];\
- npy_intp n = dimensions[0];\
- npy_intp i;\
- for(i = 0; i < n; i++, ip1 += is1, op1 += os1)
-
-/*
- * loop with contiguous specialization
- * op should be the code working on `tin in` and
- * storing the result in `tout * out`
- * combine with NPY_GCC_OPT_3 to allow autovectorization
- * should only be used where its worthwhile to avoid code bloat
- */
-#define BASE_UNARY_LOOP(tin, tout, op) \
- UNARY_LOOP { \
- const tin in = *(tin *)ip1; \
- tout * out = (tout *)op1; \
- op; \
- }
-#define UNARY_LOOP_FAST(tin, tout, op) \
- do { \
- /* condition allows compiler to optimize the generic macro */ \
- if (IS_UNARY_CONT(tin, tout)) { \
- if (args[0] == args[1]) { \
- BASE_UNARY_LOOP(tin, tout, op) \
- } \
- else { \
- BASE_UNARY_LOOP(tin, tout, op) \
- } \
- } \
- else { \
- BASE_UNARY_LOOP(tin, tout, op) \
- } \
- } \
- while (0)
-
-#define UNARY_LOOP_TWO_OUT\
- char *ip1 = args[0], *op1 = args[1], *op2 = args[2];\
- npy_intp is1 = steps[0], os1 = steps[1], os2 = steps[2];\
- npy_intp n = dimensions[0];\
- npy_intp i;\
- for(i = 0; i < n; i++, ip1 += is1, op1 += os1, op2 += os2)
-
-#define BINARY_LOOP\
- char *ip1 = args[0], *ip2 = args[1], *op1 = args[2];\
- npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2];\
- npy_intp n = dimensions[0];\
- npy_intp i;\
- for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1)
-
-/*
- * loop with contiguous specialization
- * op should be the code working on `tin in1`, `tin in2` and
- * storing the result in `tout * out`
- * combine with NPY_GCC_OPT_3 to allow autovectorization
- * should only be used where its worthwhile to avoid code bloat
- */
-#define BASE_BINARY_LOOP(tin, tout, op) \
- BINARY_LOOP { \
- const tin in1 = *(tin *)ip1; \
- const tin in2 = *(tin *)ip2; \
- tout * out = (tout *)op1; \
- op; \
- }
-/*
- * unfortunately gcc 6/7 regressed and we need to give it additional hints to
- * vectorize inplace operations (PR80198)
- * must only be used after op1 == ip1 or ip2 has been checked
- * TODO: using ivdep might allow other compilers to vectorize too
- */
-#if __GNUC__ >= 6
-#define IVDEP_LOOP _Pragma("GCC ivdep")
-#else
-#define IVDEP_LOOP
-#endif
-#define BASE_BINARY_LOOP_INP(tin, tout, op) \
- char *ip1 = args[0], *ip2 = args[1], *op1 = args[2];\
- npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2];\
- npy_intp n = dimensions[0];\
- npy_intp i;\
- IVDEP_LOOP \
- for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1) { \
- const tin in1 = *(tin *)ip1; \
- const tin in2 = *(tin *)ip2; \
- tout * out = (tout *)op1; \
- op; \
- }
-#define BASE_BINARY_LOOP_S(tin, tout, cin, cinp, vin, vinp, op) \
- const tin cin = *(tin *)cinp; \
- BINARY_LOOP { \
- const tin vin = *(tin *)vinp; \
- tout * out = (tout *)op1; \
- op; \
- }
-/* PR80198 again, scalar works without the pragma */
-#define BASE_BINARY_LOOP_S_INP(tin, tout, cin, cinp, vin, vinp, op) \
- const tin cin = *(tin *)cinp; \
- BINARY_LOOP { \
- const tin vin = *(tin *)vinp; \
- tout * out = (tout *)vinp; \
- op; \
- }
-#define BINARY_LOOP_FAST(tin, tout, op) \
- do { \
- /* condition allows compiler to optimize the generic macro */ \
- if (IS_BINARY_CONT(tin, tout)) { \
- if (abs_ptrdiff(args[2], args[0]) == 0 && \
- abs_ptrdiff(args[2], args[1]) >= NPY_MAX_SIMD_SIZE) { \
- BASE_BINARY_LOOP_INP(tin, tout, op) \
- } \
- else if (abs_ptrdiff(args[2], args[1]) == 0 && \
- abs_ptrdiff(args[2], args[0]) >= NPY_MAX_SIMD_SIZE) { \
- BASE_BINARY_LOOP_INP(tin, tout, op) \
- } \
- else { \
- BASE_BINARY_LOOP(tin, tout, op) \
- } \
- } \
- else if (IS_BINARY_CONT_S1(tin, tout)) { \
- if (abs_ptrdiff(args[2], args[1]) == 0) { \
- BASE_BINARY_LOOP_S_INP(tin, tout, in1, args[0], in2, ip2, op) \
- } \
- else { \
- BASE_BINARY_LOOP_S(tin, tout, in1, args[0], in2, ip2, op) \
- } \
- } \
- else if (IS_BINARY_CONT_S2(tin, tout)) { \
- if (abs_ptrdiff(args[2], args[0]) == 0) { \
- BASE_BINARY_LOOP_S_INP(tin, tout, in2, args[1], in1, ip1, op) \
- } \
- else { \
- BASE_BINARY_LOOP_S(tin, tout, in2, args[1], in1, ip1, op) \
- }\
- } \
- else { \
- BASE_BINARY_LOOP(tin, tout, op) \
- } \
- } \
- while (0)
-
-#define BINARY_REDUCE_LOOP_INNER\
- char *ip2 = args[1]; \
- npy_intp is2 = steps[1]; \
- npy_intp n = dimensions[0]; \
- npy_intp i; \
- for(i = 0; i < n; i++, ip2 += is2)
-
-#define BINARY_REDUCE_LOOP(TYPE)\
- char *iop1 = args[0]; \
- TYPE io1 = *(TYPE *)iop1; \
- BINARY_REDUCE_LOOP_INNER
-
-#define BINARY_LOOP_TWO_OUT\
- char *ip1 = args[0], *ip2 = args[1], *op1 = args[2], *op2 = args[3];\
- npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2], os2 = steps[3];\
- npy_intp n = dimensions[0];\
- npy_intp i;\
- for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1, op2 += os2)
/******************************************************************************
** GENERIC FLOAT LOOPS **
@@ -566,16 +375,38 @@ NPY_NO_EXPORT void
PyUFunc_O_O_method(char **args, npy_intp *dimensions, npy_intp *steps, void *func)
{
char *meth = (char *)func;
+ PyObject *tup = PyTuple_New(0);
+ if (tup == NULL) {
+ return;
+ }
UNARY_LOOP {
PyObject *in1 = *(PyObject **)ip1;
PyObject **out = (PyObject **)op1;
- PyObject *ret = PyObject_CallMethod(in1 ? in1 : Py_None, meth, NULL);
+ PyObject *ret, *func;
+ func = PyObject_GetAttrString(in1 ? in1 : Py_None, meth);
+ if (func == NULL || !PyCallable_Check(func)) {
+ PyObject *exc, *val, *tb;
+ PyTypeObject *type = in1 ? Py_TYPE(in1) : Py_TYPE(Py_None);
+ PyErr_Fetch(&exc, &val, &tb);
+ PyErr_Format(PyExc_TypeError,
+ "loop of ufunc does not support argument %d of "
+ "type %s which has no callable %s method",
+ i, type->tp_name, meth);
+ npy_PyErr_ChainExceptionsCause(exc, val, tb);
+ Py_DECREF(tup);
+ Py_XDECREF(func);
+ return;
+ }
+ ret = PyObject_Call(func, tup, NULL);
+ Py_DECREF(func);
if (ret == NULL) {
+ Py_DECREF(tup);
return;
}
Py_XDECREF(*out);
*out = ret;
}
+ Py_DECREF(tup);
}
/*UFUNC_API*/
@@ -596,6 +427,28 @@ PyUFunc_OO_O(char **args, npy_intp *dimensions, npy_intp *steps, void *func)
}
}
+NPY_NO_EXPORT void
+PyUFunc_OOO_O(char **args, npy_intp *dimensions, npy_intp *steps, void *func)
+{
+ ternaryfunc f = (ternaryfunc)func;
+ TERNARY_LOOP {
+ PyObject *in1 = *(PyObject **)ip1;
+ PyObject *in2 = *(PyObject **)ip2;
+ PyObject *in3 = *(PyObject **)ip3;
+ PyObject **out = (PyObject **)op1;
+ PyObject *ret = f(
+ in1 ? in1 : Py_None,
+ in2 ? in2 : Py_None,
+ in3 ? in3 : Py_None
+ );
+ if (ret == NULL) {
+ return;
+ }
+ Py_XDECREF(*out);
+ *out = ret;
+ }
+}
+
/*UFUNC_API*/
NPY_NO_EXPORT void
PyUFunc_OO_O_method(char **args, npy_intp *dimensions, npy_intp *steps, void *func)
@@ -815,6 +668,23 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN
}
+/**begin repeat
+ * #kind = isnan, isinf, isfinite#
+ * #func = npy_isnan, npy_isinf, npy_isfinite#
+ * #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
+ **/
+NPY_NO_EXPORT void
+BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ /*
+ * The (void)in; suppresses an unused variable warning raised by gcc and allows
+ * us to re-use this macro even though we do not depend on in
+ */
+ UNARY_LOOP_FAST(npy_bool, npy_bool, (void)in; *out = @val@);
+}
+
+/**end repeat**/
+
/*
*****************************************************************************
** INTEGER LOOPS
@@ -829,6 +699,7 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN
* #ftype = npy_float, npy_float, npy_float, npy_float, npy_double, npy_double,
* npy_double, npy_double, npy_double, npy_double#
* #SIGNED = 1, 0, 1, 0, 1, 0, 1, 0, 1, 0#
+ * #c = hh,uhh,h,uh,,u,l,ul,ll,ull#
*/
#define @TYPE@_floor_divide @TYPE@_divide
@@ -906,16 +777,15 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
/**begin repeat2
* Arithmetic
- * #kind = add, subtract, multiply, bitwise_and, bitwise_or, bitwise_xor,
- * left_shift, right_shift#
- * #OP = +, -,*, &, |, ^, <<, >>#
+ * #kind = add, subtract, multiply, bitwise_and, bitwise_or, bitwise_xor#
+ * #OP = +, -, *, &, |, ^#
*/
#if @CHK@
NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
@TYPE@_@kind@@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
- if(IS_BINARY_REDUCE) {
+ if (IS_BINARY_REDUCE) {
BINARY_REDUCE_LOOP(@type@) {
io1 @OP@= *(@type@ *)ip2;
}
@@ -929,6 +799,47 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
/**end repeat2**/
+/*
+ * Arithmetic bit shift operations.
+ *
+ * Intel hardware masks bit shift values, so large shifts wrap around
+ * and can produce surprising results. The special handling ensures that
+ * behavior is independent of compiler or hardware.
+ * TODO: We could implement consistent behavior for negative shifts,
+ * which is undefined in C.
+ */
+
+#define INT_left_shift_needs_clear_floatstatus
+#define UINT_left_shift_needs_clear_floatstatus
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_left_shift@isa@(char **args, npy_intp *dimensions, npy_intp *steps,
+ void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_FAST(@type@, @type@, *out = npy_lshift@c@(in1, in2));
+
+#ifdef @TYPE@_left_shift_needs_clear_floatstatus
+ // For some reason, our macOS CI sets an "invalid" flag here, but only
+ // for some types.
+ npy_clear_floatstatus_barrier((char*)dimensions);
+#endif
+}
+
+#undef INT_left_shift_needs_clear_floatstatus
+#undef UINT_left_shift_needs_clear_floatstatus
+
+NPY_NO_EXPORT
+#ifndef NPY_DO_NOT_OPTIMIZE_@TYPE@_right_shift
+NPY_GCC_OPT_3
+#endif
+void
+@TYPE@_right_shift@isa@(char **args, npy_intp *dimensions, npy_intp *steps,
+ void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_FAST(@type@, @type@, *out = npy_rshift@c@(in1, in2));
+}
+
+
/**begin repeat2
* #kind = equal, not_equal, greater, greater_equal, less, less_equal,
* logical_and, logical_or#
@@ -1046,6 +957,22 @@ NPY_NO_EXPORT void
}
}
+/**begin repeat1
+ * #kind = isnan, isinf, isfinite#
+ * #func = npy_isnan, npy_isinf, npy_isfinite#
+ * #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
+ **/
+NPY_NO_EXPORT void
+@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ /*
+ * The (void)in; suppresses an unused variable warning raised by gcc and allows
+ * us to re-use this macro even though we do not depend on in
+ */
+ UNARY_LOOP_FAST(@type@, npy_bool, (void)in; *out = @val@);
+}
+/**end repeat1**/
+
/**end repeat**/
/**begin repeat
@@ -1165,13 +1092,10 @@ NPY_NO_EXPORT void
* #c = u,u,u,ul,ull#
*/
-NPY_NO_EXPORT void
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
- UNARY_LOOP {
- const @type@ in1 = *(@type@ *)ip1;
- *((@type@ *)op1) = in1;
- }
+ UNARY_LOOP_FAST(@type@, @type@, *out = in);
}
NPY_NO_EXPORT NPY_GCC_OPT_3 void
@@ -1313,6 +1237,15 @@ NPY_NO_EXPORT void
}
NPY_NO_EXPORT void
+@TYPE@_isfinite(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *((npy_bool *)op1) = (in1 != NPY_DATETIME_NAT);
+ }
+}
+
+NPY_NO_EXPORT void
@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
{
OUTPUT_LOOP {
@@ -1591,6 +1524,90 @@ TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *
}
}
+NPY_NO_EXPORT void
+TIMEDELTA_mm_m_remainder(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_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ if (in2 == 0) {
+ npy_set_floatstatus_divbyzero();
+ *((npy_timedelta *)op1) = NPY_DATETIME_NAT;
+ }
+ else {
+ /* handle mixed case the way Python does */
+ const npy_timedelta rem = in1 % in2;
+ if ((in1 > 0) == (in2 > 0) || rem == 0) {
+ *((npy_timedelta *)op1) = rem;
+ }
+ else {
+ *((npy_timedelta *)op1) = rem + in2;
+ }
+ }
+ }
+ }
+}
+
+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_int64 *)op1) = 0;
+ }
+ else if (in2 == 0) {
+ npy_set_floatstatus_divbyzero();
+ *((npy_int64 *)op1) = 0;
+ }
+ else {
+ if (((in1 > 0) != (in2 > 0)) && (in1 % in2 != 0)) {
+ *((npy_int64 *)op1) = in1/in2 - 1;
+ }
+ else {
+ *((npy_int64 *)op1) = in1/in2;
+ }
+ }
+ }
+}
+
+NPY_NO_EXPORT void
+TIMEDELTA_mm_qm_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_TWO_OUT {
+ 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_int64 *)op1) = 0;
+ *((npy_timedelta *)op2) = NPY_DATETIME_NAT;
+ }
+ else if (in2 == 0) {
+ npy_set_floatstatus_divbyzero();
+ *((npy_int64 *)op1) = 0;
+ *((npy_timedelta *)op2) = NPY_DATETIME_NAT;
+ }
+ else {
+ const npy_int64 quo = in1 / in2;
+ const npy_timedelta rem = in1 % in2;
+ if ((in1 > 0) == (in2 > 0) || rem == 0) {
+ *((npy_int64 *)op1) = quo;
+ *((npy_timedelta *)op2) = rem;
+ }
+ else {
+ *((npy_int64 *)op1) = quo - 1;
+ *((npy_timedelta *)op2) = rem + in2;
+ }
+ }
+ }
+}
+
/*
*****************************************************************************
** FLOAT LOOPS **
@@ -1617,6 +1634,176 @@ NPY_NO_EXPORT void
/**end repeat**/
+/**begin repeat
+ * #func = rint, ceil, floor, trunc#
+ * #scalarf = npy_rint, npy_ceil, npy_floor, npy_trunc#
+ */
+
+/**begin repeat1
+* #TYPE = FLOAT, DOUBLE#
+* #type = npy_float, npy_double#
+* #typesub = f, #
+*/
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_@func@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *(@type@ *)op1 = @scalarf@@typesub@(in1);
+ }
+}
+
+
+/**end repeat1**/
+/**end repeat**/
+
+/**begin repeat
+ * #func = sin, cos, exp, log#
+ * #scalarf = npy_sinf, npy_cosf, npy_expf, npy_logf#
+ */
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+FLOAT_@func@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ UNARY_LOOP {
+ const npy_float in1 = *(npy_float *)ip1;
+ *(npy_float *)op1 = @scalarf@(in1);
+ }
+}
+
+/**end repeat**/
+
+/**begin repeat
+ * #isa = avx512f, fma#
+ * #ISA = AVX512F, FMA#
+ * #CHK = HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS#
+ */
+
+/**begin repeat1
+ * #TYPE = FLOAT, DOUBLE#
+ * #type = npy_float, npy_double#
+ * #typesub = f, #
+ */
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_sqrt_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ if (!run_unary_@isa@_sqrt_@TYPE@(args, dimensions, steps)) {
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *(@type@ *)op1 = npy_sqrt@typesub@(in1);
+ }
+ }
+}
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_absolute_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ if (!run_unary_@isa@_absolute_@TYPE@(args, dimensions, steps)) {
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ const @type@ tmp = in1 > 0 ? in1 : -in1;
+ /* add 0 to clear -0.0 */
+ *((@type@ *)op1) = tmp + 0;
+ }
+ }
+ npy_clear_floatstatus_barrier((char*)dimensions);
+}
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_square_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ if (!run_unary_@isa@_square_@TYPE@(args, dimensions, steps)) {
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *(@type@ *)op1 = in1*in1;
+ }
+ }
+}
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_reciprocal_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ if (!run_unary_@isa@_reciprocal_@TYPE@(args, dimensions, steps)) {
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *(@type@ *)op1 = 1.0f/in1;
+ }
+ }
+}
+
+/**begin repeat2
+ * #func = rint, ceil, floor, trunc#
+ * #scalarf = npy_rint, npy_ceil, npy_floor, npy_trunc#
+ */
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_@func@_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ if (!run_unary_@isa@_@func@_@TYPE@(args, dimensions, steps)) {
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *(@type@ *)op1 = @scalarf@@typesub@(in1);
+ }
+ }
+}
+
+/**end repeat2**/
+/**end repeat1**/
+
+/**begin repeat1
+ * #func = exp, log#
+ * #scalarf = npy_expf, npy_logf#
+ */
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+FLOAT_@func@_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ if (!run_unary_@isa@_@func@_FLOAT(args, dimensions, steps)) {
+ UNARY_LOOP {
+ /*
+ * We use the AVX function to compute exp/log for scalar elements as well.
+ * This is needed to ensure the output of strided and non-strided
+ * cases match. SIMD code handles strided input cases, but not
+ * strided output.
+ */
+#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS
+ @ISA@_@func@_FLOAT((npy_float *)op1, (npy_float *)ip1, 1, steps[0]);
+#else
+ const npy_float in1 = *(npy_float *)ip1;
+ *(npy_float *)op1 = @scalarf@(in1);
+#endif
+ }
+ }
+}
+
+/**end repeat1**/
+
+/**begin repeat1
+ * #func = cos, sin#
+ * #enum = npy_compute_cos, npy_compute_sin#
+ * #scalarf = npy_cosf, npy_sinf#
+ */
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+FLOAT_@func@_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
+{
+ if (!run_unary_@isa@_sincos_FLOAT(args, dimensions, steps, @enum@)) {
+ UNARY_LOOP {
+#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS
+ @ISA@_sincos_FLOAT((npy_float *)op1, (npy_float *)ip1, 1, steps[0], @enum@);
+#else
+ const npy_float in1 = *(npy_float *)ip1;
+ *(npy_float *)op1 = @scalarf@(in1);
+#endif
+ }
+ }
+}
+
+/**end repeat1**/
+/**end repeat**/
+
/**begin repeat
* Float types
@@ -1833,11 +2020,9 @@ NPY_NO_EXPORT void
if (!run_unary_reduce_simd_@kind@_@TYPE@(args, dimensions, steps)) {
BINARY_REDUCE_LOOP(@type@) {
const @type@ in2 = *(@type@ *)ip2;
+ /* Order of operations important for MSVC 2015 */
io1 = (io1 @OP@ in2 || npy_isnan(io1)) ? io1 : in2;
}
- if (npy_isnan(io1)) {
- npy_set_floatstatus_invalid();
- }
*((@type@ *)iop1) = io1;
}
}
@@ -1845,13 +2030,12 @@ NPY_NO_EXPORT void
BINARY_LOOP {
@type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
+ /* Order of operations important for MSVC 2015 */
in1 = (in1 @OP@ in2 || npy_isnan(in1)) ? in1 : in2;
- if (npy_isnan(in1)) {
- npy_set_floatstatus_invalid();
- }
*((@type@ *)op1) = in1;
}
}
+ npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/
@@ -1866,6 +2050,7 @@ NPY_NO_EXPORT void
if (IS_BINARY_REDUCE) {
BINARY_REDUCE_LOOP(@type@) {
const @type@ in2 = *(@type@ *)ip2;
+ /* Order of operations important for MSVC 2015 */
io1 = (io1 @OP@ in2 || npy_isnan(in2)) ? io1 : in2;
}
*((@type@ *)iop1) = io1;
@@ -1874,6 +2059,7 @@ NPY_NO_EXPORT void
BINARY_LOOP {
const @type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
+ /* Order of operations important for MSVC 2015 */
*((@type@ *)op1) = (in1 @OP@ in2 || npy_isnan(in2)) ? in1 : in2;
}
}
@@ -2195,14 +2381,11 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED
{
/* */
BINARY_LOOP {
- npy_half in1 = *(npy_half *)ip1;
+ const npy_half in1 = *(npy_half *)ip1;
const npy_half in2 = *(npy_half *)ip2;
- in1 = (@OP@(in1, in2) || npy_half_isnan(in1)) ? in1 : in2;
- if (npy_half_isnan(in1)) {
- npy_set_floatstatus_invalid();
- }
- *((npy_half *)op1) = in1;
+ *((npy_half *)op1) = (@OP@(in1, in2) || npy_half_isnan(in1)) ? in1 : in2;
}
+ /* npy_half_isnan will never set floatstatus_invalid, so do not clear */
}
/**end repeat**/
@@ -2219,7 +2402,7 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED
const npy_half in2 = *(npy_half *)ip2;
*((npy_half *)op1) = (@OP@(in1, in2) || npy_half_isnan(in2)) ? in1 : in2;
}
- npy_clear_floatstatus_barrier((char*)dimensions);
+ /* npy_half_isnan will never set floatstatus_invalid, so do not clear */
}
/**end repeat**/
@@ -2289,13 +2472,10 @@ HALF_conjugate(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU
}
}
-NPY_NO_EXPORT void
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
HALF_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
- UNARY_LOOP {
- const npy_half in1 = *(npy_half *)ip1;
- *((npy_half *)op1) = in1&0x7fffu;
- }
+ UNARY_LOOP_FAST(npy_half, npy_half, *out = in&0x7fffu);
}
NPY_NO_EXPORT void
@@ -2761,16 +2941,14 @@ NPY_NO_EXPORT void
@ftype@ in1i = ((@ftype@ *)ip1)[1];
const @ftype@ in2r = ((@ftype@ *)ip2)[0];
const @ftype@ in2i = ((@ftype@ *)ip2)[1];
- if ( !(@OP@(in1r, in1i, in2r, in2i) || npy_isnan(in1r) || npy_isnan(in1i))) {
+ if ( !(npy_isnan(in1r) || npy_isnan(in1i) || @OP@(in1r, in1i, in2r, in2i))) {
in1r = in2r;
in1i = in2i;
}
- if (npy_isnan(in1r) || npy_isnan(in1i)) {
- npy_set_floatstatus_invalid();
- }
((@ftype@ *)op1)[0] = in1r;
((@ftype@ *)op1)[1] = in1i;
}
+ npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/
@@ -2786,7 +2964,7 @@ NPY_NO_EXPORT void
const @ftype@ in1i = ((@ftype@ *)ip1)[1];
const @ftype@ in2r = ((@ftype@ *)ip2)[0];
const @ftype@ in2i = ((@ftype@ *)ip2)[1];
- if (@OP@(in1r, in1i, in2r, in2i) || npy_isnan(in2r) || npy_isnan(in2i)) {
+ if (npy_isnan(in2r) || npy_isnan(in2i) || @OP@(in1r, in1i, in2r, in2i)) {
((@ftype@ *)op1)[0] = in1r;
((@ftype@ *)op1)[1] = in1i;
}
@@ -2795,6 +2973,7 @@ NPY_NO_EXPORT void
((@ftype@ *)op1)[1] = in2i;
}
}
+ npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/
diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src
index 5c2b2c22c..e98a1ac3c 100644
--- a/numpy/core/src/umath/loops.h.src
+++ b/numpy/core/src/umath/loops.h.src
@@ -38,6 +38,13 @@ BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED
NPY_NO_EXPORT void
BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data));
+/**begin repeat
+ * #kind = isnan, isinf, isfinite#
+ **/
+NPY_NO_EXPORT void
+BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+/**end repeat**/
+
/*
*****************************************************************************
** INTEGER LOOPS
@@ -146,6 +153,13 @@ NPY_NO_EXPORT void
NPY_NO_EXPORT void
@S@@TYPE@_lcm(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+/**begin repeat2
+ * #kind = isnan, isinf, isfinite#
+ **/
+NPY_NO_EXPORT void
+@S@@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+/**end repeat2**/
+
/**end repeat1**/
/**end repeat**/
@@ -161,6 +175,55 @@ NPY_NO_EXPORT void
*/
NPY_NO_EXPORT void
@TYPE@_sqrt(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
+/**begin repeat1
+ * #isa = avx512f, fma#
+ */
+
+/**begin repeat2
+ * #func = sqrt, absolute, square, reciprocal#
+ */
+NPY_NO_EXPORT void
+@TYPE@_@func@_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
+/**end repeat2**/
+/**end repeat1**/
+/**end repeat**/
+
+/**begin repeat
+ * #func = sin, cos, exp, log#
+ */
+NPY_NO_EXPORT void
+FLOAT_@func@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
+/**begin repeat1
+ * #isa = avx512f, fma#
+ */
+
+NPY_NO_EXPORT void
+FLOAT_@func@_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
+/**end repeat1**/
+/**end repeat**/
+
+/**begin repeat
+ * #func = rint, ceil, floor, trunc#
+ */
+
+/**begin repeat1
+* #TYPE = FLOAT, DOUBLE#
+*/
+
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_@func@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data));
+
+/**begin repeat2
+ * #isa = avx512f, fma#
+ */
+NPY_NO_EXPORT NPY_GCC_OPT_3 void
+@TYPE@_@func@_@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data));
+/**end repeat2**/
+/**end repeat1**/
/**end repeat**/
/**begin repeat
@@ -414,6 +477,9 @@ NPY_NO_EXPORT void
@TYPE@_isnat(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
+@TYPE@_isfinite(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data));
/**begin repeat1
@@ -473,6 +539,15 @@ TIMEDELTA_md_m_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *
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));
+
+NPY_NO_EXPORT void
+TIMEDELTA_mm_qm_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
/* Special case equivalents to above functions */
#define TIMEDELTA_mq_m_true_divide TIMEDELTA_mq_m_divide
@@ -507,6 +582,9 @@ OBJECT@suffix@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *
NPY_NO_EXPORT void
OBJECT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+NPY_NO_EXPORT void
+PyUFunc_OOO_O(char **args, npy_intp *dimensions, npy_intp *steps, void *func);
+
/*
*****************************************************************************
** END LOOPS **
diff --git a/numpy/core/src/umath/matmul.c.src b/numpy/core/src/umath/matmul.c.src
new file mode 100644
index 000000000..b5204eca5
--- /dev/null
+++ b/numpy/core/src/umath/matmul.c.src
@@ -0,0 +1,504 @@
+/* -*- c -*- */
+
+#define _UMATHMODULE
+#define _MULTIARRAYMODULE
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+
+#include "Python.h"
+
+#include "npy_config.h"
+#include "numpy/npy_common.h"
+#include "numpy/arrayobject.h"
+#include "numpy/ufuncobject.h"
+#include "numpy/npy_math.h"
+#include "numpy/halffloat.h"
+#include "lowlevel_strided_loops.h"
+
+#include "npy_pycompat.h"
+
+#include "npy_cblas.h"
+#include "arraytypes.h" /* For TYPE_dot functions */
+
+#include <assert.h>
+
+/*
+ *****************************************************************************
+ ** BASICS **
+ *****************************************************************************
+ */
+
+/*
+ * -1 to be conservative, in case blas internally uses a for loop with an
+ * inclusive upper bound
+ */
+#define BLAS_MAXSIZE (NPY_MAX_INT - 1)
+
+/*
+ * Determine if a 2d matrix can be used by BLAS
+ * 1. Strides must not alias or overlap
+ * 2. The faster (second) axis must be contiguous
+ * 3. The slower (first) axis stride, in unit steps, must be larger than
+ * the faster axis dimension
+ */
+static NPY_INLINE npy_bool
+is_blasable2d(npy_intp byte_stride1, npy_intp byte_stride2,
+ npy_intp d1, npy_intp d2, npy_intp itemsize)
+{
+ npy_intp unit_stride1 = byte_stride1 / itemsize;
+ if (byte_stride2 != itemsize) {
+ return NPY_FALSE;
+ }
+ if ((byte_stride1 % itemsize ==0) &&
+ (unit_stride1 >= d2) &&
+ (unit_stride1 <= BLAS_MAXSIZE))
+ {
+ return NPY_TRUE;
+ }
+ return NPY_FALSE;
+}
+
+#if defined(HAVE_CBLAS)
+static const npy_cdouble oneD = {1.0, 0.0}, zeroD = {0.0, 0.0};
+static const npy_cfloat oneF = {1.0, 0.0}, zeroF = {0.0, 0.0};
+
+/**begin repeat
+ *
+ * #name = FLOAT, DOUBLE, CFLOAT, CDOUBLE#
+ * #ctype = npy_float, npy_double, npy_cfloat, npy_cdouble#
+ * #typ = npy_float, npy_double, npy_cfloat, npy_cdouble#
+ * #prefix = s, d, c, z#
+ * #step1 = 1.F, 1., &oneF, &oneD#
+ * #step0 = 0.F, 0., &zeroF, &zeroD#
+ */
+NPY_NO_EXPORT void
+@name@_gemv(void *ip1, npy_intp is1_m, npy_intp is1_n,
+ void *ip2, npy_intp is2_n, npy_intp NPY_UNUSED(is2_p),
+ void *op, npy_intp op_m, npy_intp NPY_UNUSED(op_p),
+ npy_intp m, npy_intp n, npy_intp NPY_UNUSED(p))
+{
+ /*
+ * Vector matrix multiplication -- Level 2 BLAS
+ * arguments
+ * ip1: contiguous data, m*n shape
+ * ip2: data in c order, n*1 shape
+ * op: data in c order, m shape
+ */
+ enum CBLAS_ORDER order;
+ int M, N, lda;
+
+ assert(m <= BLAS_MAXSIZE && n <= BLAS_MAXSIZE);
+ assert (is_blasable2d(is2_n, sizeof(@typ@), n, 1, sizeof(@typ@)));
+ M = (int)m;
+ N = (int)n;
+
+ if (is_blasable2d(is1_m, is1_n, m, n, sizeof(@typ@))) {
+ order = CblasColMajor;
+ lda = (int)(is1_m / sizeof(@typ@));
+ }
+ else {
+ /* If not ColMajor, caller should have ensured we are RowMajor */
+ /* will not assert in release mode */
+ order = CblasRowMajor;
+ assert(is_blasable2d(is1_n, is1_m, n, m, sizeof(@typ@)));
+ lda = (int)(is1_n / sizeof(@typ@));
+ }
+ cblas_@prefix@gemv(order, CblasTrans, N, M, @step1@, ip1, lda, ip2,
+ is2_n / sizeof(@typ@), @step0@, op, op_m / sizeof(@typ@));
+}
+
+NPY_NO_EXPORT void
+@name@_matmul_matrixmatrix(void *ip1, npy_intp is1_m, npy_intp is1_n,
+ void *ip2, npy_intp is2_n, npy_intp is2_p,
+ void *op, npy_intp os_m, npy_intp os_p,
+ npy_intp m, npy_intp n, npy_intp p)
+{
+ /*
+ * matrix matrix multiplication -- Level 3 BLAS
+ */
+ enum CBLAS_ORDER order = CblasRowMajor;
+ enum CBLAS_TRANSPOSE trans1, trans2;
+ int M, N, P, lda, ldb, ldc;
+ assert(m <= BLAS_MAXSIZE && n <= BLAS_MAXSIZE && p <= BLAS_MAXSIZE);
+ M = (int)m;
+ N = (int)n;
+ P = (int)p;
+
+ assert(is_blasable2d(os_m, os_p, m, p, sizeof(@typ@)));
+ ldc = (int)(os_m / sizeof(@typ@));
+
+ if (is_blasable2d(is1_m, is1_n, m, n, sizeof(@typ@))) {
+ trans1 = CblasNoTrans;
+ lda = (int)(is1_m / sizeof(@typ@));
+ }
+ else {
+ /* If not ColMajor, caller should have ensured we are RowMajor */
+ /* will not assert in release mode */
+ assert(is_blasable2d(is1_n, is1_m, n, m, sizeof(@typ@)));
+ trans1 = CblasTrans;
+ lda = (int)(is1_n / sizeof(@typ@));
+ }
+
+ if (is_blasable2d(is2_n, is2_p, n, p, sizeof(@typ@))) {
+ trans2 = CblasNoTrans;
+ ldb = (int)(is2_n / sizeof(@typ@));
+ }
+ else {
+ /* If not ColMajor, caller should have ensured we are RowMajor */
+ /* will not assert in release mode */
+ assert(is_blasable2d(is2_p, is2_n, p, n, sizeof(@typ@)));
+ trans2 = CblasTrans;
+ ldb = (int)(is2_p / sizeof(@typ@));
+ }
+ /*
+ * Use syrk if we have a case of a matrix times its transpose.
+ * Otherwise, use gemm for all other cases.
+ */
+ if (
+ (ip1 == ip2) &&
+ (m == p) &&
+ (is1_m == is2_p) &&
+ (is1_n == is2_n) &&
+ (trans1 != trans2)
+ ) {
+ npy_intp i,j;
+ if (trans1 == CblasNoTrans) {
+ cblas_@prefix@syrk(order, CblasUpper, trans1, P, N, @step1@,
+ ip1, lda, @step0@, op, ldc);
+ }
+ else {
+ cblas_@prefix@syrk(order, CblasUpper, trans1, P, N, @step1@,
+ ip1, ldb, @step0@, op, ldc);
+ }
+ /* Copy the triangle */
+ for (i = 0; i < P; i++) {
+ for (j = i + 1; j < P; j++) {
+ ((@typ@*)op)[j * ldc + i] = ((@typ@*)op)[i * ldc + j];
+ }
+ }
+
+ }
+ else {
+ cblas_@prefix@gemm(order, trans1, trans2, M, P, N, @step1@, ip1, lda,
+ ip2, ldb, @step0@, op, ldc);
+ }
+}
+
+/**end repeat**/
+#endif
+
+/*
+ * matmul loops
+ * signature is (m?,n),(n,p?)->(m?,p?)
+ */
+
+/**begin repeat
+ * #TYPE = LONGDOUBLE,
+ * FLOAT, DOUBLE, HALF,
+ * CFLOAT, CDOUBLE, CLONGDOUBLE,
+ * UBYTE, USHORT, UINT, ULONG, ULONGLONG,
+ * BYTE, SHORT, INT, LONG, LONGLONG#
+ * #typ = npy_longdouble,
+ * npy_float,npy_double,npy_half,
+ * npy_cfloat, npy_cdouble, npy_clongdouble,
+ * npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong,
+ * npy_byte, npy_short, npy_int, npy_long, npy_longlong#
+ * #IS_COMPLEX = 0, 0, 0, 0, 1, 1, 1, 0*10#
+ * #IS_HALF = 0, 0, 0, 1, 0*13#
+ */
+
+NPY_NO_EXPORT void
+@TYPE@_matmul_inner_noblas(void *_ip1, npy_intp is1_m, npy_intp is1_n,
+ void *_ip2, npy_intp is2_n, npy_intp is2_p,
+ void *_op, npy_intp os_m, npy_intp os_p,
+ npy_intp dm, npy_intp dn, npy_intp dp)
+
+{
+ npy_intp m, n, p;
+ npy_intp ib1_n, ib2_n, ib2_p, ob_p;
+ char *ip1 = (char *)_ip1, *ip2 = (char *)_ip2, *op = (char *)_op;
+
+ ib1_n = is1_n * dn;
+ ib2_n = is2_n * dn;
+ ib2_p = is2_p * dp;
+ ob_p = os_p * dp;
+
+ for (m = 0; m < dm; m++) {
+ for (p = 0; p < dp; p++) {
+#if @IS_COMPLEX@ == 1
+ (*(@typ@ *)op).real = 0;
+ (*(@typ@ *)op).imag = 0;
+#elif @IS_HALF@
+ float sum = 0;
+#else
+ *(@typ@ *)op = 0;
+#endif
+ for (n = 0; n < dn; n++) {
+ @typ@ val1 = (*(@typ@ *)ip1);
+ @typ@ val2 = (*(@typ@ *)ip2);
+#if @IS_HALF@
+ sum += npy_half_to_float(val1) * npy_half_to_float(val2);
+#elif @IS_COMPLEX@ == 1
+ (*(@typ@ *)op).real += (val1.real * val2.real) -
+ (val1.imag * val2.imag);
+ (*(@typ@ *)op).imag += (val1.real * val2.imag) +
+ (val1.imag * val2.real);
+#else
+ *(@typ@ *)op += val1 * val2;
+#endif
+ ip2 += is2_n;
+ ip1 += is1_n;
+ }
+#if @IS_HALF@
+ *(@typ@ *)op = npy_float_to_half(sum);
+#endif
+ ip1 -= ib1_n;
+ ip2 -= ib2_n;
+ op += os_p;
+ ip2 += is2_p;
+ }
+ op -= ob_p;
+ ip2 -= ib2_p;
+ ip1 += is1_m;
+ op += os_m;
+ }
+}
+
+/**end repeat**/
+NPY_NO_EXPORT void
+BOOL_matmul_inner_noblas(void *_ip1, npy_intp is1_m, npy_intp is1_n,
+ void *_ip2, npy_intp is2_n, npy_intp is2_p,
+ void *_op, npy_intp os_m, npy_intp os_p,
+ npy_intp dm, npy_intp dn, npy_intp dp)
+
+{
+ npy_intp m, n, p;
+ npy_intp ib2_p, ob_p;
+ char *ip1 = (char *)_ip1, *ip2 = (char *)_ip2, *op = (char *)_op;
+
+ ib2_p = is2_p * dp;
+ ob_p = os_p * dp;
+
+ for (m = 0; m < dm; m++) {
+ for (p = 0; p < dp; p++) {
+ char *ip1tmp = ip1;
+ char *ip2tmp = ip2;
+ *(npy_bool *)op = NPY_FALSE;
+ for (n = 0; n < dn; n++) {
+ npy_bool val1 = (*(npy_bool *)ip1tmp);
+ npy_bool val2 = (*(npy_bool *)ip2tmp);
+ if (val1 != 0 && val2 != 0) {
+ *(npy_bool *)op = NPY_TRUE;
+ break;
+ }
+ ip2tmp += is2_n;
+ ip1tmp += is1_n;
+ }
+ op += os_p;
+ ip2 += is2_p;
+ }
+ op -= ob_p;
+ ip2 -= ib2_p;
+ ip1 += is1_m;
+ op += os_m;
+ }
+}
+
+NPY_NO_EXPORT void
+OBJECT_matmul_inner_noblas(void *_ip1, npy_intp is1_m, npy_intp is1_n,
+ void *_ip2, npy_intp is2_n, npy_intp is2_p,
+ void *_op, npy_intp os_m, npy_intp os_p,
+ npy_intp dm, npy_intp dn, npy_intp dp)
+{
+ char *ip1 = (char *)_ip1, *ip2 = (char *)_ip2, *op = (char *)_op;
+
+ npy_intp ib1_n = is1_n * dn;
+ npy_intp ib2_n = is2_n * dn;
+ npy_intp ib2_p = is2_p * dp;
+ npy_intp ob_p = os_p * dp;
+
+ PyObject *product, *sum_of_products = NULL;
+
+ for (npy_intp m = 0; m < dm; m++) {
+ for (npy_intp p = 0; p < dp; p++) {
+ if ( 0 == dn ) {
+ sum_of_products = PyLong_FromLong(0);
+ if (sum_of_products == NULL) {
+ return;
+ }
+ }
+
+ for (npy_intp n = 0; n < dn; n++) {
+ PyObject *obj1 = *(PyObject**)ip1, *obj2 = *(PyObject**)ip2;
+ if (obj1 == NULL) {
+ obj1 = Py_None;
+ }
+ if (obj2 == NULL) {
+ obj2 = Py_None;
+ }
+
+ product = PyNumber_Multiply(obj1, obj2);
+ if (product == NULL) {
+ Py_XDECREF(sum_of_products);
+ return;
+ }
+
+ if (n == 0) {
+ sum_of_products = product;
+ }
+ else {
+ Py_SETREF(sum_of_products, PyNumber_Add(sum_of_products, product));
+ Py_DECREF(product);
+ if (sum_of_products == NULL) {
+ return;
+ }
+ }
+
+ ip2 += is2_n;
+ ip1 += is1_n;
+ }
+
+ *((PyObject **)op) = sum_of_products;
+ ip1 -= ib1_n;
+ ip2 -= ib2_n;
+ op += os_p;
+ ip2 += is2_p;
+ }
+ op -= ob_p;
+ ip2 -= ib2_p;
+ ip1 += is1_m;
+ op += os_m;
+ }
+}
+
+
+/**begin repeat
+ * #TYPE = FLOAT, DOUBLE, LONGDOUBLE, HALF,
+ * CFLOAT, CDOUBLE, CLONGDOUBLE,
+ * UBYTE, USHORT, UINT, ULONG, ULONGLONG,
+ * BYTE, SHORT, INT, LONG, LONGLONG,
+ * BOOL, OBJECT#
+ * #typ = npy_float,npy_double,npy_longdouble, npy_half,
+ * npy_cfloat, npy_cdouble, npy_clongdouble,
+ * npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong,
+ * npy_byte, npy_short, npy_int, npy_long, npy_longlong,
+ * npy_bool,npy_object#
+ * #IS_COMPLEX = 0, 0, 0, 0, 1, 1, 1, 0*12#
+ * #USEBLAS = 1, 1, 0, 0, 1, 1, 0*13#
+ */
+
+
+NPY_NO_EXPORT void
+@TYPE@_matmul(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ npy_intp dOuter = *dimensions++;
+ npy_intp iOuter;
+ npy_intp s0 = *steps++;
+ npy_intp s1 = *steps++;
+ npy_intp s2 = *steps++;
+ npy_intp dm = dimensions[0];
+ npy_intp dn = dimensions[1];
+ npy_intp dp = dimensions[2];
+ npy_intp is1_m=steps[0], is1_n=steps[1], is2_n=steps[2], is2_p=steps[3],
+ os_m=steps[4], os_p=steps[5];
+#if @USEBLAS@ && defined(HAVE_CBLAS)
+ npy_intp sz = sizeof(@typ@);
+ npy_bool special_case = (dm == 1 || dn == 1 || dp == 1);
+ npy_bool any_zero_dim = (dm == 0 || dn == 0 || dp == 0);
+ npy_bool scalar_out = (dm == 1 && dp == 1);
+ npy_bool scalar_vec = (dn == 1 && (dp == 1 || dm == 1));
+ npy_bool too_big_for_blas = (dm > BLAS_MAXSIZE || dn > BLAS_MAXSIZE ||
+ dp > BLAS_MAXSIZE);
+ npy_bool i1_c_blasable = is_blasable2d(is1_m, is1_n, dm, dn, sz);
+ npy_bool i2_c_blasable = is_blasable2d(is2_n, is2_p, dn, dp, sz);
+ npy_bool i1_f_blasable = is_blasable2d(is1_n, is1_m, dn, dm, sz);
+ npy_bool i2_f_blasable = is_blasable2d(is2_p, is2_n, dp, dn, sz);
+ npy_bool i1blasable = i1_c_blasable || i1_f_blasable;
+ npy_bool i2blasable = i2_c_blasable || i2_f_blasable;
+ npy_bool o_c_blasable = is_blasable2d(os_m, os_p, dm, dp, sz);
+ npy_bool o_f_blasable = is_blasable2d(os_p, os_m, dp, dm, sz);
+ npy_bool vector_matrix = ((dm == 1) && i2blasable &&
+ is_blasable2d(is1_n, sz, dn, 1, sz));
+ npy_bool matrix_vector = ((dp == 1) && i1blasable &&
+ is_blasable2d(is2_n, sz, dn, 1, sz));
+#endif
+
+ for (iOuter = 0; iOuter < dOuter; iOuter++,
+ args[0] += s0, args[1] += s1, args[2] += s2) {
+ void *ip1=args[0], *ip2=args[1], *op=args[2];
+#if @USEBLAS@ && defined(HAVE_CBLAS)
+ /*
+ * TODO: refactor this out to a inner_loop_selector, in
+ * PyUFunc_MatmulLoopSelector. But that call does not have access to
+ * n, m, p and strides.
+ */
+ if (too_big_for_blas || any_zero_dim) {
+ @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n,
+ ip2, is2_n, is2_p,
+ op, os_m, os_p, dm, dn, dp);
+ }
+ else if (special_case) {
+ /* Special case variants that have a 1 in the core dimensions */
+ if (scalar_out) {
+ /* row @ column, 1,1 output */
+ @TYPE@_dot(ip1, is1_n, ip2, is2_n, op, dn, NULL);
+ } else if (scalar_vec){
+ /*
+ * 1,1d @ vector or vector @ 1,1d
+ * could use cblas_Xaxy, but that requires 0ing output
+ * and would not be faster (XXX prove it)
+ */
+ @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n,
+ ip2, is2_n, is2_p,
+ op, os_m, os_p, dm, dn, dp);
+ } else if (vector_matrix) {
+ /* vector @ matrix, switch ip1, ip2, p and m */
+ @TYPE@_gemv(ip2, is2_p, is2_n, ip1, is1_n, is1_m,
+ op, os_p, os_m, dp, dn, dm);
+ } else if (matrix_vector) {
+ /* matrix @ vector */
+ @TYPE@_gemv(ip1, is1_m, is1_n, ip2, is2_n, is2_p,
+
+ op, os_m, os_p, dm, dn, dp);
+ } else {
+ /* column @ row, 2d output, no blas needed or non-blas-able input */
+ @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n,
+ ip2, is2_n, is2_p,
+ op, os_m, os_p, dm, dn, dp);
+ }
+ } else {
+ /* matrix @ matrix */
+ if (i1blasable && i2blasable && o_c_blasable) {
+ @TYPE@_matmul_matrixmatrix(ip1, is1_m, is1_n,
+ ip2, is2_n, is2_p,
+ op, os_m, os_p,
+ dm, dn, dp);
+ } else if (i1blasable && i2blasable && o_f_blasable) {
+ /*
+ * Use transpose equivalence:
+ * matmul(a, b, o) == matmul(b.T, a.T, o.T)
+ */
+ @TYPE@_matmul_matrixmatrix(ip2, is2_p, is2_n,
+ ip1, is1_n, is1_m,
+ op, os_p, os_m,
+ dp, dn, dm);
+ } else {
+ /*
+ * If parameters are castable to int and we copy the
+ * non-blasable (or non-ccontiguous output)
+ * we could still use BLAS, see gh-12365.
+ */
+ @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n,
+ ip2, is2_n, is2_p,
+ op, os_m, os_p, dm, dn, dp);
+ }
+ }
+#else
+ @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n,
+ ip2, is2_n, is2_p,
+ op, os_m, os_p, dm, dn, dp);
+
+#endif
+ }
+}
+
+/**end repeat**/
diff --git a/numpy/core/src/umath/matmul.h.src b/numpy/core/src/umath/matmul.h.src
new file mode 100644
index 000000000..a664b1b4e
--- /dev/null
+++ b/numpy/core/src/umath/matmul.h.src
@@ -0,0 +1,12 @@
+/**begin repeat
+ * #TYPE = FLOAT, DOUBLE, LONGDOUBLE, HALF,
+ * CFLOAT, CDOUBLE, CLONGDOUBLE,
+ * UBYTE, USHORT, UINT, ULONG, ULONGLONG,
+ * BYTE, SHORT, INT, LONG, LONGLONG,
+ * BOOL, OBJECT#
+ **/
+NPY_NO_EXPORT void
+@TYPE@_matmul(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+/**end repeat**/
+
+
diff --git a/numpy/core/src/umath/override.c b/numpy/core/src/umath/override.c
index 4a381ba12..8d67f96ac 100644
--- a/numpy/core/src/umath/override.c
+++ b/numpy/core/src/umath/override.c
@@ -5,8 +5,98 @@
#include "numpy/ufuncobject.h"
#include "npy_import.h"
-#include "ufunc_override.h"
#include "override.h"
+#include "ufunc_override.h"
+
+/*
+ * For each positional argument and each argument in a possible "out"
+ * keyword, look for overrides of the standard ufunc behaviour, i.e.,
+ * non-default __array_ufunc__ methods.
+ *
+ * Returns the number of overrides, setting corresponding objects
+ * in PyObject array ``with_override`` and the corresponding
+ * __array_ufunc__ methods in ``methods`` (both using new references).
+ *
+ * Only the first override for a given class is returned.
+ *
+ * Returns -1 on failure.
+ */
+static int
+get_array_ufunc_overrides(PyObject *args, PyObject *kwds,
+ PyObject **with_override, PyObject **methods)
+{
+ int i;
+ int num_override_args = 0;
+ int narg, nout = 0;
+ PyObject *out_kwd_obj;
+ PyObject **arg_objs, **out_objs;
+
+ narg = PyTuple_Size(args);
+ if (narg < 0) {
+ return -1;
+ }
+ arg_objs = PySequence_Fast_ITEMS(args);
+
+ nout = PyUFuncOverride_GetOutObjects(kwds, &out_kwd_obj, &out_objs);
+ if (nout < 0) {
+ return -1;
+ }
+
+ for (i = 0; i < narg + nout; ++i) {
+ PyObject *obj;
+ int j;
+ int new_class = 1;
+
+ if (i < narg) {
+ obj = arg_objs[i];
+ }
+ else {
+ obj = out_objs[i - narg];
+ }
+ /*
+ * Have we seen this class before? If so, ignore.
+ */
+ for (j = 0; j < num_override_args; j++) {
+ new_class = (Py_TYPE(obj) != Py_TYPE(with_override[j]));
+ if (!new_class) {
+ break;
+ }
+ }
+ if (new_class) {
+ /*
+ * Now see if the object provides an __array_ufunc__. However, we should
+ * ignore the base ndarray.__ufunc__, so we skip any ndarray as well as
+ * any ndarray subclass instances that did not override __array_ufunc__.
+ */
+ PyObject *method = PyUFuncOverride_GetNonDefaultArrayUfunc(obj);
+ if (method == NULL) {
+ continue;
+ }
+ if (method == Py_None) {
+ PyErr_Format(PyExc_TypeError,
+ "operand '%.200s' does not support ufuncs "
+ "(__array_ufunc__=None)",
+ obj->ob_type->tp_name);
+ Py_DECREF(method);
+ goto fail;
+ }
+ Py_INCREF(obj);
+ with_override[num_override_args] = obj;
+ methods[num_override_args] = method;
+ ++num_override_args;
+ }
+ }
+ Py_DECREF(out_kwd_obj);
+ return num_override_args;
+
+fail:
+ for (i = 0; i < num_override_args; i++) {
+ Py_DECREF(with_override[i]);
+ Py_DECREF(methods[i]);
+ }
+ Py_DECREF(out_kwd_obj);
+ return -1;
+}
/*
* The following functions normalize ufunc arguments. The work done is similar
@@ -136,14 +226,14 @@ normalize_reduce_args(PyUFuncObject *ufunc, PyObject *args,
PyObject *obj;
static PyObject *NoValue = NULL;
static char *kwlist[] = {"array", "axis", "dtype", "out", "keepdims",
- "initial"};
+ "initial", "where"};
npy_cache_import("numpy", "_NoValue", &NoValue);
if (NoValue == NULL) return -1;
- if (nargs < 1 || nargs > 6) {
+ if (nargs < 1 || nargs > 7) {
PyErr_Format(PyExc_TypeError,
- "ufunc.reduce() takes from 1 to 6 positional "
+ "ufunc.reduce() takes from 1 to 7 positional "
"arguments but %"NPY_INTP_FMT" were given", nargs);
return -1;
}
@@ -359,7 +449,7 @@ PyUFunc_CheckOverride(PyUFuncObject *ufunc, char *method,
/*
* Check inputs for overrides
*/
- num_override_args = PyUFunc_WithOverride(
+ num_override_args = get_array_ufunc_overrides(
args, kwds, with_override, array_ufunc_methods);
if (num_override_args == -1) {
goto fail;
diff --git a/numpy/core/src/umath/reduction.c b/numpy/core/src/umath/reduction.c
index 6d04ce372..4ce8d8ab7 100644
--- a/numpy/core/src/umath/reduction.c
+++ b/numpy/core/src/umath/reduction.c
@@ -36,7 +36,7 @@
* If 'dtype' isn't NULL, this function steals its reference.
*/
static PyArrayObject *
-allocate_reduce_result(PyArrayObject *arr, npy_bool *axis_flags,
+allocate_reduce_result(PyArrayObject *arr, const npy_bool *axis_flags,
PyArray_Descr *dtype, int subok)
{
npy_intp strides[NPY_MAXDIMS], stride;
@@ -54,7 +54,9 @@ allocate_reduce_result(PyArrayObject *arr, npy_bool *axis_flags,
/* Build the new strides and shape */
stride = dtype->elsize;
- memcpy(shape, arr_shape, ndim * sizeof(shape[0]));
+ if (ndim) {
+ memcpy(shape, arr_shape, ndim * sizeof(shape[0]));
+ }
for (idim = ndim-1; idim >= 0; --idim) {
npy_intp i_perm = strideperm[idim].perm;
if (axis_flags[i_perm]) {
@@ -82,7 +84,7 @@ allocate_reduce_result(PyArrayObject *arr, npy_bool *axis_flags,
* The return value is a view into 'out'.
*/
static PyArrayObject *
-conform_reduce_result(int ndim, npy_bool *axis_flags,
+conform_reduce_result(int ndim, const npy_bool *axis_flags,
PyArrayObject *out, int keepdims, const char *funcname,
int need_copy)
{
@@ -186,7 +188,6 @@ conform_reduce_result(int ndim, npy_bool *axis_flags,
return NULL;
}
- Py_INCREF(ret);
if (PyArray_SetWritebackIfCopyBase(ret_copy, (PyArrayObject *)ret) < 0) {
Py_DECREF(ret);
Py_DECREF(ret_copy);
@@ -250,7 +251,7 @@ PyArray_CreateReduceResult(PyArrayObject *operand, PyArrayObject *out,
* Count the number of dimensions selected in 'axis_flags'
*/
static int
-count_axes(int ndim, npy_bool *axis_flags)
+count_axes(int ndim, const npy_bool *axis_flags)
{
int idim;
int naxes = 0;
@@ -298,7 +299,7 @@ count_axes(int ndim, npy_bool *axis_flags)
NPY_NO_EXPORT PyArrayObject *
PyArray_InitializeReduceResult(
PyArrayObject *result, PyArrayObject *operand,
- npy_bool *axis_flags,
+ const npy_bool *axis_flags,
npy_intp *out_skip_first_count, const char *funcname)
{
npy_intp *strides, *shape, shape_orig[NPY_MAXDIMS];
@@ -326,7 +327,9 @@ PyArray_InitializeReduceResult(
*/
shape = PyArray_SHAPE(op_view);
nreduce_axes = 0;
- memcpy(shape_orig, shape, ndim * sizeof(npy_intp));
+ if (ndim) {
+ memcpy(shape_orig, shape, ndim * sizeof(npy_intp));
+ }
for (idim = 0; idim < ndim; ++idim) {
if (axis_flags[idim]) {
if (shape[idim] == 0) {
@@ -444,9 +447,9 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out,
/* Iterator parameters */
NpyIter *iter = NULL;
- PyArrayObject *op[2];
- PyArray_Descr *op_dtypes[2];
- npy_uint32 flags, op_flags[2];
+ PyArrayObject *op[3];
+ PyArray_Descr *op_dtypes[3];
+ npy_uint32 flags, op_flags[3];
/* More than one axis means multiple orders are possible */
if (!reorderable && count_axes(PyArray_NDIM(operand), axis_flags) > 1) {
@@ -456,13 +459,12 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out,
funcname);
return NULL;
}
-
-
- /* Validate that the parameters for future expansion are NULL */
- if (wheremask != NULL) {
- PyErr_SetString(PyExc_RuntimeError,
- "Reduce operations in NumPy do not yet support "
- "a where mask");
+ /* Can only use where with an initial ( from identity or argument) */
+ if (wheremask != NULL && identity == Py_None) {
+ PyErr_Format(PyExc_ValueError,
+ "reduction operation '%s' does not have an identity, "
+ "so to use a where mask one has to specify 'initial'",
+ funcname);
return NULL;
}
@@ -524,8 +526,18 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out,
NPY_ITER_NO_SUBTYPE;
op_flags[1] = NPY_ITER_READONLY |
NPY_ITER_ALIGNED;
+ if (wheremask != NULL) {
+ op[2] = wheremask;
+ /* wheremask is guaranteed to be NPY_BOOL, so borrow its reference */
+ op_dtypes[2] = PyArray_DESCR(wheremask);
+ assert(op_dtypes[2]->type_num == NPY_BOOL);
+ if (op_dtypes[2] == NULL) {
+ goto fail;
+ }
+ op_flags[2] = NPY_ITER_READONLY;
+ }
- iter = NpyIter_AdvancedNew(2, op, flags,
+ iter = NpyIter_AdvancedNew(wheremask == NULL ? 2 : 3, op, flags,
NPY_KEEPORDER, casting,
op_flags,
op_dtypes,
@@ -568,7 +580,7 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out,
goto fail;
}
}
-
+
/* Check whether any errors occurred during the loop */
if (PyErr_Occurred() ||
_check_ufunc_fperr(errormask, NULL, "reduce") < 0) {
diff --git a/numpy/core/src/umath/scalarmath.c.src b/numpy/core/src/umath/scalarmath.c.src
index e98d9f865..d5d8d659b 100644
--- a/numpy/core/src/umath/scalarmath.c.src
+++ b/numpy/core/src/umath/scalarmath.c.src
@@ -246,25 +246,26 @@ static void
/**end repeat**/
-
-/* QUESTION: Should we check for overflow / underflow in (l,r)shift? */
-
/**begin repeat
* #name = byte, ubyte, short, ushort, int, uint,
* long, ulong, longlong, ulonglong#
* #type = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
* npy_long, npy_ulong, npy_longlong, npy_ulonglong#
+ * #suffix = hh,uhh,h,uh,,u,l,ul,ll,ull#
*/
/**begin repeat1
- * #oper = and, xor, or, lshift, rshift#
- * #op = &, ^, |, <<, >>#
+ * #oper = and, xor, or#
+ * #op = &, ^, |#
*/
#define @name@_ctype_@oper@(arg1, arg2, out) *(out) = (arg1) @op@ (arg2)
/**end repeat1**/
+#define @name@_ctype_lshift(arg1, arg2, out) *(out) = npy_lshift@suffix@(arg1, arg2)
+#define @name@_ctype_rshift(arg1, arg2, out) *(out) = npy_rshift@suffix@(arg1, arg2)
+
/**end repeat**/
/**begin repeat
@@ -405,21 +406,22 @@ half_ctype_divmod(npy_half a, npy_half b, npy_half *out1, npy_half *out2) {
/**begin repeat
* #name = float, double, longdouble#
* #type = npy_float, npy_double, npy_longdouble#
+ * #c = f,,l#
*/
-static npy_@name@ (*_basic_@name@_pow)(@type@ a, @type@ b);
static void
@name@_ctype_power(@type@ a, @type@ b, @type@ *out)
{
- *out = _basic_@name@_pow(a, b);
+ *out = npy_pow@c@(a, b);
}
+
/**end repeat**/
static void
half_ctype_power(npy_half a, npy_half b, npy_half *out)
{
const npy_float af = npy_half_to_float(a);
const npy_float bf = npy_half_to_float(b);
- const npy_float outf = _basic_float_pow(af,bf);
+ const npy_float outf = npy_powf(af,bf);
*out = npy_float_to_half(outf);
}
@@ -476,14 +478,10 @@ static void
}
/**end repeat**/
-/*
- * Get the nc_powf, nc_pow, and nc_powl functions from
- * the data area of the power ufunc in umathmodule.
- */
-
/**begin repeat
* #name = cfloat, cdouble, clongdouble#
* #type = npy_cfloat, npy_cdouble, npy_clongdouble#
+ * #c = f,,l#
*/
static void
@name@_ctype_positive(@type@ a, @type@ *out)
@@ -492,12 +490,10 @@ static void
out->imag = a.imag;
}
-static void (*_basic_@name@_pow)(@type@ *, @type@ *, @type@ *);
-
static void
@name@_ctype_power(@type@ a, @type@ b, @type@ *out)
{
- _basic_@name@_pow(&a, &b, out);
+ *out = npy_cpow@c@(a, b);
}
/**end repeat**/
@@ -570,7 +566,7 @@ static void
* 1) Convert the types to the common type if both are scalars (0 return)
* 2) If both are not scalars use ufunc machinery (-2 return)
* 3) If both are scalars but cannot be cast to the right type
- * return NotImplmented (-1 return)
+ * return NotImplemented (-1 return)
*
* 4) Perform the function on the C-type.
* 5) If an error condition occurred, check to see
@@ -1429,24 +1425,53 @@ static PyObject *
/**begin repeat
*
+ * #name = byte, ubyte, short, ushort, int, uint,
+ * long, ulong, longlong, ulonglong,
+ * half, float, double, longdouble,
+ * cfloat, cdouble, clongdouble#
+ * #Name = Byte, UByte, Short, UShort, Int, UInt,
+ * Long, ULong, LongLong, ULongLong,
+ * Half, Float, Double, LongDouble,
+ * CFloat, CDouble, CLongDouble#
+ * #cmplx = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1#
+ * #to_ctype = , , , , , , , , , , npy_half_to_double, , , , , , #
+ * #func = PyFloat_FromDouble*17#
+ */
+static NPY_INLINE PyObject *
+@name@_float(PyObject *obj)
+{
+#if @cmplx@
+ if (emit_complexwarning() < 0) {
+ return NULL;
+ }
+ return @func@(@to_ctype@(PyArrayScalar_VAL(obj, @Name@).real));
+#else
+ return @func@(@to_ctype@(PyArrayScalar_VAL(obj, @Name@)));
+#endif
+}
+/**end repeat**/
+
+
+#if !defined(NPY_PY3K)
+
+/**begin repeat
+ *
* #name = (byte, ubyte, short, ushort, int, uint,
* long, ulong, longlong, ulonglong,
* half, float, double, longdouble,
- * cfloat, cdouble, clongdouble)*2#
+ * cfloat, cdouble, clongdouble)#
* #Name = (Byte, UByte, Short, UShort, Int, UInt,
* Long, ULong, LongLong, ULongLong,
* Half, Float, Double, LongDouble,
- * CFloat, CDouble, CLongDouble)*2#
- * #cmplx = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1)*2#
- * #to_ctype = (, , , , , , , , , , npy_half_to_double, , , , , , )*2#
- * #which = long*17, float*17#
+ * CFloat, CDouble, CLongDouble)#
+ * #cmplx = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1)#
+ * #to_ctype = (, , , , , , , , , , npy_half_to_double, , , , , , )#
* #func = (PyLong_FromLongLong, PyLong_FromUnsignedLongLong)*5,
* PyLong_FromDouble*3, npy_longdouble_to_PyLong,
- * PyLong_FromDouble*2, npy_longdouble_to_PyLong,
- * PyFloat_FromDouble*17#
+ * PyLong_FromDouble*2, npy_longdouble_to_PyLong#
*/
static NPY_INLINE PyObject *
-@name@_@which@(PyObject *obj)
+@name@_long(PyObject *obj)
{
#if @cmplx@
if (emit_complexwarning() < 0) {
@@ -1459,8 +1484,6 @@ static NPY_INLINE PyObject *
}
/**end repeat**/
-#if !defined(NPY_PY3K)
-
/**begin repeat
*
* #name = (byte, ubyte, short, ushort, int, uint,
@@ -1564,7 +1587,6 @@ static PyObject*
}
/**end repeat**/
-
/**begin repeat
* #name = byte, ubyte, short, ushort, int, uint,
* long, ulong, longlong, ulonglong,
@@ -1575,8 +1597,7 @@ static PyNumberMethods @name@_as_number = {
(binaryfunc)@name@_add, /*nb_add*/
(binaryfunc)@name@_subtract, /*nb_subtract*/
(binaryfunc)@name@_multiply, /*nb_multiply*/
-#if defined(NPY_PY3K)
-#else
+#if !defined(NPY_PY3K)
(binaryfunc)@name@_divide, /*nb_divide*/
#endif
(binaryfunc)@name@_remainder, /*nb_remainder*/
@@ -1596,8 +1617,7 @@ static PyNumberMethods @name@_as_number = {
(binaryfunc)@name@_and, /*nb_and*/
(binaryfunc)@name@_xor, /*nb_xor*/
(binaryfunc)@name@_or, /*nb_or*/
-#if defined(NPY_PY3K)
-#else
+#if !defined(NPY_PY3K)
0, /*nb_coerce*/
#endif
(unaryfunc)@name@_int, /*nb_int*/
@@ -1607,16 +1627,14 @@ static PyNumberMethods @name@_as_number = {
(unaryfunc)@name@_long, /*nb_long*/
#endif
(unaryfunc)@name@_float, /*nb_float*/
-#if defined(NPY_PY3K)
-#else
+#if !defined(NPY_PY3K)
(unaryfunc)@name@_oct, /*nb_oct*/
(unaryfunc)@name@_hex, /*nb_hex*/
#endif
0, /*inplace_add*/
0, /*inplace_subtract*/
0, /*inplace_multiply*/
-#if defined(NPY_PY3K)
-#else
+#if !defined(NPY_PY3K)
0, /*inplace_divide*/
#endif
0, /*inplace_remainder*/
@@ -1631,6 +1649,10 @@ static PyNumberMethods @name@_as_number = {
0, /*nb_inplace_floor_divide*/
0, /*nb_inplace_true_divide*/
(unaryfunc)NULL, /*nb_index*/
+#if PY_VERSION_HEX >= 0x03050000
+ 0, /*nb_matrix_multiply*/
+ 0, /*nb_inplace_matrix_multiply*/
+#endif
};
/**end repeat**/
@@ -1653,52 +1675,9 @@ add_scalarmath(void)
/**end repeat**/
}
-static int
-get_functions(PyObject * mm)
-{
- PyObject *obj;
- void **funcdata;
- char *signatures;
- int i, j;
- int ret = -1;
-
- /* Get the nc_pow functions */
- /* Get the pow functions */
- obj = PyObject_GetAttrString(mm, "power");
- if (obj == NULL) {
- goto fail;
- }
- funcdata = ((PyUFuncObject *)obj)->data;
- signatures = ((PyUFuncObject *)obj)->types;
-
- i = 0;
- j = 0;
- while (signatures[i] != NPY_FLOAT) {
- i += 3;
- j++;
- }
- _basic_float_pow = funcdata[j];
- _basic_double_pow = funcdata[j + 1];
- _basic_longdouble_pow = funcdata[j + 2];
- _basic_cfloat_pow = funcdata[j + 3];
- _basic_cdouble_pow = funcdata[j + 4];
- _basic_clongdouble_pow = funcdata[j + 5];
- Py_DECREF(obj);
-
- return ret = 0;
-
- fail:
- Py_DECREF(mm);
- return ret;
-}
-
NPY_NO_EXPORT int initscalarmath(PyObject * m)
{
- if (get_functions(m) < 0) {
- return -1;
- }
-
add_scalarmath();
return 0;
diff --git a/numpy/core/src/umath/simd.inc.src b/numpy/core/src/umath/simd.inc.src
index 5c0568c12..74f52cc9d 100644
--- a/numpy/core/src/umath/simd.inc.src
+++ b/numpy/core/src/umath/simd.inc.src
@@ -17,8 +17,6 @@
#include "lowlevel_strided_loops.h"
#include "numpy/npy_common.h"
-/* for NO_FLOATING_POINT_SUPPORT */
-#include "numpy/ufuncobject.h"
#include "numpy/npy_math.h"
#ifdef NPY_HAVE_SSE2_INTRINSICS
#include <emmintrin.h>
@@ -34,13 +32,14 @@
#include <float.h>
#include <string.h> /* for memcpy */
+#define VECTOR_SIZE_BYTES 16
+
static NPY_INLINE npy_uintp
abs_ptrdiff(char *a, char *b)
{
return (a > b) ? (a - b) : (b - a);
}
-
/*
* stride is equal to element size and input and destination are equal or
* don't overlap within one register. The check of the steps against
@@ -52,6 +51,15 @@ abs_ptrdiff(char *a, char *b)
((abs_ptrdiff(args[1], args[0]) >= (vsize)) || \
((abs_ptrdiff(args[1], args[0]) == 0))))
+/*
+ * output should be contiguous, can handle strided input data
+ */
+#define IS_OUTPUT_BLOCKABLE_UNARY(esize, vsize) \
+ (steps[1] == (esize) && \
+ (npy_is_aligned(args[0], esize) && npy_is_aligned(args[1], esize)) && \
+ ((abs_ptrdiff(args[1], args[0]) >= (vsize)) || \
+ ((abs_ptrdiff(args[1], args[0]) == 0))))
+
#define IS_BLOCKABLE_REDUCE(esize, vsize) \
(steps[1] == (esize) && abs_ptrdiff(args[1], args[0]) >= (vsize) && \
npy_is_aligned(args[1], (esize)) && \
@@ -122,6 +130,93 @@ abs_ptrdiff(char *a, char *b)
*/
/**begin repeat
+ * #ISA = FMA, AVX512F#
+ * #isa = fma, avx512f#
+ * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS#
+ * #REGISTER_SIZE = 32, 64#
+ */
+
+/* prototypes */
+
+/**begin repeat1
+ * #type = npy_float, npy_double#
+ * #TYPE = FLOAT, DOUBLE#
+ */
+
+/**begin repeat2
+ * #func = sqrt, absolute, square, reciprocal, rint, floor, ceil, trunc#
+ */
+
+#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS
+static NPY_INLINE NPY_GCC_TARGET_@ISA@ void
+@ISA@_@func@_@TYPE@(@type@ *, @type@ *, const npy_intp n, const npy_intp stride);
+#endif
+
+static NPY_INLINE int
+run_unary_@isa@_@func@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps)
+{
+#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS
+ if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(@type@), @REGISTER_SIZE@)) {
+ @ISA@_@func@_@TYPE@((@type@*)args[1], (@type@*)args[0], dimensions[0], steps[0]);
+ return 1;
+ }
+ else
+ return 0;
+#endif
+ return 0;
+}
+
+/**end repeat2**/
+/**end repeat1**/
+
+/**begin repeat1
+ * #func = exp, log#
+ */
+
+#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS
+static NPY_INLINE void
+@ISA@_@func@_FLOAT(npy_float *, npy_float *, const npy_intp n, const npy_intp stride);
+#endif
+
+static NPY_INLINE int
+run_unary_@isa@_@func@_FLOAT(char **args, npy_intp *dimensions, npy_intp *steps)
+{
+#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS
+ if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(npy_float), @REGISTER_SIZE@)) {
+ @ISA@_@func@_FLOAT((npy_float*)args[1], (npy_float*)args[0], dimensions[0], steps[0]);
+ return 1;
+ }
+ else
+ return 0;
+#endif
+ return 0;
+}
+
+/**end repeat1**/
+
+#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS
+static NPY_INLINE void
+@ISA@_sincos_FLOAT(npy_float *, npy_float *, const npy_intp n, const npy_intp steps, NPY_TRIG_OP);
+#endif
+
+static NPY_INLINE int
+run_unary_@isa@_sincos_FLOAT(char **args, npy_intp *dimensions, npy_intp *steps, NPY_TRIG_OP my_trig_op)
+{
+#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS
+ if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(npy_float), @REGISTER_SIZE@)) {
+ @ISA@_sincos_FLOAT((npy_float*)args[1], (npy_float*)args[0], dimensions[0], steps[0], my_trig_op);
+ return 1;
+ }
+ else
+ return 0;
+#endif
+ return 0;
+}
+
+/**end repeat**/
+
+
+/**begin repeat
* Float types
* #type = npy_float, npy_double, npy_longdouble#
* #TYPE = FLOAT, DOUBLE, LONGDOUBLE#
@@ -132,7 +227,6 @@ abs_ptrdiff(char *a, char *b)
* #func = sqrt, absolute, negative, minimum, maximum#
* #check = IS_BLOCKABLE_UNARY*3, IS_BLOCKABLE_REDUCE*2 #
* #name = unary*3, unary_reduce*2#
- * #minmax = 0*3, 1*2#
*/
#if @vector@ && defined NPY_HAVE_SSE2_INTRINSICS
@@ -146,17 +240,13 @@ sse2_@func@_@TYPE@(@type@ *, @type@ *, const npy_intp n);
static NPY_INLINE int
run_@name@_simd_@func@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps)
{
-#if @minmax@ && (defined NO_FLOATING_POINT_SUPPORT)
- return 0;
-#else
#if @vector@ && defined NPY_HAVE_SSE2_INTRINSICS
- if (@check@(sizeof(@type@), 16)) {
+ if (@check@(sizeof(@type@), VECTOR_SIZE_BYTES)) {
sse2_@func@_@TYPE@((@type@*)args[1], (@type@*)args[0], dimensions[0]);
return 1;
}
#endif
return 0;
-#endif
}
/**end repeat1**/
@@ -189,17 +279,24 @@ run_binary_simd_@kind@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps
@type@ * ip2 = (@type@ *)args[1];
@type@ * op = (@type@ *)args[2];
npy_intp n = dimensions[0];
+#if defined __AVX512F__
+ const npy_intp vector_size_bytes = 64;
+#elif defined __AVX2__
+ const npy_intp vector_size_bytes = 32;
+#else
+ const npy_intp vector_size_bytes = 32;
+#endif
/* argument one scalar */
- if (IS_BLOCKABLE_BINARY_SCALAR1(sizeof(@type@), 16)) {
+ if (IS_BLOCKABLE_BINARY_SCALAR1(sizeof(@type@), vector_size_bytes)) {
sse2_binary_scalar1_@kind@_@TYPE@(op, ip1, ip2, n);
return 1;
}
/* argument two scalar */
- else if (IS_BLOCKABLE_BINARY_SCALAR2(sizeof(@type@), 16)) {
+ else if (IS_BLOCKABLE_BINARY_SCALAR2(sizeof(@type@), vector_size_bytes)) {
sse2_binary_scalar2_@kind@_@TYPE@(op, ip1, ip2, n);
return 1;
}
- else if (IS_BLOCKABLE_BINARY(sizeof(@type@), 16)) {
+ else if (IS_BLOCKABLE_BINARY(sizeof(@type@), vector_size_bytes)) {
sse2_binary_@kind@_@TYPE@(op, ip1, ip2, n);
return 1;
}
@@ -239,16 +336,16 @@ run_binary_simd_@kind@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps
npy_bool * op = (npy_bool *)args[2];
npy_intp n = dimensions[0];
/* argument one scalar */
- if (IS_BLOCKABLE_BINARY_SCALAR1_BOOL(sizeof(@type@), 16)) {
+ if (IS_BLOCKABLE_BINARY_SCALAR1_BOOL(sizeof(@type@), VECTOR_SIZE_BYTES)) {
sse2_binary_scalar1_@kind@_@TYPE@(op, ip1, ip2, n);
return 1;
}
/* argument two scalar */
- else if (IS_BLOCKABLE_BINARY_SCALAR2_BOOL(sizeof(@type@), 16)) {
+ else if (IS_BLOCKABLE_BINARY_SCALAR2_BOOL(sizeof(@type@), VECTOR_SIZE_BYTES)) {
sse2_binary_scalar2_@kind@_@TYPE@(op, ip1, ip2, n);
return 1;
}
- else if (IS_BLOCKABLE_BINARY_BOOL(sizeof(@type@), 16)) {
+ else if (IS_BLOCKABLE_BINARY_BOOL(sizeof(@type@), VECTOR_SIZE_BYTES)) {
sse2_binary_@kind@_@TYPE@(op, ip1, ip2, n);
return 1;
}
@@ -309,7 +406,8 @@ static NPY_INLINE int
run_binary_simd_@kind@_BOOL(char **args, npy_intp *dimensions, npy_intp *steps)
{
#if defined NPY_HAVE_SSE2_INTRINSICS
- if (sizeof(npy_bool) == 1 && IS_BLOCKABLE_BINARY(sizeof(npy_bool), 16)) {
+ if (sizeof(npy_bool) == 1 &&
+ IS_BLOCKABLE_BINARY(sizeof(npy_bool), VECTOR_SIZE_BYTES)) {
sse2_binary_@kind@_BOOL((npy_bool*)args[2], (npy_bool*)args[0],
(npy_bool*)args[1], dimensions[0]);
return 1;
@@ -323,7 +421,8 @@ static NPY_INLINE int
run_reduce_simd_@kind@_BOOL(char **args, npy_intp *dimensions, npy_intp *steps)
{
#if defined NPY_HAVE_SSE2_INTRINSICS
- if (sizeof(npy_bool) == 1 && IS_BLOCKABLE_REDUCE(sizeof(npy_bool), 16)) {
+ if (sizeof(npy_bool) == 1 &&
+ IS_BLOCKABLE_REDUCE(sizeof(npy_bool), VECTOR_SIZE_BYTES)) {
sse2_reduce_@kind@_BOOL((npy_bool*)args[0], (npy_bool*)args[1],
dimensions[0]);
return 1;
@@ -347,7 +446,8 @@ static NPY_INLINE int
run_unary_simd_@kind@_BOOL(char **args, npy_intp *dimensions, npy_intp *steps)
{
#if defined NPY_HAVE_SSE2_INTRINSICS
- if (sizeof(npy_bool) == 1 && IS_BLOCKABLE_UNARY(sizeof(npy_bool), 16)) {
+ if (sizeof(npy_bool) == 1 &&
+ IS_BLOCKABLE_UNARY(sizeof(npy_bool), VECTOR_SIZE_BYTES)) {
sse2_@kind@_BOOL((npy_bool*)args[1], (npy_bool*)args[0], dimensions[0]);
return 1;
}
@@ -423,19 +523,20 @@ static void
sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
{
#ifdef __AVX512F__
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 64)
+ const npy_intp vector_size_bytes = 64;
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes)
op[i] = ip1[i] @OP@ ip2[i];
/* lots of specializations, to squeeze out max performance */
- if (npy_is_aligned(&ip1[i], 64) && npy_is_aligned(&ip2[i], 64)) {
+ if (npy_is_aligned(&ip1[i], vector_size_bytes) && npy_is_aligned(&ip2[i], vector_size_bytes)) {
if (ip1 == ip2) {
- LOOP_BLOCKED(@type@, 64) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, a);
@vpre512@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 64) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]);
@vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b);
@@ -443,16 +544,16 @@ sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
}
}
}
- else if (npy_is_aligned(&ip1[i], 64)) {
- LOOP_BLOCKED(@type@, 64) {
+ else if (npy_is_aligned(&ip1[i], vector_size_bytes)) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]);
@vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b);
@vpre512@_store_@vsuf@(&op[i], c);
}
}
- else if (npy_is_aligned(&ip2[i], 64)) {
- LOOP_BLOCKED(@type@, 64) {
+ else if (npy_is_aligned(&ip2[i], vector_size_bytes)) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]);
@vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b);
@@ -461,14 +562,14 @@ sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
}
else {
if (ip1 == ip2) {
- LOOP_BLOCKED(@type@, 64) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, a);
@vpre512@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 64) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]);
@vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b);
@@ -477,19 +578,21 @@ sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
}
}
#elif __AVX2__
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 32)
+ const npy_intp vector_size_bytes = 32;
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes)
op[i] = ip1[i] @OP@ ip2[i];
/* lots of specializations, to squeeze out max performance */
- if (npy_is_aligned(&ip1[i], 32) && npy_is_aligned(&ip2[i], 32)) {
+ if (npy_is_aligned(&ip1[i], vector_size_bytes) &&
+ npy_is_aligned(&ip2[i], vector_size_bytes)) {
if (ip1 == ip2) {
- LOOP_BLOCKED(@type@, 32) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, a);
@vpre256@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 32) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]);
@vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b);
@@ -497,16 +600,16 @@ sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
}
}
}
- else if (npy_is_aligned(&ip1[i], 32)) {
- LOOP_BLOCKED(@type@, 32) {
+ else if (npy_is_aligned(&ip1[i], vector_size_bytes)) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]);
@vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b);
@vpre256@_store_@vsuf@(&op[i], c);
}
}
- else if (npy_is_aligned(&ip2[i], 32)) {
- LOOP_BLOCKED(@type@, 32) {
+ else if (npy_is_aligned(&ip2[i], vector_size_bytes)) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]);
@vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b);
@@ -515,14 +618,14 @@ sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
}
else {
if (ip1 == ip2) {
- LOOP_BLOCKED(@type@, 32) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, a);
@vpre256@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 32) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]);
@vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b);
@@ -531,19 +634,20 @@ sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
}
}
#else
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 16)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES)
op[i] = ip1[i] @OP@ ip2[i];
/* lots of specializations, to squeeze out max performance */
- if (npy_is_aligned(&ip1[i], 16) && npy_is_aligned(&ip2[i], 16)) {
+ if (npy_is_aligned(&ip1[i], VECTOR_SIZE_BYTES) &&
+ npy_is_aligned(&ip2[i], VECTOR_SIZE_BYTES)) {
if (ip1 == ip2) {
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_load_@vsuf@(&ip1[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, a);
@vpre@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_load_@vsuf@(&ip1[i]);
@vtype@ b = @vpre@_load_@vsuf@(&ip2[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, b);
@@ -551,16 +655,16 @@ sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
}
}
}
- else if (npy_is_aligned(&ip1[i], 16)) {
- LOOP_BLOCKED(@type@, 16) {
+ else if (npy_is_aligned(&ip1[i], VECTOR_SIZE_BYTES)) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_load_@vsuf@(&ip1[i]);
@vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, b);
@vpre@_store_@vsuf@(&op[i], c);
}
}
- else if (npy_is_aligned(&ip2[i], 16)) {
- LOOP_BLOCKED(@type@, 16) {
+ else if (npy_is_aligned(&ip2[i], VECTOR_SIZE_BYTES)) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]);
@vtype@ b = @vpre@_load_@vsuf@(&ip2[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, b);
@@ -569,14 +673,14 @@ sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
}
else {
if (ip1 == ip2) {
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, a);
@vpre@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]);
@vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, b);
@@ -595,18 +699,19 @@ static void
sse2_binary_scalar1_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
{
#ifdef __AVX512F__
+ const npy_intp vector_size_bytes = 64;
const @vtype512@ a = @vpre512@_set1_@vsuf@(ip1[0]);
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 64)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes)
op[i] = ip1[0] @OP@ ip2[i];
- if (npy_is_aligned(&ip2[i], 64)) {
- LOOP_BLOCKED(@type@, 64) {
+ if (npy_is_aligned(&ip2[i], vector_size_bytes)) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b);
@vpre512@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 64) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b);
@vpre512@_store_@vsuf@(&op[i], c);
@@ -615,18 +720,19 @@ sse2_binary_scalar1_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_i
#elif __AVX2__
+ const npy_intp vector_size_bytes = 32;
const @vtype256@ a = @vpre256@_set1_@vsuf@(ip1[0]);
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 32)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes)
op[i] = ip1[0] @OP@ ip2[i];
- if (npy_is_aligned(&ip2[i], 32)) {
- LOOP_BLOCKED(@type@, 32) {
+ if (npy_is_aligned(&ip2[i], vector_size_bytes)) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b);
@vpre256@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 32) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b);
@vpre256@_store_@vsuf@(&op[i], c);
@@ -634,17 +740,17 @@ sse2_binary_scalar1_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_i
}
#else
const @vtype@ a = @vpre@_set1_@vsuf@(ip1[0]);
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 16)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES)
op[i] = ip1[0] @OP@ ip2[i];
- if (npy_is_aligned(&ip2[i], 16)) {
- LOOP_BLOCKED(@type@, 16) {
+ if (npy_is_aligned(&ip2[i], VECTOR_SIZE_BYTES)) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ b = @vpre@_load_@vsuf@(&ip2[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, b);
@vpre@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, b);
@vpre@_store_@vsuf@(&op[i], c);
@@ -661,18 +767,19 @@ static void
sse2_binary_scalar2_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n)
{
#ifdef __AVX512F__
+ const npy_intp vector_size_bytes = 64;
const @vtype512@ b = @vpre512@_set1_@vsuf@(ip2[0]);
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 64)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes)
op[i] = ip1[i] @OP@ ip2[0];
- if (npy_is_aligned(&ip1[i], 64)) {
- LOOP_BLOCKED(@type@, 64) {
+ if (npy_is_aligned(&ip1[i], vector_size_bytes)) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b);
@vpre512@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 64) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]);
@vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b);
@vpre512@_store_@vsuf@(&op[i], c);
@@ -680,18 +787,19 @@ sse2_binary_scalar2_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_i
}
#elif __AVX2__
+ const npy_intp vector_size_bytes = 32;
const @vtype256@ b = @vpre256@_set1_@vsuf@(ip2[0]);
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 32)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes)
op[i] = ip1[i] @OP@ ip2[0];
- if (npy_is_aligned(&ip1[i], 32)) {
- LOOP_BLOCKED(@type@, 32) {
+ if (npy_is_aligned(&ip1[i], vector_size_bytes)) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b);
@vpre256@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 32) {
+ LOOP_BLOCKED(@type@, vector_size_bytes) {
@vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]);
@vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b);
@vpre256@_store_@vsuf@(&op[i], c);
@@ -699,17 +807,17 @@ sse2_binary_scalar2_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_i
}
#else
const @vtype@ b = @vpre@_set1_@vsuf@(ip2[0]);
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 16)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES)
op[i] = ip1[i] @OP@ ip2[0];
- if (npy_is_aligned(&ip1[i], 16)) {
- LOOP_BLOCKED(@type@, 16) {
+ if (npy_is_aligned(&ip1[i], VECTOR_SIZE_BYTES)) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_load_@vsuf@(&ip1[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, b);
@vpre@_store_@vsuf@(&op[i], c);
}
}
else {
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]);
@vtype@ c = @vpre@_@VOP@_@vsuf@(a, b);
@vpre@_store_@vsuf@(&op[i], c);
@@ -749,10 +857,10 @@ sse2_compress4_to_byte_@TYPE@(@vtype@ r1, @vtype@ r2, @vtype@ r3, @vtype@ * r4,
static void
sse2_signbit_@TYPE@(npy_bool * op, @type@ * ip1, npy_intp n)
{
- LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) {
+ LOOP_BLOCK_ALIGN_VAR(ip1, @type@, VECTOR_SIZE_BYTES) {
op[i] = npy_signbit(ip1[i]) != 0;
}
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_load_@vsuf@(&ip1[i]);
int r = @vpre@_movemask_@vsuf@(a);
if (sizeof(@type@) == 8) {
@@ -790,14 +898,14 @@ sse2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, npy_intp n)
const @vtype@ fltmax = @vpre@_set1_@vsuf@(FLT_MAX);
#endif
#endif
- LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) {
+ LOOP_BLOCK_ALIGN_VAR(ip1, @type@, VECTOR_SIZE_BYTES) {
op[i] = npy_@kind@(ip1[i]) != 0;
}
- LOOP_BLOCKED(@type@, 64) {
- @vtype@ a = @vpre@_load_@vsuf@(&ip1[i + 0 * 16 / sizeof(@type@)]);
- @vtype@ b = @vpre@_load_@vsuf@(&ip1[i + 1 * 16 / sizeof(@type@)]);
- @vtype@ c = @vpre@_load_@vsuf@(&ip1[i + 2 * 16 / sizeof(@type@)]);
- @vtype@ d = @vpre@_load_@vsuf@(&ip1[i + 3 * 16 / sizeof(@type@)]);
+ LOOP_BLOCKED(@type@, 4 * VECTOR_SIZE_BYTES) {
+ @vtype@ a = @vpre@_load_@vsuf@(&ip1[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ b = @vpre@_load_@vsuf@(&ip1[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ c = @vpre@_load_@vsuf@(&ip1[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ d = @vpre@_load_@vsuf@(&ip1[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
@vtype@ r1, r2, r3, r4;
#if @var@ != 0 /* isinf/isfinite */
/* fabs via masking of sign bit */
@@ -860,18 +968,18 @@ sse2_ordered_cmp_@kind@_@TYPE@(const @type@ a, const @type@ b)
static void
sse2_binary_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n)
{
- LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) {
+ LOOP_BLOCK_ALIGN_VAR(ip1, @type@, VECTOR_SIZE_BYTES) {
op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[i], ip2[i]);
}
- LOOP_BLOCKED(@type@, 64) {
- @vtype@ a1 = @vpre@_load_@vsuf@(&ip1[i + 0 * 16 / sizeof(@type@)]);
- @vtype@ b1 = @vpre@_load_@vsuf@(&ip1[i + 1 * 16 / sizeof(@type@)]);
- @vtype@ c1 = @vpre@_load_@vsuf@(&ip1[i + 2 * 16 / sizeof(@type@)]);
- @vtype@ d1 = @vpre@_load_@vsuf@(&ip1[i + 3 * 16 / sizeof(@type@)]);
- @vtype@ a2 = @vpre@_loadu_@vsuf@(&ip2[i + 0 * 16 / sizeof(@type@)]);
- @vtype@ b2 = @vpre@_loadu_@vsuf@(&ip2[i + 1 * 16 / sizeof(@type@)]);
- @vtype@ c2 = @vpre@_loadu_@vsuf@(&ip2[i + 2 * 16 / sizeof(@type@)]);
- @vtype@ d2 = @vpre@_loadu_@vsuf@(&ip2[i + 3 * 16 / sizeof(@type@)]);
+ LOOP_BLOCKED(@type@, 4 * VECTOR_SIZE_BYTES) {
+ @vtype@ a1 = @vpre@_load_@vsuf@(&ip1[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ b1 = @vpre@_load_@vsuf@(&ip1[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ c1 = @vpre@_load_@vsuf@(&ip1[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ d1 = @vpre@_load_@vsuf@(&ip1[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ a2 = @vpre@_loadu_@vsuf@(&ip2[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ b2 = @vpre@_loadu_@vsuf@(&ip2[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ c2 = @vpre@_loadu_@vsuf@(&ip2[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ d2 = @vpre@_loadu_@vsuf@(&ip2[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
@vtype@ r1 = @vpre@_@VOP@_@vsuf@(a1, a2);
@vtype@ r2 = @vpre@_@VOP@_@vsuf@(b1, b2);
@vtype@ r3 = @vpre@_@VOP@_@vsuf@(c1, c2);
@@ -888,14 +996,14 @@ static void
sse2_binary_scalar1_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n)
{
@vtype@ s = @vpre@_set1_@vsuf@(ip1[0]);
- LOOP_BLOCK_ALIGN_VAR(ip2, @type@, 16) {
+ LOOP_BLOCK_ALIGN_VAR(ip2, @type@, VECTOR_SIZE_BYTES) {
op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[0], ip2[i]);
}
- LOOP_BLOCKED(@type@, 64) {
- @vtype@ a = @vpre@_load_@vsuf@(&ip2[i + 0 * 16 / sizeof(@type@)]);
- @vtype@ b = @vpre@_load_@vsuf@(&ip2[i + 1 * 16 / sizeof(@type@)]);
- @vtype@ c = @vpre@_load_@vsuf@(&ip2[i + 2 * 16 / sizeof(@type@)]);
- @vtype@ d = @vpre@_load_@vsuf@(&ip2[i + 3 * 16 / sizeof(@type@)]);
+ LOOP_BLOCKED(@type@, 4 * VECTOR_SIZE_BYTES) {
+ @vtype@ a = @vpre@_load_@vsuf@(&ip2[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ b = @vpre@_load_@vsuf@(&ip2[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ c = @vpre@_load_@vsuf@(&ip2[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ d = @vpre@_load_@vsuf@(&ip2[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
@vtype@ r1 = @vpre@_@VOP@_@vsuf@(s, a);
@vtype@ r2 = @vpre@_@VOP@_@vsuf@(s, b);
@vtype@ r3 = @vpre@_@VOP@_@vsuf@(s, c);
@@ -912,14 +1020,14 @@ static void
sse2_binary_scalar2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n)
{
@vtype@ s = @vpre@_set1_@vsuf@(ip2[0]);
- LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) {
+ LOOP_BLOCK_ALIGN_VAR(ip1, @type@, VECTOR_SIZE_BYTES) {
op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[i], ip2[0]);
}
- LOOP_BLOCKED(@type@, 64) {
- @vtype@ a = @vpre@_load_@vsuf@(&ip1[i + 0 * 16 / sizeof(@type@)]);
- @vtype@ b = @vpre@_load_@vsuf@(&ip1[i + 1 * 16 / sizeof(@type@)]);
- @vtype@ c = @vpre@_load_@vsuf@(&ip1[i + 2 * 16 / sizeof(@type@)]);
- @vtype@ d = @vpre@_load_@vsuf@(&ip1[i + 3 * 16 / sizeof(@type@)]);
+ LOOP_BLOCKED(@type@, 4 * VECTOR_SIZE_BYTES) {
+ @vtype@ a = @vpre@_load_@vsuf@(&ip1[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ b = @vpre@_load_@vsuf@(&ip1[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ c = @vpre@_load_@vsuf@(&ip1[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
+ @vtype@ d = @vpre@_load_@vsuf@(&ip1[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]);
@vtype@ r1 = @vpre@_@VOP@_@vsuf@(a, s);
@vtype@ r2 = @vpre@_@VOP@_@vsuf@(b, s);
@vtype@ r3 = @vpre@_@VOP@_@vsuf@(c, s);
@@ -935,19 +1043,20 @@ sse2_binary_scalar2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy
static void
sse2_sqrt_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n)
{
- /* align output to 16 bytes */
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) {
+ /* align output to VECTOR_SIZE_BYTES bytes */
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES) {
op[i] = @scalarf@(ip[i]);
}
- assert(n < (16 / sizeof(@type@)) || npy_is_aligned(&op[i], 16));
- if (npy_is_aligned(&ip[i], 16)) {
- LOOP_BLOCKED(@type@, 16) {
+ assert((npy_uintp)n < (VECTOR_SIZE_BYTES / sizeof(@type@)) ||
+ npy_is_aligned(&op[i], VECTOR_SIZE_BYTES));
+ if (npy_is_aligned(&ip[i], VECTOR_SIZE_BYTES)) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ d = @vpre@_load_@vsuf@(&ip[i]);
@vpre@_store_@vsuf@(&op[i], @vpre@_sqrt_@vsuf@(d));
}
}
else {
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ d = @vpre@_loadu_@vsuf@(&ip[i]);
@vpre@_store_@vsuf@(&op[i], @vpre@_sqrt_@vsuf@(d));
}
@@ -986,19 +1095,20 @@ sse2_@kind@_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n)
*/
const @vtype@ mask = @vpre@_set1_@vsuf@(-0.@c@);
- /* align output to 16 bytes */
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) {
+ /* align output to VECTOR_SIZE_BYTES bytes */
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES) {
op[i] = @scalar@_@type@(ip[i]);
}
- assert(n < (16 / sizeof(@type@)) || npy_is_aligned(&op[i], 16));
- if (npy_is_aligned(&ip[i], 16)) {
- LOOP_BLOCKED(@type@, 16) {
+ assert((npy_uintp)n < (VECTOR_SIZE_BYTES / sizeof(@type@)) ||
+ npy_is_aligned(&op[i], VECTOR_SIZE_BYTES));
+ if (npy_is_aligned(&ip[i], VECTOR_SIZE_BYTES)) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_load_@vsuf@(&ip[i]);
@vpre@_store_@vsuf@(&op[i], @vpre@_@VOP@_@vsuf@(mask, a));
}
}
else {
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vpre@_loadu_@vsuf@(&ip[i]);
@vpre@_store_@vsuf@(&op[i], @vpre@_@VOP@_@vsuf@(mask, a));
}
@@ -1019,11 +1129,12 @@ sse2_@kind@_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n)
static void
sse2_@kind@_@TYPE@(@type@ * ip, @type@ * op, const npy_intp n)
{
- const npy_intp stride = 16 / (npy_intp)sizeof(@type@);
- LOOP_BLOCK_ALIGN_VAR(ip, @type@, 16) {
+ const npy_intp stride = VECTOR_SIZE_BYTES / (npy_intp)sizeof(@type@);
+ LOOP_BLOCK_ALIGN_VAR(ip, @type@, VECTOR_SIZE_BYTES) {
+ /* Order of operations important for MSVC 2015 */
*op = (*op @OP@ ip[i] || npy_isnan(*op)) ? *op : ip[i];
}
- assert(n < (stride) || npy_is_aligned(&ip[i], 16));
+ assert((npy_uintp)n < (stride) || npy_is_aligned(&ip[i], VECTOR_SIZE_BYTES));
if (i + 3 * stride <= n) {
/* load the first elements */
@vtype@ c1 = @vpre@_load_@vsuf@((@type@*)&ip[i]);
@@ -1032,7 +1143,7 @@ sse2_@kind@_@TYPE@(@type@ * ip, @type@ * op, const npy_intp n)
/* minps/minpd will set invalid flag if nan is encountered */
npy_clear_floatstatus_barrier((char*)&c1);
- LOOP_BLOCKED(@type@, 32) {
+ LOOP_BLOCKED(@type@, 2 * VECTOR_SIZE_BYTES) {
@vtype@ v1 = @vpre@_load_@vsuf@((@type@*)&ip[i]);
@vtype@ v2 = @vpre@_load_@vsuf@((@type@*)&ip[i + stride]);
c1 = @vpre@_@VOP@_@vsuf@(c1, v1);
@@ -1045,18 +1156,1106 @@ sse2_@kind@_@TYPE@(@type@ * ip, @type@ * op, const npy_intp n)
}
else {
@type@ tmp = sse2_horizontal_@VOP@_@vtype@(c1);
+ /* Order of operations important for MSVC 2015 */
*op = (*op @OP@ tmp || npy_isnan(*op)) ? *op : tmp;
}
}
LOOP_BLOCKED_END {
+ /* Order of operations important for MSVC 2015 */
*op = (*op @OP@ ip[i] || npy_isnan(*op)) ? *op : ip[i];
}
- if (npy_isnan(*op)) {
- npy_set_floatstatus_invalid();
+ npy_clear_floatstatus_barrier((char*)op);
+}
+/**end repeat1**/
+
+/**end repeat**/
+
+/* bunch of helper functions used in ISA_exp/log_FLOAT*/
+
+#if defined HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_get_full_load_mask_ps(void)
+{
+ return _mm256_set1_ps(-1.0);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256i
+fma_get_full_load_mask_pd(void)
+{
+ return _mm256_castpd_si256(_mm256_set1_pd(-1.0));
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_get_partial_load_mask_ps(const npy_int num_elem, const npy_int num_lanes)
+{
+ float maskint[16] = {-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,
+ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
+ float* addr = maskint + num_lanes - num_elem;
+ return _mm256_loadu_ps(addr);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256i
+fma_get_partial_load_mask_pd(const npy_int num_elem, const npy_int num_lanes)
+{
+ npy_int maskint[16] = {-1,-1,-1,-1,-1,-1,-1,-1,1,1,1,1,1,1,1,1};
+ npy_int* addr = maskint + 2*num_lanes - 2*num_elem;
+ return _mm256_loadu_si256((__m256i*) addr);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_masked_gather_ps(__m256 src,
+ npy_float* addr,
+ __m256i vindex,
+ __m256 mask)
+{
+ return _mm256_mask_i32gather_ps(src, addr, vindex, mask, 4);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256d
+fma_masked_gather_pd(__m256d src,
+ npy_double* addr,
+ __m128i vindex,
+ __m256d mask)
+{
+ return _mm256_mask_i32gather_pd(src, addr, vindex, mask, 8);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_masked_load_ps(__m256 mask, npy_float* addr)
+{
+ return _mm256_maskload_ps(addr, _mm256_cvtps_epi32(mask));
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256d
+fma_masked_load_pd(__m256i mask, npy_double* addr)
+{
+ return _mm256_maskload_pd(addr, mask);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_set_masked_lanes_ps(__m256 x, __m256 val, __m256 mask)
+{
+ return _mm256_blendv_ps(x, val, mask);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256d
+fma_set_masked_lanes_pd(__m256d x, __m256d val, __m256d mask)
+{
+ return _mm256_blendv_pd(x, val, mask);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_blend(__m256 x, __m256 y, __m256 ymask)
+{
+ return _mm256_blendv_ps(x, y, ymask);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_invert_mask_ps(__m256 ymask)
+{
+ return _mm256_andnot_ps(ymask, _mm256_set1_ps(-1.0));
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256i
+fma_invert_mask_pd(__m256i ymask)
+{
+ return _mm256_andnot_si256(ymask, _mm256_set1_epi32(0xFFFFFFFF));
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_should_calculate_sine(__m256i k, __m256i andop, __m256i cmp)
+{
+ return _mm256_cvtepi32_ps(
+ _mm256_cmpeq_epi32(_mm256_and_si256(k, andop), cmp));
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_should_negate(__m256i k, __m256i andop, __m256i cmp)
+{
+ return fma_should_calculate_sine(k, andop, cmp);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_get_exponent(__m256 x)
+{
+ /*
+ * Special handling of denormals:
+ * 1) Multiply denormal elements with 2**100 (0x71800000)
+ * 2) Get the 8 bits of unbiased exponent
+ * 3) Subtract 100 from exponent of denormals
+ */
+
+ __m256 two_power_100 = _mm256_castsi256_ps(_mm256_set1_epi32(0x71800000));
+ __m256 denormal_mask = _mm256_cmp_ps(x, _mm256_set1_ps(FLT_MIN), _CMP_LT_OQ);
+ __m256 normal_mask = _mm256_cmp_ps(x, _mm256_set1_ps(FLT_MIN), _CMP_GE_OQ);
+
+ __m256 temp1 = _mm256_blendv_ps(x, _mm256_set1_ps(0.0f), normal_mask);
+ __m256 temp = _mm256_mul_ps(temp1, two_power_100);
+ x = _mm256_blendv_ps(x, temp, denormal_mask);
+
+ __m256 exp = _mm256_cvtepi32_ps(
+ _mm256_sub_epi32(
+ _mm256_srli_epi32(
+ _mm256_castps_si256(x), 23),_mm256_set1_epi32(0x7E)));
+
+ __m256 denorm_exp = _mm256_sub_ps(exp, _mm256_set1_ps(100.0f));
+ return _mm256_blendv_ps(exp, denorm_exp, denormal_mask);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256
+fma_get_mantissa(__m256 x)
+{
+ /*
+ * Special handling of denormals:
+ * 1) Multiply denormal elements with 2**100 (0x71800000)
+ * 2) Get the 23 bits of mantissa
+ * 3) Mantissa for denormals is not affected by the multiplication
+ */
+
+ __m256 two_power_100 = _mm256_castsi256_ps(_mm256_set1_epi32(0x71800000));
+ __m256 denormal_mask = _mm256_cmp_ps(x, _mm256_set1_ps(FLT_MIN), _CMP_LT_OQ);
+ __m256 normal_mask = _mm256_cmp_ps(x, _mm256_set1_ps(FLT_MIN), _CMP_GE_OQ);
+
+ __m256 temp1 = _mm256_blendv_ps(x, _mm256_set1_ps(0.0f), normal_mask);
+ __m256 temp = _mm256_mul_ps(temp1, two_power_100);
+ x = _mm256_blendv_ps(x, temp, denormal_mask);
+
+ __m256i mantissa_bits = _mm256_set1_epi32(0x7fffff);
+ __m256i exp_126_bits = _mm256_set1_epi32(126 << 23);
+ return _mm256_castsi256_ps(
+ _mm256_or_si256(
+ _mm256_and_si256(
+ _mm256_castps_si256(x), mantissa_bits), exp_126_bits));
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX2 __m256
+fma_scalef_ps(__m256 poly, __m256 quadrant)
+{
+ /*
+ * Handle denormals (which occur when quadrant <= -125):
+ * 1) This function computes poly*(2^quad) by adding the exponent of
+ poly to quad
+ * 2) When quad <= -125, the output is a denormal and the above logic
+ breaks down
+ * 3) To handle such cases, we split quadrant: -125 + (quadrant + 125)
+ * 4) poly*(2^-125) is computed the usual way
+ * 5) 2^(quad-125) can be computed by: 2 << abs(quad-125)
+ * 6) The final div operation generates the denormal
+ */
+ __m256 minquadrant = _mm256_set1_ps(-125.0f);
+ __m256 denormal_mask = _mm256_cmp_ps(quadrant, minquadrant, _CMP_LE_OQ);
+ if (_mm256_movemask_ps(denormal_mask) != 0x0000) {
+ __m256 quad_diff = _mm256_sub_ps(quadrant, minquadrant);
+ quad_diff = _mm256_sub_ps(_mm256_setzero_ps(), quad_diff);
+ quad_diff = _mm256_blendv_ps(_mm256_setzero_ps(), quad_diff, denormal_mask);
+ __m256i two_power_diff = _mm256_sllv_epi32(
+ _mm256_set1_epi32(1), _mm256_cvtps_epi32(quad_diff));
+ quadrant = _mm256_max_ps(quadrant, minquadrant); //keep quadrant >= -126
+ __m256i exponent = _mm256_slli_epi32(_mm256_cvtps_epi32(quadrant), 23);
+ poly = _mm256_castsi256_ps(
+ _mm256_add_epi32(
+ _mm256_castps_si256(poly), exponent));
+ __m256 denorm_poly = _mm256_div_ps(poly, _mm256_cvtepi32_ps(two_power_diff));
+ return _mm256_blendv_ps(poly, denorm_poly, denormal_mask);
+ }
+ else {
+ __m256i exponent = _mm256_slli_epi32(_mm256_cvtps_epi32(quadrant), 23);
+ poly = _mm256_castsi256_ps(
+ _mm256_add_epi32(
+ _mm256_castps_si256(poly), exponent));
+ return poly;
+ }
+}
+
+/**begin repeat
+ * #vsub = ps, pd#
+ * #vtype = __m256, __m256d#
+ */
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@
+fma_abs_@vsub@(@vtype@ x)
+{
+ return _mm256_andnot_@vsub@(_mm256_set1_@vsub@(-0.0), x);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@
+fma_reciprocal_@vsub@(@vtype@ x)
+{
+ return _mm256_div_@vsub@(_mm256_set1_@vsub@(1.0f), x);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@
+fma_rint_@vsub@(@vtype@ x)
+{
+ return _mm256_round_@vsub@(x, _MM_FROUND_TO_NEAREST_INT);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@
+fma_floor_@vsub@(@vtype@ x)
+{
+ return _mm256_round_@vsub@(x, _MM_FROUND_TO_NEG_INF);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@
+fma_ceil_@vsub@(@vtype@ x)
+{
+ return _mm256_round_@vsub@(x, _MM_FROUND_TO_POS_INF);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@
+fma_trunc_@vsub@(@vtype@ x)
+{
+ return _mm256_round_@vsub@(x, _MM_FROUND_TO_ZERO);
+}
+/**end repeat**/
+#endif
+
+#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16
+avx512_get_full_load_mask_ps(void)
+{
+ return 0xFFFF;
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask8
+avx512_get_full_load_mask_pd(void)
+{
+ return 0xFF;
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16
+avx512_get_partial_load_mask_ps(const npy_int num_elem, const npy_int total_elem)
+{
+ return (0x0001 << num_elem) - 0x0001;
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask8
+avx512_get_partial_load_mask_pd(const npy_int num_elem, const npy_int total_elem)
+{
+ return (0x01 << num_elem) - 0x01;
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512
+avx512_masked_gather_ps(__m512 src,
+ npy_float* addr,
+ __m512i vindex,
+ __mmask16 kmask)
+{
+ return _mm512_mask_i32gather_ps(src, kmask, vindex, addr, 4);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512d
+avx512_masked_gather_pd(__m512d src,
+ npy_double* addr,
+ __m256i vindex,
+ __mmask8 kmask)
+{
+ return _mm512_mask_i32gather_pd(src, kmask, vindex, addr, 8);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512
+avx512_masked_load_ps(__mmask16 mask, npy_float* addr)
+{
+ return _mm512_maskz_loadu_ps(mask, (__m512 *)addr);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512d
+avx512_masked_load_pd(__mmask8 mask, npy_double* addr)
+{
+ return _mm512_maskz_loadu_pd(mask, (__m512d *)addr);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512
+avx512_set_masked_lanes_ps(__m512 x, __m512 val, __mmask16 mask)
+{
+ return _mm512_mask_blend_ps(mask, x, val);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512d
+avx512_set_masked_lanes_pd(__m512d x, __m512d val, __mmask8 mask)
+{
+ return _mm512_mask_blend_pd(mask, x, val);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512
+avx512_blend(__m512 x, __m512 y, __mmask16 ymask)
+{
+ return _mm512_mask_mov_ps(x, ymask, y);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16
+avx512_invert_mask_ps(__mmask16 ymask)
+{
+ return _mm512_knot(ymask);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask8
+avx512_invert_mask_pd(__mmask8 ymask)
+{
+ return _mm512_knot(ymask);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16
+avx512_should_calculate_sine(__m512i k, __m512i andop, __m512i cmp)
+{
+ return _mm512_cmpeq_epi32_mask(_mm512_and_epi32(k, andop), cmp);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16
+avx512_should_negate(__m512i k, __m512i andop, __m512i cmp)
+{
+ return avx512_should_calculate_sine(k, andop, cmp);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512
+avx512_get_exponent(__m512 x)
+{
+ return _mm512_add_ps(_mm512_getexp_ps(x), _mm512_set1_ps(1.0f));
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512
+avx512_get_mantissa(__m512 x)
+{
+ return _mm512_getmant_ps(x, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_src);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512
+avx512_scalef_ps(__m512 poly, __m512 quadrant)
+{
+ return _mm512_scalef_ps(poly, quadrant);
+}
+/**begin repeat
+ * #vsub = ps, pd#
+ * #epi_vsub = epi32, epi64#
+ * #vtype = __m512, __m512d#
+ * #and_const = 0x7fffffff, 0x7fffffffffffffffLL#
+ */
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@
+avx512_abs_@vsub@(@vtype@ x)
+{
+ return (@vtype@) _mm512_and_@epi_vsub@((__m512i) x,
+ _mm512_set1_@epi_vsub@ (@and_const@));
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@
+avx512_reciprocal_@vsub@(@vtype@ x)
+{
+ return _mm512_div_@vsub@(_mm512_set1_@vsub@(1.0f), x);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@
+avx512_rint_@vsub@(@vtype@ x)
+{
+ return _mm512_roundscale_@vsub@(x, 0x08);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@
+avx512_floor_@vsub@(@vtype@ x)
+{
+ return _mm512_roundscale_@vsub@(x, 0x09);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@
+avx512_ceil_@vsub@(@vtype@ x)
+{
+ return _mm512_roundscale_@vsub@(x, 0x0A);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@
+avx512_trunc_@vsub@(@vtype@ x)
+{
+ return _mm512_roundscale_@vsub@(x, 0x0B);
+}
+/**end repeat**/
+#endif
+
+/**begin repeat
+ * #ISA = FMA, AVX512F#
+ * #isa = fma, avx512#
+ * #vtype = __m256, __m512#
+ * #vsize = 256, 512#
+ * #or = or_ps, kor#
+ * #vsub = , _mask#
+ * #mask = __m256, __mmask16#
+ * #fmadd = _mm256_fmadd_ps, _mm512_fmadd_ps#
+ * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS#
+ **/
+
+#if defined @CHK@
+
+/*
+ * Vectorized Cody-Waite range reduction technique
+ * Performs the reduction step x* = x - y*C in three steps:
+ * 1) x* = x - y*c1
+ * 2) x* = x - y*c2
+ * 3) x* = x - y*c3
+ * c1, c2 are exact floating points, c3 = C - c1 - c2 simulates higher precision
+ */
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@
+@isa@_range_reduction(@vtype@ x, @vtype@ y, @vtype@ c1, @vtype@ c2, @vtype@ c3)
+{
+ @vtype@ reduced_x = @fmadd@(y, c1, x);
+ reduced_x = @fmadd@(y, c2, reduced_x);
+ reduced_x = @fmadd@(y, c3, reduced_x);
+ return reduced_x;
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @mask@
+@isa@_in_range_mask(@vtype@ x, npy_float fmax, npy_float fmin)
+{
+ @mask@ m1 = _mm@vsize@_cmp_ps@vsub@(
+ x, _mm@vsize@_set1_ps(fmax), _CMP_GT_OQ);
+ @mask@ m2 = _mm@vsize@_cmp_ps@vsub@(
+ x, _mm@vsize@_set1_ps(fmin), _CMP_LT_OQ);
+ return _mm@vsize@_@or@(m1,m2);
+}
+
+/*
+ * Approximate cosine algorithm for x \in [-PI/4, PI/4]
+ * Maximum ULP across all 32-bit floats = 0.875
+ */
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@
+@isa@_cosine(@vtype@ x2, @vtype@ invf8, @vtype@ invf6, @vtype@ invf4,
+ @vtype@ invf2, @vtype@ invf0)
+{
+ @vtype@ cos = @fmadd@(invf8, x2, invf6);
+ cos = @fmadd@(cos, x2, invf4);
+ cos = @fmadd@(cos, x2, invf2);
+ cos = @fmadd@(cos, x2, invf0);
+ return cos;
+}
+
+/*
+ * Approximate sine algorithm for x \in [-PI/4, PI/4]
+ * Maximum ULP across all 32-bit floats = 0.647
+ */
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@
+@isa@_sine(@vtype@ x, @vtype@ x2, @vtype@ invf9, @vtype@ invf7,
+ @vtype@ invf5, @vtype@ invf3,
+ @vtype@ zero)
+{
+ @vtype@ sin = @fmadd@(invf9, x2, invf7);
+ sin = @fmadd@(sin, x2, invf5);
+ sin = @fmadd@(sin, x2, invf3);
+ sin = @fmadd@(sin, x2, zero);
+ sin = @fmadd@(sin, x, x);
+ return sin;
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@
+@isa@_sqrt_ps(@vtype@ x)
+{
+ return _mm@vsize@_sqrt_ps(x);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@d
+@isa@_sqrt_pd(@vtype@d x)
+{
+ return _mm@vsize@_sqrt_pd(x);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@
+@isa@_square_ps(@vtype@ x)
+{
+ return _mm@vsize@_mul_ps(x,x);
+}
+
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@d
+@isa@_square_pd(@vtype@d x)
+{
+ return _mm@vsize@_mul_pd(x,x);
+}
+
+#endif
+/**end repeat**/
+
+
+/**begin repeat
+ * #ISA = FMA, AVX512F#
+ * #isa = fma, avx512#
+ * #vsize = 256, 512#
+ * #BYTES = 32, 64#
+ * #cvtps_epi32 = _mm256_cvtps_epi32, #
+ * #mask = __m256, __mmask16#
+ * #vsub = , _mask#
+ * #vtype = __m256, __m512#
+ * #cvtps_epi32 = _mm256_cvtps_epi32, #
+ * #masked_store = _mm256_maskstore_ps, _mm512_mask_storeu_ps#
+ * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS#
+ */
+
+/**begin repeat1
+ * #func = sqrt, absolute, square, reciprocal, rint, ceil, floor, trunc#
+ * #vectorf = sqrt, abs, square, reciprocal, rint, ceil, floor, trunc#
+ * #replace_0_with_1 = 0, 0, 0, 1, 0, 0, 0, 0#
+ */
+
+#if defined @CHK@
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void
+@ISA@_@func@_FLOAT(npy_float* op,
+ npy_float* ip,
+ const npy_intp array_size,
+ const npy_intp steps)
+{
+ const npy_intp stride = steps/sizeof(npy_float);
+ const npy_int num_lanes = @BYTES@/sizeof(npy_float);
+ npy_intp num_remaining_elements = array_size;
+ @vtype@ ones_f = _mm@vsize@_set1_ps(1.0f);
+ @mask@ load_mask = @isa@_get_full_load_mask_ps();
+#if @replace_0_with_1@
+ @mask@ inv_load_mask = @isa@_invert_mask_ps(load_mask);
+#endif
+ npy_int indexarr[16];
+ for (npy_int ii = 0; ii < 16; ii++) {
+ indexarr[ii] = ii*stride;
+ }
+ @vtype@i vindex = _mm@vsize@_loadu_si@vsize@((@vtype@i*)&indexarr[0]);
+
+ while (num_remaining_elements > 0) {
+ if (num_remaining_elements < num_lanes) {
+ load_mask = @isa@_get_partial_load_mask_ps(num_remaining_elements,
+ num_lanes);
+#if @replace_0_with_1@
+ inv_load_mask = @isa@_invert_mask_ps(load_mask);
+#endif
+ }
+ @vtype@ x;
+ if (stride == 1) {
+ x = @isa@_masked_load_ps(load_mask, ip);
+#if @replace_0_with_1@
+ /*
+ * Replace masked elements with 1.0f to avoid divide by zero fp
+ * exception in reciprocal
+ */
+ x = @isa@_set_masked_lanes_ps(x, ones_f, inv_load_mask);
+#endif
+ }
+ else {
+ x = @isa@_masked_gather_ps(ones_f, ip, vindex, load_mask);
+ }
+ @vtype@ out = @isa@_@vectorf@_ps(x);
+ @masked_store@(op, @cvtps_epi32@(load_mask), out);
+
+ ip += num_lanes*stride;
+ op += num_lanes;
+ num_remaining_elements -= num_lanes;
}
}
+#endif
/**end repeat1**/
+/**end repeat**/
+
+/**begin repeat
+ * #ISA = FMA, AVX512F#
+ * #isa = fma, avx512#
+ * #vsize = 256, 512#
+ * #BYTES = 32, 64#
+ * #cvtps_epi32 = _mm256_cvtps_epi32, #
+ * #mask = __m256i, __mmask8#
+ * #vsub = , _mask#
+ * #vtype = __m256d, __m512d#
+ * #vindextype = __m128i, __m256i#
+ * #vindexsize = 128, 256#
+ * #vindexload = _mm_loadu_si128, _mm256_loadu_si256#
+ * #cvtps_epi32 = _mm256_cvtpd_epi32, #
+ * #castmask = _mm256_castsi256_pd, #
+ * #masked_store = _mm256_maskstore_pd, _mm512_mask_storeu_pd#
+ * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS#
+ */
+
+/**begin repeat1
+ * #func = sqrt, absolute, square, reciprocal, rint, ceil, floor, trunc#
+ * #vectorf = sqrt, abs, square, reciprocal, rint, ceil, floor, trunc#
+ * #replace_0_with_1 = 0, 0, 0, 1, 0, 0, 0, 0#
+ */
+#if defined @CHK@
+static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void
+@ISA@_@func@_DOUBLE(npy_double* op,
+ npy_double* ip,
+ const npy_intp array_size,
+ const npy_intp steps)
+{
+ const npy_intp stride = steps/sizeof(npy_double);
+ const npy_int num_lanes = @BYTES@/sizeof(npy_double);
+ npy_intp num_remaining_elements = array_size;
+ @mask@ load_mask = @isa@_get_full_load_mask_pd();
+#if @replace_0_with_1@
+ @mask@ inv_load_mask = @isa@_invert_mask_pd(load_mask);
+#endif
+ @vtype@ ones_d = _mm@vsize@_set1_pd(1.0f);
+ npy_int indexarr[8];
+ for (npy_int ii = 0; ii < 8; ii++) {
+ indexarr[ii] = ii*stride;
+ }
+ @vindextype@ vindex = @vindexload@((@vindextype@*)&indexarr[0]);
+
+ while (num_remaining_elements > 0) {
+ if (num_remaining_elements < num_lanes) {
+ load_mask = @isa@_get_partial_load_mask_pd(num_remaining_elements,
+ num_lanes);
+#if @replace_0_with_1@
+ inv_load_mask = @isa@_invert_mask_pd(load_mask);
+#endif
+ }
+ @vtype@ x;
+ if (stride == 1) {
+ x = @isa@_masked_load_pd(load_mask, ip);
+#if @replace_0_with_1@
+ /*
+ * Replace masked elements with 1.0f to avoid divide by zero fp
+ * exception in reciprocal
+ */
+ x = @isa@_set_masked_lanes_pd(x, ones_d, @castmask@(inv_load_mask));
+#endif
+ }
+ else {
+ x = @isa@_masked_gather_pd(ones_d, ip, vindex, @castmask@(load_mask));
+ }
+ @vtype@ out = @isa@_@vectorf@_pd(x);
+ @masked_store@(op, load_mask, out);
+
+ ip += num_lanes*stride;
+ op += num_lanes;
+ num_remaining_elements -= num_lanes;
+ }
+}
+#endif
+/**end repeat1**/
+/**end repeat**/
+
+/**begin repeat
+ * #ISA = FMA, AVX512F#
+ * #isa = fma, avx512#
+ * #vtype = __m256, __m512#
+ * #vsize = 256, 512#
+ * #BYTES = 32, 64#
+ * #mask = __m256, __mmask16#
+ * #vsub = , _mask#
+ * #or_masks =_mm256_or_ps, _mm512_kor#
+ * #and_masks =_mm256_and_ps, _mm512_kand#
+ * #xor_masks =_mm256_xor_ps, _mm512_kxor#
+ * #fmadd = _mm256_fmadd_ps, _mm512_fmadd_ps#
+ * #mask_to_int = _mm256_movemask_ps, #
+ * #full_mask= 0xFF, 0xFFFF#
+ * #masked_store = _mm256_maskstore_ps, _mm512_mask_storeu_ps#
+ * #cvtps_epi32 = _mm256_cvtps_epi32, #
+ * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS#
+ */
+
+/*
+ * Vectorized approximate sine/cosine algorithms: The following code is a
+ * vectorized version of the algorithm presented here:
+ * https://stackoverflow.com/questions/30463616/payne-hanek-algorithm-implementation-in-c/30465751#30465751
+ * (1) Load data in ZMM/YMM registers and generate mask for elements that are
+ * within range [-71476.0625f, 71476.0625f] for cosine and [-117435.992f,
+ * 117435.992f] for sine.
+ * (2) For elements within range, perform range reduction using Cody-Waite's
+ * method: x* = x - y*PI/2, where y = rint(x*2/PI). x* \in [-PI/4, PI/4].
+ * (3) Map cos(x) to (+/-)sine or (+/-)cosine of x* based on the quadrant k =
+ * int(y).
+ * (4) For elements outside that range, Cody-Waite reduction peforms poorly
+ * leading to catastrophic cancellation. We compute cosine by calling glibc in
+ * a scalar fashion.
+ * (5) Vectorized implementation has a max ULP of 1.49 and performs at least
+ * 5-7x faster than scalar implementations when magnitude of all elements in
+ * the array < 71476.0625f (117435.992f for sine). Worst case performance is
+ * when all the elements are large leading to about 1-2% reduction in
+ * performance.
+ */
+
+#if defined @CHK@
+static NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void
+@ISA@_sincos_FLOAT(npy_float * op,
+ npy_float * ip,
+ const npy_intp array_size,
+ const npy_intp steps,
+ NPY_TRIG_OP my_trig_op)
+{
+ const npy_intp stride = steps/sizeof(npy_float);
+ const npy_int num_lanes = @BYTES@/sizeof(npy_float);
+ npy_float large_number = 71476.0625f;
+ if (my_trig_op == npy_compute_sin) {
+ large_number = 117435.992f;
+ }
+
+ /* Load up frequently used constants */
+ @vtype@i zeros = _mm@vsize@_set1_epi32(0);
+ @vtype@i ones = _mm@vsize@_set1_epi32(1);
+ @vtype@i twos = _mm@vsize@_set1_epi32(2);
+ @vtype@ two_over_pi = _mm@vsize@_set1_ps(NPY_TWO_O_PIf);
+ @vtype@ codyw_c1 = _mm@vsize@_set1_ps(NPY_CODY_WAITE_PI_O_2_HIGHf);
+ @vtype@ codyw_c2 = _mm@vsize@_set1_ps(NPY_CODY_WAITE_PI_O_2_MEDf);
+ @vtype@ codyw_c3 = _mm@vsize@_set1_ps(NPY_CODY_WAITE_PI_O_2_LOWf);
+ @vtype@ cos_invf0 = _mm@vsize@_set1_ps(NPY_COEFF_INVF0_COSINEf);
+ @vtype@ cos_invf2 = _mm@vsize@_set1_ps(NPY_COEFF_INVF2_COSINEf);
+ @vtype@ cos_invf4 = _mm@vsize@_set1_ps(NPY_COEFF_INVF4_COSINEf);
+ @vtype@ cos_invf6 = _mm@vsize@_set1_ps(NPY_COEFF_INVF6_COSINEf);
+ @vtype@ cos_invf8 = _mm@vsize@_set1_ps(NPY_COEFF_INVF8_COSINEf);
+ @vtype@ sin_invf3 = _mm@vsize@_set1_ps(NPY_COEFF_INVF3_SINEf);
+ @vtype@ sin_invf5 = _mm@vsize@_set1_ps(NPY_COEFF_INVF5_SINEf);
+ @vtype@ sin_invf7 = _mm@vsize@_set1_ps(NPY_COEFF_INVF7_SINEf);
+ @vtype@ sin_invf9 = _mm@vsize@_set1_ps(NPY_COEFF_INVF9_SINEf);
+ @vtype@ cvt_magic = _mm@vsize@_set1_ps(NPY_RINT_CVT_MAGICf);
+ @vtype@ zero_f = _mm@vsize@_set1_ps(0.0f);
+ @vtype@ quadrant, reduced_x, reduced_x2, cos, sin;
+ @vtype@i iquadrant;
+ @mask@ nan_mask, glibc_mask, sine_mask, negate_mask;
+ @mask@ load_mask = @isa@_get_full_load_mask_ps();
+ npy_intp num_remaining_elements = array_size;
+ npy_int indexarr[16];
+ for (npy_int ii = 0; ii < 16; ii++) {
+ indexarr[ii] = ii*stride;
+ }
+ @vtype@i vindex = _mm@vsize@_loadu_si@vsize@((@vtype@i*)&indexarr[0]);
+
+ while (num_remaining_elements > 0) {
+
+ if (num_remaining_elements < num_lanes) {
+ load_mask = @isa@_get_partial_load_mask_ps(num_remaining_elements,
+ num_lanes);
+ }
+
+ @vtype@ x;
+ if (stride == 1) {
+ x = @isa@_masked_load_ps(load_mask, ip);
+ }
+ else {
+ x = @isa@_masked_gather_ps(zero_f, ip, vindex, load_mask);
+ }
+
+ /*
+ * For elements outside of this range, Cody-Waite's range reduction
+ * becomes inaccurate and we will call glibc to compute cosine for
+ * these numbers
+ */
+
+ glibc_mask = @isa@_in_range_mask(x, large_number,-large_number);
+ glibc_mask = @and_masks@(load_mask, glibc_mask);
+ nan_mask = _mm@vsize@_cmp_ps@vsub@(x, x, _CMP_NEQ_UQ);
+ x = @isa@_set_masked_lanes_ps(x, zero_f, @or_masks@(nan_mask, glibc_mask));
+ npy_int iglibc_mask = @mask_to_int@(glibc_mask);
+
+ if (iglibc_mask != @full_mask@) {
+ quadrant = _mm@vsize@_mul_ps(x, two_over_pi);
+
+ /* round to nearest */
+ quadrant = _mm@vsize@_add_ps(quadrant, cvt_magic);
+ quadrant = _mm@vsize@_sub_ps(quadrant, cvt_magic);
+
+ /* Cody-Waite's range reduction algorithm */
+ reduced_x = @isa@_range_reduction(x, quadrant,
+ codyw_c1, codyw_c2, codyw_c3);
+ reduced_x2 = _mm@vsize@_mul_ps(reduced_x, reduced_x);
+
+ /* compute cosine and sine */
+ cos = @isa@_cosine(reduced_x2, cos_invf8, cos_invf6, cos_invf4,
+ cos_invf2, cos_invf0);
+ sin = @isa@_sine(reduced_x, reduced_x2, sin_invf9, sin_invf7,
+ sin_invf5, sin_invf3, zero_f);
+
+ iquadrant = _mm@vsize@_cvtps_epi32(quadrant);
+ if (my_trig_op == npy_compute_cos) {
+ iquadrant = _mm@vsize@_add_epi32(iquadrant, ones);
+ }
+
+ /* blend sin and cos based on the quadrant */
+ sine_mask = @isa@_should_calculate_sine(iquadrant, ones, zeros);
+ cos = @isa@_blend(cos, sin, sine_mask);
+
+ /* multiply by -1 for appropriate elements */
+ negate_mask = @isa@_should_negate(iquadrant, twos, twos);
+ cos = @isa@_blend(cos, _mm@vsize@_sub_ps(zero_f, cos), negate_mask);
+ cos = @isa@_set_masked_lanes_ps(cos, _mm@vsize@_set1_ps(NPY_NANF), nan_mask);
+
+ @masked_store@(op, @cvtps_epi32@(load_mask), cos);
+ }
+
+ /* process elements using glibc for large elements */
+ if (my_trig_op == npy_compute_cos) {
+ for (int ii = 0; iglibc_mask != 0; ii++) {
+ if (iglibc_mask & 0x01) {
+ op[ii] = npy_cosf(ip[ii]);
+ }
+ iglibc_mask = iglibc_mask >> 1;
+ }
+ }
+ else {
+ for (int ii = 0; iglibc_mask != 0; ii++) {
+ if (iglibc_mask & 0x01) {
+ op[ii] = npy_sinf(ip[ii]);
+ }
+ iglibc_mask = iglibc_mask >> 1;
+ }
+ }
+ ip += num_lanes*stride;
+ op += num_lanes;
+ num_remaining_elements -= num_lanes;
+ }
+}
+
+/*
+ * Vectorized implementation of exp using AVX2 and AVX512:
+ * 1) if x >= xmax; return INF (overflow)
+ * 2) if x <= xmin; return 0.0f (underflow)
+ * 3) Range reduction (using Coyd-Waite):
+ * a) y = x - k*ln(2); k = rint(x/ln(2)); y \in [0, ln(2)]
+ * 4) Compute exp(y) = P/Q, ratio of 2 polynomials P and Q
+ * b) P = 5th order and Q = 2nd order polynomials obtained from Remez's
+ * algorithm (mini-max polynomial approximation)
+ * 5) Compute exp(x) = exp(y) * 2^k
+ * 6) Max ULP error measured across all 32-bit FP's = 2.52 (x = 0xc2781e37)
+ * 7) Max relative error measured across all 32-bit FP's= 2.1264E-07 (for the
+ * same x = 0xc2781e37)
+ */
+
+static NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void
+@ISA@_exp_FLOAT(npy_float * op,
+ npy_float * ip,
+ const npy_intp array_size,
+ const npy_intp steps)
+{
+ const npy_intp stride = steps/sizeof(npy_float);
+ const npy_int num_lanes = @BYTES@/sizeof(npy_float);
+ npy_float xmax = 88.72283935546875f;
+ npy_float xmin = -103.97208404541015625f;
+ npy_int indexarr[16];
+ for (npy_int ii = 0; ii < 16; ii++) {
+ indexarr[ii] = ii*stride;
+ }
+
+ /* Load up frequently used constants */
+ @vtype@ codyw_c1 = _mm@vsize@_set1_ps(NPY_CODY_WAITE_LOGE_2_HIGHf);
+ @vtype@ codyw_c2 = _mm@vsize@_set1_ps(NPY_CODY_WAITE_LOGE_2_LOWf);
+ @vtype@ exp_p0 = _mm@vsize@_set1_ps(NPY_COEFF_P0_EXPf);
+ @vtype@ exp_p1 = _mm@vsize@_set1_ps(NPY_COEFF_P1_EXPf);
+ @vtype@ exp_p2 = _mm@vsize@_set1_ps(NPY_COEFF_P2_EXPf);
+ @vtype@ exp_p3 = _mm@vsize@_set1_ps(NPY_COEFF_P3_EXPf);
+ @vtype@ exp_p4 = _mm@vsize@_set1_ps(NPY_COEFF_P4_EXPf);
+ @vtype@ exp_p5 = _mm@vsize@_set1_ps(NPY_COEFF_P5_EXPf);
+ @vtype@ exp_q0 = _mm@vsize@_set1_ps(NPY_COEFF_Q0_EXPf);
+ @vtype@ exp_q1 = _mm@vsize@_set1_ps(NPY_COEFF_Q1_EXPf);
+ @vtype@ exp_q2 = _mm@vsize@_set1_ps(NPY_COEFF_Q2_EXPf);
+ @vtype@ cvt_magic = _mm@vsize@_set1_ps(NPY_RINT_CVT_MAGICf);
+ @vtype@ log2e = _mm@vsize@_set1_ps(NPY_LOG2Ef);
+ @vtype@ inf = _mm@vsize@_set1_ps(NPY_INFINITYF);
+ @vtype@ zeros_f = _mm@vsize@_set1_ps(0.0f);
+ @vtype@ poly, num_poly, denom_poly, quadrant;
+ @vtype@i vindex = _mm@vsize@_loadu_si@vsize@((@vtype@i*)&indexarr[0]);
+
+ @mask@ xmax_mask, xmin_mask, nan_mask, inf_mask;
+ @mask@ overflow_mask = @isa@_get_partial_load_mask_ps(0, num_lanes);
+ @mask@ load_mask = @isa@_get_full_load_mask_ps();
+ npy_intp num_remaining_elements = array_size;
+
+ while (num_remaining_elements > 0) {
+
+ if (num_remaining_elements < num_lanes) {
+ load_mask = @isa@_get_partial_load_mask_ps(num_remaining_elements,
+ num_lanes);
+ }
+
+ @vtype@ x;
+ if (stride == 1) {
+ x = @isa@_masked_load_ps(load_mask, ip);
+ }
+ else {
+ x = @isa@_masked_gather_ps(zeros_f, ip, vindex, load_mask);
+ }
+
+ nan_mask = _mm@vsize@_cmp_ps@vsub@(x, x, _CMP_NEQ_UQ);
+ x = @isa@_set_masked_lanes_ps(x, zeros_f, nan_mask);
+
+ xmax_mask = _mm@vsize@_cmp_ps@vsub@(x, _mm@vsize@_set1_ps(xmax), _CMP_GE_OQ);
+ xmin_mask = _mm@vsize@_cmp_ps@vsub@(x, _mm@vsize@_set1_ps(xmin), _CMP_LE_OQ);
+ inf_mask = _mm@vsize@_cmp_ps@vsub@(x, inf, _CMP_EQ_OQ);
+ overflow_mask = @or_masks@(overflow_mask,
+ @xor_masks@(xmax_mask, inf_mask));
+
+ x = @isa@_set_masked_lanes_ps(x, zeros_f, @or_masks@(
+ @or_masks@(nan_mask, xmin_mask), xmax_mask));
+
+ quadrant = _mm@vsize@_mul_ps(x, log2e);
+
+ /* round to nearest */
+ quadrant = _mm@vsize@_add_ps(quadrant, cvt_magic);
+ quadrant = _mm@vsize@_sub_ps(quadrant, cvt_magic);
+
+ /* Cody-Waite's range reduction algorithm */
+ x = @isa@_range_reduction(x, quadrant, codyw_c1, codyw_c2, zeros_f);
+
+ num_poly = @fmadd@(exp_p5, x, exp_p4);
+ num_poly = @fmadd@(num_poly, x, exp_p3);
+ num_poly = @fmadd@(num_poly, x, exp_p2);
+ num_poly = @fmadd@(num_poly, x, exp_p1);
+ num_poly = @fmadd@(num_poly, x, exp_p0);
+ denom_poly = @fmadd@(exp_q2, x, exp_q1);
+ denom_poly = @fmadd@(denom_poly, x, exp_q0);
+ poly = _mm@vsize@_div_ps(num_poly, denom_poly);
+
+ /*
+ * compute val = poly * 2^quadrant; which is same as adding the
+ * exponent of quadrant to the exponent of poly. quadrant is an int,
+ * so extracting exponent is simply extracting 8 bits.
+ */
+ poly = @isa@_scalef_ps(poly, quadrant);
+
+ /*
+ * elem > xmax; return inf
+ * elem < xmin; return 0.0f
+ * elem = +/- nan, return nan
+ */
+ poly = @isa@_set_masked_lanes_ps(poly, _mm@vsize@_set1_ps(NPY_NANF), nan_mask);
+ poly = @isa@_set_masked_lanes_ps(poly, inf, xmax_mask);
+ poly = @isa@_set_masked_lanes_ps(poly, zeros_f, xmin_mask);
+
+ @masked_store@(op, @cvtps_epi32@(load_mask), poly);
+
+ ip += num_lanes*stride;
+ op += num_lanes;
+ num_remaining_elements -= num_lanes;
+ }
+
+ if (@mask_to_int@(overflow_mask)) {
+ npy_set_floatstatus_overflow();
+ }
+}
+
+/*
+ * Vectorized implementation of log using AVX2 and AVX512
+ * 1) if x < 0.0f; return -NAN (invalid input)
+ * 2) Range reduction: y = x/2^k;
+ * a) y = normalized mantissa, k is the exponent (0.5 <= y < 1)
+ * 3) Compute log(y) = P/Q, ratio of 2 polynomials P and Q
+ * b) P = 5th order and Q = 5th order polynomials obtained from Remez's
+ * algorithm (mini-max polynomial approximation)
+ * 5) Compute log(x) = log(y) + k*ln(2)
+ * 6) Max ULP error measured across all 32-bit FP's = 3.83 (x = 0x3f486945)
+ * 7) Max relative error measured across all 32-bit FP's = 2.359E-07 (for same
+ * x = 0x3f486945)
+ */
+
+static NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void
+@ISA@_log_FLOAT(npy_float * op,
+ npy_float * ip,
+ const npy_intp array_size,
+ const npy_intp steps)
+{
+ const npy_intp stride = steps/sizeof(npy_float);
+ const npy_int num_lanes = @BYTES@/sizeof(npy_float);
+ npy_int indexarr[16];
+ for (npy_int ii = 0; ii < 16; ii++) {
+ indexarr[ii] = ii*stride;
+ }
+
+ /* Load up frequently used constants */
+ @vtype@ log_p0 = _mm@vsize@_set1_ps(NPY_COEFF_P0_LOGf);
+ @vtype@ log_p1 = _mm@vsize@_set1_ps(NPY_COEFF_P1_LOGf);
+ @vtype@ log_p2 = _mm@vsize@_set1_ps(NPY_COEFF_P2_LOGf);
+ @vtype@ log_p3 = _mm@vsize@_set1_ps(NPY_COEFF_P3_LOGf);
+ @vtype@ log_p4 = _mm@vsize@_set1_ps(NPY_COEFF_P4_LOGf);
+ @vtype@ log_p5 = _mm@vsize@_set1_ps(NPY_COEFF_P5_LOGf);
+ @vtype@ log_q0 = _mm@vsize@_set1_ps(NPY_COEFF_Q0_LOGf);
+ @vtype@ log_q1 = _mm@vsize@_set1_ps(NPY_COEFF_Q1_LOGf);
+ @vtype@ log_q2 = _mm@vsize@_set1_ps(NPY_COEFF_Q2_LOGf);
+ @vtype@ log_q3 = _mm@vsize@_set1_ps(NPY_COEFF_Q3_LOGf);
+ @vtype@ log_q4 = _mm@vsize@_set1_ps(NPY_COEFF_Q4_LOGf);
+ @vtype@ log_q5 = _mm@vsize@_set1_ps(NPY_COEFF_Q5_LOGf);
+ @vtype@ loge2 = _mm@vsize@_set1_ps(NPY_LOGE2f);
+ @vtype@ nan = _mm@vsize@_set1_ps(NPY_NANF);
+ @vtype@ neg_nan = _mm@vsize@_set1_ps(-NPY_NANF);
+ @vtype@ neg_inf = _mm@vsize@_set1_ps(-NPY_INFINITYF);
+ @vtype@ inf = _mm@vsize@_set1_ps(NPY_INFINITYF);
+ @vtype@ zeros_f = _mm@vsize@_set1_ps(0.0f);
+ @vtype@ ones_f = _mm@vsize@_set1_ps(1.0f);
+ @vtype@i vindex = _mm@vsize@_loadu_si@vsize@((@vtype@i*)indexarr);
+ @vtype@ poly, num_poly, denom_poly, exponent;
+
+ @mask@ inf_mask, nan_mask, sqrt2_mask, zero_mask, negx_mask;
+ @mask@ invalid_mask = @isa@_get_partial_load_mask_ps(0, num_lanes);
+ @mask@ divide_by_zero_mask = invalid_mask;
+ @mask@ load_mask = @isa@_get_full_load_mask_ps();
+ npy_intp num_remaining_elements = array_size;
+
+ while (num_remaining_elements > 0) {
+
+ if (num_remaining_elements < num_lanes) {
+ load_mask = @isa@_get_partial_load_mask_ps(num_remaining_elements,
+ num_lanes);
+ }
+
+ @vtype@ x_in;
+ if (stride == 1) {
+ x_in = @isa@_masked_load_ps(load_mask, ip);
+ }
+ else {
+ x_in = @isa@_masked_gather_ps(zeros_f, ip, vindex, load_mask);
+ }
+
+ negx_mask = _mm@vsize@_cmp_ps@vsub@(x_in, zeros_f, _CMP_LT_OQ);
+ zero_mask = _mm@vsize@_cmp_ps@vsub@(x_in, zeros_f, _CMP_EQ_OQ);
+ inf_mask = _mm@vsize@_cmp_ps@vsub@(x_in, inf, _CMP_EQ_OQ);
+ nan_mask = _mm@vsize@_cmp_ps@vsub@(x_in, x_in, _CMP_NEQ_UQ);
+ divide_by_zero_mask = @or_masks@(divide_by_zero_mask,
+ @and_masks@(zero_mask, load_mask));
+ invalid_mask = @or_masks@(invalid_mask, negx_mask);
+
+ @vtype@ x = @isa@_set_masked_lanes_ps(x_in, zeros_f, negx_mask);
+
+ /* set x = normalized mantissa */
+ exponent = @isa@_get_exponent(x);
+ x = @isa@_get_mantissa(x);
+
+ /* if x < sqrt(2) {exp = exp-1; x = 2*x} */
+ sqrt2_mask = _mm@vsize@_cmp_ps@vsub@(x, _mm@vsize@_set1_ps(NPY_SQRT1_2f), _CMP_LE_OQ);
+ x = @isa@_blend(x, _mm@vsize@_add_ps(x,x), sqrt2_mask);
+ exponent = @isa@_blend(exponent,
+ _mm@vsize@_sub_ps(exponent,ones_f), sqrt2_mask);
+
+ /* x = x - 1 */
+ x = _mm@vsize@_sub_ps(x, ones_f);
+
+ /* Polynomial approximation for log(1+x) */
+ num_poly = @fmadd@(log_p5, x, log_p4);
+ num_poly = @fmadd@(num_poly, x, log_p3);
+ num_poly = @fmadd@(num_poly, x, log_p2);
+ num_poly = @fmadd@(num_poly, x, log_p1);
+ num_poly = @fmadd@(num_poly, x, log_p0);
+ denom_poly = @fmadd@(log_q5, x, log_q4);
+ denom_poly = @fmadd@(denom_poly, x, log_q3);
+ denom_poly = @fmadd@(denom_poly, x, log_q2);
+ denom_poly = @fmadd@(denom_poly, x, log_q1);
+ denom_poly = @fmadd@(denom_poly, x, log_q0);
+ poly = _mm@vsize@_div_ps(num_poly, denom_poly);
+ poly = @fmadd@(exponent, loge2, poly);
+
+ /*
+ * x < 0.0f; return -NAN
+ * x = +/- NAN; return NAN
+ * x = 0.0f; return -INF
+ */
+ poly = @isa@_set_masked_lanes_ps(poly, nan, nan_mask);
+ poly = @isa@_set_masked_lanes_ps(poly, neg_nan, negx_mask);
+ poly = @isa@_set_masked_lanes_ps(poly, neg_inf, zero_mask);
+ poly = @isa@_set_masked_lanes_ps(poly, inf, inf_mask);
+
+ @masked_store@(op, @cvtps_epi32@(load_mask), poly);
+
+ ip += num_lanes*stride;
+ op += num_lanes;
+ num_remaining_elements -= num_lanes;
+ }
+
+ if (@mask_to_int@(invalid_mask)) {
+ npy_set_floatstatus_invalid();
+ }
+ if (@mask_to_int@(divide_by_zero_mask)) {
+ npy_set_floatstatus_divbyzero();
+ }
+}
+#endif
/**end repeat**/
/*
@@ -1099,9 +2298,9 @@ static NPY_INLINE @vtype@ byte_to_true(@vtype@ v)
static void
sse2_binary_@kind@_BOOL(npy_bool * op, npy_bool * ip1, npy_bool * ip2, npy_intp n)
{
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 16)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES)
op[i] = ip1[i] @op@ ip2[i];
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vloadu@((@vtype@*)&ip1[i]);
@vtype@ b = @vloadu@((@vtype@*)&ip2[i]);
#if @and@
@@ -1126,16 +2325,16 @@ static void
sse2_reduce_@kind@_BOOL(npy_bool * op, npy_bool * ip, const npy_intp n)
{
const @vtype@ zero = @vpre@_setzero_@vsuf@();
- LOOP_BLOCK_ALIGN_VAR(ip, npy_bool, 16) {
+ LOOP_BLOCK_ALIGN_VAR(ip, npy_bool, VECTOR_SIZE_BYTES) {
*op = *op @op@ ip[i];
if (*op @sc@ 0) {
return;
}
}
/* unrolled once to replace a slow movmsk with a fast pmaxb */
- LOOP_BLOCKED(npy_bool, 32) {
+ LOOP_BLOCKED(npy_bool, 2 * VECTOR_SIZE_BYTES) {
@vtype@ v = @vload@((@vtype@*)&ip[i]);
- @vtype@ v2 = @vload@((@vtype@*)&ip[i + 16]);
+ @vtype@ v2 = @vload@((@vtype@*)&ip[i + VECTOR_SIZE_BYTES]);
v = @vpre@_cmpeq_epi8(v, zero);
v2 = @vpre@_cmpeq_epi8(v2, zero);
#if @and@
@@ -1173,9 +2372,9 @@ sse2_reduce_@kind@_BOOL(npy_bool * op, npy_bool * ip, const npy_intp n)
static void
sse2_@kind@_BOOL(@type@ * op, @type@ * ip, const npy_intp n)
{
- LOOP_BLOCK_ALIGN_VAR(op, @type@, 16)
+ LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES)
op[i] = (ip[i] @op@ 0);
- LOOP_BLOCKED(@type@, 16) {
+ LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) {
@vtype@ a = @vloadu@((@vtype@*)&ip[i]);
#if @not@
const @vtype@ zero = @vpre@_setzero_@vsuf@();
@@ -1196,6 +2395,8 @@ sse2_@kind@_BOOL(@type@ * op, @type@ * ip, const npy_intp n)
/**end repeat**/
+#undef VECTOR_SIZE_BYTES
+
#endif /* NPY_HAVE_SSE2_INTRINSICS */
#endif
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 459b0a594..e4ad3dc84 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -46,6 +46,7 @@
#include "npy_import.h"
#include "extobj.h"
#include "common.h"
+#include "numpyos.h"
/********** PRINTF DEBUG TRACING **************/
#define NPY_UF_DBG_TRACING 0
@@ -307,6 +308,78 @@ _find_array_prepare(ufunc_full_args args,
return;
}
+#define NPY_UFUNC_DEFAULT_INPUT_FLAGS \
+ NPY_ITER_READONLY | \
+ NPY_ITER_ALIGNED | \
+ NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE
+
+#define NPY_UFUNC_DEFAULT_OUTPUT_FLAGS \
+ NPY_ITER_ALIGNED | \
+ NPY_ITER_ALLOCATE | \
+ NPY_ITER_NO_BROADCAST | \
+ NPY_ITER_NO_SUBTYPE | \
+ NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE
+
+/* Called at module initialization to set the matmul ufunc output flags */
+NPY_NO_EXPORT int
+set_matmul_flags(PyObject *d)
+{
+ PyObject *matmul = PyDict_GetItemString(d, "matmul");
+ if (matmul == NULL) {
+ return -1;
+ }
+ /*
+ * The default output flag NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE allows
+ * perfectly overlapping input and output (in-place operations). While
+ * correct for the common mathematical operations, this assumption is
+ * incorrect in the general case and specifically in the case of matmul.
+ *
+ * NPY_ITER_UPDATEIFCOPY is added by default in
+ * PyUFunc_GeneralizedFunction, which is the variant called for gufuncs
+ * with a signature
+ *
+ * Enabling NPY_ITER_WRITEONLY can prevent a copy in some cases.
+ */
+ ((PyUFuncObject *)matmul)->op_flags[2] = (NPY_ITER_WRITEONLY |
+ NPY_ITER_UPDATEIFCOPY |
+ NPY_UFUNC_DEFAULT_OUTPUT_FLAGS) &
+ ~NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE;
+ return 0;
+}
+
+
+/*
+ * Set per-operand flags according to desired input or output flags.
+ * op_flags[i] for i in input (as determined by ufunc->nin) will be
+ * merged with op_in_flags, perhaps overriding per-operand flags set
+ * in previous stages.
+ * op_flags[i] for i in output will be set to op_out_flags only if previously
+ * unset.
+ * The input flag behavior preserves backward compatibility, while the
+ * output flag behaviour is the "correct" one for maximum flexibility.
+ */
+NPY_NO_EXPORT void
+_ufunc_setup_flags(PyUFuncObject *ufunc, npy_uint32 op_in_flags,
+ npy_uint32 op_out_flags, npy_uint32 *op_flags)
+{
+ int nin = ufunc->nin;
+ int nout = ufunc->nout;
+ int nop = nin + nout, i;
+ /* Set up the flags */
+ for (i = 0; i < nin; ++i) {
+ op_flags[i] = ufunc->op_flags[i] | op_in_flags;
+ /*
+ * If READWRITE flag has been set for this operand,
+ * then clear default READONLY flag
+ */
+ if (op_flags[i] & (NPY_ITER_READWRITE | NPY_ITER_WRITEONLY)) {
+ op_flags[i] &= ~NPY_ITER_READONLY;
+ }
+ }
+ for (i = nin; i < nop; ++i) {
+ op_flags[i] = ufunc->op_flags[i] ? ufunc->op_flags[i] : op_out_flags;
+ }
+}
/*
* This function analyzes the input arguments
@@ -480,7 +553,27 @@ _is_alnum_underscore(char ch)
}
/*
- * Return the ending position of a variable name
+ * Convert a string into a number
+ */
+static npy_intp
+_get_size(const char* str)
+{
+ char *stop;
+ npy_longlong size = NumPyOS_strtoll(str, &stop, 10);
+
+ if (stop == str || _is_alpha_underscore(*stop)) {
+ /* not a well formed number */
+ return -1;
+ }
+ if (size >= NPY_MAX_INTP || size <= NPY_MIN_INTP) {
+ /* len(str) too long */
+ return -1;
+ }
+ return size;
+}
+
+/*
+ * Return the ending position of a variable name including optional modifier
*/
static int
_get_end_of_name(const char* str, int offset)
@@ -489,6 +582,9 @@ _get_end_of_name(const char* str, int offset)
while (_is_alnum_underscore(str[ret])) {
ret++;
}
+ if (str[ret] == '?') {
+ ret ++;
+ }
return ret;
}
@@ -530,7 +626,6 @@ _parse_signature(PyUFuncObject *ufunc, const char *signature)
"_parse_signature with NULL signature");
return -1;
}
-
len = strlen(signature);
ufunc->core_signature = PyArray_malloc(sizeof(char) * (len+1));
if (ufunc->core_signature) {
@@ -546,13 +641,22 @@ _parse_signature(PyUFuncObject *ufunc, const char *signature)
ufunc->core_enabled = 1;
ufunc->core_num_dim_ix = 0;
ufunc->core_num_dims = PyArray_malloc(sizeof(int) * ufunc->nargs);
- ufunc->core_dim_ixs = PyArray_malloc(sizeof(int) * len); /* shrink this later */
ufunc->core_offsets = PyArray_malloc(sizeof(int) * ufunc->nargs);
- if (ufunc->core_num_dims == NULL || ufunc->core_dim_ixs == NULL
- || ufunc->core_offsets == NULL) {
+ /* The next three items will be shrunk later */
+ ufunc->core_dim_ixs = PyArray_malloc(sizeof(int) * len);
+ ufunc->core_dim_sizes = PyArray_malloc(sizeof(npy_intp) * len);
+ ufunc->core_dim_flags = PyArray_malloc(sizeof(npy_uint32) * len);
+
+ if (ufunc->core_num_dims == NULL || ufunc->core_dim_ixs == NULL ||
+ ufunc->core_offsets == NULL ||
+ ufunc->core_dim_sizes == NULL ||
+ ufunc->core_dim_flags == NULL) {
PyErr_NoMemory();
goto fail;
}
+ for (size_t j = 0; j < len; j++) {
+ ufunc->core_dim_flags[j] = 0;
+ }
i = _next_non_white_space(signature, 0);
while (signature[i] != '\0') {
@@ -577,26 +681,70 @@ _parse_signature(PyUFuncObject *ufunc, const char *signature)
i = _next_non_white_space(signature, i + 1);
while (signature[i] != ')') {
/* loop over core dimensions */
- int j = 0;
- if (!_is_alpha_underscore(signature[i])) {
- parse_error = "expect dimension name";
+ int ix, i_end;
+ npy_intp frozen_size;
+ npy_bool can_ignore;
+
+ if (signature[i] == '\0') {
+ parse_error = "unexpected end of signature string";
goto fail;
}
- while (j < ufunc->core_num_dim_ix) {
- if (_is_same_name(signature+i, var_names[j])) {
+ /*
+ * Is this a variable or a fixed size dimension?
+ */
+ if (_is_alpha_underscore(signature[i])) {
+ frozen_size = -1;
+ }
+ else {
+ frozen_size = (npy_intp)_get_size(signature + i);
+ if (frozen_size <= 0) {
+ parse_error = "expect dimension name or non-zero frozen size";
+ goto fail;
+ }
+ }
+ /* Is this dimension flexible? */
+ i_end = _get_end_of_name(signature, i);
+ can_ignore = (i_end > 0 && signature[i_end - 1] == '?');
+ /*
+ * Determine whether we already saw this dimension name,
+ * get its index, and set its properties
+ */
+ for(ix = 0; ix < ufunc->core_num_dim_ix; ix++) {
+ if (frozen_size > 0 ?
+ frozen_size == ufunc->core_dim_sizes[ix] :
+ _is_same_name(signature + i, var_names[ix])) {
break;
}
- j++;
}
- if (j >= ufunc->core_num_dim_ix) {
- var_names[j] = signature+i;
+ /*
+ * If a new dimension, store its properties; if old, check consistency.
+ */
+ if (ix == ufunc->core_num_dim_ix) {
ufunc->core_num_dim_ix++;
+ var_names[ix] = signature + i;
+ ufunc->core_dim_sizes[ix] = frozen_size;
+ if (frozen_size < 0) {
+ ufunc->core_dim_flags[ix] |= UFUNC_CORE_DIM_SIZE_INFERRED;
+ }
+ if (can_ignore) {
+ ufunc->core_dim_flags[ix] |= UFUNC_CORE_DIM_CAN_IGNORE;
+ }
+ } else {
+ if (can_ignore && !(ufunc->core_dim_flags[ix] &
+ UFUNC_CORE_DIM_CAN_IGNORE)) {
+ parse_error = "? cannot be used, name already seen without ?";
+ goto fail;
+ }
+ if (!can_ignore && (ufunc->core_dim_flags[ix] &
+ UFUNC_CORE_DIM_CAN_IGNORE)) {
+ parse_error = "? must be used, name already seen with ?";
+ goto fail;
+ }
}
- ufunc->core_dim_ixs[cur_core_dim] = j;
+ ufunc->core_dim_ixs[cur_core_dim] = ix;
cur_core_dim++;
nd++;
- i = _get_end_of_name(signature, i);
- i = _next_non_white_space(signature, i);
+ i = _next_non_white_space(signature, i_end);
if (signature[i] != ',' && signature[i] != ')') {
parse_error = "expect ',' or ')'";
goto fail;
@@ -633,7 +781,14 @@ _parse_signature(PyUFuncObject *ufunc, const char *signature)
goto fail;
}
ufunc->core_dim_ixs = PyArray_realloc(ufunc->core_dim_ixs,
- sizeof(int)*cur_core_dim);
+ sizeof(int) * cur_core_dim);
+ ufunc->core_dim_sizes = PyArray_realloc(
+ ufunc->core_dim_sizes,
+ sizeof(npy_intp) * ufunc->core_num_dim_ix);
+ ufunc->core_dim_flags = PyArray_realloc(
+ ufunc->core_dim_flags,
+ sizeof(npy_uint32) * ufunc->core_num_dim_ix);
+
/* check for trivial core-signature, e.g. "(),()->()" */
if (cur_core_dim == 0) {
ufunc->core_enabled = 0;
@@ -664,7 +819,7 @@ _set_out_array(PyObject *obj, PyArrayObject **store)
/* Translate None to NULL */
return 0;
}
- if PyArray_Check(obj) {
+ if (PyArray_Check(obj)) {
/* If it's an array, store it */
if (PyArray_FailUnlessWriteable((PyArrayObject *)obj,
"output array") < 0) {
@@ -753,7 +908,7 @@ parse_ufunc_keywords(PyUFuncObject *ufunc, PyObject *kwds, PyObject **kwnames, .
typedef int converter(PyObject *, void *);
while (PyDict_Next(kwds, &pos, &key, &value)) {
- int i;
+ npy_intp i;
converter *convert;
void *output = NULL;
npy_intp index = locate_key(kwnames, key);
@@ -1038,34 +1193,11 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
}
}
else {
- /*
- * If the deprecated behavior is ever removed,
- * keep only the else branch of this if-else
- */
- if (PyArray_Check(out_kwd) || out_kwd == Py_None) {
- if (DEPRECATE("passing a single array to the "
- "'out' keyword argument of a "
- "ufunc with\n"
- "more than one output will "
- "result in an error in the "
- "future") < 0) {
- /* The future error message */
- PyErr_SetString(PyExc_TypeError,
- "'out' must be a tuple of arrays");
- goto fail;
- }
- if (_set_out_array(out_kwd, out_op+nin) < 0) {
- goto fail;
- }
- }
- else {
- PyErr_SetString(PyExc_TypeError,
- nout > 1 ? "'out' must be a tuple "
- "of arrays" :
- "'out' must be an array or a "
- "tuple of a single array");
- goto fail;
- }
+ PyErr_SetString(PyExc_TypeError,
+ nout > 1 ? "'out' must be a tuple of arrays" :
+ "'out' must be an array or a tuple with "
+ "a single array");
+ goto fail;
}
}
/*
@@ -1311,11 +1443,11 @@ iterator_loop(PyUFuncObject *ufunc,
PyObject **arr_prep,
ufunc_full_args full_args,
PyUFuncGenericFunction innerloop,
- void *innerloopdata)
+ void *innerloopdata,
+ npy_uint32 *op_flags)
{
npy_intp i, nin = ufunc->nin, nout = ufunc->nout;
npy_intp nop = nin + nout;
- npy_uint32 op_flags[NPY_MAXARGS];
NpyIter *iter;
char *baseptrs[NPY_MAXARGS];
@@ -1329,29 +1461,6 @@ iterator_loop(PyUFuncObject *ufunc,
NPY_BEGIN_THREADS_DEF;
- /* Set up the flags */
- for (i = 0; i < nin; ++i) {
- op_flags[i] = NPY_ITER_READONLY |
- NPY_ITER_ALIGNED |
- NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE;
- /*
- * If READWRITE flag has been set for this operand,
- * then clear default READONLY flag
- */
- op_flags[i] |= ufunc->op_flags[i];
- if (op_flags[i] & (NPY_ITER_READWRITE | NPY_ITER_WRITEONLY)) {
- op_flags[i] &= ~NPY_ITER_READONLY;
- }
- }
- for (i = nin; i < nop; ++i) {
- op_flags[i] = NPY_ITER_WRITEONLY |
- NPY_ITER_ALIGNED |
- NPY_ITER_ALLOCATE |
- NPY_ITER_NO_BROADCAST |
- NPY_ITER_NO_SUBTYPE |
- NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE;
- }
-
iter_flags = ufunc->iter_flags |
NPY_ITER_EXTERNAL_LOOP |
NPY_ITER_REFS_OK |
@@ -1455,15 +1564,15 @@ iterator_loop(PyUFuncObject *ufunc,
}
/*
+ * ufunc - the ufunc to call
* trivial_loop_ok - 1 if no alignment, data conversion, etc required
- * nin - number of inputs
- * nout - number of outputs
- * op - the operands (nin + nout of them)
+ * op - the operands (ufunc->nin + ufunc->nout of them)
+ * dtypes - the dtype of each operand
* order - the loop execution order/output memory order
* buffersize - how big of a buffer to use
* arr_prep - the __array_prepare__ functions for the outputs
- * innerloop - the inner loop function
- * innerloopdata - data to pass to the inner loop
+ * full_args - the original input, output PyObject *
+ * op_flags - per-operand flags, a combination of NPY_ITER_* constants
*/
static int
execute_legacy_ufunc_loop(PyUFuncObject *ufunc,
@@ -1473,7 +1582,8 @@ execute_legacy_ufunc_loop(PyUFuncObject *ufunc,
NPY_ORDER order,
npy_intp buffersize,
PyObject **arr_prep,
- ufunc_full_args full_args)
+ ufunc_full_args full_args,
+ npy_uint32 *op_flags)
{
npy_intp nin = ufunc->nin, nout = ufunc->nout;
PyUFuncGenericFunction innerloop;
@@ -1608,7 +1718,7 @@ execute_legacy_ufunc_loop(PyUFuncObject *ufunc,
NPY_UF_DBG_PRINT("iterator loop\n");
if (iterator_loop(ufunc, op, dtypes, order,
buffersize, arr_prep, full_args,
- innerloop, innerloopdata) < 0) {
+ innerloop, innerloopdata, op_flags) < 0) {
return -1;
}
@@ -1634,14 +1744,13 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc,
NPY_ORDER order,
npy_intp buffersize,
PyObject **arr_prep,
- ufunc_full_args full_args)
+ ufunc_full_args full_args,
+ npy_uint32 *op_flags)
{
int i, nin = ufunc->nin, nout = ufunc->nout;
int nop = nin + nout;
- npy_uint32 op_flags[NPY_MAXARGS];
NpyIter *iter;
int needs_api;
- npy_intp default_op_in_flags = 0, default_op_out_flags = 0;
NpyIter_IterNextFunc *iternext;
char **dataptr;
@@ -1651,48 +1760,10 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc,
PyArrayObject **op_it;
npy_uint32 iter_flags;
- if (wheremask != NULL) {
- if (nop + 1 > NPY_MAXARGS) {
- PyErr_SetString(PyExc_ValueError,
- "Too many operands when including where= parameter");
- return -1;
- }
- op[nop] = wheremask;
- dtypes[nop] = NULL;
- default_op_out_flags |= NPY_ITER_WRITEMASKED;
- }
-
- /* Set up the flags */
- for (i = 0; i < nin; ++i) {
- op_flags[i] = default_op_in_flags |
- NPY_ITER_READONLY |
- NPY_ITER_ALIGNED |
- NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE;
- /*
- * If READWRITE flag has been set for this operand,
- * then clear default READONLY flag
- */
- op_flags[i] |= ufunc->op_flags[i];
- if (op_flags[i] & (NPY_ITER_READWRITE | NPY_ITER_WRITEONLY)) {
- op_flags[i] &= ~NPY_ITER_READONLY;
- }
- }
for (i = nin; i < nop; ++i) {
- /*
- * We don't write to all elements, and the iterator may make
- * UPDATEIFCOPY temporary copies. The output arrays (unless they are
- * allocated by the iterator itself) must be considered READWRITE by the
- * iterator, so that the elements we don't write to are copied to the
- * possible temporary array.
- */
- op_flags[i] = default_op_out_flags |
- (op[i] != NULL ? NPY_ITER_READWRITE : NPY_ITER_WRITEONLY) |
- NPY_ITER_ALIGNED |
- NPY_ITER_ALLOCATE |
- NPY_ITER_NO_BROADCAST |
- NPY_ITER_NO_SUBTYPE |
- NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE;
+ op_flags[i] |= (op[i] != NULL ? NPY_ITER_READWRITE : NPY_ITER_WRITEONLY);
}
+
if (wheremask != NULL) {
op_flags[nop] = NPY_ITER_READONLY | NPY_ITER_ARRAYMASK;
}
@@ -1935,6 +2006,72 @@ fail:
}
/*
+ * Validate that operands have enough dimensions, accounting for
+ * possible flexible dimensions that may be absent.
+ */
+static int
+_validate_num_dims(PyUFuncObject *ufunc, PyArrayObject **op,
+ npy_uint32 *core_dim_flags,
+ int *op_core_num_dims) {
+ int i, j;
+ int nin = ufunc->nin;
+ int nop = ufunc->nargs;
+
+ for (i = 0; i < nop; i++) {
+ if (op[i] != NULL) {
+ int op_ndim = PyArray_NDIM(op[i]);
+
+ if (op_ndim < op_core_num_dims[i]) {
+ int core_offset = ufunc->core_offsets[i];
+ /* We've too few, but some dimensions might be flexible */
+ for (j = core_offset;
+ j < core_offset + ufunc->core_num_dims[i]; j++) {
+ int core_dim_index = ufunc->core_dim_ixs[j];
+ if ((core_dim_flags[core_dim_index] &
+ UFUNC_CORE_DIM_CAN_IGNORE)) {
+ int i1, j1, k;
+ /*
+ * Found a dimension that can be ignored. Flag that
+ * it is missing, and unflag that it can be ignored,
+ * since we are doing so already.
+ */
+ core_dim_flags[core_dim_index] |= UFUNC_CORE_DIM_MISSING;
+ core_dim_flags[core_dim_index] ^= UFUNC_CORE_DIM_CAN_IGNORE;
+ /*
+ * Reduce the number of core dimensions for all
+ * operands that use this one (including ours),
+ * and check whether we're now OK.
+ */
+ for (i1 = 0, k=0; i1 < nop; i1++) {
+ for (j1 = 0; j1 < ufunc->core_num_dims[i1]; j1++) {
+ if (ufunc->core_dim_ixs[k++] == core_dim_index) {
+ op_core_num_dims[i1]--;
+ }
+ }
+ }
+ if (op_ndim == op_core_num_dims[i]) {
+ break;
+ }
+ }
+ }
+ if (op_ndim < op_core_num_dims[i]) {
+ PyErr_Format(PyExc_ValueError,
+ "%s: %s operand %d does not have enough "
+ "dimensions (has %d, gufunc core with "
+ "signature %s requires %d)",
+ ufunc_get_name_cstr(ufunc),
+ i < nin ? "Input" : "Output",
+ i < nin ? i : i - nin, PyArray_NDIM(op[i]),
+ ufunc->core_signature, op_core_num_dims[i]);
+ return -1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
* Check whether any of the outputs of a gufunc has core dimensions.
*/
static int
@@ -2007,7 +2144,7 @@ _check_keepdims_support(PyUFuncObject *ufunc) {
* Returns 0 on success, and -1 on failure
*/
static int
-_parse_axes_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axes,
+_parse_axes_arg(PyUFuncObject *ufunc, int op_core_num_dims[], PyObject *axes,
PyArrayObject **op, int broadcast_ndim, int **remap_axis) {
int nin = ufunc->nin;
int nop = ufunc->nargs;
@@ -2037,7 +2174,7 @@ _parse_axes_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axes,
PyObject *op_axes_tuple, *axis_item;
int axis, op_axis;
- op_ncore = core_num_dims[iop];
+ op_ncore = op_core_num_dims[iop];
if (op[iop] != NULL) {
op_ndim = PyArray_NDIM(op[iop]);
op_nbroadcast = op_ndim - op_ncore;
@@ -2137,7 +2274,7 @@ _parse_axes_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axes,
* Returns 0 on success, and -1 on failure
*/
static int
-_parse_axis_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axis,
+_parse_axis_arg(PyUFuncObject *ufunc, const int core_num_dims[], PyObject *axis,
PyArrayObject **op, int broadcast_ndim, int **remap_axis) {
int nop = ufunc->nargs;
int iop, axis_int;
@@ -2191,57 +2328,72 @@ _parse_axis_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axis,
*
* Returns 0 on success, and -1 on failure
*
- * The behavior has been changed in NumPy 1.10.0, and the following
+ * The behavior has been changed in NumPy 1.16.0, and the following
* requirements must be fulfilled or an error will be raised:
* * Arguments, both input and output, must have at least as many
* dimensions as the corresponding number of core dimensions. In
- * previous versions, 1's were prepended to the shape as needed.
+ * versions before 1.10, 1's were prepended to the shape as needed.
* * Core dimensions with same labels must have exactly matching sizes.
- * In previous versions, core dimensions of size 1 would broadcast
+ * In versions before 1.10, core dimensions of size 1 would broadcast
* against other core dimensions with the same label.
* * All core dimensions must have their size specified by a passed in
- * input or output argument. In previous versions, core dimensions in
+ * input or output argument. In versions before 1.10, core dimensions in
* an output argument that were not specified in an input argument,
* and whose size could not be inferred from a passed in output
* argument, would have their size set to 1.
+ * * Core dimensions may be fixed, new in NumPy 1.16
*/
static int
_get_coredim_sizes(PyUFuncObject *ufunc, PyArrayObject **op,
- npy_intp* core_dim_sizes, int **remap_axis) {
+ const int *op_core_num_dims, npy_uint32 *core_dim_flags,
+ npy_intp *core_dim_sizes, int **remap_axis) {
int i;
int nin = ufunc->nin;
int nout = ufunc->nout;
int nop = nin + nout;
- for (i = 0; i < ufunc->core_num_dim_ix; ++i) {
- core_dim_sizes[i] = -1;
- }
for (i = 0; i < nop; ++i) {
if (op[i] != NULL) {
int idim;
int dim_offset = ufunc->core_offsets[i];
- int num_dims = ufunc->core_num_dims[i];
- int core_start_dim = PyArray_NDIM(op[i]) - num_dims;
+ int core_start_dim = PyArray_NDIM(op[i]) - op_core_num_dims[i];
+ int dim_delta = 0;
+
+ /* checked before this routine gets called */
+ assert(core_start_dim >= 0);
+
/*
* Make sure every core dimension exactly matches all other core
- * dimensions with the same label.
+ * dimensions with the same label. Note that flexible dimensions
+ * may have been removed at this point, if so, they are marked
+ * with UFUNC_CORE_DIM_MISSING.
*/
- for (idim = 0; idim < num_dims; ++idim) {
- int core_dim_index = ufunc->core_dim_ixs[dim_offset+idim];
- npy_intp op_dim_size = PyArray_DIM(
- op[i], REMAP_AXIS(i, core_start_dim+idim));
-
- if (core_dim_sizes[core_dim_index] == -1) {
+ for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) {
+ int core_index = dim_offset + idim;
+ int core_dim_index = ufunc->core_dim_ixs[core_index];
+ npy_intp core_dim_size = core_dim_sizes[core_dim_index];
+ npy_intp op_dim_size;
+
+ /* can only happen if flexible; dimension missing altogether */
+ if (core_dim_flags[core_dim_index] & UFUNC_CORE_DIM_MISSING) {
+ op_dim_size = 1;
+ dim_delta++; /* for indexing in dimensions */
+ }
+ else {
+ op_dim_size = PyArray_DIM(op[i],
+ REMAP_AXIS(i, core_start_dim + idim - dim_delta));
+ }
+ if (core_dim_sizes[core_dim_index] < 0) {
core_dim_sizes[core_dim_index] = op_dim_size;
}
- else if (op_dim_size != core_dim_sizes[core_dim_index]) {
+ else if (op_dim_size != core_dim_size) {
PyErr_Format(PyExc_ValueError,
"%s: %s operand %d has a mismatch in its "
"core dimension %d, with gufunc "
"signature %s (size %zd is different "
"from %zd)",
ufunc_get_name_cstr(ufunc), i < nin ? "Input" : "Output",
- i < nin ? i : i - nin, idim,
+ i < nin ? i : i - nin, idim - dim_delta,
ufunc->core_signature, op_dim_size,
core_dim_sizes[core_dim_index]);
return -1;
@@ -2253,39 +2405,29 @@ _get_coredim_sizes(PyUFuncObject *ufunc, PyArrayObject **op,
/*
* Make sure no core dimension is unspecified.
*/
- for (i = 0; i < ufunc->core_num_dim_ix; ++i) {
- if (core_dim_sizes[i] == -1) {
- break;
- }
- }
- if (i != ufunc->core_num_dim_ix) {
- /*
- * There is at least one core dimension missing, find in which
- * operand it comes up first (it has to be an output operand).
- */
- const int missing_core_dim = i;
- int out_op;
- for (out_op = nin; out_op < nop; ++out_op) {
- int first_idx = ufunc->core_offsets[out_op];
- int last_idx = first_idx + ufunc->core_num_dims[out_op];
- for (i = first_idx; i < last_idx; ++i) {
- if (ufunc->core_dim_ixs[i] == missing_core_dim) {
- break;
- }
- }
- if (i < last_idx) {
- /* Change index offsets for error message */
- out_op -= nin;
- i -= first_idx;
- break;
+ for (i = nin; i < nop; ++i) {
+ int idim;
+ int dim_offset = ufunc->core_offsets[i];
+
+ for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) {
+ int core_dim_index = ufunc->core_dim_ixs[dim_offset + idim];
+
+ /* check all cases where the size has not yet been set */
+ if (core_dim_sizes[core_dim_index] < 0) {
+ /*
+ * Oops, this dimension was never specified
+ * (can only happen if output op not given)
+ */
+ PyErr_Format(PyExc_ValueError,
+ "%s: Output operand %d has core dimension %d "
+ "unspecified, with gufunc signature %s",
+ ufunc_get_name_cstr(ufunc), i - nin, idim,
+ ufunc->core_signature);
+ return -1;
}
}
- PyErr_Format(PyExc_ValueError,
- "%s: Output operand %d has core dimension %d "
- "unspecified, with gufunc signature %s",
- ufunc_get_name_cstr(ufunc), out_op, i, ufunc->core_signature);
- return -1;
}
+
return 0;
}
@@ -2317,6 +2459,11 @@ _get_identity(PyUFuncObject *ufunc, npy_bool *reorderable) {
*reorderable = 0;
Py_RETURN_NONE;
+ case PyUFunc_IdentityValue:
+ *reorderable = 1;
+ Py_INCREF(ufunc->identity_value);
+ return ufunc->identity_value;
+
default:
PyErr_Format(PyExc_ValueError,
"ufunc %s has an invalid identity", ufunc_get_name_cstr(ufunc));
@@ -2324,6 +2471,26 @@ _get_identity(PyUFuncObject *ufunc, npy_bool *reorderable) {
}
}
+/*
+ * Copy over parts of the ufunc structure that may need to be
+ * changed during execution. Returns 0 on success; -1 otherwise.
+ */
+static int
+_initialize_variable_parts(PyUFuncObject *ufunc,
+ int op_core_num_dims[],
+ npy_intp core_dim_sizes[],
+ npy_uint32 core_dim_flags[]) {
+ int i;
+
+ for (i = 0; i < ufunc->nargs; i++) {
+ op_core_num_dims[i] = ufunc->core_num_dims[i];
+ }
+ for (i = 0; i < ufunc->core_num_dim_ix; i++) {
+ core_dim_sizes[i] = ufunc->core_dim_sizes[i];
+ core_dim_flags[i] = ufunc->core_dim_flags[i];
+ }
+ return 0;
+}
static int
PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
@@ -2340,10 +2507,10 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
/* Use remapped axes for generalized ufunc */
int broadcast_ndim, iter_ndim;
- int core_num_dims_array[NPY_MAXARGS];
- int *core_num_dims;
+ int op_core_num_dims[NPY_MAXARGS];
int op_axes_arrays[NPY_MAXARGS][NPY_MAXDIMS];
int *op_axes[NPY_MAXARGS];
+ npy_uint32 core_dim_flags[NPY_MAXARGS];
npy_uint32 op_flags[NPY_MAXARGS];
npy_intp iter_shape[NPY_MAXARGS];
@@ -2398,6 +2565,12 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
dtypes[i] = NULL;
arr_prep[i] = NULL;
}
+ /* Initialize possibly variable parts to the values from the ufunc */
+ retval = _initialize_variable_parts(ufunc, op_core_num_dims,
+ core_dim_sizes, core_dim_flags);
+ if (retval < 0) {
+ goto fail;
+ }
NPY_UF_DBG_PRINT("Getting arguments\n");
@@ -2429,41 +2602,28 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
}
}
/*
- * If keepdims is set and true, signal all dimensions will be the same.
+ * If keepdims is set and true, which means all input dimensions are
+ * the same, signal that all output dimensions will be the same too.
*/
if (keepdims == 1) {
- int num_dims = ufunc->core_num_dims[0];
- for (i = 0; i < nop; ++i) {
- core_num_dims_array[i] = num_dims;
+ int num_dims = op_core_num_dims[0];
+ for (i = nin; i < nop; ++i) {
+ op_core_num_dims[i] = num_dims;
}
- core_num_dims = core_num_dims_array;
}
else {
/* keepdims was not set or was false; no adjustment necessary */
- core_num_dims = ufunc->core_num_dims;
keepdims = 0;
}
/*
* Check that operands have the minimum dimensions required.
* (Just checks core; broadcast dimensions are tested by the iterator.)
*/
- for (i = 0; i < nop; i++) {
- if (op[i] != NULL && PyArray_NDIM(op[i]) < core_num_dims[i]) {
- PyErr_Format(PyExc_ValueError,
- "%s: %s operand %d does not have enough "
- "dimensions (has %d, gufunc core with "
- "signature %s requires %d)",
- ufunc_name,
- i < nin ? "Input" : "Output",
- i < nin ? i : i - nin,
- PyArray_NDIM(op[i]),
- ufunc->core_signature,
- core_num_dims[i]);
- retval = -1;
- goto fail;
- }
+ retval = _validate_num_dims(ufunc, op, core_dim_flags,
+ op_core_num_dims);
+ if (retval < 0) {
+ goto fail;
}
-
/*
* Figure out the number of iteration dimensions, which
* is the broadcast result of all the input non-core
@@ -2471,30 +2631,12 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
*/
broadcast_ndim = 0;
for (i = 0; i < nin; ++i) {
- int n = PyArray_NDIM(op[i]) - core_num_dims[i];
+ int n = PyArray_NDIM(op[i]) - op_core_num_dims[i];
if (n > broadcast_ndim) {
broadcast_ndim = n;
}
}
- /*
- * Figure out the number of iterator creation dimensions,
- * which is the broadcast dimensions + all the core dimensions of
- * the outputs, so that the iterator can allocate those output
- * dimensions following the rules of order='F', for example.
- */
- iter_ndim = broadcast_ndim;
- for (i = nin; i < nop; ++i) {
- iter_ndim += core_num_dims[i];
- }
- if (iter_ndim > NPY_MAXDIMS) {
- PyErr_Format(PyExc_ValueError,
- "too many dimensions for generalized ufunc %s",
- ufunc_name);
- retval = -1;
- goto fail;
- }
-
/* Possibly remap axes. */
if (axes != NULL || axis != NULL) {
remap_axis = PyArray_malloc(sizeof(remap_axis[0]) * nop);
@@ -2508,11 +2650,11 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
remap_axis[i] = remap_axis_memory + i * NPY_MAXDIMS;
}
if (axis) {
- retval = _parse_axis_arg(ufunc, core_num_dims, axis, op,
+ retval = _parse_axis_arg(ufunc, op_core_num_dims, axis, op,
broadcast_ndim, remap_axis);
}
else {
- retval = _parse_axes_arg(ufunc, core_num_dims, axes, op,
+ retval = _parse_axes_arg(ufunc, op_core_num_dims, axes, op,
broadcast_ndim, remap_axis);
}
if(retval < 0) {
@@ -2521,10 +2663,28 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
}
/* Collect the lengths of the labelled core dimensions */
- retval = _get_coredim_sizes(ufunc, op, core_dim_sizes, remap_axis);
+ retval = _get_coredim_sizes(ufunc, op, op_core_num_dims, core_dim_flags,
+ core_dim_sizes, remap_axis);
if(retval < 0) {
goto fail;
}
+ /*
+ * Figure out the number of iterator creation dimensions,
+ * which is the broadcast dimensions + all the core dimensions of
+ * the outputs, so that the iterator can allocate those output
+ * dimensions following the rules of order='F', for example.
+ */
+ iter_ndim = broadcast_ndim;
+ for (i = nin; i < nop; ++i) {
+ iter_ndim += op_core_num_dims[i];
+ }
+ if (iter_ndim > NPY_MAXDIMS) {
+ PyErr_Format(PyExc_ValueError,
+ "too many dimensions for generalized ufunc %s",
+ ufunc_name);
+ retval = -1;
+ goto fail;
+ }
/* Fill in the initial part of 'iter_shape' */
for (idim = 0; idim < broadcast_ndim; ++idim) {
@@ -2537,11 +2697,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
int n;
if (op[i]) {
- /*
- * Note that n may be negative if broadcasting
- * extends into the core dimensions.
- */
- n = PyArray_NDIM(op[i]) - core_num_dims[i];
+ n = PyArray_NDIM(op[i]) - op_core_num_dims[i];
}
else {
n = broadcast_ndim;
@@ -2565,24 +2721,49 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
/* Except for when it belongs to this output */
if (i >= nin) {
int dim_offset = ufunc->core_offsets[i];
- int num_dims = core_num_dims[i];
+ int num_removed = 0;
/*
* Fill in 'iter_shape' and 'op_axes' for the core dimensions
* of this output. Here, we have to be careful: if keepdims
- * was used, then this axis is not a real core dimension,
- * but is being added back for broadcasting, so its size is 1.
+ * was used, then the axes are not real core dimensions, but
+ * are being added back for broadcasting, so their size is 1.
+ * If the axis was removed, we should skip altogether.
*/
- for (idim = 0; idim < num_dims; ++idim) {
- iter_shape[j] = keepdims ? 1 : core_dim_sizes[
- ufunc->core_dim_ixs[dim_offset + idim]];
- op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim);
- ++j;
+ if (keepdims) {
+ for (idim = 0; idim < op_core_num_dims[i]; ++idim) {
+ iter_shape[j] = 1;
+ op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim);
+ ++j;
+ }
+ }
+ else {
+ for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) {
+ int core_index = dim_offset + idim;
+ int core_dim_index = ufunc->core_dim_ixs[core_index];
+ if ((core_dim_flags[core_dim_index] &
+ UFUNC_CORE_DIM_MISSING)) {
+ /* skip it */
+ num_removed++;
+ continue;
+ }
+ iter_shape[j] = core_dim_sizes[ufunc->core_dim_ixs[core_index]];
+ op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim - num_removed);
+ ++j;
+ }
}
}
op_axes[i] = op_axes_arrays[i];
}
+#if NPY_UF_DBG_TRACING
+ printf("iter shapes:");
+ for (j=0; j < iter_ndim; j++) {
+ printf(" %ld", iter_shape[j]);
+ }
+ printf("\n");
+#endif
+
/* Get the buffersize and errormask */
if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) {
retval = -1;
@@ -2597,6 +2778,18 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
if (retval < 0) {
goto fail;
}
+ /*
+ * We don't write to all elements, and the iterator may make
+ * UPDATEIFCOPY temporary copies. The output arrays (unless they are
+ * allocated by the iterator itself) must be considered READWRITE by the
+ * iterator, so that the elements we don't write to are copied to the
+ * possible temporary array.
+ */
+ _ufunc_setup_flags(ufunc, NPY_ITER_COPY | NPY_UFUNC_DEFAULT_INPUT_FLAGS,
+ NPY_ITER_UPDATEIFCOPY |
+ NPY_ITER_READWRITE |
+ NPY_UFUNC_DEFAULT_OUTPUT_FLAGS,
+ op_flags);
/* For the generalized ufunc, we get the loop right away too */
retval = ufunc->legacy_inner_loop_selector(ufunc, dtypes,
&innerloop, &innerloopdata, &needs_api);
@@ -2639,28 +2832,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
* Set up the iterator per-op flags. For generalized ufuncs, we
* can't do buffering, so must COPY or UPDATEIFCOPY.
*/
- for (i = 0; i < nin; ++i) {
- op_flags[i] = NPY_ITER_READONLY |
- NPY_ITER_COPY |
- NPY_ITER_ALIGNED |
- NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE;
- /*
- * If READWRITE flag has been set for this operand,
- * then clear default READONLY flag
- */
- op_flags[i] |= ufunc->op_flags[i];
- if (op_flags[i] & (NPY_ITER_READWRITE | NPY_ITER_WRITEONLY)) {
- op_flags[i] &= ~NPY_ITER_READONLY;
- }
- }
- for (i = nin; i < nop; ++i) {
- op_flags[i] = NPY_ITER_READWRITE|
- NPY_ITER_UPDATEIFCOPY|
- NPY_ITER_ALIGNED|
- NPY_ITER_ALLOCATE|
- NPY_ITER_NO_BROADCAST|
- NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE;
- }
iter_flags = ufunc->iter_flags |
NPY_ITER_MULTI_INDEX |
@@ -2680,13 +2851,15 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
}
/* Fill in any allocated outputs */
- for (i = nin; i < nop; ++i) {
- if (op[i] == NULL) {
- op[i] = NpyIter_GetOperandArray(iter)[i];
- Py_INCREF(op[i]);
+ {
+ PyArrayObject **operands = NpyIter_GetOperandArray(iter);
+ for (i = 0; i < nop; ++i) {
+ if (op[i] == NULL) {
+ op[i] = operands[i];
+ Py_INCREF(op[i]);
+ }
}
}
-
/*
* Set up the inner strides array. Because we're not doing
* buffering, the strides are fixed throughout the looping.
@@ -2705,8 +2878,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
/* Copy the strides after the first nop */
idim = nop;
for (i = 0; i < nop; ++i) {
- int num_dims = ufunc->core_num_dims[i];
- int core_start_dim = PyArray_NDIM(op[i]) - num_dims;
/*
* Need to use the arrays in the iterator, not op, because
* a copy with a different-sized type may have been made.
@@ -2714,20 +2885,31 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
PyArrayObject *arr = NpyIter_GetOperandArray(iter)[i];
npy_intp *shape = PyArray_SHAPE(arr);
npy_intp *strides = PyArray_STRIDES(arr);
- for (j = 0; j < num_dims; ++j) {
- if (core_start_dim + j >= 0) {
- /*
- * Force the stride to zero when the shape is 1, so
- * that the broadcasting works right.
- */
- int remapped_axis = REMAP_AXIS(i, core_start_dim + j);
+ /*
+ * Could be negative if flexible dims are used, but not for
+ * keepdims, since those dimensions are allocated in arr.
+ */
+ int core_start_dim = PyArray_NDIM(arr) - op_core_num_dims[i];
+ int num_removed = 0;
+ int dim_offset = ufunc->core_offsets[i];
+
+ for (j = 0; j < ufunc->core_num_dims[i]; ++j) {
+ int core_dim_index = ufunc->core_dim_ixs[dim_offset + j];
+ /*
+ * Force zero stride when the shape is 1 (always the case for
+ * for missing dimensions), so that broadcasting works right.
+ */
+ if (core_dim_flags[core_dim_index] & UFUNC_CORE_DIM_MISSING) {
+ num_removed++;
+ inner_strides[idim++] = 0;
+ }
+ else {
+ int remapped_axis = REMAP_AXIS(i, core_start_dim + j - num_removed);
if (shape[remapped_axis] != 1) {
inner_strides[idim++] = strides[remapped_axis];
} else {
inner_strides[idim++] = 0;
}
- } else {
- inner_strides[idim++] = 0;
}
}
}
@@ -2858,8 +3040,10 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
Py_XDECREF(axis);
Py_XDECREF(full_args.in);
Py_XDECREF(full_args.out);
+ PyArray_free(remap_axis_memory);
+ PyArray_free(remap_axis);
- NPY_UF_DBG_PRINT1("Returning code %d\n", reval);
+ NPY_UF_DBG_PRINT1("Returning code %d\n", retval);
return retval;
@@ -2900,7 +3084,8 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc,
int i, nop;
const char *ufunc_name;
int retval = -1, subok = 1;
- int need_fancy = 0;
+ npy_uint32 op_flags[NPY_MAXARGS];
+ npy_intp default_op_out_flags;
PyArray_Descr *dtypes[NPY_MAXARGS];
@@ -2959,13 +3144,6 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc,
return retval;
}
- /*
- * Use the masked loop if a wheremask was specified.
- */
- if (wheremask != NULL) {
- need_fancy = 1;
- }
-
/* Get the buffersize and errormask */
if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) {
retval = -1;
@@ -2980,16 +3158,20 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc,
goto fail;
}
- /* Only do the trivial loop check for the unmasked version. */
- if (!need_fancy) {
- /*
- * This checks whether a trivial loop is ok, making copies of
- * scalar and one dimensional operands if that will help.
- */
- trivial_loop_ok = check_for_trivial_loop(ufunc, op, dtypes, buffersize);
- if (trivial_loop_ok < 0) {
- goto fail;
- }
+ if (wheremask != NULL) {
+ /* Set up the flags. */
+ default_op_out_flags = NPY_ITER_NO_SUBTYPE |
+ NPY_ITER_WRITEMASKED |
+ NPY_UFUNC_DEFAULT_OUTPUT_FLAGS;
+ _ufunc_setup_flags(ufunc, NPY_UFUNC_DEFAULT_INPUT_FLAGS,
+ default_op_out_flags, op_flags);
+ }
+ else {
+ /* Set up the flags. */
+ default_op_out_flags = NPY_ITER_WRITEONLY |
+ NPY_UFUNC_DEFAULT_OUTPUT_FLAGS;
+ _ufunc_setup_flags(ufunc, NPY_UFUNC_DEFAULT_INPUT_FLAGS,
+ default_op_out_flags, op_flags);
}
#if NPY_UF_DBG_TRACING
@@ -3017,23 +3199,46 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc,
_find_array_prepare(full_args, arr_prep, nin, nout);
}
- /* Start with the floating-point exception flags cleared */
- npy_clear_floatstatus_barrier((char*)&ufunc);
/* Do the ufunc loop */
- if (need_fancy) {
+ if (wheremask != NULL) {
NPY_UF_DBG_PRINT("Executing fancy inner loop\n");
+ if (nop + 1 > NPY_MAXARGS) {
+ PyErr_SetString(PyExc_ValueError,
+ "Too many operands when including where= parameter");
+ return -1;
+ }
+ op[nop] = wheremask;
+ dtypes[nop] = NULL;
+
+ /* Set up the flags */
+
+ npy_clear_floatstatus_barrier((char*)&ufunc);
retval = execute_fancy_ufunc_loop(ufunc, wheremask,
op, dtypes, order,
- buffersize, arr_prep, full_args);
+ buffersize, arr_prep, full_args, op_flags);
}
else {
NPY_UF_DBG_PRINT("Executing legacy inner loop\n");
+ /*
+ * This checks whether a trivial loop is ok, making copies of
+ * scalar and one dimensional operands if that will help.
+ * Since it requires dtypes, it can only be called after
+ * ufunc->type_resolver
+ */
+ trivial_loop_ok = check_for_trivial_loop(ufunc, op, dtypes, buffersize);
+ if (trivial_loop_ok < 0) {
+ goto fail;
+ }
+
+ /* check_for_trivial_loop on half-floats can overflow */
+ npy_clear_floatstatus_barrier((char*)&ufunc);
+
retval = execute_legacy_ufunc_loop(ufunc, trivial_loop_ok,
op, dtypes, order,
- buffersize, arr_prep, full_args);
+ buffersize, arr_prep, full_args, op_flags);
}
if (retval < 0) {
goto fail;
@@ -3240,12 +3445,15 @@ reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides,
PyUFuncObject *ufunc = (PyUFuncObject *)data;
char *dataptrs_copy[3];
npy_intp strides_copy[3];
+ npy_bool masked;
/* The normal selected inner loop */
PyUFuncGenericFunction innerloop = NULL;
void *innerloopdata = NULL;
NPY_BEGIN_THREADS_DEF;
+ /* Get the number of operands, to determine whether "where" is used */
+ masked = (NpyIter_GetNOp(iter) == 3);
/* Get the inner loop */
iter_dtypes = NpyIter_GetDescrArray(iter);
@@ -3305,8 +3513,36 @@ reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides,
strides_copy[0] = strides[0];
strides_copy[1] = strides[1];
strides_copy[2] = strides[0];
- innerloop(dataptrs_copy, countptr,
- strides_copy, innerloopdata);
+
+ if (!masked) {
+ innerloop(dataptrs_copy, countptr,
+ strides_copy, innerloopdata);
+ }
+ else {
+ npy_intp count = *countptr;
+ char *maskptr = dataptrs[2];
+ npy_intp mask_stride = strides[2];
+ /* Optimization for when the mask is broadcast */
+ npy_intp n = mask_stride == 0 ? count : 1;
+ while (count) {
+ char mask = *maskptr;
+ maskptr += mask_stride;
+ while (n < count && mask == *maskptr) {
+ n++;
+ maskptr += mask_stride;
+ }
+ /* If mask set, apply inner loop on this contiguous region */
+ if (mask) {
+ innerloop(dataptrs_copy, &n,
+ strides_copy, innerloopdata);
+ }
+ dataptrs_copy[0] += n * strides[0];
+ dataptrs_copy[1] += n * strides[1];
+ dataptrs_copy[2] = dataptrs_copy[0];
+ count -= n;
+ n = 1;
+ }
+ }
} while (iternext(iter));
finish_loop:
@@ -3335,7 +3571,7 @@ finish_loop:
static PyArrayObject *
PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out,
int naxes, int *axes, PyArray_Descr *odtype, int keepdims,
- PyObject *initial)
+ PyObject *initial, PyArrayObject *wheremask)
{
int iaxes, ndim;
npy_bool reorderable;
@@ -3401,7 +3637,7 @@ PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out,
return NULL;
}
- result = PyUFunc_ReduceWrapper(arr, out, NULL, dtype, dtype,
+ result = PyUFunc_ReduceWrapper(arr, out, wheremask, dtype, dtype,
NPY_UNSAFE_CASTING,
axis_flags, reorderable,
keepdims, 0,
@@ -3794,14 +4030,14 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind,
int *op_axes[3] = {op_axes_arrays[0], op_axes_arrays[1],
op_axes_arrays[2]};
npy_uint32 op_flags[3];
- int i, idim, ndim, otype_final;
+ int idim, ndim, otype_final;
int need_outer_iterator = 0;
NpyIter *iter = NULL;
/* The reduceat indices - ind must be validated outside this call */
npy_intp *reduceat_ind;
- npy_intp ind_size, red_axis_size;
+ npy_intp i, ind_size, red_axis_size;
/* The selected inner loop */
PyUFuncGenericFunction innerloop = NULL;
void *innerloopdata = NULL;
@@ -3887,7 +4123,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind,
#endif
/* Set up the op_axes for the outer loop */
- for (i = 0, idim = 0; idim < ndim; ++idim) {
+ for (idim = 0; idim < ndim; ++idim) {
/* Use the i-th iteration dimension to match up ind */
if (idim == axis) {
op_axes_arrays[0][idim] = axis;
@@ -4158,7 +4394,7 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
int i, naxes=0, ndim;
int axes[NPY_MAXDIMS];
PyObject *axes_in = NULL;
- PyArrayObject *mp = NULL, *ret = NULL;
+ PyArrayObject *mp = NULL, *wheremask = NULL, *ret = NULL;
PyObject *op;
PyObject *obj_ind, *context;
PyArrayObject *indices = NULL;
@@ -4167,7 +4403,7 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
int keepdims = 0;
PyObject *initial = NULL;
static char *reduce_kwlist[] = {
- "array", "axis", "dtype", "out", "keepdims", "initial", NULL};
+ "array", "axis", "dtype", "out", "keepdims", "initial", "where", NULL};
static char *accumulate_kwlist[] = {
"array", "axis", "dtype", "out", NULL};
static char *reduceat_kwlist[] = {
@@ -4230,22 +4466,23 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
}
else if (operation == UFUNC_ACCUMULATE) {
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO&O&:accumulate",
- accumulate_kwlist,
- &op,
- &axes_in,
- PyArray_DescrConverter2, &otype,
- PyArray_OutputConverter, &out)) {
+ accumulate_kwlist,
+ &op,
+ &axes_in,
+ PyArray_DescrConverter2, &otype,
+ PyArray_OutputConverter, &out)) {
goto fail;
}
}
else {
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO&O&iO:reduce",
- reduce_kwlist,
- &op,
- &axes_in,
- PyArray_DescrConverter2, &otype,
- PyArray_OutputConverter, &out,
- &keepdims, &initial)) {
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO&O&iOO&:reduce",
+ reduce_kwlist,
+ &op,
+ &axes_in,
+ PyArray_DescrConverter2, &otype,
+ PyArray_OutputConverter, &out,
+ &keepdims, &initial,
+ _wheremask_converter, &wheremask)) {
goto fail;
}
}
@@ -4275,11 +4512,17 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
/* Convert the 'axis' parameter into a list of axes */
if (axes_in == NULL) {
- naxes = 1;
- axes[0] = 0;
+ /* apply defaults */
+ if (ndim == 0) {
+ naxes = 0;
+ }
+ else {
+ naxes = 1;
+ axes[0] = 0;
+ }
}
- /* Convert 'None' into all the axes */
else if (axes_in == Py_None) {
+ /* Convert 'None' into all the axes */
naxes = ndim;
for (i = 0; i < naxes; ++i) {
axes[i] = i;
@@ -4304,40 +4547,28 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
axes[i] = (int)axis;
}
}
- /* Try to interpret axis as an integer */
else {
+ /* Try to interpret axis as an integer */
int axis = PyArray_PyIntAsInt(axes_in);
/* TODO: PyNumber_Index would be good to use here */
if (error_converting(axis)) {
goto fail;
}
- /* Special case letting axis={0 or -1} slip through for scalars */
- if (ndim == 0 && (axis == 0 || axis == -1)) {
- axis = 0;
- }
- else if (check_and_adjust_axis(&axis, ndim) < 0) {
- goto fail;
- }
- axes[0] = (int)axis;
- naxes = 1;
- }
-
- /* Check to see if input is zero-dimensional. */
- if (ndim == 0) {
/*
- * A reduction with no axes is still valid but trivial.
* As a special case for backwards compatibility in 'sum',
- * 'prod', et al, also allow a reduction where axis=0, even
+ * 'prod', et al, also allow a reduction for scalars even
* though this is technically incorrect.
*/
- naxes = 0;
-
- if (!(operation == UFUNC_REDUCE &&
- (naxes == 0 || (naxes == 1 && axes[0] == 0)))) {
- PyErr_Format(PyExc_TypeError, "cannot %s on a scalar",
- _reduce_type[operation]);
+ if (ndim == 0 && (axis == 0 || axis == -1)) {
+ naxes = 0;
+ }
+ else if (check_and_adjust_axis(&axis, ndim) < 0) {
goto fail;
}
+ else {
+ axes[0] = (int)axis;
+ naxes = 1;
+ }
}
/*
@@ -4376,9 +4607,14 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
switch(operation) {
case UFUNC_REDUCE:
ret = PyUFunc_Reduce(ufunc, mp, out, naxes, axes,
- otype, keepdims, initial);
+ otype, keepdims, initial, wheremask);
+ Py_XDECREF(wheremask);
break;
case UFUNC_ACCUMULATE:
+ if (ndim == 0) {
+ PyErr_SetString(PyExc_TypeError, "cannot accumulate on a scalar");
+ goto fail;
+ }
if (naxes != 1) {
PyErr_SetString(PyExc_ValueError,
"accumulate does not allow multiple axes");
@@ -4388,6 +4624,10 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
otype->type_num);
break;
case UFUNC_REDUCEAT:
+ if (ndim == 0) {
+ PyErr_SetString(PyExc_TypeError, "cannot reduceat on a scalar");
+ goto fail;
+ }
if (naxes != 1) {
PyErr_SetString(PyExc_ValueError,
"reduceat does not allow multiple axes");
@@ -4434,6 +4674,7 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
fail:
Py_XDECREF(otype);
Py_XDECREF(mp);
+ Py_XDECREF(wheremask);
return NULL;
}
@@ -4502,6 +4743,9 @@ ufunc_generic_call(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
wrapped = _apply_array_wrap(wraparr[i], mps[j], &context);
mps[j] = NULL; /* Prevent fail double-freeing this */
if (wrapped == NULL) {
+ for (j = 0; j < i; j++) {
+ Py_DECREF(retobj[j]);
+ }
goto fail;
}
@@ -4599,7 +4843,7 @@ ufunc_seterr(PyObject *NPY_UNUSED(dummy), PyObject *args)
NPY_NO_EXPORT int
PyUFunc_ReplaceLoopBySignature(PyUFuncObject *func,
PyUFuncGenericFunction newfunc,
- int *signature,
+ const int *signature,
PyUFuncGenericFunction *oldfunc)
{
int i, j;
@@ -4643,8 +4887,21 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
const char *name, const char *doc,
int unused, const char *signature)
{
- PyUFuncObject *ufunc;
+ return PyUFunc_FromFuncAndDataAndSignatureAndIdentity(
+ func, data, types, ntypes, nin, nout, identity, name, doc,
+ unused, signature, NULL);
+}
+/*UFUNC_API*/
+NPY_NO_EXPORT PyObject *
+PyUFunc_FromFuncAndDataAndSignatureAndIdentity(PyUFuncGenericFunction *func, void **data,
+ char *types, int ntypes,
+ int nin, int nout, int identity,
+ const char *name, const char *doc,
+ const int unused, const char *signature,
+ PyObject *identity_value)
+{
+ PyUFuncObject *ufunc;
if (nin + nout > NPY_MAXARGS) {
PyErr_Format(PyExc_ValueError,
"Cannot construct a ufunc with more than %d operands "
@@ -4653,27 +4910,46 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
return NULL;
}
- ufunc = PyArray_malloc(sizeof(PyUFuncObject));
+ ufunc = PyObject_GC_New(PyUFuncObject, &PyUFunc_Type);
+ /*
+ * We use GC_New here for ufunc->obj, but do not use GC_Track since
+ * ufunc->obj is still NULL at the end of this function.
+ * See ufunc_frompyfunc where ufunc->obj is set and GC_Track is called.
+ */
if (ufunc == NULL) {
return NULL;
}
- PyObject_Init((PyObject *)ufunc, &PyUFunc_Type);
-
- ufunc->reserved1 = 0;
- ufunc->reserved2 = NULL;
ufunc->nin = nin;
ufunc->nout = nout;
ufunc->nargs = nin+nout;
ufunc->identity = identity;
+ if (ufunc->identity == PyUFunc_IdentityValue) {
+ Py_INCREF(identity_value);
+ ufunc->identity_value = identity_value;
+ }
+ else {
+ ufunc->identity_value = NULL;
+ }
ufunc->functions = func;
ufunc->data = data;
ufunc->types = types;
ufunc->ntypes = ntypes;
- ufunc->ptr = NULL;
+ ufunc->core_signature = NULL;
+ ufunc->core_enabled = 0;
ufunc->obj = NULL;
- ufunc->userloops=NULL;
+ ufunc->core_num_dims = NULL;
+ ufunc->core_num_dim_ix = 0;
+ ufunc->core_offsets = NULL;
+ ufunc->core_dim_ixs = NULL;
+ ufunc->core_dim_sizes = NULL;
+ ufunc->core_dim_flags = NULL;
+ ufunc->userloops = NULL;
+ ufunc->ptr = NULL;
+ ufunc->reserved2 = NULL;
+ ufunc->reserved1 = 0;
+ ufunc->iter_flags = 0;
/* Type resolution and inner loop selection functions */
ufunc->type_resolver = &PyUFunc_DefaultTypeResolver;
@@ -4690,19 +4966,11 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
ufunc->op_flags = PyArray_malloc(sizeof(npy_uint32)*ufunc->nargs);
if (ufunc->op_flags == NULL) {
+ Py_DECREF(ufunc);
return PyErr_NoMemory();
}
memset(ufunc->op_flags, 0, sizeof(npy_uint32)*ufunc->nargs);
- ufunc->iter_flags = 0;
-
- /* generalized ufunc */
- ufunc->core_enabled = 0;
- ufunc->core_num_dim_ix = 0;
- ufunc->core_num_dims = NULL;
- ufunc->core_dim_ixs = NULL;
- ufunc->core_offsets = NULL;
- ufunc->core_signature = NULL;
if (signature != NULL) {
if (_parse_signature(ufunc, signature) != 0) {
Py_DECREF(ufunc);
@@ -4824,11 +5092,14 @@ _loop1d_list_free(void *ptr)
* instead of dtype type num values. This allows a 1-d loop to be registered
* for a structured array dtype or a custom dtype. The ufunc is called
* whenever any of it's input arguments match the user_dtype argument.
- * ufunc - ufunc object created from call to PyUFunc_FromFuncAndData
+ *
+ * ufunc - ufunc object created from call to PyUFunc_FromFuncAndData
* user_dtype - dtype that ufunc will be registered with
- * function - 1-d loop function pointer
+ * function - 1-d loop function pointer
* arg_dtypes - array of dtype objects describing the ufunc operands
- * data - arbitrary data pointer passed in to loop function
+ * data - arbitrary data pointer passed in to loop function
+ *
+ * returns 0 on success, -1 for failure
*/
/*UFUNC_API*/
NPY_NO_EXPORT int
@@ -4892,7 +5163,7 @@ PyUFunc_RegisterLoopForDescr(PyUFuncObject *ufunc,
}
current = current->next;
}
- if (cmp == 0 && current->arg_dtypes == NULL) {
+ if (cmp == 0 && current != NULL && current->arg_dtypes == NULL) {
current->arg_dtypes = PyArray_malloc(ufunc->nargs *
sizeof(PyArray_Descr*));
if (arg_dtypes != NULL) {
@@ -4910,6 +5181,8 @@ PyUFunc_RegisterLoopForDescr(PyUFuncObject *ufunc,
current->nargs = ufunc->nargs;
}
else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "loop already registered");
result = -1;
}
}
@@ -4927,7 +5200,7 @@ NPY_NO_EXPORT int
PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc,
int usertype,
PyUFuncGenericFunction function,
- int *arg_types,
+ const int *arg_types,
void *data)
{
PyArray_Descr *descr;
@@ -5047,15 +5320,23 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc,
static void
ufunc_dealloc(PyUFuncObject *ufunc)
{
+ PyObject_GC_UnTrack((PyObject *)ufunc);
PyArray_free(ufunc->core_num_dims);
PyArray_free(ufunc->core_dim_ixs);
+ PyArray_free(ufunc->core_dim_sizes);
+ PyArray_free(ufunc->core_dim_flags);
PyArray_free(ufunc->core_offsets);
PyArray_free(ufunc->core_signature);
PyArray_free(ufunc->ptr);
PyArray_free(ufunc->op_flags);
Py_XDECREF(ufunc->userloops);
- Py_XDECREF(ufunc->obj);
- PyArray_free(ufunc);
+ if (ufunc->identity == PyUFunc_IdentityValue) {
+ Py_DECREF(ufunc->identity_value);
+ }
+ if (ufunc->obj != NULL) {
+ Py_DECREF(ufunc->obj);
+ }
+ PyObject_GC_Del(ufunc);
}
static PyObject *
@@ -5064,6 +5345,15 @@ ufunc_repr(PyUFuncObject *ufunc)
return PyUString_FromFormat("<ufunc '%s'>", ufunc->name);
}
+static int
+ufunc_traverse(PyUFuncObject *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->obj);
+ if (self->identity == PyUFunc_IdentityValue) {
+ Py_VISIT(self->identity_value);
+ }
+ return 0;
+}
/******************************************************************************
*** UFUNC METHODS ***
@@ -5086,6 +5376,8 @@ ufunc_outer(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
PyArrayObject *ap1 = NULL, *ap2 = NULL, *ap_new = NULL;
PyObject *new_args, *tmp;
PyObject *shape1, *shape2, *newshape;
+ static PyObject *_numpy_matrix;
+
errval = PyUFunc_CheckOverride(ufunc, "outer", args, kwds, &override);
if (errval) {
@@ -5118,7 +5410,18 @@ ufunc_outer(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
if (tmp == NULL) {
return NULL;
}
- ap1 = (PyArrayObject *) PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0);
+
+ npy_cache_import(
+ "numpy",
+ "matrix",
+ &_numpy_matrix);
+
+ if (PyObject_IsInstance(tmp, _numpy_matrix)) {
+ ap1 = (PyArrayObject *) PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0);
+ }
+ else {
+ ap1 = (PyArrayObject *) PyArray_FROM_O(tmp);
+ }
Py_DECREF(tmp);
if (ap1 == NULL) {
return NULL;
@@ -5127,7 +5430,12 @@ ufunc_outer(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
if (tmp == NULL) {
return NULL;
}
- ap2 = (PyArrayObject *)PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0);
+ if (PyObject_IsInstance(tmp, _numpy_matrix)) {
+ ap2 = (PyArrayObject *) PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0);
+ }
+ else {
+ ap2 = (PyArrayObject *) PyArray_FROM_O(tmp);
+ }
Py_DECREF(tmp);
if (ap2 == NULL) {
Py_DECREF(ap1);
@@ -5264,7 +5572,7 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args)
PyUFuncGenericFunction innerloop;
void *innerloopdata;
- int i;
+ npy_intp i;
int nop;
/* override vars */
@@ -5365,18 +5673,13 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args)
* Create dtypes array for either one or two input operands.
* The output operand is set to the first input operand
*/
- dtypes[0] = PyArray_DESCR(op1_array);
operands[0] = op1_array;
if (op2_array != NULL) {
- dtypes[1] = PyArray_DESCR(op2_array);
- dtypes[2] = dtypes[0];
operands[1] = op2_array;
operands[2] = op1_array;
nop = 3;
}
else {
- dtypes[1] = dtypes[0];
- dtypes[2] = NULL;
operands[1] = op1_array;
operands[2] = NULL;
nop = 2;
@@ -5533,9 +5836,10 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args)
Py_XDECREF(op2_array);
Py_XDECREF(iter);
Py_XDECREF(iter2);
- Py_XDECREF(array_operands[0]);
- Py_XDECREF(array_operands[1]);
- Py_XDECREF(array_operands[2]);
+ for (i = 0; i < 3; i++) {
+ Py_XDECREF(dtypes[i]);
+ Py_XDECREF(array_operands[i]);
+ }
if (needs_api && PyErr_Occurred()) {
return NULL;
@@ -5552,9 +5856,10 @@ fail:
Py_XDECREF(op2_array);
Py_XDECREF(iter);
Py_XDECREF(iter2);
- Py_XDECREF(array_operands[0]);
- Py_XDECREF(array_operands[1]);
- Py_XDECREF(array_operands[2]);
+ for (i = 0; i < 3; i++) {
+ Py_XDECREF(dtypes[i]);
+ Py_XDECREF(array_operands[i]);
+ }
return NULL;
}
@@ -5780,9 +6085,9 @@ NPY_NO_EXPORT PyTypeObject PyUFunc_Type = {
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
- 0, /* tp_traverse */
+ (traverseproc)ufunc_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c
index 807b03512..9be7b63a0 100644
--- a/numpy/core/src/umath/ufunc_type_resolution.c
+++ b/numpy/core/src/umath/ufunc_type_resolution.c
@@ -12,15 +12,44 @@
#define _MULTIARRAYMODULE
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+#include <stdbool.h>
+
#include "Python.h"
#include "npy_config.h"
#include "npy_pycompat.h"
+#include "npy_import.h"
#include "numpy/ufuncobject.h"
#include "ufunc_type_resolution.h"
#include "ufunc_object.h"
#include "common.h"
+#include "convert_datatype.h"
+
+#include "mem_overlap.h"
+#if defined(HAVE_CBLAS)
+#include "cblasfuncs.h"
+#endif
+
+static PyObject *
+npy_casting_to_py_object(NPY_CASTING casting)
+{
+ switch (casting) {
+ case NPY_NO_CASTING:
+ return PyUString_FromString("no");
+ case NPY_EQUIV_CASTING:
+ return PyUString_FromString("equiv");
+ case NPY_SAFE_CASTING:
+ return PyUString_FromString("safe");
+ case NPY_SAME_KIND_CASTING:
+ return PyUString_FromString("same_kind");
+ case NPY_UNSAFE_CASTING:
+ return PyUString_FromString("unsafe");
+ default:
+ return PyInt_FromLong(casting);
+ }
+}
+
static const char *
npy_casting_to_string(NPY_CASTING casting)
@@ -40,6 +69,158 @@ npy_casting_to_string(NPY_CASTING casting)
return "<unknown>";
}
}
+
+/**
+ * Always returns -1 to indicate the exception was raised, for convenience
+ */
+static int
+raise_binary_type_reso_error(PyUFuncObject *ufunc, PyArrayObject **operands) {
+ static PyObject *exc_type = NULL;
+ PyObject *exc_value;
+
+ npy_cache_import(
+ "numpy.core._exceptions", "_UFuncBinaryResolutionError",
+ &exc_type);
+ if (exc_type == NULL) {
+ return -1;
+ }
+
+ /* produce an error object */
+ exc_value = Py_BuildValue(
+ "O(OO)", ufunc,
+ (PyObject *)PyArray_DESCR(operands[0]),
+ (PyObject *)PyArray_DESCR(operands[1])
+ );
+ if (exc_value == NULL){
+ return -1;
+ }
+ PyErr_SetObject(exc_type, exc_value);
+ Py_DECREF(exc_value);
+
+ return -1;
+}
+
+/** Helper function to raise UFuncNoLoopError
+ * Always returns -1 to indicate the exception was raised, for convenience
+ */
+static int
+raise_no_loop_found_error(
+ PyUFuncObject *ufunc, PyArray_Descr **dtypes)
+{
+ static PyObject *exc_type = NULL;
+ PyObject *exc_value;
+ PyObject *dtypes_tup;
+ npy_intp i;
+
+ npy_cache_import(
+ "numpy.core._exceptions", "_UFuncNoLoopError",
+ &exc_type);
+ if (exc_type == NULL) {
+ return -1;
+ }
+
+ /* convert dtypes to a tuple */
+ dtypes_tup = PyTuple_New(ufunc->nargs);
+ if (dtypes_tup == NULL) {
+ return -1;
+ }
+ for (i = 0; i < ufunc->nargs; ++i) {
+ Py_INCREF(dtypes[i]);
+ PyTuple_SET_ITEM(dtypes_tup, i, (PyObject *)dtypes[i]);
+ }
+
+ /* produce an error object */
+ exc_value = PyTuple_Pack(2, ufunc, dtypes_tup);
+ Py_DECREF(dtypes_tup);
+ if (exc_value == NULL){
+ return -1;
+ }
+ PyErr_SetObject(exc_type, exc_value);
+ Py_DECREF(exc_value);
+
+ return -1;
+}
+
+static int
+raise_casting_error(
+ PyObject *exc_type,
+ PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArray_Descr *from,
+ PyArray_Descr *to,
+ npy_intp i)
+{
+ PyObject *exc_value;
+ PyObject *casting_value;
+
+ casting_value = npy_casting_to_py_object(casting);
+ if (casting_value == NULL) {
+ return -1;
+ }
+
+ exc_value = Py_BuildValue(
+ "ONOOi",
+ ufunc,
+ casting_value,
+ (PyObject *)from,
+ (PyObject *)to,
+ i
+ );
+ if (exc_value == NULL){
+ return -1;
+ }
+ PyErr_SetObject(exc_type, exc_value);
+ Py_DECREF(exc_value);
+
+ return -1;
+}
+
+/** Helper function to raise UFuncInputCastingError
+ * Always returns -1 to indicate the exception was raised, for convenience
+ */
+static int
+raise_input_casting_error(
+ PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArray_Descr *from,
+ PyArray_Descr *to,
+ npy_intp i)
+{
+ static PyObject *exc_type = NULL;
+ npy_cache_import(
+ "numpy.core._exceptions", "_UFuncInputCastingError",
+ &exc_type);
+ if (exc_type == NULL) {
+ return -1;
+ }
+
+ return raise_casting_error(exc_type, ufunc, casting, from, to, i);
+}
+
+
+/** Helper function to raise UFuncOutputCastingError
+ * Always returns -1 to indicate the exception was raised, for convenience
+ */
+static int
+raise_output_casting_error(
+ PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArray_Descr *from,
+ PyArray_Descr *to,
+ npy_intp i)
+{
+ static PyObject *exc_type = NULL;
+ npy_cache_import(
+ "numpy.core._exceptions", "_UFuncOutputCastingError",
+ &exc_type);
+ if (exc_type == NULL) {
+ return -1;
+ }
+
+ return raise_casting_error(exc_type, ufunc, casting, from, to, i);
+}
+
+
/*UFUNC_API
*
* Validates that the input operands can be cast to
@@ -55,45 +236,18 @@ PyUFunc_ValidateCasting(PyUFuncObject *ufunc,
PyArray_Descr **dtypes)
{
int i, nin = ufunc->nin, nop = nin + ufunc->nout;
- const char *ufunc_name = ufunc_get_name_cstr(ufunc);
for (i = 0; i < nop; ++i) {
if (i < nin) {
if (!PyArray_CanCastArrayTo(operands[i], dtypes[i], casting)) {
- PyObject *errmsg;
- errmsg = PyUString_FromFormat("Cannot cast ufunc %s "
- "input from ", ufunc_name);
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[i])));
- PyUString_ConcatAndDel(&errmsg,
- PyUString_FromString(" to "));
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)dtypes[i]));
- PyUString_ConcatAndDel(&errmsg,
- PyUString_FromFormat(" with casting rule %s",
- npy_casting_to_string(casting)));
- PyErr_SetObject(PyExc_TypeError, errmsg);
- Py_DECREF(errmsg);
- return -1;
+ return raise_input_casting_error(
+ ufunc, casting, PyArray_DESCR(operands[i]), dtypes[i], i);
}
} else if (operands[i] != NULL) {
if (!PyArray_CanCastTypeTo(dtypes[i],
PyArray_DESCR(operands[i]), casting)) {
- PyObject *errmsg;
- errmsg = PyUString_FromFormat("Cannot cast ufunc %s "
- "output from ", ufunc_name);
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)dtypes[i]));
- PyUString_ConcatAndDel(&errmsg,
- PyUString_FromString(" to "));
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[i])));
- PyUString_ConcatAndDel(&errmsg,
- PyUString_FromFormat(" with casting rule %s",
- npy_casting_to_string(casting)));
- PyErr_SetObject(PyExc_TypeError, errmsg);
- Py_DECREF(errmsg);
- return -1;
+ return raise_output_casting_error(
+ ufunc, casting, dtypes[i], PyArray_DESCR(operands[i]), i);
}
}
}
@@ -266,99 +420,6 @@ PyUFunc_SimpleBinaryComparisonTypeResolver(PyUFuncObject *ufunc,
return 0;
}
-/*
- * This function applies special type resolution rules for the case
- * where all the functions have the pattern X->X, copying
- * the input descr directly so that metadata is maintained.
- *
- * Note that a simpler linear search through the functions loop
- * is still done, but switching to a simple array lookup for
- * built-in types would be better at some point.
- *
- * Returns 0 on success, -1 on error.
- */
-NPY_NO_EXPORT int
-PyUFunc_SimpleUnaryOperationTypeResolver(PyUFuncObject *ufunc,
- NPY_CASTING casting,
- PyArrayObject **operands,
- PyObject *type_tup,
- PyArray_Descr **out_dtypes)
-{
- int i, type_num1;
- const char *ufunc_name = ufunc_get_name_cstr(ufunc);
-
- if (ufunc->nin != 1 || ufunc->nout != 1) {
- PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured "
- "to use unary operation type resolution but has "
- "the wrong number of inputs or outputs",
- ufunc_name);
- return -1;
- }
-
- /*
- * Use the default type resolution if there's a custom data type
- * or object arrays.
- */
- type_num1 = PyArray_DESCR(operands[0])->type_num;
- if (type_num1 >= NPY_NTYPES || type_num1 == NPY_OBJECT) {
- return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
- type_tup, out_dtypes);
- }
-
- if (type_tup == NULL) {
- /* Input types are the result type */
- out_dtypes[0] = ensure_dtype_nbo(PyArray_DESCR(operands[0]));
- if (out_dtypes[0] == NULL) {
- return -1;
- }
- out_dtypes[1] = out_dtypes[0];
- Py_INCREF(out_dtypes[1]);
- }
- else {
- PyObject *item;
- PyArray_Descr *dtype = NULL;
-
- /*
- * If the type tuple isn't a single-element tuple, let the
- * default type resolution handle this one.
- */
- if (!PyTuple_Check(type_tup) || PyTuple_GET_SIZE(type_tup) != 1) {
- return PyUFunc_DefaultTypeResolver(ufunc, casting,
- operands, type_tup, out_dtypes);
- }
-
- item = PyTuple_GET_ITEM(type_tup, 0);
-
- if (item == Py_None) {
- PyErr_SetString(PyExc_ValueError,
- "require data type in the type tuple");
- return -1;
- }
- else if (!PyArray_DescrConverter(item, &dtype)) {
- return -1;
- }
-
- out_dtypes[0] = ensure_dtype_nbo(dtype);
- if (out_dtypes[0] == NULL) {
- return -1;
- }
- out_dtypes[1] = out_dtypes[0];
- Py_INCREF(out_dtypes[1]);
- }
-
- /* Check against the casting rules */
- if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
- for (i = 0; i < 2; ++i) {
- Py_DECREF(out_dtypes[i]);
- out_dtypes[i] = NULL;
- }
- return -1;
- }
-
- return 0;
-}
-
-
NPY_NO_EXPORT int
PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
@@ -367,7 +428,7 @@ PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc,
PyArray_Descr **out_dtypes)
{
int ret;
- ret = PyUFunc_SimpleUnaryOperationTypeResolver(ufunc, casting, operands,
+ ret = PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
if (ret < 0) {
return ret;
@@ -397,16 +458,15 @@ PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
- return PyUFunc_SimpleUnaryOperationTypeResolver(ufunc,
+ return PyUFunc_SimpleUniformOperationTypeResolver(ufunc,
NPY_UNSAFE_CASTING,
operands, type_tup, out_dtypes);
}
-
/*
* This function applies special type resolution rules for the case
- * where all the functions have the pattern XX->X, using
- * PyArray_ResultType instead of a linear search to get the best
+ * where all of the types in the signature are the same, eg XX->X or XX->XX.
+ * It uses PyArray_ResultType instead of a linear search to get the best
* loop.
*
* Note that a simpler linear search through the functions loop
@@ -416,45 +476,52 @@ PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc,
* Returns 0 on success, -1 on error.
*/
NPY_NO_EXPORT int
-PyUFunc_SimpleBinaryOperationTypeResolver(PyUFuncObject *ufunc,
- NPY_CASTING casting,
- PyArrayObject **operands,
- PyObject *type_tup,
- PyArray_Descr **out_dtypes)
+PyUFunc_SimpleUniformOperationTypeResolver(
+ PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes)
{
- int i, type_num1, type_num2;
const char *ufunc_name = ufunc_get_name_cstr(ufunc);
- if (ufunc->nin != 2 || ufunc->nout != 1) {
+ if (ufunc->nin < 1) {
PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured "
- "to use binary operation type resolution but has "
- "the wrong number of inputs or outputs",
+ "to use uniform operation type resolution but has "
+ "no inputs",
ufunc_name);
return -1;
}
+ int nop = ufunc->nin + ufunc->nout;
/*
- * Use the default type resolution if there's a custom data type
- * or object arrays.
+ * There's a custom data type or an object array
*/
- type_num1 = PyArray_DESCR(operands[0])->type_num;
- type_num2 = PyArray_DESCR(operands[1])->type_num;
- if (type_num1 >= NPY_NTYPES || type_num2 >= NPY_NTYPES ||
- type_num1 == NPY_OBJECT || type_num2 == NPY_OBJECT) {
+ bool has_custom_or_object = false;
+ for (int iop = 0; iop < ufunc->nin; iop++) {
+ int type_num = PyArray_DESCR(operands[iop])->type_num;
+ if (type_num >= NPY_NTYPES || type_num == NPY_OBJECT) {
+ has_custom_or_object = true;
+ break;
+ }
+ }
+
+ if (has_custom_or_object) {
return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
type_tup, out_dtypes);
}
if (type_tup == NULL) {
- /* Input types are the result type */
- out_dtypes[0] = PyArray_ResultType(2, operands, 0, NULL);
+ /* PyArray_ResultType forgets to force a byte order when n == 1 */
+ if (ufunc->nin == 1){
+ out_dtypes[0] = ensure_dtype_nbo(PyArray_DESCR(operands[0]));
+ }
+ else {
+ out_dtypes[0] = PyArray_ResultType(ufunc->nin, operands, 0, NULL);
+ }
if (out_dtypes[0] == NULL) {
return -1;
}
- out_dtypes[1] = out_dtypes[0];
- Py_INCREF(out_dtypes[1]);
- out_dtypes[2] = out_dtypes[0];
- Py_INCREF(out_dtypes[2]);
}
else {
PyObject *item;
@@ -481,20 +548,23 @@ PyUFunc_SimpleBinaryOperationTypeResolver(PyUFuncObject *ufunc,
}
out_dtypes[0] = ensure_dtype_nbo(dtype);
+ Py_DECREF(dtype);
if (out_dtypes[0] == NULL) {
return -1;
}
- out_dtypes[1] = out_dtypes[0];
- Py_INCREF(out_dtypes[1]);
- out_dtypes[2] = out_dtypes[0];
- Py_INCREF(out_dtypes[2]);
+ }
+
+ /* All types are the same - copy the first one to the rest */
+ for (int iop = 1; iop < nop; iop++) {
+ out_dtypes[iop] = out_dtypes[0];
+ Py_INCREF(out_dtypes[iop]);
}
/* Check against the casting rules */
if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
- for (i = 0; i < 3; ++i) {
- Py_DECREF(out_dtypes[i]);
- out_dtypes[i] = NULL;
+ for (int iop = 0; iop < nop; iop++) {
+ Py_DECREF(out_dtypes[iop]);
+ out_dtypes[iop] = NULL;
}
return -1;
}
@@ -522,7 +592,7 @@ PyUFunc_AbsoluteTypeResolver(PyUFuncObject *ufunc,
type_tup, out_dtypes);
}
else {
- return PyUFunc_SimpleUnaryOperationTypeResolver(ufunc, casting,
+ return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
}
}
@@ -553,6 +623,26 @@ PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc,
return 0;
}
+
+NPY_NO_EXPORT int
+PyUFunc_IsFiniteTypeResolver(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes)
+{
+ if (!PyTypeNum_ISDATETIME(PyArray_DESCR(operands[0])->type_num)) {
+ return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
+ type_tup, out_dtypes);
+ }
+
+ out_dtypes[0] = ensure_dtype_nbo(PyArray_DESCR(operands[0]));
+ out_dtypes[1] = PyArray_DescrFromType(NPY_BOOL);
+
+ return 0;
+}
+
+
/*
* Creates a new NPY_TIMEDELTA dtype, copying the datetime metadata
* from the given dtype.
@@ -605,14 +695,13 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
{
int type_num1, type_num2;
int i;
- const char *ufunc_name = ufunc_get_name_cstr(ufunc);
type_num1 = PyArray_DESCR(operands[0])->type_num;
type_num2 = PyArray_DESCR(operands[1])->type_num;
/* Use the default when datetime and timedelta are not involved */
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
- return PyUFunc_SimpleBinaryOperationTypeResolver(ufunc, casting,
+ return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
}
@@ -661,7 +750,7 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
type_num2 = NPY_TIMEDELTA;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else if (type_num1 == NPY_DATETIME) {
@@ -703,7 +792,7 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
type_num2 = NPY_TIMEDELTA;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
@@ -739,11 +828,11 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
type_num1 = NPY_TIMEDELTA;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
/* Check against the casting rules */
@@ -756,21 +845,6 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
}
return 0;
-
-type_reso_error: {
- PyObject *errmsg;
- errmsg = PyUString_FromFormat("ufunc %s cannot use operands "
- "with types ", ufunc_name);
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[0])));
- PyUString_ConcatAndDel(&errmsg,
- PyUString_FromString(" and "));
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[1])));
- PyErr_SetObject(PyExc_TypeError, errmsg);
- Py_DECREF(errmsg);
- return -1;
- }
}
/*
@@ -793,7 +867,6 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
{
int type_num1, type_num2;
int i;
- const char *ufunc_name = ufunc_get_name_cstr(ufunc);
type_num1 = PyArray_DESCR(operands[0])->type_num;
type_num2 = PyArray_DESCR(operands[1])->type_num;
@@ -801,7 +874,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
/* Use the default when datetime and timedelta are not involved */
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
int ret;
- ret = PyUFunc_SimpleBinaryOperationTypeResolver(ufunc, casting,
+ ret = PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
if (ret < 0) {
return ret;
@@ -846,7 +919,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
type_num2 = NPY_TIMEDELTA;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else if (type_num1 == NPY_DATETIME) {
@@ -904,7 +977,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
Py_INCREF(out_dtypes[1]);
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
@@ -922,11 +995,11 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
type_num1 = NPY_TIMEDELTA;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
/* Check against the casting rules */
@@ -939,21 +1012,6 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
}
return 0;
-
-type_reso_error: {
- PyObject *errmsg;
- errmsg = PyUString_FromFormat("ufunc %s cannot use operands "
- "with types ", ufunc_name);
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[0])));
- PyUString_ConcatAndDel(&errmsg,
- PyUString_FromString(" and "));
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[1])));
- PyErr_SetObject(PyExc_TypeError, errmsg);
- Py_DECREF(errmsg);
- return -1;
- }
}
/*
@@ -973,14 +1031,13 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc,
{
int type_num1, type_num2;
int i;
- const char *ufunc_name = ufunc_get_name_cstr(ufunc);
type_num1 = PyArray_DESCR(operands[0])->type_num;
type_num2 = PyArray_DESCR(operands[1])->type_num;
/* Use the default when datetime and timedelta are not involved */
if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
- return PyUFunc_SimpleBinaryOperationTypeResolver(ufunc, casting,
+ return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting,
operands, type_tup, out_dtypes);
}
@@ -1020,7 +1077,7 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc,
type_num2 = NPY_DOUBLE;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) {
@@ -1042,7 +1099,7 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc,
type_num1 = NPY_LONGLONG;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else if (PyTypeNum_ISFLOAT(type_num1)) {
@@ -1064,11 +1121,11 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc,
type_num1 = NPY_DOUBLE;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
/* Check against the casting rules */
@@ -1081,21 +1138,6 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc,
}
return 0;
-
-type_reso_error: {
- PyObject *errmsg;
- errmsg = PyUString_FromFormat("ufunc %s cannot use operands "
- "with types ", ufunc_name);
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[0])));
- PyUString_ConcatAndDel(&errmsg,
- PyUString_FromString(" and "));
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[1])));
- PyErr_SetObject(PyExc_TypeError, errmsg);
- Py_DECREF(errmsg);
- return -1;
- }
}
@@ -1115,7 +1157,6 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc,
{
int type_num1, type_num2;
int i;
- const char *ufunc_name = ufunc_get_name_cstr(ufunc);
type_num1 = PyArray_DESCR(operands[0])->type_num;
type_num2 = PyArray_DESCR(operands[1])->type_num;
@@ -1139,7 +1180,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;
@@ -1183,11 +1233,11 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc,
type_num2 = NPY_DOUBLE;
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
}
else {
- goto type_reso_error;
+ return raise_binary_type_reso_error(ufunc, operands);
}
/* Check against the casting rules */
@@ -1200,21 +1250,57 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc,
}
return 0;
+}
+
+
+NPY_NO_EXPORT int
+PyUFunc_RemainderTypeResolver(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes)
+{
+ int type_num1, type_num2;
+ int i;
+
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ type_num2 = PyArray_DESCR(operands[1])->type_num;
+
+ /* Use the default when datetime and timedelta are not involved */
+ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
+ return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
+ type_tup, out_dtypes);
+ }
+ if (type_num1 == NPY_TIMEDELTA) {
+ if (type_num2 == NPY_TIMEDELTA) {
+ out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
+ PyArray_DESCR(operands[1]));
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = out_dtypes[0];
+ Py_INCREF(out_dtypes[2]);
+ }
+ else {
+ return raise_binary_type_reso_error(ufunc, operands);
+ }
+ }
+ else {
+ return raise_binary_type_reso_error(ufunc, operands);
+ }
-type_reso_error: {
- PyObject *errmsg;
- errmsg = PyUString_FromFormat("ufunc %s cannot use operands "
- "with types ", ufunc_name);
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[0])));
- PyUString_ConcatAndDel(&errmsg,
- PyUString_FromString(" and "));
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)PyArray_DESCR(operands[1])));
- PyErr_SetObject(PyExc_TypeError, errmsg);
- Py_DECREF(errmsg);
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 3; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
return -1;
}
+
+ return 0;
}
@@ -1275,7 +1361,7 @@ PyUFunc_MixedDivisionTypeResolver(PyUFuncObject *ufunc,
PyObject *type_tup,
PyArray_Descr **out_dtypes)
{
- /* Depreciation checks needed only on python 2 */
+ /* Deprecation checks needed only on python 2 */
#if !defined(NPY_PY3K)
int type_num1, type_num2;
@@ -1293,7 +1379,6 @@ PyUFunc_MixedDivisionTypeResolver(PyUFuncObject *ufunc,
type_tup, out_dtypes);
}
-
static int
find_userloop(PyUFuncObject *ufunc,
PyArray_Descr **dtypes,
@@ -1363,12 +1448,8 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc,
{
int nargs = ufunc->nargs;
char *types;
- const char *ufunc_name;
- PyObject *errmsg;
int i, j;
- ufunc_name = ufunc_get_name_cstr(ufunc);
-
/*
* If there are user-loops search them first.
* TODO: There needs to be a loop selection acceleration structure,
@@ -1403,19 +1484,7 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc,
types += nargs;
}
- errmsg = PyUString_FromFormat("ufunc '%s' did not contain a loop "
- "with signature matching types ", ufunc_name);
- for (i = 0; i < nargs; ++i) {
- PyUString_ConcatAndDel(&errmsg,
- PyObject_Repr((PyObject *)dtypes[i]));
- if (i < nargs - 1) {
- PyUString_ConcatAndDel(&errmsg, PyUString_FromString(" "));
- }
- }
- PyErr_SetObject(PyExc_TypeError, errmsg);
- Py_DECREF(errmsg);
-
- return -1;
+ return raise_no_loop_found_error(ufunc, dtypes);
}
typedef struct {
@@ -1680,7 +1749,7 @@ set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op,
}
/*
* For outputs, copy the dtype from op[0] if the type_num
- * matches, similarly to preserve metdata.
+ * matches, similarly to preserve metadata.
*/
else if (i >= nin && op[0] != NULL &&
PyArray_DESCR(op[0])->type_num == type_nums[i]) {
@@ -1871,73 +1940,6 @@ type_tuple_userloop_type_resolver(PyUFuncObject *self,
return 0;
}
-/*
- * Provides an ordering for the dtype 'kind' character codes, to help
- * determine when to use the min_scalar_type function. This groups
- * 'kind' into boolean, integer, floating point, and everything else.
- */
-
-static int
-dtype_kind_to_simplified_ordering(char kind)
-{
- switch (kind) {
- /* Boolean kind */
- case 'b':
- return 0;
- /* Unsigned int kind */
- case 'u':
- /* Signed int kind */
- case 'i':
- return 1;
- /* Float kind */
- case 'f':
- /* Complex kind */
- case 'c':
- return 2;
- /* Anything else */
- default:
- return 3;
- }
-}
-
-static int
-should_use_min_scalar(PyArrayObject **op, int nop)
-{
- int i, use_min_scalar, kind;
- int all_scalars = 1, max_scalar_kind = -1, max_array_kind = -1;
-
- /*
- * Determine if there are any scalars, and if so, whether
- * the maximum "kind" of the scalars surpasses the maximum
- * "kind" of the arrays
- */
- use_min_scalar = 0;
- if (nop > 1) {
- for(i = 0; i < nop; ++i) {
- kind = dtype_kind_to_simplified_ordering(
- PyArray_DESCR(op[i])->kind);
- if (PyArray_NDIM(op[i]) == 0) {
- if (kind > max_scalar_kind) {
- max_scalar_kind = kind;
- }
- }
- else {
- all_scalars = 0;
- if (kind > max_array_kind) {
- max_array_kind = kind;
- }
-
- }
- }
-
- /* Indicate whether to use the min_scalar_type function */
- if (!all_scalars && max_array_kind >= max_scalar_kind) {
- use_min_scalar = 1;
- }
- }
-
- return use_min_scalar;
-}
/*
* Does a linear search for the best inner loop of the ufunc.
@@ -1956,14 +1958,15 @@ linear_search_type_resolver(PyUFuncObject *self,
npy_intp i, j, nin = self->nin, nop = nin + self->nout;
int types[NPY_MAXARGS];
const char *ufunc_name;
- int no_castable_output, use_min_scalar;
+ int no_castable_output = 0;
+ int use_min_scalar;
/* For making a better error message on coercion error */
char err_dst_typecode = '-', err_src_typecode = '-';
ufunc_name = ufunc_get_name_cstr(self);
- use_min_scalar = should_use_min_scalar(op, nin);
+ use_min_scalar = should_use_min_scalar(nin, op, 0, NULL);
/* If the ufunc has userloops, search for them. */
if (self->userloops) {
@@ -2072,7 +2075,7 @@ type_tuple_type_resolver(PyUFuncObject *self,
ufunc_name = ufunc_get_name_cstr(self);
- use_min_scalar = should_use_min_scalar(op, nin);
+ use_min_scalar = should_use_min_scalar(nin, op, 0, NULL);
/* Fill in specified_types from the tuple or string */
if (PyTuple_Check(type_tup)) {
@@ -2232,8 +2235,56 @@ type_tuple_type_resolver(PyUFuncObject *self,
/* If no function was found, throw an error */
PyErr_Format(PyExc_TypeError,
- "No loop matching the specified signature and casting\n"
+ "No loop matching the specified signature and casting "
"was found for ufunc %s", ufunc_name);
return -1;
}
+
+NPY_NO_EXPORT int
+PyUFunc_DivmodTypeResolver(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes)
+{
+ int type_num1, type_num2;
+ int i;
+
+ type_num1 = PyArray_DESCR(operands[0])->type_num;
+ type_num2 = PyArray_DESCR(operands[1])->type_num;
+
+ /* Use the default when datetime and timedelta are not involved */
+ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) {
+ return PyUFunc_DefaultTypeResolver(ufunc, casting, operands,
+ type_tup, out_dtypes);
+ }
+ if (type_num1 == NPY_TIMEDELTA) {
+ if (type_num2 == NPY_TIMEDELTA) {
+ out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]),
+ PyArray_DESCR(operands[1]));
+ out_dtypes[1] = out_dtypes[0];
+ Py_INCREF(out_dtypes[1]);
+ out_dtypes[2] = PyArray_DescrFromType(NPY_LONGLONG);
+ out_dtypes[3] = out_dtypes[0];
+ Py_INCREF(out_dtypes[3]);
+ }
+ else {
+ return raise_binary_type_reso_error(ufunc, operands);
+ }
+ }
+ else {
+ return raise_binary_type_reso_error(ufunc, operands);
+ }
+
+ /* Check against the casting rules */
+ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) {
+ for (i = 0; i < 4; ++i) {
+ Py_DECREF(out_dtypes[i]);
+ out_dtypes[i] = NULL;
+ }
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/numpy/core/src/umath/ufunc_type_resolution.h b/numpy/core/src/umath/ufunc_type_resolution.h
index fa9f1dbfa..a4e670a8e 100644
--- a/numpy/core/src/umath/ufunc_type_resolution.h
+++ b/numpy/core/src/umath/ufunc_type_resolution.h
@@ -9,13 +9,6 @@ PyUFunc_SimpleBinaryComparisonTypeResolver(PyUFuncObject *ufunc,
PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
-PyUFunc_SimpleUnaryOperationTypeResolver(PyUFuncObject *ufunc,
- NPY_CASTING casting,
- PyArrayObject **operands,
- PyObject *type_tup,
- PyArray_Descr **out_dtypes);
-
-NPY_NO_EXPORT int
PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
@@ -30,7 +23,7 @@ PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc,
PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
-PyUFunc_SimpleBinaryOperationTypeResolver(PyUFuncObject *ufunc,
+PyUFunc_SimpleUniformOperationTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
PyObject *type_tup,
@@ -51,6 +44,13 @@ PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc,
PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
+PyUFunc_IsFiniteTypeResolver(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes);
+
+NPY_NO_EXPORT int
PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
NPY_CASTING casting,
PyArrayObject **operands,
@@ -92,6 +92,20 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
+NPY_NO_EXPORT int
+PyUFunc_RemainderTypeResolver(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes);
+
+NPY_NO_EXPORT int
+PyUFunc_DivmodTypeResolver(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes);
+
/*
* Does a linear search for the best inner loop of the ufunc.
*
@@ -138,5 +152,4 @@ PyUFunc_DefaultMaskedInnerLoopSelector(PyUFuncObject *ufunc,
NpyAuxData **out_innerloopdata,
int *out_needs_api);
-
#endif
diff --git a/numpy/core/src/umath/umathmodule.c b/numpy/core/src/umath/umathmodule.c
index 20bd2b0a8..6ec474376 100644
--- a/numpy/core/src/umath/umathmodule.c
+++ b/numpy/core/src/umath/umathmodule.c
@@ -29,6 +29,7 @@
#include "abstract.h"
#include "numpy/npy_math.h"
+#include "number.h"
static PyUFuncGenericFunction pyfunc_functions[] = {PyUFunc_On_Om};
@@ -160,6 +161,7 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS
self->type_resolver = &object_ufunc_type_resolver;
self->legacy_inner_loop_selector = &object_ufunc_loop_selector;
+ PyObject_GC_Track(self);
return (PyObject *)self;
}
@@ -169,7 +171,7 @@ PyObject *
add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args)
{
PyUFuncObject *ufunc;
- PyObject *str;
+ PyObject *str, *tmp;
char *docstr, *newdocstr;
#if defined(NPY_PY3K)
@@ -177,11 +179,15 @@ add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args)
&PyUnicode_Type, &str)) {
return NULL;
}
- docstr = PyBytes_AS_STRING(PyUnicode_AsUTF8String(str));
+ tmp = PyUnicode_AsUTF8String(str);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ docstr = PyBytes_AS_STRING(tmp);
#else
if (!PyArg_ParseTuple(args, "O!O!:_add_newdoc_ufunc", &PyUFunc_Type, &ufunc,
&PyString_Type, &str)) {
- return NULL;
+ return NULL;
}
docstr = PyString_AS_STRING(str);
#endif
@@ -189,6 +195,9 @@ add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args)
if (NULL != ufunc->doc) {
PyErr_SetString(PyExc_ValueError,
"Cannot change docstring of ufunc with non-NULL docstring");
+#if defined(NPY_PY3K)
+ Py_DECREF(tmp);
+#endif
return NULL;
}
@@ -202,6 +211,9 @@ add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args)
strcpy(newdocstr, docstr);
ufunc->doc = newdocstr;
+#if defined(NPY_PY3K)
+ Py_DECREF(tmp);
+#endif
Py_RETURN_NONE;
}
@@ -267,10 +279,6 @@ int initumath(PyObject *m)
UFUNC_FLOATING_POINT_SUPPORT = 0;
#endif
- /* Initialize the types */
- if (PyType_Ready(&PyUFunc_Type) < 0)
- return -1;
-
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
@@ -325,7 +333,7 @@ int initumath(PyObject *m)
s2 = PyDict_GetItemString(d, "remainder");
/* Setup the array object's numerical structures with appropriate
ufuncs in d*/
- PyArray_SetNumericOps(d);
+ _PyArray_SetNumericOps(d);
PyDict_SetItemString(d, "conj", s);
PyDict_SetItemString(d, "mod", s2);
diff --git a/numpy/core/tests/data/umath-validation-set-README b/numpy/core/tests/data/umath-validation-set-README
new file mode 100644
index 000000000..6561ca3b5
--- /dev/null
+++ b/numpy/core/tests/data/umath-validation-set-README
@@ -0,0 +1,15 @@
+Steps to validate transcendental functions:
+1) Add a file 'umath-validation-set-<ufuncname>', where ufuncname is name of
+ the function in NumPy you want to validate
+2) The file should contain 4 columns: dtype,input,expected output,ulperror
+ a. dtype: one of np.float16, np.float32, np.float64
+ b. input: floating point input to ufunc in hex. Example: 0x414570a4
+ represents 12.340000152587890625
+ c. expected output: floating point output for the corresponding input in hex.
+ This should be computed using a high(er) precision library and then rounded to
+ same format as the input.
+ d. ulperror: expected maximum ulp error of the function. This
+ should be same across all rows of the same dtype. Otherwise, the function is
+ tested for the maximum ulp error among all entries of that dtype.
+3) Add file umath-validation-set-<ufuncname> to the test file test_umath_accuracy.py
+ which will then validate your ufunc.
diff --git a/numpy/core/tests/data/umath-validation-set-cos b/numpy/core/tests/data/umath-validation-set-cos
new file mode 100644
index 000000000..360ebcd6a
--- /dev/null
+++ b/numpy/core/tests/data/umath-validation-set-cos
@@ -0,0 +1,707 @@
+dtype,input,output,ulperrortol
+## +ve denormals ##
+np.float32,0x004b4716,0x3f800000,2
+np.float32,0x007b2490,0x3f800000,2
+np.float32,0x007c99fa,0x3f800000,2
+np.float32,0x00734a0c,0x3f800000,2
+np.float32,0x0070de24,0x3f800000,2
+np.float32,0x007fffff,0x3f800000,2
+np.float32,0x00000001,0x3f800000,2
+## -ve denormals ##
+np.float32,0x80495d65,0x3f800000,2
+np.float32,0x806894f6,0x3f800000,2
+np.float32,0x80555a76,0x3f800000,2
+np.float32,0x804e1fb8,0x3f800000,2
+np.float32,0x80687de9,0x3f800000,2
+np.float32,0x807fffff,0x3f800000,2
+np.float32,0x80000001,0x3f800000,2
+## +/-0.0f, +/-FLT_MIN +/-FLT_MAX ##
+np.float32,0x00000000,0x3f800000,2
+np.float32,0x80000000,0x3f800000,2
+np.float32,0x00800000,0x3f800000,2
+np.float32,0x7f7fffff,0x3f5a5f96,2
+np.float32,0x80800000,0x3f800000,2
+np.float32,0xff7fffff,0x3f5a5f96,2
+## 1.00f + 0x00000001 ##
+np.float32,0x3f800000,0x3f0a5140,2
+np.float32,0x3f800001,0x3f0a513f,2
+np.float32,0x3f800002,0x3f0a513d,2
+np.float32,0xc090a8b0,0xbe4332ce,2
+np.float32,0x41ce3184,0x3f4d1de1,2
+np.float32,0xc1d85848,0xbeaa8980,2
+np.float32,0x402b8820,0xbf653aa3,2
+np.float32,0x42b4e454,0xbf4a338b,2
+np.float32,0x42a67a60,0x3c58202e,2
+np.float32,0x41d92388,0xbed987c7,2
+np.float32,0x422dd66c,0x3f5dcab3,2
+np.float32,0xc28f5be6,0xbf5688d8,2
+np.float32,0x41ab2674,0xbf53aa3b,2
+np.float32,0xd0102756,0x3f45d12d,2
+np.float32,0xcf99405e,0xbe9cf281,2
+np.float32,0xcfd83a12,0x3eaae4ca,2
+np.float32,0x4fb54db0,0xbf7b2894,2
+np.float32,0xcfcca29d,0x3f752e4e,2
+np.float32,0xceec2ac0,0xbf745303,2
+np.float32,0xcfdca97f,0x3ef554a7,2
+np.float32,0xcfe92b0a,0x3f4618f2,2
+np.float32,0x5014b0eb,0x3ee933e6,2
+np.float32,0xcfa7ee96,0xbeedeeb2,2
+np.float32,0x754c09a0,0xbef298de,2
+np.float32,0x77a731fb,0x3f24599f,2
+np.float32,0x76de2494,0x3f79576c,2
+np.float32,0xf74920dc,0xbf4d196e,2
+np.float32,0x7707a312,0xbeb5cb8e,2
+np.float32,0x75bf9790,0xbf7fd7fe,2
+np.float32,0xf4ca7c40,0xbe15107d,2
+np.float32,0x77e91899,0xbe8a968b,2
+np.float32,0xf74c9820,0xbf7f9677,2
+np.float32,0x7785ca29,0xbe6ef93b,2
+np.float32,0x3f490fdb,0x3f3504f3,2
+np.float32,0xbf490fdb,0x3f3504f3,2
+np.float32,0x3fc90fdb,0xb33bbd2e,2
+np.float32,0xbfc90fdb,0xb33bbd2e,2
+np.float32,0x40490fdb,0xbf800000,2
+np.float32,0xc0490fdb,0xbf800000,2
+np.float32,0x3fc90fdb,0xb33bbd2e,2
+np.float32,0xbfc90fdb,0xb33bbd2e,2
+np.float32,0x40490fdb,0xbf800000,2
+np.float32,0xc0490fdb,0xbf800000,2
+np.float32,0x40c90fdb,0x3f800000,2
+np.float32,0xc0c90fdb,0x3f800000,2
+np.float32,0x4016cbe4,0xbf3504f3,2
+np.float32,0xc016cbe4,0xbf3504f3,2
+np.float32,0x4096cbe4,0x324cde2e,2
+np.float32,0xc096cbe4,0x324cde2e,2
+np.float32,0x4116cbe4,0xbf800000,2
+np.float32,0xc116cbe4,0xbf800000,2
+np.float32,0x40490fdb,0xbf800000,2
+np.float32,0xc0490fdb,0xbf800000,2
+np.float32,0x40c90fdb,0x3f800000,2
+np.float32,0xc0c90fdb,0x3f800000,2
+np.float32,0x41490fdb,0x3f800000,2
+np.float32,0xc1490fdb,0x3f800000,2
+np.float32,0x407b53d2,0xbf3504f1,2
+np.float32,0xc07b53d2,0xbf3504f1,2
+np.float32,0x40fb53d2,0xb4b5563d,2
+np.float32,0xc0fb53d2,0xb4b5563d,2
+np.float32,0x417b53d2,0xbf800000,2
+np.float32,0xc17b53d2,0xbf800000,2
+np.float32,0x4096cbe4,0x324cde2e,2
+np.float32,0xc096cbe4,0x324cde2e,2
+np.float32,0x4116cbe4,0xbf800000,2
+np.float32,0xc116cbe4,0xbf800000,2
+np.float32,0x4196cbe4,0x3f800000,2
+np.float32,0xc196cbe4,0x3f800000,2
+np.float32,0x40afede0,0x3f3504f7,2
+np.float32,0xc0afede0,0x3f3504f7,2
+np.float32,0x412fede0,0x353222c4,2
+np.float32,0xc12fede0,0x353222c4,2
+np.float32,0x41afede0,0xbf800000,2
+np.float32,0xc1afede0,0xbf800000,2
+np.float32,0x40c90fdb,0x3f800000,2
+np.float32,0xc0c90fdb,0x3f800000,2
+np.float32,0x41490fdb,0x3f800000,2
+np.float32,0xc1490fdb,0x3f800000,2
+np.float32,0x41c90fdb,0x3f800000,2
+np.float32,0xc1c90fdb,0x3f800000,2
+np.float32,0x40e231d6,0x3f3504f3,2
+np.float32,0xc0e231d6,0x3f3504f3,2
+np.float32,0x416231d6,0xb319a6a2,2
+np.float32,0xc16231d6,0xb319a6a2,2
+np.float32,0x41e231d6,0xbf800000,2
+np.float32,0xc1e231d6,0xbf800000,2
+np.float32,0x40fb53d2,0xb4b5563d,2
+np.float32,0xc0fb53d2,0xb4b5563d,2
+np.float32,0x417b53d2,0xbf800000,2
+np.float32,0xc17b53d2,0xbf800000,2
+np.float32,0x41fb53d2,0x3f800000,2
+np.float32,0xc1fb53d2,0x3f800000,2
+np.float32,0x410a3ae7,0xbf3504fb,2
+np.float32,0xc10a3ae7,0xbf3504fb,2
+np.float32,0x418a3ae7,0x35b08908,2
+np.float32,0xc18a3ae7,0x35b08908,2
+np.float32,0x420a3ae7,0xbf800000,2
+np.float32,0xc20a3ae7,0xbf800000,2
+np.float32,0x4116cbe4,0xbf800000,2
+np.float32,0xc116cbe4,0xbf800000,2
+np.float32,0x4196cbe4,0x3f800000,2
+np.float32,0xc196cbe4,0x3f800000,2
+np.float32,0x4216cbe4,0x3f800000,2
+np.float32,0xc216cbe4,0x3f800000,2
+np.float32,0x41235ce2,0xbf3504ef,2
+np.float32,0xc1235ce2,0xbf3504ef,2
+np.float32,0x41a35ce2,0xb53889b6,2
+np.float32,0xc1a35ce2,0xb53889b6,2
+np.float32,0x42235ce2,0xbf800000,2
+np.float32,0xc2235ce2,0xbf800000,2
+np.float32,0x412fede0,0x353222c4,2
+np.float32,0xc12fede0,0x353222c4,2
+np.float32,0x41afede0,0xbf800000,2
+np.float32,0xc1afede0,0xbf800000,2
+np.float32,0x422fede0,0x3f800000,2
+np.float32,0xc22fede0,0x3f800000,2
+np.float32,0x413c7edd,0x3f3504f4,2
+np.float32,0xc13c7edd,0x3f3504f4,2
+np.float32,0x41bc7edd,0x33800add,2
+np.float32,0xc1bc7edd,0x33800add,2
+np.float32,0x423c7edd,0xbf800000,2
+np.float32,0xc23c7edd,0xbf800000,2
+np.float32,0x41490fdb,0x3f800000,2
+np.float32,0xc1490fdb,0x3f800000,2
+np.float32,0x41c90fdb,0x3f800000,2
+np.float32,0xc1c90fdb,0x3f800000,2
+np.float32,0x42490fdb,0x3f800000,2
+np.float32,0xc2490fdb,0x3f800000,2
+np.float32,0x4155a0d9,0x3f3504eb,2
+np.float32,0xc155a0d9,0x3f3504eb,2
+np.float32,0x41d5a0d9,0xb5b3bc81,2
+np.float32,0xc1d5a0d9,0xb5b3bc81,2
+np.float32,0x4255a0d9,0xbf800000,2
+np.float32,0xc255a0d9,0xbf800000,2
+np.float32,0x416231d6,0xb319a6a2,2
+np.float32,0xc16231d6,0xb319a6a2,2
+np.float32,0x41e231d6,0xbf800000,2
+np.float32,0xc1e231d6,0xbf800000,2
+np.float32,0x426231d6,0x3f800000,2
+np.float32,0xc26231d6,0x3f800000,2
+np.float32,0x416ec2d4,0xbf3504f7,2
+np.float32,0xc16ec2d4,0xbf3504f7,2
+np.float32,0x41eec2d4,0x353ef0a7,2
+np.float32,0xc1eec2d4,0x353ef0a7,2
+np.float32,0x426ec2d4,0xbf800000,2
+np.float32,0xc26ec2d4,0xbf800000,2
+np.float32,0x417b53d2,0xbf800000,2
+np.float32,0xc17b53d2,0xbf800000,2
+np.float32,0x41fb53d2,0x3f800000,2
+np.float32,0xc1fb53d2,0x3f800000,2
+np.float32,0x427b53d2,0x3f800000,2
+np.float32,0xc27b53d2,0x3f800000,2
+np.float32,0x4183f268,0xbf3504e7,2
+np.float32,0xc183f268,0xbf3504e7,2
+np.float32,0x4203f268,0xb6059a13,2
+np.float32,0xc203f268,0xb6059a13,2
+np.float32,0x4283f268,0xbf800000,2
+np.float32,0xc283f268,0xbf800000,2
+np.float32,0x418a3ae7,0x35b08908,2
+np.float32,0xc18a3ae7,0x35b08908,2
+np.float32,0x420a3ae7,0xbf800000,2
+np.float32,0xc20a3ae7,0xbf800000,2
+np.float32,0x428a3ae7,0x3f800000,2
+np.float32,0xc28a3ae7,0x3f800000,2
+np.float32,0x41908365,0x3f3504f0,2
+np.float32,0xc1908365,0x3f3504f0,2
+np.float32,0x42108365,0xb512200d,2
+np.float32,0xc2108365,0xb512200d,2
+np.float32,0x42908365,0xbf800000,2
+np.float32,0xc2908365,0xbf800000,2
+np.float32,0x4196cbe4,0x3f800000,2
+np.float32,0xc196cbe4,0x3f800000,2
+np.float32,0x4216cbe4,0x3f800000,2
+np.float32,0xc216cbe4,0x3f800000,2
+np.float32,0x4296cbe4,0x3f800000,2
+np.float32,0xc296cbe4,0x3f800000,2
+np.float32,0x419d1463,0x3f3504ef,2
+np.float32,0xc19d1463,0x3f3504ef,2
+np.float32,0x421d1463,0xb5455799,2
+np.float32,0xc21d1463,0xb5455799,2
+np.float32,0x429d1463,0xbf800000,2
+np.float32,0xc29d1463,0xbf800000,2
+np.float32,0x41a35ce2,0xb53889b6,2
+np.float32,0xc1a35ce2,0xb53889b6,2
+np.float32,0x42235ce2,0xbf800000,2
+np.float32,0xc2235ce2,0xbf800000,2
+np.float32,0x42a35ce2,0x3f800000,2
+np.float32,0xc2a35ce2,0x3f800000,2
+np.float32,0x41a9a561,0xbf3504ff,2
+np.float32,0xc1a9a561,0xbf3504ff,2
+np.float32,0x4229a561,0x360733d0,2
+np.float32,0xc229a561,0x360733d0,2
+np.float32,0x42a9a561,0xbf800000,2
+np.float32,0xc2a9a561,0xbf800000,2
+np.float32,0x41afede0,0xbf800000,2
+np.float32,0xc1afede0,0xbf800000,2
+np.float32,0x422fede0,0x3f800000,2
+np.float32,0xc22fede0,0x3f800000,2
+np.float32,0x42afede0,0x3f800000,2
+np.float32,0xc2afede0,0x3f800000,2
+np.float32,0x41b6365e,0xbf3504f6,2
+np.float32,0xc1b6365e,0xbf3504f6,2
+np.float32,0x4236365e,0x350bb91c,2
+np.float32,0xc236365e,0x350bb91c,2
+np.float32,0x42b6365e,0xbf800000,2
+np.float32,0xc2b6365e,0xbf800000,2
+np.float32,0x41bc7edd,0x33800add,2
+np.float32,0xc1bc7edd,0x33800add,2
+np.float32,0x423c7edd,0xbf800000,2
+np.float32,0xc23c7edd,0xbf800000,2
+np.float32,0x42bc7edd,0x3f800000,2
+np.float32,0xc2bc7edd,0x3f800000,2
+np.float32,0x41c2c75c,0x3f3504f8,2
+np.float32,0xc1c2c75c,0x3f3504f8,2
+np.float32,0x4242c75c,0x354bbe8a,2
+np.float32,0xc242c75c,0x354bbe8a,2
+np.float32,0x42c2c75c,0xbf800000,2
+np.float32,0xc2c2c75c,0xbf800000,2
+np.float32,0x41c90fdb,0x3f800000,2
+np.float32,0xc1c90fdb,0x3f800000,2
+np.float32,0x42490fdb,0x3f800000,2
+np.float32,0xc2490fdb,0x3f800000,2
+np.float32,0x42c90fdb,0x3f800000,2
+np.float32,0xc2c90fdb,0x3f800000,2
+np.float32,0x41cf585a,0x3f3504e7,2
+np.float32,0xc1cf585a,0x3f3504e7,2
+np.float32,0x424f585a,0xb608cd8c,2
+np.float32,0xc24f585a,0xb608cd8c,2
+np.float32,0x42cf585a,0xbf800000,2
+np.float32,0xc2cf585a,0xbf800000,2
+np.float32,0x41d5a0d9,0xb5b3bc81,2
+np.float32,0xc1d5a0d9,0xb5b3bc81,2
+np.float32,0x4255a0d9,0xbf800000,2
+np.float32,0xc255a0d9,0xbf800000,2
+np.float32,0x42d5a0d9,0x3f800000,2
+np.float32,0xc2d5a0d9,0x3f800000,2
+np.float32,0x41dbe958,0xbf350507,2
+np.float32,0xc1dbe958,0xbf350507,2
+np.float32,0x425be958,0x365eab75,2
+np.float32,0xc25be958,0x365eab75,2
+np.float32,0x42dbe958,0xbf800000,2
+np.float32,0xc2dbe958,0xbf800000,2
+np.float32,0x41e231d6,0xbf800000,2
+np.float32,0xc1e231d6,0xbf800000,2
+np.float32,0x426231d6,0x3f800000,2
+np.float32,0xc26231d6,0x3f800000,2
+np.float32,0x42e231d6,0x3f800000,2
+np.float32,0xc2e231d6,0x3f800000,2
+np.float32,0x41e87a55,0xbf3504ef,2
+np.float32,0xc1e87a55,0xbf3504ef,2
+np.float32,0x42687a55,0xb552257b,2
+np.float32,0xc2687a55,0xb552257b,2
+np.float32,0x42e87a55,0xbf800000,2
+np.float32,0xc2e87a55,0xbf800000,2
+np.float32,0x41eec2d4,0x353ef0a7,2
+np.float32,0xc1eec2d4,0x353ef0a7,2
+np.float32,0x426ec2d4,0xbf800000,2
+np.float32,0xc26ec2d4,0xbf800000,2
+np.float32,0x42eec2d4,0x3f800000,2
+np.float32,0xc2eec2d4,0x3f800000,2
+np.float32,0x41f50b53,0x3f3504ff,2
+np.float32,0xc1f50b53,0x3f3504ff,2
+np.float32,0x42750b53,0x360a6748,2
+np.float32,0xc2750b53,0x360a6748,2
+np.float32,0x42f50b53,0xbf800000,2
+np.float32,0xc2f50b53,0xbf800000,2
+np.float32,0x41fb53d2,0x3f800000,2
+np.float32,0xc1fb53d2,0x3f800000,2
+np.float32,0x427b53d2,0x3f800000,2
+np.float32,0xc27b53d2,0x3f800000,2
+np.float32,0x42fb53d2,0x3f800000,2
+np.float32,0xc2fb53d2,0x3f800000,2
+np.float32,0x4200ce28,0x3f3504f6,2
+np.float32,0xc200ce28,0x3f3504f6,2
+np.float32,0x4280ce28,0x34fdd672,2
+np.float32,0xc280ce28,0x34fdd672,2
+np.float32,0x4300ce28,0xbf800000,2
+np.float32,0xc300ce28,0xbf800000,2
+np.float32,0x4203f268,0xb6059a13,2
+np.float32,0xc203f268,0xb6059a13,2
+np.float32,0x4283f268,0xbf800000,2
+np.float32,0xc283f268,0xbf800000,2
+np.float32,0x4303f268,0x3f800000,2
+np.float32,0xc303f268,0x3f800000,2
+np.float32,0x420716a7,0xbf3504f8,2
+np.float32,0xc20716a7,0xbf3504f8,2
+np.float32,0x428716a7,0x35588c6d,2
+np.float32,0xc28716a7,0x35588c6d,2
+np.float32,0x430716a7,0xbf800000,2
+np.float32,0xc30716a7,0xbf800000,2
+np.float32,0x420a3ae7,0xbf800000,2
+np.float32,0xc20a3ae7,0xbf800000,2
+np.float32,0x428a3ae7,0x3f800000,2
+np.float32,0xc28a3ae7,0x3f800000,2
+np.float32,0x430a3ae7,0x3f800000,2
+np.float32,0xc30a3ae7,0x3f800000,2
+np.float32,0x420d5f26,0xbf3504e7,2
+np.float32,0xc20d5f26,0xbf3504e7,2
+np.float32,0x428d5f26,0xb60c0105,2
+np.float32,0xc28d5f26,0xb60c0105,2
+np.float32,0x430d5f26,0xbf800000,2
+np.float32,0xc30d5f26,0xbf800000,2
+np.float32,0x42108365,0xb512200d,2
+np.float32,0xc2108365,0xb512200d,2
+np.float32,0x42908365,0xbf800000,2
+np.float32,0xc2908365,0xbf800000,2
+np.float32,0x43108365,0x3f800000,2
+np.float32,0xc3108365,0x3f800000,2
+np.float32,0x4213a7a5,0x3f350507,2
+np.float32,0xc213a7a5,0x3f350507,2
+np.float32,0x4293a7a5,0x3661deee,2
+np.float32,0xc293a7a5,0x3661deee,2
+np.float32,0x4313a7a5,0xbf800000,2
+np.float32,0xc313a7a5,0xbf800000,2
+np.float32,0x4216cbe4,0x3f800000,2
+np.float32,0xc216cbe4,0x3f800000,2
+np.float32,0x4296cbe4,0x3f800000,2
+np.float32,0xc296cbe4,0x3f800000,2
+np.float32,0x4316cbe4,0x3f800000,2
+np.float32,0xc316cbe4,0x3f800000,2
+np.float32,0x4219f024,0x3f3504d8,2
+np.float32,0xc219f024,0x3f3504d8,2
+np.float32,0x4299f024,0xb69bde6c,2
+np.float32,0xc299f024,0xb69bde6c,2
+np.float32,0x4319f024,0xbf800000,2
+np.float32,0xc319f024,0xbf800000,2
+np.float32,0x421d1463,0xb5455799,2
+np.float32,0xc21d1463,0xb5455799,2
+np.float32,0x429d1463,0xbf800000,2
+np.float32,0xc29d1463,0xbf800000,2
+np.float32,0x431d1463,0x3f800000,2
+np.float32,0xc31d1463,0x3f800000,2
+np.float32,0x422038a3,0xbf350516,2
+np.float32,0xc22038a3,0xbf350516,2
+np.float32,0x42a038a3,0x36c6cd61,2
+np.float32,0xc2a038a3,0x36c6cd61,2
+np.float32,0x432038a3,0xbf800000,2
+np.float32,0xc32038a3,0xbf800000,2
+np.float32,0x42235ce2,0xbf800000,2
+np.float32,0xc2235ce2,0xbf800000,2
+np.float32,0x42a35ce2,0x3f800000,2
+np.float32,0xc2a35ce2,0x3f800000,2
+np.float32,0x43235ce2,0x3f800000,2
+np.float32,0xc3235ce2,0x3f800000,2
+np.float32,0x42268121,0xbf3504f6,2
+np.float32,0xc2268121,0xbf3504f6,2
+np.float32,0x42a68121,0x34e43aac,2
+np.float32,0xc2a68121,0x34e43aac,2
+np.float32,0x43268121,0xbf800000,2
+np.float32,0xc3268121,0xbf800000,2
+np.float32,0x4229a561,0x360733d0,2
+np.float32,0xc229a561,0x360733d0,2
+np.float32,0x42a9a561,0xbf800000,2
+np.float32,0xc2a9a561,0xbf800000,2
+np.float32,0x4329a561,0x3f800000,2
+np.float32,0xc329a561,0x3f800000,2
+np.float32,0x422cc9a0,0x3f3504f8,2
+np.float32,0xc22cc9a0,0x3f3504f8,2
+np.float32,0x42acc9a0,0x35655a50,2
+np.float32,0xc2acc9a0,0x35655a50,2
+np.float32,0x432cc9a0,0xbf800000,2
+np.float32,0xc32cc9a0,0xbf800000,2
+np.float32,0x422fede0,0x3f800000,2
+np.float32,0xc22fede0,0x3f800000,2
+np.float32,0x42afede0,0x3f800000,2
+np.float32,0xc2afede0,0x3f800000,2
+np.float32,0x432fede0,0x3f800000,2
+np.float32,0xc32fede0,0x3f800000,2
+np.float32,0x4233121f,0x3f3504e7,2
+np.float32,0xc233121f,0x3f3504e7,2
+np.float32,0x42b3121f,0xb60f347d,2
+np.float32,0xc2b3121f,0xb60f347d,2
+np.float32,0x4333121f,0xbf800000,2
+np.float32,0xc333121f,0xbf800000,2
+np.float32,0x4236365e,0x350bb91c,2
+np.float32,0xc236365e,0x350bb91c,2
+np.float32,0x42b6365e,0xbf800000,2
+np.float32,0xc2b6365e,0xbf800000,2
+np.float32,0x4336365e,0x3f800000,2
+np.float32,0xc336365e,0x3f800000,2
+np.float32,0x42395a9e,0xbf350507,2
+np.float32,0xc2395a9e,0xbf350507,2
+np.float32,0x42b95a9e,0x36651267,2
+np.float32,0xc2b95a9e,0x36651267,2
+np.float32,0x43395a9e,0xbf800000,2
+np.float32,0xc3395a9e,0xbf800000,2
+np.float32,0x423c7edd,0xbf800000,2
+np.float32,0xc23c7edd,0xbf800000,2
+np.float32,0x42bc7edd,0x3f800000,2
+np.float32,0xc2bc7edd,0x3f800000,2
+np.float32,0x433c7edd,0x3f800000,2
+np.float32,0xc33c7edd,0x3f800000,2
+np.float32,0x423fa31d,0xbf3504d7,2
+np.float32,0xc23fa31d,0xbf3504d7,2
+np.float32,0x42bfa31d,0xb69d7828,2
+np.float32,0xc2bfa31d,0xb69d7828,2
+np.float32,0x433fa31d,0xbf800000,2
+np.float32,0xc33fa31d,0xbf800000,2
+np.float32,0x4242c75c,0x354bbe8a,2
+np.float32,0xc242c75c,0x354bbe8a,2
+np.float32,0x42c2c75c,0xbf800000,2
+np.float32,0xc2c2c75c,0xbf800000,2
+np.float32,0x4342c75c,0x3f800000,2
+np.float32,0xc342c75c,0x3f800000,2
+np.float32,0x4245eb9c,0x3f350517,2
+np.float32,0xc245eb9c,0x3f350517,2
+np.float32,0x42c5eb9c,0x36c8671d,2
+np.float32,0xc2c5eb9c,0x36c8671d,2
+np.float32,0x4345eb9c,0xbf800000,2
+np.float32,0xc345eb9c,0xbf800000,2
+np.float32,0x42490fdb,0x3f800000,2
+np.float32,0xc2490fdb,0x3f800000,2
+np.float32,0x42c90fdb,0x3f800000,2
+np.float32,0xc2c90fdb,0x3f800000,2
+np.float32,0x43490fdb,0x3f800000,2
+np.float32,0xc3490fdb,0x3f800000,2
+np.float32,0x424c341a,0x3f3504f5,2
+np.float32,0xc24c341a,0x3f3504f5,2
+np.float32,0x42cc341a,0x34ca9ee6,2
+np.float32,0xc2cc341a,0x34ca9ee6,2
+np.float32,0x434c341a,0xbf800000,2
+np.float32,0xc34c341a,0xbf800000,2
+np.float32,0x424f585a,0xb608cd8c,2
+np.float32,0xc24f585a,0xb608cd8c,2
+np.float32,0x42cf585a,0xbf800000,2
+np.float32,0xc2cf585a,0xbf800000,2
+np.float32,0x434f585a,0x3f800000,2
+np.float32,0xc34f585a,0x3f800000,2
+np.float32,0x42527c99,0xbf3504f9,2
+np.float32,0xc2527c99,0xbf3504f9,2
+np.float32,0x42d27c99,0x35722833,2
+np.float32,0xc2d27c99,0x35722833,2
+np.float32,0x43527c99,0xbf800000,2
+np.float32,0xc3527c99,0xbf800000,2
+np.float32,0x4255a0d9,0xbf800000,2
+np.float32,0xc255a0d9,0xbf800000,2
+np.float32,0x42d5a0d9,0x3f800000,2
+np.float32,0xc2d5a0d9,0x3f800000,2
+np.float32,0x4355a0d9,0x3f800000,2
+np.float32,0xc355a0d9,0x3f800000,2
+np.float32,0x4258c518,0xbf3504e6,2
+np.float32,0xc258c518,0xbf3504e6,2
+np.float32,0x42d8c518,0xb61267f6,2
+np.float32,0xc2d8c518,0xb61267f6,2
+np.float32,0x4358c518,0xbf800000,2
+np.float32,0xc358c518,0xbf800000,2
+np.float32,0x425be958,0x365eab75,2
+np.float32,0xc25be958,0x365eab75,2
+np.float32,0x42dbe958,0xbf800000,2
+np.float32,0xc2dbe958,0xbf800000,2
+np.float32,0x435be958,0x3f800000,2
+np.float32,0xc35be958,0x3f800000,2
+np.float32,0x425f0d97,0x3f350508,2
+np.float32,0xc25f0d97,0x3f350508,2
+np.float32,0x42df0d97,0x366845e0,2
+np.float32,0xc2df0d97,0x366845e0,2
+np.float32,0x435f0d97,0xbf800000,2
+np.float32,0xc35f0d97,0xbf800000,2
+np.float32,0x426231d6,0x3f800000,2
+np.float32,0xc26231d6,0x3f800000,2
+np.float32,0x42e231d6,0x3f800000,2
+np.float32,0xc2e231d6,0x3f800000,2
+np.float32,0x436231d6,0x3f800000,2
+np.float32,0xc36231d6,0x3f800000,2
+np.float32,0x42655616,0x3f3504d7,2
+np.float32,0xc2655616,0x3f3504d7,2
+np.float32,0x42e55616,0xb69f11e5,2
+np.float32,0xc2e55616,0xb69f11e5,2
+np.float32,0x43655616,0xbf800000,2
+np.float32,0xc3655616,0xbf800000,2
+np.float32,0x42687a55,0xb552257b,2
+np.float32,0xc2687a55,0xb552257b,2
+np.float32,0x42e87a55,0xbf800000,2
+np.float32,0xc2e87a55,0xbf800000,2
+np.float32,0x43687a55,0x3f800000,2
+np.float32,0xc3687a55,0x3f800000,2
+np.float32,0x426b9e95,0xbf350517,2
+np.float32,0xc26b9e95,0xbf350517,2
+np.float32,0x42eb9e95,0x36ca00d9,2
+np.float32,0xc2eb9e95,0x36ca00d9,2
+np.float32,0x436b9e95,0xbf800000,2
+np.float32,0xc36b9e95,0xbf800000,2
+np.float32,0x426ec2d4,0xbf800000,2
+np.float32,0xc26ec2d4,0xbf800000,2
+np.float32,0x42eec2d4,0x3f800000,2
+np.float32,0xc2eec2d4,0x3f800000,2
+np.float32,0x436ec2d4,0x3f800000,2
+np.float32,0xc36ec2d4,0x3f800000,2
+np.float32,0x4271e713,0xbf3504f5,2
+np.float32,0xc271e713,0xbf3504f5,2
+np.float32,0x42f1e713,0x34b10321,2
+np.float32,0xc2f1e713,0x34b10321,2
+np.float32,0x4371e713,0xbf800000,2
+np.float32,0xc371e713,0xbf800000,2
+np.float32,0x42750b53,0x360a6748,2
+np.float32,0xc2750b53,0x360a6748,2
+np.float32,0x42f50b53,0xbf800000,2
+np.float32,0xc2f50b53,0xbf800000,2
+np.float32,0x43750b53,0x3f800000,2
+np.float32,0xc3750b53,0x3f800000,2
+np.float32,0x42782f92,0x3f3504f9,2
+np.float32,0xc2782f92,0x3f3504f9,2
+np.float32,0x42f82f92,0x357ef616,2
+np.float32,0xc2f82f92,0x357ef616,2
+np.float32,0x43782f92,0xbf800000,2
+np.float32,0xc3782f92,0xbf800000,2
+np.float32,0x427b53d2,0x3f800000,2
+np.float32,0xc27b53d2,0x3f800000,2
+np.float32,0x42fb53d2,0x3f800000,2
+np.float32,0xc2fb53d2,0x3f800000,2
+np.float32,0x437b53d2,0x3f800000,2
+np.float32,0xc37b53d2,0x3f800000,2
+np.float32,0x427e7811,0x3f3504e6,2
+np.float32,0xc27e7811,0x3f3504e6,2
+np.float32,0x42fe7811,0xb6159b6f,2
+np.float32,0xc2fe7811,0xb6159b6f,2
+np.float32,0x437e7811,0xbf800000,2
+np.float32,0xc37e7811,0xbf800000,2
+np.float32,0x4280ce28,0x34fdd672,2
+np.float32,0xc280ce28,0x34fdd672,2
+np.float32,0x4300ce28,0xbf800000,2
+np.float32,0xc300ce28,0xbf800000,2
+np.float32,0x4380ce28,0x3f800000,2
+np.float32,0xc380ce28,0x3f800000,2
+np.float32,0x42826048,0xbf350508,2
+np.float32,0xc2826048,0xbf350508,2
+np.float32,0x43026048,0x366b7958,2
+np.float32,0xc3026048,0x366b7958,2
+np.float32,0x43826048,0xbf800000,2
+np.float32,0xc3826048,0xbf800000,2
+np.float32,0x4283f268,0xbf800000,2
+np.float32,0xc283f268,0xbf800000,2
+np.float32,0x4303f268,0x3f800000,2
+np.float32,0xc303f268,0x3f800000,2
+np.float32,0x4383f268,0x3f800000,2
+np.float32,0xc383f268,0x3f800000,2
+np.float32,0x42858487,0xbf350504,2
+np.float32,0xc2858487,0xbf350504,2
+np.float32,0x43058487,0x363ea8be,2
+np.float32,0xc3058487,0x363ea8be,2
+np.float32,0x43858487,0xbf800000,2
+np.float32,0xc3858487,0xbf800000,2
+np.float32,0x428716a7,0x35588c6d,2
+np.float32,0xc28716a7,0x35588c6d,2
+np.float32,0x430716a7,0xbf800000,2
+np.float32,0xc30716a7,0xbf800000,2
+np.float32,0x438716a7,0x3f800000,2
+np.float32,0xc38716a7,0x3f800000,2
+np.float32,0x4288a8c7,0x3f350517,2
+np.float32,0xc288a8c7,0x3f350517,2
+np.float32,0x4308a8c7,0x36cb9a96,2
+np.float32,0xc308a8c7,0x36cb9a96,2
+np.float32,0x4388a8c7,0xbf800000,2
+np.float32,0xc388a8c7,0xbf800000,2
+np.float32,0x428a3ae7,0x3f800000,2
+np.float32,0xc28a3ae7,0x3f800000,2
+np.float32,0x430a3ae7,0x3f800000,2
+np.float32,0xc30a3ae7,0x3f800000,2
+np.float32,0x438a3ae7,0x3f800000,2
+np.float32,0xc38a3ae7,0x3f800000,2
+np.float32,0x428bcd06,0x3f3504f5,2
+np.float32,0xc28bcd06,0x3f3504f5,2
+np.float32,0x430bcd06,0x3497675b,2
+np.float32,0xc30bcd06,0x3497675b,2
+np.float32,0x438bcd06,0xbf800000,2
+np.float32,0xc38bcd06,0xbf800000,2
+np.float32,0x428d5f26,0xb60c0105,2
+np.float32,0xc28d5f26,0xb60c0105,2
+np.float32,0x430d5f26,0xbf800000,2
+np.float32,0xc30d5f26,0xbf800000,2
+np.float32,0x438d5f26,0x3f800000,2
+np.float32,0xc38d5f26,0x3f800000,2
+np.float32,0x428ef146,0xbf350526,2
+np.float32,0xc28ef146,0xbf350526,2
+np.float32,0x430ef146,0x3710bc40,2
+np.float32,0xc30ef146,0x3710bc40,2
+np.float32,0x438ef146,0xbf800000,2
+np.float32,0xc38ef146,0xbf800000,2
+np.float32,0x42908365,0xbf800000,2
+np.float32,0xc2908365,0xbf800000,2
+np.float32,0x43108365,0x3f800000,2
+np.float32,0xc3108365,0x3f800000,2
+np.float32,0x43908365,0x3f800000,2
+np.float32,0xc3908365,0x3f800000,2
+np.float32,0x42921585,0xbf3504e6,2
+np.float32,0xc2921585,0xbf3504e6,2
+np.float32,0x43121585,0xb618cee8,2
+np.float32,0xc3121585,0xb618cee8,2
+np.float32,0x43921585,0xbf800000,2
+np.float32,0xc3921585,0xbf800000,2
+np.float32,0x4293a7a5,0x3661deee,2
+np.float32,0xc293a7a5,0x3661deee,2
+np.float32,0x4313a7a5,0xbf800000,2
+np.float32,0xc313a7a5,0xbf800000,2
+np.float32,0x4393a7a5,0x3f800000,2
+np.float32,0xc393a7a5,0x3f800000,2
+np.float32,0x429539c5,0x3f350536,2
+np.float32,0xc29539c5,0x3f350536,2
+np.float32,0x431539c5,0x373bab34,2
+np.float32,0xc31539c5,0x373bab34,2
+np.float32,0x439539c5,0xbf800000,2
+np.float32,0xc39539c5,0xbf800000,2
+np.float32,0x4296cbe4,0x3f800000,2
+np.float32,0xc296cbe4,0x3f800000,2
+np.float32,0x4316cbe4,0x3f800000,2
+np.float32,0xc316cbe4,0x3f800000,2
+np.float32,0x4396cbe4,0x3f800000,2
+np.float32,0xc396cbe4,0x3f800000,2
+np.float32,0x42985e04,0x3f3504d7,2
+np.float32,0xc2985e04,0x3f3504d7,2
+np.float32,0x43185e04,0xb6a2455d,2
+np.float32,0xc3185e04,0xb6a2455d,2
+np.float32,0x43985e04,0xbf800000,2
+np.float32,0xc3985e04,0xbf800000,2
+np.float32,0x4299f024,0xb69bde6c,2
+np.float32,0xc299f024,0xb69bde6c,2
+np.float32,0x4319f024,0xbf800000,2
+np.float32,0xc319f024,0xbf800000,2
+np.float32,0x4399f024,0x3f800000,2
+np.float32,0xc399f024,0x3f800000,2
+np.float32,0x429b8243,0xbf3504ea,2
+np.float32,0xc29b8243,0xbf3504ea,2
+np.float32,0x431b8243,0xb5cb2eb8,2
+np.float32,0xc31b8243,0xb5cb2eb8,2
+np.float32,0x439b8243,0xbf800000,2
+np.float32,0xc39b8243,0xbf800000,2
+np.float32,0x435b2047,0x3f3504c1,2
+np.float32,0x42a038a2,0xb5e4ca7e,2
+np.float32,0x432038a2,0xbf800000,2
+np.float32,0x4345eb9b,0xbf800000,2
+np.float32,0x42c5eb9b,0xb5de638c,2
+np.float32,0x42eb9e94,0xb5d7fc9b,2
+np.float32,0x4350ea79,0x3631dadb,2
+np.float32,0x42dbe957,0xbf800000,2
+np.float32,0x425be957,0xb505522a,2
+np.float32,0x435be957,0x3f800000,2
+np.float32,0x487fe5ab,0xba140185,2
+np.float32,0x497fe5ab,0x3f7fffd5,2
+np.float32,0x49ffe5ab,0x3f7fff55,2
+np.float32,0x49ffeb37,0x3b9382f5,2
+np.float32,0x497ff0c3,0x3b13049f,2
+np.float32,0x49fff0c3,0xbf7fff57,2
+np.float32,0x49fff64f,0xbb928618,2
+np.float32,0x497ffbdb,0xbf7fffd6,2
+np.float32,0x49fffbdb,0x3f7fff59,2
+np.float32,0x48fffbdb,0xba9207c6,2
+np.float32,0x4e736e56,0xbf800000,2
+np.float32,0x4d4da377,0xbf800000,2
+np.float32,0x4ece58c3,0xbf800000,2
+np.float32,0x4ee0db9c,0xbf800000,2
+np.float32,0x4dee7002,0x3f800000,2
+np.float32,0x4ee86afc,0x38857a23,2
+np.float32,0x4dca4f3f,0xbf800000,2
+np.float32,0x4ecb48af,0xb95d1e10,2
+np.float32,0x4e51e33f,0xbf800000,2
+np.float32,0x4ef5f421,0xbf800000,2
+np.float32,0x46027eb2,0x3e7d94c9,2
+np.float32,0x4477baed,0xbe7f1824,2
+np.float32,0x454b8024,0x3e7f5268,2
+np.float32,0x455d2c09,0x3e7f40cb,2
+np.float32,0x4768d3de,0xba14b4af,2
+np.float32,0x46c1e7cd,0x3e7fb102,2
+np.float32,0x44a52949,0xbe7dc9d5,2
+np.float32,0x4454633a,0x3e7dbc7d,2
+np.float32,0x4689810b,0x3e7eb02b,2
+np.float32,0x473473cd,0xbe7eef6f,2
+np.float32,0x44a5193f,0x3e7e1b1f,2
+np.float32,0x46004b36,0x3e7dac59,2
+np.float32,0x467f604b,0x3d7ffd3a,2
+np.float32,0x45ea1805,0x3dffd2e0,2
+np.float32,0x457b6af3,0x3dff7831,2
+np.float32,0x44996159,0xbe7d85f4,2
+np.float32,0x47883553,0xbb80584e,2
+np.float32,0x44e19f0c,0xbdffcfe6,2
+np.float32,0x472b3bf6,0xbe7f7a82,2
+np.float32,0x4600bb4e,0x3a135e33,2
+np.float32,0x449f4556,0x3e7e42e5,2
+np.float32,0x474e9420,0x3dff77b2,2
+np.float32,0x45cbdb23,0x3dff7240,2
+np.float32,0x44222747,0x3dffb039,2
+np.float32,0x4772e419,0xbdff74b8,2
diff --git a/numpy/core/tests/data/umath-validation-set-exp b/numpy/core/tests/data/umath-validation-set-exp
new file mode 100644
index 000000000..1b2cc9ce4
--- /dev/null
+++ b/numpy/core/tests/data/umath-validation-set-exp
@@ -0,0 +1,135 @@
+dtype,input,output,ulperrortol
+## +ve denormals ##
+np.float32,0x004b4716,0x3f800000,3
+np.float32,0x007b2490,0x3f800000,3
+np.float32,0x007c99fa,0x3f800000,3
+np.float32,0x00734a0c,0x3f800000,3
+np.float32,0x0070de24,0x3f800000,3
+np.float32,0x00495d65,0x3f800000,3
+np.float32,0x006894f6,0x3f800000,3
+np.float32,0x00555a76,0x3f800000,3
+np.float32,0x004e1fb8,0x3f800000,3
+np.float32,0x00687de9,0x3f800000,3
+## -ve denormals ##
+np.float32,0x805b59af,0x3f800000,3
+np.float32,0x807ed8ed,0x3f800000,3
+np.float32,0x807142ad,0x3f800000,3
+np.float32,0x80772002,0x3f800000,3
+np.float32,0x8062abcb,0x3f800000,3
+np.float32,0x8045e31c,0x3f800000,3
+np.float32,0x805f01c2,0x3f800000,3
+np.float32,0x80506432,0x3f800000,3
+np.float32,0x8060089d,0x3f800000,3
+np.float32,0x8071292f,0x3f800000,3
+## floats that output a denormal ##
+np.float32,0xc2cf3fc1,0x00000001,3
+np.float32,0xc2c79726,0x00000021,3
+np.float32,0xc2cb295d,0x00000005,3
+np.float32,0xc2b49e6b,0x00068c4c,3
+np.float32,0xc2ca8116,0x00000008,3
+np.float32,0xc2c23f82,0x000001d7,3
+np.float32,0xc2cb69c0,0x00000005,3
+np.float32,0xc2cc1f4d,0x00000003,3
+np.float32,0xc2ae094e,0x00affc4c,3
+np.float32,0xc2c86c44,0x00000015,3
+## random floats between -87.0f and 88.0f ##
+np.float32,0x4030d7e0,0x417d9a05,3
+np.float32,0x426f60e8,0x6aa1be2c,3
+np.float32,0x41a1b220,0x4e0efc11,3
+np.float32,0xc20cc722,0x26159da7,3
+np.float32,0x41c492bc,0x512ec79d,3
+np.float32,0x40980210,0x42e73a0e,3
+np.float32,0xbf1f7b80,0x3f094de3,3
+np.float32,0x42a678a4,0x7b87a383,3
+np.float32,0xc20f3cfd,0x25a1c304,3
+np.float32,0x423ff34c,0x6216467f,3
+np.float32,0x00000000,0x3f800000,3
+## floats that cause an overflow ##
+np.float32,0x7f06d8c1,0x7f800000,3
+np.float32,0x7f451912,0x7f800000,3
+np.float32,0x7ecceac3,0x7f800000,3
+np.float32,0x7f643b45,0x7f800000,3
+np.float32,0x7e910ea0,0x7f800000,3
+np.float32,0x7eb4756b,0x7f800000,3
+np.float32,0x7f4ec708,0x7f800000,3
+np.float32,0x7f6b4551,0x7f800000,3
+np.float32,0x7d8edbda,0x7f800000,3
+np.float32,0x7f730718,0x7f800000,3
+np.float32,0x42b17217,0x7f7fff84,3
+np.float32,0x42b17218,0x7f800000,3
+np.float32,0x42b17219,0x7f800000,3
+np.float32,0xfef2b0bc,0x00000000,3
+np.float32,0xff69f83e,0x00000000,3
+np.float32,0xff4ecb12,0x00000000,3
+np.float32,0xfeac6d86,0x00000000,3
+np.float32,0xfde0cdb8,0x00000000,3
+np.float32,0xff26aef4,0x00000000,3
+np.float32,0xff6f9277,0x00000000,3
+np.float32,0xff7adfc4,0x00000000,3
+np.float32,0xff0ad40e,0x00000000,3
+np.float32,0xff6fd8f3,0x00000000,3
+np.float32,0xc2cff1b4,0x00000001,3
+np.float32,0xc2cff1b5,0x00000000,3
+np.float32,0xc2cff1b6,0x00000000,3
+np.float32,0x7f800000,0x7f800000,3
+np.float32,0xff800000,0x00000000,3
+np.float32,0x4292f27c,0x7480000a,3
+np.float32,0x42a920be,0x7c7fff94,3
+np.float32,0x41c214c9,0x50ffffd9,3
+np.float32,0x41abe686,0x4effffd9,3
+np.float32,0x4287db5a,0x707fffd3,3
+np.float32,0x41902cbb,0x4c800078,3
+np.float32,0x42609466,0x67ffffeb,3
+np.float32,0x41a65af5,0x4e7fffd1,3
+np.float32,0x417f13ff,0x4affffc9,3
+np.float32,0x426d0e6c,0x6a3504f2,3
+np.float32,0x41bc8934,0x507fff51,3
+np.float32,0x42a7bdde,0x7c0000d6,3
+np.float32,0x4120cf66,0x46b504f6,3
+np.float32,0x4244da8f,0x62ffff1a,3
+np.float32,0x41a0cf69,0x4e000034,3
+np.float32,0x41cd2bec,0x52000005,3
+np.float32,0x42893e41,0x7100009e,3
+np.float32,0x41b437e1,0x4fb50502,3
+np.float32,0x41d8430f,0x5300001d,3
+np.float32,0x4244da92,0x62ffffda,3
+np.float32,0x41a0cf63,0x4dffffa9,3
+np.float32,0x3eb17218,0x3fb504f3,3
+np.float32,0x428729e8,0x703504dc,3
+np.float32,0x41a0cf67,0x4e000014,3
+np.float32,0x4252b77d,0x65800011,3
+np.float32,0x41902cb9,0x4c800058,3
+np.float32,0x42a0cf67,0x79800052,3
+np.float32,0x4152b77b,0x48ffffe9,3
+np.float32,0x41265af3,0x46ffffc8,3
+np.float32,0x42187e0b,0x5affff9a,3
+np.float32,0xc0d2b77c,0x3ab504f6,3
+np.float32,0xc283b2ac,0x10000072,3
+np.float32,0xc1cff1b4,0x2cb504f5,3
+np.float32,0xc05dce9e,0x3d000000,3
+np.float32,0xc28ec9d2,0x0bfffea5,3
+np.float32,0xc23c893a,0x1d7fffde,3
+np.float32,0xc2a920c0,0x027fff6c,3
+np.float32,0xc1f9886f,0x2900002b,3
+np.float32,0xc2c42920,0x000000b5,3
+np.float32,0xc2893e41,0x0dfffec5,3
+np.float32,0xc2c4da93,0x00000080,3
+np.float32,0xc17f1401,0x3400000c,3
+np.float32,0xc1902cb6,0x327fffaf,3
+np.float32,0xc27c4e3b,0x11ffffc5,3
+np.float32,0xc268e5c5,0x157ffe9d,3
+np.float32,0xc2b4e953,0x0005a826,3
+np.float32,0xc287db5a,0x0e800016,3
+np.float32,0xc207db5a,0x2700000b,3
+np.float32,0xc2b2d4fe,0x000ffff1,3
+np.float32,0xc268e5c0,0x157fffdd,3
+np.float32,0xc22920bd,0x2100003b,3
+np.float32,0xc2902caf,0x0b80011e,3
+np.float32,0xc1902cba,0x327fff2f,3
+np.float32,0xc2ca6625,0x00000008,3
+np.float32,0xc280ece8,0x10fffeb5,3
+np.float32,0xc2918f94,0x0b0000ea,3
+np.float32,0xc29b43d5,0x077ffffc,3
+np.float32,0xc1e61ff7,0x2ab504f5,3
+np.float32,0xc2867878,0x0effff15,3
+np.float32,0xc2a2324a,0x04fffff4,3
diff --git a/numpy/core/tests/data/umath-validation-set-log b/numpy/core/tests/data/umath-validation-set-log
new file mode 100644
index 000000000..a7bd98481
--- /dev/null
+++ b/numpy/core/tests/data/umath-validation-set-log
@@ -0,0 +1,118 @@
+dtype,input,output,ulperrortol
+## +ve denormals ##
+np.float32,0x004b4716,0xc2afbc1b,4
+np.float32,0x007b2490,0xc2aec01e,4
+np.float32,0x007c99fa,0xc2aeba17,4
+np.float32,0x00734a0c,0xc2aee1dc,4
+np.float32,0x0070de24,0xc2aeecba,4
+np.float32,0x007fffff,0xc2aeac50,4
+np.float32,0x00000001,0xc2ce8ed0,4
+## -ve denormals ##
+np.float32,0x80495d65,0xffc00000,4
+np.float32,0x806894f6,0xffc00000,4
+np.float32,0x80555a76,0xffc00000,4
+np.float32,0x804e1fb8,0xffc00000,4
+np.float32,0x80687de9,0xffc00000,4
+np.float32,0x807fffff,0xffc00000,4
+np.float32,0x80000001,0xffc00000,4
+## +/-0.0f, +/-FLT_MIN +/-FLT_MAX ##
+np.float32,0x00000000,0xff800000,4
+np.float32,0x80000000,0xff800000,4
+np.float32,0x7f7fffff,0x42b17218,4
+np.float32,0x80800000,0xffc00000,4
+np.float32,0xff7fffff,0xffc00000,4
+## 1.00f + 0x00000001 ##
+np.float32,0x3f800000,0x00000000,4
+np.float32,0x3f800001,0x33ffffff,4
+np.float32,0x3f800002,0x347ffffe,4
+np.float32,0x3f7fffff,0xb3800000,4
+np.float32,0x3f7ffffe,0xb4000000,4
+np.float32,0x3f7ffffd,0xb4400001,4
+np.float32,0x402df853,0x3f7ffffe,4
+np.float32,0x402df854,0x3f7fffff,4
+np.float32,0x402df855,0x3f800000,4
+np.float32,0x402df856,0x3f800001,4
+np.float32,0x3ebc5ab0,0xbf800001,4
+np.float32,0x3ebc5ab1,0xbf800000,4
+np.float32,0x3ebc5ab2,0xbf800000,4
+np.float32,0x3ebc5ab3,0xbf7ffffe,4
+np.float32,0x423ef575,0x407768ab,4
+np.float32,0x427b8c61,0x408485dd,4
+np.float32,0x4211e9ee,0x406630b0,4
+np.float32,0x424d5c41,0x407c0fed,4
+np.float32,0x42be722a,0x4091cc91,4
+np.float32,0x42b73d30,0x4090908b,4
+np.float32,0x427e48e2,0x4084de7f,4
+np.float32,0x428f759b,0x4088bba3,4
+np.float32,0x41629069,0x4029a0cc,4
+np.float32,0x4272c99d,0x40836379,4
+np.float32,0x4d1b7458,0x4197463d,4
+np.float32,0x4f10c594,0x41ace2b2,4
+np.float32,0x4ea397c2,0x41a85171,4
+np.float32,0x4fefa9d1,0x41b6769c,4
+np.float32,0x4ebac6ab,0x41a960dc,4
+np.float32,0x4f6efb42,0x41b0e535,4
+np.float32,0x4e9ab8e7,0x41a7df44,4
+np.float32,0x4e81b5d1,0x41a67625,4
+np.float32,0x5014d9f2,0x41b832bd,4
+np.float32,0x4f02175c,0x41ac07b8,4
+np.float32,0x7f034f89,0x42b01c47,4
+np.float32,0x7f56d00e,0x42b11849,4
+np.float32,0x7f1cd5f6,0x42b0773a,4
+np.float32,0x7e979174,0x42af02d7,4
+np.float32,0x7f23369f,0x42b08ba2,4
+np.float32,0x7f0637ae,0x42b0277d,4
+np.float32,0x7efcb6e8,0x42b00897,4
+np.float32,0x7f7907c8,0x42b163f6,4
+np.float32,0x7e95c4c2,0x42aefcba,4
+np.float32,0x7f4577b2,0x42b0ed2d,4
+np.float32,0x3f49c92e,0xbe73ae84,4
+np.float32,0x3f4a23d1,0xbe71e2f8,4
+np.float32,0x3f4abb67,0xbe6ee430,4
+np.float32,0x3f48169a,0xbe7c5532,4
+np.float32,0x3f47f5fa,0xbe7cfc37,4
+np.float32,0x3f488309,0xbe7a2ad8,4
+np.float32,0x3f479df4,0xbe7ebf5f,4
+np.float32,0x3f47cfff,0xbe7dbec9,4
+np.float32,0x3f496704,0xbe75a125,4
+np.float32,0x3f478ee8,0xbe7f0c92,4
+np.float32,0x3f4a763b,0xbe7041ce,4
+np.float32,0x3f47a108,0xbe7eaf94,4
+np.float32,0x3f48136c,0xbe7c6578,4
+np.float32,0x3f481c17,0xbe7c391c,4
+np.float32,0x3f47cd28,0xbe7dcd56,4
+np.float32,0x3f478be8,0xbe7f1bf7,4
+np.float32,0x3f4c1f8e,0xbe67e367,4
+np.float32,0x3f489b0c,0xbe79b03f,4
+np.float32,0x3f4934cf,0xbe76a08a,4
+np.float32,0x3f4954df,0xbe75fd6a,4
+np.float32,0x3f47a3f5,0xbe7ea093,4
+np.float32,0x3f4ba4fc,0xbe6a4b02,4
+np.float32,0x3f47a0e1,0xbe7eb05c,4
+np.float32,0x3f48c30a,0xbe78e42f,4
+np.float32,0x3f48cab8,0xbe78bd05,4
+np.float32,0x3f4b0569,0xbe6d6ea4,4
+np.float32,0x3f47de32,0xbe7d7607,4
+np.float32,0x3f477328,0xbe7f9b00,4
+np.float32,0x3f496dab,0xbe757f52,4
+np.float32,0x3f47662c,0xbe7fddac,4
+np.float32,0x3f48ddd8,0xbe785b80,4
+np.float32,0x3f481866,0xbe7c4bff,4
+np.float32,0x3f48b119,0xbe793fb6,4
+np.float32,0x3f48c7e8,0xbe78cb5c,4
+np.float32,0x3f4985f6,0xbe7503da,4
+np.float32,0x3f483fdf,0xbe7b8212,4
+np.float32,0x3f4b1c76,0xbe6cfa67,4
+np.float32,0x3f480b2e,0xbe7c8fa8,4
+np.float32,0x3f48745f,0xbe7a75bf,4
+np.float32,0x3f485bda,0xbe7af308,4
+np.float32,0x3f47a660,0xbe7e942c,4
+np.float32,0x3f47d4d5,0xbe7da600,4
+np.float32,0x3f4b0a26,0xbe6d56be,4
+np.float32,0x3f4a4883,0xbe712924,4
+np.float32,0x3f4769e7,0xbe7fca84,4
+np.float32,0x3f499702,0xbe74ad3f,4
+np.float32,0x3f494ab1,0xbe763131,4
+np.float32,0x3f476b69,0xbe7fc2c6,4
+np.float32,0x3f4884e8,0xbe7a214a,4
+np.float32,0x3f486945,0xbe7aae76,4
diff --git a/numpy/core/tests/data/umath-validation-set-sin b/numpy/core/tests/data/umath-validation-set-sin
new file mode 100644
index 000000000..a56273195
--- /dev/null
+++ b/numpy/core/tests/data/umath-validation-set-sin
@@ -0,0 +1,707 @@
+dtype,input,output,ulperrortol
+## +ve denormals ##
+np.float32,0x004b4716,0x004b4716,2
+np.float32,0x007b2490,0x007b2490,2
+np.float32,0x007c99fa,0x007c99fa,2
+np.float32,0x00734a0c,0x00734a0c,2
+np.float32,0x0070de24,0x0070de24,2
+np.float32,0x007fffff,0x007fffff,2
+np.float32,0x00000001,0x00000001,2
+## -ve denormals ##
+np.float32,0x80495d65,0x80495d65,2
+np.float32,0x806894f6,0x806894f6,2
+np.float32,0x80555a76,0x80555a76,2
+np.float32,0x804e1fb8,0x804e1fb8,2
+np.float32,0x80687de9,0x80687de9,2
+np.float32,0x807fffff,0x807fffff,2
+np.float32,0x80000001,0x80000001,2
+## +/-0.0f, +/-FLT_MIN +/-FLT_MAX ##
+np.float32,0x00000000,0x00000000,2
+np.float32,0x80000000,0x80000000,2
+np.float32,0x00800000,0x00800000,2
+np.float32,0x7f7fffff,0xbf0599b3,2
+np.float32,0x80800000,0x80800000,2
+np.float32,0xff7fffff,0x3f0599b3,2
+## 1.00f ##
+np.float32,0x3f800000,0x3f576aa4,2
+np.float32,0x3f800001,0x3f576aa6,2
+np.float32,0x3f800002,0x3f576aa7,2
+np.float32,0xc090a8b0,0x3f7b4e48,2
+np.float32,0x41ce3184,0x3f192d43,2
+np.float32,0xc1d85848,0xbf7161cb,2
+np.float32,0x402b8820,0x3ee3f29f,2
+np.float32,0x42b4e454,0x3f1d0151,2
+np.float32,0x42a67a60,0x3f7ffa4c,2
+np.float32,0x41d92388,0x3f67beef,2
+np.float32,0x422dd66c,0xbeffb0c1,2
+np.float32,0xc28f5be6,0xbf0bae79,2
+np.float32,0x41ab2674,0x3f0ffe2b,2
+np.float32,0xd0102756,0x3f227e8a,2
+np.float32,0xcf99405e,0x3f73ad00,2
+np.float32,0xcfd83a12,0xbf7151a7,2
+np.float32,0x4fb54db0,0xbe46354b,2
+np.float32,0xcfcca29d,0xbe9345e6,2
+np.float32,0xceec2ac0,0x3e98dc89,2
+np.float32,0xcfdca97f,0xbf60b2b4,2
+np.float32,0xcfe92b0a,0xbf222705,2
+np.float32,0x5014b0eb,0x3f63e75c,2
+np.float32,0xcfa7ee96,0x3f62ada4,2
+np.float32,0x754c09a0,0xbf617056,2
+np.float32,0x77a731fb,0x3f44472b,2
+np.float32,0x76de2494,0xbe680739,2
+np.float32,0xf74920dc,0xbf193338,2
+np.float32,0x7707a312,0xbf6f51b1,2
+np.float32,0x75bf9790,0xbd0f1a47,2
+np.float32,0xf4ca7c40,0xbf7d45e7,2
+np.float32,0x77e91899,0x3f767181,2
+np.float32,0xf74c9820,0xbd685b75,2
+np.float32,0x7785ca29,0x3f78ee61,2
+np.float32,0x3f490fdb,0x3f3504f3,2
+np.float32,0xbf490fdb,0xbf3504f3,2
+np.float32,0x3fc90fdb,0x3f800000,2
+np.float32,0xbfc90fdb,0xbf800000,2
+np.float32,0x40490fdb,0xb3bbbd2e,2
+np.float32,0xc0490fdb,0x33bbbd2e,2
+np.float32,0x3fc90fdb,0x3f800000,2
+np.float32,0xbfc90fdb,0xbf800000,2
+np.float32,0x40490fdb,0xb3bbbd2e,2
+np.float32,0xc0490fdb,0x33bbbd2e,2
+np.float32,0x40c90fdb,0x343bbd2e,2
+np.float32,0xc0c90fdb,0xb43bbd2e,2
+np.float32,0x4016cbe4,0x3f3504f3,2
+np.float32,0xc016cbe4,0xbf3504f3,2
+np.float32,0x4096cbe4,0xbf800000,2
+np.float32,0xc096cbe4,0x3f800000,2
+np.float32,0x4116cbe4,0xb2ccde2e,2
+np.float32,0xc116cbe4,0x32ccde2e,2
+np.float32,0x40490fdb,0xb3bbbd2e,2
+np.float32,0xc0490fdb,0x33bbbd2e,2
+np.float32,0x40c90fdb,0x343bbd2e,2
+np.float32,0xc0c90fdb,0xb43bbd2e,2
+np.float32,0x41490fdb,0x34bbbd2e,2
+np.float32,0xc1490fdb,0xb4bbbd2e,2
+np.float32,0x407b53d2,0xbf3504f5,2
+np.float32,0xc07b53d2,0x3f3504f5,2
+np.float32,0x40fb53d2,0x3f800000,2
+np.float32,0xc0fb53d2,0xbf800000,2
+np.float32,0x417b53d2,0xb535563d,2
+np.float32,0xc17b53d2,0x3535563d,2
+np.float32,0x4096cbe4,0xbf800000,2
+np.float32,0xc096cbe4,0x3f800000,2
+np.float32,0x4116cbe4,0xb2ccde2e,2
+np.float32,0xc116cbe4,0x32ccde2e,2
+np.float32,0x4196cbe4,0x334cde2e,2
+np.float32,0xc196cbe4,0xb34cde2e,2
+np.float32,0x40afede0,0xbf3504ef,2
+np.float32,0xc0afede0,0x3f3504ef,2
+np.float32,0x412fede0,0xbf800000,2
+np.float32,0xc12fede0,0x3f800000,2
+np.float32,0x41afede0,0xb5b222c4,2
+np.float32,0xc1afede0,0x35b222c4,2
+np.float32,0x40c90fdb,0x343bbd2e,2
+np.float32,0xc0c90fdb,0xb43bbd2e,2
+np.float32,0x41490fdb,0x34bbbd2e,2
+np.float32,0xc1490fdb,0xb4bbbd2e,2
+np.float32,0x41c90fdb,0x353bbd2e,2
+np.float32,0xc1c90fdb,0xb53bbd2e,2
+np.float32,0x40e231d6,0x3f3504f3,2
+np.float32,0xc0e231d6,0xbf3504f3,2
+np.float32,0x416231d6,0x3f800000,2
+np.float32,0xc16231d6,0xbf800000,2
+np.float32,0x41e231d6,0xb399a6a2,2
+np.float32,0xc1e231d6,0x3399a6a2,2
+np.float32,0x40fb53d2,0x3f800000,2
+np.float32,0xc0fb53d2,0xbf800000,2
+np.float32,0x417b53d2,0xb535563d,2
+np.float32,0xc17b53d2,0x3535563d,2
+np.float32,0x41fb53d2,0x35b5563d,2
+np.float32,0xc1fb53d2,0xb5b5563d,2
+np.float32,0x410a3ae7,0x3f3504eb,2
+np.float32,0xc10a3ae7,0xbf3504eb,2
+np.float32,0x418a3ae7,0xbf800000,2
+np.float32,0xc18a3ae7,0x3f800000,2
+np.float32,0x420a3ae7,0xb6308908,2
+np.float32,0xc20a3ae7,0x36308908,2
+np.float32,0x4116cbe4,0xb2ccde2e,2
+np.float32,0xc116cbe4,0x32ccde2e,2
+np.float32,0x4196cbe4,0x334cde2e,2
+np.float32,0xc196cbe4,0xb34cde2e,2
+np.float32,0x4216cbe4,0x33ccde2e,2
+np.float32,0xc216cbe4,0xb3ccde2e,2
+np.float32,0x41235ce2,0xbf3504f7,2
+np.float32,0xc1235ce2,0x3f3504f7,2
+np.float32,0x41a35ce2,0x3f800000,2
+np.float32,0xc1a35ce2,0xbf800000,2
+np.float32,0x42235ce2,0xb5b889b6,2
+np.float32,0xc2235ce2,0x35b889b6,2
+np.float32,0x412fede0,0xbf800000,2
+np.float32,0xc12fede0,0x3f800000,2
+np.float32,0x41afede0,0xb5b222c4,2
+np.float32,0xc1afede0,0x35b222c4,2
+np.float32,0x422fede0,0x363222c4,2
+np.float32,0xc22fede0,0xb63222c4,2
+np.float32,0x413c7edd,0xbf3504f3,2
+np.float32,0xc13c7edd,0x3f3504f3,2
+np.float32,0x41bc7edd,0xbf800000,2
+np.float32,0xc1bc7edd,0x3f800000,2
+np.float32,0x423c7edd,0xb4000add,2
+np.float32,0xc23c7edd,0x34000add,2
+np.float32,0x41490fdb,0x34bbbd2e,2
+np.float32,0xc1490fdb,0xb4bbbd2e,2
+np.float32,0x41c90fdb,0x353bbd2e,2
+np.float32,0xc1c90fdb,0xb53bbd2e,2
+np.float32,0x42490fdb,0x35bbbd2e,2
+np.float32,0xc2490fdb,0xb5bbbd2e,2
+np.float32,0x4155a0d9,0x3f3504fb,2
+np.float32,0xc155a0d9,0xbf3504fb,2
+np.float32,0x41d5a0d9,0x3f800000,2
+np.float32,0xc1d5a0d9,0xbf800000,2
+np.float32,0x4255a0d9,0xb633bc81,2
+np.float32,0xc255a0d9,0x3633bc81,2
+np.float32,0x416231d6,0x3f800000,2
+np.float32,0xc16231d6,0xbf800000,2
+np.float32,0x41e231d6,0xb399a6a2,2
+np.float32,0xc1e231d6,0x3399a6a2,2
+np.float32,0x426231d6,0x3419a6a2,2
+np.float32,0xc26231d6,0xb419a6a2,2
+np.float32,0x416ec2d4,0x3f3504ef,2
+np.float32,0xc16ec2d4,0xbf3504ef,2
+np.float32,0x41eec2d4,0xbf800000,2
+np.float32,0xc1eec2d4,0x3f800000,2
+np.float32,0x426ec2d4,0xb5bef0a7,2
+np.float32,0xc26ec2d4,0x35bef0a7,2
+np.float32,0x417b53d2,0xb535563d,2
+np.float32,0xc17b53d2,0x3535563d,2
+np.float32,0x41fb53d2,0x35b5563d,2
+np.float32,0xc1fb53d2,0xb5b5563d,2
+np.float32,0x427b53d2,0x3635563d,2
+np.float32,0xc27b53d2,0xb635563d,2
+np.float32,0x4183f268,0xbf3504ff,2
+np.float32,0xc183f268,0x3f3504ff,2
+np.float32,0x4203f268,0x3f800000,2
+np.float32,0xc203f268,0xbf800000,2
+np.float32,0x4283f268,0xb6859a13,2
+np.float32,0xc283f268,0x36859a13,2
+np.float32,0x418a3ae7,0xbf800000,2
+np.float32,0xc18a3ae7,0x3f800000,2
+np.float32,0x420a3ae7,0xb6308908,2
+np.float32,0xc20a3ae7,0x36308908,2
+np.float32,0x428a3ae7,0x36b08908,2
+np.float32,0xc28a3ae7,0xb6b08908,2
+np.float32,0x41908365,0xbf3504f6,2
+np.float32,0xc1908365,0x3f3504f6,2
+np.float32,0x42108365,0xbf800000,2
+np.float32,0xc2108365,0x3f800000,2
+np.float32,0x42908365,0x3592200d,2
+np.float32,0xc2908365,0xb592200d,2
+np.float32,0x4196cbe4,0x334cde2e,2
+np.float32,0xc196cbe4,0xb34cde2e,2
+np.float32,0x4216cbe4,0x33ccde2e,2
+np.float32,0xc216cbe4,0xb3ccde2e,2
+np.float32,0x4296cbe4,0x344cde2e,2
+np.float32,0xc296cbe4,0xb44cde2e,2
+np.float32,0x419d1463,0x3f3504f8,2
+np.float32,0xc19d1463,0xbf3504f8,2
+np.float32,0x421d1463,0x3f800000,2
+np.float32,0xc21d1463,0xbf800000,2
+np.float32,0x429d1463,0xb5c55799,2
+np.float32,0xc29d1463,0x35c55799,2
+np.float32,0x41a35ce2,0x3f800000,2
+np.float32,0xc1a35ce2,0xbf800000,2
+np.float32,0x42235ce2,0xb5b889b6,2
+np.float32,0xc2235ce2,0x35b889b6,2
+np.float32,0x42a35ce2,0x363889b6,2
+np.float32,0xc2a35ce2,0xb63889b6,2
+np.float32,0x41a9a561,0x3f3504e7,2
+np.float32,0xc1a9a561,0xbf3504e7,2
+np.float32,0x4229a561,0xbf800000,2
+np.float32,0xc229a561,0x3f800000,2
+np.float32,0x42a9a561,0xb68733d0,2
+np.float32,0xc2a9a561,0x368733d0,2
+np.float32,0x41afede0,0xb5b222c4,2
+np.float32,0xc1afede0,0x35b222c4,2
+np.float32,0x422fede0,0x363222c4,2
+np.float32,0xc22fede0,0xb63222c4,2
+np.float32,0x42afede0,0x36b222c4,2
+np.float32,0xc2afede0,0xb6b222c4,2
+np.float32,0x41b6365e,0xbf3504f0,2
+np.float32,0xc1b6365e,0x3f3504f0,2
+np.float32,0x4236365e,0x3f800000,2
+np.float32,0xc236365e,0xbf800000,2
+np.float32,0x42b6365e,0x358bb91c,2
+np.float32,0xc2b6365e,0xb58bb91c,2
+np.float32,0x41bc7edd,0xbf800000,2
+np.float32,0xc1bc7edd,0x3f800000,2
+np.float32,0x423c7edd,0xb4000add,2
+np.float32,0xc23c7edd,0x34000add,2
+np.float32,0x42bc7edd,0x34800add,2
+np.float32,0xc2bc7edd,0xb4800add,2
+np.float32,0x41c2c75c,0xbf3504ef,2
+np.float32,0xc1c2c75c,0x3f3504ef,2
+np.float32,0x4242c75c,0xbf800000,2
+np.float32,0xc242c75c,0x3f800000,2
+np.float32,0x42c2c75c,0xb5cbbe8a,2
+np.float32,0xc2c2c75c,0x35cbbe8a,2
+np.float32,0x41c90fdb,0x353bbd2e,2
+np.float32,0xc1c90fdb,0xb53bbd2e,2
+np.float32,0x42490fdb,0x35bbbd2e,2
+np.float32,0xc2490fdb,0xb5bbbd2e,2
+np.float32,0x42c90fdb,0x363bbd2e,2
+np.float32,0xc2c90fdb,0xb63bbd2e,2
+np.float32,0x41cf585a,0x3f3504ff,2
+np.float32,0xc1cf585a,0xbf3504ff,2
+np.float32,0x424f585a,0x3f800000,2
+np.float32,0xc24f585a,0xbf800000,2
+np.float32,0x42cf585a,0xb688cd8c,2
+np.float32,0xc2cf585a,0x3688cd8c,2
+np.float32,0x41d5a0d9,0x3f800000,2
+np.float32,0xc1d5a0d9,0xbf800000,2
+np.float32,0x4255a0d9,0xb633bc81,2
+np.float32,0xc255a0d9,0x3633bc81,2
+np.float32,0x42d5a0d9,0x36b3bc81,2
+np.float32,0xc2d5a0d9,0xb6b3bc81,2
+np.float32,0x41dbe958,0x3f3504e0,2
+np.float32,0xc1dbe958,0xbf3504e0,2
+np.float32,0x425be958,0xbf800000,2
+np.float32,0xc25be958,0x3f800000,2
+np.float32,0x42dbe958,0xb6deab75,2
+np.float32,0xc2dbe958,0x36deab75,2
+np.float32,0x41e231d6,0xb399a6a2,2
+np.float32,0xc1e231d6,0x3399a6a2,2
+np.float32,0x426231d6,0x3419a6a2,2
+np.float32,0xc26231d6,0xb419a6a2,2
+np.float32,0x42e231d6,0x3499a6a2,2
+np.float32,0xc2e231d6,0xb499a6a2,2
+np.float32,0x41e87a55,0xbf3504f8,2
+np.float32,0xc1e87a55,0x3f3504f8,2
+np.float32,0x42687a55,0x3f800000,2
+np.float32,0xc2687a55,0xbf800000,2
+np.float32,0x42e87a55,0xb5d2257b,2
+np.float32,0xc2e87a55,0x35d2257b,2
+np.float32,0x41eec2d4,0xbf800000,2
+np.float32,0xc1eec2d4,0x3f800000,2
+np.float32,0x426ec2d4,0xb5bef0a7,2
+np.float32,0xc26ec2d4,0x35bef0a7,2
+np.float32,0x42eec2d4,0x363ef0a7,2
+np.float32,0xc2eec2d4,0xb63ef0a7,2
+np.float32,0x41f50b53,0xbf3504e7,2
+np.float32,0xc1f50b53,0x3f3504e7,2
+np.float32,0x42750b53,0xbf800000,2
+np.float32,0xc2750b53,0x3f800000,2
+np.float32,0x42f50b53,0xb68a6748,2
+np.float32,0xc2f50b53,0x368a6748,2
+np.float32,0x41fb53d2,0x35b5563d,2
+np.float32,0xc1fb53d2,0xb5b5563d,2
+np.float32,0x427b53d2,0x3635563d,2
+np.float32,0xc27b53d2,0xb635563d,2
+np.float32,0x42fb53d2,0x36b5563d,2
+np.float32,0xc2fb53d2,0xb6b5563d,2
+np.float32,0x4200ce28,0x3f3504f0,2
+np.float32,0xc200ce28,0xbf3504f0,2
+np.float32,0x4280ce28,0x3f800000,2
+np.float32,0xc280ce28,0xbf800000,2
+np.float32,0x4300ce28,0x357dd672,2
+np.float32,0xc300ce28,0xb57dd672,2
+np.float32,0x4203f268,0x3f800000,2
+np.float32,0xc203f268,0xbf800000,2
+np.float32,0x4283f268,0xb6859a13,2
+np.float32,0xc283f268,0x36859a13,2
+np.float32,0x4303f268,0x37059a13,2
+np.float32,0xc303f268,0xb7059a13,2
+np.float32,0x420716a7,0x3f3504ee,2
+np.float32,0xc20716a7,0xbf3504ee,2
+np.float32,0x428716a7,0xbf800000,2
+np.float32,0xc28716a7,0x3f800000,2
+np.float32,0x430716a7,0xb5d88c6d,2
+np.float32,0xc30716a7,0x35d88c6d,2
+np.float32,0x420a3ae7,0xb6308908,2
+np.float32,0xc20a3ae7,0x36308908,2
+np.float32,0x428a3ae7,0x36b08908,2
+np.float32,0xc28a3ae7,0xb6b08908,2
+np.float32,0x430a3ae7,0x37308908,2
+np.float32,0xc30a3ae7,0xb7308908,2
+np.float32,0x420d5f26,0xbf350500,2
+np.float32,0xc20d5f26,0x3f350500,2
+np.float32,0x428d5f26,0x3f800000,2
+np.float32,0xc28d5f26,0xbf800000,2
+np.float32,0x430d5f26,0xb68c0105,2
+np.float32,0xc30d5f26,0x368c0105,2
+np.float32,0x42108365,0xbf800000,2
+np.float32,0xc2108365,0x3f800000,2
+np.float32,0x42908365,0x3592200d,2
+np.float32,0xc2908365,0xb592200d,2
+np.float32,0x43108365,0xb612200d,2
+np.float32,0xc3108365,0x3612200d,2
+np.float32,0x4213a7a5,0xbf3504df,2
+np.float32,0xc213a7a5,0x3f3504df,2
+np.float32,0x4293a7a5,0xbf800000,2
+np.float32,0xc293a7a5,0x3f800000,2
+np.float32,0x4313a7a5,0xb6e1deee,2
+np.float32,0xc313a7a5,0x36e1deee,2
+np.float32,0x4216cbe4,0x33ccde2e,2
+np.float32,0xc216cbe4,0xb3ccde2e,2
+np.float32,0x4296cbe4,0x344cde2e,2
+np.float32,0xc296cbe4,0xb44cde2e,2
+np.float32,0x4316cbe4,0x34ccde2e,2
+np.float32,0xc316cbe4,0xb4ccde2e,2
+np.float32,0x4219f024,0x3f35050f,2
+np.float32,0xc219f024,0xbf35050f,2
+np.float32,0x4299f024,0x3f800000,2
+np.float32,0xc299f024,0xbf800000,2
+np.float32,0x4319f024,0xb71bde6c,2
+np.float32,0xc319f024,0x371bde6c,2
+np.float32,0x421d1463,0x3f800000,2
+np.float32,0xc21d1463,0xbf800000,2
+np.float32,0x429d1463,0xb5c55799,2
+np.float32,0xc29d1463,0x35c55799,2
+np.float32,0x431d1463,0x36455799,2
+np.float32,0xc31d1463,0xb6455799,2
+np.float32,0x422038a3,0x3f3504d0,2
+np.float32,0xc22038a3,0xbf3504d0,2
+np.float32,0x42a038a3,0xbf800000,2
+np.float32,0xc2a038a3,0x3f800000,2
+np.float32,0x432038a3,0xb746cd61,2
+np.float32,0xc32038a3,0x3746cd61,2
+np.float32,0x42235ce2,0xb5b889b6,2
+np.float32,0xc2235ce2,0x35b889b6,2
+np.float32,0x42a35ce2,0x363889b6,2
+np.float32,0xc2a35ce2,0xb63889b6,2
+np.float32,0x43235ce2,0x36b889b6,2
+np.float32,0xc3235ce2,0xb6b889b6,2
+np.float32,0x42268121,0xbf3504f1,2
+np.float32,0xc2268121,0x3f3504f1,2
+np.float32,0x42a68121,0x3f800000,2
+np.float32,0xc2a68121,0xbf800000,2
+np.float32,0x43268121,0x35643aac,2
+np.float32,0xc3268121,0xb5643aac,2
+np.float32,0x4229a561,0xbf800000,2
+np.float32,0xc229a561,0x3f800000,2
+np.float32,0x42a9a561,0xb68733d0,2
+np.float32,0xc2a9a561,0x368733d0,2
+np.float32,0x4329a561,0x370733d0,2
+np.float32,0xc329a561,0xb70733d0,2
+np.float32,0x422cc9a0,0xbf3504ee,2
+np.float32,0xc22cc9a0,0x3f3504ee,2
+np.float32,0x42acc9a0,0xbf800000,2
+np.float32,0xc2acc9a0,0x3f800000,2
+np.float32,0x432cc9a0,0xb5e55a50,2
+np.float32,0xc32cc9a0,0x35e55a50,2
+np.float32,0x422fede0,0x363222c4,2
+np.float32,0xc22fede0,0xb63222c4,2
+np.float32,0x42afede0,0x36b222c4,2
+np.float32,0xc2afede0,0xb6b222c4,2
+np.float32,0x432fede0,0x373222c4,2
+np.float32,0xc32fede0,0xb73222c4,2
+np.float32,0x4233121f,0x3f350500,2
+np.float32,0xc233121f,0xbf350500,2
+np.float32,0x42b3121f,0x3f800000,2
+np.float32,0xc2b3121f,0xbf800000,2
+np.float32,0x4333121f,0xb68f347d,2
+np.float32,0xc333121f,0x368f347d,2
+np.float32,0x4236365e,0x3f800000,2
+np.float32,0xc236365e,0xbf800000,2
+np.float32,0x42b6365e,0x358bb91c,2
+np.float32,0xc2b6365e,0xb58bb91c,2
+np.float32,0x4336365e,0xb60bb91c,2
+np.float32,0xc336365e,0x360bb91c,2
+np.float32,0x42395a9e,0x3f3504df,2
+np.float32,0xc2395a9e,0xbf3504df,2
+np.float32,0x42b95a9e,0xbf800000,2
+np.float32,0xc2b95a9e,0x3f800000,2
+np.float32,0x43395a9e,0xb6e51267,2
+np.float32,0xc3395a9e,0x36e51267,2
+np.float32,0x423c7edd,0xb4000add,2
+np.float32,0xc23c7edd,0x34000add,2
+np.float32,0x42bc7edd,0x34800add,2
+np.float32,0xc2bc7edd,0xb4800add,2
+np.float32,0x433c7edd,0x35000add,2
+np.float32,0xc33c7edd,0xb5000add,2
+np.float32,0x423fa31d,0xbf35050f,2
+np.float32,0xc23fa31d,0x3f35050f,2
+np.float32,0x42bfa31d,0x3f800000,2
+np.float32,0xc2bfa31d,0xbf800000,2
+np.float32,0x433fa31d,0xb71d7828,2
+np.float32,0xc33fa31d,0x371d7828,2
+np.float32,0x4242c75c,0xbf800000,2
+np.float32,0xc242c75c,0x3f800000,2
+np.float32,0x42c2c75c,0xb5cbbe8a,2
+np.float32,0xc2c2c75c,0x35cbbe8a,2
+np.float32,0x4342c75c,0x364bbe8a,2
+np.float32,0xc342c75c,0xb64bbe8a,2
+np.float32,0x4245eb9c,0xbf3504d0,2
+np.float32,0xc245eb9c,0x3f3504d0,2
+np.float32,0x42c5eb9c,0xbf800000,2
+np.float32,0xc2c5eb9c,0x3f800000,2
+np.float32,0x4345eb9c,0xb748671d,2
+np.float32,0xc345eb9c,0x3748671d,2
+np.float32,0x42490fdb,0x35bbbd2e,2
+np.float32,0xc2490fdb,0xb5bbbd2e,2
+np.float32,0x42c90fdb,0x363bbd2e,2
+np.float32,0xc2c90fdb,0xb63bbd2e,2
+np.float32,0x43490fdb,0x36bbbd2e,2
+np.float32,0xc3490fdb,0xb6bbbd2e,2
+np.float32,0x424c341a,0x3f3504f1,2
+np.float32,0xc24c341a,0xbf3504f1,2
+np.float32,0x42cc341a,0x3f800000,2
+np.float32,0xc2cc341a,0xbf800000,2
+np.float32,0x434c341a,0x354a9ee6,2
+np.float32,0xc34c341a,0xb54a9ee6,2
+np.float32,0x424f585a,0x3f800000,2
+np.float32,0xc24f585a,0xbf800000,2
+np.float32,0x42cf585a,0xb688cd8c,2
+np.float32,0xc2cf585a,0x3688cd8c,2
+np.float32,0x434f585a,0x3708cd8c,2
+np.float32,0xc34f585a,0xb708cd8c,2
+np.float32,0x42527c99,0x3f3504ee,2
+np.float32,0xc2527c99,0xbf3504ee,2
+np.float32,0x42d27c99,0xbf800000,2
+np.float32,0xc2d27c99,0x3f800000,2
+np.float32,0x43527c99,0xb5f22833,2
+np.float32,0xc3527c99,0x35f22833,2
+np.float32,0x4255a0d9,0xb633bc81,2
+np.float32,0xc255a0d9,0x3633bc81,2
+np.float32,0x42d5a0d9,0x36b3bc81,2
+np.float32,0xc2d5a0d9,0xb6b3bc81,2
+np.float32,0x4355a0d9,0x3733bc81,2
+np.float32,0xc355a0d9,0xb733bc81,2
+np.float32,0x4258c518,0xbf350500,2
+np.float32,0xc258c518,0x3f350500,2
+np.float32,0x42d8c518,0x3f800000,2
+np.float32,0xc2d8c518,0xbf800000,2
+np.float32,0x4358c518,0xb69267f6,2
+np.float32,0xc358c518,0x369267f6,2
+np.float32,0x425be958,0xbf800000,2
+np.float32,0xc25be958,0x3f800000,2
+np.float32,0x42dbe958,0xb6deab75,2
+np.float32,0xc2dbe958,0x36deab75,2
+np.float32,0x435be958,0x375eab75,2
+np.float32,0xc35be958,0xb75eab75,2
+np.float32,0x425f0d97,0xbf3504df,2
+np.float32,0xc25f0d97,0x3f3504df,2
+np.float32,0x42df0d97,0xbf800000,2
+np.float32,0xc2df0d97,0x3f800000,2
+np.float32,0x435f0d97,0xb6e845e0,2
+np.float32,0xc35f0d97,0x36e845e0,2
+np.float32,0x426231d6,0x3419a6a2,2
+np.float32,0xc26231d6,0xb419a6a2,2
+np.float32,0x42e231d6,0x3499a6a2,2
+np.float32,0xc2e231d6,0xb499a6a2,2
+np.float32,0x436231d6,0x3519a6a2,2
+np.float32,0xc36231d6,0xb519a6a2,2
+np.float32,0x42655616,0x3f35050f,2
+np.float32,0xc2655616,0xbf35050f,2
+np.float32,0x42e55616,0x3f800000,2
+np.float32,0xc2e55616,0xbf800000,2
+np.float32,0x43655616,0xb71f11e5,2
+np.float32,0xc3655616,0x371f11e5,2
+np.float32,0x42687a55,0x3f800000,2
+np.float32,0xc2687a55,0xbf800000,2
+np.float32,0x42e87a55,0xb5d2257b,2
+np.float32,0xc2e87a55,0x35d2257b,2
+np.float32,0x43687a55,0x3652257b,2
+np.float32,0xc3687a55,0xb652257b,2
+np.float32,0x426b9e95,0x3f3504cf,2
+np.float32,0xc26b9e95,0xbf3504cf,2
+np.float32,0x42eb9e95,0xbf800000,2
+np.float32,0xc2eb9e95,0x3f800000,2
+np.float32,0x436b9e95,0xb74a00d9,2
+np.float32,0xc36b9e95,0x374a00d9,2
+np.float32,0x426ec2d4,0xb5bef0a7,2
+np.float32,0xc26ec2d4,0x35bef0a7,2
+np.float32,0x42eec2d4,0x363ef0a7,2
+np.float32,0xc2eec2d4,0xb63ef0a7,2
+np.float32,0x436ec2d4,0x36bef0a7,2
+np.float32,0xc36ec2d4,0xb6bef0a7,2
+np.float32,0x4271e713,0xbf3504f1,2
+np.float32,0xc271e713,0x3f3504f1,2
+np.float32,0x42f1e713,0x3f800000,2
+np.float32,0xc2f1e713,0xbf800000,2
+np.float32,0x4371e713,0x35310321,2
+np.float32,0xc371e713,0xb5310321,2
+np.float32,0x42750b53,0xbf800000,2
+np.float32,0xc2750b53,0x3f800000,2
+np.float32,0x42f50b53,0xb68a6748,2
+np.float32,0xc2f50b53,0x368a6748,2
+np.float32,0x43750b53,0x370a6748,2
+np.float32,0xc3750b53,0xb70a6748,2
+np.float32,0x42782f92,0xbf3504ee,2
+np.float32,0xc2782f92,0x3f3504ee,2
+np.float32,0x42f82f92,0xbf800000,2
+np.float32,0xc2f82f92,0x3f800000,2
+np.float32,0x43782f92,0xb5fef616,2
+np.float32,0xc3782f92,0x35fef616,2
+np.float32,0x427b53d2,0x3635563d,2
+np.float32,0xc27b53d2,0xb635563d,2
+np.float32,0x42fb53d2,0x36b5563d,2
+np.float32,0xc2fb53d2,0xb6b5563d,2
+np.float32,0x437b53d2,0x3735563d,2
+np.float32,0xc37b53d2,0xb735563d,2
+np.float32,0x427e7811,0x3f350500,2
+np.float32,0xc27e7811,0xbf350500,2
+np.float32,0x42fe7811,0x3f800000,2
+np.float32,0xc2fe7811,0xbf800000,2
+np.float32,0x437e7811,0xb6959b6f,2
+np.float32,0xc37e7811,0x36959b6f,2
+np.float32,0x4280ce28,0x3f800000,2
+np.float32,0xc280ce28,0xbf800000,2
+np.float32,0x4300ce28,0x357dd672,2
+np.float32,0xc300ce28,0xb57dd672,2
+np.float32,0x4380ce28,0xb5fdd672,2
+np.float32,0xc380ce28,0x35fdd672,2
+np.float32,0x42826048,0x3f3504de,2
+np.float32,0xc2826048,0xbf3504de,2
+np.float32,0x43026048,0xbf800000,2
+np.float32,0xc3026048,0x3f800000,2
+np.float32,0x43826048,0xb6eb7958,2
+np.float32,0xc3826048,0x36eb7958,2
+np.float32,0x4283f268,0xb6859a13,2
+np.float32,0xc283f268,0x36859a13,2
+np.float32,0x4303f268,0x37059a13,2
+np.float32,0xc303f268,0xb7059a13,2
+np.float32,0x4383f268,0x37859a13,2
+np.float32,0xc383f268,0xb7859a13,2
+np.float32,0x42858487,0xbf3504e2,2
+np.float32,0xc2858487,0x3f3504e2,2
+np.float32,0x43058487,0x3f800000,2
+np.float32,0xc3058487,0xbf800000,2
+np.float32,0x43858487,0x36bea8be,2
+np.float32,0xc3858487,0xb6bea8be,2
+np.float32,0x428716a7,0xbf800000,2
+np.float32,0xc28716a7,0x3f800000,2
+np.float32,0x430716a7,0xb5d88c6d,2
+np.float32,0xc30716a7,0x35d88c6d,2
+np.float32,0x438716a7,0x36588c6d,2
+np.float32,0xc38716a7,0xb6588c6d,2
+np.float32,0x4288a8c7,0xbf3504cf,2
+np.float32,0xc288a8c7,0x3f3504cf,2
+np.float32,0x4308a8c7,0xbf800000,2
+np.float32,0xc308a8c7,0x3f800000,2
+np.float32,0x4388a8c7,0xb74b9a96,2
+np.float32,0xc388a8c7,0x374b9a96,2
+np.float32,0x428a3ae7,0x36b08908,2
+np.float32,0xc28a3ae7,0xb6b08908,2
+np.float32,0x430a3ae7,0x37308908,2
+np.float32,0xc30a3ae7,0xb7308908,2
+np.float32,0x438a3ae7,0x37b08908,2
+np.float32,0xc38a3ae7,0xb7b08908,2
+np.float32,0x428bcd06,0x3f3504f2,2
+np.float32,0xc28bcd06,0xbf3504f2,2
+np.float32,0x430bcd06,0x3f800000,2
+np.float32,0xc30bcd06,0xbf800000,2
+np.float32,0x438bcd06,0x3517675b,2
+np.float32,0xc38bcd06,0xb517675b,2
+np.float32,0x428d5f26,0x3f800000,2
+np.float32,0xc28d5f26,0xbf800000,2
+np.float32,0x430d5f26,0xb68c0105,2
+np.float32,0xc30d5f26,0x368c0105,2
+np.float32,0x438d5f26,0x370c0105,2
+np.float32,0xc38d5f26,0xb70c0105,2
+np.float32,0x428ef146,0x3f3504c0,2
+np.float32,0xc28ef146,0xbf3504c0,2
+np.float32,0x430ef146,0xbf800000,2
+np.float32,0xc30ef146,0x3f800000,2
+np.float32,0x438ef146,0xb790bc40,2
+np.float32,0xc38ef146,0x3790bc40,2
+np.float32,0x42908365,0x3592200d,2
+np.float32,0xc2908365,0xb592200d,2
+np.float32,0x43108365,0xb612200d,2
+np.float32,0xc3108365,0x3612200d,2
+np.float32,0x43908365,0xb692200d,2
+np.float32,0xc3908365,0x3692200d,2
+np.float32,0x42921585,0xbf350501,2
+np.float32,0xc2921585,0x3f350501,2
+np.float32,0x43121585,0x3f800000,2
+np.float32,0xc3121585,0xbf800000,2
+np.float32,0x43921585,0xb698cee8,2
+np.float32,0xc3921585,0x3698cee8,2
+np.float32,0x4293a7a5,0xbf800000,2
+np.float32,0xc293a7a5,0x3f800000,2
+np.float32,0x4313a7a5,0xb6e1deee,2
+np.float32,0xc313a7a5,0x36e1deee,2
+np.float32,0x4393a7a5,0x3761deee,2
+np.float32,0xc393a7a5,0xb761deee,2
+np.float32,0x429539c5,0xbf3504b1,2
+np.float32,0xc29539c5,0x3f3504b1,2
+np.float32,0x431539c5,0xbf800000,2
+np.float32,0xc31539c5,0x3f800000,2
+np.float32,0x439539c5,0xb7bbab34,2
+np.float32,0xc39539c5,0x37bbab34,2
+np.float32,0x4296cbe4,0x344cde2e,2
+np.float32,0xc296cbe4,0xb44cde2e,2
+np.float32,0x4316cbe4,0x34ccde2e,2
+np.float32,0xc316cbe4,0xb4ccde2e,2
+np.float32,0x4396cbe4,0x354cde2e,2
+np.float32,0xc396cbe4,0xb54cde2e,2
+np.float32,0x42985e04,0x3f350510,2
+np.float32,0xc2985e04,0xbf350510,2
+np.float32,0x43185e04,0x3f800000,2
+np.float32,0xc3185e04,0xbf800000,2
+np.float32,0x43985e04,0xb722455d,2
+np.float32,0xc3985e04,0x3722455d,2
+np.float32,0x4299f024,0x3f800000,2
+np.float32,0xc299f024,0xbf800000,2
+np.float32,0x4319f024,0xb71bde6c,2
+np.float32,0xc319f024,0x371bde6c,2
+np.float32,0x4399f024,0x379bde6c,2
+np.float32,0xc399f024,0xb79bde6c,2
+np.float32,0x429b8243,0x3f3504fc,2
+np.float32,0xc29b8243,0xbf3504fc,2
+np.float32,0x431b8243,0xbf800000,2
+np.float32,0xc31b8243,0x3f800000,2
+np.float32,0x439b8243,0x364b2eb8,2
+np.float32,0xc39b8243,0xb64b2eb8,2
+np.float32,0x435b2047,0xbf350525,2
+np.float32,0x42a038a2,0xbf800000,2
+np.float32,0x432038a2,0x3664ca7e,2
+np.float32,0x4345eb9b,0x365e638c,2
+np.float32,0x42c5eb9b,0xbf800000,2
+np.float32,0x42eb9e94,0xbf800000,2
+np.float32,0x4350ea79,0x3f800000,2
+np.float32,0x42dbe957,0x3585522a,2
+np.float32,0x425be957,0xbf800000,2
+np.float32,0x435be957,0xb605522a,2
+np.float32,0x487fe5ab,0xbf7ffffd,2
+np.float32,0x497fe5ab,0xbb14017d,2
+np.float32,0x49ffe5ab,0xbb940164,2
+np.float32,0x49ffeb37,0x3f7fff56,2
+np.float32,0x497ff0c3,0x3f7fffd6,2
+np.float32,0x49fff0c3,0x3b930487,2
+np.float32,0x49fff64f,0xbf7fff58,2
+np.float32,0x497ffbdb,0x3b1207c0,2
+np.float32,0x49fffbdb,0xbb9207a9,2
+np.float32,0x48fffbdb,0xbf7ffff6,2
+np.float32,0x4e736e56,0x397fa7f2,2
+np.float32,0x4d4da377,0xb57c64bc,2
+np.float32,0x4ece58c3,0xb80846c8,2
+np.float32,0x4ee0db9c,0x394c4786,2
+np.float32,0x4dee7002,0x381bce96,2
+np.float32,0x4ee86afc,0x3f800000,2
+np.float32,0x4dca4f3f,0xb8e25111,2
+np.float32,0x4ecb48af,0xbf800000,2
+np.float32,0x4e51e33f,0xb8a4fa6f,2
+np.float32,0x4ef5f421,0x387ca7df,2
+np.float32,0x476362a2,0xbd7ff911,2
+np.float32,0x464c99a4,0x3e7f4d41,2
+np.float32,0x4471f73d,0x3e7fe1b0,2
+np.float32,0x445a6752,0x3e7ef367,2
+np.float32,0x474fa400,0x3e7f9fcd,2
+np.float32,0x47c9e70e,0xbb4bba09,2
+np.float32,0x45c1e72f,0xbe7fc7af,2
+np.float32,0x4558c91d,0x3e7e9f31,2
+np.float32,0x43784f94,0xbdff6654,2
+np.float32,0x466e8500,0xbe7ea0a3,2
+np.float32,0x468e1c25,0x3e7e22fb,2
+np.float32,0x47d28adc,0xbe7d5e6b,2
+np.float32,0x44ea6cfc,0x3dff70c3,2
+np.float32,0x4605126c,0x3e7f89ef,2
+np.float32,0x4788b3c6,0xbb87d853,2
+np.float32,0x4531b042,0x3dffd163,2
+np.float32,0x47e46c29,0xbe7def2b,2
+np.float32,0x47c10e07,0xbdff63d4,2
+np.float32,0x43f1f71d,0x3dfff387,2
+np.float32,0x47c3e38c,0x3e7f0b2f,2
+np.float32,0x462c3fa5,0xbd7fe13d,2
+np.float32,0x441c5354,0xbdff76b4,2
+np.float32,0x44908b69,0x3e7dcf0d,2
+np.float32,0x478813ad,0xbe7e9d80,2
+np.float32,0x441c4351,0x3dff937b,2
diff --git a/numpy/core/tests/test__exceptions.py b/numpy/core/tests/test__exceptions.py
new file mode 100644
index 000000000..494b51f34
--- /dev/null
+++ b/numpy/core/tests/test__exceptions.py
@@ -0,0 +1,42 @@
+"""
+Tests of the ._exceptions module. Primarily for exercising the __str__ methods.
+"""
+import numpy as np
+
+_ArrayMemoryError = np.core._exceptions._ArrayMemoryError
+
+class TestArrayMemoryError:
+ def test_str(self):
+ e = _ArrayMemoryError((1023,), np.dtype(np.uint8))
+ str(e) # not crashing is enough
+
+ # testing these properties is easier than testing the full string repr
+ def test__size_to_string(self):
+ """ Test e._size_to_string """
+ f = _ArrayMemoryError._size_to_string
+ Ki = 1024
+ assert f(0) == '0 bytes'
+ assert f(1) == '1 bytes'
+ assert f(1023) == '1023 bytes'
+ assert f(Ki) == '1.00 KiB'
+ assert f(Ki+1) == '1.00 KiB'
+ assert f(10*Ki) == '10.0 KiB'
+ assert f(int(999.4*Ki)) == '999. KiB'
+ assert f(int(1023.4*Ki)) == '1023. KiB'
+ assert f(int(1023.5*Ki)) == '1.00 MiB'
+ assert f(Ki*Ki) == '1.00 MiB'
+
+ # 1023.9999 Mib should round to 1 GiB
+ assert f(int(Ki*Ki*Ki*0.9999)) == '1.00 GiB'
+ assert f(Ki*Ki*Ki*Ki*Ki*Ki) == '1.00 EiB'
+ # larger than sys.maxsize, adding larger prefices isn't going to help
+ # anyway.
+ assert f(Ki*Ki*Ki*Ki*Ki*Ki*123456) == '123456. EiB'
+
+ def test__total_size(self):
+ """ Test e._total_size """
+ e = _ArrayMemoryError((1,), np.dtype(np.uint8))
+ assert e._total_size == 1
+
+ e = _ArrayMemoryError((2, 4), np.dtype((np.uint64, 16)))
+ assert e._total_size == 1024
diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py
index 9755e7b36..32e2ea537 100644
--- a/numpy/core/tests/test_api.py
+++ b/numpy/core/tests/test_api.py
@@ -3,8 +3,10 @@ from __future__ import division, absolute_import, print_function
import sys
import numpy as np
+import pytest
from numpy.testing import (
- assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT
+ assert_, assert_equal, assert_array_equal, assert_raises, assert_warns,
+ HAS_REFCOUNT
)
# Switch between new behaviour when NPY_RELAXED_STRIDES_CHECKING is set.
@@ -289,6 +291,14 @@ def test_array_astype():
a = np.array(1000, dtype='i4')
assert_raises(TypeError, a.astype, 'U1', casting='safe')
+@pytest.mark.parametrize("t",
+ np.sctypes['uint'] + np.sctypes['int'] + np.sctypes['float']
+)
+def test_array_astype_warning(t):
+ # test ComplexWarning when casting from complex to float or int
+ a = np.array(10, dtype=np.complex)
+ assert_warns(np.ComplexWarning, a.astype, t)
+
def test_copyto_fromscalar():
a = np.arange(6, dtype='f4').reshape(2, 3)
diff --git a/numpy/core/tests/test_arrayprint.py b/numpy/core/tests/test_arrayprint.py
index 6522c6e8a..702e68e76 100644
--- a/numpy/core/tests/test_arrayprint.py
+++ b/numpy/core/tests/test_arrayprint.py
@@ -90,6 +90,7 @@ class TestArrayRepr(object):
assert_equal(repr(x),
'sub(sub(sub(..., dtype=object), dtype=object), dtype=object)')
assert_equal(str(x), '...')
+ x[()] = 0 # resolve circular references for garbage collector
# nested 0d-subclass-object
x = sub(None)
@@ -124,11 +125,13 @@ class TestArrayRepr(object):
arr0d[()] = arr0d
assert_equal(repr(arr0d),
'array(array(..., dtype=object), dtype=object)')
+ arr0d[()] = 0 # resolve recursion for garbage collector
arr1d = np.array([None, None])
arr1d[1] = arr1d
assert_equal(repr(arr1d),
'array([None, array(..., dtype=object)], dtype=object)')
+ arr1d[1] = 0 # resolve recursion for garbage collector
first = np.array(None)
second = np.array(None)
@@ -136,6 +139,7 @@ class TestArrayRepr(object):
second[()] = first
assert_equal(repr(first),
'array(array(array(..., dtype=object), dtype=object), dtype=object)')
+ first[()] = 0 # resolve circular references for garbage collector
def test_containing_list(self):
# printing square brackets directly would be ambiguuous
@@ -258,11 +262,6 @@ class TestArray2String(object):
assert_(np.array2string(s, formatter={'numpystr':lambda s: s*2}) ==
'[abcabc defdef]')
- # check for backcompat that using FloatFormat works and emits warning
- with assert_warns(DeprecationWarning):
- fmt = np.core.arrayprint.FloatFormat(x, 9, 'maxprec', False)
- assert_equal(np.array2string(x, formatter={'float_kind': fmt}),
- '[0. 1. 2.]')
def test_structure_format(self):
dt = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))])
@@ -842,6 +841,10 @@ class TestPrintOptions(object):
[[ 0.]]]])""")
)
+ def test_bad_args(self):
+ assert_raises(ValueError, np.set_printoptions, threshold=float('nan'))
+ assert_raises(TypeError, np.set_printoptions, threshold='1')
+ assert_raises(TypeError, np.set_printoptions, threshold=b'1')
def test_unicode_object_array():
import sys
diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py
index 6303c043a..f99c0f72b 100644
--- a/numpy/core/tests/test_datetime.py
+++ b/numpy/core/tests/test_datetime.py
@@ -1,6 +1,5 @@
from __future__ import division, absolute_import, print_function
-import pickle
import numpy
import numpy as np
@@ -8,7 +7,9 @@ import datetime
import pytest
from numpy.testing import (
assert_, assert_equal, assert_raises, assert_warns, suppress_warnings,
+ assert_raises_regex,
)
+from numpy.compat import pickle
# Use pytz to test out various time zones if available
try:
@@ -257,6 +258,21 @@ class TestDateTime(object):
arr = np.array([dt, dt]).astype('datetime64')
assert_equal(arr.dtype, np.dtype('M8[us]'))
+ @pytest.mark.parametrize("unit", [
+ # test all date / time units and use
+ # "generic" to select generic unit
+ ("Y"), ("M"), ("W"), ("D"), ("h"), ("m"),
+ ("s"), ("ms"), ("us"), ("ns"), ("ps"),
+ ("fs"), ("as"), ("generic") ])
+ def test_timedelta_np_int_construction(self, unit):
+ # regression test for gh-7617
+ if unit != "generic":
+ assert_equal(np.timedelta64(np.int64(123), unit),
+ np.timedelta64(123, unit))
+ else:
+ assert_equal(np.timedelta64(np.int64(123)),
+ np.timedelta64(123))
+
def test_timedelta_scalar_construction(self):
# Construct with different units
assert_equal(np.timedelta64(7, 'D'),
@@ -1065,6 +1081,133 @@ 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
+
+ @pytest.mark.parametrize("op1, op2", [
+ # reuse the test cases from floordiv
+ (np.timedelta64(7, 's'),
+ np.timedelta64(4, 's')),
+ # m8 same units round down with negative
+ (np.timedelta64(7, 's'),
+ np.timedelta64(-4, 's')),
+ # m8 same units negative no round down
+ (np.timedelta64(8, 's'),
+ np.timedelta64(-4, 's')),
+ # m8 different units
+ (np.timedelta64(1, 'm'),
+ np.timedelta64(31, 's')),
+ # m8 generic units
+ (np.timedelta64(1890),
+ np.timedelta64(31)),
+ # Y // M works
+ (np.timedelta64(2, 'Y'),
+ np.timedelta64('13', 'M')),
+ # handle 1D arrays
+ (np.array([1, 2, 3], dtype='m8'),
+ np.array([2], dtype='m8')),
+ ])
+ def test_timedelta_divmod(self, op1, op2):
+ expected = (op1 // op2, op1 % op2)
+ assert_equal(divmod(op1, op2), expected)
+
+ @pytest.mark.parametrize("op1, op2", [
+ # reuse cases from floordiv
+ # 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_divmod_warnings(self, op1, op2):
+ with assert_warns(RuntimeWarning):
+ expected = (op1 // op2, op1 % op2)
+ with assert_warns(RuntimeWarning):
+ actual = divmod(op1, op2)
+ assert_equal(actual, expected)
+
def test_datetime_divide(self):
for dta, tda, tdb, tdc, tdd in \
[
@@ -1095,8 +1238,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
@@ -1402,6 +1543,12 @@ class TestDateTime(object):
assert_equal(x[0].astype(np.int64), 322689600000000000)
+ # gh-13062
+ with pytest.raises(OverflowError):
+ np.datetime64(2**64, 'D')
+ with pytest.raises(OverflowError):
+ np.timedelta64(2**64, 'D')
+
def test_datetime_as_string(self):
# Check all the units with default string conversion
date = '1959-10-13'
@@ -1611,6 +1758,76 @@ class TestDateTime(object):
assert_raises(TypeError, np.arange, np.timedelta64(0, 'Y'),
np.timedelta64(5, 'D'))
+ @pytest.mark.parametrize("val1, val2, expected", [
+ # case from gh-12092
+ (np.timedelta64(7, 's'),
+ np.timedelta64(3, 's'),
+ np.timedelta64(1, 's')),
+ # negative value cases
+ (np.timedelta64(3, 's'),
+ np.timedelta64(-2, 's'),
+ np.timedelta64(-1, 's')),
+ (np.timedelta64(-3, 's'),
+ np.timedelta64(2, 's'),
+ np.timedelta64(1, 's')),
+ # larger value cases
+ (np.timedelta64(17, 's'),
+ np.timedelta64(22, 's'),
+ np.timedelta64(17, 's')),
+ (np.timedelta64(22, 's'),
+ np.timedelta64(17, 's'),
+ np.timedelta64(5, 's')),
+ # different units
+ (np.timedelta64(1, 'm'),
+ np.timedelta64(57, 's'),
+ np.timedelta64(3, 's')),
+ (np.timedelta64(1, 'us'),
+ np.timedelta64(727, 'ns'),
+ np.timedelta64(273, 'ns')),
+ # NaT is propagated
+ (np.timedelta64('NaT'),
+ np.timedelta64(50, 'ns'),
+ np.timedelta64('NaT')),
+ # Y % M works
+ (np.timedelta64(2, 'Y'),
+ np.timedelta64(22, 'M'),
+ np.timedelta64(2, 'M')),
+ ])
+ def test_timedelta_modulus(self, val1, val2, expected):
+ assert_equal(val1 % val2, expected)
+
+ @pytest.mark.parametrize("val1, val2", [
+ # years and months sometimes can't be unambiguously
+ # divided for modulus operation
+ (np.timedelta64(7, 'Y'),
+ np.timedelta64(3, 's')),
+ (np.timedelta64(7, 'M'),
+ np.timedelta64(1, 'D')),
+ ])
+ def test_timedelta_modulus_error(self, val1, val2):
+ with assert_raises_regex(TypeError, "common metadata divisor"):
+ val1 % val2
+
+ def test_timedelta_modulus_div_by_zero(self):
+ with assert_warns(RuntimeWarning):
+ actual = np.timedelta64(10, 's') % np.timedelta64(0, 's')
+ assert_equal(actual, np.timedelta64('NaT'))
+
+ @pytest.mark.parametrize("val1, val2", [
+ # cases where one operand is not
+ # timedelta64
+ (np.timedelta64(7, 'Y'),
+ 15,),
+ (7.5,
+ np.timedelta64(1, 'D')),
+ ])
+ def test_timedelta_modulus_type_resolution(self, val1, val2):
+ # NOTE: some of the operations may be supported
+ # in the future
+ with assert_raises_regex(TypeError,
+ "'remainder' cannot use operands with types"):
+ val1 % val2
+
def test_timedelta_arange_no_dtype(self):
d = np.array(5, dtype="m8[D]")
assert_equal(np.arange(d, d + 1), d)
@@ -1991,6 +2208,27 @@ class TestDateTime(object):
continue
assert_raises(TypeError, np.isnat, np.zeros(10, t))
+ def test_isfinite(self):
+ assert_(not np.isfinite(np.datetime64('NaT', 'ms')))
+ assert_(not np.isfinite(np.datetime64('NaT', 'ns')))
+ assert_(np.isfinite(np.datetime64('2038-01-19T03:14:07')))
+
+ assert_(not np.isfinite(np.timedelta64('NaT', "ms")))
+ assert_(np.isfinite(np.timedelta64(34, "ms")))
+
+ res = np.array([True, True, False])
+ for unit in ['Y', 'M', 'W', 'D',
+ 'h', 'm', 's', 'ms', 'us',
+ 'ns', 'ps', 'fs', 'as']:
+ arr = np.array([123, -321, "NaT"], dtype='<datetime64[%s]' % unit)
+ assert_equal(np.isfinite(arr), res)
+ arr = np.array([123, -321, "NaT"], dtype='>datetime64[%s]' % unit)
+ assert_equal(np.isfinite(arr), res)
+ arr = np.array([123, -321, "NaT"], dtype='<timedelta64[%s]' % unit)
+ assert_equal(np.isfinite(arr), res)
+ arr = np.array([123, -321, "NaT"], dtype='>timedelta64[%s]' % unit)
+ assert_equal(np.isfinite(arr), res)
+
def test_corecursive_input(self):
# construct a co-recursive list
a, b = [], []
@@ -2003,6 +2241,44 @@ class TestDateTime(object):
assert_raises(RecursionError, obj_arr.astype, 'M8')
assert_raises(RecursionError, obj_arr.astype, 'm8')
+ @pytest.mark.parametrize("time_unit", [
+ "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps", "fs", "as",
+ # compound units
+ "10D", "2M",
+ ])
+ def test_limit_symmetry(self, time_unit):
+ """
+ Dates should have symmetric limits around the unix epoch at +/-np.int64
+ """
+ epoch = np.datetime64(0, time_unit)
+ latest = np.datetime64(np.iinfo(np.int64).max, time_unit)
+ earliest = np.datetime64(-np.iinfo(np.int64).max, time_unit)
+
+ # above should not have overflowed
+ assert earliest < epoch < latest
+
+ @pytest.mark.parametrize("time_unit", [
+ "Y", "M",
+ pytest.param("W", marks=pytest.mark.xfail(reason="gh-13197")),
+ "D", "h", "m",
+ "s", "ms", "us", "ns", "ps", "fs", "as",
+ pytest.param("10D", marks=pytest.mark.xfail(reason="similar to gh-13197")),
+ ])
+ @pytest.mark.parametrize("sign", [-1, 1])
+ def test_limit_str_roundtrip(self, time_unit, sign):
+ """
+ Limits should roundtrip when converted to strings.
+
+ This tests the conversion to and from npy_datetimestruct.
+ """
+ # TODO: add absolute (gold standard) time span limit strings
+ limit = np.datetime64(np.iinfo(np.int64).max * sign, time_unit)
+
+ # Convert to string and back. Explicit unit needed since the day and
+ # week reprs are not distinguishable.
+ limit_via_str = np.datetime64(str(limit), time_unit)
+ assert limit_via_str == limit
+
class TestDateTimeData(object):
diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py
index 10ef16800..8bffaa9af 100644
--- a/numpy/core/tests/test_deprecations.py
+++ b/numpy/core/tests/test_deprecations.py
@@ -10,13 +10,16 @@ import sys
import operator
import warnings
import pytest
+import shutil
+import tempfile
import numpy as np
from numpy.testing import (
- assert_raises, assert_warns, assert_no_warnings, assert_array_equal,
- assert_
+ assert_raises, assert_warns, assert_, assert_array_equal
)
+from numpy.core._multiarray_tests import fromstring_null_term_c_api
+
try:
import pytz
_has_pytz = True
@@ -102,7 +105,7 @@ class _DeprecationTestCase(object):
(self.warning_cls.__name__, warning.category))
if num is not None and num_found != num:
msg = "%i warnings found but %i expected." % (len(self.log), num)
- lst = [str(w.category) for w in self.log]
+ lst = [str(w) for w in self.log]
raise AssertionError("\n".join([msg] + lst))
with warnings.catch_warnings():
@@ -150,16 +153,6 @@ class TestNonTupleNDIndexDeprecation(object):
a[[0, 1]]
-class TestRankDeprecation(_DeprecationTestCase):
- """Test that np.rank is deprecated. The function should simply be
- removed. The VisibleDeprecationWarning may become unnecessary.
- """
-
- def test(self):
- a = np.arange(10)
- assert_warns(np.VisibleDeprecationWarning, np.rank, a)
-
-
class TestComparisonDeprecations(_DeprecationTestCase):
"""This tests the deprecation, for non-element-wise comparison logic.
This used to mean that when an error occurred during element-wise comparison
@@ -282,36 +275,6 @@ class TestNonCContiguousViewDeprecation(_DeprecationTestCase):
self.assert_deprecated(np.ones((2,2)).T.view, args=(np.int8,))
-class TestInvalidOrderParameterInputForFlattenArrayDeprecation(_DeprecationTestCase):
- """Invalid arguments to the ORDER parameter in array.flatten() should not be
- allowed and should raise an error. However, in the interests of not breaking
- code that may inadvertently pass invalid arguments to this parameter, a
- DeprecationWarning will be issued instead for the time being to give developers
- time to refactor relevant code.
- """
-
- def test_flatten_array_non_string_arg(self):
- x = np.zeros((3, 5))
- self.message = ("Non-string object detected for "
- "the array ordering. Please pass "
- "in 'C', 'F', 'A', or 'K' instead")
- self.assert_deprecated(x.flatten, args=(np.pi,))
-
- def test_flatten_array_invalid_string_arg(self):
- # Tests that a DeprecationWarning is raised
- # when a string of length greater than one
- # starting with "C", "F", "A", or "K" (case-
- # and unicode-insensitive) is passed in for
- # the ORDER parameter. Otherwise, a TypeError
- # will be raised!
-
- x = np.zeros((3, 5))
- self.message = ("Non length-one string passed "
- "in for the array ordering. Please "
- "pass in 'C', 'F', 'A', or 'K' instead")
- self.assert_deprecated(x.flatten, args=("FACK",))
-
-
class TestArrayDataAttributeAssignmentDeprecation(_DeprecationTestCase):
"""Assigning the 'data' attribute of an ndarray is unsafe as pointed
out in gh-7093. Eventually, such assignment should NOT be allowed, but
@@ -330,22 +293,6 @@ class TestArrayDataAttributeAssignmentDeprecation(_DeprecationTestCase):
self.assert_deprecated(a.__setattr__, args=('data', b.data))
-class TestLinspaceInvalidNumParameter(_DeprecationTestCase):
- """Argument to the num parameter in linspace that cannot be
- safely interpreted as an integer is deprecated in 1.12.0.
-
- Argument to the num parameter in linspace that cannot be
- safely interpreted as an integer should not be allowed.
- In the interest of not breaking code that passes
- an argument that could still be interpreted as an integer, a
- DeprecationWarning will be issued for the time being to give
- developers time to refactor relevant code.
- """
- def test_float_arg(self):
- # 2016-02-25, PR#7328
- self.assert_deprecated(np.linspace, args=(0, 10, 2.5))
-
-
class TestBinaryReprInsufficientWidthParameterForRepresentation(_DeprecationTestCase):
"""
If a 'width' parameter is passed into ``binary_repr`` that is insufficient to
@@ -453,6 +400,18 @@ class TestNPY_CHAR(_DeprecationTestCase):
assert_(npy_char_deprecation() == 'S1')
+class TestPyArray_AS1D(_DeprecationTestCase):
+ def test_npy_pyarrayas1d_deprecation(self):
+ from numpy.core._multiarray_tests import npy_pyarrayas1d_deprecation
+ assert_raises(NotImplementedError, npy_pyarrayas1d_deprecation)
+
+
+class TestPyArray_AS2D(_DeprecationTestCase):
+ def test_npy_pyarrayas2d_deprecation(self):
+ from numpy.core._multiarray_tests import npy_pyarrayas2d_deprecation
+ assert_raises(NotImplementedError, npy_pyarrayas2d_deprecation)
+
+
class Test_UPDATEIFCOPY(_DeprecationTestCase):
"""
v1.14 deprecates creating an array with the UPDATEIFCOPY flag, use
@@ -500,6 +459,12 @@ class TestBincount(_DeprecationTestCase):
self.assert_deprecated(lambda: np.bincount([1, 2, 3], minlength=None))
+class TestAlen(_DeprecationTestCase):
+ # 2019-08-02, 1.18.0
+ def test_alen(self):
+ self.assert_deprecated(lambda: np.alen(np.array([1, 2, 3])))
+
+
class TestGeneratorSum(_DeprecationTestCase):
# 2018-02-25, 1.15.0
def test_generator_sum(self):
@@ -519,7 +484,87 @@ class TestPositiveOnNonNumerical(_DeprecationTestCase):
def test_positive_on_non_number(self):
self.assert_deprecated(operator.pos, args=(np.array('foo'),))
+
class TestFromstring(_DeprecationTestCase):
# 2017-10-19, 1.14
def test_fromstring(self):
self.assert_deprecated(np.fromstring, args=('\x00'*80,))
+
+
+class TestFromStringAndFileInvalidData(_DeprecationTestCase):
+ # 2019-06-08, 1.17.0
+ # Tests should be moved to real tests when deprecation is done.
+ message = "string or file could not be read to its end"
+
+ @pytest.mark.parametrize("invalid_str", [",invalid_data", "invalid_sep"])
+ def test_deprecate_unparsable_data_file(self, invalid_str):
+ x = np.array([1.51, 2, 3.51, 4], dtype=float)
+
+ with tempfile.TemporaryFile(mode="w") as f:
+ x.tofile(f, sep=',', format='%.2f')
+ f.write(invalid_str)
+
+ f.seek(0)
+ self.assert_deprecated(lambda: np.fromfile(f, sep=","))
+ f.seek(0)
+ self.assert_deprecated(lambda: np.fromfile(f, sep=",", count=5))
+ # Should not raise:
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", DeprecationWarning)
+ f.seek(0)
+ res = np.fromfile(f, sep=",", count=4)
+ assert_array_equal(res, x)
+
+ @pytest.mark.parametrize("invalid_str", [",invalid_data", "invalid_sep"])
+ def test_deprecate_unparsable_string(self, invalid_str):
+ x = np.array([1.51, 2, 3.51, 4], dtype=float)
+ x_str = "1.51,2,3.51,4{}".format(invalid_str)
+
+ self.assert_deprecated(lambda: np.fromstring(x_str, sep=","))
+ self.assert_deprecated(lambda: np.fromstring(x_str, sep=",", count=5))
+
+ # The C-level API can use not fixed size, but 0 terminated strings,
+ # so test that as well:
+ bytestr = x_str.encode("ascii")
+ self.assert_deprecated(lambda: fromstring_null_term_c_api(bytestr))
+
+ with assert_warns(DeprecationWarning):
+ # this is slightly strange, in that fromstring leaves data
+ # potentially uninitialized (would be good to error when all is
+ # read, but count is larger then actual data maybe).
+ res = np.fromstring(x_str, sep=",", count=5)
+ assert_array_equal(res[:-1], x)
+
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", DeprecationWarning)
+
+ # Should not raise:
+ res = np.fromstring(x_str, sep=",", count=4)
+ assert_array_equal(res, x)
+
+
+class Test_GetSet_NumericOps(_DeprecationTestCase):
+ # 2018-09-20, 1.16.0
+ def test_get_numeric_ops(self):
+ from numpy.core._multiarray_tests import getset_numericops
+ self.assert_deprecated(getset_numericops, num=2)
+
+ # empty kwargs prevents any state actually changing which would break
+ # other tests.
+ self.assert_deprecated(np.set_numeric_ops, kwargs={})
+ assert_raises(ValueError, np.set_numeric_ops, add='abc')
+
+
+class TestShape1Fields(_DeprecationTestCase):
+ warning_cls = FutureWarning
+
+ # 2019-05-20, 1.17.0
+ def test_shape_1_fields(self):
+ self.assert_deprecated(np.dtype, args=([('a', int, 1)],))
+
+
+class TestNonZero(_DeprecationTestCase):
+ # 2019-05-26, 1.17.0
+ def test_zerod(self):
+ self.assert_deprecated(lambda: np.nonzero(np.array(0)))
+ self.assert_deprecated(lambda: np.nonzero(np.array(1)))
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index fc4dc952a..d2fbbae5b 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -1,14 +1,17 @@
from __future__ import division, absolute_import, print_function
-import pickle
import sys
import operator
import pytest
import ctypes
+import gc
import numpy as np
from numpy.core._rational_tests import rational
-from numpy.testing import assert_, assert_equal, assert_raises
+from numpy.testing import (
+ assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT)
+from numpy.compat import pickle
+from itertools import permutations
def assert_dtype_equal(a, b):
assert_equal(a, b)
@@ -85,6 +88,36 @@ class TestBuiltin(object):
assert_raises(TypeError, np.dtype, 'q8')
assert_raises(TypeError, np.dtype, 'Q8')
+ @pytest.mark.parametrize(
+ 'value',
+ ['m8', 'M8', 'datetime64', 'timedelta64',
+ 'i4, (2,3)f8, f4', 'a3, 3u8, (3,4)a10',
+ '>f', '<f', '=f', '|f',
+ ])
+ def test_dtype_bytes_str_equivalence(self, value):
+ bytes_value = value.encode('ascii')
+ from_bytes = np.dtype(bytes_value)
+ from_str = np.dtype(value)
+ assert_dtype_equal(from_bytes, from_str)
+
+ def test_dtype_from_bytes(self):
+ # Empty bytes object
+ assert_raises(TypeError, np.dtype, b'')
+ # Byte order indicator, but no type
+ assert_raises(TypeError, np.dtype, b'|')
+
+ # Single character with ordinal < NPY_NTYPES returns
+ # type by index into _builtin_descrs
+ assert_dtype_equal(np.dtype(bytes([0])), np.dtype('bool'))
+ assert_dtype_equal(np.dtype(bytes([17])), np.dtype(object))
+
+ # Single character where value is a valid type code
+ assert_dtype_equal(np.dtype(b'f'), np.dtype('float32'))
+
+ # Bytes with non-ascii values raise errors
+ assert_raises(TypeError, np.dtype, b'\xff')
+ assert_raises(TypeError, np.dtype, b's\xff')
+
def test_bad_param(self):
# Can't give a size that's too small
assert_raises(ValueError, np.dtype,
@@ -136,6 +169,18 @@ class TestRecord(object):
'titles': ['RRed pixel', 'Blue pixel']})
assert_dtype_not_equal(a, b)
+ @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts")
+ def test_refcount_dictionary_setting(self):
+ names = ["name1"]
+ formats = ["f8"]
+ titles = ["t1"]
+ offsets = [0]
+ d = dict(names=names, formats=formats, titles=titles, offsets=offsets)
+ refcounts = {k: sys.getrefcount(i) for k, i in d.items()}
+ np.dtype(d)
+ refcounts_new = {k: sys.getrefcount(i) for k, i in d.items()}
+ assert refcounts == refcounts_new
+
def test_mutate(self):
# Mutating a dtype should reset the cached hash value
a = np.dtype([('yo', int)])
@@ -156,9 +201,9 @@ class TestRecord(object):
the dtype constructor.
"""
assert_raises(TypeError, np.dtype,
- dict(names=set(['A', 'B']), formats=['f8', 'i4']))
+ dict(names={'A', 'B'}, formats=['f8', 'i4']))
assert_raises(TypeError, np.dtype,
- dict(names=['A', 'B'], formats=set(['f8', 'i4'])))
+ dict(names=['A', 'B'], formats={'f8', 'i4'}))
def test_aligned_size(self):
# Check that structured dtypes get padded to an aligned size
@@ -213,7 +258,6 @@ class TestRecord(object):
assert_equal(dt1.descr, [('a', '|i1'), ('', '|V3'),
('b', [('f0', '<i2'), ('', '|V2'),
('f1', '<f4')], (2,))])
-
def test_union_struct(self):
# Should be able to create union dtypes
@@ -315,10 +359,91 @@ class TestRecord(object):
assert_raises(IndexError, lambda: dt[-3])
assert_raises(TypeError, operator.getitem, dt, 3.0)
- assert_raises(TypeError, operator.getitem, dt, [])
assert_equal(dt[1], dt[np.int8(1)])
+ @pytest.mark.parametrize('align_flag',[False, True])
+ def test_multifield_index(self, align_flag):
+ # indexing with a list produces subfields
+ # the align flag should be preserved
+ dt = np.dtype([
+ (('title', 'col1'), '<U20'), ('A', '<f8'), ('B', '<f8')
+ ], align=align_flag)
+
+ dt_sub = dt[['B', 'col1']]
+ assert_equal(
+ dt_sub,
+ np.dtype({
+ 'names': ['B', 'col1'],
+ 'formats': ['<f8', '<U20'],
+ 'offsets': [88, 0],
+ 'titles': [None, 'title'],
+ 'itemsize': 96
+ })
+ )
+ assert_equal(dt_sub.isalignedstruct, align_flag)
+
+ dt_sub = dt[['B']]
+ assert_equal(
+ dt_sub,
+ np.dtype({
+ 'names': ['B'],
+ 'formats': ['<f8'],
+ 'offsets': [88],
+ 'itemsize': 96
+ })
+ )
+ assert_equal(dt_sub.isalignedstruct, align_flag)
+
+ dt_sub = dt[[]]
+ assert_equal(
+ dt_sub,
+ np.dtype({
+ 'names': [],
+ 'formats': [],
+ 'offsets': [],
+ 'itemsize': 96
+ })
+ )
+ assert_equal(dt_sub.isalignedstruct, align_flag)
+
+ assert_raises(TypeError, operator.getitem, dt, ())
+ assert_raises(TypeError, operator.getitem, dt, [1, 2, 3])
+ assert_raises(TypeError, operator.getitem, dt, ['col1', 2])
+ assert_raises(KeyError, operator.getitem, dt, ['fake'])
+ assert_raises(KeyError, operator.getitem, dt, ['title'])
+ assert_raises(ValueError, operator.getitem, dt, ['col1', 'col1'])
+
+ def test_partial_dict(self):
+ # 'names' is missing
+ assert_raises(ValueError, np.dtype,
+ {'formats': ['i4', 'i4'], 'f0': ('i4', 0), 'f1':('i4', 4)})
+
+ def test_fieldless_views(self):
+ a = np.zeros(2, dtype={'names':[], 'formats':[], 'offsets':[],
+ 'itemsize':8})
+ assert_raises(ValueError, a.view, np.dtype([]))
+
+ d = np.dtype((np.dtype([]), 10))
+ assert_equal(d.shape, (10,))
+ assert_equal(d.itemsize, 0)
+ assert_equal(d.base, np.dtype([]))
+
+ arr = np.fromiter((() for i in range(10)), [])
+ assert_equal(arr.dtype, np.dtype([]))
+ assert_raises(ValueError, np.frombuffer, b'', dtype=[])
+ assert_equal(np.frombuffer(b'', dtype=[], count=2),
+ np.empty(2, dtype=[]))
+
+ assert_raises(ValueError, np.dtype, ([], 'f8'))
+ assert_raises(ValueError, np.zeros(1, dtype='i4').view, [])
+
+ assert_equal(np.zeros(2, dtype=[]) == np.zeros(2, dtype=[]),
+ np.ones(2, dtype=bool))
+
+ assert_equal(np.zeros((1, 2), dtype=[]) == a,
+ np.ones((1, 2), dtype=bool))
+
class TestSubarray(object):
def test_single_subarray(self):
@@ -352,7 +477,10 @@ class TestSubarray(object):
def test_shape_equal(self):
"""Test some data types that are equal"""
assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', tuple())))
- assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', 1)))
+ # FutureWarning during deprecation period; after it is passed this
+ # should instead check that "(1)f8" == "1f8" == ("f8", 1).
+ with pytest.warns(FutureWarning):
+ assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', 1)))
assert_dtype_equal(np.dtype((int, 2)), np.dtype((int, (2,))))
assert_dtype_equal(np.dtype(('<f4', (3, 2))), np.dtype(('<f4', (3, 2))))
d = ([('a', 'f4', (1, 2)), ('b', 'f8', (3, 1))], (3, 2))
@@ -441,11 +569,178 @@ class TestSubarray(object):
def test_alignment(self):
#Check that subarrays are aligned
- t1 = np.dtype('1i4', align=True)
+ t1 = np.dtype('(1,)i4', align=True)
t2 = np.dtype('2i4', align=True)
assert_equal(t1.alignment, t2.alignment)
+def iter_struct_object_dtypes():
+ """
+ Iterates over a few complex dtypes and object pattern which
+ fill the array with a given object (defaults to a singleton).
+
+ Yields
+ ------
+ dtype : dtype
+ pattern : tuple
+ Structured tuple for use with `np.array`.
+ count : int
+ Number of objects stored in the dtype.
+ singleton : object
+ A singleton object. The returned pattern is constructed so that
+ all objects inside the datatype are set to the singleton.
+ """
+ obj = object()
+
+ dt = np.dtype([('b', 'O', (2, 3))])
+ p = ([[obj] * 3] * 2,)
+ yield pytest.param(dt, p, 6, obj, id="<subarray>")
+
+ dt = np.dtype([('a', 'i4'), ('b', 'O', (2, 3))])
+ p = (0, [[obj] * 3] * 2)
+ yield pytest.param(dt, p, 6, obj, id="<subarray in field>")
+
+ dt = np.dtype([('a', 'i4'),
+ ('b', [('ba', 'O'), ('bb', 'i1')], (2, 3))])
+ p = (0, [[(obj, 0)] * 3] * 2)
+ yield pytest.param(dt, p, 6, obj, id="<structured subarray 1>")
+
+ dt = np.dtype([('a', 'i4'),
+ ('b', [('ba', 'O'), ('bb', 'O')], (2, 3))])
+ p = (0, [[(obj, obj)] * 3] * 2)
+ yield pytest.param(dt, p, 12, obj, id="<structured subarray 2>")
+
+
+@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts")
+class TestStructuredObjectRefcounting:
+ """These tests cover various uses of complicated structured types which
+ include objects and thus require reference counting.
+ """
+ @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'],
+ iter_struct_object_dtypes())
+ @pytest.mark.parametrize(["creation_func", "creation_obj"], [
+ pytest.param(np.empty, None,
+ # None is probably used for too many things
+ marks=pytest.mark.skip("unreliable due to python's behaviour")),
+ (np.ones, 1),
+ (np.zeros, 0)])
+ def test_structured_object_create_delete(self, dt, pat, count, singleton,
+ creation_func, creation_obj):
+ """Structured object reference counting in creation and deletion"""
+ # The test assumes that 0, 1, and None are singletons.
+ gc.collect()
+ before = sys.getrefcount(creation_obj)
+ arr = creation_func(3, dt)
+
+ now = sys.getrefcount(creation_obj)
+ assert now - before == count * 3
+ del arr
+ now = sys.getrefcount(creation_obj)
+ assert now == before
+
+ @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'],
+ iter_struct_object_dtypes())
+ def test_structured_object_item_setting(self, dt, pat, count, singleton):
+ """Structured object reference counting for simple item setting"""
+ one = 1
+
+ gc.collect()
+ before = sys.getrefcount(singleton)
+ arr = np.array([pat] * 3, dt)
+ assert sys.getrefcount(singleton) - before == count * 3
+ # Fill with `1` and check that it was replaced correctly:
+ before2 = sys.getrefcount(one)
+ arr[...] = one
+ after2 = sys.getrefcount(one)
+ assert after2 - before2 == count * 3
+ del arr
+ gc.collect()
+ assert sys.getrefcount(one) == before2
+ assert sys.getrefcount(singleton) == before
+
+ @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'],
+ iter_struct_object_dtypes())
+ @pytest.mark.parametrize(
+ ['shape', 'index', 'items_changed'],
+ [((3,), ([0, 2],), 2),
+ ((3, 2), ([0, 2], slice(None)), 4),
+ ((3, 2), ([0, 2], [1]), 2),
+ ((3,), ([True, False, True]), 2)])
+ def test_structured_object_indexing(self, shape, index, items_changed,
+ dt, pat, count, singleton):
+ """Structured object reference counting for advanced indexing."""
+ zero = 0
+ one = 1
+
+ arr = np.zeros(shape, dt)
+
+ gc.collect()
+ before_zero = sys.getrefcount(zero)
+ before_one = sys.getrefcount(one)
+ # Test item getting:
+ part = arr[index]
+ after_zero = sys.getrefcount(zero)
+ assert after_zero - before_zero == count * items_changed
+ del part
+ # Test item setting:
+ arr[index] = one
+ gc.collect()
+ after_zero = sys.getrefcount(zero)
+ after_one = sys.getrefcount(one)
+ assert before_zero - after_zero == count * items_changed
+ assert after_one - before_one == count * items_changed
+
+ @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'],
+ iter_struct_object_dtypes())
+ def test_structured_object_take_and_repeat(self, dt, pat, count, singleton):
+ """Structured object reference counting for specialized functions.
+ The older functions such as take and repeat use different code paths
+ then item setting (when writing this).
+ """
+ indices = [0, 1]
+
+ arr = np.array([pat] * 3, dt)
+ gc.collect()
+ before = sys.getrefcount(singleton)
+ res = arr.take(indices)
+ after = sys.getrefcount(singleton)
+ assert after - before == count * 2
+ new = res.repeat(10)
+ gc.collect()
+ after_repeat = sys.getrefcount(singleton)
+ assert after_repeat - after == count * 2 * 10
+
+
+class TestStructuredDtypeSparseFields(object):
+ """Tests subarray fields which contain sparse dtypes so that
+ not all memory is used by the dtype work. Such dtype's should
+ leave the underlying memory unchanged.
+ """
+ dtype = np.dtype([('a', {'names':['aa', 'ab'], 'formats':['f', 'f'],
+ 'offsets':[0, 4]}, (2, 3))])
+ sparse_dtype = np.dtype([('a', {'names':['ab'], 'formats':['f'],
+ 'offsets':[4]}, (2, 3))])
+
+ @pytest.mark.xfail(reason="inaccessible data is changed see gh-12686.")
+ @pytest.mark.valgrind_error(reason="reads from uninitialized buffers.")
+ def test_sparse_field_assignment(self):
+ arr = np.zeros(3, self.dtype)
+ sparse_arr = arr.view(self.sparse_dtype)
+
+ sparse_arr[...] = np.finfo(np.float32).max
+ # dtype is reduced when accessing the field, so shape is (3, 2, 3):
+ assert_array_equal(arr["a"]["aa"], np.zeros((3, 2, 3)))
+
+ def test_sparse_field_assignment_fancy(self):
+ # Fancy assignment goes to the copyswap function for comlex types:
+ arr = np.zeros(3, self.dtype)
+ sparse_arr = arr.view(self.sparse_dtype)
+
+ sparse_arr[[0, 1, 2]] = np.finfo(np.float32).max
+ # dtype is reduced when accessing the field, so shape is (3, 2, 3):
+ assert_array_equal(arr["a"]["aa"], np.zeros((3, 2, 3)))
+
+
class TestMonsterType(object):
"""Test deeply nested subtypes."""
@@ -620,6 +915,25 @@ class TestString(object):
# Pull request #4722
np.array(["", ""]).astype(object)
+ def test_void_subclass_unsized(self):
+ dt = np.dtype(np.record)
+ assert_equal(repr(dt), "dtype('V')")
+ assert_equal(str(dt), '|V0')
+ assert_equal(dt.name, 'record')
+
+ def test_void_subclass_sized(self):
+ dt = np.dtype((np.record, 2))
+ assert_equal(repr(dt), "dtype('V2')")
+ assert_equal(str(dt), '|V2')
+ assert_equal(dt.name, 'record16')
+
+ def test_void_subclass_fields(self):
+ dt = np.dtype((np.record, [('a', '<u2')]))
+ assert_equal(repr(dt), "dtype((numpy.record, [('a', '<u2')]))")
+ assert_equal(str(dt), "(numpy.record, [('a', '<u2')])")
+ assert_equal(dt.name, 'record16')
+
+
class TestDtypeAttributeDeletion(object):
def test_dtype_non_writable_attributes_deletion(self):
@@ -649,13 +963,6 @@ class TestDtypeAttributes(object):
new_dtype = np.dtype(dtype.descr)
assert_equal(new_dtype.itemsize, 16)
- @pytest.mark.parametrize('t', np.typeDict.values())
- def test_name_builtin(self, t):
- name = t.__name__
- if name.endswith('_'):
- name = name[:-1]
- assert_equal(np.dtype(t).name, name)
-
def test_name_dtype_subclass(self):
# Ticket #4357
class user_def_subcls(np.void):
@@ -747,6 +1054,50 @@ def test_invalid_dtype_string():
assert_raises(TypeError, np.dtype, u'Fl\xfcgel')
+class TestFromDTypeAttribute(object):
+ def test_simple(self):
+ class dt:
+ dtype = "f8"
+
+ assert np.dtype(dt) == np.float64
+ assert np.dtype(dt()) == np.float64
+
+ def test_recursion(self):
+ class dt:
+ pass
+
+ dt.dtype = dt
+ with pytest.raises(RecursionError):
+ np.dtype(dt)
+
+ dt_instance = dt()
+ dt_instance.dtype = dt
+ with pytest.raises(RecursionError):
+ np.dtype(dt_instance)
+
+ def test_void_subtype(self):
+ class dt(np.void):
+ # This code path is fully untested before, so it is unclear
+ # what this should be useful for. Note that if np.void is used
+ # numpy will think we are deallocating a base type [1.17, 2019-02].
+ dtype = np.dtype("f,f")
+ pass
+
+ np.dtype(dt)
+ np.dtype(dt(1))
+
+ def test_void_subtype_recursion(self):
+ class dt(np.void):
+ pass
+
+ dt.dtype = dt
+
+ with pytest.raises(RecursionError):
+ np.dtype(dt)
+
+ with pytest.raises(RecursionError):
+ np.dtype(dt(1))
+
class TestFromCTypes(object):
@staticmethod
@@ -775,7 +1126,82 @@ class TestFromCTypes(object):
], align=True)
self.check(PaddedStruct, expected)
- @pytest.mark.xfail(reason="_pack_ is ignored - see gh-11651")
+ def test_bit_fields(self):
+ class BitfieldStruct(ctypes.Structure):
+ _fields_ = [
+ ('a', ctypes.c_uint8, 7),
+ ('b', ctypes.c_uint8, 1)
+ ]
+ assert_raises(TypeError, np.dtype, BitfieldStruct)
+ assert_raises(TypeError, np.dtype, BitfieldStruct())
+
+ def test_pointer(self):
+ p_uint8 = ctypes.POINTER(ctypes.c_uint8)
+ assert_raises(TypeError, np.dtype, p_uint8)
+
+ def test_void_pointer(self):
+ self.check(ctypes.c_void_p, np.uintp)
+
+ def test_union(self):
+ class Union(ctypes.Union):
+ _fields_ = [
+ ('a', ctypes.c_uint8),
+ ('b', ctypes.c_uint16),
+ ]
+ expected = np.dtype(dict(
+ names=['a', 'b'],
+ formats=[np.uint8, np.uint16],
+ offsets=[0, 0],
+ itemsize=2
+ ))
+ self.check(Union, expected)
+
+ def test_union_with_struct_packed(self):
+ class Struct(ctypes.Structure):
+ _pack_ = 1
+ _fields_ = [
+ ('one', ctypes.c_uint8),
+ ('two', ctypes.c_uint32)
+ ]
+
+ class Union(ctypes.Union):
+ _fields_ = [
+ ('a', ctypes.c_uint8),
+ ('b', ctypes.c_uint16),
+ ('c', ctypes.c_uint32),
+ ('d', Struct),
+ ]
+ expected = np.dtype(dict(
+ names=['a', 'b', 'c', 'd'],
+ formats=['u1', np.uint16, np.uint32, [('one', 'u1'), ('two', np.uint32)]],
+ offsets=[0, 0, 0, 0],
+ itemsize=ctypes.sizeof(Union)
+ ))
+ self.check(Union, expected)
+
+ def test_union_packed(self):
+ class Struct(ctypes.Structure):
+ _fields_ = [
+ ('one', ctypes.c_uint8),
+ ('two', ctypes.c_uint32)
+ ]
+ _pack_ = 1
+ class Union(ctypes.Union):
+ _pack_ = 1
+ _fields_ = [
+ ('a', ctypes.c_uint8),
+ ('b', ctypes.c_uint16),
+ ('c', ctypes.c_uint32),
+ ('d', Struct),
+ ]
+ expected = np.dtype(dict(
+ names=['a', 'b', 'c', 'd'],
+ formats=['u1', np.uint16, np.uint32, [('one', 'u1'), ('two', np.uint32)]],
+ offsets=[0, 0, 0, 0],
+ itemsize=ctypes.sizeof(Union)
+ ))
+ self.check(Union, expected)
+
def test_packed_structure(self):
class PackedStructure(ctypes.Structure):
_pack_ = 1
@@ -789,8 +1215,45 @@ class TestFromCTypes(object):
])
self.check(PackedStructure, expected)
- @pytest.mark.xfail(sys.byteorder != 'little',
- reason="non-native endianness does not work - see gh-10533")
+ def test_large_packed_structure(self):
+ class PackedStructure(ctypes.Structure):
+ _pack_ = 2
+ _fields_ = [
+ ('a', ctypes.c_uint8),
+ ('b', ctypes.c_uint16),
+ ('c', ctypes.c_uint8),
+ ('d', ctypes.c_uint16),
+ ('e', ctypes.c_uint32),
+ ('f', ctypes.c_uint32),
+ ('g', ctypes.c_uint8)
+ ]
+ expected = np.dtype(dict(
+ formats=[np.uint8, np.uint16, np.uint8, np.uint16, np.uint32, np.uint32, np.uint8 ],
+ offsets=[0, 2, 4, 6, 8, 12, 16],
+ names=['a', 'b', 'c', 'd', 'e', 'f', 'g'],
+ itemsize=18))
+ self.check(PackedStructure, expected)
+
+ def test_big_endian_structure_packed(self):
+ class BigEndStruct(ctypes.BigEndianStructure):
+ _fields_ = [
+ ('one', ctypes.c_uint8),
+ ('two', ctypes.c_uint32)
+ ]
+ _pack_ = 1
+ expected = np.dtype([('one', 'u1'), ('two', '>u4')])
+ self.check(BigEndStruct, expected)
+
+ def test_little_endian_structure_packed(self):
+ class LittleEndStruct(ctypes.LittleEndianStructure):
+ _fields_ = [
+ ('one', ctypes.c_uint8),
+ ('two', ctypes.c_uint32)
+ ]
+ _pack_ = 1
+ expected = np.dtype([('one', 'u1'), ('two', '<u4')])
+ self.check(LittleEndStruct, expected)
+
def test_little_endian_structure(self):
class PaddedStruct(ctypes.LittleEndianStructure):
_fields_ = [
@@ -803,8 +1266,6 @@ class TestFromCTypes(object):
], align=True)
self.check(PaddedStruct, expected)
- @pytest.mark.xfail(sys.byteorder != 'big',
- reason="non-native endianness does not work - see gh-10533")
def test_big_endian_structure(self):
class PaddedStruct(ctypes.BigEndianStructure):
_fields_ = [
@@ -816,3 +1277,24 @@ class TestFromCTypes(object):
('b', '>H')
], align=True)
self.check(PaddedStruct, expected)
+
+ def test_simple_endian_types(self):
+ self.check(ctypes.c_uint16.__ctype_le__, np.dtype('<u2'))
+ self.check(ctypes.c_uint16.__ctype_be__, np.dtype('>u2'))
+ self.check(ctypes.c_uint8.__ctype_le__, np.dtype('u1'))
+ self.check(ctypes.c_uint8.__ctype_be__, np.dtype('u1'))
+
+ all_types = set(np.typecodes['All'])
+ all_pairs = permutations(all_types, 2)
+
+ @pytest.mark.parametrize("pair", all_pairs)
+ def test_pairs(self, pair):
+ """
+ Check that np.dtype('x,y') matches [np.dtype('x'), np.dtype('y')]
+ Example: np.dtype('d,I') -> dtype([('f0', '<f8'), ('f1', '<u4')])
+ """
+ # gh-5645: check that np.dtype('i,L') can be used
+ pair_type = np.dtype('{},{}'.format(*pair))
+ expected = np.dtype([('f0', pair[0]), ('f1', pair[1])])
+ assert_equal(pair_type, expected)
+
diff --git a/numpy/core/tests/test_einsum.py b/numpy/core/tests/test_einsum.py
index 6b5b9c06e..cfeeb8a90 100644
--- a/numpy/core/tests/test_einsum.py
+++ b/numpy/core/tests/test_einsum.py
@@ -5,15 +5,13 @@ import itertools
import numpy as np
from numpy.testing import (
assert_, assert_equal, assert_array_equal, assert_almost_equal,
- assert_raises, suppress_warnings
+ assert_raises, suppress_warnings, assert_raises_regex
)
# Setup for optimize einsum
chars = 'abcdefghij'
sizes = np.array([2, 3, 4, 5, 4, 3, 2, 6, 5, 4, 3])
-global_size_dict = {}
-for size, char in zip(sizes, chars):
- global_size_dict[char] = size
+global_size_dict = dict(zip(chars, sizes))
class TestEinsum(object):
@@ -92,6 +90,11 @@ class TestEinsum(object):
optimize=do_opt)
assert_raises(ValueError, np.einsum, "i->i", [[0, 1], [0, 1]],
out=np.arange(4).reshape(2, 2), optimize=do_opt)
+ with assert_raises_regex(ValueError, "'b'"):
+ # gh-11221 - 'c' erroneously appeared in the error message
+ a = np.ones((3, 3, 4, 5, 6))
+ b = np.ones((3, 4, 5))
+ np.einsum('aabcb,abc', a, b)
def test_einsum_views(self):
# pass-through
diff --git a/numpy/core/tests/test_errstate.py b/numpy/core/tests/test_errstate.py
index 670d485c1..0008c4cc8 100644
--- a/numpy/core/tests/test_errstate.py
+++ b/numpy/core/tests/test_errstate.py
@@ -39,3 +39,11 @@ class TestErrstate(object):
with np.errstate(call=None):
assert_(np.geterrcall() is None, 'call is not None')
assert_(np.geterrcall() is olderrcall, 'call is not olderrcall')
+
+ def test_errstate_decorator(self):
+ @np.errstate(all='ignore')
+ def foo():
+ a = -np.arange(3)
+ a // 0
+
+ foo()
diff --git a/numpy/core/tests/test_extint128.py b/numpy/core/tests/test_extint128.py
index 0e9c07fd5..7c454a603 100644
--- a/numpy/core/tests/test_extint128.py
+++ b/numpy/core/tests/test_extint128.py
@@ -1,6 +1,5 @@
from __future__ import division, absolute_import, print_function
-import sys
import itertools
import contextlib
import operator
@@ -8,7 +7,6 @@ import pytest
import numpy as np
import numpy.core._multiarray_tests as mt
-from numpy.compat import long
from numpy.testing import assert_raises, assert_equal
diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py
index d0ff1c15f..84b60b19c 100644
--- a/numpy/core/tests/test_function_base.py
+++ b/numpy/core/tests/test_function_base.py
@@ -2,7 +2,7 @@ from __future__ import division, absolute_import, print_function
from numpy import (
logspace, linspace, geomspace, dtype, array, sctypes, arange, isnan,
- ndarray, sqrt, nextafter
+ ndarray, sqrt, nextafter, stack
)
from numpy.testing import (
assert_, assert_equal, assert_raises, assert_array_equal, assert_allclose,
@@ -49,11 +49,25 @@ class TestLogspace(object):
assert_(len(y) == 50)
y = logspace(0, 6, num=100)
assert_(y[-1] == 10 ** 6)
- y = logspace(0, 6, endpoint=0)
+ y = logspace(0, 6, endpoint=False)
assert_(y[-1] < 10 ** 6)
y = logspace(0, 6, num=7)
assert_array_equal(y, [1, 10, 100, 1e3, 1e4, 1e5, 1e6])
+ def test_start_stop_array(self):
+ start = array([0., 1.])
+ stop = array([6., 7.])
+ t1 = logspace(start, stop, 6)
+ t2 = stack([logspace(_start, _stop, 6)
+ for _start, _stop in zip(start, stop)], axis=1)
+ assert_equal(t1, t2)
+ t3 = logspace(start, stop[0], 6)
+ t4 = stack([logspace(_start, stop[0], 6)
+ for _start in start], axis=1)
+ assert_equal(t3, t4)
+ t5 = logspace(start, stop, 6, axis=-1)
+ assert_equal(t5, t2.T)
+
def test_dtype(self):
y = logspace(0, 6, dtype='float32')
assert_equal(y.dtype, dtype('float32'))
@@ -156,7 +170,7 @@ class TestGeomspace(object):
y = geomspace(1, 1e6, dtype=complex)
assert_equal(y.dtype, dtype('complex'))
- def test_array_scalar(self):
+ def test_start_stop_array_scalar(self):
lim1 = array([120, 100], dtype="int8")
lim2 = array([-120, -100], dtype="int8")
lim3 = array([1200, 1000], dtype="uint16")
@@ -172,6 +186,21 @@ class TestGeomspace(object):
assert_allclose(t2, t5, rtol=1e-2)
assert_allclose(t3, t6, rtol=1e-5)
+ def test_start_stop_array(self):
+ # Try to use all special cases.
+ start = array([1.e0, 32., 1j, -4j, 1+1j, -1])
+ stop = array([1.e4, 2., 16j, -324j, 10000+10000j, 1])
+ t1 = geomspace(start, stop, 5)
+ t2 = stack([geomspace(_start, _stop, 5)
+ for _start, _stop in zip(start, stop)], axis=1)
+ assert_equal(t1, t2)
+ t3 = geomspace(start, stop[0], 5)
+ t4 = stack([geomspace(_start, stop[0], 5)
+ for _start in start], axis=1)
+ assert_equal(t3, t4)
+ t5 = geomspace(start, stop, 5, axis=-1)
+ assert_equal(t5, t2.T)
+
def test_physical_quantities(self):
a = PhysicalQuantity(1.0)
b = PhysicalQuantity(5.0)
@@ -200,17 +229,14 @@ class TestLinspace(object):
assert_(len(y) == 50)
y = linspace(2, 10, num=100)
assert_(y[-1] == 10)
- y = linspace(2, 10, endpoint=0)
+ y = linspace(2, 10, endpoint=False)
assert_(y[-1] < 10)
assert_raises(ValueError, linspace, 0, 10, num=-1)
def test_corner(self):
y = list(linspace(0, 1, 1))
assert_(y == [0.0], y)
- with suppress_warnings() as sup:
- sup.filter(DeprecationWarning, ".*safely interpreted as an integer")
- y = list(linspace(0, 1, 2.5))
- assert_(y == [0.0, 1.0])
+ assert_raises(TypeError, linspace, 0, 1, num=2.5)
def test_type(self):
t1 = linspace(0, 1, 0).dtype
@@ -227,7 +253,7 @@ class TestLinspace(object):
y = linspace(0, 6, dtype='int32')
assert_equal(y.dtype, dtype('int32'))
- def test_array_scalar(self):
+ def test_start_stop_array_scalar(self):
lim1 = array([-120, 100], dtype="int8")
lim2 = array([120, -100], dtype="int8")
lim3 = array([1200, 1000], dtype="uint16")
@@ -241,6 +267,20 @@ class TestLinspace(object):
assert_equal(t2, t5)
assert_equal(t3, t6)
+ def test_start_stop_array(self):
+ start = array([-120, 120], dtype="int8")
+ stop = array([100, -100], dtype="int8")
+ t1 = linspace(start, stop, 5)
+ t2 = stack([linspace(_start, _stop, 5)
+ for _start, _stop in zip(start, stop)], axis=1)
+ assert_equal(t1, t2)
+ t3 = linspace(start, stop[0], 5)
+ t4 = stack([linspace(_start, stop[0], 5)
+ for _start in start], axis=1)
+ assert_equal(t3, t4)
+ t5 = linspace(start, stop, 5, axis=-1)
+ assert_equal(t5, t2.T)
+
def test_complex(self):
lim1 = linspace(1 + 2j, 3 + 4j, 5)
t1 = array([1.0+2.j, 1.5+2.5j, 2.0+3j, 2.5+3.5j, 3.0+4j])
@@ -285,9 +325,7 @@ class TestLinspace(object):
@property
def __array_interface__(self):
- # Ideally should be `'shape': ()` but the current interface
- # does not allow that
- return {'shape': (1,), 'typestr': '<i4', 'data': self._data,
+ return {'shape': (), 'typestr': '<i4', 'data': self._data,
'version': 3}
def __mul__(self, other):
@@ -321,3 +359,9 @@ class TestLinspace(object):
assert_(isinstance(y, tuple) and len(y) == 2 and
len(y[0]) == num and isnan(y[1]),
'num={0}, endpoint={1}'.format(num, ept))
+
+ def test_object(self):
+ start = array(1, dtype='O')
+ stop = array(2, dtype='O')
+ y = linspace(start, stop, 3)
+ assert_array_equal(y, array([1., 1.5, 2.]))
diff --git a/numpy/core/tests/test_getlimits.py b/numpy/core/tests/test_getlimits.py
index ca8093c62..2f6648183 100644
--- a/numpy/core/tests/test_getlimits.py
+++ b/numpy/core/tests/test_getlimits.py
@@ -7,10 +7,7 @@ import numpy as np
from numpy.core import finfo, iinfo
from numpy import half, single, double, longdouble
from numpy.testing import assert_equal, assert_, assert_raises
-from numpy.core.getlimits import (
- _discovered_machar, _float16_ma, _float32_ma, _float64_ma, _float128_ma,
- _float80_ma
- )
+from numpy.core.getlimits import _discovered_machar, _float_ma
##################################################
@@ -101,9 +98,9 @@ def assert_ma_equal(discovered, ma_like):
def test_known_types():
# Test we are correctly compiling parameters for known types
- for ftype, ma_like in ((np.float16, _float16_ma),
- (np.float32, _float32_ma),
- (np.float64, _float64_ma)):
+ for ftype, ma_like in ((np.float16, _float_ma[16]),
+ (np.float32, _float_ma[32]),
+ (np.float64, _float_ma[64])):
assert_ma_equal(_discovered_machar(ftype), ma_like)
# Suppress warning for broken discovery of double double on PPC
with np.errstate(all='ignore'):
@@ -111,10 +108,10 @@ def test_known_types():
bytes = np.dtype(np.longdouble).itemsize
if (ld_ma.it, ld_ma.maxexp) == (63, 16384) and bytes in (12, 16):
# 80-bit extended precision
- assert_ma_equal(ld_ma, _float80_ma)
+ assert_ma_equal(ld_ma, _float_ma[80])
elif (ld_ma.it, ld_ma.maxexp) == (112, 16384) and bytes == 16:
# IEE 754 128-bit
- assert_ma_equal(ld_ma, _float128_ma)
+ assert_ma_equal(ld_ma, _float_ma[128])
def test_plausible_finfo():
diff --git a/numpy/core/tests/test_half.py b/numpy/core/tests/test_half.py
index d715569f8..1e1e6d7d9 100644
--- a/numpy/core/tests/test_half.py
+++ b/numpy/core/tests/test_half.py
@@ -5,7 +5,7 @@ import pytest
import numpy as np
from numpy import uint16, float16, float32, float64
-from numpy.testing import assert_, assert_equal, suppress_warnings
+from numpy.testing import assert_, assert_equal
def assert_raises_fpe(strmatch, callable, *args, **kwargs):
@@ -69,6 +69,85 @@ class TestHalf(object):
j = np.array(i_f16, dtype=int)
assert_equal(i_int, j)
+ @pytest.mark.parametrize("offset", [None, "up", "down"])
+ @pytest.mark.parametrize("shift", [None, "up", "down"])
+ @pytest.mark.parametrize("float_t", [np.float32, np.float64])
+ def test_half_conversion_rounding(self, float_t, shift, offset):
+ # Assumes that round to even is used during casting.
+ max_pattern = np.float16(np.finfo(np.float16).max).view(np.uint16)
+
+ # Test all (positive) finite numbers, denormals are most interesting
+ # however:
+ f16s_patterns = np.arange(0, max_pattern+1, dtype=np.uint16)
+ f16s_float = f16s_patterns.view(np.float16).astype(float_t)
+
+ # Shift the values by half a bit up or a down (or do not shift),
+ if shift == "up":
+ f16s_float = 0.5 * (f16s_float[:-1] + f16s_float[1:])[1:]
+ elif shift == "down":
+ f16s_float = 0.5 * (f16s_float[:-1] + f16s_float[1:])[:-1]
+ else:
+ f16s_float = f16s_float[1:-1]
+
+ # Increase the float by a minimal value:
+ if offset == "up":
+ f16s_float = np.nextafter(f16s_float, float_t(1e50))
+ elif offset == "down":
+ f16s_float = np.nextafter(f16s_float, float_t(-1e50))
+
+ # Convert back to float16 and its bit pattern:
+ res_patterns = f16s_float.astype(np.float16).view(np.uint16)
+
+ # The above calculations tries the original values, or the exact
+ # mid points between the float16 values. It then further offsets them
+ # by as little as possible. If no offset occurs, "round to even"
+ # logic will be necessary, an arbitrarily small offset should cause
+ # normal up/down rounding always.
+
+ # Calculate the expected pattern:
+ cmp_patterns = f16s_patterns[1:-1].copy()
+
+ if shift == "down" and offset != "up":
+ shift_pattern = -1
+ elif shift == "up" and offset != "down":
+ shift_pattern = 1
+ else:
+ # There cannot be a shift, either shift is None, so all rounding
+ # will go back to original, or shift is reduced by offset too much.
+ shift_pattern = 0
+
+ # If rounding occurs, is it normal rounding or round to even?
+ if offset is None:
+ # Round to even occurs, modify only non-even, cast to allow + (-1)
+ cmp_patterns[0::2].view(np.int16)[...] += shift_pattern
+ else:
+ cmp_patterns.view(np.int16)[...] += shift_pattern
+
+ assert_equal(res_patterns, cmp_patterns)
+
+ @pytest.mark.parametrize(["float_t", "uint_t", "bits"],
+ [(np.float32, np.uint32, 23),
+ (np.float64, np.uint64, 52)])
+ def test_half_conversion_denormal_round_even(self, float_t, uint_t, bits):
+ # Test specifically that all bits are considered when deciding
+ # whether round to even should occur (i.e. no bits are lost at the
+ # end. Compare also gh-12721. The most bits can get lost for the
+ # smallest denormal:
+ smallest_value = np.uint16(1).view(np.float16).astype(float_t)
+ assert smallest_value == 2**-24
+
+ # Will be rounded to zero based on round to even rule:
+ rounded_to_zero = smallest_value / float_t(2)
+ assert rounded_to_zero.astype(np.float16) == 0
+
+ # The significand will be all 0 for the float_t, test that we do not
+ # lose the lower ones of these:
+ for i in range(bits):
+ # slightly increasing the value should make it round up:
+ larger_pattern = rounded_to_zero.view(uint_t) | uint_t(1 << i)
+ larger_value = larger_pattern.view(float_t)
+ assert larger_value.astype(np.float16) == smallest_value
+
def test_nans_infs(self):
with np.errstate(all='ignore'):
# Check some of the ufuncs
@@ -301,21 +380,19 @@ class TestHalf(object):
assert_equal(np.copysign(b, a), [2, 5, 1, 4, 3])
assert_equal(np.maximum(a, b), [0, 5, 2, 4, 3])
- with suppress_warnings() as sup:
- sup.record(RuntimeWarning)
- x = np.maximum(b, c)
- assert_(np.isnan(x[3]))
- assert_equal(len(sup.log), 1)
+
+ x = np.maximum(b, c)
+ assert_(np.isnan(x[3]))
x[3] = 0
assert_equal(x, [0, 5, 1, 0, 6])
+
assert_equal(np.minimum(a, b), [-2, 1, 1, 4, 2])
- with suppress_warnings() as sup:
- sup.record(RuntimeWarning)
- x = np.minimum(b, c)
- assert_(np.isnan(x[3]))
- assert_equal(len(sup.log), 1)
+
+ x = np.minimum(b, c)
+ assert_(np.isnan(x[3]))
x[3] = 0
assert_equal(x, [-2, -1, -np.inf, 0, 3])
+
assert_equal(np.fmax(a, b), [0, 5, 2, 4, 3])
assert_equal(np.fmax(b, c), [0, 5, 1, 4, 6])
assert_equal(np.fmin(a, b), [-2, 1, 1, 4, 2])
diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py
index 99792cee7..70a5a246f 100644
--- a/numpy/core/tests/test_indexing.py
+++ b/numpy/core/tests/test_indexing.py
@@ -249,6 +249,15 @@ class TestIndexing(object):
[4, 0, 6],
[0, 8, 0]])
+ def test_boolean_indexing_list(self):
+ # Regression test for #13715. It's a use-after-free bug which the
+ # test won't directly catch, but it will show up in valgrind.
+ a = np.array([1, 2, 3])
+ b = [True, False, True]
+ # Two variants of the test because the first takes a fast path
+ assert_equal(a[b], [1, 3])
+ assert_equal(a[None, b], [[1, 3]])
+
def test_reverse_strides_and_subspace_bufferinit(self):
# This tests that the strides are not reversed for simple and
# subspace fancy indexing.
@@ -608,6 +617,19 @@ class TestSubclasses(object):
assert_array_equal(s_bool, a[a > 0])
assert_array_equal(s_bool.base, a[a > 0])
+ def test_fancy_on_read_only(self):
+ # Test that fancy indexing on read-only SubClass does not make a
+ # read-only copy (gh-14132)
+ class SubClass(np.ndarray):
+ pass
+
+ a = np.arange(5)
+ s = a.view(SubClass)
+ s.flags.writeable = False
+ s_fancy = s[[0, 1, 2]]
+ assert_(s_fancy.flags.writeable)
+
+
def test_finalize_gets_full_info(self):
# Array finalize should be called on the filled array.
class SubClass(np.ndarray):
diff --git a/numpy/core/tests/test_item_selection.py b/numpy/core/tests/test_item_selection.py
index 3bc24fc95..9bd246866 100644
--- a/numpy/core/tests/test_item_selection.py
+++ b/numpy/core/tests/test_item_selection.py
@@ -79,9 +79,9 @@ class TestTake(object):
assert_array_equal(a, a_original)
def test_empty_argpartition(self):
- # In reference to github issue #6530
- a = np.array([0, 2, 4, 6, 8, 10])
- a = a.argpartition(np.array([], dtype=np.int16))
+ # In reference to github issue #6530
+ a = np.array([0, 2, 4, 6, 8, 10])
+ a = a.argpartition(np.array([], dtype=np.int16))
- b = np.array([0, 1, 2, 3, 4, 5])
- assert_array_equal(a, b)
+ b = np.array([0, 1, 2, 3, 4, 5])
+ assert_array_equal(a, b)
diff --git a/numpy/core/tests/test_longdouble.py b/numpy/core/tests/test_longdouble.py
index cf50d5d5c..59ac5923c 100644
--- a/numpy/core/tests/test_longdouble.py
+++ b/numpy/core/tests/test_longdouble.py
@@ -1,10 +1,12 @@
from __future__ import division, absolute_import, print_function
+import warnings
import pytest
import numpy as np
from numpy.testing import (
- assert_, assert_equal, assert_raises, assert_array_equal, temppath,
+ assert_, assert_equal, assert_raises, assert_warns, assert_array_equal,
+ temppath,
)
from numpy.core.tests._locales import CommaDecimalPointLocale
@@ -70,18 +72,21 @@ def test_fromstring():
def test_fromstring_bogus():
- assert_equal(np.fromstring("1. 2. 3. flop 4.", dtype=float, sep=" "),
- np.array([1., 2., 3.]))
+ with assert_warns(DeprecationWarning):
+ assert_equal(np.fromstring("1. 2. 3. flop 4.", dtype=float, sep=" "),
+ np.array([1., 2., 3.]))
def test_fromstring_empty():
- assert_equal(np.fromstring("xxxxx", sep="x"),
- np.array([]))
+ with assert_warns(DeprecationWarning):
+ assert_equal(np.fromstring("xxxxx", sep="x"),
+ np.array([]))
def test_fromstring_missing():
- assert_equal(np.fromstring("1xx3x4x5x6", sep="x"),
- np.array([1]))
+ with assert_warns(DeprecationWarning):
+ assert_equal(np.fromstring("1xx3x4x5x6", sep="x"),
+ np.array([1]))
class TestFileBased(object):
@@ -94,7 +99,9 @@ class TestFileBased(object):
with temppath() as path:
with open(path, 'wt') as f:
f.write("1. 2. 3. flop 4.\n")
- res = np.fromfile(path, dtype=float, sep=" ")
+
+ with assert_warns(DeprecationWarning):
+ res = np.fromfile(path, dtype=float, sep=" ")
assert_equal(res, np.array([1., 2., 3.]))
@pytest.mark.skipif(string_to_longdouble_inaccurate,
@@ -185,12 +192,14 @@ class TestCommaDecimalPointLocale(CommaDecimalPointLocale):
assert_equal(a[0], f)
def test_fromstring_best_effort_float(self):
- assert_equal(np.fromstring("1,234", dtype=float, sep=" "),
- np.array([1.]))
+ with assert_warns(DeprecationWarning):
+ assert_equal(np.fromstring("1,234", dtype=float, sep=" "),
+ np.array([1.]))
def test_fromstring_best_effort(self):
- assert_equal(np.fromstring("1,234", dtype=np.longdouble, sep=" "),
- np.array([1.]))
+ with assert_warns(DeprecationWarning):
+ assert_equal(np.fromstring("1,234", dtype=np.longdouble, sep=" "),
+ np.array([1.]))
def test_fromstring_foreign(self):
s = "1.234"
@@ -203,5 +212,32 @@ class TestCommaDecimalPointLocale(CommaDecimalPointLocale):
assert_array_equal(a, b)
def test_fromstring_foreign_value(self):
- b = np.fromstring("1,234", dtype=np.longdouble, sep=" ")
- assert_array_equal(b[0], 1)
+ with assert_warns(DeprecationWarning):
+ b = np.fromstring("1,234", dtype=np.longdouble, sep=" ")
+ assert_array_equal(b[0], 1)
+
+
+@pytest.mark.parametrize("int_val", [
+ # cases discussed in gh-10723
+ # and gh-9968
+ 2 ** 1024, 0])
+def test_longdouble_from_int(int_val):
+ # for issue gh-9968
+ str_val = str(int_val)
+ # we'll expect a RuntimeWarning on platforms
+ # with np.longdouble equivalent to np.double
+ # for large integer input
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always', '', RuntimeWarning)
+ # can be inf==inf on some platforms
+ assert np.longdouble(int_val) == np.longdouble(str_val)
+ # we can't directly compare the int and
+ # max longdouble value on all platforms
+ if np.allclose(np.finfo(np.longdouble).max,
+ np.finfo(np.double).max) and w:
+ assert w[0].category is RuntimeWarning
+
+@pytest.mark.parametrize("bool_val", [
+ True, False])
+def test_longdouble_from_bool(bool_val):
+ assert np.longdouble(bool_val) == np.longdouble(int(bool_val))
diff --git a/numpy/core/tests/test_mem_overlap.py b/numpy/core/tests/test_mem_overlap.py
index f4ce6a84a..3c8e0e722 100644
--- a/numpy/core/tests/test_mem_overlap.py
+++ b/numpy/core/tests/test_mem_overlap.py
@@ -10,7 +10,7 @@ from numpy.core import _umath_tests
from numpy.lib.stride_tricks import as_strided
from numpy.compat import long
from numpy.testing import (
- assert_, assert_raises, assert_equal, assert_array_equal, assert_allclose
+ assert_, assert_raises, assert_equal, assert_array_equal
)
if sys.version_info[0] >= 3:
diff --git a/numpy/core/tests/test_memmap.py b/numpy/core/tests/test_memmap.py
index 990d0ae26..d2ae564b2 100644
--- a/numpy/core/tests/test_memmap.py
+++ b/numpy/core/tests/test_memmap.py
@@ -204,3 +204,13 @@ class TestMemmap(object):
self.tmpfp.write(b'a'*16)
mm = memmap(self.tmpfp, dtype='float64')
assert_equal(mm.shape, (2,))
+
+ def test_empty_array(self):
+ # gh-12653
+ with pytest.raises(ValueError, match='empty file'):
+ memmap(self.tmpfp, shape=(0,4), mode='w+')
+
+ self.tmpfp.write(b'\0')
+
+ # ok now the file is not empty
+ memmap(self.tmpfp, shape=(0,4), mode='w+')
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index e8353a702..9b124f603 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -20,6 +20,17 @@ import gc
import weakref
import pytest
from contextlib import contextmanager
+
+from numpy.compat import pickle
+
+try:
+ import pathlib
+except ImportError:
+ try:
+ import pathlib2 as pathlib
+ except ImportError:
+ pathlib = None
+
if sys.version_info[0] >= 3:
import builtins
else:
@@ -33,7 +44,7 @@ from numpy.testing import (
assert_, assert_raises, assert_warns, assert_equal, assert_almost_equal,
assert_array_equal, assert_raises_regex, assert_array_almost_equal,
assert_allclose, IS_PYPY, HAS_REFCOUNT, assert_array_less, runstring,
- temppath, suppress_warnings
+ temppath, suppress_warnings, break_cycles,
)
from numpy.core.tests._locales import CommaDecimalPointLocale
@@ -51,7 +62,12 @@ else:
def _aligned_zeros(shape, dtype=float, order="C", align=None):
- """Allocate a new ndarray with aligned memory."""
+ """
+ Allocate a new ndarray with aligned memory.
+
+ The ndarray is guaranteed *not* aligned to twice the requested alignment.
+ Eg, if align=4, guarantees it is not aligned to 8. If align=None uses
+ dtype.alignment."""
dtype = np.dtype(dtype)
if dtype == np.dtype(object):
# Can't do this, fall back to standard allocation (which
@@ -64,10 +80,15 @@ def _aligned_zeros(shape, dtype=float, order="C", align=None):
if not hasattr(shape, '__len__'):
shape = (shape,)
size = functools.reduce(operator.mul, shape) * dtype.itemsize
- buf = np.empty(size + align + 1, np.uint8)
- offset = buf.__array_interface__['data'][0] % align
+ buf = np.empty(size + 2*align + 1, np.uint8)
+
+ ptr = buf.__array_interface__['data'][0]
+ offset = ptr % align
if offset != 0:
offset = align - offset
+ if (ptr % (2*align)) == 0:
+ offset += align
+
# Note: slices producing 0-size arrays do not necessarily change
# data pointer --- so we use and allocate size+1
buf = buf[offset:offset+size+1][:-1]
@@ -89,6 +110,130 @@ class TestFlags(object):
self.a[0] = 5
self.a[0] = 0
+ def test_writeable_any_base(self):
+ # Ensure that any base being writeable is sufficient to change flag;
+ # this is especially interesting for arrays from an array interface.
+ arr = np.arange(10)
+
+ class subclass(np.ndarray):
+ pass
+
+ # Create subclass so base will not be collapsed, this is OK to change
+ view1 = arr.view(subclass)
+ view2 = view1[...]
+ arr.flags.writeable = False
+ view2.flags.writeable = False
+ view2.flags.writeable = True # Can be set to True again.
+
+ arr = np.arange(10)
+
+ class frominterface:
+ def __init__(self, arr):
+ self.arr = arr
+ self.__array_interface__ = arr.__array_interface__
+
+ view1 = np.asarray(frominterface)
+ view2 = view1[...]
+ view2.flags.writeable = False
+ view2.flags.writeable = True
+
+ view1.flags.writeable = False
+ view2.flags.writeable = False
+ with assert_raises(ValueError):
+ # Must assume not writeable, since only base is not:
+ view2.flags.writeable = True
+
+ def test_writeable_from_readonly(self):
+ # gh-9440 - make sure fromstring, from buffer on readonly buffers
+ # set writeable False
+ data = b'\x00' * 100
+ vals = np.frombuffer(data, 'B')
+ assert_raises(ValueError, vals.setflags, write=True)
+ types = np.dtype( [('vals', 'u1'), ('res3', 'S4')] )
+ values = np.core.records.fromstring(data, types)
+ vals = values['vals']
+ assert_raises(ValueError, vals.setflags, write=True)
+
+ def test_writeable_from_buffer(self):
+ data = bytearray(b'\x00' * 100)
+ vals = np.frombuffer(data, 'B')
+ assert_(vals.flags.writeable)
+ vals.setflags(write=False)
+ assert_(vals.flags.writeable is False)
+ vals.setflags(write=True)
+ assert_(vals.flags.writeable)
+ types = np.dtype( [('vals', 'u1'), ('res3', 'S4')] )
+ values = np.core.records.fromstring(data, types)
+ vals = values['vals']
+ assert_(vals.flags.writeable)
+ vals.setflags(write=False)
+ assert_(vals.flags.writeable is False)
+ vals.setflags(write=True)
+ assert_(vals.flags.writeable)
+
+ @pytest.mark.skipif(sys.version_info[0] < 3, reason="Python 2 always copies")
+ @pytest.mark.skipif(IS_PYPY, reason="PyPy always copies")
+ def test_writeable_pickle(self):
+ import pickle
+ # Small arrays will be copied without setting base.
+ # See condition for using PyArray_SetBaseObject in
+ # array_setstate.
+ a = np.arange(1000)
+ for v in range(pickle.HIGHEST_PROTOCOL):
+ vals = pickle.loads(pickle.dumps(a, v))
+ assert_(vals.flags.writeable)
+ assert_(isinstance(vals.base, bytes))
+
+ def test_writeable_from_c_data(self):
+ # Test that the writeable flag can be changed for an array wrapping
+ # low level C-data, but not owning its data.
+ # Also see that this is deprecated to change from python.
+ from numpy.core._multiarray_tests import get_c_wrapping_array
+
+ arr_writeable = get_c_wrapping_array(True)
+ assert not arr_writeable.flags.owndata
+ assert arr_writeable.flags.writeable
+ view = arr_writeable[...]
+
+ # Toggling the writeable flag works on the view:
+ view.flags.writeable = False
+ assert not view.flags.writeable
+ view.flags.writeable = True
+ assert view.flags.writeable
+ # Flag can be unset on the arr_writeable:
+ arr_writeable.flags.writeable = False
+
+ arr_readonly = get_c_wrapping_array(False)
+ assert not arr_readonly.flags.owndata
+ assert not arr_readonly.flags.writeable
+
+ for arr in [arr_writeable, arr_readonly]:
+ view = arr[...]
+ view.flags.writeable = False # make sure it is readonly
+ arr.flags.writeable = False
+ assert not arr.flags.writeable
+
+ with assert_raises(ValueError):
+ view.flags.writeable = True
+
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", DeprecationWarning)
+ with assert_raises(DeprecationWarning):
+ arr.flags.writeable = True
+
+ with assert_warns(DeprecationWarning):
+ arr.flags.writeable = True
+
+ def test_warnonwrite(self):
+ a = np.arange(10)
+ a.flags._warn_on_write = True
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always')
+ a[1] = 10
+ a[2] = 10
+ # only warn once
+ assert_(len(w) == 1)
+
def test_otherflags(self):
assert_equal(self.a.flags.carray, True)
assert_equal(self.a.flags['C'], True)
@@ -824,6 +969,29 @@ class TestCreation(object):
assert_equal(np.array([long(4), 2**80, long(4)]).dtype, object)
assert_equal(np.array([2**80, long(4)]).dtype, object)
+ def test_sequence_of_array_like(self):
+ class ArrayLike:
+ def __init__(self):
+ self.__array_interface__ = {
+ "shape": (42,),
+ "typestr": "<i1",
+ "data": bytes(42)
+ }
+
+ # Make sure __array_*__ is used instead of Sequence methods.
+ def __iter__(self):
+ raise AssertionError("__iter__ was called")
+
+ def __getitem__(self, idx):
+ raise AssertionError("__getitem__ was called")
+
+ def __len__(self):
+ return 42
+
+ assert_equal(
+ np.array([ArrayLike()]),
+ np.zeros((1, 42), dtype=np.byte))
+
def test_non_sequence_sequence(self):
"""Should not segfault.
@@ -1342,10 +1510,10 @@ class TestZeroSizeFlexible(object):
sort_func(zs, kind=kind, **kwargs)
def test_sort(self):
- self._test_sort_partition('sort', kinds='qhm')
+ self._test_sort_partition('sort', kinds='qhs')
def test_argsort(self):
- self._test_sort_partition('argsort', kinds='qhm')
+ self._test_sort_partition('argsort', kinds='qhs')
def test_partition(self):
self._test_sort_partition('partition', kinds=['introselect'], kth=2)
@@ -1370,8 +1538,11 @@ class TestZeroSizeFlexible(object):
# viewing as any non-empty type gives an empty result
assert_equal(zs.view((dt, 1)).shape, (0,))
+ def test_dumps(self):
+ zs = self._zeros(10, int)
+ assert_equal(zs, pickle.loads(zs.dumps()))
+
def test_pickle(self):
- import pickle
for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
for dt in [bytes, np.void, unicode]:
zs = self._zeros(10, dt)
@@ -1380,8 +1551,26 @@ class TestZeroSizeFlexible(object):
assert_equal(zs.dtype, zs2.dtype)
+ @pytest.mark.skipif(pickle.HIGHEST_PROTOCOL < 5,
+ reason="requires pickle protocol 5")
+ def test_pickle_with_buffercallback(self):
+ array = np.arange(10)
+ buffers = []
+ bytes_string = pickle.dumps(array, buffer_callback=buffers.append,
+ protocol=5)
+ array_from_buffer = pickle.loads(bytes_string, buffers=buffers)
+ # when using pickle protocol 5 with buffer callbacks,
+ # array_from_buffer is reconstructed from a buffer holding a view
+ # to the initial array's data, so modifying an element in array
+ # should modify it in array_from_buffer too.
+ array[0] = -1
+ assert array_from_buffer[0] == -1, array_from_buffer[0]
+
class TestMethods(object):
+
+ sort_kinds = ['quicksort', 'heapsort', 'stable']
+
def test_compress(self):
tgt = [[5, 6, 7, 8, 9]]
arr = np.arange(10).reshape(2, 5)
@@ -1421,6 +1610,11 @@ class TestMethods(object):
# gh-12031, caused SEGFAULT
assert_raises(TypeError, oned.choose,np.void(0), [oned])
+ # gh-6272 check overlap on out
+ x = np.arange(5)
+ y = np.choose([0,0,0], [x[:3], x[:3], x[:3]], out=x[1:4], mode='wrap')
+ assert_equal(y, np.array([0, 1, 2]))
+
def test_prod(self):
ba = [1, 2, 10, 11, 6, 5, 4]
ba2 = [[1, 2, 3, 4], [5, 6, 7, 9], [10, 3, 4, 5]]
@@ -1540,22 +1734,37 @@ class TestMethods(object):
# of sorted items must be greater than ~50 to check the actual
# algorithm because quick and merge sort fall over to insertion
# sort for small arrays.
- a = np.arange(101)
- b = a[::-1].copy()
- for kind in ['q', 'm', 'h']:
- msg = "scalar sort, kind=%s" % kind
- c = a.copy()
- c.sort(kind=kind)
- assert_equal(c, a, msg)
- c = b.copy()
- c.sort(kind=kind)
- assert_equal(c, a, msg)
+ # Test unsigned dtypes and nonnegative numbers
+ for dtype in [np.uint8, np.uint16, np.uint32, np.uint64, np.float16, np.float32, np.float64, np.longdouble]:
+ a = np.arange(101, dtype=dtype)
+ b = a[::-1].copy()
+ for kind in self.sort_kinds:
+ msg = "scalar sort, kind=%s, dtype=%s" % (kind, dtype)
+ c = a.copy()
+ c.sort(kind=kind)
+ assert_equal(c, a, msg)
+ c = b.copy()
+ c.sort(kind=kind)
+ assert_equal(c, a, msg)
+
+ # Test signed dtypes and negative numbers as well
+ for dtype in [np.int8, np.int16, np.int32, np.int64, np.float16, np.float32, np.float64, np.longdouble]:
+ a = np.arange(-50, 51, dtype=dtype)
+ b = a[::-1].copy()
+ for kind in self.sort_kinds:
+ msg = "scalar sort, kind=%s, dtype=%s" % (kind, dtype)
+ c = a.copy()
+ c.sort(kind=kind)
+ assert_equal(c, a, msg)
+ c = b.copy()
+ c.sort(kind=kind)
+ assert_equal(c, a, msg)
# test complex sorts. These use the same code as the scalars
# but the compare function differs.
ai = a*1j + 1
bi = b*1j + 1
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "complex sort, real part == 1, kind=%s" % kind
c = ai.copy()
c.sort(kind=kind)
@@ -1565,7 +1774,7 @@ class TestMethods(object):
assert_equal(c, ai, msg)
ai = a + 1j
bi = b + 1j
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "complex sort, imag part == 1, kind=%s" % kind
c = ai.copy()
c.sort(kind=kind)
@@ -1587,7 +1796,7 @@ class TestMethods(object):
s = 'aaaaaaaa'
a = np.array([s + chr(i) for i in range(101)])
b = a[::-1].copy()
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "string sort, kind=%s" % kind
c = a.copy()
c.sort(kind=kind)
@@ -1600,7 +1809,7 @@ class TestMethods(object):
s = 'aaaaaaaa'
a = np.array([s + chr(i) for i in range(101)], dtype=np.unicode)
b = a[::-1].copy()
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "unicode sort, kind=%s" % kind
c = a.copy()
c.sort(kind=kind)
@@ -1690,7 +1899,7 @@ class TestMethods(object):
return True
a = np.array([Boom()]*100, dtype=object)
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "bogus comparison object sort, kind=%s" % kind
c.sort(kind=kind)
@@ -1710,7 +1919,7 @@ class TestMethods(object):
def test_sort_raises(self):
#gh-9404
arr = np.array([0, datetime.now(), 1], dtype=object)
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
assert_raises(TypeError, arr.sort, kind=kind)
#gh-3879
class Raiser(object):
@@ -1719,7 +1928,7 @@ class TestMethods(object):
__eq__ = __ne__ = __lt__ = __gt__ = __ge__ = __le__ = raises_anything
arr = np.array([[Raiser(), n] for n in range(10)]).reshape(-1)
np.random.shuffle(arr)
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
assert_raises(TypeError, arr.sort, kind=kind)
def test_sort_degraded(self):
@@ -1805,24 +2014,26 @@ class TestMethods(object):
# of sorted items must be greater than ~50 to check the actual
# algorithm because quick and merge sort fall over to insertion
# sort for small arrays.
- a = np.arange(101)
- b = a[::-1].copy()
- for kind in ['q', 'm', 'h']:
- msg = "scalar argsort, kind=%s" % kind
- assert_equal(a.copy().argsort(kind=kind), a, msg)
- assert_equal(b.copy().argsort(kind=kind), b, msg)
+
+ for dtype in [np.int32, np.uint32, np.float32]:
+ a = np.arange(101, dtype=dtype)
+ b = a[::-1].copy()
+ for kind in self.sort_kinds:
+ msg = "scalar argsort, kind=%s, dtype=%s" % (kind, dtype)
+ assert_equal(a.copy().argsort(kind=kind), a, msg)
+ assert_equal(b.copy().argsort(kind=kind), b, msg)
# test complex argsorts. These use the same code as the scalars
# but the compare function differs.
ai = a*1j + 1
bi = b*1j + 1
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "complex argsort, kind=%s" % kind
assert_equal(ai.copy().argsort(kind=kind), a, msg)
assert_equal(bi.copy().argsort(kind=kind), b, msg)
ai = a + 1j
bi = b + 1j
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "complex argsort, kind=%s" % kind
assert_equal(ai.copy().argsort(kind=kind), a, msg)
assert_equal(bi.copy().argsort(kind=kind), b, msg)
@@ -1841,7 +2052,7 @@ class TestMethods(object):
b = a[::-1].copy()
r = np.arange(101)
rr = r[::-1]
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "string argsort, kind=%s" % kind
assert_equal(a.copy().argsort(kind=kind), r, msg)
assert_equal(b.copy().argsort(kind=kind), rr, msg)
@@ -1852,7 +2063,7 @@ class TestMethods(object):
b = a[::-1]
r = np.arange(101)
rr = r[::-1]
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "unicode argsort, kind=%s" % kind
assert_equal(a.copy().argsort(kind=kind), r, msg)
assert_equal(b.copy().argsort(kind=kind), rr, msg)
@@ -1863,7 +2074,7 @@ class TestMethods(object):
b = a[::-1]
r = np.arange(101)
rr = r[::-1]
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "object argsort, kind=%s" % kind
assert_equal(a.copy().argsort(kind=kind), r, msg)
assert_equal(b.copy().argsort(kind=kind), rr, msg)
@@ -1874,7 +2085,7 @@ class TestMethods(object):
b = a[::-1]
r = np.arange(101)
rr = r[::-1]
- for kind in ['q', 'm', 'h']:
+ for kind in self.sort_kinds:
msg = "structured array argsort, kind=%s" % kind
assert_equal(a.copy().argsort(kind=kind), r, msg)
assert_equal(b.copy().argsort(kind=kind), rr, msg)
@@ -2578,7 +2789,14 @@ class TestMethods(object):
assert_equal(x1.flatten('F'), y1f)
assert_equal(x1.flatten('F'), x1.T.flatten())
- def test_dot(self):
+ def test_flatten_invalid_order(self):
+ # invalid after gh-14596
+ for order in ['Z', 'c', False, True, 0, 8]:
+ x = np.array([[1, 2, 3], [4, 5, 6]], np.int32)
+ assert_raises(ValueError, x.flatten, {"order": order})
+
+ @pytest.mark.parametrize('func', (np.dot, np.matmul))
+ def test_arr_mult(self, func):
a = np.array([[1, 0], [0, 1]])
b = np.array([[0, 1], [1, 0]])
c = np.array([[9, 1], [1, -9]])
@@ -2602,49 +2820,49 @@ class TestMethods(object):
# gemm vs syrk optimizations
for et in [np.float32, np.float64, np.complex64, np.complex128]:
eaf = a.astype(et)
- assert_equal(np.dot(eaf, eaf), eaf)
- assert_equal(np.dot(eaf.T, eaf), eaf)
- assert_equal(np.dot(eaf, eaf.T), eaf)
- assert_equal(np.dot(eaf.T, eaf.T), eaf)
- assert_equal(np.dot(eaf.T.copy(), eaf), eaf)
- assert_equal(np.dot(eaf, eaf.T.copy()), eaf)
- assert_equal(np.dot(eaf.T.copy(), eaf.T.copy()), eaf)
+ assert_equal(func(eaf, eaf), eaf)
+ assert_equal(func(eaf.T, eaf), eaf)
+ assert_equal(func(eaf, eaf.T), eaf)
+ assert_equal(func(eaf.T, eaf.T), eaf)
+ assert_equal(func(eaf.T.copy(), eaf), eaf)
+ assert_equal(func(eaf, eaf.T.copy()), eaf)
+ assert_equal(func(eaf.T.copy(), eaf.T.copy()), eaf)
# syrk validations
for et in [np.float32, np.float64, np.complex64, np.complex128]:
eaf = a.astype(et)
ebf = b.astype(et)
- assert_equal(np.dot(ebf, ebf), eaf)
- assert_equal(np.dot(ebf.T, ebf), eaf)
- assert_equal(np.dot(ebf, ebf.T), eaf)
- assert_equal(np.dot(ebf.T, ebf.T), eaf)
+ assert_equal(func(ebf, ebf), eaf)
+ assert_equal(func(ebf.T, ebf), eaf)
+ assert_equal(func(ebf, ebf.T), eaf)
+ assert_equal(func(ebf.T, ebf.T), eaf)
# syrk - different shape, stride, and view validations
for et in [np.float32, np.float64, np.complex64, np.complex128]:
edf = d.astype(et)
assert_equal(
- np.dot(edf[::-1, :], edf.T),
- np.dot(edf[::-1, :].copy(), edf.T.copy())
+ func(edf[::-1, :], edf.T),
+ func(edf[::-1, :].copy(), edf.T.copy())
)
assert_equal(
- np.dot(edf[:, ::-1], edf.T),
- np.dot(edf[:, ::-1].copy(), edf.T.copy())
+ func(edf[:, ::-1], edf.T),
+ func(edf[:, ::-1].copy(), edf.T.copy())
)
assert_equal(
- np.dot(edf, edf[::-1, :].T),
- np.dot(edf, edf[::-1, :].T.copy())
+ func(edf, edf[::-1, :].T),
+ func(edf, edf[::-1, :].T.copy())
)
assert_equal(
- np.dot(edf, edf[:, ::-1].T),
- np.dot(edf, edf[:, ::-1].T.copy())
+ func(edf, edf[:, ::-1].T),
+ func(edf, edf[:, ::-1].T.copy())
)
assert_equal(
- np.dot(edf[:edf.shape[0] // 2, :], edf[::2, :].T),
- np.dot(edf[:edf.shape[0] // 2, :].copy(), edf[::2, :].T.copy())
+ func(edf[:edf.shape[0] // 2, :], edf[::2, :].T),
+ func(edf[:edf.shape[0] // 2, :].copy(), edf[::2, :].T.copy())
)
assert_equal(
- np.dot(edf[::2, :], edf[:edf.shape[0] // 2, :].T),
- np.dot(edf[::2, :].copy(), edf[:edf.shape[0] // 2, :].T.copy())
+ func(edf[::2, :], edf[:edf.shape[0] // 2, :].T),
+ func(edf[::2, :].copy(), edf[:edf.shape[0] // 2, :].T.copy())
)
# syrk - different shape
@@ -2652,9 +2870,43 @@ class TestMethods(object):
edf = d.astype(et)
eddtf = ddt.astype(et)
edtdf = dtd.astype(et)
- assert_equal(np.dot(edf, edf.T), eddtf)
- assert_equal(np.dot(edf.T, edf), edtdf)
+ assert_equal(func(edf, edf.T), eddtf)
+ assert_equal(func(edf.T, edf), edtdf)
+
+ @pytest.mark.parametrize('func', (np.dot, np.matmul))
+ @pytest.mark.parametrize('dtype', 'ifdFD')
+ def test_no_dgemv(self, func, dtype):
+ # check vector arg for contiguous before gemv
+ # gh-12156
+ a = np.arange(8.0, dtype=dtype).reshape(2, 4)
+ b = np.broadcast_to(1., (4, 1))
+ ret1 = func(a, b)
+ ret2 = func(a, b.copy())
+ assert_equal(ret1, ret2)
+
+ ret1 = func(b.T, a.T)
+ ret2 = func(b.T.copy(), a.T)
+ assert_equal(ret1, ret2)
+
+ # check for unaligned data
+ dt = np.dtype(dtype)
+ a = np.zeros(8 * dt.itemsize // 2 + 1, dtype='int16')[1:].view(dtype)
+ a = a.reshape(2, 4)
+ b = a[0]
+ # make sure it is not aligned
+ assert_(a.__array_interface__['data'][0] % dt.itemsize != 0)
+ ret1 = func(a, b)
+ ret2 = func(a.copy(), b.copy())
+ assert_equal(ret1, ret2)
+
+ ret1 = func(b.T, a.T)
+ ret2 = func(b.T.copy(), a.T.copy())
+ assert_equal(ret1, ret2)
+ def test_dot(self):
+ a = np.array([[1, 0], [0, 1]])
+ b = np.array([[0, 1], [1, 0]])
+ c = np.array([[9, 1], [1, -9]])
# function versus methods
assert_equal(np.dot(a, b), a.dot(b))
assert_equal(np.dot(np.dot(a, b), c), a.dot(b).dot(c))
@@ -2710,6 +2962,29 @@ class TestMethods(object):
np.dot(a, b, out=out)
np.matmul(a, b, out=out)
+ def test_dot_matmul_inner_array_casting_fails(self):
+
+ class A(object):
+ def __array__(self, *args, **kwargs):
+ raise NotImplementedError
+
+ # Don't override the error from calling __array__()
+ assert_raises(NotImplementedError, np.dot, A(), A())
+ assert_raises(NotImplementedError, np.matmul, A(), A())
+ assert_raises(NotImplementedError, np.inner, A(), A())
+
+ def test_matmul_out(self):
+ # overlapping memory
+ a = np.arange(18).reshape(2, 3, 3)
+ b = np.matmul(a, a)
+ c = np.matmul(a, a, out=a)
+ assert_(c is a)
+ assert_equal(c, b)
+ a = np.arange(18).reshape(2, 3, 3)
+ c = np.matmul(a, a, out=a[::-1, ...])
+ assert_(c.base is a.base)
+ assert_equal(c, b)
+
def test_diagonal(self):
a = np.arange(12).reshape((3, 4))
assert_equal(a.diagonal(), [0, 5, 10])
@@ -2734,8 +3009,6 @@ class TestMethods(object):
assert_equal(b.diagonal(0, 2, 1), [[0, 3], [4, 7]])
def test_diagonal_view_notwriteable(self):
- # this test is only for 1.9, the diagonal view will be
- # writeable in 1.10.
a = np.eye(3).diagonal()
assert_(not a.flags.writeable)
assert_(not a.flags.owndata)
@@ -3009,8 +3282,8 @@ class TestMethods(object):
assert_equal(ac, np.conjugate(a))
a = np.array([1-1j, 1, 2.0, 'f'], object)
- assert_raises(AttributeError, lambda: a.conj())
- assert_raises(AttributeError, lambda: a.conjugate())
+ assert_raises(TypeError, lambda: a.conj())
+ assert_raises(TypeError, lambda: a.conjugate())
def test__complex__(self):
dtypes = ['i1', 'i2', 'i4', 'i8',
@@ -3119,6 +3392,8 @@ class TestBinop(object):
# 'eq': (np.equal, False),
# 'ne': (np.not_equal, False),
}
+ if sys.version_info >= (3, 5):
+ ops['matmul'] = (np.matmul, False, float)
class Coerced(Exception):
pass
@@ -3161,7 +3436,7 @@ class TestBinop(object):
if issubclass(MyType, np.ndarray):
# Use this range to avoid special case weirdnesses around
# divide-by-0, pow(x, 2), overflow due to pow(big, big), etc.
- return np.arange(3, 5).view(MyType)
+ return np.arange(3, 7).reshape(2, 2).view(MyType)
else:
return MyType()
@@ -3170,7 +3445,7 @@ class TestBinop(object):
for op, (ufunc, has_inplace, dtype) in ops.items():
err_msg = ('op: %s, ufunc: %s, has_inplace: %s, dtype: %s'
% (op, ufunc, has_inplace, dtype))
- check_objs = [np.arange(3, 5, dtype=dtype)]
+ check_objs = [np.arange(3, 7, dtype=dtype).reshape(2, 2)]
if check_scalar:
check_objs.append(check_objs[0][0])
for arr in check_objs:
@@ -3549,8 +3824,79 @@ class TestSubscripting(object):
class TestPickling(object):
+ @pytest.mark.skipif(pickle.HIGHEST_PROTOCOL >= 5,
+ reason=('this tests the error messages when trying to'
+ 'protocol 5 although it is not available'))
+ def test_correct_protocol5_error_message(self):
+ array = np.arange(10)
+
+ if sys.version_info[:2] in ((3, 6), (3, 7)):
+ # For the specific case of python3.6 and 3.7, raise a clear import
+ # error about the pickle5 backport when trying to use protocol=5
+ # without the pickle5 package
+ with pytest.raises(ImportError):
+ array.__reduce_ex__(5)
+
+ elif sys.version_info[:2] < (3, 6):
+ # when calling __reduce_ex__ explicitly with protocol=5 on python
+ # raise a ValueError saying that protocol 5 is not available for
+ # this python version
+ with pytest.raises(ValueError):
+ array.__reduce_ex__(5)
+
+ def test_record_array_with_object_dtype(self):
+ my_object = object()
+
+ arr_with_object = np.array(
+ [(my_object, 1, 2.0)],
+ dtype=[('a', object), ('b', int), ('c', float)])
+ arr_without_object = np.array(
+ [('xxx', 1, 2.0)],
+ dtype=[('a', str), ('b', int), ('c', float)])
+
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ depickled_arr_with_object = pickle.loads(
+ pickle.dumps(arr_with_object, protocol=proto))
+ depickled_arr_without_object = pickle.loads(
+ pickle.dumps(arr_without_object, protocol=proto))
+
+ assert_equal(arr_with_object.dtype,
+ depickled_arr_with_object.dtype)
+ assert_equal(arr_without_object.dtype,
+ depickled_arr_without_object.dtype)
+
+ @pytest.mark.skipif(pickle.HIGHEST_PROTOCOL < 5,
+ reason="requires pickle protocol 5")
+ def test_f_contiguous_array(self):
+ f_contiguous_array = np.array([[1, 2, 3], [4, 5, 6]], order='F')
+ buffers = []
+
+ # When using pickle protocol 5, Fortran-contiguous arrays can be
+ # serialized using out-of-band buffers
+ bytes_string = pickle.dumps(f_contiguous_array, protocol=5,
+ buffer_callback=buffers.append)
+
+ assert len(buffers) > 0
+
+ depickled_f_contiguous_array = pickle.loads(bytes_string,
+ buffers=buffers)
+
+ assert_equal(f_contiguous_array, depickled_f_contiguous_array)
+
+ def test_non_contiguous_array(self):
+ non_contiguous_array = np.arange(12).reshape(3, 4)[:, :2]
+ assert not non_contiguous_array.flags.c_contiguous
+ assert not non_contiguous_array.flags.f_contiguous
+
+ # make sure non-contiguous arrays can be pickled-depickled
+ # using any protocol
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ depickled_non_contiguous_array = pickle.loads(
+ pickle.dumps(non_contiguous_array, protocol=proto))
+
+ assert_equal(non_contiguous_array, depickled_non_contiguous_array)
+
def test_roundtrip(self):
- import pickle
for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
carray = np.array([[2, 9], [7, 0], [3, 8]])
DATA = [
@@ -3560,13 +3906,18 @@ class TestPickling(object):
('c', float)])
]
+ refs = [weakref.ref(a) for a in DATA]
for a in DATA:
assert_equal(
a, pickle.loads(pickle.dumps(a, protocol=proto)),
err_msg="%r" % a)
+ del a, DATA, carray
+ break_cycles()
+ # check for reference leaks (gh-12793)
+ for ref in refs:
+ assert ref() is None
def _loads(self, obj):
- import pickle
if sys.version_info[0] >= 3:
return pickle.loads(obj, encoding='latin1')
else:
@@ -3617,6 +3968,17 @@ class TestPickling(object):
p = self._loads(s)
assert_equal(a, p)
+ def test_datetime64_byteorder(self):
+ original = np.array([['2015-02-24T00:00:00.000000000']], dtype='datetime64[ns]')
+
+ original_byte_reversed = original.copy(order='K')
+ original_byte_reversed.dtype = original_byte_reversed.dtype.newbyteorder('S')
+ original_byte_reversed.byteswap(inplace=True)
+
+ new = pickle.loads(pickle.dumps(original_byte_reversed))
+
+ assert_equal(original.dtype, new.dtype)
+
class TestFancyIndexing(object):
def test_list(self):
@@ -3827,6 +4189,7 @@ class TestArgmax(object):
assert_equal(a.argmax(out=out1, axis=0), np.argmax(a, out=out2, axis=0))
assert_equal(out1, out2)
+ @pytest.mark.leaks_references(reason="replaces None with NULL.")
def test_object_argmax_with_NULLs(self):
# See gh-6032
a = np.empty(4, dtype='O')
@@ -3975,6 +4338,7 @@ class TestArgmin(object):
assert_equal(a.argmin(out=out1, axis=0), np.argmin(a, out=out2, axis=0))
assert_equal(out1, out2)
+ @pytest.mark.leaks_references(reason="replaces None with NULL.")
def test_object_argmin_with_NULLs(self):
# See gh-6032
a = np.empty(4, dtype='O')
@@ -4047,7 +4411,11 @@ class TestClip(object):
x = (np.random.random(1000) * array_max).astype(dtype)
if inplace:
- x.clip(clip_min, clip_max, x)
+ # The tests that call us pass clip_min and clip_max that
+ # might not fit in the destination dtype. They were written
+ # assuming the previous unsafe casting, which now must be
+ # passed explicitly to avoid a warning.
+ x.clip(clip_min, clip_max, x, casting='unsafe')
else:
x = x.clip(clip_min, clip_max)
byteorder = '='
@@ -4066,7 +4434,7 @@ class TestClip(object):
'float', 1024, 0, 0, inplace=inplace)
self._clip_type(
- 'int', 1024, -120, 100.5, inplace=inplace)
+ 'int', 1024, -120, 100, inplace=inplace)
self._clip_type(
'int', 1024, 0, 0, inplace=inplace)
@@ -4160,6 +4528,16 @@ class TestPutmask(object):
assert_array_equal(rec['y'], [11, 4])
assert_array_equal(rec['z'], [3, 3])
+ def test_overlaps(self):
+ # gh-6272 check overlap
+ x = np.array([True, False, True, False])
+ np.putmask(x[1:4], [True, True, True], x[:3])
+ assert_equal(x, np.array([True, True, False, True]))
+
+ x = np.array([True, False, True, False])
+ np.putmask(x[1:4], x[:3], [True, False, True])
+ assert_equal(x, np.array([True, True, True, True]))
+
class TestTake(object):
def tst_basic(self, x):
@@ -4208,20 +4586,33 @@ class TestTake(object):
rec1 = rec.take([1])
assert_(rec1['x'] == 5.0 and rec1['y'] == 4.0)
+ def test_out_overlap(self):
+ # gh-6272 check overlap on out
+ x = np.arange(5)
+ y = np.take(x, [1, 2, 3], out=x[2:5], mode='wrap')
+ assert_equal(y, np.array([1, 2, 3]))
class TestLexsort(object):
- def test_basic(self):
- a = [1, 2, 1, 3, 1, 5]
- b = [0, 4, 5, 6, 2, 3]
+ @pytest.mark.parametrize('dtype',[
+ np.uint8, np.uint16, np.uint32, np.uint64,
+ np.int8, np.int16, np.int32, np.int64,
+ np.float16, np.float32, np.float64
+ ])
+ def test_basic(self, dtype):
+ a = np.array([1, 2, 1, 3, 1, 5], dtype=dtype)
+ b = np.array([0, 4, 5, 6, 2, 3], dtype=dtype)
idx = np.lexsort((b, a))
expected_idx = np.array([0, 4, 2, 1, 3, 5])
assert_array_equal(idx, expected_idx)
+ assert_array_equal(a[idx], np.sort(a))
- x = np.vstack((b, a))
- idx = np.lexsort(x)
- assert_array_equal(idx, expected_idx)
+ def test_mixed(self):
+ a = np.array([1, 2, 1, 3, 1, 5])
+ b = np.array([0, 4, 5, 6, 2, 3], dtype='datetime64[D]')
- assert_array_equal(x[1][idx], np.sort(x[1]))
+ idx = np.lexsort((b, a))
+ expected_idx = np.array([0, 4, 2, 1, 3, 5])
+ assert_array_equal(idx, expected_idx)
def test_datetime(self):
a = np.array([0,0,0], dtype='datetime64[D]')
@@ -4323,6 +4714,20 @@ class TestIO(object):
y = np.fromfile(self.filename, dtype=self.dtype)
assert_array_equal(y, self.x.flat)
+ @pytest.mark.skipif(pathlib is None, reason="pathlib not found")
+ def test_roundtrip_pathlib(self):
+ p = pathlib.Path(self.filename)
+ self.x.tofile(p)
+ y = np.fromfile(p, dtype=self.dtype)
+ assert_array_equal(y, self.x.flat)
+
+ @pytest.mark.skipif(pathlib is None, reason="pathlib not found")
+ def test_roundtrip_dump_pathlib(self):
+ p = pathlib.Path(self.filename)
+ self.x.dump(p)
+ y = np.load(p, allow_pickle=True)
+ assert_array_equal(y, self.x)
+
def test_roundtrip_binary_str(self):
s = self.x.tobytes()
y = np.frombuffer(s, dtype=self.dtype)
@@ -4442,6 +4847,49 @@ class TestIO(object):
f.close()
assert_equal(pos, 10, err_msg=err_msg)
+ def test_load_object_array_fromfile(self):
+ # gh-12300
+ with open(self.filename, 'w') as f:
+ # Ensure we have a file with consistent contents
+ pass
+
+ with open(self.filename, 'rb') as f:
+ assert_raises_regex(ValueError, "Cannot read into object array",
+ np.fromfile, f, dtype=object)
+
+ assert_raises_regex(ValueError, "Cannot read into object array",
+ np.fromfile, self.filename, dtype=object)
+
+ def test_fromfile_offset(self):
+ with open(self.filename, 'wb') as f:
+ self.x.tofile(f)
+
+ with open(self.filename, 'rb') as f:
+ y = np.fromfile(f, dtype=self.dtype, offset=0)
+ assert_array_equal(y, self.x.flat)
+
+ with open(self.filename, 'rb') as f:
+ count_items = len(self.x.flat) // 8
+ offset_items = len(self.x.flat) // 4
+ offset_bytes = self.dtype.itemsize * offset_items
+ y = np.fromfile(f, dtype=self.dtype, count=count_items, offset=offset_bytes)
+ assert_array_equal(y, self.x.flat[offset_items:offset_items+count_items])
+
+ # subsequent seeks should stack
+ offset_bytes = self.dtype.itemsize
+ z = np.fromfile(f, dtype=self.dtype, offset=offset_bytes)
+ assert_array_equal(z, self.x.flat[offset_items+count_items+1:])
+
+ with open(self.filename, 'wb') as f:
+ self.x.tofile(f, sep=",")
+
+ with open(self.filename, 'rb') as f:
+ assert_raises_regex(
+ TypeError,
+ "'offset' argument only permitted for binary files",
+ np.fromfile, self.filename, dtype=self.dtype,
+ sep=",", offset=1)
+
def _check_from(self, s, value, **kw):
if 'sep' not in kw:
y = np.frombuffer(s, **kw)
@@ -4521,7 +4969,8 @@ class TestIO(object):
self._check_from(b'1,2,3,4', [1., 2., 3., 4.], dtype=float, sep=',')
def test_malformed(self):
- self._check_from(b'1.234 1,234', [1.234, 1.], sep=' ')
+ with assert_warns(DeprecationWarning):
+ self._check_from(b'1.234 1,234', [1.234, 1.], sep=' ')
def test_long_sep(self):
self._check_from(b'1_x_3_x_4_x_5', [1, 3, 4, 5], sep='_x_')
@@ -4574,6 +5023,19 @@ class TestIO(object):
self.test_tofile_sep()
self.test_tofile_format()
+ def test_fromfile_subarray_binary(self):
+ # Test subarray dtypes which are absorbed into the shape
+ x = np.arange(24, dtype="i4").reshape(2, 3, 4)
+ x.tofile(self.filename)
+ res = np.fromfile(self.filename, dtype="(3,4)i4")
+ assert_array_equal(x, res)
+
+ x_str = x.tobytes()
+ with assert_warns(DeprecationWarning):
+ # binary fromstring is deprecated
+ res = np.fromstring(x_str, dtype="(3,4)i4")
+ assert_array_equal(x, res)
+
class TestFromBuffer(object):
@pytest.mark.parametrize('byteorder', ['<', '>'])
@@ -4643,6 +5105,22 @@ class TestFlat(object):
assert_(e.flags.writebackifcopy is False)
assert_(f.flags.writebackifcopy is False)
+ @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts")
+ def test_refcount(self):
+ # includes regression test for reference count error gh-13165
+ inds = [np.intp(0), np.array([True]*self.a.size), np.array([0]), None]
+ indtype = np.dtype(np.intp)
+ rc_indtype = sys.getrefcount(indtype)
+ for ind in inds:
+ rc_ind = sys.getrefcount(ind)
+ for _ in range(100):
+ try:
+ self.a.flat[ind]
+ except IndexError:
+ pass
+ assert_(abs(sys.getrefcount(ind) - rc_ind) < 50)
+ assert_(abs(sys.getrefcount(indtype) - rc_indtype) < 50)
+
class TestResize(object):
def test_basic(self):
@@ -4730,6 +5208,12 @@ class TestResize(object):
x_view.resize((0, 10))
x_view.resize((0, 100))
+ def test_check_weakref(self):
+ x = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
+ xref = weakref.ref(x)
+ assert_raises(ValueError, x.resize, (5, 1))
+ del xref # avoid pyflakes unused variable warning.
+
class TestRecord(object):
def test_field_rename(self):
@@ -4738,11 +5222,11 @@ class TestRecord(object):
assert_equal(dt.names, ['p', 'q'])
def test_multiple_field_name_occurrence(self):
- def test_assign():
- dtype = np.dtype([("A", "f8"), ("B", "f8"), ("A", "f8")])
+ def test_dtype_init():
+ np.dtype([("A", "f8"), ("B", "f8"), ("A", "f8")])
# Error raised when multiple fields have the same name
- assert_raises(ValueError, test_assign)
+ assert_raises(ValueError, test_dtype_init)
@pytest.mark.skipif(sys.version_info[0] < 3, reason="Not Python 3")
def test_bytes_fields(self):
@@ -4762,13 +5246,11 @@ class TestRecord(object):
@pytest.mark.skipif(sys.version_info[0] < 3, reason="Not Python 3")
def test_multiple_field_name_unicode(self):
- def test_assign_unicode():
- dt = np.dtype([("\u20B9", "f8"),
- ("B", "f8"),
- ("\u20B9", "f8")])
+ def test_dtype_unicode():
+ np.dtype([("\u20B9", "f8"), ("B", "f8"), ("\u20B9", "f8")])
# Error raised when multiple fields have the same name(unicode included)
- assert_raises(ValueError, test_assign_unicode)
+ assert_raises(ValueError, test_dtype_unicode)
@pytest.mark.skipif(sys.version_info[0] >= 3, reason="Not Python 2")
def test_unicode_field_titles(self):
@@ -4850,25 +5332,9 @@ class TestRecord(object):
fn2 = func('f2')
b[fn2] = 3
- # In 1.16 code below can be replaced by:
- # assert_equal(b[['f1', 'f2']][0].tolist(), (2, 3))
- # assert_equal(b[['f2', 'f1']][0].tolist(), (3, 2))
- # assert_equal(b[['f1', 'f3']][0].tolist(), (2, (1,)))
- with suppress_warnings() as sup:
- sup.filter(FutureWarning,
- ".* selecting multiple fields .*")
-
- assert_equal(b[['f1', 'f2']][0].tolist(), (2, 3))
- assert_equal(b[['f2', 'f1']][0].tolist(), (3, 2))
- assert_equal(b[['f1', 'f3']][0].tolist(), (2, (1,)))
- # view of subfield view/copy
- assert_equal(b[['f1', 'f2']][0].view(('i4', 2)).tolist(),
- (2, 3))
- assert_equal(b[['f2', 'f1']][0].view(('i4', 2)).tolist(),
- (3, 2))
- view_dtype = [('f1', 'i4'), ('f3', [('', 'i4')])]
- assert_equal(b[['f1', 'f3']][0].view(view_dtype).tolist(),
- (2, (1,)))
+ assert_equal(b[['f1', 'f2']][0].tolist(), (2, 3))
+ assert_equal(b[['f2', 'f1']][0].tolist(), (3, 2))
+ assert_equal(b[['f1', 'f3']][0].tolist(), (2, (1,)))
# non-ascii unicode field indexing is well behaved
if not is_py3:
@@ -4878,50 +5344,6 @@ class TestRecord(object):
assert_raises(ValueError, a.__setitem__, u'\u03e0', 1)
assert_raises(ValueError, a.__getitem__, u'\u03e0')
- # can be removed in 1.16
- def test_field_names_deprecation(self):
-
- def collect_warnings(f, *args, **kwargs):
- with warnings.catch_warnings(record=True) as log:
- warnings.simplefilter("always")
- f(*args, **kwargs)
- return [w.category for w in log]
-
- a = np.zeros((1,), dtype=[('f1', 'i4'),
- ('f2', 'i4'),
- ('f3', [('sf1', 'i4')])])
- a['f1'][0] = 1
- a['f2'][0] = 2
- a['f3'][0] = (3,)
- b = np.zeros((1,), dtype=[('f1', 'i4'),
- ('f2', 'i4'),
- ('f3', [('sf1', 'i4')])])
- b['f1'][0] = 1
- b['f2'][0] = 2
- b['f3'][0] = (3,)
-
- # All the different functions raise a warning, but not an error
- assert_equal(collect_warnings(a[['f1', 'f2']].__setitem__, 0, (10, 20)),
- [FutureWarning])
- # For <=1.12 a is not modified, but it will be in 1.13
- assert_equal(a, b)
-
- # Views also warn
- subset = a[['f1', 'f2']]
- subset_view = subset.view()
- assert_equal(collect_warnings(subset_view['f1'].__setitem__, 0, 10),
- [FutureWarning])
- # But the write goes through:
- assert_equal(subset['f1'][0], 10)
- # Only one warning per multiple field indexing, though (even if there
- # are multiple views involved):
- assert_equal(collect_warnings(subset['f1'].__setitem__, 0, 10), [])
-
- # make sure views of a multi-field index warn too
- c = np.zeros(3, dtype='i8,i8,i8')
- assert_equal(collect_warnings(c[['f0', 'f2']].view, 'i8,i8'),
- [FutureWarning])
-
def test_record_hash(self):
a = np.array([(1, 2), (1, 2)], dtype='i1,i2')
a.flags.writeable = False
@@ -4945,6 +5367,16 @@ class TestRecord(object):
np.array([(), (), (), (), ()], dtype={'names': [], 'formats': [],
'offsets': [], 'itemsize': 12})
+ def test_multifield_indexing_view(self):
+ a = np.ones(3, dtype=[('a', 'i4'), ('b', 'f4'), ('c', 'u4')])
+ v = a[['a', 'c']]
+ assert_(v.base is a)
+ assert_(v.dtype == np.dtype({'names': ['a', 'c'],
+ 'formats': ['i4', 'u4'],
+ 'offsets': [0, 8]}))
+ v[:] = (4,5)
+ assert_equal(a[0].item(), (4, 1, 5))
+
class TestView(object):
def test_basic(self):
x = np.array([(1, 2, 3, 4), (5, 6, 7, 8)],
@@ -5514,7 +5946,7 @@ class MatmulCommon(object):
"""
# Should work with these types. Will want to add
# "O" at some point
- types = "?bhilqBHILQefdgFDG"
+ types = "?bhilqBHILQefdgFDGO"
def test_exceptions(self):
dims = [
@@ -5565,16 +5997,40 @@ class MatmulCommon(object):
assert_(res.dtype == dt)
# vector vector returns scalars
- res = self.matmul(v, v)
- assert_(type(res) is np.dtype(dt).type)
+ if dt != "O":
+ res = self.matmul(v, v)
+ assert_(type(res) is np.dtype(dt).type)
+
+ def test_scalar_output(self):
+ vec1 = np.array([2])
+ vec2 = np.array([3, 4]).reshape(1, -1)
+ tgt = np.array([6, 8])
+ for dt in self.types[1:]:
+ v1 = vec1.astype(dt)
+ v2 = vec2.astype(dt)
+ res = self.matmul(v1, v2)
+ assert_equal(res, tgt)
+ res = self.matmul(v2.T, v1)
+ assert_equal(res, tgt)
+
+ # boolean type
+ vec = np.array([True, True], dtype='?').reshape(1, -1)
+ res = self.matmul(vec[:, 0], vec)
+ assert_equal(res, True)
def test_vector_vector_values(self):
- vec = np.array([1, 2])
- tgt = 5
+ vec1 = np.array([1, 2])
+ vec2 = np.array([3, 4]).reshape(-1, 1)
+ tgt1 = np.array([11])
+ tgt2 = np.array([[3, 6], [4, 8]])
for dt in self.types[1:]:
- v1 = vec.astype(dt)
- res = self.matmul(v1, v1)
- assert_equal(res, tgt)
+ v1 = vec1.astype(dt)
+ v2 = vec2.astype(dt)
+ res = self.matmul(v1, v2)
+ assert_equal(res, tgt1)
+ # no broadcast, we must make v1 into a 2d ndarray
+ res = self.matmul(v2, v1.reshape(1, -1))
+ assert_equal(res, tgt2)
# boolean type
vec = np.array([True, True], dtype='?')
@@ -5705,44 +6161,158 @@ class TestMatmul(MatmulCommon):
matmul = np.matmul
def test_out_arg(self):
- a = np.ones((2, 2), dtype=float)
- b = np.ones((2, 2), dtype=float)
- tgt = np.full((2,2), 2, dtype=float)
+ a = np.ones((5, 2), dtype=float)
+ b = np.array([[1, 3], [5, 7]], dtype=float)
+ tgt = np.dot(a, b)
# test as positional argument
msg = "out positional argument"
- out = np.zeros((2, 2), dtype=float)
+ out = np.zeros((5, 2), dtype=float)
self.matmul(a, b, out)
assert_array_equal(out, tgt, err_msg=msg)
# test as keyword argument
msg = "out keyword argument"
- out = np.zeros((2, 2), dtype=float)
+ out = np.zeros((5, 2), dtype=float)
self.matmul(a, b, out=out)
assert_array_equal(out, tgt, err_msg=msg)
# test out with not allowed type cast (safe casting)
- # einsum and cblas raise different error types, so
- # use Exception.
- msg = "out argument with illegal cast"
- out = np.zeros((2, 2), dtype=np.int32)
- assert_raises(Exception, self.matmul, a, b, out=out)
-
- # skip following tests for now, cblas does not allow non-contiguous
- # outputs and consistency with dot would require same type,
- # dimensions, subtype, and c_contiguous.
-
- # test out with allowed type cast
- # msg = "out argument with allowed cast"
- # out = np.zeros((2, 2), dtype=np.complex128)
- # self.matmul(a, b, out=out)
- # assert_array_equal(out, tgt, err_msg=msg)
+ msg = "Cannot cast ufunc .* output"
+ out = np.zeros((5, 2), dtype=np.int32)
+ assert_raises_regex(TypeError, msg, self.matmul, a, b, out=out)
+
+ # test out with type upcast to complex
+ out = np.zeros((5, 2), dtype=np.complex128)
+ c = self.matmul(a, b, out=out)
+ assert_(c is out)
+ with suppress_warnings() as sup:
+ sup.filter(np.ComplexWarning, '')
+ c = c.astype(tgt.dtype)
+ assert_array_equal(c, tgt)
+
+ def test_out_contiguous(self):
+ a = np.ones((5, 2), dtype=float)
+ b = np.array([[1, 3], [5, 7]], dtype=float)
+ v = np.array([1, 3], dtype=float)
+ tgt = np.dot(a, b)
+ tgt_mv = np.dot(a, v)
# test out non-contiguous
- # msg = "out argument with non-contiguous layout"
- # c = np.zeros((2, 2, 2), dtype=float)
- # self.matmul(a, b, out=c[..., 0])
- # assert_array_equal(c, tgt, err_msg=msg)
+ out = np.ones((5, 2, 2), dtype=float)
+ c = self.matmul(a, b, out=out[..., 0])
+ assert c.base is out
+ assert_array_equal(c, tgt)
+ c = self.matmul(a, v, out=out[:, 0, 0])
+ assert_array_equal(c, tgt_mv)
+ c = self.matmul(v, a.T, out=out[:, 0, 0])
+ assert_array_equal(c, tgt_mv)
+
+ # test out contiguous in only last dim
+ out = np.ones((10, 2), dtype=float)
+ c = self.matmul(a, b, out=out[::2, :])
+ assert_array_equal(c, tgt)
+
+ # test transposes of out, args
+ out = np.ones((5, 2), dtype=float)
+ c = self.matmul(b.T, a.T, out=out.T)
+ assert_array_equal(out, tgt)
+
+ m1 = np.arange(15.).reshape(5, 3)
+ m2 = np.arange(21.).reshape(3, 7)
+ m3 = np.arange(30.).reshape(5, 6)[:, ::2] # non-contiguous
+ vc = np.arange(10.)
+ vr = np.arange(6.)
+ m0 = np.zeros((3, 0))
+ @pytest.mark.parametrize('args', (
+ # matrix-matrix
+ (m1, m2), (m2.T, m1.T), (m2.T.copy(), m1.T), (m2.T, m1.T.copy()),
+ # matrix-matrix-transpose, contiguous and non
+ (m1, m1.T), (m1.T, m1), (m1, m3.T), (m3, m1.T),
+ (m3, m3.T), (m3.T, m3),
+ # matrix-matrix non-contiguous
+ (m3, m2), (m2.T, m3.T), (m2.T.copy(), m3.T),
+ # vector-matrix, matrix-vector, contiguous
+ (m1, vr[:3]), (vc[:5], m1), (m1.T, vc[:5]), (vr[:3], m1.T),
+ # vector-matrix, matrix-vector, vector non-contiguous
+ (m1, vr[::2]), (vc[::2], m1), (m1.T, vc[::2]), (vr[::2], m1.T),
+ # vector-matrix, matrix-vector, matrix non-contiguous
+ (m3, vr[:3]), (vc[:5], m3), (m3.T, vc[:5]), (vr[:3], m3.T),
+ # vector-matrix, matrix-vector, both non-contiguous
+ (m3, vr[::2]), (vc[::2], m3), (m3.T, vc[::2]), (vr[::2], m3.T),
+ # size == 0
+ (m0, m0.T), (m0.T, m0), (m1, m0), (m0.T, m1.T),
+ ))
+ def test_dot_equivalent(self, args):
+ r1 = np.matmul(*args)
+ r2 = np.dot(*args)
+ assert_equal(r1, r2)
+
+ r3 = np.matmul(args[0].copy(), args[1].copy())
+ assert_equal(r1, r3)
+
+ def test_matmul_object(self):
+ import fractions
+
+ f = np.vectorize(fractions.Fraction)
+ def random_ints():
+ return np.random.randint(1, 1000, size=(10, 3, 3))
+ M1 = f(random_ints(), random_ints())
+ M2 = f(random_ints(), random_ints())
+
+ M3 = self.matmul(M1, M2)
+
+ [N1, N2, N3] = [a.astype(float) for a in [M1, M2, M3]]
+
+ assert_allclose(N3, self.matmul(N1, N2))
+
+ def test_matmul_object_type_scalar(self):
+ from fractions import Fraction as F
+ v = np.array([F(2,3), F(5,7)])
+ res = self.matmul(v, v)
+ assert_(type(res) is F)
+
+ def test_matmul_empty(self):
+ a = np.empty((3, 0), dtype=object)
+ b = np.empty((0, 3), dtype=object)
+ c = np.zeros((3, 3))
+ assert_array_equal(np.matmul(a, b), c)
+
+ def test_matmul_exception_multiply(self):
+ # test that matmul fails if `__mul__` is missing
+ class add_not_multiply():
+ def __add__(self, other):
+ return self
+ a = np.full((3,3), add_not_multiply())
+ with assert_raises(TypeError):
+ b = np.matmul(a, a)
+
+ def test_matmul_exception_add(self):
+ # test that matmul fails if `__add__` is missing
+ class multiply_not_add():
+ def __mul__(self, other):
+ return self
+ a = np.full((3,3), multiply_not_add())
+ with assert_raises(TypeError):
+ b = np.matmul(a, a)
+
+ def test_matmul_bool(self):
+ # gh-14439
+ a = np.array([[1, 0],[1, 1]], dtype=bool)
+ assert np.max(a.view(np.uint8)) == 1
+ b = np.matmul(a, a)
+ # matmul with boolean output should always be 0, 1
+ assert np.max(b.view(np.uint8)) == 1
+
+ rg = np.random.default_rng(np.random.PCG64(43))
+ d = rg.integers(2, size=4*5, dtype=np.int8)
+ d = d.reshape(4, 5) > 0
+ out1 = np.matmul(d, d.reshape(5, 4))
+ out2 = np.dot(d, d.reshape(5, 4))
+ assert_equal(out1, out2)
+
+ c = np.matmul(np.zeros((2, 0), dtype=bool), np.zeros(0, dtype=bool))
+ assert not np.any(c)
if sys.version_info[:2] >= (3, 5):
@@ -5766,6 +6336,11 @@ if sys.version_info[:2] >= (3, 5):
assert_equal(self.matmul(a, b), "A")
assert_equal(self.matmul(b, a), "A")
+ def test_matmul_raises(self):
+ assert_raises(TypeError, self.matmul, np.int8(5), np.int8(5))
+ assert_raises(TypeError, self.matmul, np.void(b'abc'), np.void(b'abc'))
+ assert_raises(ValueError, self.matmul, np.arange(10), np.void(b'abc'))
+
def test_matmul_inplace():
# It would be nice to support in-place matmul eventually, but for now
# we don't have a working implementation, so better just to error out
@@ -5780,6 +6355,17 @@ if sys.version_info[:2] >= (3, 5):
exec_ = getattr(builtins, "exec")
assert_raises(TypeError, exec_, "a @= b", globals(), locals())
+ def test_matmul_axes():
+ a = np.arange(3*4*5).reshape(3, 4, 5)
+ c = np.matmul(a, a, axes=[(-2, -1), (-1, -2), (1, 2)])
+ assert c.shape == (3, 4, 4)
+ d = np.matmul(a, a, axes=[(-2, -1), (-1, -2), (0, 1)])
+ assert d.shape == (4, 4, 3)
+ e = np.swapaxes(d, 0, 2)
+ assert_array_equal(e, c)
+ f = np.matmul(a, np.arange(3), axes=[(1, 0), (0), (0)])
+ assert f.shape == (4, 5)
+
class TestInner(object):
@@ -5861,20 +6447,22 @@ class TestInner(object):
class TestAlen(object):
def test_basic(self):
- m = np.array([1, 2, 3])
- assert_equal(np.alen(m), 3)
+ with pytest.warns(DeprecationWarning):
+ m = np.array([1, 2, 3])
+ assert_equal(np.alen(m), 3)
- m = np.array([[1, 2, 3], [4, 5, 7]])
- assert_equal(np.alen(m), 2)
+ m = np.array([[1, 2, 3], [4, 5, 7]])
+ assert_equal(np.alen(m), 2)
- m = [1, 2, 3]
- assert_equal(np.alen(m), 3)
+ m = [1, 2, 3]
+ assert_equal(np.alen(m), 3)
- m = [[1, 2, 3], [4, 5, 7]]
- assert_equal(np.alen(m), 2)
+ m = [[1, 2, 3], [4, 5, 7]]
+ assert_equal(np.alen(m), 2)
def test_singleton(self):
- assert_equal(np.alen(5), 1)
+ with pytest.warns(DeprecationWarning):
+ assert_equal(np.alen(5), 1)
class TestChoose(object):
@@ -6661,6 +7249,13 @@ class TestNewBufferProtocol(object):
RuntimeError, "ndim",
np.array, m)
+ # The above seems to create some deep cycles, clean them up for
+ # easier reference count debugging:
+ del c_u8_33d, m
+ for i in range(33):
+ if gc.collect() == 0:
+ break
+
def test_error_pointer_type(self):
# gh-6741
m = memoryview(ctypes.pointer(ctypes.c_uint8()))
@@ -6670,7 +7265,7 @@ class TestNewBufferProtocol(object):
ValueError, "format string",
np.array, m)
- def test_error_message(self):
+ def test_error_message_unsupported(self):
# wchar has no corresponding numpy type - if this changes in future, we
# need a better way to construct an invalid memoryview format.
t = ctypes.c_wchar * 4
@@ -6679,14 +7274,17 @@ class TestNewBufferProtocol(object):
exc = cm.exception
if sys.version_info.major > 2:
- with assert_raises_regex(ValueError, "Unknown .* specifier 'u'"):
+ with assert_raises_regex(
+ NotImplementedError,
+ r"Unrepresentable .* 'u' \(UCS-2 strings\)"
+ ):
raise exc.__cause__
def test_ctypes_integer_via_memoryview(self):
# gh-11150, due to bpo-10746
for c_integer in {ctypes.c_int, ctypes.c_long, ctypes.c_longlong}:
value = c_integer(42)
- with warnings.catch_warnings(record=True) as w:
+ with warnings.catch_warnings(record=True):
warnings.filterwarnings('always', r'.*\bctypes\b', RuntimeWarning)
np.asarray(value)
@@ -6696,7 +7294,7 @@ class TestNewBufferProtocol(object):
_fields_ = [('a', ctypes.c_uint8), ('b', ctypes.c_uint32)]
f = foo(a=1, b=2)
- with warnings.catch_warnings(record=True) as w:
+ with warnings.catch_warnings(record=True):
warnings.filterwarnings('always', r'.*\bctypes\b', RuntimeWarning)
arr = np.asarray(f)
@@ -6740,12 +7338,11 @@ class TestArrayAttributeDeletion(object):
assert_raises(AttributeError, delattr, a, s)
-def test_array_interface():
- # Test scalar coercion within the array interface
+class TestArrayInterface():
class Foo(object):
def __init__(self, value):
self.value = value
- self.iface = {'typestr': '=f8'}
+ self.iface = {'typestr': 'f8'}
def __float__(self):
return float(self.value)
@@ -6754,22 +7351,39 @@ def test_array_interface():
def __array_interface__(self):
return self.iface
+
f = Foo(0.5)
- assert_equal(np.array(f), 0.5)
- assert_equal(np.array([f]), [0.5])
- assert_equal(np.array([f, f]), [0.5, 0.5])
- assert_equal(np.array(f).dtype, np.dtype('=f8'))
- # Test various shape definitions
- f.iface['shape'] = ()
- assert_equal(np.array(f), 0.5)
- f.iface['shape'] = None
- assert_raises(TypeError, np.array, f)
- f.iface['shape'] = (1, 1)
- assert_equal(np.array(f), [[0.5]])
- f.iface['shape'] = (2,)
- assert_raises(ValueError, np.array, f)
-
- # test scalar with no shape
+
+ @pytest.mark.parametrize('val, iface, expected', [
+ (f, {}, 0.5),
+ ([f], {}, [0.5]),
+ ([f, f], {}, [0.5, 0.5]),
+ (f, {'shape': ()}, 0.5),
+ (f, {'shape': None}, TypeError),
+ (f, {'shape': (1, 1)}, [[0.5]]),
+ (f, {'shape': (2,)}, ValueError),
+ (f, {'strides': ()}, 0.5),
+ (f, {'strides': (2,)}, ValueError),
+ (f, {'strides': 16}, TypeError),
+ ])
+ def test_scalar_interface(self, val, iface, expected):
+ # Test scalar coercion within the array interface
+ self.f.iface = {'typestr': 'f8'}
+ self.f.iface.update(iface)
+ if HAS_REFCOUNT:
+ pre_cnt = sys.getrefcount(np.dtype('f8'))
+ if isinstance(expected, type):
+ assert_raises(expected, np.array, val)
+ else:
+ result = np.array(val)
+ assert_equal(np.array(val), expected)
+ assert result.dtype == 'f8'
+ del result
+ if HAS_REFCOUNT:
+ post_cnt = sys.getrefcount(np.dtype('f8'))
+ assert_equal(pre_cnt, post_cnt)
+
+def test_interface_no_shape():
class ArrayLike(object):
array = np.array(1)
__array_interface__ = array.__array_interface__
@@ -6811,6 +7425,19 @@ def test_array_interface_empty_shape():
assert_equal(arr1, arr2)
assert_equal(arr1, arr3)
+def test_array_interface_offset():
+ arr = np.array([1, 2, 3], dtype='int32')
+ interface = dict(arr.__array_interface__)
+ interface['data'] = memoryview(arr)
+ interface['shape'] = (2,)
+ interface['offset'] = 4
+
+
+ class DummyArray(object):
+ __array_interface__ = interface
+
+ arr1 = np.asarray(DummyArray())
+ assert_equal(arr1, arr[1:])
def test_flat_element_deletion():
it = np.ones(3).flat
@@ -6837,7 +7464,7 @@ class TestMemEventHook(object):
# needs to be larger then limit of small memory cacher in ctors.c
a = np.zeros(1000)
del a
- gc.collect()
+ break_cycles()
_multiarray_tests.test_pydatamem_seteventhook_end()
class TestMapIter(object):
@@ -6942,6 +7569,7 @@ class TestConversion(object):
except NameError:
Error = RuntimeError # python < 3.5
assert_raises(Error, bool, self_containing) # previously stack overflow
+ self_containing[0] = None # resolve circular reference
def test_to_int_scalar(self):
# gh-9972 means that these aren't always the same
@@ -7367,6 +7995,55 @@ class TestCTypes(object):
finally:
_internal.ctypes = ctypes
+ def _make_readonly(x):
+ x.flags.writeable = False
+ return x
+
+ @pytest.mark.parametrize('arr', [
+ np.array([1, 2, 3]),
+ np.array([['one', 'two'], ['three', 'four']]),
+ np.array((1, 2), dtype='i4,i4'),
+ np.zeros((2,), dtype=
+ np.dtype(dict(
+ formats=['<i4', '<i4'],
+ names=['a', 'b'],
+ offsets=[0, 2],
+ itemsize=6
+ ))
+ ),
+ np.array([None], dtype=object),
+ np.array([]),
+ np.empty((0, 0)),
+ _make_readonly(np.array([1, 2, 3])),
+ ], ids=[
+ '1d',
+ '2d',
+ 'structured',
+ 'overlapping',
+ 'object',
+ 'empty',
+ 'empty-2d',
+ 'readonly'
+ ])
+ def test_ctypes_data_as_holds_reference(self, arr):
+ # gh-9647
+ # create a copy to ensure that pytest does not mess with the refcounts
+ arr = arr.copy()
+
+ arr_ref = weakref.ref(arr)
+
+ ctypes_ptr = arr.ctypes.data_as(ctypes.c_void_p)
+
+ # `ctypes_ptr` should hold onto `arr`
+ del arr
+ break_cycles()
+ assert_(arr_ref() is not None, "ctypes pointer did not hold onto a reference")
+
+ # but when the `ctypes_ptr` object dies, so should `arr`
+ del ctypes_ptr
+ break_cycles()
+ assert_(arr_ref() is None, "unknowable whether ctypes pointer holds a reference")
+
class TestWritebackIfCopy(object):
# all these tests use the WRITEBACKIFCOPY mechanism
@@ -7382,17 +8059,13 @@ class TestWritebackIfCopy(object):
res = np.argmin(mat, 0, out=out)
assert_equal(res, range(5))
- def test_clip_with_out(self):
- mat = np.eye(5)
- out = np.eye(5, dtype='i2')
- res = np.clip(mat, a_min=-10, a_max=0, out=out)
- assert_equal(np.sum(out), 0)
-
def test_insert_noncontiguous(self):
a = np.arange(6).reshape(2,3).T # force non-c-contiguous
# uses arr_insert
np.place(a, a>2, [44, 55])
assert_equal(a, np.array([[0, 44], [1, 55], [2, 44]]))
+ # hit one of the failing paths
+ assert_raises(ValueError, np.place, a, a>20, [])
def test_put_noncontiguous(self):
a = np.arange(6).reshape(2,3).T # force non-c-contiguous
@@ -7451,6 +8124,8 @@ class TestWritebackIfCopy(object):
arr_wb[...] = 100
assert_equal(arr, -100)
+ @pytest.mark.leaks_references(
+ reason="increments self in dealloc; ignore since deprecated path.")
def test_dealloc_warning(self):
with suppress_warnings() as sup:
sup.record(RuntimeWarning)
@@ -7543,15 +8218,15 @@ class TestArrayFinalize(object):
assert_(isinstance(obj_subarray, RaisesInFinalize))
# reference should still be held by obj_arr
- gc.collect()
+ break_cycles()
assert_(obj_ref() is not None, "object should not already be dead")
del obj_arr
- gc.collect()
+ break_cycles()
assert_(obj_ref() is not None, "obj_arr should not hold the last reference")
del obj_subarray
- gc.collect()
+ break_cycles()
assert_(obj_ref() is None, "no references should remain")
@@ -7663,6 +8338,77 @@ def test_uintalignment_and_alignment():
dst = np.zeros((2,2), dtype='c8')
dst[:,1] = src[:,1] # assert in lowlevel_strided_loops fails?
+class TestAlignment(object):
+ # adapted from scipy._lib.tests.test__util.test__aligned_zeros
+ # Checks that unusual memory alignments don't trip up numpy.
+ # In particular, check RELAXED_STRIDES don't trip alignment assertions in
+ # NDEBUG mode for size-0 arrays (gh-12503)
+
+ def check(self, shape, dtype, order, align):
+ err_msg = repr((shape, dtype, order, align))
+ x = _aligned_zeros(shape, dtype, order, align=align)
+ if align is None:
+ align = np.dtype(dtype).alignment
+ assert_equal(x.__array_interface__['data'][0] % align, 0)
+ if hasattr(shape, '__len__'):
+ assert_equal(x.shape, shape, err_msg)
+ else:
+ assert_equal(x.shape, (shape,), err_msg)
+ assert_equal(x.dtype, dtype)
+ if order == "C":
+ assert_(x.flags.c_contiguous, err_msg)
+ elif order == "F":
+ if x.size > 0:
+ assert_(x.flags.f_contiguous, err_msg)
+ elif order is None:
+ assert_(x.flags.c_contiguous, err_msg)
+ else:
+ raise ValueError()
+
+ def test_various_alignments(self):
+ 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 list(np.typecodes["All"]) + ['i4,i4,i4']:
+ if dtype == 'O':
+ # object dtype can't be misaligned
+ continue
+ for shape in [n, (1, 2, 3, n)]:
+ self.check(shape, np.dtype(dtype), order, align)
+
+ def test_strided_loop_alignments(self):
+ # particularly test that complex64 and float128 use right alignment
+ # code-paths, since these are particularly problematic. It is useful to
+ # turn on USE_DEBUG for this test, so lowlevel-loop asserts are run.
+ for align in [1, 2, 4, 8, 12, 16, None]:
+ xf64 = _aligned_zeros(3, np.float64)
+
+ xc64 = _aligned_zeros(3, np.complex64, align=align)
+ xf128 = _aligned_zeros(3, np.longdouble, align=align)
+
+ # test casting, both to and from misaligned
+ with suppress_warnings() as sup:
+ sup.filter(np.ComplexWarning, "Casting complex values")
+ xc64.astype('f8')
+ xf64.astype(np.complex64)
+ test = xc64 + xf64
+
+ xf128.astype('f8')
+ xf64.astype(np.longdouble)
+ test = xf128 + xf64
+
+ test = xf128 + xc64
+
+ # test copy, both to and from misaligned
+ # contig copy
+ xf64[:] = xf64.copy()
+ xc64[:] = xc64.copy()
+ xf128[:] = xf128.copy()
+ # strided copy
+ xf64[::2] = xf64[::2].copy()
+ xc64[::2] = xc64[::2].copy()
+ xf128[::2] = xf128[::2].copy()
+
def test_getfield():
a = np.arange(32, dtype='uint16')
if sys.byteorder == 'little':
diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py
index 5e8165bc5..cf66751f8 100644
--- a/numpy/core/tests/test_nditer.py
+++ b/numpy/core/tests/test_nditer.py
@@ -1,14 +1,13 @@
from __future__ import division, absolute_import, print_function
import sys
-import warnings
import pytest
import numpy as np
import numpy.core._multiarray_tests as _multiarray_tests
from numpy import array, arange, nditer, all
from numpy.testing import (
- assert_, assert_equal, assert_array_equal, assert_raises, assert_warns,
+ assert_, assert_equal, assert_array_equal, assert_raises,
HAS_REFCOUNT, suppress_warnings
)
@@ -1865,7 +1864,7 @@ def test_iter_buffered_cast_structured_type():
# make sure multi-field struct type -> simple doesn't work
sdt = [('a', 'f4'), ('b', 'i8'), ('d', 'O')]
a = np.array([(5.5, 7, 'test'), (8, 10, 11)], dtype=sdt)
- assert_raises(ValueError, lambda: (
+ assert_raises(TypeError, lambda: (
nditer(a, ['buffered', 'refs_ok'], ['readonly'],
casting='unsafe',
op_dtypes='i4')))
@@ -2196,21 +2195,15 @@ class TestIterNested(object):
a = arange(12).reshape(2, 3, 2)
i, j = np.nested_iters(a, [[0], [1, 2]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]])
i, j = np.nested_iters(a, [[0, 1], [2]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]])
i, j = np.nested_iters(a, [[0, 2], [1]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 2, 4], [1, 3, 5], [6, 8, 10], [7, 9, 11]])
def test_reorder(self):
@@ -2219,40 +2212,28 @@ class TestIterNested(object):
# In 'K' order (default), it gets reordered
i, j = np.nested_iters(a, [[0], [2, 1]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]])
i, j = np.nested_iters(a, [[1, 0], [2]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]])
i, j = np.nested_iters(a, [[2, 0], [1]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 2, 4], [1, 3, 5], [6, 8, 10], [7, 9, 11]])
# In 'C' order, it doesn't
i, j = np.nested_iters(a, [[0], [2, 1]], order='C')
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 2, 4, 1, 3, 5], [6, 8, 10, 7, 9, 11]])
i, j = np.nested_iters(a, [[1, 0], [2]], order='C')
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1], [6, 7], [2, 3], [8, 9], [4, 5], [10, 11]])
i, j = np.nested_iters(a, [[2, 0], [1]], order='C')
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 2, 4], [6, 8, 10], [1, 3, 5], [7, 9, 11]])
def test_flip_axes(self):
@@ -2261,40 +2242,28 @@ class TestIterNested(object):
# In 'K' order (default), the axes all get flipped
i, j = np.nested_iters(a, [[0], [1, 2]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]])
i, j = np.nested_iters(a, [[0, 1], [2]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]])
i, j = np.nested_iters(a, [[0, 2], [1]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 2, 4], [1, 3, 5], [6, 8, 10], [7, 9, 11]])
# In 'C' order, flipping axes is disabled
i, j = np.nested_iters(a, [[0], [1, 2]], order='C')
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[11, 10, 9, 8, 7, 6], [5, 4, 3, 2, 1, 0]])
i, j = np.nested_iters(a, [[0, 1], [2]], order='C')
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[11, 10], [9, 8], [7, 6], [5, 4], [3, 2], [1, 0]])
i, j = np.nested_iters(a, [[0, 2], [1]], order='C')
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[11, 9, 7], [10, 8, 6], [5, 3, 1], [4, 2, 0]])
def test_broadcast(self):
@@ -2303,15 +2272,11 @@ class TestIterNested(object):
b = arange(3).reshape(1, 3)
i, j = np.nested_iters([a, b], [[0], [1]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[[0, 0], [0, 1], [0, 2]], [[1, 0], [1, 1], [1, 2]]])
i, j = np.nested_iters([a, b], [[1], [0]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[[0, 0], [1, 0]], [[0, 1], [1, 1]], [[0, 2], [1, 2]]])
def test_dtype_copy(self):
@@ -2323,13 +2288,11 @@ class TestIterNested(object):
op_flags=['readonly', 'copy'],
op_dtypes='f8')
assert_equal(j[0].dtype, np.dtype('f8'))
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1, 2], [3, 4, 5]])
vals = None
- # writebackifcopy - using conext manager
+ # writebackifcopy - using context manager
a = arange(6, dtype='f4').reshape(2, 3)
i, j = np.nested_iters(a, [[0], [1]],
op_flags=['readwrite', 'updateifcopy'],
@@ -2376,15 +2339,11 @@ class TestIterNested(object):
def test_0d(self):
a = np.arange(12).reshape(2, 3, 2)
i, j = np.nested_iters(a, [[], [1, 0, 2]])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])
i, j = np.nested_iters(a, [[1, 0, 2], []])
- vals = []
- for x in i:
- vals.append([y for y in j])
+ vals = [list(j) for _ in i]
assert_equal(vals, [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11]])
i, j, k = np.nested_iters(a, [[2, 0], [], [1]])
@@ -2556,10 +2515,8 @@ def test_iter_buffering_reduction_reuse_reduce_loops():
op_flags=[['readonly'], ['readwrite']],
buffersize=5)
- bufsizes = []
with it:
- for x, y in it:
- bufsizes.append(x.shape[0])
+ bufsizes = [x.shape[0] for x, y in it]
assert_equal(bufsizes, [5, 2, 5, 2])
assert_equal(sum(bufsizes), a.size)
diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py
index f264c4ab0..1358b45e9 100644
--- a/numpy/core/tests/test_numeric.py
+++ b/numpy/core/tests/test_numeric.py
@@ -13,7 +13,7 @@ from numpy.random import rand, randint, randn
from numpy.testing import (
assert_, assert_equal, assert_raises, assert_raises_regex,
assert_array_equal, assert_almost_equal, assert_array_almost_equal,
- suppress_warnings, HAS_REFCOUNT
+ assert_warns, HAS_REFCOUNT
)
@@ -43,7 +43,7 @@ class TestResize(object):
def test_reshape_from_zero(self):
# See also gh-6740
- A = np.zeros(0, dtype=[('a', np.float32, 1)])
+ A = np.zeros(0, dtype=[('a', np.float32)])
Ar = np.resize(A, (2, 1))
assert_array_equal(Ar, np.zeros((2, 1), Ar.dtype))
assert_equal(A.dtype, Ar.dtype)
@@ -152,7 +152,15 @@ class TestNonarrayArgs(object):
def test_squeeze(self):
A = [[[1, 1, 1], [2, 2, 2], [3, 3, 3]]]
- assert_(np.squeeze(A).shape == (3, 3))
+ assert_equal(np.squeeze(A).shape, (3, 3))
+ assert_equal(np.squeeze(np.zeros((1, 3, 1))).shape, (3,))
+ assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=0).shape, (3, 1))
+ assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=-1).shape, (1, 3))
+ assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=2).shape, (1, 3))
+ assert_equal(np.squeeze([np.zeros((3, 1))]).shape, (3,))
+ assert_equal(np.squeeze([np.zeros((3, 1))], axis=0).shape, (3, 1))
+ assert_equal(np.squeeze([np.zeros((3, 1))], axis=2).shape, (1, 3))
+ assert_equal(np.squeeze([np.zeros((3, 1))], axis=-1).shape, (1, 3))
def test_std(self):
A = [[1, 2, 3], [4, 5, 6]]
@@ -208,6 +216,9 @@ class TestNonarrayArgs(object):
assert_(np.isnan(np.var([])))
assert_(w[0].category is RuntimeWarning)
+ B = np.array([None, 0])
+ B[0] = 1j
+ assert_almost_equal(np.var(B), 0.25)
class TestIsscalar(object):
def test_isscalar(self):
@@ -888,6 +899,41 @@ class TestTypes(object):
# Also test keyword arguments
assert_(np.can_cast(from_=np.int32, to=np.int64))
+ def test_can_cast_simple_to_structured(self):
+ # Non-structured can only be cast to structured in 'unsafe' mode.
+ assert_(not np.can_cast('i4', 'i4,i4'))
+ assert_(not np.can_cast('i4', 'i4,i2'))
+ assert_(np.can_cast('i4', 'i4,i4', casting='unsafe'))
+ assert_(np.can_cast('i4', 'i4,i2', casting='unsafe'))
+ # Even if there is just a single field which is OK.
+ assert_(not np.can_cast('i2', [('f1', 'i4')]))
+ assert_(not np.can_cast('i2', [('f1', 'i4')], casting='same_kind'))
+ assert_(np.can_cast('i2', [('f1', 'i4')], casting='unsafe'))
+ # It should be the same for recursive structured or subarrays.
+ assert_(not np.can_cast('i2', [('f1', 'i4,i4')]))
+ assert_(np.can_cast('i2', [('f1', 'i4,i4')], casting='unsafe'))
+ assert_(not np.can_cast('i2', [('f1', '(2,3)i4')]))
+ assert_(np.can_cast('i2', [('f1', '(2,3)i4')], casting='unsafe'))
+
+ def test_can_cast_structured_to_simple(self):
+ # Need unsafe casting for structured to simple.
+ assert_(not np.can_cast([('f1', 'i4')], 'i4'))
+ assert_(np.can_cast([('f1', 'i4')], 'i4', casting='unsafe'))
+ assert_(np.can_cast([('f1', 'i4')], 'i2', casting='unsafe'))
+ # Since it is unclear what is being cast, multiple fields to
+ # single should not work even for unsafe casting.
+ assert_(not np.can_cast('i4,i4', 'i4', casting='unsafe'))
+ # But a single field inside a single field is OK.
+ assert_(not np.can_cast([('f1', [('x', 'i4')])], 'i4'))
+ assert_(np.can_cast([('f1', [('x', 'i4')])], 'i4', casting='unsafe'))
+ # And a subarray is fine too - it will just take the first element
+ # (arguably not very consistently; might also take the first field).
+ assert_(not np.can_cast([('f0', '(3,)i4')], 'i4'))
+ assert_(np.can_cast([('f0', '(3,)i4')], 'i4', casting='unsafe'))
+ # But a structured subarray with multiple fields should fail.
+ assert_(not np.can_cast([('f0', ('i4,i4'), (2,))], 'i4',
+ casting='unsafe'))
+
def test_can_cast_values(self):
# gh-5917
for dt in np.sctypes['int'] + np.sctypes['uint']:
@@ -965,12 +1011,24 @@ class TestNonzero(object):
assert_equal(np.count_nonzero(np.array([], dtype='?')), 0)
assert_equal(np.nonzero(np.array([])), ([],))
+ assert_equal(np.count_nonzero(np.array([0])), 0)
+ assert_equal(np.count_nonzero(np.array([0], dtype='?')), 0)
+ assert_equal(np.nonzero(np.array([0])), ([],))
+
+ assert_equal(np.count_nonzero(np.array([1])), 1)
+ assert_equal(np.count_nonzero(np.array([1], dtype='?')), 1)
+ assert_equal(np.nonzero(np.array([1])), ([0],))
+
+ def test_nonzero_zerod(self):
assert_equal(np.count_nonzero(np.array(0)), 0)
assert_equal(np.count_nonzero(np.array(0, dtype='?')), 0)
- assert_equal(np.nonzero(np.array(0)), ([],))
+ with assert_warns(DeprecationWarning):
+ assert_equal(np.nonzero(np.array(0)), ([],))
+
assert_equal(np.count_nonzero(np.array(1)), 1)
assert_equal(np.count_nonzero(np.array(1, dtype='?')), 1)
- assert_equal(np.nonzero(np.array(1)), ([0],))
+ with assert_warns(DeprecationWarning):
+ assert_equal(np.nonzero(np.array(1)), ([0],))
def test_nonzero_onedim(self):
x = np.array([1, 0, 2, -1, 0, 0, 8])
@@ -1164,6 +1222,71 @@ class TestNonzero(object):
assert_raises(ValueError, np.nonzero, np.array([BoolErrors()]))
+ def test_nonzero_sideeffect_safety(self):
+ # gh-13631
+ class FalseThenTrue:
+ _val = False
+ def __bool__(self):
+ try:
+ return self._val
+ finally:
+ self._val = True
+
+ class TrueThenFalse:
+ _val = True
+ def __bool__(self):
+ try:
+ return self._val
+ finally:
+ self._val = False
+
+ # result grows on the second pass
+ a = np.array([True, FalseThenTrue()])
+ assert_raises(RuntimeError, np.nonzero, a)
+
+ a = np.array([[True], [FalseThenTrue()]])
+ assert_raises(RuntimeError, np.nonzero, a)
+
+ # result shrinks on the second pass
+ a = np.array([False, TrueThenFalse()])
+ assert_raises(RuntimeError, np.nonzero, a)
+
+ a = np.array([[False], [TrueThenFalse()]])
+ assert_raises(RuntimeError, np.nonzero, a)
+
+ def test_nonzero_exception_safe(self):
+ # gh-13930
+
+ class ThrowsAfter:
+ def __init__(self, iters):
+ self.iters_left = iters
+
+ def __bool__(self):
+ if self.iters_left == 0:
+ raise ValueError("called `iters` times")
+
+ self.iters_left -= 1
+ return True
+
+ """
+ Test that a ValueError is raised instead of a SystemError
+
+ If the __bool__ function is called after the error state is set,
+ Python (cpython) will raise a SystemError.
+ """
+
+ # assert that an exception in first pass is handled correctly
+ a = np.array([ThrowsAfter(5)]*10)
+ assert_raises(ValueError, np.nonzero, a)
+
+ # raise exception in second pass for 1-dimensional loop
+ a = np.array([ThrowsAfter(15)]*10)
+ assert_raises(ValueError, np.nonzero, a)
+
+ # raise exception in second pass for n-dimensional loop
+ a = np.array([[ThrowsAfter(15)]]*10)
+ assert_raises(ValueError, np.nonzero, a)
+
class TestIndex(object):
def test_boolean(self):
@@ -1218,6 +1341,11 @@ class TestBinaryRepr(object):
exp = '1' + (width - 1) * '0'
assert_equal(np.binary_repr(num, width=width), exp)
+ def test_large_neg_int64(self):
+ # See gh-14289.
+ assert_equal(np.binary_repr(np.int64(-2**62), width=64),
+ '11' + '0'*62)
+
class TestBaseRepr(object):
def test_base3(self):
@@ -1325,11 +1453,17 @@ class TestClip(object):
self.nr = 5
self.nc = 3
- def fastclip(self, a, m, M, out=None):
+ def fastclip(self, a, m, M, out=None, casting=None):
if out is None:
- return a.clip(m, M)
+ if casting is None:
+ return a.clip(m, M)
+ else:
+ return a.clip(m, M, casting=casting)
else:
- return a.clip(m, M, out)
+ if casting is None:
+ return a.clip(m, M, out)
+ else:
+ return a.clip(m, M, out, casting=casting)
def clip(self, a, m, M, out=None):
# use slow-clip
@@ -1367,6 +1501,20 @@ class TestClip(object):
return (10 * rand(n, m)).astype(np.int32)
# Now the real test cases
+
+ @pytest.mark.parametrize("dtype", '?bhilqpBHILQPefdgFDGO')
+ def test_ones_pathological(self, dtype):
+ # for preservation of behavior described in
+ # gh-12519; amin > amax behavior may still change
+ # in the future
+ arr = np.ones(10, dtype=dtype)
+ expected = np.zeros(10, dtype=dtype)
+ actual = np.clip(arr, 1, 0)
+ if dtype == 'O':
+ assert actual.tolist() == expected.tolist()
+ else:
+ assert_equal(actual, expected)
+
def test_simple_double(self):
# Test native double input with scalar min/max.
a = self._generate_data(self.nr, self.nc)
@@ -1465,14 +1613,21 @@ class TestClip(object):
self.clip(a, m, M, act)
assert_array_strict_equal(ac, act)
- def test_simple_int32_inout(self):
+ @pytest.mark.parametrize("casting", [None, "unsafe"])
+ def test_simple_int32_inout(self, casting):
# Test native int32 input with double min/max and int32 out.
a = self._generate_int32_data(self.nr, self.nc)
m = np.float64(0)
M = np.float64(2)
ac = np.zeros(a.shape, dtype=np.int32)
act = ac.copy()
- self.fastclip(a, m, M, ac)
+ if casting is None:
+ with assert_warns(DeprecationWarning):
+ # NumPy 1.17.0, 2018-02-24 - casting is unsafe
+ self.fastclip(a, m, M, ac, casting=casting)
+ else:
+ # explicitly passing "unsafe" will silence warning
+ self.fastclip(a, m, M, ac, casting=casting)
self.clip(a, m, M, act)
assert_array_strict_equal(ac, act)
@@ -1494,7 +1649,9 @@ class TestClip(object):
M = np.float64(1)
ac = np.zeros(a.shape, dtype=np.int32)
act = ac.copy()
- self.fastclip(a, m, M, ac)
+ with assert_warns(DeprecationWarning):
+ # NumPy 1.17.0, 2018-02-24 - casting is unsafe
+ self.fastclip(a, m, M, ac)
self.clip(a, m, M, act)
assert_array_strict_equal(ac, act)
@@ -1505,7 +1662,9 @@ class TestClip(object):
M = 2.0
ac = np.zeros(a.shape, dtype=np.int32)
act = ac.copy()
- self.fastclip(a, m, M, ac)
+ with assert_warns(DeprecationWarning):
+ # NumPy 1.17.0, 2018-02-24 - casting is unsafe
+ self.fastclip(a, m, M, ac)
self.clip(a, m, M, act)
assert_array_strict_equal(ac, act)
@@ -1681,7 +1840,9 @@ class TestClip(object):
M = np.float64(2)
ac = np.zeros(a.shape, dtype=np.int32)
act = ac.copy()
- self.fastclip(a, m, M, ac)
+ with assert_warns(DeprecationWarning):
+ # NumPy 1.17.0, 2018-02-24 - casting is unsafe
+ self.fastclip(a, m, M, ac)
self.clip(a, m, M, act)
assert_array_strict_equal(ac, act)
@@ -1703,7 +1864,9 @@ class TestClip(object):
M = np.float64(1)
ac = np.zeros(a.shape, dtype=np.int32)
act = ac.copy()
- self.fastclip(a, m, M, ac)
+ with assert_warns(DeprecationWarning):
+ # NumPy 1.17.0, 2018-02-24 - casting is unsafe
+ self.fastclip(a, m, M, ac)
self.clip(a, m, M, act)
assert_array_strict_equal(ac, act)
@@ -1714,7 +1877,9 @@ class TestClip(object):
M = 2.0
ac = np.zeros(a.shape, dtype=np.int32)
act = ac.copy()
- self.fastclip(a, m, M, ac)
+ with assert_warns(DeprecationWarning):
+ # NumPy 1.17.0, 2018-02-24 - casting is unsafe
+ self.fastclip(a, m, M, ac)
self.clip(a, m, M, act)
assert_array_strict_equal(ac, act)
@@ -1767,11 +1932,94 @@ class TestClip(object):
def test_clip_nan(self):
d = np.arange(7.)
- assert_equal(d.clip(min=np.nan), d)
- assert_equal(d.clip(max=np.nan), d)
- assert_equal(d.clip(min=np.nan, max=np.nan), d)
- assert_equal(d.clip(min=-2, max=np.nan), d)
- assert_equal(d.clip(min=np.nan, max=10), d)
+ with assert_warns(DeprecationWarning):
+ assert_equal(d.clip(min=np.nan), d)
+ with assert_warns(DeprecationWarning):
+ assert_equal(d.clip(max=np.nan), d)
+ with assert_warns(DeprecationWarning):
+ assert_equal(d.clip(min=np.nan, max=np.nan), d)
+ with assert_warns(DeprecationWarning):
+ assert_equal(d.clip(min=-2, max=np.nan), d)
+ with assert_warns(DeprecationWarning):
+ assert_equal(d.clip(min=np.nan, max=10), d)
+
+ def test_object_clip(self):
+ a = np.arange(10, dtype=object)
+ actual = np.clip(a, 1, 5)
+ expected = np.array([1, 1, 2, 3, 4, 5, 5, 5, 5, 5])
+ assert actual.tolist() == expected.tolist()
+
+ def test_clip_all_none(self):
+ a = np.arange(10, dtype=object)
+ with assert_raises_regex(ValueError, 'max or min'):
+ np.clip(a, None, None)
+
+ def test_clip_invalid_casting(self):
+ a = np.arange(10, dtype=object)
+ with assert_raises_regex(ValueError,
+ 'casting must be one of'):
+ self.fastclip(a, 1, 8, casting="garbage")
+
+ @pytest.mark.parametrize("amin, amax", [
+ # two scalars
+ (1, 0),
+ # mix scalar and array
+ (1, np.zeros(10)),
+ # two arrays
+ (np.ones(10), np.zeros(10)),
+ ])
+ def test_clip_value_min_max_flip(self, amin, amax):
+ a = np.arange(10, dtype=np.int64)
+ # requirement from ufunc_docstrings.py
+ expected = np.minimum(np.maximum(a, amin), amax)
+ actual = np.clip(a, amin, amax)
+ assert_equal(actual, expected)
+
+ @pytest.mark.parametrize("arr, amin, amax, exp", [
+ # for a bug in npy_ObjectClip, based on a
+ # case produced by hypothesis
+ (np.zeros(10, dtype=np.int64),
+ 0,
+ -2**64+1,
+ np.full(10, -2**64+1, dtype=object)),
+ # for bugs in NPY_TIMEDELTA_MAX, based on a case
+ # produced by hypothesis
+ (np.zeros(10, dtype='m8') - 1,
+ 0,
+ 0,
+ np.zeros(10, dtype='m8')),
+ ])
+ def test_clip_problem_cases(self, arr, amin, amax, exp):
+ actual = np.clip(arr, amin, amax)
+ assert_equal(actual, exp)
+
+ @pytest.mark.xfail(reason="no scalar nan propagation yet")
+ @pytest.mark.parametrize("arr, amin, amax", [
+ # problematic scalar nan case from hypothesis
+ (np.zeros(10, dtype=np.int64),
+ np.array(np.nan),
+ np.zeros(10, dtype=np.int32)),
+ ])
+ def test_clip_scalar_nan_propagation(self, arr, amin, amax):
+ # enforcement of scalar nan propagation for comparisons
+ # called through clip()
+ expected = np.minimum(np.maximum(a, amin), amax)
+ with assert_warns(DeprecationWarning):
+ actual = np.clip(arr, amin, amax)
+ assert_equal(actual, expected)
+
+ @pytest.mark.xfail(reason="propagation doesn't match spec")
+ @pytest.mark.parametrize("arr, amin, amax", [
+ (np.array([1] * 10, dtype='m8'),
+ np.timedelta64('NaT'),
+ np.zeros(10, dtype=np.int32)),
+ ])
+ def test_NaT_propagation(self, arr, amin, amax):
+ # NOTE: the expected function spec doesn't
+ # propagate NaT, but clip() now does
+ expected = np.minimum(np.maximum(a, amin), amax)
+ actual = np.clip(arr, amin, amax)
+ assert_equal(actual, expected)
class TestAllclose(object):
@@ -2146,6 +2394,7 @@ class TestLikeFuncs(object):
(np.arange(24).reshape(2, 3, 4).swapaxes(0, 1), None),
(np.arange(24).reshape(4, 3, 2).swapaxes(0, 1), '?'),
]
+ self.shapes = [(5,), (5,6,), (5,6,7,)]
def compare_array_value(self, dz, value, fill_value):
if value is not None:
@@ -2211,6 +2460,34 @@ class TestLikeFuncs(object):
assert_equal(dz.dtype, np.dtype(dtype))
self.compare_array_value(dz, value, fill_value)
+ # Test the 'shape' parameter
+ for s in self.shapes:
+ for o in 'CFA':
+ sz = like_function(d, dtype=dtype, shape=s, order=o,
+ **fill_kwarg)
+ assert_equal(sz.shape, s)
+ if dtype is None:
+ assert_equal(sz.dtype, d.dtype)
+ else:
+ assert_equal(sz.dtype, np.dtype(dtype))
+ if o == 'C' or (o == 'A' and d.flags.c_contiguous):
+ assert_(sz.flags.c_contiguous)
+ elif o == 'F' or (o == 'A' and d.flags.f_contiguous):
+ assert_(sz.flags.f_contiguous)
+ self.compare_array_value(sz, value, fill_value)
+
+ if (d.ndim != len(s)):
+ assert_equal(np.argsort(like_function(d, dtype=dtype,
+ shape=s, order='K',
+ **fill_kwarg).strides),
+ np.argsort(np.empty(s, dtype=dtype,
+ order='C').strides))
+ else:
+ assert_equal(np.argsort(like_function(d, dtype=dtype,
+ shape=s, order='K',
+ **fill_kwarg).strides),
+ np.argsort(d.strides))
+
# Test the 'subok' parameter
class MyNDArray(np.ndarray):
pass
@@ -2306,6 +2583,30 @@ class TestConvolve(object):
class TestArgwhere(object):
+
+ @pytest.mark.parametrize('nd', [0, 1, 2])
+ def test_nd(self, nd):
+ # get an nd array with multiple elements in every dimension
+ x = np.empty((2,)*nd, bool)
+
+ # none
+ x[...] = False
+ assert_equal(np.argwhere(x).shape, (0, nd))
+
+ # only one
+ x[...] = False
+ x.flat[0] = True
+ assert_equal(np.argwhere(x).shape, (1, nd))
+
+ # all but one
+ x[...] = True
+ x.flat[0] = False
+ assert_equal(np.argwhere(x).shape, (x.size - 1, nd))
+
+ # all
+ x[...] = True
+ assert_equal(np.argwhere(x).shape, (x.size, nd))
+
def test_2D(self):
x = np.arange(6).reshape((2, 3))
assert_array_equal(np.argwhere(x > 1),
@@ -2616,6 +2917,47 @@ def test_outer_out_param():
assert_equal(np.outer(arr2, arr3, out2), out2)
+class TestIndices(object):
+
+ def test_simple(self):
+ [x, y] = np.indices((4, 3))
+ assert_array_equal(x, np.array([[0, 0, 0],
+ [1, 1, 1],
+ [2, 2, 2],
+ [3, 3, 3]]))
+ assert_array_equal(y, np.array([[0, 1, 2],
+ [0, 1, 2],
+ [0, 1, 2],
+ [0, 1, 2]]))
+
+ def test_single_input(self):
+ [x] = np.indices((4,))
+ assert_array_equal(x, np.array([0, 1, 2, 3]))
+
+ [x] = np.indices((4,), sparse=True)
+ assert_array_equal(x, np.array([0, 1, 2, 3]))
+
+ def test_scalar_input(self):
+ assert_array_equal([], np.indices(()))
+ assert_array_equal([], np.indices((), sparse=True))
+ assert_array_equal([[]], np.indices((0,)))
+ assert_array_equal([[]], np.indices((0,), sparse=True))
+
+ def test_sparse(self):
+ [x, y] = np.indices((4,3), sparse=True)
+ assert_array_equal(x, np.array([[0], [1], [2], [3]]))
+ assert_array_equal(y, np.array([[0, 1, 2]]))
+
+ @pytest.mark.parametrize("dtype", [np.int, np.float32, np.float64])
+ @pytest.mark.parametrize("dims", [(), (0,), (4, 3)])
+ def test_return_type(self, dtype, dims):
+ inds = np.indices(dims, dtype=dtype)
+ assert_(inds.dtype == dtype)
+
+ for arr in np.indices(dims, dtype=dtype, sparse=True):
+ assert_(arr.dtype == dtype)
+
+
class TestRequire(object):
flag_names = ['C', 'C_CONTIGUOUS', 'CONTIGUOUS',
'F', 'F_CONTIGUOUS', 'FORTRAN',
@@ -2696,6 +3038,8 @@ class TestBroadcast(object):
arrs = [np.empty((6, 7)), np.empty((5, 6, 1)), np.empty((7,)),
np.empty((5, 1, 7))]
mits = [np.broadcast(*arrs),
+ np.broadcast(np.broadcast(*arrs[:0]), np.broadcast(*arrs[0:])),
+ np.broadcast(np.broadcast(*arrs[:1]), np.broadcast(*arrs[1:])),
np.broadcast(np.broadcast(*arrs[:2]), np.broadcast(*arrs[2:])),
np.broadcast(arrs[0], np.broadcast(*arrs[1:-1]), arrs[-1])]
for mit in mits:
@@ -2720,12 +3064,24 @@ class TestBroadcast(object):
arr = np.empty((5,))
for j in range(35):
arrs = [arr] * j
- if j < 1 or j > 32:
+ if j > 32:
assert_raises(ValueError, np.broadcast, *arrs)
else:
mit = np.broadcast(*arrs)
assert_equal(mit.numiter, j)
+ def test_broadcast_error_kwargs(self):
+ #gh-13455
+ arrs = [np.empty((5, 6, 7))]
+ mit = np.broadcast(*arrs)
+ mit2 = np.broadcast(*arrs, **{})
+ assert_equal(mit.shape, mit2.shape)
+ assert_equal(mit.ndim, mit2.ndim)
+ assert_equal(mit.nd, mit2.nd)
+ assert_equal(mit.numiter, mit2.numiter)
+ assert_(mit.iters[0].base is mit2.iters[0].base)
+
+ assert_raises(ValueError, np.broadcast, 1, **{'x': 1})
class TestKeepdims(object):
@@ -2748,3 +3104,9 @@ class TestTensordot(object):
td = np.tensordot(a, b, (1, 0))
assert_array_equal(td, np.dot(a, b))
assert_array_equal(td, np.einsum('ij,jk', a, b))
+
+ def test_zero_dimensional(self):
+ # gh-12130
+ arr_0d = np.array(1)
+ ret = np.tensordot(arr_0d, arr_0d, ([], [])) # contracting no axes is well defined
+ assert_array_equal(ret, arr_0d)
diff --git a/numpy/core/tests/test_numerictypes.py b/numpy/core/tests/test_numerictypes.py
index 27e4fdeec..387740e35 100644
--- a/numpy/core/tests/test_numerictypes.py
+++ b/numpy/core/tests/test_numerictypes.py
@@ -5,7 +5,7 @@ import itertools
import pytest
import numpy as np
-from numpy.testing import assert_, assert_equal, assert_raises
+from numpy.testing import assert_, assert_equal, assert_raises, IS_PYPY
# This is the structure of the table used for plain objects:
#
@@ -87,10 +87,8 @@ def normalize_descr(descr):
else:
nitem = (item[0], dtype)
out.append(nitem)
- elif isinstance(item[1], list):
- l = []
- for j in normalize_descr(item[1]):
- l.append(j)
+ elif isinstance(dtype, list):
+ l = normalize_descr(dtype)
out.append((item[0], l))
else:
raise ValueError("Expected a str or list and got %s" %
@@ -493,9 +491,39 @@ def test_issctype(rep, expected):
@pytest.mark.skipif(sys.flags.optimize > 1,
reason="no docstrings present to inspect when PYTHONOPTIMIZE/Py_OptimizeFlag > 1")
+@pytest.mark.xfail(IS_PYPY, reason="PyPy does not modify tp_doc")
class TestDocStrings(object):
def test_platform_dependent_aliases(self):
if np.int64 is np.int_:
assert_('int64' in np.int_.__doc__)
elif np.int64 is np.longlong:
assert_('int64' in np.longlong.__doc__)
+
+
+class TestScalarTypeNames:
+ # gh-9799
+
+ numeric_types = [
+ np.byte, np.short, np.intc, np.int_, np.longlong,
+ np.ubyte, np.ushort, np.uintc, np.uint, np.ulonglong,
+ np.half, np.single, np.double, np.longdouble,
+ np.csingle, np.cdouble, np.clongdouble,
+ ]
+
+ def test_names_are_unique(self):
+ # none of the above may be aliases for each other
+ assert len(set(self.numeric_types)) == len(self.numeric_types)
+
+ # names must be unique
+ names = [t.__name__ for t in self.numeric_types]
+ assert len(set(names)) == len(names)
+
+ @pytest.mark.parametrize('t', numeric_types)
+ def test_names_reflect_attributes(self, t):
+ """ Test that names correspond to where the type is under ``np.`` """
+ assert getattr(np, t.__name__) is t
+
+ @pytest.mark.parametrize('t', numeric_types)
+ def test_names_are_undersood_by_dtype(self, t):
+ """ Test the dtype constructor maps names back to the type """
+ assert np.dtype(t.__name__).type is t
diff --git a/numpy/core/tests/test_overrides.py b/numpy/core/tests/test_overrides.py
index 895f221da..63b0e4539 100644
--- a/numpy/core/tests/test_overrides.py
+++ b/numpy/core/tests/test_overrides.py
@@ -1,51 +1,62 @@
from __future__ import division, absolute_import, print_function
-import pickle
+import inspect
import sys
+from unittest import mock
import numpy as np
from numpy.testing import (
assert_, assert_equal, assert_raises, assert_raises_regex)
from numpy.core.overrides import (
- get_overloaded_types_and_args, array_function_dispatch,
- verify_matching_signatures)
+ _get_implementing_args, array_function_dispatch,
+ verify_matching_signatures, ARRAY_FUNCTION_ENABLED)
+from numpy.compat import pickle
+import pytest
-def _get_overloaded_args(relevant_args):
- types, args = get_overloaded_types_and_args(relevant_args)
- return args
+requires_array_function = pytest.mark.skipif(
+ not ARRAY_FUNCTION_ENABLED,
+ reason="__array_function__ dispatch not enabled.")
-def _return_self(self, *args, **kwargs):
- return self
+def _return_not_implemented(self, *args, **kwargs):
+ return NotImplemented
-class TestGetOverloadedTypesAndArgs(object):
+# need to define this at the top level to test pickling
+@array_function_dispatch(lambda array: (array,))
+def dispatched_one_arg(array):
+ """Docstring."""
+ return 'original'
+
+
+@array_function_dispatch(lambda array1, array2: (array1, array2))
+def dispatched_two_arg(array1, array2):
+ """Docstring."""
+ return 'original'
+
+
+class TestGetImplementingArgs(object):
def test_ndarray(self):
array = np.array(1)
- types, args = get_overloaded_types_and_args([array])
- assert_equal(set(types), {np.ndarray})
- assert_equal(list(args), [])
+ args = _get_implementing_args([array])
+ assert_equal(list(args), [array])
- types, args = get_overloaded_types_and_args([array, array])
- assert_equal(len(types), 1)
- assert_equal(set(types), {np.ndarray})
- assert_equal(list(args), [])
+ args = _get_implementing_args([array, array])
+ assert_equal(list(args), [array])
- types, args = get_overloaded_types_and_args([array, 1])
- assert_equal(set(types), {np.ndarray})
- assert_equal(list(args), [])
+ args = _get_implementing_args([array, 1])
+ assert_equal(list(args), [array])
- types, args = get_overloaded_types_and_args([1, array])
- assert_equal(set(types), {np.ndarray})
- assert_equal(list(args), [])
+ args = _get_implementing_args([1, array])
+ assert_equal(list(args), [array])
def test_ndarray_subclasses(self):
class OverrideSub(np.ndarray):
- __array_function__ = _return_self
+ __array_function__ = _return_not_implemented
class NoOverrideSub(np.ndarray):
pass
@@ -54,122 +65,157 @@ class TestGetOverloadedTypesAndArgs(object):
override_sub = np.array(1).view(OverrideSub)
no_override_sub = np.array(1).view(NoOverrideSub)
- types, args = get_overloaded_types_and_args([array, override_sub])
- assert_equal(set(types), {np.ndarray, OverrideSub})
- assert_equal(list(args), [override_sub])
+ args = _get_implementing_args([array, override_sub])
+ assert_equal(list(args), [override_sub, array])
- types, args = get_overloaded_types_and_args([array, no_override_sub])
- assert_equal(set(types), {np.ndarray, NoOverrideSub})
- assert_equal(list(args), [])
+ args = _get_implementing_args([array, no_override_sub])
+ assert_equal(list(args), [no_override_sub, array])
- types, args = get_overloaded_types_and_args(
+ args = _get_implementing_args(
[override_sub, no_override_sub])
- assert_equal(set(types), {OverrideSub, NoOverrideSub})
- assert_equal(list(args), [override_sub])
+ assert_equal(list(args), [override_sub, no_override_sub])
def test_ndarray_and_duck_array(self):
class Other(object):
- __array_function__ = _return_self
+ __array_function__ = _return_not_implemented
array = np.array(1)
other = Other()
- types, args = get_overloaded_types_and_args([other, array])
- assert_equal(set(types), {np.ndarray, Other})
- assert_equal(list(args), [other])
+ args = _get_implementing_args([other, array])
+ assert_equal(list(args), [other, array])
- types, args = get_overloaded_types_and_args([array, other])
- assert_equal(set(types), {np.ndarray, Other})
- assert_equal(list(args), [other])
+ args = _get_implementing_args([array, other])
+ assert_equal(list(args), [array, other])
def test_ndarray_subclass_and_duck_array(self):
class OverrideSub(np.ndarray):
- __array_function__ = _return_self
+ __array_function__ = _return_not_implemented
class Other(object):
- __array_function__ = _return_self
+ __array_function__ = _return_not_implemented
array = np.array(1)
subarray = np.array(1).view(OverrideSub)
other = Other()
- assert_equal(_get_overloaded_args([array, subarray, other]),
- [subarray, other])
- assert_equal(_get_overloaded_args([array, other, subarray]),
- [subarray, other])
+ assert_equal(_get_implementing_args([array, subarray, other]),
+ [subarray, array, other])
+ assert_equal(_get_implementing_args([array, other, subarray]),
+ [subarray, array, other])
def test_many_duck_arrays(self):
class A(object):
- __array_function__ = _return_self
+ __array_function__ = _return_not_implemented
class B(A):
- __array_function__ = _return_self
+ __array_function__ = _return_not_implemented
class C(A):
- __array_function__ = _return_self
+ __array_function__ = _return_not_implemented
class D(object):
- __array_function__ = _return_self
+ __array_function__ = _return_not_implemented
a = A()
b = B()
c = C()
d = D()
- assert_equal(_get_overloaded_args([1]), [])
- assert_equal(_get_overloaded_args([a]), [a])
- assert_equal(_get_overloaded_args([a, 1]), [a])
- assert_equal(_get_overloaded_args([a, a, a]), [a])
- assert_equal(_get_overloaded_args([a, d, a]), [a, d])
- assert_equal(_get_overloaded_args([a, b]), [b, a])
- assert_equal(_get_overloaded_args([b, a]), [b, a])
- assert_equal(_get_overloaded_args([a, b, c]), [b, c, a])
- assert_equal(_get_overloaded_args([a, c, b]), [c, b, a])
+ assert_equal(_get_implementing_args([1]), [])
+ assert_equal(_get_implementing_args([a]), [a])
+ assert_equal(_get_implementing_args([a, 1]), [a])
+ assert_equal(_get_implementing_args([a, a, a]), [a])
+ assert_equal(_get_implementing_args([a, d, a]), [a, d])
+ assert_equal(_get_implementing_args([a, b]), [b, a])
+ assert_equal(_get_implementing_args([b, a]), [b, a])
+ assert_equal(_get_implementing_args([a, b, c]), [b, c, a])
+ assert_equal(_get_implementing_args([a, c, b]), [c, b, a])
+
+ def test_too_many_duck_arrays(self):
+ namespace = dict(__array_function__=_return_not_implemented)
+ types = [type('A' + str(i), (object,), namespace) for i in range(33)]
+ relevant_args = [t() for t in types]
+
+ actual = _get_implementing_args(relevant_args[:32])
+ assert_equal(actual, relevant_args[:32])
+
+ with assert_raises_regex(TypeError, 'distinct argument types'):
+ _get_implementing_args(relevant_args)
class TestNDArrayArrayFunction(object):
+ @requires_array_function
def test_method(self):
- class SubOverride(np.ndarray):
- __array_function__ = _return_self
+ class Other(object):
+ __array_function__ = _return_not_implemented
class NoOverrideSub(np.ndarray):
pass
- array = np.array(1)
+ class OverrideSub(np.ndarray):
+ __array_function__ = _return_not_implemented
- def func():
- return 'original'
+ array = np.array([1])
+ other = Other()
+ no_override_sub = array.view(NoOverrideSub)
+ override_sub = array.view(OverrideSub)
- result = array.__array_function__(
- func=func, types=(np.ndarray,), args=(), kwargs={})
+ result = array.__array_function__(func=dispatched_two_arg,
+ types=(np.ndarray,),
+ args=(array, 1.), kwargs={})
assert_equal(result, 'original')
- result = array.__array_function__(
- func=func, types=(np.ndarray, SubOverride), args=(), kwargs={})
+ result = array.__array_function__(func=dispatched_two_arg,
+ types=(np.ndarray, Other),
+ args=(array, other), kwargs={})
assert_(result is NotImplemented)
- result = array.__array_function__(
- func=func, types=(np.ndarray, NoOverrideSub), args=(), kwargs={})
+ result = array.__array_function__(func=dispatched_two_arg,
+ types=(np.ndarray, NoOverrideSub),
+ args=(array, no_override_sub),
+ kwargs={})
assert_equal(result, 'original')
+ result = array.__array_function__(func=dispatched_two_arg,
+ types=(np.ndarray, OverrideSub),
+ args=(array, override_sub),
+ kwargs={})
+ assert_equal(result, 'original')
-# need to define this at the top level to test pickling
-@array_function_dispatch(lambda array: (array,))
-def dispatched_one_arg(array):
- """Docstring."""
- return 'original'
+ with assert_raises_regex(TypeError, 'no implementation found'):
+ np.concatenate((array, other))
+
+ expected = np.concatenate((array, array))
+ result = np.concatenate((array, no_override_sub))
+ assert_equal(result, expected.view(NoOverrideSub))
+ result = np.concatenate((array, override_sub))
+ assert_equal(result, expected.view(OverrideSub))
+
+ def test_no_wrapper(self):
+ # This shouldn't happen unless a user intentionally calls
+ # __array_function__ with invalid arguments, but check that we raise
+ # an appropriate error all the same.
+ array = np.array(1)
+ func = lambda x: x
+ with assert_raises_regex(AttributeError, '_implementation'):
+ array.__array_function__(func=func, types=(np.ndarray,),
+ args=(array,), kwargs={})
+@requires_array_function
class TestArrayFunctionDispatch(object):
def test_pickle(self):
- roundtripped = pickle.loads(pickle.dumps(dispatched_one_arg))
- assert_(roundtripped is dispatched_one_arg)
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ roundtripped = pickle.loads(
+ pickle.dumps(dispatched_one_arg, protocol=proto))
+ assert_(roundtripped is dispatched_one_arg)
def test_name_and_docstring(self):
assert_equal(dispatched_one_arg.__name__, 'dispatched_one_arg')
@@ -187,7 +233,8 @@ class TestArrayFunctionDispatch(object):
assert_(obj is original)
assert_(func is dispatched_one_arg)
assert_equal(set(types), {MyArray})
- assert_equal(args, (original,))
+ # assert_equal uses the overloaded np.iscomplexobj() internally
+ assert_(args == (original,))
assert_equal(kwargs, {})
def test_not_implemented(self):
@@ -201,6 +248,7 @@ class TestArrayFunctionDispatch(object):
dispatched_one_arg(array)
+@requires_array_function
class TestVerifyMatchingSignatures(object):
def test_verify_matching_signatures(self):
@@ -253,6 +301,7 @@ def _new_duck_type_and_implements():
return (MyArray, implements)
+@requires_array_function
class TestArrayFunctionImplementation(object):
def test_one_arg(self):
@@ -293,12 +342,88 @@ class TestArrayFunctionImplementation(object):
def test_not_implemented(self):
MyArray, implements = _new_duck_type_and_implements()
- @array_function_dispatch(lambda array: (array,))
+ @array_function_dispatch(lambda array: (array,), module='my')
def func(array):
return array
array = np.array(1)
assert_(func(array) is array)
+ assert_equal(func.__module__, 'my')
- with assert_raises_regex(TypeError, 'no implementation found'):
+ with assert_raises_regex(
+ TypeError, "no implementation found for 'my.func'"):
func(MyArray())
+
+
+class TestNDArrayMethods(object):
+
+ def test_repr(self):
+ # gh-12162: should still be defined even if __array_function__ doesn't
+ # implement np.array_repr()
+
+ class MyArray(np.ndarray):
+ def __array_function__(*args, **kwargs):
+ return NotImplemented
+
+ array = np.array(1).view(MyArray)
+ assert_equal(repr(array), 'MyArray(1)')
+ assert_equal(str(array), '1')
+
+
+class TestNumPyFunctions(object):
+
+ def test_set_module(self):
+ assert_equal(np.sum.__module__, 'numpy')
+ assert_equal(np.char.equal.__module__, 'numpy.char')
+ assert_equal(np.fft.fft.__module__, 'numpy.fft')
+ assert_equal(np.linalg.solve.__module__, 'numpy.linalg')
+
+ def test_inspect_sum(self):
+ signature = inspect.signature(np.sum)
+ assert_('axis' in signature.parameters)
+
+ @requires_array_function
+ def test_override_sum(self):
+ MyArray, implements = _new_duck_type_and_implements()
+
+ @implements(np.sum)
+ def _(array):
+ return 'yes'
+
+ assert_equal(np.sum(MyArray()), 'yes')
+
+ @requires_array_function
+ def test_sum_on_mock_array(self):
+
+ # We need a proxy for mocks because __array_function__ is only looked
+ # up in the class dict
+ class ArrayProxy:
+ def __init__(self, value):
+ self.value = value
+ def __array_function__(self, *args, **kwargs):
+ return self.value.__array_function__(*args, **kwargs)
+ def __array__(self, *args, **kwargs):
+ return self.value.__array__(*args, **kwargs)
+
+ proxy = ArrayProxy(mock.Mock(spec=ArrayProxy))
+ proxy.value.__array_function__.return_value = 1
+ result = np.sum(proxy)
+ assert_equal(result, 1)
+ proxy.value.__array_function__.assert_called_once_with(
+ np.sum, (ArrayProxy,), (proxy,), {})
+ proxy.value.__array__.assert_not_called()
+
+ @requires_array_function
+ def test_sum_forwarding_implementation(self):
+
+ class MyArray(np.ndarray):
+
+ def sum(self, axis, out):
+ return 'summed'
+
+ def __array_function__(self, func, types, args, kwargs):
+ return super().__array_function__(func, types, args, kwargs)
+
+ # note: the internal implementation of np.sum() calls the .sum() method
+ array = np.array(1).view(MyArray)
+ assert_equal(np.sum(array), 'summed')
diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py
index 056d39db8..c1b794145 100644
--- a/numpy/core/tests/test_records.py
+++ b/numpy/core/tests/test_records.py
@@ -7,17 +7,17 @@ try:
import collections.abc as collections_abc
except ImportError:
import collections as collections_abc
-import pickle
-import warnings
import textwrap
from os import path
import pytest
import numpy as np
+from numpy.compat import Path
from numpy.testing import (
assert_, assert_equal, assert_array_equal, assert_array_almost_equal,
- assert_raises, assert_warns
+ assert_raises, temppath
)
+from numpy.compat import pickle
class TestFromrecords(object):
@@ -325,6 +325,23 @@ class TestFromrecords(object):
assert_equal(rec['f1'], [b'', b'', b''])
+@pytest.mark.skipif(Path is None, reason="No pathlib.Path")
+class TestPathUsage(object):
+ # Test that pathlib.Path can be used
+ def test_tofile_fromfile(self):
+ with temppath(suffix='.bin') as path:
+ path = Path(path)
+ np.random.seed(123)
+ a = np.random.rand(10).astype('f8,i4,a5')
+ a[5] = (0.5,10,'abcde')
+ with path.open("wb") as fd:
+ a.tofile(fd)
+ x = np.core.records.fromfile(path,
+ formats='f8,i4,a5',
+ shape=10)
+ assert_array_equal(x, a)
+
+
class TestRecord(object):
def setup(self):
self.data = np.rec.fromrecords([(1, 2, 3), (4, 5, 6)],
@@ -361,7 +378,6 @@ class TestRecord(object):
with assert_raises(ValueError):
r.setfield([2,3], *r.dtype.fields['f'])
- @pytest.mark.xfail(reason="See gh-10411, becomes real error in 1.16")
def test_out_of_order_fields(self):
# names in the same order, padding added to descr
x = self.data[['col1', 'col2']]
@@ -419,7 +435,56 @@ class TestRecord(object):
def test_missing_field(self):
# https://github.com/numpy/numpy/issues/4806
arr = np.zeros((3,), dtype=[('x', int), ('y', int)])
- assert_raises(ValueError, lambda: arr[['nofield']])
+ assert_raises(KeyError, lambda: arr[['nofield']])
+
+ def test_fromarrays_nested_structured_arrays(self):
+ arrays = [
+ np.arange(10),
+ np.ones(10, dtype=[('a', '<u2'), ('b', '<f4')]),
+ ]
+ arr = np.rec.fromarrays(arrays) # ValueError?
+
+ @pytest.mark.parametrize('nfields', [0, 1, 2])
+ def test_assign_dtype_attribute(self, nfields):
+ dt = np.dtype([('a', np.uint8), ('b', np.uint8), ('c', np.uint8)][:nfields])
+ data = np.zeros(3, dt).view(np.recarray)
+
+ # the original and resulting dtypes differ on whether they are records
+ assert data.dtype.type == np.record
+ assert dt.type != np.record
+
+ # ensure that the dtype remains a record even when assigned
+ data.dtype = dt
+ assert data.dtype.type == np.record
+
+ @pytest.mark.parametrize('nfields', [0, 1, 2])
+ def test_nested_fields_are_records(self, nfields):
+ """ Test that nested structured types are treated as records too """
+ dt = np.dtype([('a', np.uint8), ('b', np.uint8), ('c', np.uint8)][:nfields])
+ dt_outer = np.dtype([('inner', dt)])
+
+ data = np.zeros(3, dt_outer).view(np.recarray)
+ assert isinstance(data, np.recarray)
+ assert isinstance(data['inner'], np.recarray)
+
+ data0 = data[0]
+ assert isinstance(data0, np.record)
+ assert isinstance(data0['inner'], np.record)
+
+ def test_nested_dtype_padding(self):
+ """ test that trailing padding is preserved """
+ # construct a dtype with padding at the end
+ dt = np.dtype([('a', np.uint8), ('b', np.uint8), ('c', np.uint8)])
+ dt_padded_end = dt[['a', 'b']]
+ assert dt_padded_end.itemsize == dt.itemsize
+
+ dt_outer = np.dtype([('inner', dt_padded_end)])
+
+ data = np.zeros(3, dt_outer).view(np.recarray)
+ assert_equal(data['inner'].dtype, dt_padded_end)
+
+ data0 = data[0]
+ assert_equal(data0['inner'].dtype, dt_padded_end)
def test_find_duplicate():
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
index 947ee5f86..9dc231deb 100644
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -1,11 +1,8 @@
from __future__ import division, absolute_import, print_function
import copy
-import pickle
import sys
-import platform
import gc
-import warnings
import tempfile
import pytest
from os import path
@@ -19,7 +16,7 @@ from numpy.testing import (
assert_raises_regex, assert_warns, suppress_warnings,
_assert_valid_refcount, HAS_REFCOUNT,
)
-from numpy.compat import asbytes, asunicode, long
+from numpy.compat import asbytes, asunicode, long, pickle
try:
RecursionError
@@ -39,15 +36,16 @@ class TestRegression(object):
def test_pickle_transposed(self):
# Ticket #16
a = np.transpose(np.array([[2, 9], [7, 0], [3, 8]]))
- f = BytesIO()
- pickle.dump(a, f)
- f.seek(0)
- b = pickle.load(f)
- f.close()
- assert_array_equal(a, b)
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ f = BytesIO()
+ pickle.dump(a, f, protocol=proto)
+ f.seek(0)
+ b = pickle.load(f)
+ f.close()
+ assert_array_equal(a, b)
def test_typeNA(self):
- # Issue gh-515
+ # Issue gh-515
with suppress_warnings() as sup:
sup.filter(np.VisibleDeprecationWarning)
assert_equal(np.typeNA[np.int64], 'Int64')
@@ -95,12 +93,13 @@ class TestRegression(object):
def test_char_dump(self):
# Ticket #50
- f = BytesIO()
ca = np.char.array(np.arange(1000, 1010), itemsize=4)
- ca.dump(f)
- f.seek(0)
- ca = np.load(f)
- f.close()
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ f = BytesIO()
+ pickle.dump(ca, f, protocol=proto)
+ f.seek(0)
+ ca = np.load(f, allow_pickle=True)
+ f.close()
def test_noncontiguous_fill(self):
# Ticket #58.
@@ -359,12 +358,13 @@ class TestRegression(object):
def test_unpickle_dtype_with_object(self):
# Implemented in r2840
dt = np.dtype([('x', int), ('y', np.object_), ('z', 'O')])
- f = BytesIO()
- pickle.dump(dt, f)
- f.seek(0)
- dt_ = pickle.load(f)
- f.close()
- assert_equal(dt, dt_)
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ f = BytesIO()
+ pickle.dump(dt, f, protocol=proto)
+ f.seek(0)
+ dt_ = pickle.load(f)
+ f.close()
+ assert_equal(dt, dt_)
def test_mem_array_creation_invalid_specification(self):
# Ticket #196
@@ -436,6 +436,32 @@ class TestRegression(object):
assert_raises(KeyError, np.lexsort, BuggySequence())
+ def test_lexsort_zerolen_custom_strides(self):
+ # Ticket #14228
+ xs = np.array([], dtype='i8')
+ assert xs.strides == (8,)
+ assert np.lexsort((xs,)).shape[0] == 0 # Works
+
+ xs.strides = (16,)
+ assert np.lexsort((xs,)).shape[0] == 0 # Was: MemoryError
+
+ def test_lexsort_zerolen_custom_strides_2d(self):
+ xs = np.array([], dtype='i8')
+
+ xs.shape = (0, 2)
+ xs.strides = (16, 16)
+ assert np.lexsort((xs,), axis=0).shape[0] == 0
+
+ xs.shape = (2, 0)
+ xs.strides = (16, 16)
+ assert np.lexsort((xs,), axis=0).shape[0] == 2
+
+ def test_lexsort_zerolen_element(self):
+ dt = np.dtype([]) # a void dtype with no fields
+ xs = np.empty(4, dt)
+
+ assert np.lexsort((xs,)).shape[0] == xs.shape[0]
+
def test_pickle_py2_bytes_encoding(self):
# Check that arrays and scalars pickled on Py2 are
# unpickleable on Py3 using encoding='bytes'
@@ -468,13 +494,14 @@ class TestRegression(object):
result = pickle.loads(data, encoding='bytes')
assert_equal(result, original)
- if isinstance(result, np.ndarray) and result.dtype.names:
+ if isinstance(result, np.ndarray) and result.dtype.names is not None:
for name in result.dtype.names:
assert_(isinstance(name, str))
def test_pickle_dtype(self):
# Ticket #251
- pickle.dumps(float)
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ pickle.dumps(float, protocol=proto)
def test_swap_real(self):
# Ticket #265
@@ -1512,7 +1539,8 @@ class TestRegression(object):
def test_fromstring_crash(self):
# Ticket #1345: the following should not cause a crash
- np.fromstring(b'aa, aa, 1.0', sep=',')
+ with assert_warns(DeprecationWarning):
+ np.fromstring(b'aa, aa, 1.0', sep=',')
def test_ticket_1539(self):
dtypes = [x for x in np.typeDict.values()
@@ -1553,10 +1581,7 @@ class TestRegression(object):
def test_complex_nan_maximum(self):
cnan = complex(0, np.nan)
- with suppress_warnings() as sup:
- sup.record(RuntimeWarning)
- assert_equal(np.maximum(1, cnan), cnan)
- assert_equal(len(sup.log), 1)
+ assert_equal(np.maximum(1, cnan), cnan)
def test_subclass_int_tuple_assignment(self):
# ticket #1563
@@ -2408,11 +2433,81 @@ class TestRegression(object):
t = np.dtype([((s, 'f1'), np.float64)])
data = np.zeros(10, t)
for i in range(10):
- v = str(data[['f1']])
+ str(data[['f1']])
if HAS_REFCOUNT:
assert_(base <= sys.getrefcount(s))
+ @pytest.mark.parametrize('val', [
+ # arrays and scalars
+ np.ones((10, 10), dtype='int32'),
+ np.uint64(10),
+ ])
+ @pytest.mark.parametrize('protocol',
+ range(2, pickle.HIGHEST_PROTOCOL + 1)
+ )
+ def test_pickle_module(self, protocol, val):
+ # gh-12837
+ s = pickle.dumps(val, protocol)
+ assert b'_multiarray_umath' not in s
+ if protocol == 5 and len(val.shape) > 0:
+ # unpickling ndarray goes through _frombuffer for protocol 5
+ assert b'numpy.core.numeric' in s
+ else:
+ assert b'numpy.core.multiarray' in s
+
def test_object_casting_errors(self):
# gh-11993
arr = np.array(['AAAAA', 18465886.0, 18465886.0], dtype=object)
assert_raises(TypeError, arr.astype, 'c8')
+
+ def test_eff1d_casting(self):
+ # gh-12711
+ x = np.array([1, 2, 4, 7, 0], dtype=np.int16)
+ res = np.ediff1d(x, to_begin=-99, to_end=np.array([88, 99]))
+ assert_equal(res, [-99, 1, 2, 3, -7, 88, 99])
+ assert_raises(ValueError, np.ediff1d, x, to_begin=(1<<20))
+ assert_raises(ValueError, np.ediff1d, x, to_end=(1<<20))
+
+ def test_pickle_datetime64_array(self):
+ # gh-12745 (would fail with pickle5 installed)
+ d = np.datetime64('2015-07-04 12:59:59.50', 'ns')
+ arr = np.array([d])
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ dumped = pickle.dumps(arr, protocol=proto)
+ assert_equal(pickle.loads(dumped), arr)
+
+ def test_bad_array_interface(self):
+ class T(object):
+ __array_interface__ = {}
+
+ np.array([T()])
+
+ def test_2d__array__shape(self):
+ class T(object):
+ def __array__(self):
+ return np.ndarray(shape=(0,0))
+
+ # Make sure __array__ is used instead of Sequence methods.
+ def __iter__(self):
+ return iter([])
+
+ def __getitem__(self, idx):
+ raise AssertionError("__getitem__ was called")
+
+ def __len__(self):
+ return 0
+
+
+ t = T()
+ #gh-13659, would raise in broadcasting [x=t for x in result]
+ np.array([t])
+
+ @pytest.mark.skipif(sys.maxsize < 2 ** 31 + 1, reason='overflows 32-bit python')
+ @pytest.mark.skipif(sys.platform == 'win32' and sys.version_info[:2] < (3, 8),
+ reason='overflows on windows, fixed in bpo-16865')
+ def test_to_ctypes(self):
+ #gh-14214
+ arr = np.zeros((2 ** 31 + 1,), 'b')
+ assert arr.size * arr.itemsize > 2 ** 31
+ c_arr = np.ctypeslib.as_ctypes(arr)
+ assert_equal(c_arr._length_, arr.size)
diff --git a/numpy/core/tests/test_scalar_methods.py b/numpy/core/tests/test_scalar_methods.py
new file mode 100644
index 000000000..93434dd1b
--- /dev/null
+++ b/numpy/core/tests/test_scalar_methods.py
@@ -0,0 +1,109 @@
+"""
+Test the scalar constructors, which also do type-coercion
+"""
+from __future__ import division, absolute_import, print_function
+
+import os
+import fractions
+import platform
+
+import pytest
+import numpy as np
+
+from numpy.testing import (
+ run_module_suite,
+ assert_equal, assert_almost_equal, assert_raises, assert_warns,
+ dec
+)
+
+class TestAsIntegerRatio(object):
+ # derived in part from the cpython test "test_floatasratio"
+
+ @pytest.mark.parametrize("ftype", [
+ np.half, np.single, np.double, np.longdouble])
+ @pytest.mark.parametrize("f, ratio", [
+ (0.875, (7, 8)),
+ (-0.875, (-7, 8)),
+ (0.0, (0, 1)),
+ (11.5, (23, 2)),
+ ])
+ def test_small(self, ftype, f, ratio):
+ assert_equal(ftype(f).as_integer_ratio(), ratio)
+
+ @pytest.mark.parametrize("ftype", [
+ np.half, np.single, np.double, np.longdouble])
+ def test_simple_fractions(self, ftype):
+ R = fractions.Fraction
+ assert_equal(R(0, 1),
+ R(*ftype(0.0).as_integer_ratio()))
+ assert_equal(R(5, 2),
+ R(*ftype(2.5).as_integer_ratio()))
+ assert_equal(R(1, 2),
+ R(*ftype(0.5).as_integer_ratio()))
+ assert_equal(R(-2100, 1),
+ R(*ftype(-2100.0).as_integer_ratio()))
+
+ @pytest.mark.parametrize("ftype", [
+ np.half, np.single, np.double, np.longdouble])
+ def test_errors(self, ftype):
+ assert_raises(OverflowError, ftype('inf').as_integer_ratio)
+ assert_raises(OverflowError, ftype('-inf').as_integer_ratio)
+ assert_raises(ValueError, ftype('nan').as_integer_ratio)
+
+ def test_against_known_values(self):
+ R = fractions.Fraction
+ assert_equal(R(1075, 512),
+ R(*np.half(2.1).as_integer_ratio()))
+ assert_equal(R(-1075, 512),
+ R(*np.half(-2.1).as_integer_ratio()))
+ assert_equal(R(4404019, 2097152),
+ R(*np.single(2.1).as_integer_ratio()))
+ assert_equal(R(-4404019, 2097152),
+ R(*np.single(-2.1).as_integer_ratio()))
+ assert_equal(R(4728779608739021, 2251799813685248),
+ R(*np.double(2.1).as_integer_ratio()))
+ assert_equal(R(-4728779608739021, 2251799813685248),
+ R(*np.double(-2.1).as_integer_ratio()))
+ # longdouble is platform dependent
+
+ @pytest.mark.parametrize("ftype, frac_vals, exp_vals", [
+ # dtype test cases generated using hypothesis
+ # first five generated cases per dtype
+ (np.half, [0.0, 0.01154830649280303, 0.31082276347447274,
+ 0.527350517124794, 0.8308562335072596],
+ [0, 1, 0, -8, 12]),
+ (np.single, [0.0, 0.09248576989263226, 0.8160498218131407,
+ 0.17389442853722373, 0.7956044195067877],
+ [0, 12, 10, 17, -26]),
+ (np.double, [0.0, 0.031066908499895136, 0.5214135908877832,
+ 0.45780736035689296, 0.5906586745934036],
+ [0, -801, 51, 194, -653]),
+ pytest.param(
+ np.longdouble,
+ [0.0, 0.20492557202724854, 0.4277180662199366, 0.9888085019891495,
+ 0.9620175814461964],
+ [0, -7400, 14266, -7822, -8721],
+ marks=[
+ pytest.mark.skipif(
+ np.finfo(np.double) == np.finfo(np.longdouble),
+ reason="long double is same as double"),
+ pytest.mark.skipif(
+ platform.machine().startswith("ppc"),
+ reason="IBM double double"),
+ ]
+ )
+ ])
+ def test_roundtrip(self, ftype, frac_vals, exp_vals):
+ for frac, exp in zip(frac_vals, exp_vals):
+ f = np.ldexp(frac, exp, dtype=ftype)
+ n, d = f.as_integer_ratio()
+
+ try:
+ # workaround for gh-9968
+ nf = np.longdouble(str(n))
+ df = np.longdouble(str(d))
+ except (OverflowError, RuntimeWarning):
+ # the values may not fit in any float type
+ pytest.skip("longdouble too small on this platform")
+
+ assert_equal(nf / df, f, "{}/{}".format(n, d))
diff --git a/numpy/core/tests/test_scalarbuffer.py b/numpy/core/tests/test_scalarbuffer.py
index cd520d99b..3ded7eecd 100644
--- a/numpy/core/tests/test_scalarbuffer.py
+++ b/numpy/core/tests/test_scalarbuffer.py
@@ -65,7 +65,7 @@ class TestScalarPEP3118(object):
assert_(isinstance(x, np.void))
mv_x = memoryview(x)
expected_size = 16 * np.dtype((np.unicode_, 1)).itemsize
- expected_size += 2 * np.dtype((np.float64, 1)).itemsize
+ expected_size += 2 * np.dtype(np.float64).itemsize
assert_equal(mv_x.itemsize, expected_size)
assert_equal(mv_x.ndim, 0)
assert_equal(mv_x.shape, ())
diff --git a/numpy/core/tests/test_scalarinherit.py b/numpy/core/tests/test_scalarinherit.py
index 28436f6c7..9e32cf624 100644
--- a/numpy/core/tests/test_scalarinherit.py
+++ b/numpy/core/tests/test_scalarinherit.py
@@ -69,6 +69,7 @@ class TestCharacter(object):
np_s = np.string_('abc')
np_u = np.unicode_('abc')
np_i = np.int(5)
- res_np = np_s * np_i
res_s = b'abc' * 5
- assert_(res_np == res_s)
+ res_u = u'abc' * 5
+ assert_(np_s * np_i == res_s)
+ assert_(np_u * np_i == res_u)
diff --git a/numpy/core/tests/test_scalarmath.py b/numpy/core/tests/test_scalarmath.py
index a55f06b69..854df5590 100644
--- a/numpy/core/tests/test_scalarmath.py
+++ b/numpy/core/tests/test_scalarmath.py
@@ -9,7 +9,7 @@ import pytest
import numpy as np
from numpy.testing import (
- assert_, assert_equal, assert_raises, assert_almost_equal, assert_allclose,
+ assert_, assert_equal, assert_raises, assert_almost_equal,
assert_array_equal, IS_PYPY, suppress_warnings, _gen_alignment_data,
assert_warns
)
@@ -184,7 +184,7 @@ class TestPower(object):
a = 5
b = 4
c = 10
- expected = pow(a, b, c)
+ expected = pow(a, b, c) # noqa: F841
for t in (np.int32, np.float32, np.complex64):
# note that 3-operand power only dispatches on the first argument
assert_raises(TypeError, operator.pow, t(a), b, c)
@@ -422,7 +422,7 @@ class TestConversion(object):
@pytest.mark.skipif(np.finfo(np.double) == np.finfo(np.longdouble),
reason="long double is same as double")
- @pytest.mark.skipif(platform.machine().startswith("ppc64"),
+ @pytest.mark.skipif(platform.machine().startswith("ppc"),
reason="IBM double double")
def test_int_from_huge_longdouble(self):
# Produce a longdouble that would overflow a double,
@@ -565,10 +565,10 @@ class TestMultiply(object):
# Some of this behaviour may be controversial and could be open for
# change.
accepted_types = set(np.typecodes["AllInteger"])
- deprecated_types = set('?')
+ deprecated_types = {'?'}
forbidden_types = (
set(np.typecodes["All"]) - accepted_types - deprecated_types)
- forbidden_types -= set('V') # can't default-construct void scalars
+ forbidden_types -= {'V'} # can't default-construct void scalars
for seq_type in (list, tuple):
seq = seq_type([1, 2, 3])
@@ -664,3 +664,31 @@ class TestAbs(object):
def test_numpy_abs(self):
self._test_abs_func(np.abs)
+
+
+class TestBitShifts(object):
+
+ @pytest.mark.parametrize('type_code', np.typecodes['AllInteger'])
+ @pytest.mark.parametrize('op',
+ [operator.rshift, operator.lshift], ids=['>>', '<<'])
+ def test_shift_all_bits(self, type_code, op):
+ """ Shifts where the shift amount is the width of the type or wider """
+ # gh-2449
+ dt = np.dtype(type_code)
+ nbits = dt.itemsize * 8
+ for val in [5, -5]:
+ for shift in [nbits, nbits + 4]:
+ val_scl = dt.type(val)
+ shift_scl = dt.type(shift)
+ res_scl = op(val_scl, shift_scl)
+ if val_scl < 0 and op is operator.rshift:
+ # sign bit is preserved
+ assert_equal(res_scl, -1)
+ else:
+ assert_equal(res_scl, 0)
+
+ # Result on scalars should be the same as on arrays
+ val_arr = np.array([val]*32, dtype=dt)
+ shift_arr = np.array([shift]*32, dtype=dt)
+ res_arr = op(val_arr, shift_arr)
+ assert_equal(res_arr, res_scl)
diff --git a/numpy/core/tests/test_scalarprint.py b/numpy/core/tests/test_scalarprint.py
index 472ff691d..86b0ca199 100644
--- a/numpy/core/tests/test_scalarprint.py
+++ b/numpy/core/tests/test_scalarprint.py
@@ -10,7 +10,7 @@ import pytest
from tempfile import TemporaryFile
import numpy as np
-from numpy.testing import assert_, assert_equal, suppress_warnings, dec
+from numpy.testing import assert_, assert_equal, suppress_warnings
class TestRealScalars(object):
def test_str(self):
@@ -51,7 +51,7 @@ class TestRealScalars(object):
def test_py2_float_print(self):
# gh-10753
- # In python2, the python float type implements an obsolte method
+ # In python2, the python float type implements an obsolete method
# tp_print, which overrides tp_repr and tp_str when using "print" to
# output to a "real file" (ie, not a StringIO). Make sure we don't
# inherit it.
diff --git a/numpy/core/tests/test_shape_base.py b/numpy/core/tests/test_shape_base.py
index df819b73f..53d272fc5 100644
--- a/numpy/core/tests/test_shape_base.py
+++ b/numpy/core/tests/test_shape_base.py
@@ -1,14 +1,17 @@
from __future__ import division, absolute_import, print_function
-import warnings
+import pytest
+import sys
import numpy as np
from numpy.core import (
array, arange, atleast_1d, atleast_2d, atleast_3d, block, vstack, hstack,
newaxis, concatenate, stack
)
+from numpy.core.shape_base import (_block_dispatcher, _block_setup,
+ _block_concatenate, _block_slicing)
from numpy.testing import (
assert_, assert_raises, assert_array_equal, assert_equal,
- assert_raises_regex, assert_almost_equal
+ assert_raises_regex, assert_warns
)
from numpy.compat import long
@@ -153,6 +156,14 @@ class TestHstack(object):
desired = array([[1, 1], [2, 2]])
assert_array_equal(res, desired)
+ def test_generator(self):
+ with assert_warns(FutureWarning):
+ hstack((np.arange(3) for _ in range(2)))
+ if sys.version_info.major > 2:
+ # map returns a list on Python 2
+ with assert_warns(FutureWarning):
+ hstack(map(lambda x: x, np.ones((3, 2))))
+
class TestVstack(object):
def test_non_iterable(self):
@@ -189,6 +200,10 @@ class TestVstack(object):
desired = array([[1, 2], [1, 2]])
assert_array_equal(res, desired)
+ def test_generator(self):
+ with assert_warns(FutureWarning):
+ vstack((np.arange(3) for _ in range(2)))
+
class TestConcatenate(object):
def test_returns_copy(self):
@@ -209,13 +224,27 @@ class TestConcatenate(object):
assert_raises(ValueError, concatenate, (0,))
assert_raises(ValueError, concatenate, (np.array(0),))
+ # dimensionality must match
+ assert_raises_regex(
+ ValueError,
+ r"all the input arrays must have same number of dimensions, but "
+ r"the array at index 0 has 1 dimension\(s\) and the array at "
+ r"index 1 has 2 dimension\(s\)",
+ np.concatenate, (np.zeros(1), np.zeros((1, 1))))
+
# test shapes must match except for concatenation axis
a = np.ones((1, 2, 3))
b = np.ones((2, 2, 3))
axis = list(range(3))
for i in range(3):
np.concatenate((a, b), axis=axis[0]) # OK
- assert_raises(ValueError, np.concatenate, (a, b), axis=axis[1])
+ assert_raises_regex(
+ ValueError,
+ "all the input array dimensions for the concatenation axis "
+ "must match exactly, but along dimension {}, the array at "
+ "index 0 has size 1 and the array at index 1 has size 2"
+ .format(i),
+ np.concatenate, (a, b), axis=axis[1])
assert_raises(ValueError, np.concatenate, (a, b), axis=axis[2])
a = np.moveaxis(a, -1, 0)
b = np.moveaxis(b, -1, 0)
@@ -352,12 +381,16 @@ def test_stack():
arrays = [np.random.randn(3, 4) for _ in range(10)]
axes = [0, 1, 2, -1, -2, -3]
expected_shapes = [(10, 3, 4), (3, 10, 4), (3, 4, 10),
- (3, 4, 10), (3, 10, 4), (10, 3, 4)]
+ (3, 4, 10), (3, 10, 4), (10, 3, 4)]
for axis, expected_shape in zip(axes, expected_shapes):
assert_equal(np.stack(arrays, axis).shape, expected_shape)
# empty arrays
assert_(stack([[], [], []]).shape == (3, 0))
assert_(stack([[], [], []], axis=1).shape == (0, 3))
+ # out
+ out = np.zeros_like(r1)
+ np.stack((a, b), out=out)
+ assert_array_equal(out, r1)
# edge cases
assert_raises_regex(ValueError, 'need at least one array', stack, [])
assert_raises_regex(ValueError, 'must have the same shape',
@@ -370,16 +403,62 @@ def test_stack():
stack, [np.zeros((3, 3)), np.zeros(3)], axis=1)
assert_raises_regex(ValueError, 'must have the same shape',
stack, [np.arange(2), np.arange(3)])
+ # generator is deprecated
+ with assert_warns(FutureWarning):
+ result = stack((x for x in range(3)))
+ assert_array_equal(result, np.array([0, 1, 2]))
class TestBlock(object):
- def test_returns_copy(self):
+ @pytest.fixture(params=['block', 'force_concatenate', 'force_slicing'])
+ def block(self, request):
+ # blocking small arrays and large arrays go through different paths.
+ # the algorithm is triggered depending on the number of element
+ # copies required.
+ # We define a test fixture that forces most tests to go through
+ # both code paths.
+ # Ultimately, this should be removed if a single algorithm is found
+ # to be faster for both small and large arrays.
+ def _block_force_concatenate(arrays):
+ arrays, list_ndim, result_ndim, _ = _block_setup(arrays)
+ return _block_concatenate(arrays, list_ndim, result_ndim)
+
+ def _block_force_slicing(arrays):
+ arrays, list_ndim, result_ndim, _ = _block_setup(arrays)
+ return _block_slicing(arrays, list_ndim, result_ndim)
+
+ if request.param == 'force_concatenate':
+ return _block_force_concatenate
+ elif request.param == 'force_slicing':
+ return _block_force_slicing
+ elif request.param == 'block':
+ return block
+ else:
+ raise ValueError('Unknown blocking request. There is a typo in the tests.')
+
+ def test_returns_copy(self, block):
a = np.eye(3)
- b = np.block(a)
+ b = block(a)
b[0, 0] = 2
assert b[0, 0] != a[0, 0]
- def test_block_simple_row_wise(self):
+ def test_block_total_size_estimate(self, block):
+ _, _, _, total_size = _block_setup([1])
+ assert total_size == 1
+
+ _, _, _, total_size = _block_setup([[1]])
+ assert total_size == 1
+
+ _, _, _, total_size = _block_setup([[1, 1]])
+ assert total_size == 2
+
+ _, _, _, total_size = _block_setup([[1], [1]])
+ assert total_size == 2
+
+ _, _, _, total_size = _block_setup([[1, 2], [3, 4]])
+ assert total_size == 4
+
+ def test_block_simple_row_wise(self, block):
a_2d = np.ones((2, 2))
b_2d = 2 * a_2d
desired = np.array([[1, 1, 2, 2],
@@ -387,7 +466,7 @@ class TestBlock(object):
result = block([a_2d, b_2d])
assert_equal(desired, result)
- def test_block_simple_column_wise(self):
+ def test_block_simple_column_wise(self, block):
a_2d = np.ones((2, 2))
b_2d = 2 * a_2d
expected = np.array([[1, 1],
@@ -397,7 +476,7 @@ class TestBlock(object):
result = block([[a_2d], [b_2d]])
assert_equal(expected, result)
- def test_block_with_1d_arrays_row_wise(self):
+ def test_block_with_1d_arrays_row_wise(self, block):
# # # 1-D vectors are treated as row arrays
a = np.array([1, 2, 3])
b = np.array([2, 3, 4])
@@ -405,7 +484,7 @@ class TestBlock(object):
result = block([a, b])
assert_equal(expected, result)
- def test_block_with_1d_arrays_multiple_rows(self):
+ def test_block_with_1d_arrays_multiple_rows(self, block):
a = np.array([1, 2, 3])
b = np.array([2, 3, 4])
expected = np.array([[1, 2, 3, 2, 3, 4],
@@ -413,7 +492,7 @@ class TestBlock(object):
result = block([[a, b], [a, b]])
assert_equal(expected, result)
- def test_block_with_1d_arrays_column_wise(self):
+ def test_block_with_1d_arrays_column_wise(self, block):
# # # 1-D vectors are treated as row arrays
a_1d = np.array([1, 2, 3])
b_1d = np.array([2, 3, 4])
@@ -422,7 +501,7 @@ class TestBlock(object):
result = block([[a_1d], [b_1d]])
assert_equal(expected, result)
- def test_block_mixed_1d_and_2d(self):
+ def test_block_mixed_1d_and_2d(self, block):
a_2d = np.ones((2, 2))
b_1d = np.array([2, 2])
result = block([[a_2d], [b_1d]])
@@ -431,7 +510,7 @@ class TestBlock(object):
[2, 2]])
assert_equal(expected, result)
- def test_block_complicated(self):
+ def test_block_complicated(self, block):
# a bit more complicated
one_2d = np.array([[1, 1, 1]])
two_2d = np.array([[2, 2, 2]])
@@ -455,7 +534,7 @@ class TestBlock(object):
[zero_2d]])
assert_equal(result, expected)
- def test_nested(self):
+ def test_nested(self, block):
one = np.array([1, 1, 1])
two = np.array([[2, 2, 2], [2, 2, 2], [2, 2, 2]])
three = np.array([3, 3, 3])
@@ -464,9 +543,9 @@ class TestBlock(object):
six = np.array([6, 6, 6, 6, 6])
zero = np.zeros((2, 6))
- result = np.block([
+ result = block([
[
- np.block([
+ block([
[one],
[three],
[four]
@@ -485,7 +564,7 @@ class TestBlock(object):
assert_equal(result, expected)
- def test_3d(self):
+ def test_3d(self, block):
a000 = np.ones((2, 2, 2), int) * 1
a100 = np.ones((3, 2, 2), int) * 2
@@ -498,7 +577,7 @@ class TestBlock(object):
a111 = np.ones((3, 3, 3), int) * 8
- result = np.block([
+ result = block([
[
[a000, a001],
[a010, a011],
@@ -540,55 +619,102 @@ class TestBlock(object):
assert_array_equal(result, expected)
- def test_block_with_mismatched_shape(self):
+ def test_block_with_mismatched_shape(self, block):
a = np.array([0, 0])
b = np.eye(2)
- assert_raises(ValueError, np.block, [a, b])
- assert_raises(ValueError, np.block, [b, a])
+ assert_raises(ValueError, block, [a, b])
+ assert_raises(ValueError, block, [b, a])
- def test_no_lists(self):
- assert_equal(np.block(1), np.array(1))
- assert_equal(np.block(np.eye(3)), np.eye(3))
+ to_block = [[np.ones((2,3)), np.ones((2,2))],
+ [np.ones((2,2)), np.ones((2,2))]]
+ assert_raises(ValueError, block, to_block)
+ def test_no_lists(self, block):
+ assert_equal(block(1), np.array(1))
+ assert_equal(block(np.eye(3)), np.eye(3))
- def test_invalid_nesting(self):
+ def test_invalid_nesting(self, block):
msg = 'depths are mismatched'
- assert_raises_regex(ValueError, msg, np.block, [1, [2]])
- assert_raises_regex(ValueError, msg, np.block, [1, []])
- assert_raises_regex(ValueError, msg, np.block, [[1], 2])
- assert_raises_regex(ValueError, msg, np.block, [[], 2])
- assert_raises_regex(ValueError, msg, np.block, [
+ assert_raises_regex(ValueError, msg, block, [1, [2]])
+ assert_raises_regex(ValueError, msg, block, [1, []])
+ assert_raises_regex(ValueError, msg, block, [[1], 2])
+ assert_raises_regex(ValueError, msg, block, [[], 2])
+ assert_raises_regex(ValueError, msg, block, [
[[1], [2]],
[[3, 4]],
[5] # missing brackets
])
- def test_empty_lists(self):
- assert_raises_regex(ValueError, 'empty', np.block, [])
- assert_raises_regex(ValueError, 'empty', np.block, [[]])
- assert_raises_regex(ValueError, 'empty', np.block, [[1], []])
+ def test_empty_lists(self, block):
+ assert_raises_regex(ValueError, 'empty', block, [])
+ assert_raises_regex(ValueError, 'empty', block, [[]])
+ assert_raises_regex(ValueError, 'empty', block, [[1], []])
- def test_tuple(self):
- assert_raises_regex(TypeError, 'tuple', np.block, ([1, 2], [3, 4]))
- assert_raises_regex(TypeError, 'tuple', np.block, [(1, 2), (3, 4)])
+ def test_tuple(self, block):
+ assert_raises_regex(TypeError, 'tuple', block, ([1, 2], [3, 4]))
+ assert_raises_regex(TypeError, 'tuple', block, [(1, 2), (3, 4)])
- def test_different_ndims(self):
+ def test_different_ndims(self, block):
a = 1.
b = 2 * np.ones((1, 2))
c = 3 * np.ones((1, 1, 3))
- result = np.block([a, b, c])
+ result = block([a, b, c])
expected = np.array([[[1., 2., 2., 3., 3., 3.]]])
assert_equal(result, expected)
- def test_different_ndims_depths(self):
+ def test_different_ndims_depths(self, block):
a = 1.
b = 2 * np.ones((1, 2))
c = 3 * np.ones((1, 2, 3))
- result = np.block([[a, b], [c]])
+ result = block([[a, b], [c]])
expected = np.array([[[1., 2., 2.],
[3., 3., 3.],
[3., 3., 3.]]])
assert_equal(result, expected)
+
+ def test_block_memory_order(self, block):
+ # 3D
+ arr_c = np.zeros((3,)*3, order='C')
+ arr_f = np.zeros((3,)*3, order='F')
+
+ b_c = [[[arr_c, arr_c],
+ [arr_c, arr_c]],
+ [[arr_c, arr_c],
+ [arr_c, arr_c]]]
+
+ b_f = [[[arr_f, arr_f],
+ [arr_f, arr_f]],
+ [[arr_f, arr_f],
+ [arr_f, arr_f]]]
+
+ assert block(b_c).flags['C_CONTIGUOUS']
+ assert block(b_f).flags['F_CONTIGUOUS']
+
+ arr_c = np.zeros((3, 3), order='C')
+ arr_f = np.zeros((3, 3), order='F')
+ # 2D
+ b_c = [[arr_c, arr_c],
+ [arr_c, arr_c]]
+
+ b_f = [[arr_f, arr_f],
+ [arr_f, arr_f]]
+
+ assert block(b_c).flags['C_CONTIGUOUS']
+ assert block(b_f).flags['F_CONTIGUOUS']
+
+
+def test_block_dispatcher():
+ class ArrayLike(object):
+ pass
+ a = ArrayLike()
+ b = ArrayLike()
+ c = ArrayLike()
+ assert_equal(list(_block_dispatcher(a)), [a])
+ assert_equal(list(_block_dispatcher([a])), [a])
+ assert_equal(list(_block_dispatcher([a, b])), [a, b])
+ assert_equal(list(_block_dispatcher([[a], [b, [c]]])), [a, b, c])
+ # don't recurse into non-lists
+ assert_equal(list(_block_dispatcher((a, b))), [(a, b)])
diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py
index 85d9f41bd..707c690dd 100644
--- a/numpy/core/tests/test_ufunc.py
+++ b/numpy/core/tests/test_ufunc.py
@@ -3,6 +3,8 @@ from __future__ import division, absolute_import, print_function
import warnings
import itertools
+import pytest
+
import numpy as np
import numpy.core._umath_tests as umt
import numpy.linalg._umath_linalg as uml
@@ -13,6 +15,7 @@ from numpy.testing import (
assert_almost_equal, assert_array_almost_equal, assert_no_warnings,
assert_allclose,
)
+from numpy.compat import pickle
class TestUfuncKwargs(object):
@@ -41,18 +44,116 @@ class TestUfuncKwargs(object):
assert_raises(TypeError, np.add, 1, 2, extobj=[4096], parrot=True)
+class TestUfuncGenericLoops(object):
+ """Test generic loops.
+
+ The loops to be tested are:
+
+ PyUFunc_ff_f_As_dd_d
+ PyUFunc_ff_f
+ PyUFunc_dd_d
+ PyUFunc_gg_g
+ PyUFunc_FF_F_As_DD_D
+ PyUFunc_DD_D
+ PyUFunc_FF_F
+ PyUFunc_GG_G
+ PyUFunc_OO_O
+ PyUFunc_OO_O_method
+ PyUFunc_f_f_As_d_d
+ PyUFunc_d_d
+ PyUFunc_f_f
+ PyUFunc_g_g
+ PyUFunc_F_F_As_D_D
+ PyUFunc_F_F
+ PyUFunc_D_D
+ PyUFunc_G_G
+ PyUFunc_O_O
+ PyUFunc_O_O_method
+ PyUFunc_On_Om
+
+ Where:
+
+ f -- float
+ d -- double
+ g -- long double
+ F -- complex float
+ D -- complex double
+ G -- complex long double
+ O -- python object
+
+ It is difficult to assure that each of these loops is entered from the
+ Python level as the special cased loops are a moving target and the
+ corresponding types are architecture dependent. We probably need to
+ define C level testing ufuncs to get at them. For the time being, I've
+ just looked at the signatures registered in the build directory to find
+ relevant functions.
+
+ """
+ np_dtypes = [
+ (np.single, np.single), (np.single, np.double),
+ (np.csingle, np.csingle), (np.csingle, np.cdouble),
+ (np.double, np.double), (np.longdouble, np.longdouble),
+ (np.cdouble, np.cdouble), (np.clongdouble, np.clongdouble)]
+
+ @pytest.mark.parametrize('input_dtype,output_dtype', np_dtypes)
+ def test_unary_PyUFunc(self, input_dtype, output_dtype, f=np.exp, x=0, y=1):
+ xs = np.full(10, input_dtype(x), dtype=output_dtype)
+ ys = f(xs)[::2]
+ assert_allclose(ys, y)
+ assert_equal(ys.dtype, output_dtype)
+
+ def f2(x, y):
+ return x**y
+
+ @pytest.mark.parametrize('input_dtype,output_dtype', np_dtypes)
+ def test_binary_PyUFunc(self, input_dtype, output_dtype, f=f2, x=0, y=1):
+ xs = np.full(10, input_dtype(x), dtype=output_dtype)
+ ys = f(xs, xs)[::2]
+ assert_allclose(ys, y)
+ assert_equal(ys.dtype, output_dtype)
+
+ # class to use in testing object method loops
+ class foo(object):
+ def conjugate(self):
+ return np.bool_(1)
+
+ def logical_xor(self, obj):
+ return np.bool_(1)
+
+ def test_unary_PyUFunc_O_O(self):
+ x = np.ones(10, dtype=object)
+ assert_(np.all(np.abs(x) == 1))
+
+ def test_unary_PyUFunc_O_O_method(self, foo=foo):
+ x = np.full(10, foo(), dtype=object)
+ assert_(np.all(np.conjugate(x) == True))
+
+ def test_binary_PyUFunc_OO_O(self):
+ x = np.ones(10, dtype=object)
+ assert_(np.all(np.add(x, x) == 2))
+
+ def test_binary_PyUFunc_OO_O_method(self, foo=foo):
+ x = np.full(10, foo(), dtype=object)
+ assert_(np.all(np.logical_xor(x, x)))
+
+ def test_binary_PyUFunc_On_Om_method(self, foo=foo):
+ x = np.full((10, 2, 3), foo(), dtype=object)
+ assert_(np.all(np.logical_xor(x, x)))
+
+
class TestUfunc(object):
def test_pickle(self):
- import pickle
- assert_(pickle.loads(pickle.dumps(np.sin)) is np.sin)
+ for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
+ assert_(pickle.loads(pickle.dumps(np.sin,
+ protocol=proto)) is np.sin)
- # Check that ufunc not defined in the top level numpy namespace such as
- # numpy.core._rational_tests.test_add can also be pickled
- res = pickle.loads(pickle.dumps(_rational_tests.test_add))
- assert_(res is _rational_tests.test_add)
+ # Check that ufunc not defined in the top level numpy namespace
+ # such as numpy.core._rational_tests.test_add can also be pickled
+ res = pickle.loads(pickle.dumps(_rational_tests.test_add,
+ protocol=proto))
+ assert_(res is _rational_tests.test_add)
def test_pickle_withstring(self):
- import pickle
astring = (b"cnumpy.core\n_ufunc_reconstruct\np0\n"
b"(S'numpy.core.umath'\np1\nS'cos'\np2\ntp3\nRp4\n.")
assert_(pickle.loads(astring) is np.cos)
@@ -63,146 +164,6 @@ class TestUfunc(object):
idx = np.array(list(zip(np.arange(L - 2), np.arange(L - 2) + 2))).ravel()
assert_array_equal(np.add.reduceat(x, idx)[::2], [1, 3, 5, 7])
- def test_generic_loops(self):
- """Test generic loops.
-
- The loops to be tested are:
-
- PyUFunc_ff_f_As_dd_d
- PyUFunc_ff_f
- PyUFunc_dd_d
- PyUFunc_gg_g
- PyUFunc_FF_F_As_DD_D
- PyUFunc_DD_D
- PyUFunc_FF_F
- PyUFunc_GG_G
- PyUFunc_OO_O
- PyUFunc_OO_O_method
- PyUFunc_f_f_As_d_d
- PyUFunc_d_d
- PyUFunc_f_f
- PyUFunc_g_g
- PyUFunc_F_F_As_D_D
- PyUFunc_F_F
- PyUFunc_D_D
- PyUFunc_G_G
- PyUFunc_O_O
- PyUFunc_O_O_method
- PyUFunc_On_Om
-
- Where:
-
- f -- float
- d -- double
- g -- long double
- F -- complex float
- D -- complex double
- G -- complex long double
- O -- python object
-
- It is difficult to assure that each of these loops is entered from the
- Python level as the special cased loops are a moving target and the
- corresponding types are architecture dependent. We probably need to
- define C level testing ufuncs to get at them. For the time being, I've
- just looked at the signatures registered in the build directory to find
- relevant functions.
-
- Fixme, currently untested:
-
- PyUFunc_ff_f_As_dd_d
- PyUFunc_FF_F_As_DD_D
- PyUFunc_f_f_As_d_d
- PyUFunc_F_F_As_D_D
- PyUFunc_On_Om
-
- """
- fone = np.exp
- ftwo = lambda x, y: x**y
- fone_val = 1
- ftwo_val = 1
- # check unary PyUFunc_f_f.
- msg = "PyUFunc_f_f"
- x = np.zeros(10, dtype=np.single)[0::2]
- assert_almost_equal(fone(x), fone_val, err_msg=msg)
- # check unary PyUFunc_d_d.
- msg = "PyUFunc_d_d"
- x = np.zeros(10, dtype=np.double)[0::2]
- assert_almost_equal(fone(x), fone_val, err_msg=msg)
- # check unary PyUFunc_g_g.
- msg = "PyUFunc_g_g"
- x = np.zeros(10, dtype=np.longdouble)[0::2]
- assert_almost_equal(fone(x), fone_val, err_msg=msg)
- # check unary PyUFunc_F_F.
- msg = "PyUFunc_F_F"
- x = np.zeros(10, dtype=np.csingle)[0::2]
- assert_almost_equal(fone(x), fone_val, err_msg=msg)
- # check unary PyUFunc_D_D.
- msg = "PyUFunc_D_D"
- x = np.zeros(10, dtype=np.cdouble)[0::2]
- assert_almost_equal(fone(x), fone_val, err_msg=msg)
- # check unary PyUFunc_G_G.
- msg = "PyUFunc_G_G"
- x = np.zeros(10, dtype=np.clongdouble)[0::2]
- assert_almost_equal(fone(x), fone_val, err_msg=msg)
-
- # check binary PyUFunc_ff_f.
- msg = "PyUFunc_ff_f"
- x = np.ones(10, dtype=np.single)[0::2]
- assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg)
- # check binary PyUFunc_dd_d.
- msg = "PyUFunc_dd_d"
- x = np.ones(10, dtype=np.double)[0::2]
- assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg)
- # check binary PyUFunc_gg_g.
- msg = "PyUFunc_gg_g"
- x = np.ones(10, dtype=np.longdouble)[0::2]
- assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg)
- # check binary PyUFunc_FF_F.
- msg = "PyUFunc_FF_F"
- x = np.ones(10, dtype=np.csingle)[0::2]
- assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg)
- # check binary PyUFunc_DD_D.
- msg = "PyUFunc_DD_D"
- x = np.ones(10, dtype=np.cdouble)[0::2]
- assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg)
- # check binary PyUFunc_GG_G.
- msg = "PyUFunc_GG_G"
- x = np.ones(10, dtype=np.clongdouble)[0::2]
- assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg)
-
- # class to use in testing object method loops
- class foo(object):
- def conjugate(self):
- return np.bool_(1)
-
- def logical_xor(self, obj):
- return np.bool_(1)
-
- # check unary PyUFunc_O_O
- msg = "PyUFunc_O_O"
- x = np.ones(10, dtype=object)[0::2]
- assert_(np.all(np.abs(x) == 1), msg)
- # check unary PyUFunc_O_O_method
- msg = "PyUFunc_O_O_method"
- x = np.zeros(10, dtype=object)[0::2]
- for i in range(len(x)):
- x[i] = foo()
- assert_(np.all(np.conjugate(x) == True), msg)
-
- # check binary PyUFunc_OO_O
- msg = "PyUFunc_OO_O"
- x = np.ones(10, dtype=object)[0::2]
- assert_(np.all(np.add(x, x) == 2), msg)
- # check binary PyUFunc_OO_O_method
- msg = "PyUFunc_OO_O_method"
- x = np.zeros(10, dtype=object)[0::2]
- for i in range(len(x)):
- x[i] = foo()
- assert_(np.all(np.logical_xor(x, x)), msg)
-
- # check PyUFunc_On_Om
- # fixme -- I don't know how to do this yet
-
def test_all_ufunc(self):
"""Try to check presence and results of all ufuncs.
@@ -286,67 +247,112 @@ class TestUfunc(object):
"""
pass
+ # from include/numpy/ufuncobject.h
+ size_inferred = 2
+ can_ignore = 4
def test_signature0(self):
# the arguments to test_signature are: nin, nout, core_signature
- # pass
- enabled, num_dims, ixs = umt.test_signature(2, 1, "(i),(i)->()")
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 2, 1, "(i),(i)->()")
assert_equal(enabled, 1)
assert_equal(num_dims, (1, 1, 0))
assert_equal(ixs, (0, 0))
+ assert_equal(flags, (self.size_inferred,))
+ assert_equal(sizes, (-1,))
def test_signature1(self):
# empty core signature; treat as plain ufunc (with trivial core)
- enabled, num_dims, ixs = umt.test_signature(2, 1, "(),()->()")
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 2, 1, "(),()->()")
assert_equal(enabled, 0)
assert_equal(num_dims, (0, 0, 0))
assert_equal(ixs, ())
+ assert_equal(flags, ())
+ assert_equal(sizes, ())
def test_signature2(self):
# more complicated names for variables
- enabled, num_dims, ixs = umt.test_signature(2, 1, "(i1,i2),(J_1)->(_kAB)")
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 2, 1, "(i1,i2),(J_1)->(_kAB)")
assert_equal(enabled, 1)
assert_equal(num_dims, (2, 1, 1))
assert_equal(ixs, (0, 1, 2, 3))
+ assert_equal(flags, (self.size_inferred,)*4)
+ assert_equal(sizes, (-1, -1, -1, -1))
- def test_signature_failure0(self):
- # in the following calls, a ValueError should be raised because
- # of error in core signature
- # FIXME These should be using assert_raises
+ def test_signature3(self):
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 2, 1, u"(i1, i12), (J_1)->(i12, i2)")
+ assert_equal(enabled, 1)
+ assert_equal(num_dims, (2, 1, 2))
+ assert_equal(ixs, (0, 1, 2, 1, 3))
+ assert_equal(flags, (self.size_inferred,)*4)
+ assert_equal(sizes, (-1, -1, -1, -1))
+
+ def test_signature4(self):
+ # matrix_multiply signature from _umath_tests
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 2, 1, "(n,k),(k,m)->(n,m)")
+ assert_equal(enabled, 1)
+ assert_equal(num_dims, (2, 2, 2))
+ assert_equal(ixs, (0, 1, 1, 2, 0, 2))
+ assert_equal(flags, (self.size_inferred,)*3)
+ assert_equal(sizes, (-1, -1, -1))
+
+ def test_signature5(self):
+ # matmul signature from _umath_tests
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 2, 1, "(n?,k),(k,m?)->(n?,m?)")
+ assert_equal(enabled, 1)
+ assert_equal(num_dims, (2, 2, 2))
+ assert_equal(ixs, (0, 1, 1, 2, 0, 2))
+ assert_equal(flags, (self.size_inferred | self.can_ignore,
+ self.size_inferred,
+ self.size_inferred | self.can_ignore))
+ assert_equal(sizes, (-1, -1, -1))
+
+ def test_signature6(self):
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 1, 1, "(3)->()")
+ assert_equal(enabled, 1)
+ assert_equal(num_dims, (1, 0))
+ assert_equal(ixs, (0,))
+ assert_equal(flags, (0,))
+ assert_equal(sizes, (3,))
+
+ def test_signature7(self):
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 3, 1, "(3),(03,3),(n)->(9)")
+ assert_equal(enabled, 1)
+ assert_equal(num_dims, (1, 2, 1, 1))
+ assert_equal(ixs, (0, 0, 0, 1, 2))
+ assert_equal(flags, (0, self.size_inferred, 0))
+ assert_equal(sizes, (3, -1, 9))
+
+ def test_signature8(self):
+ enabled, num_dims, ixs, flags, sizes = umt.test_signature(
+ 3, 1, "(3?),(3?,3?),(n)->(9)")
+ assert_equal(enabled, 1)
+ assert_equal(num_dims, (1, 2, 1, 1))
+ assert_equal(ixs, (0, 0, 0, 1, 2))
+ assert_equal(flags, (self.can_ignore, self.size_inferred, 0))
+ assert_equal(sizes, (3, -1, 9))
- # error: extra parenthesis
- msg = "core_sig: extra parenthesis"
- try:
- ret = umt.test_signature(2, 1, "((i)),(i)->()")
- assert_equal(ret, None, err_msg=msg)
- except ValueError:
- pass
+ def test_signature_failure_extra_parenthesis(self):
+ with assert_raises(ValueError):
+ umt.test_signature(2, 1, "((i)),(i)->()")
- def test_signature_failure1(self):
- # error: parenthesis matching
- msg = "core_sig: parenthesis matching"
- try:
- ret = umt.test_signature(2, 1, "(i),)i(->()")
- assert_equal(ret, None, err_msg=msg)
- except ValueError:
- pass
+ def test_signature_failure_mismatching_parenthesis(self):
+ with assert_raises(ValueError):
+ umt.test_signature(2, 1, "(i),)i(->()")
- def test_signature_failure2(self):
- # error: incomplete signature. letters outside of parenthesis are ignored
- msg = "core_sig: incomplete signature"
- try:
- ret = umt.test_signature(2, 1, "(i),->()")
- assert_equal(ret, None, err_msg=msg)
- except ValueError:
- pass
+ def test_signature_failure_signature_missing_input_arg(self):
+ with assert_raises(ValueError):
+ umt.test_signature(2, 1, "(i),->()")
- def test_signature_failure3(self):
- # error: incomplete signature. 2 output arguments are specified
- msg = "core_sig: incomplete signature"
- try:
- ret = umt.test_signature(2, 2, "(i),(i)->()")
- assert_equal(ret, None, err_msg=msg)
- except ValueError:
- pass
+ def test_signature_failure_signature_missing_output_arg(self):
+ with assert_raises(ValueError):
+ umt.test_signature(2, 2, "(i),(i)->()")
def test_get_signature(self):
assert_equal(umt.inner1d.signature, "(i),(i)->()")
@@ -525,6 +531,12 @@ class TestUfunc(object):
assert_equal(np.sum(np.ones((2, 3, 5), dtype=np.int64), axis=(0, 2), initial=2),
[12, 12, 12])
+ def test_sum_where(self):
+ # More extensive tests done in test_reduction_with_where.
+ assert_equal(np.sum([[1., 2.], [3., 4.]], where=[True, False]), 4.)
+ assert_equal(np.sum([[1., 2.], [3., 4.]], axis=0, initial=5.,
+ where=[True, False]), [9., 5.])
+
def test_inner1d(self):
a = np.arange(6).reshape((2, 3))
assert_array_equal(umt.inner1d(a, a), np.sum(a*a, axis=-1))
@@ -552,6 +564,18 @@ class TestUfunc(object):
b = np.arange(3).reshape((3, 1, 1))
assert_raises(ValueError, umt.inner1d, a, b)
+ # Writing to a broadcasted array with overlap should warn, gh-2705
+ a = np.arange(2)
+ b = np.arange(4).reshape((2, 2))
+ u, v = np.broadcast_arrays(a, b)
+ assert_equal(u.strides[0], 0)
+ x = u + v
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+ u += v
+ assert_equal(len(w), 1)
+ assert_(x[0,0] != u[0, 0])
+
def test_type_cast(self):
msg = "type cast"
a = np.arange(6, dtype='short').reshape((2, 3))
@@ -872,6 +896,89 @@ class TestUfunc(object):
w = np.array([], dtype='f8')
assert_array_equal(umt.innerwt(a, b, w), np.sum(a*b*w, axis=-1))
+ def test_cross1d(self):
+ """Test with fixed-sized signature."""
+ a = np.eye(3)
+ assert_array_equal(umt.cross1d(a, a), np.zeros((3, 3)))
+ out = np.zeros((3, 3))
+ result = umt.cross1d(a[0], a, out)
+ assert_(result is out)
+ assert_array_equal(result, np.vstack((np.zeros(3), a[2], -a[1])))
+ assert_raises(ValueError, umt.cross1d, np.eye(4), np.eye(4))
+ assert_raises(ValueError, umt.cross1d, a, np.arange(4.))
+ assert_raises(ValueError, umt.cross1d, a, np.arange(3.), np.zeros((3, 4)))
+
+ def test_can_ignore_signature(self):
+ # Comparing the effects of ? in signature:
+ # matrix_multiply: (m,n),(n,p)->(m,p) # all must be there.
+ # matmul: (m?,n),(n,p?)->(m?,p?) # allow missing m, p.
+ mat = np.arange(12).reshape((2, 3, 2))
+ single_vec = np.arange(2)
+ col_vec = single_vec[:, np.newaxis]
+ col_vec_array = np.arange(8).reshape((2, 2, 2, 1)) + 1
+ # matrix @ single column vector with proper dimension
+ mm_col_vec = umt.matrix_multiply(mat, col_vec)
+ # matmul does the same thing
+ matmul_col_vec = umt.matmul(mat, col_vec)
+ assert_array_equal(matmul_col_vec, mm_col_vec)
+ # matrix @ vector without dimension making it a column vector.
+ # matrix multiply fails -> missing core dim.
+ assert_raises(ValueError, umt.matrix_multiply, mat, single_vec)
+ # matmul mimicker passes, and returns a vector.
+ matmul_col = umt.matmul(mat, single_vec)
+ assert_array_equal(matmul_col, mm_col_vec.squeeze())
+ # Now with a column array: same as for column vector,
+ # broadcasting sensibly.
+ mm_col_vec = umt.matrix_multiply(mat, col_vec_array)
+ matmul_col_vec = umt.matmul(mat, col_vec_array)
+ assert_array_equal(matmul_col_vec, mm_col_vec)
+ # As above, but for row vector
+ single_vec = np.arange(3)
+ row_vec = single_vec[np.newaxis, :]
+ row_vec_array = np.arange(24).reshape((4, 2, 1, 1, 3)) + 1
+ # row vector @ matrix
+ mm_row_vec = umt.matrix_multiply(row_vec, mat)
+ matmul_row_vec = umt.matmul(row_vec, mat)
+ assert_array_equal(matmul_row_vec, mm_row_vec)
+ # single row vector @ matrix
+ assert_raises(ValueError, umt.matrix_multiply, single_vec, mat)
+ matmul_row = umt.matmul(single_vec, mat)
+ assert_array_equal(matmul_row, mm_row_vec.squeeze())
+ # row vector array @ matrix
+ mm_row_vec = umt.matrix_multiply(row_vec_array, mat)
+ matmul_row_vec = umt.matmul(row_vec_array, mat)
+ assert_array_equal(matmul_row_vec, mm_row_vec)
+ # Now for vector combinations
+ # row vector @ column vector
+ col_vec = row_vec.T
+ col_vec_array = row_vec_array.swapaxes(-2, -1)
+ mm_row_col_vec = umt.matrix_multiply(row_vec, col_vec)
+ matmul_row_col_vec = umt.matmul(row_vec, col_vec)
+ assert_array_equal(matmul_row_col_vec, mm_row_col_vec)
+ # single row vector @ single col vector
+ assert_raises(ValueError, umt.matrix_multiply, single_vec, single_vec)
+ matmul_row_col = umt.matmul(single_vec, single_vec)
+ assert_array_equal(matmul_row_col, mm_row_col_vec.squeeze())
+ # row vector array @ matrix
+ mm_row_col_array = umt.matrix_multiply(row_vec_array, col_vec_array)
+ matmul_row_col_array = umt.matmul(row_vec_array, col_vec_array)
+ assert_array_equal(matmul_row_col_array, mm_row_col_array)
+ # Finally, check that things are *not* squeezed if one gives an
+ # output.
+ out = np.zeros_like(mm_row_col_array)
+ out = umt.matrix_multiply(row_vec_array, col_vec_array, out=out)
+ assert_array_equal(out, mm_row_col_array)
+ out[:] = 0
+ out = umt.matmul(row_vec_array, col_vec_array, out=out)
+ assert_array_equal(out, mm_row_col_array)
+ # And check one cannot put missing dimensions back.
+ out = np.zeros_like(mm_row_col_vec)
+ assert_raises(ValueError, umt.matrix_multiply, single_vec, single_vec,
+ out)
+ # But fine for matmul, since it is just a broadcast.
+ out = umt.matmul(single_vec, single_vec, out)
+ assert_array_equal(out, mm_row_col_vec.squeeze())
+
def test_matrix_multiply(self):
self.compare_matrix_multiply_results(np.long)
self.compare_matrix_multiply_results(np.double)
@@ -1008,6 +1115,8 @@ class TestUfunc(object):
assert_equal(np.array([[1]], dtype=object).sum(), 1)
assert_equal(np.array([[[1, 2]]], dtype=object).sum((0, 1)), [1, 2])
assert_equal(np.array([1], dtype=object).sum(initial=1), 2)
+ assert_equal(np.array([[1], [2, 3]], dtype=object)
+ .sum(initial=[0], where=[False, True]), [0, 2, 3])
def test_object_array_accumulate_inplace(self):
# Checks that in-place accumulates work, see also gh-7402
@@ -1242,6 +1351,44 @@ class TestUfunc(object):
res = np.add.reduce(a, initial=5)
assert_equal(res, 15)
+ @pytest.mark.parametrize('axis', (0, 1, None))
+ @pytest.mark.parametrize('where', (np.array([False, True, True]),
+ np.array([[True], [False], [True]]),
+ np.array([[True, False, False],
+ [False, True, False],
+ [False, True, True]])))
+ def test_reduction_with_where(self, axis, where):
+ a = np.arange(9.).reshape(3, 3)
+ a_copy = a.copy()
+ a_check = np.zeros_like(a)
+ np.positive(a, out=a_check, where=where)
+
+ res = np.add.reduce(a, axis=axis, where=where)
+ check = a_check.sum(axis)
+ assert_equal(res, check)
+ # Check we do not overwrite elements of a internally.
+ assert_array_equal(a, a_copy)
+
+ @pytest.mark.parametrize(('axis', 'where'),
+ ((0, np.array([True, False, True])),
+ (1, [True, True, False]),
+ (None, True)))
+ @pytest.mark.parametrize('initial', (-np.inf, 5.))
+ def test_reduction_with_where_and_initial(self, axis, where, initial):
+ a = np.arange(9.).reshape(3, 3)
+ a_copy = a.copy()
+ a_check = np.full(a.shape, -np.inf)
+ np.positive(a, out=a_check, where=where)
+
+ res = np.maximum.reduce(a, axis=axis, where=where, initial=initial)
+ check = a_check.max(axis, initial=initial)
+ assert_equal(res, check)
+
+ def test_reduction_where_initial_needed(self):
+ a = np.arange(9.).reshape(3, 3)
+ m = [False, True, False]
+ assert_raises(ValueError, np.maximum.reduce, a, where=m)
+
def test_identityless_reduction_nonreorderable(self):
a = np.array([[8.0, 2.0, 2.0], [1.0, 0.5, 0.25]])
@@ -1378,6 +1525,7 @@ class TestUfunc(object):
result = struct_ufunc.add_triplet(a, b)
assert_equal(result, np.array([(2, 4, 6)], dtype='u8,u8,u8'))
+ assert_raises(RuntimeError, struct_ufunc.register_fail)
def test_custom_ufunc(self):
a = np.array(
@@ -1595,16 +1743,19 @@ class TestUfunc(object):
assert_equal(f(d, 0, None, None, True), r.reshape((1,) + r.shape))
assert_equal(f(d, 0, None, None, False, 0), r)
assert_equal(f(d, 0, None, None, False, initial=0), r)
+ assert_equal(f(d, 0, None, None, False, 0, True), r)
+ assert_equal(f(d, 0, None, None, False, 0, where=True), r)
# multiple keywords
assert_equal(f(d, axis=0, dtype=None, out=None, keepdims=False), r)
assert_equal(f(d, 0, dtype=None, out=None, keepdims=False), r)
assert_equal(f(d, 0, None, out=None, keepdims=False), r)
- assert_equal(f(d, 0, None, out=None, keepdims=False, initial=0), r)
+ assert_equal(f(d, 0, None, out=None, keepdims=False, initial=0,
+ where=True), r)
# too little
assert_raises(TypeError, f)
# too much
- assert_raises(TypeError, f, d, 0, None, None, False, 0, 1)
+ assert_raises(TypeError, f, d, 0, None, None, False, 0, True, 1)
# invalid axis
assert_raises(TypeError, f, d, "invalid")
assert_raises(TypeError, f, d, axis="invalid")
@@ -1703,3 +1854,96 @@ class TestUfunc(object):
def test_no_doc_string(self):
# gh-9337
assert_('\n' not in umt.inner1d_no_doc.__doc__)
+
+ def test_invalid_args(self):
+ # gh-7961
+ exc = pytest.raises(TypeError, np.sqrt, None)
+ # minimally check the exception text
+ assert exc.match('loop of ufunc does not support')
+
+ @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
+ def test_nat_is_not_finite(self, nat):
+ try:
+ assert not np.isfinite(nat)
+ except TypeError:
+ pass # ok, just not implemented
+
+ @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
+ def test_nat_is_nan(self, nat):
+ try:
+ assert np.isnan(nat)
+ except TypeError:
+ pass # ok, just not implemented
+
+ @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
+ def test_nat_is_not_inf(self, nat):
+ try:
+ assert not np.isinf(nat)
+ except TypeError:
+ pass # ok, just not implemented
+
+
+@pytest.mark.parametrize('ufunc', [getattr(np, x) for x in dir(np)
+ if isinstance(getattr(np, x), np.ufunc)])
+def test_ufunc_types(ufunc):
+ '''
+ Check all ufuncs that the correct type is returned. Avoid
+ object and boolean types since many operations are not defined for
+ for them.
+
+ Choose the shape so even dot and matmul will succeed
+ '''
+ for typ in ufunc.types:
+ # types is a list of strings like ii->i
+ if 'O' in typ or '?' in typ:
+ continue
+ inp, out = typ.split('->')
+ args = [np.ones((3, 3), t) for t in inp]
+ with warnings.catch_warnings(record=True):
+ warnings.filterwarnings("always")
+ res = ufunc(*args)
+ if isinstance(res, tuple):
+ outs = tuple(out)
+ assert len(res) == len(outs)
+ for r, t in zip(res, outs):
+ assert r.dtype == np.dtype(t)
+ else:
+ assert res.dtype == np.dtype(out)
+
+@pytest.mark.parametrize('ufunc', [getattr(np, x) for x in dir(np)
+ if isinstance(getattr(np, x), np.ufunc)])
+def test_ufunc_noncontiguous(ufunc):
+ '''
+ Check that contiguous and non-contiguous calls to ufuncs
+ have the same results for values in range(9)
+ '''
+ for typ in ufunc.types:
+ # types is a list of strings like ii->i
+ if any(set('O?mM') & set(typ)):
+ # bool, object, datetime are too irregular for this simple test
+ continue
+ inp, out = typ.split('->')
+ args_c = [np.empty(6, t) for t in inp]
+ args_n = [np.empty(18, t)[::3] for t in inp]
+ for a in args_c:
+ a.flat = range(1,7)
+ for a in args_n:
+ a.flat = range(1,7)
+ with warnings.catch_warnings(record=True):
+ warnings.filterwarnings("always")
+ res_c = ufunc(*args_c)
+ res_n = ufunc(*args_n)
+ if len(out) == 1:
+ res_c = (res_c,)
+ res_n = (res_n,)
+ for c_ar, n_ar in zip(res_c, res_n):
+ dt = c_ar.dtype
+ if np.issubdtype(dt, np.floating):
+ # for floating point results allow a small fuss in comparisons
+ # since different algorithms (libm vs. intrinsics) can be used
+ # for different input strides
+ res_eps = np.finfo(dt).eps
+ tol = 2*res_eps
+ assert_allclose(res_c, res_n, atol=tol, rtol=tol)
+ else:
+ assert_equal(c_ar, n_ar)
diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py
index c15ce83f6..9b4ce9e47 100644
--- a/numpy/core/tests/test_umath.py
+++ b/numpy/core/tests/test_umath.py
@@ -1,11 +1,11 @@
from __future__ import division, absolute_import, print_function
-import sys
import platform
import warnings
import fnmatch
import itertools
import pytest
+from fractions import Fraction
import numpy.core.umath as ncu
from numpy.core import _umath_tests as ncu_tests
@@ -13,11 +13,10 @@ import numpy as np
from numpy.testing import (
assert_, assert_equal, assert_raises, assert_raises_regex,
assert_array_equal, assert_almost_equal, assert_array_almost_equal,
- assert_allclose, assert_no_warnings, suppress_warnings,
- _gen_alignment_data, assert_warns
+ assert_array_max_ulp, assert_allclose, assert_no_warnings, suppress_warnings,
+ _gen_alignment_data, assert_array_almost_equal_nulp
)
-
def on_powerpc():
""" True if we are running on a Power PC platform."""
return platform.processor() == 'powerpc' or \
@@ -76,11 +75,9 @@ class TestOut(object):
assert_(r1 is o1)
assert_(r2 is o2)
- with warnings.catch_warnings(record=True) as w:
- warnings.filterwarnings('always', '', DeprecationWarning)
+ with assert_raises(TypeError):
+ # Out argument must be tuple, since there are multiple outputs.
r1, r2 = np.frexp(d, out=o1, subok=subok)
- assert_(r1 is o1)
- assert_(w[0].category is DeprecationWarning)
assert_raises(ValueError, np.add, a, 2, o, o, subok=subok)
assert_raises(ValueError, np.add, a, 2, o, out=o, subok=subok)
@@ -166,14 +163,9 @@ class TestOut(object):
else:
assert_(type(r1) == np.ndarray)
- with warnings.catch_warnings(record=True) as w:
- warnings.filterwarnings('always', '', DeprecationWarning)
+ with assert_raises(TypeError):
+ # Out argument must be tuple, since there are multiple outputs.
r1, r2 = np.frexp(d, out=o1, subok=subok)
- if subok:
- assert_(isinstance(r2, ArrayWrap))
- else:
- assert_(type(r2) == np.ndarray)
- assert_(w[0].category is DeprecationWarning)
class TestComparisons(object):
@@ -274,6 +266,12 @@ class TestDivision(object):
y = np.floor_divide(x**2, x)
assert_equal(y, [1.e+110, 0], err_msg=msg)
+ def test_floor_division_signed_zero(self):
+ # Check that the sign bit is correctly set when dividing positive and
+ # negative zero by one.
+ x = np.zeros(10)
+ assert_equal(np.signbit(x//1), 0)
+ assert_equal(np.signbit((-x)//1), 1)
def floor_divide_and_remainder(x, y):
return (np.floor_divide(x, y), np.remainder(x, y))
@@ -644,6 +642,180 @@ class TestExp(object):
yf = np.array(y, dtype=dt)*log2_
assert_almost_equal(np.exp(yf), xf)
+class TestSpecialFloats(object):
+ def test_exp_values(self):
+ x = [np.nan, np.nan, np.inf, 0.]
+ y = [np.nan, -np.nan, np.inf, -np.inf]
+ for dt in ['f', 'd', 'g']:
+ xf = np.array(x, dtype=dt)
+ yf = np.array(y, dtype=dt)
+ assert_equal(np.exp(yf), xf)
+
+ with np.errstate(over='raise'):
+ assert_raises(FloatingPointError, np.exp, np.float32(100.))
+ assert_raises(FloatingPointError, np.exp, np.float32(1E19))
+
+ def test_log_values(self):
+ with np.errstate(all='ignore'):
+ x = [np.nan, np.nan, np.inf, np.nan, -np.inf, np.nan]
+ y = [np.nan, -np.nan, np.inf, -np.inf, 0., -1.0]
+ for dt in ['f', 'd', 'g']:
+ xf = np.array(x, dtype=dt)
+ yf = np.array(y, dtype=dt)
+ assert_equal(np.log(yf), xf)
+
+ with np.errstate(divide='raise'):
+ assert_raises(FloatingPointError, np.log, np.float32(0.))
+
+ with np.errstate(invalid='raise'):
+ assert_raises(FloatingPointError, np.log, np.float32(-np.inf))
+ assert_raises(FloatingPointError, np.log, np.float32(-1.0))
+
+ def test_sincos_values(self):
+ with np.errstate(all='ignore'):
+ x = [np.nan, np.nan, np.nan, np.nan]
+ y = [np.nan, -np.nan, np.inf, -np.inf]
+ for dt in ['f', 'd', 'g']:
+ xf = np.array(x, dtype=dt)
+ yf = np.array(y, dtype=dt)
+ assert_equal(np.sin(yf), xf)
+ assert_equal(np.cos(yf), xf)
+
+ with np.errstate(invalid='raise'):
+ assert_raises(FloatingPointError, np.sin, np.float32(-np.inf))
+ assert_raises(FloatingPointError, np.sin, np.float32(np.inf))
+ assert_raises(FloatingPointError, np.cos, np.float32(-np.inf))
+ assert_raises(FloatingPointError, np.cos, np.float32(np.inf))
+
+ def test_sqrt_values(self):
+ with np.errstate(all='ignore'):
+ x = [np.nan, np.nan, np.inf, np.nan, 0.]
+ y = [np.nan, -np.nan, np.inf, -np.inf, 0.]
+ for dt in ['f', 'd', 'g']:
+ xf = np.array(x, dtype=dt)
+ yf = np.array(y, dtype=dt)
+ assert_equal(np.sqrt(yf), xf)
+
+ #with np.errstate(invalid='raise'):
+ # for dt in ['f', 'd', 'g']:
+ # assert_raises(FloatingPointError, np.sqrt, np.array(-100., dtype=dt))
+
+ def test_abs_values(self):
+ x = [np.nan, np.nan, np.inf, np.inf, 0., 0., 1.0, 1.0]
+ y = [np.nan, -np.nan, np.inf, -np.inf, 0., -0., -1.0, 1.0]
+ for dt in ['f', 'd', 'g']:
+ xf = np.array(x, dtype=dt)
+ yf = np.array(y, dtype=dt)
+ assert_equal(np.abs(yf), xf)
+
+ def test_square_values(self):
+ x = [np.nan, np.nan, np.inf, np.inf]
+ y = [np.nan, -np.nan, np.inf, -np.inf]
+ with np.errstate(all='ignore'):
+ for dt in ['f', 'd', 'g']:
+ xf = np.array(x, dtype=dt)
+ yf = np.array(y, dtype=dt)
+ assert_equal(np.square(yf), xf)
+
+ with np.errstate(over='raise'):
+ assert_raises(FloatingPointError, np.square, np.array(1E32, dtype='f'))
+ assert_raises(FloatingPointError, np.square, np.array(1E200, dtype='d'))
+
+ def test_reciprocal_values(self):
+ with np.errstate(all='ignore'):
+ x = [np.nan, np.nan, 0.0, -0.0, np.inf, -np.inf]
+ y = [np.nan, -np.nan, np.inf, -np.inf, 0., -0.]
+ for dt in ['f', 'd', 'g']:
+ xf = np.array(x, dtype=dt)
+ yf = np.array(y, dtype=dt)
+ assert_equal(np.reciprocal(yf), xf)
+
+ with np.errstate(divide='raise'):
+ for dt in ['f', 'd', 'g']:
+ assert_raises(FloatingPointError, np.reciprocal, np.array(-0.0, dtype=dt))
+
+# func : [maxulperror, low, high]
+avx_ufuncs = {'sqrt' :[1, 0., 100.],
+ 'absolute' :[0, -100., 100.],
+ 'reciprocal' :[1, 1., 100.],
+ 'square' :[1, -100., 100.],
+ 'rint' :[0, -100., 100.],
+ 'floor' :[0, -100., 100.],
+ 'ceil' :[0, -100., 100.],
+ 'trunc' :[0, -100., 100.]}
+
+class TestAVXUfuncs(object):
+ def test_avx_based_ufunc(self):
+ strides = np.array([-4,-3,-2,-1,1,2,3,4])
+ np.random.seed(42)
+ for func, prop in avx_ufuncs.items():
+ maxulperr = prop[0]
+ minval = prop[1]
+ maxval = prop[2]
+ # various array sizes to ensure masking in AVX is tested
+ for size in range(1,32):
+ myfunc = getattr(np, func)
+ x_f32 = np.float32(np.random.uniform(low=minval, high=maxval,
+ size=size))
+ x_f64 = np.float64(x_f32)
+ x_f128 = np.longdouble(x_f32)
+ y_true128 = myfunc(x_f128)
+ if maxulperr == 0:
+ assert_equal(myfunc(x_f32), np.float32(y_true128))
+ assert_equal(myfunc(x_f64), np.float64(y_true128))
+ else:
+ assert_array_max_ulp(myfunc(x_f32), np.float32(y_true128),
+ maxulp=maxulperr)
+ assert_array_max_ulp(myfunc(x_f64), np.float64(y_true128),
+ maxulp=maxulperr)
+ # various strides to test gather instruction
+ if size > 1:
+ y_true32 = myfunc(x_f32)
+ y_true64 = myfunc(x_f64)
+ for jj in strides:
+ assert_equal(myfunc(x_f64[::jj]), y_true64[::jj])
+ assert_equal(myfunc(x_f32[::jj]), y_true32[::jj])
+
+class TestAVXFloat32Transcendental(object):
+ def test_exp_float32(self):
+ np.random.seed(42)
+ x_f32 = np.float32(np.random.uniform(low=0.0,high=88.1,size=1000000))
+ x_f64 = np.float64(x_f32)
+ assert_array_max_ulp(np.exp(x_f32), np.float32(np.exp(x_f64)), maxulp=3)
+
+ def test_log_float32(self):
+ np.random.seed(42)
+ x_f32 = np.float32(np.random.uniform(low=0.0,high=1000,size=1000000))
+ x_f64 = np.float64(x_f32)
+ assert_array_max_ulp(np.log(x_f32), np.float32(np.log(x_f64)), maxulp=4)
+
+ def test_sincos_float32(self):
+ np.random.seed(42)
+ N = 1000000
+ M = np.int(N/20)
+ index = np.random.randint(low=0, high=N, size=M)
+ x_f32 = np.float32(np.random.uniform(low=-100.,high=100.,size=N))
+ # test coverage for elements > 117435.992f for which glibc is used
+ x_f32[index] = np.float32(10E+10*np.random.rand(M))
+ x_f64 = np.float64(x_f32)
+ assert_array_max_ulp(np.sin(x_f32), np.float32(np.sin(x_f64)), maxulp=2)
+ assert_array_max_ulp(np.cos(x_f32), np.float32(np.cos(x_f64)), maxulp=2)
+
+ def test_strided_float32(self):
+ np.random.seed(42)
+ strides = np.array([-4,-3,-2,-1,1,2,3,4])
+ sizes = np.arange(2,100)
+ for ii in sizes:
+ x_f32 = np.float32(np.random.uniform(low=0.01,high=88.1,size=ii))
+ exp_true = np.exp(x_f32)
+ log_true = np.log(x_f32)
+ sin_true = np.sin(x_f32)
+ cos_true = np.cos(x_f32)
+ for jj in strides:
+ assert_array_almost_equal_nulp(np.exp(x_f32[::jj]), exp_true[::jj], nulp=2)
+ assert_array_almost_equal_nulp(np.log(x_f32[::jj]), log_true[::jj], nulp=2)
+ assert_array_almost_equal_nulp(np.sin(x_f32[::jj]), sin_true[::jj], nulp=2)
+ assert_array_almost_equal_nulp(np.cos(x_f32[::jj]), cos_true[::jj], nulp=2)
class TestLogAddExp(_FilterInvalids):
def test_logaddexp_values(self):
@@ -685,6 +857,10 @@ class TestLogAddExp(_FilterInvalids):
assert_(np.isnan(np.logaddexp(0, np.nan)))
assert_(np.isnan(np.logaddexp(np.nan, np.nan)))
+ def test_reduce(self):
+ assert_equal(np.logaddexp.identity, -np.inf)
+ assert_equal(np.logaddexp.reduce([]), -np.inf)
+
class TestLog1p(object):
def test_log1p(self):
@@ -1294,6 +1470,7 @@ class TestSign(object):
# In reference to github issue #6229
def test_nan():
foo = np.array([np.nan])
+ # FIXME: a not used
a = np.sign(foo.astype(object))
assert_raises(TypeError, test_nan)
@@ -1327,21 +1504,18 @@ class TestMinMax(object):
assert_equal(d.max(), d[0])
assert_equal(d.min(), d[0])
- def test_reduce_warns(self):
+ def test_reduce_reorder(self):
# gh 10370, 11029 Some compilers reorder the call to npy_getfloatstatus
# and put it before the call to an intrisic function that causes
- # invalid status to be set. Also make sure warnings are emitted
+ # invalid status to be set. Also make sure warnings are not emitted
for n in (2, 4, 8, 16, 32):
for dt in (np.float32, np.float16, np.complex64):
- with suppress_warnings() as sup:
- sup.record(RuntimeWarning)
- for r in np.diagflat(np.array([np.nan] * n, dtype=dt)):
- assert_equal(np.min(r), np.nan)
- assert_equal(len(sup.log), n)
+ for r in np.diagflat(np.array([np.nan] * n, dtype=dt)):
+ assert_equal(np.min(r), np.nan)
- def test_minimize_warns(self):
- # gh 11589
- assert_warns(RuntimeWarning, np.minimum, np.nan, 1)
+ def test_minimize_no_warns(self):
+ a = np.minimum(np.nan, 1)
+ assert_equal(a, np.nan)
class TestAbsoluteNegative(object):
@@ -1595,7 +1769,6 @@ class TestSpecialMethods(object):
ok = np.empty(1).view(Ok)
bad = np.empty(1).view(Bad)
-
# double-free (segfault) of "ok" if "bad" raises an exception
for i in range(10):
assert_raises(RuntimeError, ncu.frexp, 1, ok, bad)
@@ -1893,7 +2066,8 @@ class TestSpecialMethods(object):
# reduce, kwargs
res = np.multiply.reduce(a, axis='axis0', dtype='dtype0', out='out0',
- keepdims='keep0', initial='init0')
+ keepdims='keep0', initial='init0',
+ where='where0')
assert_equal(res[0], a)
assert_equal(res[1], np.multiply)
assert_equal(res[2], 'reduce')
@@ -1902,7 +2076,8 @@ class TestSpecialMethods(object):
'out': ('out0',),
'keepdims': 'keep0',
'axis': 'axis0',
- 'initial': 'init0'})
+ 'initial': 'init0',
+ 'where': 'where0'})
# reduce, output equal to None removed, but not other explicit ones,
# even if they are at their default value.
@@ -1912,14 +2087,18 @@ class TestSpecialMethods(object):
assert_equal(res[4], {'axis': 0, 'keepdims': True})
res = np.multiply.reduce(a, None, out=(None,), dtype=None)
assert_equal(res[4], {'axis': None, 'dtype': None})
- res = np.multiply.reduce(a, 0, None, None, False, 2)
- assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, 'initial': 2})
- # np._NoValue ignored for initial.
- res = np.multiply.reduce(a, 0, None, None, False, np._NoValue)
- assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False})
- # None kept for initial.
- res = np.multiply.reduce(a, 0, None, None, False, None)
- assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, 'initial': None})
+ res = np.multiply.reduce(a, 0, None, None, False, 2, True)
+ assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False,
+ 'initial': 2, 'where': True})
+ # np._NoValue ignored for initial
+ res = np.multiply.reduce(a, 0, None, None, False,
+ np._NoValue, True)
+ assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False,
+ 'where': True})
+ # None kept for initial, True for where.
+ res = np.multiply.reduce(a, 0, None, None, False, None, True)
+ assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False,
+ 'initial': None, 'where': True})
# reduce, wrong args
assert_raises(ValueError, np.multiply.reduce, a, out=())
@@ -2063,10 +2242,9 @@ class TestSpecialMethods(object):
assert_(np.modf(a, None) == {})
assert_(np.modf(a, None, None) == {})
assert_(np.modf(a, out=(None, None)) == {})
- with warnings.catch_warnings(record=True) as w:
- warnings.filterwarnings('always', '', DeprecationWarning)
- assert_(np.modf(a, out=None) == {})
- assert_(w[0].category is DeprecationWarning)
+ with assert_raises(TypeError):
+ # Out argument must be tuple, since there are multiple outputs.
+ np.modf(a, out=None)
# don't give positional and output argument, or too many arguments.
# wrong number of arguments in the tuple is an error too.
@@ -2447,9 +2625,40 @@ class TestRationalFunctions(object):
assert_equal(np.gcd(2**100, 3**100), 1)
-def is_longdouble_finfo_bogus():
- info = np.finfo(np.longcomplex)
- return not np.isfinite(np.log10(info.tiny/info.eps))
+class TestRoundingFunctions(object):
+
+ def test_object_direct(self):
+ """ test direct implementation of these magic methods """
+ class C:
+ def __floor__(self):
+ return 1
+ def __ceil__(self):
+ return 2
+ def __trunc__(self):
+ return 3
+
+ arr = np.array([C(), C()])
+ assert_equal(np.floor(arr), [1, 1])
+ assert_equal(np.ceil(arr), [2, 2])
+ assert_equal(np.trunc(arr), [3, 3])
+
+ def test_object_indirect(self):
+ """ test implementations via __float__ """
+ class C:
+ def __float__(self):
+ return -2.5
+
+ arr = np.array([C(), C()])
+ assert_equal(np.floor(arr), [-3, -3])
+ assert_equal(np.ceil(arr), [-2, -2])
+ with pytest.raises(TypeError):
+ np.trunc(arr) # consistent with math.trunc
+
+ def test_fraction(self):
+ f = Fraction(-4, 3)
+ assert_equal(np.floor(f), -2)
+ assert_equal(np.ceil(f), -1)
+ assert_equal(np.trunc(f), -1)
class TestComplexFunctions(object):
@@ -2547,7 +2756,8 @@ class TestComplexFunctions(object):
b = cfunc(p)
assert_(abs(a - b) < atol, "%s %s: %s; cmath: %s" % (fname, p, a, b))
- def check_loss_of_precision(self, dtype):
+ @pytest.mark.parametrize('dtype', [np.complex64, np.complex_, np.longcomplex])
+ def test_loss_of_precision(self, dtype):
"""Check loss of precision in complex arc* functions"""
# Check against known-good functions
@@ -2589,10 +2799,11 @@ class TestComplexFunctions(object):
# It's not guaranteed that the system-provided arc functions
# are accurate down to a few epsilons. (Eg. on Linux 64-bit)
# So, give more leeway for long complex tests here:
- check(x_series, 50*eps)
+ # Can use 2.1 for > Ubuntu LTS Trusty (2014), glibc = 2.19.
+ check(x_series, 50.0*eps)
else:
check(x_series, 2.1*eps)
- check(x_basic, 2*eps/1e-3)
+ check(x_basic, 2.0*eps/1e-3)
# Check a few points
@@ -2632,15 +2843,6 @@ class TestComplexFunctions(object):
check(func, pts, 1j)
check(func, pts, 1+1j)
- def test_loss_of_precision(self):
- for dtype in [np.complex64, np.complex_]:
- self.check_loss_of_precision(dtype)
-
- @pytest.mark.skipif(is_longdouble_finfo_bogus(),
- reason="Bogus long double finfo")
- def test_loss_of_precision_longcomplex(self):
- self.check_loss_of_precision(np.longcomplex)
-
class TestAttributes(object):
def test_attributes(self):
@@ -2923,3 +3125,14 @@ def test_signaling_nan_exceptions():
with assert_no_warnings():
a = np.ndarray(shape=(), dtype='float32', buffer=b'\x00\xe0\xbf\xff')
np.isnan(a)
+
+@pytest.mark.parametrize("arr", [
+ np.arange(2),
+ np.matrix([0, 1]),
+ np.matrix([[0, 1], [2, 5]]),
+ ])
+def test_outer_subclass_preserve(arr):
+ # for gh-8661
+ class foo(np.ndarray): pass
+ actual = np.multiply.outer(arr.view(foo), arr.view(foo))
+ assert actual.__class__.__name__ == 'foo'
diff --git a/numpy/core/tests/test_umath_accuracy.py b/numpy/core/tests/test_umath_accuracy.py
new file mode 100644
index 000000000..0bab04df2
--- /dev/null
+++ b/numpy/core/tests/test_umath_accuracy.py
@@ -0,0 +1,54 @@
+import numpy as np
+import platform
+from os import path
+import sys
+import pytest
+from ctypes import *
+from numpy.testing import assert_array_max_ulp
+
+runtest = sys.platform.startswith('linux') and (platform.machine() == 'x86_64')
+platform_skip = pytest.mark.skipif(not runtest,
+ reason="""
+ stick to x86_64 and linux platforms.
+ test seems to fail on some of ARM and power
+ architectures.
+ """)
+
+# convert string to hex function taken from:
+# https://stackoverflow.com/questions/1592158/convert-hex-to-float #
+def convert(s):
+ i = int(s, 16) # convert from hex to a Python int
+ cp = pointer(c_int(i)) # make this into a c integer
+ fp = cast(cp, POINTER(c_float)) # cast the int pointer to a float pointer
+ return fp.contents.value # dereference the pointer, get the float
+
+str_to_float = np.vectorize(convert)
+files = ['umath-validation-set-exp',
+ 'umath-validation-set-log',
+ 'umath-validation-set-sin',
+ 'umath-validation-set-cos']
+
+class TestAccuracy(object):
+ @pytest.mark.xfail(reason="Fails for MacPython/numpy-wheels builds")
+ def test_validate_transcendentals(self):
+ with np.errstate(all='ignore'):
+ for filename in files:
+ data_dir = path.join(path.dirname(__file__), 'data')
+ filepath = path.join(data_dir, filename)
+ with open(filepath) as fid:
+ file_without_comments = (r for r in fid if not r[0] in ('$', '#'))
+ data = np.genfromtxt(file_without_comments,
+ dtype=('|S39','|S39','|S39',np.int),
+ names=('type','input','output','ulperr'),
+ delimiter=',',
+ skip_header=1)
+ npfunc = getattr(np, filename.split('-')[3])
+ for datatype in np.unique(data['type']):
+ data_subset = data[data['type'] == datatype]
+ inval = np.array(str_to_float(data_subset['input'].astype(str)), dtype=eval(datatype))
+ outval = np.array(str_to_float(data_subset['output'].astype(str)), dtype=eval(datatype))
+ perm = np.random.permutation(len(inval))
+ inval = inval[perm]
+ outval = outval[perm]
+ maxulperr = data_subset['ulperr'].max()
+ assert_array_max_ulp(npfunc(inval), outval, maxulperr)
diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py
index 785ae8c57..1f5b4077f 100644
--- a/numpy/core/tests/test_umath_complex.py
+++ b/numpy/core/tests/test_umath_complex.py
@@ -5,7 +5,8 @@ import platform
import pytest
import numpy as np
-import numpy.core.umath as ncu
+# import the c-extension module directly since _arg is not exported via umath
+import numpy.core._multiarray_umath as ncu
from numpy.testing import (
assert_raises, assert_equal, assert_array_equal, assert_almost_equal
)
diff --git a/numpy/core/umath.py b/numpy/core/umath.py
index a0e8ad427..f3b26ab72 100644
--- a/numpy/core/umath.py
+++ b/numpy/core/umath.py
@@ -9,7 +9,7 @@ by importing from the extension module.
from . import _multiarray_umath
from numpy.core._multiarray_umath import *
from numpy.core._multiarray_umath import (
- _UFUNC_API, _add_newdoc_ufunc, _arg, _ones_like
+ _UFUNC_API, _add_newdoc_ufunc, _ones_like
)
__all__ = [
@@ -18,7 +18,7 @@ __all__ = [
'FPE_DIVIDEBYZERO', 'FPE_INVALID', 'FPE_OVERFLOW', 'FPE_UNDERFLOW', 'NAN',
'NINF', 'NZERO', 'PINF', 'PZERO', 'SHIFT_DIVIDEBYZERO', 'SHIFT_INVALID',
'SHIFT_OVERFLOW', 'SHIFT_UNDERFLOW', 'UFUNC_BUFSIZE_DEFAULT',
- 'UFUNC_PYVALS_NAME', '_add_newdoc_ufunc', '_arg', 'absolute', 'add',
+ 'UFUNC_PYVALS_NAME', '_add_newdoc_ufunc', 'absolute', 'add',
'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh',
'bitwise_and', 'bitwise_or', 'bitwise_xor', 'cbrt', 'ceil', 'conj',
'conjugate', 'copysign', 'cos', 'cosh', 'deg2rad', 'degrees', 'divide',