def flip(a, axis=None): """Reverse the order of elements in an array along the given axis. Note that ``flip`` function has been introduced since NumPy v1.12. The contents of this document is the same as the original one. Args: a (~cupy.ndarray): Input array. axis (int or tuple of int or None): Axis or axes along which to flip over. The default, ``axis=None``, will flip over all of the axes of the input array. If axis is negative it counts from the last to the first axis. If axis is a tuple of ints, flipping is performed on all of the axes specified in the tuple. Returns: ~cupy.ndarray: Output array. .. seealso:: :func:`numpy.flip` """ a_ndim = a.ndim if a_ndim < 1: raise numpy.AxisError('Input must be >= 1-d') axes = internal._normalize_axis_indices(axis, a_ndim) return _flip(a, axes)
def _init_nd_and_axes(x, axes): # See documentation in scipy.fft._helper._init_nd_shape_and_axes # except shape argument is always None and doesn't return new shape axes = internal._normalize_axis_indices(axes, x.ndim, sort_axes=False) if not len(axes): raise ValueError('when provided, axes cannot be empty') if any(x.shape[ax] < 1 for ax in axes): raise ValueError('invalid number of data points specified') return axes
def gradient(f, *varargs, axis=None, edge_order=1): """Return the gradient of an N-dimensional array. The gradient is computed using second order accurate central differences in the interior points and either first or second order accurate one-sides (forward or backwards) differences at the boundaries. The returned gradient hence has the same shape as the input array. Args: f (cupy.ndarray): An N-dimensional array containing samples of a scalar function. varargs (list of scalar or array, optional): Spacing between f values. Default unitary spacing for all dimensions. Spacing can be specified using: 1. single scalar to specify a sample distance for all dimensions. 2. N scalars to specify a constant sample distance for each dimension. i.e. `dx`, `dy`, `dz`, ... 3. N arrays to specify the coordinates of the values along each dimension of F. The length of the array must match the size of the corresponding dimension 4. Any combination of N scalars/arrays with the meaning of 2. and 3. If `axis` is given, the number of varargs must equal the number of axes. Default: 1. edge_order ({1, 2}, optional): The gradient is calculated using N-th order accurate differences at the boundaries. Default: 1. axis (None or int or tuple of ints, optional): The gradient is calculated only along the given axis or axes. The default (axis = None) is to calculate the gradient for all the axes of the input array. axis may be negative, in which case it counts from the last to the first axis. Returns: gradient (cupy.ndarray or list of cupy.ndarray): A set of ndarrays (or a single ndarray if there is only one dimension) corresponding to the derivatives of f with respect to each dimension. Each derivative has the same shape as f. .. seealso:: :func:`numpy.gradient` """ f = cupy.asanyarray(f) ndim = f.ndim # number of dimensions axes = internal._normalize_axis_indices(axis, ndim, sort_axes=False) len_axes = len(axes) n = len(varargs) if n == 0: # no spacing argument - use 1 in all axes dx = [1.0] * len_axes elif n == 1 and cupy.ndim(varargs[0]) == 0: # single scalar for all axes dx = varargs * len_axes elif n == len_axes: # scalar or 1d array for each axis dx = list(varargs) for i, distances in enumerate(dx): if cupy.ndim(distances) == 0: continue elif cupy.ndim(distances) != 1: raise ValueError("distances must be either scalars or 1d") if len(distances) != f.shape[axes[i]]: raise ValueError("when 1d, distances must match " "the length of the corresponding dimension") if numpy.issubdtype(distances.dtype, numpy.integer): # Convert numpy integer types to float64 to avoid modular # arithmetic in np.diff(distances). distances = distances.astype(numpy.float64) diffx = cupy.diff(distances) # if distances are constant reduce to the scalar case # since it brings a consistent speedup if (diffx == diffx[0]).all(): # synchronize diffx = diffx[0] dx[i] = diffx else: raise TypeError("invalid number of arguments") if edge_order > 2: raise ValueError("'edge_order' greater than 2 not supported") # use central differences on interior and one-sided differences on the # endpoints. This preserves second order-accuracy over the full domain. outvals = [] # create slice objects --- initially all are [:, :, ..., :] slice1 = [slice(None)] * ndim slice2 = [slice(None)] * ndim slice3 = [slice(None)] * ndim slice4 = [slice(None)] * ndim otype = f.dtype if numpy.issubdtype(otype, numpy.inexact): pass else: # All other types convert to floating point. # First check if f is a numpy integer type; if so, convert f to float64 # to avoid modular arithmetic when computing the changes in f. if numpy.issubdtype(otype, numpy.integer): f = f.astype(numpy.float64) otype = numpy.float64 for axis, ax_dx in zip(axes, dx): if f.shape[axis] < edge_order + 1: raise ValueError( "Shape of array too small to calculate a numerical gradient, " "at least (edge_order + 1) elements are required.") # result allocation out = cupy.empty_like(f, dtype=otype) # spacing for the current axis uniform_spacing = cupy.ndim(ax_dx) == 0 # Numerical differentiation: 2nd order interior slice1[axis] = slice(1, -1) slice2[axis] = slice(None, -2) slice3[axis] = slice(1, -1) slice4[axis] = slice(2, None) if uniform_spacing: out[tuple(slice1)] = (f[tuple(slice4)] - f[tuple(slice2)]) / (2.0 * ax_dx) else: dx1 = ax_dx[0:-1] dx2 = ax_dx[1:] dx_sum = dx1 + dx2 a = -(dx2) / (dx1 * dx_sum) b = (dx2 - dx1) / (dx1 * dx2) c = dx1 / (dx2 * dx_sum) # fix the shape for broadcasting shape = [1] * ndim shape[axis] = -1 a.shape = b.shape = c.shape = tuple(shape) # 1D equivalent -- out[1:-1] = a * f[:-2] + b * f[1:-1] + c * f[2:] out[tuple(slice1)] = (a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]) # Numerical differentiation: 1st order edges if edge_order == 1: slice1[axis] = 0 slice2[axis] = 1 slice3[axis] = 0 dx_0 = ax_dx if uniform_spacing else ax_dx[0] # 1D equivalent -- out[0] = (f[1] - f[0]) / (x[1] - x[0]) out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_0 slice1[axis] = -1 slice2[axis] = -1 slice3[axis] = -2 dx_n = ax_dx if uniform_spacing else ax_dx[-1] # 1D equivalent -- out[-1] = (f[-1] - f[-2]) / (x[-1] - x[-2]) out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_n # Numerical differentiation: 2nd order edges else: slice1[axis] = 0 slice2[axis] = 0 slice3[axis] = 1 slice4[axis] = 2 if uniform_spacing: a = -1.5 / ax_dx b = 2.0 / ax_dx c = -0.5 / ax_dx else: dx1 = ax_dx[0] dx2 = ax_dx[1] dx_sum = dx1 + dx2 a = -(2.0 * dx1 + dx2) / (dx1 * (dx_sum)) b = dx_sum / (dx1 * dx2) c = -dx1 / (dx2 * (dx_sum)) # 1D equivalent -- out[0] = a * f[0] + b * f[1] + c * f[2] out[tuple(slice1)] = (a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]) slice1[axis] = -1 slice2[axis] = -3 slice3[axis] = -2 slice4[axis] = -1 if uniform_spacing: a = 0.5 / ax_dx b = -2.0 / ax_dx c = 1.5 / ax_dx else: dx1 = ax_dx[-2] dx2 = ax_dx[-1] dx_sum = dx1 + dx2 a = (dx2) / (dx1 * (dx_sum)) b = -dx_sum / (dx1 * dx2) c = (2.0 * dx2 + dx1) / (dx2 * (dx_sum)) # 1D equivalent -- out[-1] = a * f[-3] + b * f[-2] + c * f[-1] out[tuple(slice1)] = (a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]) outvals.append(out) # reset the slice object in this dimension to ":" slice1[axis] = slice(None) slice2[axis] = slice(None) slice3[axis] = slice(None) slice4[axis] = slice(None) if len_axes == 1: return outvals[0] else: return outvals