def test_assign_broadcasting(): # https://numpy.org/doc/stable/user/basics.indexing.html#assigning-values-to-indexed-arrays # Note that the above documentation does not fully capture the broadcasting behavior of NumPy. # We therefore test our tools for broadcasting with array shapes, instead of arrays. # Consider the permutations of a tuple of 10 integers ranging from 0 to 3. # We partition this tuple into 2 tuples of 5 integers, and # use the tuples to define the shapes of two arrays, # to define the LHS and RHS of an assignment. # A value of 0 means the axis is not specified in the resulting array shape. # Because we're interested in valid assignment operations, # we define empty arrays of size equal to the product of # axis dims, excluding any axes which are set to 0. # If all axes are set to 0, # then create a dimensionless array with value 0. # There is no formal proof that the proof for arrays with 5 axes # is without loss of generality. def get_array(shape): shape = tuple(filter(lambda x: x > 0, shape)) if len(shape) == 0: return np.array(0) else: return np.empty(np.product(shape)).reshape(shape) perms = list(itertools.product([0, 1, 2, 3], repeat=10)) pbar = tqdm.tqdm(total=len(perms)) for shapes in perms: A: np.ndarray = get_array(shapes[:5]) B: np.ndarray = get_array(shapes[5:]) try: if A.shape == (): continue if B.shape == (): A[:] = B else: A[:] = B[:] # This should execute without error. assert np.broadcast_to( B, A.shape).shape == array_utils.broadcast_shape_to_alt( B.shape, A.shape) assert array_utils.can_broadcast_shape_to( B.shape, A.shape), "%s can be broadcast to %s" % (B.shape, A.shape) except ValueError as _: with pytest.raises(ValueError): np.broadcast_to(B, A.shape) with pytest.raises(ValueError): array_utils.broadcast_shape_to_alt(B.shape, A.shape) assert not array_utils.can_broadcast_shape_to( B.shape, A.shape), "%s cannot be broadcast to %s" % (B.shape, A.shape) pbar.update(1)
def create(self, concrete_cls=None) -> BlockArrayBase: if self.sel.basic_steps(): if self.sel.is_aligned(self._source.block_shape): # Assertion below should form a conjunction with the above condition. # This isn't currently an issue but an assumption that # may not always hold true, depending on how the ArrayView # is constructed. assert array_utils.can_broadcast_shape_to( self.sel.get_broadcastable_block_shape(self.block_shape), self._source.block_shape) return self.create_references(concrete_cls) else: return self.create_basic_single_step(concrete_cls) else: return self.create_basic_multi_step(concrete_cls)