8000 Revert changes to contiguous flags definition while creating NPY_TEST_UNSAFE_STRIDES by seberg · Pull Request #3162 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

Revert changes to contiguous flags definition while creating NPY_TEST_UNSAFE_STRIDES #3162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 3, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ matrix:
include:
- python: 2.7
env: NPY_SEPARATE_COMPILATION=0
- python: 3.2
- python: 3.3
env: NPY_SEPARATE_COMPILATION=0
- python: 2.7
env: NPY_RELAXED_STRIDES_CHECKING=1
- python: 3.3
env: NPY_RELAXED_STRIDES_CHECKING=1
before_install:
- uname -a
- free -m
Expand Down
7 changes: 7 additions & 0 deletions numpy/core/bscript
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ from setup_common \
MANDATORY_FUNCS, C_ABI_VERSION, C_API_VERSION

ENABLE_SEPARATE_COMPILATION = (os.environ.get('NPY_SEPARATE_COMPILATION', "1") != "0")
NPY_RELAXED_STRIDES_CHECKING = (os.environ.get('NPY_RELAXED_STRIDES_CHECKING', "0") != "0")

NUMPYCONFIG_SYM = []

Expand All @@ -39,6 +40,12 @@ if ENABLE_SEPARATE_COMPILATION:
NUMPYCONFIG_SYM.append(('DEFINE_NPY_ENABLE_SEPARATE_COMPILATION', '#define NPY_ENABLE_SEPARATE_COMPILATION 1'))
else:
NUMPYCONFIG_SYM.append(('DEFINE_NPY_ENABLE_SEPARATE_COMPILATION', ''))

if NPY_RELAXED_STRIDES_CHECKING:
NUMPYCONFIG_SYM.append(('DEFINE_NPY_RELAXED_STRIDES_CHECKING', '#define NPY_RELAXED_STRIDES_CHECKING 1'))
else:
NUMPYCONFIG_SYM.append(('DEFINE_NPY_RELAXED_STRIDES_CHECKING', ''))

NUMPYCONFIG_SYM.append(('VISIBILITY_HIDDEN', '__attribute__((visibility("hidden")))'))

NUMPYCONFIG_SYM.append(('NPY_ABI_VERSION', '0x%.8X' % C_ABI_VERSION))
Expand Down
12 changes: 9 additions & 3 deletions numpy/core/include/numpy/ndarraytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -753,9 +753,15 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
#define NPY_ARRAY_F_CONTIGUOUS 0x0002

/*
* Note: all 0-d arrays are C_CONTIGUOUS and F_CONTIGUOUS. An N-d
* array that is C_CONTIGUOUS is also F_CONTIGUOUS if only
* one axis has a dimension different from one (ie. a 1x3x1 array).
* Note: all 0-d arrays are C_CONTIGUOUS and F_CONTIGUOUS. If a
* 1-d array is C_CONTIGUOUS it is also F_CONTIGUOUS. Arrays with
* more then one dimension can be C_CONTIGUOUS and F_CONTIGUOUS
* at the same time if they have either zero or one element.
* If NPY_RELAXED_STRIDES_CHECKING is set, a higher dimensional
* array is always C_CONTIGUOUS and F_CONTIGUOUS if it has zero elements
* and the array is contiguous if ndarray.squeeze() is contiguous.
* I.e. dimensions for which `ndarray.shape[dimension] == 1` are
* ignored.
*/

