def st_valid_inds_of_same_length(draw, v1, v2): len1, len2 = len(v1), len(v2) ret = hyst.just(([], [])) # TODO we should include integer arrays here by chaining `| hynp.integer_array_indices(shape=(LEN_X,))` val1 = hynp.basic_indices(shape=(len1,), allow_ellipsis=False) if len1 == len2: ret = ret | hyst.tuples(hyst.shared(val1, key="st_valid_inds_of_same_length"), hyst.shared(val1, key="st_valid_inds_of_same_length")) if len1 > 0 and len2 > 0: val2 = hynp.basic_indices(shape=(len2,), allow_ellipsis=False) ret = ret | hyst.tuples(val1, val2) return draw(ret)
def get_vector_index_axis(draw, max_shape: int = 10): N = draw(integers(min_value=2, max_value=max_shape)) index = draw( npst.basic_indices(shape=(N, ), min_dims=1, max_dims=1, allow_ellipsis=False)) axis = draw(integers(min_value=0, max_value=1)) return (N, *index, axis)
def test_basic_indices_can_generate_indices_not_covering_all_dims(): # These "flat indices" are skippable in the underlying BasicIndexStrategy, # so we ensure we're definitely generating them for nps.basic_indices(). find_any( nps.basic_indices(shape=(3, 3, 3)), lambda ix: ( (not isinstance(ix, tuple) and ix != Ellipsis) or (isinstance(ix, tuple) and Ellipsis not in ix and len(ix) < 3) ), )
def array_and_basic_indices( draw, array_min_dims: int = 0, array_max_dims: int = 2, dtype: np.dtype = None, ): arr = draw( anyarray(min_dims=array_min_dims, max_dims=array_max_dims, dtype=dtype) ) ind = draw(basic_indices(arr.shape)) return arr, ind
def st_valid_inds_of_different_length(draw, v1, v2): len1, len2 = len(v1), len(v2) ret = nothing() # TODO we should include integer arrays here val = hynp.basic_indices(shape=(len1,), allow_ellipsis=False) # | hynp.integer_array_indices(shape=(len1,)) if len1 != len2: ret = ret | hyst.just((slice(None), slice(None))) \ | hyst.tuples(hyst.shared(val, key="indfl"), hyst.shared(val, key="indfl")) if len1 > 0 and len2 > 0: ret = ret | hyst.tuples(val, val).filter(lambda x: len(x[0])!=len(x[1])) return draw(ret)
def get_chainable_arrays_shapes_and_indices(draw, base_min_dims: int = 1, base_max_dims: int = 2, max_chain: int = 8, chain_min_dims: int = 1): shape_strategy = get_chainable_array_shapes(base_min_dims=base_min_dims, base_max_dims=base_max_dims, max_chain=max_chain, chain_min_dims=chain_min_dims) shapes = draw(shape_strategy) shape = (shapes[0][0], shapes[-1][-1]) indices = draw(npst.basic_indices(shape, allow_ellipsis=False, min_dims=2)) return shapes, indices, shape
def test_basic_indices_generate_valid_indexers( shape, allow_newaxis, allow_ellipsis, data ): min_dims = data.draw( st.integers(0, 5 if allow_newaxis else len(shape)), label="min_dims" ) max_dims = data.draw( st.none() | st.integers(min_dims, 32 if allow_newaxis else len(shape)), label="max_dims", ) indexer = data.draw( nps.basic_indices( shape, min_dims=min_dims, max_dims=max_dims, allow_ellipsis=allow_ellipsis, allow_newaxis=allow_newaxis, ), label="indexer", ) # Check that disallowed things are indeed absent if not allow_newaxis: if isinstance(indexer, tuple): assert 0 <= len(indexer) <= len(shape) + int(allow_ellipsis) else: assert 1 <= len(shape) + int(allow_ellipsis) assert np.newaxis not in shape if not allow_ellipsis: assert Ellipsis not in shape if 0 in shape: # If there's a zero in the shape, the array will have no elements. array = np.zeros(shape) assert array.size == 0 elif np.prod(shape) <= 10**5: # If it's small enough to instantiate, do so with distinct elements. array = np.arange(np.prod(shape)).reshape(shape) else: # We can't cheat on this one, so just try another. assume(False) view = array[indexer] if not np.isscalar(view): assert min_dims <= view.ndim <= (32 if max_dims is None else max_dims) if view.size: assert np.shares_memory(view, array)
def get_boardcastable_arrays_shapes_and_indices(draw, base_min_dims: int = 2, base_max_dims: int = 3, broadcast_min_dims: int = 1, broadcast_max_dims: int = 5): shape_strategy = get_boardcastable_arrays_shapes( base_min_dims=base_min_dims, base_max_dims=base_max_dims, broadcast_min_dims=broadcast_min_dims, broadcast_max_dims=broadcast_max_dims) shapes = draw(shape_strategy) # What is final shape? ndim = max(len(shape) for shape in shapes) padded_shapes = np.array([[1] * (ndim - len(shape)) + list(shape) for shape in shapes]) shape = tuple(np.max(padded_shapes, axis=0).astype(int)) # TODO: raise Issue with hypothesis. Numpy types aren't allowed. uncomment next line and it fails shape = tuple(int(s) for s in shape) indices = draw(npst.basic_indices(shape, allow_ellipsis=False, min_dims=2)) return shapes, indices, shape
def test_basic_indices_can_generate_empty_tuple(): find_any(nps.basic_indices(shape=(0, 0), allow_ellipsis=True), lambda ix: ix == ()) def test_basic_indices_can_generate_long_ellipsis(): # Runs of slice(None) - such as [0,:,:,:,0] - can be replaced by e.g. [0,...,0] find_any( nps.basic_indices(shape=(1, 0, 0, 0, 1), allow_ellipsis=True), lambda ix: len(ix) == 3 and ix[1] == Ellipsis, ) @given( nps.basic_indices(shape=(0, 0, 0, 0, 0)).filter(lambda idx: Ellipsis in idx)) def test_basic_indices_replaces_whole_axis_slices_with_ellipsis(idx): # If ... is in the slice, it replaces all ,:, entries for this shape. assert slice(None) not in idx @given( shape=nps.array_shapes(min_dims=0, max_side=4) | nps.array_shapes(min_dims=0, min_side=0, max_side=10), min_dims=st.integers(0, 5), allow_ellipsis=st.booleans(), allow_newaxis=st.booleans(), data=st.data(), ) def test_basic_indices_generate_valid_indexers(shape, min_dims, allow_ellipsis, allow_newaxis, data):
def arbitrary_indices(draw, shape: Tuple[int]): """ Hypothesis search strategy: Generate a valid index for an array of a given shape. The index can contain any type of valid object used for indexing, including integers, slices, Ellipsis's, newaxis's, boolean arrays, and integer arrays. Parameters ---------- shape : Tuple[int] The shape of the array to be indexed into Notes ----- `draw` is a parameter reserved by hypothesis, and should not be specified by the user. When given a shape with a 0-dimensional axis, only a basic index will be returned. Returns ------- hypothesis.searchstrategy.SearchStrategy[Tuple[Union[int, slice, Ellipsis, NoneType, numpy.ndarray], ...]] """ def group_continuous_integers(ls): """ Given a list of integers, find and group continuous sequences Parameters ---------- ls: List[int] Returns ------- List[Tuple[int]] Examples -------- >>> group_continuous_integers([1, 3, 4, 5, 7, 8]) [(1,), (3, 4, 5), (7, 8)] """ return [ tuple(map(itemgetter(1), g)) for k, g in groupby(enumerate(ls), lambda x: x[0] - x[1]) ] if not shape or 0 in shape: return draw(hnp.basic_indices(shape=shape, allow_newaxis=True)) shape_inds = list(range(len(shape))) index = [] # stores tuples of (axis, indexing object) # add integers, slices basic_inds = sorted( draw(st.lists(st.sampled_from(shape_inds), unique=True))) if len(basic_inds) > 0: basic_dims = tuple(shape[i] for i in basic_inds) # only draw ints and slices # will handle possible ellipsis and newaxis objects later # as these can make array indices difficult to handle basics = draw(hnp.basic_indices(shape=basic_dims, allow_ellipsis=False)) if not isinstance(basics, tuple): basics = (basics, ) index += [tup for tup in zip(basic_inds, basics)] # will not necessarily index all axes from basic_inds as # `basic_indices` can return indices with omitted trailing slices # so only remove dimensions directly indexed into for i in basic_inds[:len(basics)]: shape_inds.pop(shape_inds.index(i)) if len(shape_inds) > 0: # add integer arrays to index int_arr_inds = sorted( draw(st.lists(st.sampled_from(shape_inds), unique=True))) if len(int_arr_inds) > 0: int_arr_dims = tuple(shape[i] for i in int_arr_inds) int_arrs = draw(hnp.integer_array_indices(shape=int_arr_dims)) index += [tup for tup in zip(int_arr_inds, int_arrs)] for i in int_arr_inds: shape_inds.pop(shape_inds.index(i)) if len(shape_inds) > 0: # add boolean arrays to index bool_inds = sorted( draw(st.lists(st.sampled_from(shape_inds), unique=True))) if len(bool_inds) > 0: # boolean arrays can be multi-dimensional, so by grouping all # adjacent axes to make a single boolean array, this can be tested for grouped_bool_inds = group_continuous_integers(bool_inds) bool_dims = [ tuple(shape[i] for i in ind) for ind in grouped_bool_inds ] # if multiple boolean array indices, the number of trues must be such that # the output of ind.nonzero() for each index are broadcast compatible # this must also be the same as the trailing dim of each integer array, if any used if len(int_arr_inds): max_trues = max(i.shape[-1] for i in int_arrs) else: max_trues = st.integers(min_value=0, max_value=min( bool_dims, key=lambda x: np.prod(x))) index += [( i[0], draw( hnp.arrays(shape=sh, dtype=bool).filter( lambda x: x.sum() in (1, max_trues))), ) for i, sh in zip(grouped_bool_inds, bool_dims)] for i in bool_inds: shape_inds.pop(shape_inds.index(i)) grouped_shape_inds = group_continuous_integers(sorted(shape_inds)) if len(grouped_shape_inds) == 1: # unused indices form a continuous stretch of dimensions # so can replace with an ellipsis # to test ellipsis vs omitted slices, randomly # add ellipsis when the unused axes are trailing if max(shape_inds) + 1 == len(shape): if draw(st.booleans()): index += [(min(shape_inds), Ellipsis)] else: index += [(min(shape_inds), Ellipsis)] elif len(grouped_shape_inds) == 0 and draw(st.booleans()): # all indices filled already # can randomly add ellipsis that expands to 0-d tuple # this can have counter-intuitive behavior # (particularly in conjunction with array indices) i = draw(st.integers(min_value=0, max_value=len(index))) index.insert(i, (i, Ellipsis)) else: # so that current chosen index's work, # fill in remaining any gaps with empty slices index += [(i, slice(None)) for i in shape_inds] index = sorted(index, key=lambda x: x[0]) # can now randomly add in newaxis objects newaxis_pos = sorted( draw( st.lists(st.integers(min_value=0, max_value=len(index)), unique=True)), reverse=True, ) for i in newaxis_pos: index.insert(i, (-1, np.newaxis)) out_ind = tuple(i[1] for i in index) return out_ind
def test_basic_indices_can_generate_long_ellipsis(): # Runs of slice(None) - such as [0,:,:,:,0] - can be replaced by e.g. [0,...,0] find_any( nps.basic_indices(shape=(1, 0, 0, 0, 1), allow_ellipsis=True), lambda ix: len(ix) == 3 and ix[1] == Ellipsis, )
def test_basic_indices_can_generate_non_tuples(): find_any( nps.basic_indices(shape=(0, 0), allow_ellipsis=True), lambda ix: not isinstance(ix, tuple), )
def test_basic_indices_can_generate_empty_tuple(): find_any(nps.basic_indices(shape=(0, 0), allow_ellipsis=True), lambda ix: ix == ())
def test_basic_indices_options(condition): indexers = nps.array_shapes(min_dims=0, max_dims=32).flatmap( lambda shape: nps.basic_indices(shape, allow_newaxis=True) ) find_any(indexers, condition)
nps.basic_indices(shape=(0, 0), allow_ellipsis=True), lambda ix: not isinstance(ix, tuple), ) def test_basic_indices_can_generate_long_ellipsis(): # Runs of slice(None) - such as [0,:,:,:,0] - can be replaced by e.g. [0,...,0] find_any( nps.basic_indices(shape=(1, 0, 0, 0, 1), allow_ellipsis=True), lambda ix: len(ix) == 3 and ix[1] == Ellipsis, ) @given( nps.basic_indices(shape=(0, 0, 0, 0, 0)).filter( lambda idx: isinstance(idx, tuple) and Ellipsis in idx ) ) def test_basic_indices_replaces_whole_axis_slices_with_ellipsis(idx): # `slice(None)` (aka `:`) is the only valid index for an axis of size # zero, so if all dimensions are 0 then a `...` will replace all the # slices because we generate `...` for entire contiguous runs of `:` assert slice(None) not in idx def test_basic_indices_can_generate_indices_not_covering_all_dims(): # These "flat indices" are skippable in the underlying BasicIndexStrategy, # so we ensure we're definitely generating them for nps.basic_indices(). find_any( nps.basic_indices(shape=(3, 3, 3)), lambda ix: (
def test_test_basic_indices_kwonly_emulation(): with pytest.raises(InvalidArgument): nps.basic_indices((), 0, 1).validate() with pytest.raises(InvalidArgument): nps.basic_indices((), __reserved=None).validate()
def test_basic_indices_bad_min_dims_warns(): with pytest.warns(HypothesisDeprecationWarning): with pytest.raises(InvalidArgument): nps.basic_indices((3, 3, 3), min_dims=4).example()
def test_basic_indices_default_max_dims_does_not_warn(): with catch_warnings(record=True) as record: nps.basic_indices((3, 3, 3)).example() nps.basic_indices((3, 3, 3), allow_newaxis=True).example() assert len(record) == 0
def test_basic_indices_bad_max_dims_warns(): with pytest.warns(HypothesisDeprecationWarning): nps.basic_indices((3, 3, 3), max_dims=4).example()