diff --git a/bridge/npbackend/bohrium/array_manipulation.py b/bridge/npbackend/bohrium/array_manipulation.py index ef06722ba..bcc94012b 100644 --- a/bridge/npbackend/bohrium/array_manipulation.py +++ b/bridge/npbackend/bohrium/array_manipulation.py @@ -7,6 +7,7 @@ from . import bhary from . import _util from .bhary import fix_biclass_wrapper +from . import numpy_backport @fix_biclass_wrapper @@ -154,7 +155,7 @@ def diagonal(ary, offset=0, axis1=0, axis2=1): ary = ary[..., :diag_size, offset:(offset + diag_size)] ret_strides = ary.strides[:-2] + (ary.strides[-1] + ary.strides[-2],) - return numpy.lib.stride_tricks.as_strided(ary, shape=ret_shape, strides=ret_strides) + return numpy_backport.as_strided(ary, shape=ret_shape, strides=ret_strides) @fix_biclass_wrapper diff --git a/bridge/npbackend/bohrium/bhary.pyx b/bridge/npbackend/bohrium/bhary.pyx index aa71efe70..4a6b6abac 100644 --- a/bridge/npbackend/bohrium/bhary.pyx +++ b/bridge/npbackend/bohrium/bhary.pyx @@ -28,6 +28,7 @@ If not, see . import sys from ._util import dtype_equal, dtype_support, dtype_in from . import target +from . import numpy_backport import operator import functools import numpy_force as numpy @@ -234,7 +235,7 @@ def get_bhc(ary): # All this is simply a hack to reinterpret 'ary' as a complex view of the 'base' offset = (get_cdata(ary) - get_cdata(base)) // base.itemsize cary = numpy.frombuffer(base, dtype=base.dtype, offset=offset * base.itemsize) - cary = numpy.lib.stride_tricks.as_strided(cary, ary.shape, ary.strides, subok=True) + cary = numpy_backport.as_strided(cary, ary.shape, ary.strides) # if the view/base offset is aligned with the complex dtype, we know that the # 'ary' is a view of the real part of 'base' diff --git a/bridge/npbackend/bohrium/numpy_backport.py b/bridge/npbackend/bohrium/numpy_backport.py new file mode 100644 index 000000000..4e65c7b8a --- /dev/null +++ b/bridge/npbackend/bohrium/numpy_backport.py @@ -0,0 +1,99 @@ +""" +NumPy backport implementations +============================== + +We use some NumPy functions introduced in later version of NumPy. +This module implement those versions +""" + +import numpy_force as numpy + + +# NB: The older version of NumPy `as_strided()` does'nt support `subok` so we include the implementation here and +# makes `subok` default true. +def as_strided(x, shape=None, strides=None, subok=True, writeable=True): + """ + Create a view into the array with the given shape and strides. + .. warning:: This function has to be used with extreme care, see notes. + Parameters + ---------- + x : ndarray + Array to create a new. + shape : sequence of int, optional + The shape of the new array. Defaults to ``x.shape``. + strides : sequence of int, optional + The strides of the new array. Defaults to ``x.strides``. + subok : bool, optional + .. versionadded:: 1.10 + If True, subclasses are preserved. + writeable : bool, optional + .. versionadded:: 1.12 + If set to False, the returned array will always be readonly. + Otherwise it will be writable if the original array was. It + is advisable to set this to False if possible (see Notes). + Returns + ------- + view : ndarray + See also + -------- + broadcast_to: broadcast an array to a given shape. + reshape : reshape an array. + Notes + ----- + ``as_strided`` creates a view into the array given the exact strides + and shape. This means it manipulates the internal data structure of + ndarray and, if done incorrectly, the array elements can point to + invalid memory and can corrupt results or crash your program. + It is advisable to always use the original ``x.strides`` when + calculating new strides to avoid reliance on a contiguous memory + layout. + Furthermore, arrays created with this function often contain self + overlapping memory, so that two elements are identical. + Vectorized write operations on such arrays will typically be + unpredictable. They may even give different results for small, large, + or transposed arrays. + Since writing to these arrays has to be tested and done with great + care, you may want to use ``writeable=False`` to avoid accidental write + operations. + For these reasons it is advisable to avoid ``as_strided`` when + possible. + """ + + class DummyArray(object): + """Dummy object that just exists to hang __array_interface__ dictionaries + and possibly keep alive a reference to a base array. + """ + + def __init__(self, interface, base=None): + self.__array_interface__ = interface + self.base = base + + def _maybe_view_as_subclass(original_array, new_array): + if type(original_array) is not type(new_array): + # if input was an ndarray subclass and subclasses were OK, + # then view the result as that subclass. + new_array = new_array.view(type=type(original_array)) + # Since we have done something akin to a view from original_array, we + # should let the subclass finalize (if it has it implemented, i.e., is + # not None). + if new_array.__array_finalize__: + new_array.__array_finalize__(original_array) + return new_array + + # first convert input to array, possibly keeping subclass + x = numpy.array(x, copy=False, subok=subok) + interface = dict(x.__array_interface__) + if shape is not None: + interface['shape'] = tuple(shape) + if strides is not None: + interface['strides'] = tuple(strides) + + array = numpy.asarray(DummyArray(interface, base=x)) + # The route via `__interface__` does not preserve structured + # dtypes. Since dtype should remain unchanged, we set it explicitly. + array.dtype = x.dtype + view = _maybe_view_as_subclass(x, array) + + if view.flags.writeable and not writeable: + view.flags.writeable = False + return view diff --git a/bridge/npbackend/bohrium/reorganization.py b/bridge/npbackend/bohrium/reorganization.py index 31887b6fd..328c50df5 100644 --- a/bridge/npbackend/bohrium/reorganization.py +++ b/bridge/npbackend/bohrium/reorganization.py @@ -4,7 +4,6 @@ """ import warnings import numpy_force as numpy -from numpy.lib.stride_tricks import as_strided from . import bhary from ._util import is_scalar from .bhary import fix_biclass_wrapper, get_bhc @@ -12,6 +11,7 @@ from . import array_create from . import array_manipulation from . import ufuncs +from . import numpy_backport @fix_biclass_wrapper @@ -311,11 +311,11 @@ def put(a, ind, v, mode='raise'): if indexes.size > values.size: if values.size == 1: # When 'values' is a scalar, we can broadcast it to match 'indexes' - values = as_strided(values, shape=indexes.shape, strides=(0,)) + values = numpy_backport.as_strided(values, shape=indexes.shape, strides=(0,)) else: # else we repeat 'values' enough times to be larger than 'indexes' - values = as_strided(values, - shape=(indexes.size // values.size + 2, values.size), - strides=(0, values.itemsize)) + values = numpy_backport.as_strided(values, + shape=(indexes.size // values.size + 2, values.size), + strides=(0, values.itemsize)) values = array_manipulation.flatten(values, always_copy=False) # When 'values' is too large, we simple cut the end off diff --git a/bridge/npbackend/bohrium/signal.py b/bridge/npbackend/bohrium/signal.py index c6526a469..da3a37cc8 100644 --- a/bridge/npbackend/bohrium/signal.py +++ b/bridge/npbackend/bohrium/signal.py @@ -6,13 +6,13 @@ """ import numpy_force as numpy -from numpy_force.lib.stride_tricks import as_strided from . import array_create from . import bhary from . import ufuncs from . import linalg from . import summations from . import _util +from . import numpy_backport # 1d @@ -34,8 +34,8 @@ def _correlate_and_convolve_body(vector, filter, d, mode): padded[0:filter.size - 1] = 0 padded[filter.size - 1:vector.size + filter.size - 1] = vector padded[vector.size + filter.size - 1:] = 0 - s = as_strided(padded, shape=(padded.shape[0] - filter.size + 1, filter.size), - strides=(padded.strides[0], padded.strides[0])) + s = numpy_backport.as_strided(padded, shape=(padded.shape[0] - filter.size + 1, filter.size), + strides=(padded.strides[0], padded.strides[0])) result = linalg.dot(s, filter) if mode == 'same': return result[d:vector.size + d] diff --git a/bridge/npbackend/bohrium/target/target_numpy.py b/bridge/npbackend/bohrium/target/target_numpy.py index ff087ece7..c49d4bb46 100644 --- a/bridge/npbackend/bohrium/target/target_numpy.py +++ b/bridge/npbackend/bohrium/target/target_numpy.py @@ -3,6 +3,7 @@ """ from .. import bhc from .._util import dtype_name +from .. import numpy_backport import numpy as np import mmap import time @@ -53,7 +54,7 @@ class View(interface.View): def __init__(self, ndim, start, shape, strides, base): super(View, self).__init__(ndim, start, shape, strides, base) buf = np.frombuffer(self.base.mmap, dtype=self.dtype, offset=self.start) - self.ndarray = np.lib.stride_tricks.as_strided(buf, shape, self.strides) + self.ndarray = numpy_backport.as_strided(buf, shape, self.strides) def views2numpy(views):