/*
Expand Down
9 changes: 9 additions & 0 deletions numpy/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

# Set to True to enable multiple file compilations (experimental)
ENABLE_SEPARATE_COMPILATION = (os.environ.get('NPY_SEPARATE_COMPILATION', "1") != "0")
# Set to True to enable relaxed strides checking. This (mostly) means
# that `strides[dim]` is ignored if `shape[dim] == 1` when setting flags.
NPY_RELAXED_STRIDES_CHECKING = (os.environ.get('NPY_RELAXED_STRIDES_CHECKING', "0") != "0")

# XXX: ugly, we use a class to avoid calling twice some expensive functions in
# config.h/numpyconfig.h. I don't see a better way because distutils force
Expand Down Expand Up @@ -435,6 +438,9 @@ def generate_config_h(ext, build_dir):
if ENABLE_SEPARATE_COMPILATION:
moredefs.append(('ENABLE_SEPARATE_COMPILATION', 1))

if NPY_RELAXED_STRIDES_CHECKING:
moredefs.append(('NPY_RELAXED_STRIDES_CHECKING', 1))

# Get long double representation
if sys.platform != 'darwin':
rep = check_long_double_representation(config_cmd)
Expand Down Expand Up @@ -532,6 +538,9 @@ def generate_numpyconfig_h(ext, build_dir):
if ENABLE_SEPARATE_COMPILATION:
moredefs.append(('NPY_ENABLE_SEPARATE_COMPILATION', 1))

if NPY_RELAXED_STRIDES_CHECKING:
moredefs.append(('NPY_RELAXED_STRIDES_CHECKING', 1))

# Check wether we can use inttypes (C99) formats
if config_cmd.check_decl('PRIdPTR', headers = ['inttypes.h']):
moredefs.append(('NPY_USE_C99_FORMATS', 1))
Expand Down
12 changes: 12 additions & 0 deletions numpy/core/src/multiarray/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,8 +628,20 @@ _IsAligned(PyArrayObject *ap)
}
ptr = (npy_intp) PyArray_DATA(ap);
aligned = (ptr % alignment) == 0;

for (i = 0; i < PyArray_NDIM(ap); i++) {
#if NPY_RELAXED_STRIDES_CHECKING
if (PyArray_DIM(ap, i) > 1) {
/* if shape[i] == 1, the stride is never used */
aligned &= ((PyArray_STRIDES(ap)[i] % alignment) == 0);
}
else if (PyArray_DIM(ap, i) == 0) {
/* an array with zero elements is always aligned */
return 1;
}
#else /* not NPY_RELAXED_STRIDES_CHECKING */
aligned &= ((PyArray_STRIDES(ap)[i] % alignment) == 0);
#endif /* not NPY_RELAXED_STRIDES_CHECKING */
}
return aligned != 0;
}
Expand Down
22 changes: 22 additions & 0 deletions numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -3560,6 +3560,7 @@ _array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize,
int inflag, int *objflags)
{
int i;
#if NPY_RELAXED_STRIDES_CHECKING
npy_bool not_cf_contig = 0;
npy_bool nod = 0; /* A dim != 1 was found */

Expand All @@ -3573,6 +3574,7 @@ _array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize,
nod = 1;
}
}
#endif /* NPY_RELAXED_STRIDES_CHECKING */

