def setitem(ary, loc, value): """ Set the 'value' into 'ary' at the location specified through 'loc'. 'loc' can be a scalar or a slice object, or a tuple thereof """ if not isinstance(loc, tuple): loc = (loc,) # Let's try to convert non-arrays and non-scalars to an array # e.g. converting a python list to an array if not (bhary.check(value) or np.isscalar(value)): value = array_create.array(value) # Lets make sure that not all dimensions are indexed by integers loc = list(loc) if len(loc) == ary.ndim and all((np.isscalar(s) for s in loc)): # 'slice' doesn't support negative start index if loc[0] < 0: loc[0] += ary.shape[0] loc[0] = slice(loc[0], loc[0]+1) # Copy the 'value' to 'ary' using the 'loc' if ary.ndim == 0: assign(value, ary) else: assign(value, ary[tuple(loc)])
def assign(ary, out): """Copy data from array 'ary' to 'out'""" if not np.isscalar(ary): (ary, out) = broadcast_arrays(ary, out) # We ignore self assignments if bhary.get_base(ary) is bhary.get_base(out) and \ bhary.identical_views(ary, out): return # We use a tmp array if the in-/out-put has memory conflicts if overlap_conflict(out, ary): tmp = array_create.empty_like(out) assign(ary, tmp) return assign(tmp, out) if bhary.check(out): out = get_bhc(out) if not np.isscalar(ary): if not bhary.check(ary): # Convert the NumPy array to bohrium ary = array_create.array(ary) ary = get_bhc(ary) target.ufunc(identity, out, ary) else: if bhary.check(ary): if "BH_SYNC_WARN" in os.environ: import warnings warnings.warn("BH_SYNC_WARN: Copying the array to NumPy", RuntimeWarning, stacklevel=2) get_base(ary)._data_bhc2np() out[...] = ary
def broadcast_arrays(*args): """ Broadcast any number of arrays against each other. .. note:: This function is very similar to NumPy's `broadcast_arrays()` Parameters ---------- `array_list` : array_likes The arrays to broadcast. Returns ------- broadcasted : list of arrays These arrays are views on the original arrays or the untouched originals. They are typically not contiguous. Furthermore, more than one element of a broadcasted array may refer to a single memory location. If you need to write to the arrays, make copies first. shape : tuple The shape the arrays are broadcasted to """ try: if len(args) == 0: return ([], []) # Common case where nothing needs to be broadcasted. bcast = numpy.broadcast(*args) if all(array.shape == bcast.shape for array in args if not numpy.isscalar(array)): return (args, bcast.shape) ret = [] # We use NumPy's broadcast_arrays() to broadcast the views. # Notice that the 'subok' argument is first introduced in version 10 of NumPy try: bargs = numpy.broadcast_arrays(*args, subok=True) except TypeError as err: if "subok" in err.message: bargs = numpy.broadcast_arrays(*args) else: raise for a, b in zip(args, bargs): if numpy.isscalar(a) or not isinstance(a, numpy.ndarray): ret.append(b) elif bhary.identical_views(a, b): ret.append(a) else: ret.append(b) except ValueError as msg: if str(msg).find( "shape mismatch: objects cannot be broadcast to a single shape" ) != -1: shapes = [arg.shape for arg in args] raise ValueError( "shape mismatch: objects cannot be broadcasted to a single shape: %s" % shapes) raise return (ret, bcast.shape)
def broadcast_arrays(*args): """ Broadcast any number of arrays against each other. .. note:: This function differ from NumPy in one way: it does not touch arrays that does not need broadcasting Parameters ---------- `*args` : array_likes The arrays to broadcast. Returns ------- broadcasted : list of arrays These arrays are views on the original arrays or the untouched originals. They are typically not contiguous. Furthermore, more than one element of a broadcasted array may refer to a single memory location. If you need to write to the arrays, make copies first. Examples -------- >>> x = np.array([[1,2,3]]) >>> y = np.array([[1],[2],[3]]) >>> np.broadcast_arrays(x, y) [array([[1, 2, 3], [1, 2, 3], [1, 2, 3]]), array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])] Here is a useful idiom for getting contiguous copies instead of non-contiguous views. >>> [np.array(a) for a in np.broadcast_arrays(x, y)] [array([[1, 2, 3], [1, 2, 3], [1, 2, 3]]), array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])] """ try: ret = [] bargs = numpy.broadcast_arrays(*args) for a, b in zip(args, bargs): if numpy.isscalar(a) or not isinstance(a, numpy.ndarray): ret.append(b) elif bhary.identical_views(a, b): ret.append(a) else: ret.append(b) except ValueError as msg: if str(msg).find("shape mismatch: objects cannot be broadcast to a single shape") != -1: shapes = [arg.shape for arg in args] raise ValueError("shape mismatch: objects cannot be broadcast to a single shape: %s" % shapes) return ret
def masked_set(ary, bool_mask, value): """ Set the 'value' into 'ary' at the location specified through 'bool_mask'. """ if numpy.isscalar(value) and ufuncs.isfinite(value): ary *= ~bool_mask ary += bool_mask * value else: ary[reorganization.nonzero(bool_mask)] = value
def masked_set(ary, bool_mask, value): """ Set the 'value' into 'ary' at the location specified through 'bool_mask'. """ if numpy.isscalar(value): ary *= ~bool_mask ary += bool_mask * value else: ary[reorganization.nonzero(bool_mask)] = value
def broadcast_arrays(*args): """ Broadcast any number of arrays against each other. .. note:: This function differ from NumPy in one way: it does not touch arrays that does not need broadcasting Parameters ---------- `*args` : array_likes The arrays to broadcast. Returns ------- broadcasted : list of arrays These arrays are views on the original arrays or the untouched originals. They are typically not contiguous. Furthermore, more than one element of a broadcasted array may refer to a single memory location. If you need to write to the arrays, make copies first. Examples -------- >>> x = np.array([[1,2,3]]) >>> y = np.array([[1],[2],[3]]) >>> np.broadcast_arrays(x, y) [array([[1, 2, 3], [1, 2, 3], [1, 2, 3]]), array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])] Here is a useful idiom for getting contiguous copies instead of non-contiguous views. >>> [np.array(a) for a in np.broadcast_arrays(x, y)] [array([[1, 2, 3], [1, 2, 3], [1, 2, 3]]), array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])] """ ret = [] bargs = numpy.broadcast_arrays(*args) for a, b in itertools.izip(args, bargs): if numpy.isscalar(a): ret.append(b) elif ndarray.identical_views(a, b): ret.append(a) else: ret.append(b) return ret
def broadcast_arrays(*args): """ Broadcast any number of arrays against each other. .. note:: This function differ from NumPy in one way: it does not touch arrays that does not need broadcasting Parameters ---------- `*args` : array_likes The arrays to broadcast. Returns ------- broadcasted : list of arrays These arrays are views on the original arrays or the untouched originals. They are typically not contiguous. Furthermore, more than one element of a broadcasted array may refer to a single memory location. If you need to write to the arrays, make copies first. Examples -------- >>> x = np.array([[1,2,3]]) >>> y = np.array([[1],[2],[3]]) >>> np.broadcast_arrays(x, y) [array([[1, 2, 3], [1, 2, 3], [1, 2, 3]]), array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])] Here is a useful idiom for getting contiguous copies instead of non-contiguous views. >>> [np.array(a) for a in np.broadcast_arrays(x, y)] [array([[1, 2, 3], [1, 2, 3], [1, 2, 3]]), array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])] """ ret = [] bargs = numpy.broadcast_arrays(*args) for a, b in itertools.izip(args, bargs): if numpy.isscalar(a): ret.append(b) elif bhary.identical_views(a, b): ret.append(a) else: ret.append(b) return ret
def assign(ary, out): """Copy data from array 'a' to 'out'""" if not np.isscalar(ary): (ary, out) = broadcast_arrays(ary, out) #We use a tmp array if the in-/out-put has memory conflicts if overlap_conflict(out, ary): tmp = array_create.empty_like(out) assign(ary, tmp) return assign(tmp, out) if ndarray.check(out): out = get_bhc(out) if not np.isscalar(ary): if not ndarray.check(ary): ary = array_create.array(ary)#Convert the NumPy array to bohrium ary = get_bhc(ary) target.ufunc(identity, out, ary) else: if ndarray.check(ary): get_base(ary)._data_bhc2np() out[...] = ary
def setitem(ary, loc, value): """Set the 'value' into 'ary' at the location specified through 'loc'. 'loc' can be a scalar or a slice object, or a tuple thereof""" if not isinstance(loc, tuple): loc = (loc,) #Lets make sure that not all dimensions are indexed by integers loc = list(loc) if len(loc) == ary.ndim and all((np.isscalar(s) for s in loc)): if loc[0] < 0:#'slice' doesn't support negative start index loc[0] += ary.shape[0] loc[0] = slice(loc[0], loc[0]+1) #Copy the 'value' to 'ary' using the 'loc' if ary.ndim == 0: assign(value, ary) else: assign(value, ary[tuple(loc)])
def overlap_conflict(out, *inputs): """ Return True when there is a possible memory conflict between the output and the inputs. :param Mixed out: Array in the role of being written to. :param Mixed inputs: Arrays in the role being read from. :returns: True in case of conflict. :rtype: bool """ for i in inputs: if not np.isscalar(i): if np.may_share_memory(out, i) and not (out.ndim == i.ndim and \ out.strides == i.strides and out.shape == i.shape and \ out.ctypes.data == i.ctypes.data): return True return False
def broadcast_arrays(*args): """ Broadcast any number of arrays against each other. .. note:: This function is very similar to NumPy's `broadcast_arrays()` Parameters ---------- `array_list` : array_likes The arrays to broadcast. Returns ------- broadcasted : list of arrays These arrays are views on the original arrays or the untouched originals. They are typically not contiguous. Furthermore, more than one element of a broadcasted array may refer to a single memory location. If you need to write to the arrays, make copies first. shape : tuple The shape the arrays are broadcasted to """ try: if len(args) == 0: return ([], []) if len(args) == 1: if numpy.isscalar(args[0]): # It is possible that `args[0]` is a scalar shape = (1,) else: shape = args[0].shape return (args, shape) # Common case where nothing needs to be broadcasted. bcast = numpy.broadcast(*args) if all(array.shape == bcast.shape for array in args if not numpy.isscalar(array)): return (args, bcast.shape) ret = [] # We use NumPy's broadcast_arrays() to broadcast the views. # Notice that the 'subok' argument is first introduced in version 10 of NumPy try: bargs = numpy.broadcast_arrays(*args, subok=True) except TypeError as err: if "subok" in err.message: bargs = numpy.broadcast_arrays(*args) else: raise # The broadcasted view inherits dynamic changes if there are any. # Used for broadcasting dynamic views within a do_while loop bcast_array = args[0] bcast_dvi = bcast_array.bhc_dynamic_view_info for a, b in zip(args, bargs): # If the broadcast view changes shape between iterations, # force the same change to the views being broadcasted. # Used in regard to iterators within do_while loops. a_dvi = a.bhc_dynamic_view_info # If array that is broadcasted has dynamic changes, the # broadcasted array must inherit these if a_dvi: b_dvi = deepcopy(a_dvi) else: b_dvi = loop.DynamicViewInfo({}, a.shape, a.strides) # If the array that is broadcasted from has changes in shape # must these changes also be inherited by the broadcasted array if bcast_dvi: # If the view contains a slide in a broadcasted dimension, # the slide must be inherited for dim in bcast_dvi.dims_with_changes(): # If the array, which is broadcasted from, does not have # dynamic changes, there are no changes to add if bcast_dvi.dim_shape_change(dim) == 0: continue # The array, which is broadcasted from, has dynamic changes # while the broadcasted array does not. Add the changes to the # broadcasted array elif b_dvi.dim_shape_change(dim) == 0: for (_, shape_change, step_delay, shape, stride) in bcast_dvi.changes_in_dim(dim): # No reason to add a change of 0 in the dimension if shape_change == 0: continue b_dvi.add_dynamic_change(dim, 0, shape_change, step_delay, shape, stride) # Both array, which is broadcasted from, and the broadcasted array has # dynamic changes. Make sure they are the same. If not the change cannot # be guessed, which results in an error. elif b_dvi.dim_shape_change(dim) != 0 and \ b_dvi.dim_shape_change(dim) != bcast_dvi.dim_shape_change(dim): raise loop.IteratorIllegalBroadcast( dim, a.shape, a_dvi.dim_shape_change(dim), bcast_array.shape, bcast_dvi.dim_shape_change(dim)) # Add the dynamic changes, if any if b_dvi.has_changes(): b.bhc_dynamic_view_info = b_dvi # Append the broadcasted array ret.append(b) except ValueError as msg: if str(msg).find("shape mismatch: objects cannot be broadcast to a single shape") != -1: shapes = [arg.shape for arg in args] raise ValueError("shape mismatch: objects cannot be broadcasted to a single shape: %s" % shapes) raise return (ret, bcast.shape)
def where(condition, x=None, y=None): """ where(condition, [x, y]) Return elements, either from `x` or `y`, depending on `condition`. If only `condition` is given, return ``condition.nonzero()``. Parameters ---------- condition : array_like, bool When True, yield `x`, otherwise yield `y`. x, y : array_like, optional Values from which to choose. `x` and `y` need to have the same shape as `condition`. Returns ------- out : ndarray or tuple of ndarrays If both `x` and `y` are specified, the output array contains elements of `x` where `condition` is True, and elements from `y` elsewhere. If only `condition` is given, return the tuple ``condition.nonzero()``, the indices where `condition` is True. See Also -------- nonzero, choose Notes ----- If `x` and `y` are given and input arrays are 1-D, `where` is equivalent to:: [xv if c else yv for (c,xv,yv) in zip(condition,x,y)] Examples -------- >>> np.where([[True, False], [True, True]], ... [[1, 2], [3, 4]], ... [[9, 8], [7, 6]]) array([[1, 8], [3, 4]]) >>> np.where([[0, 1], [1, 0]]) (array([0, 1]), array([1, 0])) >>> x = np.arange(9.).reshape(3, 3) >>> np.where( x > 5 ) (array([2, 2, 2]), array([0, 1, 2])) >>> x[np.where( x > 3.0 )] # Note: result is 1D. array([ 4., 5., 6., 7., 8.]) >>> np.where(x < 5, x, -1) # Note: broadcasting. array([[ 0., 1., 2.], [ 3., 4., -1.], [-1., -1., -1.]]) Find the indices of elements of `x` that are in `goodvalues`. >>> goodvalues = [3, 4, 7] >>> ix = np.in1d(x.ravel(), goodvalues).reshape(x.shape) >>> ix array([[False, False, False], [ True, True, False], [False, True, False]], dtype=bool) >>> np.where(ix) (array([1, 1, 2]), array([0, 1, 1])) """ if not (bhary.check(condition) or bhary.check(x) or bhary.check(y)): return numpy.where(condition, x, y) if x is None or y is None: warnings.warn("Bohrium only supports where() when 'x' and 'y' are specified", stacklevel=2) return numpy.where(condition) # Let's find a non-scalar and make sure that non-scalars are Bohrium arrays t = None if not numpy.isscalar(condition): condition = array_create.array(condition) t = condition if not numpy.isscalar(x): x = array_create.array(x) t = x if not numpy.isscalar(y): y = array_create.array(y) t = y # All arguments are scalars if t is None: if condition: return x else: return y ret = array_create.zeros_like(t) ret += condition * x ret += ~condition * y return ret
def where(condition, x=None, y=None): """ where(condition, [x, y]) Return elements, either from `x` or `y`, depending on `condition`. If only `condition` is given, return ``condition.nonzero()``. Parameters ---------- condition : array_like, bool When True, yield `x`, otherwise yield `y`. x, y : array_like, optional Values from which to choose. `x` and `y` need to have the same shape as `condition`. Returns ------- out : ndarray or tuple of ndarrays If both `x` and `y` are specified, the output array contains elements of `x` where `condition` is True, and elements from `y` elsewhere. If only `condition` is given, return the tuple ``condition.nonzero()``, the indices where `condition` is True. See Also -------- nonzero, choose Notes ----- If `x` and `y` are given and input arrays are 1-D, `where` is equivalent to:: [xv if c else yv for (c,xv,yv) in zip(condition,x,y)] Examples -------- >>> np.where([[True, False], [True, True]], ... [[1, 2], [3, 4]], ... [[9, 8], [7, 6]]) array([[1, 8], [3, 4]]) >>> np.where([[0, 1], [1, 0]]) (array([0, 1]), array([1, 0])) >>> x = np.arange(9.).reshape(3, 3) >>> np.where( x > 5 ) (array([2, 2, 2]), array([0, 1, 2])) >>> x[np.where( x > 3.0 )] # Note: result is 1D. array([ 4., 5., 6., 7., 8.]) >>> np.where(x < 5, x, -1) # Note: broadcasting. array([[ 0., 1., 2.], [ 3., 4., -1.], [-1., -1., -1.]]) Find the indices of elements of `x` that are in `goodvalues`. >>> goodvalues = [3, 4, 7] >>> ix = np.in1d(x.ravel(), goodvalues).reshape(x.shape) >>> ix array([[False, False, False], [ True, True, False], [False, True, False]], dtype=bool) >>> np.where(ix) (array([1, 1, 2]), array([0, 1, 1])) """ if x is None or y is None: warnings.warn( "Bohrium only supports where() when 'x' and 'y' are specified", stacklevel=2) return numpy.where(condition) if not (bhary.check(condition) or bhary.check(x) or bhary.check(y)): return numpy.where(condition, x, y) # Make sure that non-scalars are Bohrium arrays if numpy.isscalar(condition): condition = bool(condition) else: condition = array_create.array(condition).astype("bool") if not numpy.isscalar(x): x = array_create.array(x) if not numpy.isscalar(y): y = array_create.array(y) # Shortcut if all arguments are scalars if all(numpy.isscalar(k) or k.size == 1 for k in (x, y, condition)): return x if condition else y # Find appropriate output type array_types = [] scalar_types = [] for v in (x, y): if numpy.isscalar(v): scalar_types.append(type(v)) else: array_types.append(v.dtype) out_type = numpy.find_common_type(array_types, scalar_types) # Shortcut if input arrays are finite if ufuncs.isfinite(x).all() and ufuncs.isfinite(y).all(): if numpy.isscalar(condition): res = condition * x + (not condition) * y else: res = condition * x + ufuncs.logical_not(condition) * y if numpy.isscalar(res): return out_type(res) else: return res.astype(out_type) # General case: use fancy indexing (condition, x, y), newshape = array_manipulation.broadcast_arrays(condition, x, y) ret = array_create.zeros(newshape, dtype=out_type) ret[condition] = x if numpy.isscalar(x) else x[condition] ret[~condition] = y if numpy.isscalar(y) else y[~condition] return ret
def reduce(self, ary, axis=0, out=None): """ A Bohrium Reduction Reduces `ary`'s dimension by len('axis'), by applying ufunc along the axes in 'axis'. Let :math:`ary.shape = (N_0, ..., N_i, ..., N_{M-1})`. Then :math:`ufunc.reduce(ary, axis=i)[k_0, ..,k_{i-1}, k_{i+1}, .., k_{M-1}]` = the result of iterating `j` over :math:`range(N_i)`, cumulatively applying ufunc to each :math:`ary[k_0, ..,k_{i-1}, j, k_{i+1}, .., k_{M-1}]`. For a one-dimensional array, reduce produces results equivalent to: :: r = op.identity # op = ufunc for i in range(len(A)): r = op(r, A[i]) return r For example, add.reduce() is equivalent to sum(). Parameters ---------- ary : array_like The array to act on. axis : None or int or tuple of ints, optional Axis or axes along which a reduction is performed. The default (`axis` = 0) is perform a reduction over the first dimension of the input array. `axis` may be negative, in which case it counts from the last to the first axis. .. versionadded:: 1.7.0 If this is `None`, a reduction is performed over all the axes. If this is a tuple of ints, a reduction is performed on multiple axes, instead of a single axis or all the axes as before. For operations which are either not commutative or not associative, doing a reduction over multiple axes is not well-defined. The ufuncs do not currently raise an exception in this case, but will likely do so in the future. out : ndarray, optional A location into which the result is stored. If not provided, a freshly-allocated array is returned. Returns ------- r : ndarraout The reduced array. If `out` was supplied, `r` is a reference to it. Examples -------- >>> np.multiply.reduce([2,3,5]) 30 A multi-dimensional array example: >>> X = np.arange(8).reshape((2,2,2)) >>> X array([[[0, 1], [2, 3]], [[4, 5], [6, 7]]]) >>> np.add.reduce(X, 0) array([[ 4, 6], [ 8, 10]]) >>> np.add.reduce(X) # confirm: default axis value is 0 array([[ 4, 6], [ 8, 10]]) >>> np.add.reduce(X, 1) array([[ 2, 4], [10, 12]]) >>> np.add.reduce(X, 2) array([[ 1, 5], [ 9, 13]]) """ if out is not None: if ndarray.check(out): if not ndarray.check(ary): ary = array_create.array(ary) else: if ndarray.check(ary): ary = a.copy2numpy() #Let NumPy handle NumPy array reductions if not ndarray.check(ary): func = eval("np.%s.reduce" % self.info['name']) return func(ary, axis=axis, out=out) #Make sure that 'axis' is a list of dimensions to reduce if axis is None: axis = range(ary.ndim)#We reduce all dimensions elif np.isscalar(axis): axis = [axis]#We reduce one dimension else: axis = list(axis)#We reduce multiple dimensions #When reducting booleans numerically, we count the number of True values if (not self.info['name'].startswith("logical")) and dtype_equal(ary, np.bool): ary = array_create.array(ary, dtype=np.uint64) #Check for out of bounds and convert negative axis values if len(axis) > ary.ndim: raise ValueError("number of 'axises' to reduce is out of bounds") for i in xrange(len(axis)): if axis[i] < 0: axis[i] = ary.ndim+axis[i] if axis[i] >= ary.ndim: raise ValueError("'axis' is out of bounds") if len(axis) == 1:#One axis reduction we can handle directly axis = axis[0] #Find the output shape if ary.ndim == 1: shape = [] else: shape = tuple(s for i, s in enumerate(ary.shape) if i != axis) if out is not None and out.shape != shape: raise ValueError("output dimension mismatch expect "\ "shape '%s' got '%s'"%(shape, out.shape)) tmp = array_create.empty(shape, dtype=ary.dtype) target.reduce(self, get_bhc(tmp), get_bhc(ary), axis) if out is not None: out[...] = tmp else: out = tmp return out else: tmp1 = self.reduce(ary, axis[0]) axis = [i-1 for i in axis[1:]] tmp2 = self.reduce(tmp1, axis) if out is not None: out[...] = tmp2 else: out = tmp2 return out
def __call__(self, *args, **kwargs): args = list(args) #Check number of array arguments if len(args) != self.info['nop'] and len(args) != self.info['nop']-1: raise ValueError("invalid number of array arguments") #Lets make sure that 'out' is always a positional argument try: out = kwargs['out'] del kwargs['out'] if len(args) == self.info['nop']: raise ValueError("cannot specify 'out' as both a positional and keyword argument") args.append(out) except KeyError: pass #We do not support NumPy's exotic arguments for k, val in kwargs.iteritems(): if val is not None: raise ValueError( "Bohrium funcs doesn't support the '%s' argument" % str(k) ) #Broadcast the args bargs = broadcast_arrays(*args) #Pop the output from the 'bargs' list out = None if len(args) == self.info['nop']:#output given out = args.pop() if bargs[-1].shape != out.shape: raise ValueError("non-broadcastable output operand with shape %s " "doesn't match the broadcast shape %s"% (str(args[-1].shape), str(out.shape))) out_shape = bargs[-1].shape #We use a tmp array if the in-/out-put has memory conflicts if out is not None: if overlap_conflict(out, *args): tmp = self.__call__(*args, **kwargs) assign(tmp, out) return out #Copy broadcasted array back to 'args' excluding scalars for i in xrange(len(args)): if not np.isscalar(args[i]): args[i] = bargs[i] if any([ndarray.check(a) for a in args]): if out is not None and not ndarray.check(out): raise NotImplementedError("For now, the output must be a Bohrium "\ "array when the input arrays are") elif not ndarray.check(out):#All operands are regular NumPy arrays func = eval("np.%s"%self.info['name']) if out is not None: args.append(out) return func(*args) if len(args) > 2: raise ValueError("Bohrium do not support ufunc with more than two inputs") #Find the type signature (out_dtype, in_dtype) = _util.type_sig(self.info['name'], args) #Convert dtype of all inputs for i in xrange(len(args)): if not np.isscalar(args[i]) and not dtype_equal(args[i], in_dtype): tmp = array_create.empty_like(args[i], dtype=in_dtype) tmp[...] = args[i] args[i] = tmp #Insert the output array if out is None or not dtype_equal(out_dtype, out.dtype): args.insert(0, array_create.empty(out_shape, out_dtype)) else: args.insert(0, out) #Convert 'args' to Bohrium-C arrays bhcs = [] for arg in args: if np.isscalar(arg): bhcs.append(arg) elif ndarray.check(arg): bhcs.append(get_bhc(arg)) else: arg = array_create.array(arg) bhcs.append(get_bhc(arg)) #Some simple optimizations if self.info['name'] == "power" and np.isscalar(bhcs[2]) and bhcs[2] == 2: #Replace power of 2 with a multiplication target.ufunc(multiply, bhcs[0], bhcs[1], bhcs[1]) else: target.ufunc(self, *bhcs) if out is None or dtype_equal(out_dtype, out.dtype): return args[0] else:#We need to convert the output type before returning assign(args[0], out) return out return out
def where(condition, x=None, y=None): """ where(condition, [x, y]) Return elements, either from `x` or `y`, depending on `condition`. If only `condition` is given, return ``condition.nonzero()``. Parameters ---------- condition : array_like, bool When True, yield `x`, otherwise yield `y`. x, y : array_like, optional Values from which to choose. `x` and `y` need to have the same shape as `condition`. Returns ------- out : ndarray or tuple of ndarrays If both `x` and `y` are specified, the output array contains elements of `x` where `condition` is True, and elements from `y` elsewhere. If only `condition` is given, return the tuple ``condition.nonzero()``, the indices where `condition` is True. See Also -------- nonzero, choose Notes ----- If `x` and `y` are given and input arrays are 1-D, `where` is equivalent to:: [xv if c else yv for (c,xv,yv) in zip(condition,x,y)] Examples -------- >>> np.where([[True, False], [True, True]], ... [[1, 2], [3, 4]], ... [[9, 8], [7, 6]]) array([[1, 8], [3, 4]]) >>> np.where([[0, 1], [1, 0]]) (array([0, 1]), array([1, 0])) >>> x = np.arange(9.).reshape(3, 3) >>> np.where( x > 5 ) (array([2, 2, 2]), array([0, 1, 2])) >>> x[np.where( x > 3.0 )] # Note: result is 1D. array([ 4., 5., 6., 7., 8.]) >>> np.where(x < 5, x, -1) # Note: broadcasting. array([[ 0., 1., 2.], [ 3., 4., -1.], [-1., -1., -1.]]) Find the indices of elements of `x` that are in `goodvalues`. >>> goodvalues = [3, 4, 7] >>> ix = np.in1d(x.ravel(), goodvalues).reshape(x.shape) >>> ix array([[False, False, False], [ True, True, False], [False, True, False]], dtype=bool) >>> np.where(ix) (array([1, 1, 2]), array([0, 1, 1])) """ if x is None or y is None: warnings.warn("Bohrium only supports where() when 'x' and 'y' are specified", stacklevel=2) return numpy.where(condition) if not (bhary.check(condition) or bhary.check(x) or bhary.check(y)): return numpy.where(condition, x, y) # Make sure that non-scalars are Bohrium arrays if numpy.isscalar(condition): condition = bool(condition) else: condition = array_create.array(condition).astype("bool") if not numpy.isscalar(x): x = array_create.array(x) if not numpy.isscalar(y): y = array_create.array(y) # Shortcut if all arguments are scalars if all(numpy.isscalar(k) or k.size == 1 for k in (x, y, condition)): return x if condition else y # Find appropriate output type array_types = [] scalar_types = [] for v in (x, y): if numpy.isscalar(v): scalar_types.append(type(v)) else: array_types.append(v.dtype) out_type = numpy.find_common_type(array_types, scalar_types) # Shortcut if input arrays are finite if ufuncs.isfinite(x).all() and ufuncs.isfinite(y).all(): if numpy.isscalar(condition): res = condition * x + (not condition) * y else: res = condition * x + ufuncs.logical_not(condition) * y if numpy.isscalar(res): return out_type(res) else: return res.astype(out_type) # General case: use fancy indexing (condition, x, y), newshape = array_manipulation.broadcast_arrays(condition, x, y) ret = array_create.zeros(newshape, dtype=out_type) ret[condition] = x if numpy.isscalar(x) else x[condition] ret[~condition] = y if numpy.isscalar(y) else y[~condition] return ret
def __call__(self, *args, **kwargs): args = list(args) # Check number of array arguments if len(args) != self.info['nop'] and len(args) != self.info['nop']-1: raise ValueError("invalid number of array arguments") # Lets make sure that 'out' is always a positional argument try: out = kwargs['out'] del kwargs['out'] if len(args) == self.info['nop']: raise ValueError("cannot specify 'out' as both a positional and keyword argument") args.append(out) except KeyError: pass # We do not support NumPy's exotic arguments for k, val in kwargs.items(): if val is not None: raise ValueError("Bohrium ufuncs doesn't support the '%s' argument" % str(k)) # Broadcast the args bargs = broadcast_arrays(*args) # Pop the output from the 'bargs' list out = None if len(args) == self.info['nop']: out = args.pop() if bargs[-1].shape != out.shape: raise ValueError("non-broadcastable output operand with shape %s " "doesn't match the broadcast shape %s" % (str(args[-1].shape), str(out.shape))) out_shape = bargs[-1].shape # We use a tmp array if the in-/out-put has memory conflicts if out is not None: if overlap_conflict(out, *args): tmp = self.__call__(*args, **kwargs) assign(tmp, out) return out # Copy broadcasted array back to 'args' excluding scalars for i in range(len(args)): if not np.isscalar(args[i]): args[i] = bargs[i] if any([bhary.check(a) for a in args]): if out is not None and not bhary.check(out): raise NotImplementedError("For now, the output must be a Bohrium "\ "array when the input arrays are") elif not bhary.check(out): # All operands are regular NumPy arrays func = eval("np.%s"%self.info['name']) if out is not None: args.append(out) return func(*args) if len(args) > 2: raise ValueError("Bohrium do not support ufunc with more than two inputs") # Find the type signature (out_dtype, in_dtype) = _util.type_sig(self.info['name'], args) # Convert dtype of all inputs for i in range(len(args)): if not np.isscalar(args[i]) and not dtype_equal(args[i], in_dtype): tmp = array_create.empty_like(args[i], dtype=in_dtype) tmp[...] = args[i] args[i] = tmp # Insert the output array if out is None or not dtype_equal(out_dtype, out.dtype): args.insert(0, array_create.empty(out_shape, out_dtype)) else: args.insert(0, out) # Convert 'args' to Bohrium-C arrays bhcs = [] for arg in args: if np.isscalar(arg): bhcs.append(arg) elif bhary.check(arg): bhcs.append(get_bhc(arg)) else: arg = array_create.array(arg) bhcs.append(get_bhc(arg)) # Some simple optimizations if self.info['name'] == "power" and np.isscalar(bhcs[2]) and bhcs[2] == 2: # Replace power of 2 with a multiplication target.ufunc(multiply, bhcs[0], bhcs[1], bhcs[1]) else: target.ufunc(self, *bhcs) if out is None or dtype_equal(out_dtype, out.dtype): return args[0] else: # We need to convert the output type before returning assign(args[0], out) return out return out
def reduce(self, ary, axis=0, out=None): """ A Bohrium Reduction Reduces `ary`'s dimension by len('axis'), by applying ufunc along the axes in 'axis'. Let :math:`ary.shape = (N_0, ..., N_i, ..., N_{M-1})`. Then :math:`ufunc.reduce(ary, axis=i)[k_0, ..,k_{i-1}, k_{i+1}, .., k_{M-1}]` = the result of iterating `j` over :math:`range(N_i)`, cumulatively applying ufunc to each :math:`ary[k_0, ..,k_{i-1}, j, k_{i+1}, .., k_{M-1}]`. For a one-dimensional array, reduce produces results equivalent to: r = op.identity # op = ufunc for i in range(len(A)): r = op(r, A[i]) return r For example, add.reduce() is equivalent to sum(). Parameters ---------- ary : array_like The array to act on. axis : None or int or tuple of ints, optional Axis or axes along which a reduction is performed. The default (`axis` = 0) is perform a reduction over the first dimension of the input array. `axis` may be negative, in which case it counts from the last to the first axis. .. versionadded:: 1.7.0 If this is `None`, a reduction is performed over all the axes. If this is a tuple of ints, a reduction is performed on multiple axes, instead of a single axis or all the axes as before. For operations which are either not commutative or not associative, doing a reduction over multiple axes is not well-defined. The ufuncs do not currently raise an exception in this case, but will likely do so in the future. out : ndarray, optional A location into which the result is stored. If not provided, a freshly-allocated array is returned. Returns ------- r : ndarraout The reduced array. If `out` was supplied, `r` is a reference to it. Examples -------- >>> np.multiply.reduce([2,3,5]) 30 A multi-dimensional array example: >>> X = np.arange(8).reshape((2,2,2)) >>> X array([[[0, 1], [2, 3]], [[4, 5], [6, 7]]]) >>> np.add.reduce(X, 0) array([[ 4, 6], [ 8, 10]]) >>> np.add.reduce(X) # confirm: default axis value is 0 array([[ 4, 6], [ 8, 10]]) >>> np.add.reduce(X, 1) array([[ 2, 4], [10, 12]]) >>> np.add.reduce(X, 2) array([[ 1, 5], [ 9, 13]]) """ if out is not None: if bhary.check(out): if not bhary.check(ary): ary = array_create.array(ary) else: if bhary.check(ary): ary = ary.copy2numpy() # Let NumPy handle NumPy array reductions if not bhary.check(ary): func = eval("np.%s.reduce" % self.info['name']) return func(ary, axis=axis, out=out) # Make sure that 'axis' is a sorted list of dimensions to reduce if axis is None: # We reduce all dimensions axis = range(ary.ndim) elif np.isscalar(axis): # We reduce one dimension axis = [axis] else: # We reduce multiple dimensions axis = list(axis) if len(axis) != len(set(axis)): raise ValueError("duplicate value in 'axis'") axis = sorted(axis, reverse=True) # When reducing booleans numerically, we count the number of True values if (not self.info['name'].startswith("logical")) and dtype_equal(ary, np.bool): ary = array_create.array(ary, dtype=np.uint64) # Check for out of bounds and convert negative axis values if len(axis) > ary.ndim: raise ValueError("number of 'axes' to reduce is out of bounds") for i in range(len(axis)): if axis[i] < 0: axis[i] = ary.ndim + axis[i] if axis[i] >= ary.ndim: raise ValueError("'axis' is out of bounds") if len(axis) == 1: # One axis reduction we can handle directly axis = axis[0] # Find the output shape if ary.ndim == 1: shape = [] else: shape = tuple(s for i, s in enumerate(ary.shape) if i != axis) if out is not None and out.shape != shape: raise ValueError("output dimension mismatch expect "\ "shape '%s' got '%s'" % (shape, out.shape)) tmp = array_create.empty(shape, dtype=ary.dtype) # NumPy compatibility: when the axis dimension size is zero NumPy just returns the neutral value if ary.shape[axis] == 0: tmp[...] = getattr(getattr(np, self.info['name']), "identity") else: target.reduce(self, get_bhc(tmp), get_bhc(ary), axis) if out is not None: out[...] = tmp else: out = tmp return out else: # Let's reduce the first axis ary = self.reduce(ary, axis[0]) # Then we reduce the rest of the axes axis = axis[1:] ary = self.reduce(ary, axis) # Finally, we may have to copy the result to 'out' if out is not None: out[...] = ary else: out = ary return out