/* Only make Fortran strides if not contiguous as well */
if ((inflag & (NPY_ARRAY_F_CONTIGUOUS|NPY_ARRAY_C_CONTIGUOUS)) ==
Expand All @@ -3582,11 +3584,21 @@ _array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize,
if (dims[i]) {
itemsize *= dims[i];
}
#if NPY_RELAXED_STRIDES_CHECKING
else {
not_cf_contig = 0;
}
if (dims[i] == 1) {
/* For testing purpose only */
strides[i] = NPY_MAX_INTP;
}
#endif /* NPY_RELAXED_STRIDES_CHECKING */
}
#if NPY_RELAXED_STRIDES_CHECKING
if (not_cf_contig) {
#else /* not NPY_RELAXED_STRIDES_CHECKING */
if ((nd > 1) && ((strides[0] != strides[nd-1]) || (dims[nd-1] > 1))) {
#endif /* not NPY_RELAXED_STRIDES_CHECKING */
*objflags = ((*objflags)|NPY_ARRAY_F_CONTIGUOUS) &
~NPY_ARRAY_C_CONTIGUOUS;
}
Expand All @@ -3600,11 +3612,21 @@ _array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize,
if (dims[i]) {
itemsize *= dims[i];
}
#if NPY_RELAXED_STRIDES_CHECKING
else {
not_cf_contig = 0;
}
if (dims[i] == 1) {
/* For testing purpose only */
strides[i] = NPY_MAX_INTP;
}
#endif /* NPY_RELAXED_STRIDES_CHECKING */
}
#if NPY_RELAXED_STRIDES_CHECKING
if (not_cf_contig) {
#else /* not NPY_RELAXED_STRIDES_CHECKING */
if ((nd > 1) && ((strides[0] != strides[nd-1]) || (dims[0] > 1))) {
#endif /* not NPY_RELAXED_STRIDES_CHECKING */
*objflags = ((*objflags)|NPY_ARRAY_C_CONTIGUOUS) &
~NPY_ARRAY_F_CONTIGUOUS;
}
Expand Down
56 changes: 52 additions & 4 deletions numpy/core/src/multiarray/flagsobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,33 @@ PyArray_UpdateFlags(PyArrayObject *ret, int flagmask)
* Check whether the given array is stored contiguously
* in memory. And update the passed in ap flags apropriately.
*
* A dimension == 1 stride is ignored for contiguous flags and a 0-sized array
* is always both C- and F-Contiguous. 0-strided arrays are not contiguous.
* The traditional rule is that for an array to be flagged as C contiguous,
* the following must hold:
*
* strides[-1] == itemsize
* strides[i] == shape[i+1] * strides[i + 1]
*
* And for an array to be flagged as F contiguous, the obvious reversal:
*
* strides[0] == itemsize
* strides[i] == shape[i - 1] * strides[i - 1]
*
* According to these rules, a 0- or 1-dimensional array is either both
* C- and F-contiguous, or neither; and an array with 2+ dimensions
* can be C- or F- contiguous, or neither, but not both. Though there
* there are exceptions for arrays with zero or one item, in the first
* case the check is relaxed up to and including the first dimension
* with shape[i] == 0. In the second case `strides == itemsize` will
* can be true for all dimensions and both flags are set.
*
* When NPY_RELAXED_STRIDES_CHECKING is set, we use a more accurate
* definition of C- and F-contiguity, in which all 0-sized arrays are
* contiguous (regardless of dimensionality), and if shape[i] == 1
* then we ignore strides[i] (since it has no affect on memory layout).
* With these new rules, it is possible for e.g. a 10x1 array to be both
* C- and F-contiguous -- but, they break downstream code which assumes
* that for contiguous arrays strides[-1] (resp. strides[0]) always
* contains the itemsize.
*/
static void
_UpdateContiguousFlags(PyArrayObject *ap)
Expand All @@ -101,9 +126,10 @@ _UpdateContiguousFlags(PyArrayObject *ap)
int i;
npy_bool is_c_contig = 1;

sd = PyArray_DESCR(ap)->elsize;
sd = PyArray_ITEMSIZE(ap);
for (i = PyArray_NDIM(ap) - 1; i >= 0; --i) {
dim = PyArray_DIMS(ap)[i];
#if NPY_RELAXED_STRIDES_CHECKING
/* contiguous by definition */
if (dim == 0) {
PyArray_ENABLEFLAGS(ap, NPY_ARRAY_C_CONTIGUOUS);
Expand All @@ -116,6 +142,17 @@ _UpdateContiguousFlags(PyArrayObject *ap)
}
sd *= dim;
}
#else /* not NPY_RELAXED_STRIDES_CHECKING */
if (PyArray_STRIDES(ap)[i] != sd) {
is_c_contig = 0;
break;
}
/* contiguous, if it got this far */
if (dim == 0) {
break;
}
sd *= dim;
#endif /* not NPY_RELAXED_STRIDES_CHECKING */
}
if (is_c_contig) {
PyArray_ENABLEFLAGS(ap, NPY_ARRAY_C_CONTIGUOUS);
Expand All @@ -125,16 +162,27 @@ _UpdateContiguousFlags(PyArrayObject *ap)
}

/* check if fortran contiguous */
sd = PyArray_DESCR(ap)->elsize;
sd = PyArray_ITEMSIZE(ap);
for (i = 0; i < PyArray_NDIM(ap); ++i) {
dim = PyArray_DIMS(ap)[i];
#if NPY_RELAXED_STRIDES_CHECKING
if (dim != 1) {
if (PyArray_STRIDES(ap)[i] != sd) {
PyArray_CLEARFLAGS(ap, NPY_ARRAY_F_CONTIGUOUS);
return;
}
sd *= dim;
}
#else /* not NPY_RELAXED_STRIDES_CHECKING */
if (PyArray_STRIDES(ap)[i] != sd) {
PyArray_CLEARFLAGS(ap, NPY_ARRAY_F_CONTIGUOUS);
return;
}
if (dim == 0) {
break;
}
sd *= dim;
#endif /* not NPY_RELAXED_STRIDES_CHECKING */
}
PyArray_ENABLEFLAGS(ap, NPY_ARRAY_F_CONTIGUOUS);
return;
Expand Down
19 changes: 15 additions & 4 deletions numpy/core/src/multiarray/multiarraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1510,20 +1510,31 @@ PyArray_EquivTypenums(int typenum1, int typenum2)
}

/*** END C-API FUNCTIONS **/

/*
* NPY_RELAXED_STRIDES_CHECKING: If the strides logic is changed, the
* order specific stride setting is not necessary.
*/
static PyObject *
_prepend_ones(PyArrayObject *arr, int nd, int ndmin)
_prepend_ones(PyArrayObject *arr, int nd, int ndmin, NPY_ORDER order)
{
npy_intp newdims[NPY_MAXDIMS];
npy_intp newstrides[NPY_MAXDIMS];
npy_intp newstride;
int i, k, num;
PyArrayObject *ret;
PyArray_Descr *dtype;

if (order == NPY_FORTRANORDER || PyArray_ISFORTRAN(arr) || PyArray_NDIM(arr) == 0) {
newstride = PyArray_DESCR(arr)->elsize;
}
else {
newstride = PyArray_STRIDES(arr)[0] * PyArray_DIMS(arr)[0];
}

num = ndmin - nd;
for (i = 0; i < num; i++) {
newdims[i] = 1;
newstrides[i] = PyArray_DESCR(arr)->elsize;
newstrides[i] = newstride;
}
for (i = num; i < ndmin; i++) {
k = i - num;
Expand Down Expand Up @@ -1664,7 +1675,7 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
* create a new array from the same data with ones in the shape
* steals a reference to ret
*/
return _prepend_ones(ret, nd, ndmin);
return _prepend_ones(ret, nd, ndmin, order);

clean_type:
Py_XDECREF(type);
Expand Down
13 changes: 10 additions & 3 deletions numpy/core/src/multiarray/shape.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,11 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
* in order to get the right orientation and
* because we can't just re-use the buffer with the
* data in the order it is in.
* NPY_RELAXED_STRIDES_CHECKING: size check is unnecessary when set.
*/
if ((order == NPY_CORDER && !PyArray_IS_C_CONTIGUOUS(self)) ||
(order == NPY_FORTRANORDER && !PyArray_IS_F_CONTIGUOUS(self))) {
if ((PyArray_SIZE(self) > 1) &&
((order == NPY_CORDER && !PyArray_IS_C_CONTIGUOUS(self)) ||
(order == NPY_FORTRANORDER && !PyArray_IS_F_CONTIGUOUS(self)))) {
int success = 0;
success = _attempt_nocopy_reshape(self, ndim, dimensions,
newstrides, order);
Expand Down Expand Up @@ -1102,7 +1104,9 @@ build_shape_string(npy_intp n, npy_intp *vals)
* the array will point to invalid memory. The caller must
* validate this!
* If an axis flagged for removal has a shape larger then one,
* the arrays contiguous flags may require updating.
* the aligned flag (and in the future the contiguous flags),
* may need explicite update.
* (check also NPY_RELAXED_STRIDES_CHECKING)
*
* For example, this can be used to remove the reduction axes
* from a reduction result once its computation is complete.
Expand All @@ -1125,4 +1129,7 @@ PyArray_RemoveAxesInPlace(PyArrayObject *arr, npy_bool *flags)

/* The final number of dimensions */
fa->nd = idim_out;

/* May not be necessary for NPY_RELAXED_STRIDES_CHECKING (see comment) */
PyArray_UpdateFlags(arr, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
}
Loading
0