def _sample_basic(self, rfunc_name, shape, block_shape, dtype, rfunc_args) -> BlockArray: if shape is None: assert block_shape is None shape = () block_shape = () else: assert block_shape is not None if dtype is None: dtype = np.float64 assert isinstance(dtype, type) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype=dtype.__name__) ba: BlockArray = BlockArray(grid, self._system) for grid_entry in ba.grid.get_entry_iterator(): rng_params = list(self._rng.new_block_rng_params()) # Size and dtype to begin with. this_block_shape = grid.get_block_shape(grid_entry) size = int(np.product(this_block_shape)) # Inconsistent param orderings. if rfunc_name == "random": rfunc_args_final = tuple([size] + list(rfunc_args)) elif rfunc_name == "integers": # rfunc_args == (low, high, dtype, endpoint) rfunc_args_final = tuple( list(rfunc_args[:2]) + [size] + list(rfunc_args[2:])) else: rfunc_args_final = tuple(list(rfunc_args) + [size]) block: Block = ba.blocks[grid_entry] block.oid = self._system.random_block(rng_params, rfunc_name, rfunc_args_final, this_block_shape, dtype, syskwargs={ "grid_entry": grid_entry, "grid_shape": grid.grid_shape }) return ba
def swapaxes(self, axis1, axis2): meta_swap = self.grid.to_meta() shape = list(meta_swap["shape"]) block_shape = list(meta_swap["block_shape"]) dim = len(shape) if axis1 >= dim or axis2 >= dim: raise ValueError("axis is larger than the array dimension") shape[axis1], shape[axis2] = shape[axis2], shape[axis1] block_shape[axis1], block_shape[axis2] = block_shape[ axis2], block_shape[axis1] meta_swap["shape"] = tuple(shape) meta_swap["block_shape"] = tuple(block_shape) grid_swap = ArrayGrid.from_meta(meta_swap) rarr_src = np.ndarray(self.blocks.shape, dtype='O') for grid_entry in self.grid.get_entry_iterator(): rarr_src[grid_entry] = self.blocks[grid_entry].swapaxes( axis1, axis2) rarr_src = rarr_src.swapaxes(axis1, axis2) rarr_swap = BlockArray(grid_swap, self.system, rarr_src) return rarr_swap
def __init__(self, source, sel: BasicSelection = None, block_shape: tuple = None): self._source: BlockArrayBase = source self._system: System = self._source.system if sel is None: sel = BasicSelection.from_shape(self._source.shape) # Currently, this is all we support. assert len(sel.axes) == len(self._source.shape) self.sel = sel self.shape: tuple = self.sel.get_output_shape() if block_shape is None: block_shape: tuple = array_utils.block_shape_from_subscript( self.sel.selector(), self._source.block_shape) self.block_shape = block_shape assert len(self.block_shape) == len(self.shape) self.grid: ArrayGrid = ArrayGrid(self.shape, self.block_shape, dtype=self._source.dtype.__name__)
def _broadcast_bop(self, op_name, arr_1, arr_2) -> BlockArray: """ We want to avoid invoking this op whenever possible; NumPy's imp is faster. :param op_name: Name of binary operation. :param arr_1: A BlockArray. :param arr_2: A BlockArray. :return: A BlockArray. """ if arr_1.shape != arr_2.shape: output_grid_shape = array_utils.broadcast_shape( arr_1.grid.grid_shape, arr_2.grid.grid_shape) arr_1 = arr_1.broadcast_to(output_grid_shape) arr_2 = arr_2.broadcast_to(output_grid_shape) dtype = array_utils.get_bop_output_type(op_name, arr_1.dtype, arr_2.dtype) grid = ArrayGrid(arr_1.shape, arr_1.block_shape, dtype.__name__) rarr = BlockArray(grid, self.system) for grid_entry in rarr.grid.get_entry_iterator(): block_1: Block = arr_1.blocks[grid_entry] block_2: Block = arr_2.blocks[grid_entry] rarr.blocks[grid_entry] = block_1.bop(op_name, block_2, {}) return rarr
def diag(self, X: BlockArray) -> BlockArray: if len(X.shape) == 1: shape = X.shape[0], X.shape[0] block_shape = X.block_shape[0], X.block_shape[0] grid = ArrayGrid(shape, block_shape, X.dtype.__name__) grid_meta = grid.to_meta() rarr = BlockArray(grid, self.system) for grid_entry in grid.get_entry_iterator(): syskwargs = { "grid_entry": grid_entry, "grid_shape": grid.grid_shape } if np.all(np.diff(grid_entry) == 0): # This is a diagonal block. rarr.blocks[grid_entry].oid = self.system.diag( X.blocks[grid_entry[0]].oid, syskwargs=syskwargs) else: rarr.blocks[grid_entry].oid = self.system.new_block( "zeros", grid_entry, grid_meta, syskwargs=syskwargs) elif len(X.shape) == 2: assert X.shape[0] == X.shape[1] assert X.block_shape[0] == X.block_shape[1] shape = X.shape[0], block_shape = X.block_shape[0], grid = ArrayGrid(shape, block_shape, X.dtype.__name__) rarr = BlockArray(grid, self.system) for grid_entry in X.grid.get_entry_iterator(): out_grid_entry = grid_entry[:1] out_grid_shape = grid.grid_shape[:1] syskwargs = { "grid_entry": out_grid_entry, "grid_shape": out_grid_shape } if np.all(np.diff(grid_entry) == 0): # This is a diagonal block. rarr.blocks[out_grid_entry].oid = self.system.diag( X.blocks[grid_entry].oid, syskwargs=syskwargs) else: raise ValueError("X must have 1 or 2 axes.") return rarr
def delete_block_s3(filename: AnyStr, grid_entry: Tuple, grid_meta: Dict): return np.array(StoredArrayS3( filename, ArrayGrid.from_meta(grid_meta)).delete(grid_entry), dtype=dict)
def read_block_s3(filename: AnyStr, grid_entry: Tuple, grid_meta: Dict): return StoredArrayS3(filename, ArrayGrid.from_meta(grid_meta)).get(grid_entry)
def write_block_s3(block: Any, filename: AnyStr, grid_entry: Tuple, grid_meta: Dict): return np.array(StoredArrayS3(filename, ArrayGrid.from_meta(grid_meta)).put( grid_entry, block), dtype=dict)
def loadtxt(self, fname, dtype=float, comments='# ', delimiter=' ', converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0, encoding='bytes', max_rows=None, num_workers=4) -> BlockArray: # pylint: disable=unused-variable bytes_per_char, bytes_per_row, bytes_per_col, num_cols = storage_utils.get_np_txt_info( fname, comments, delimiter) chars_per_row = bytes_per_row // bytes_per_char assert np.allclose(float(chars_per_row), bytes_per_row / bytes_per_char) comment_lines, trailing_newlines = storage_utils.get_np_comments( fname, comments) nonrow_chars = trailing_newlines for line in comment_lines: nonrow_chars += len(line) file_size = storage_utils.get_file_size(fname) file_chars = file_size // bytes_per_char assert np.allclose(float(file_chars), file_size / bytes_per_char) row_chars = file_chars - nonrow_chars num_rows = row_chars // chars_per_row assert np.allclose(float(num_rows), float(row_chars / chars_per_row)) num_rows_final = num_rows - skiprows if max_rows is not None: num_rows_final = (num_rows_final, max_rows) row_batches: storage_utils.Batch = storage_utils.Batch.from_num_batches( num_rows_final, num_workers) grid = ArrayGrid( shape=(num_rows_final, num_cols), block_shape=(row_batches.batch_size, num_cols), dtype=np.float64.__name__ if dtype is float else dtype.__name__) result: BlockArray = BlockArray(grid, system=self.system) for i, grid_entry in enumerate(grid.get_entry_iterator()): row_start, row_end = row_batches.batches[i] batch_skiprows = skiprows + row_start + 1 batch_max_rows = grid.get_block_shape(grid_entry)[0] assert batch_max_rows == row_end - row_start result.blocks[grid_entry].oid = self.loadtxt_block( fname, dtype=dtype, comments=comments, delimiter=delimiter, converters=converters, skiprows=batch_skiprows, usecols=usecols, unpack=unpack, ndmin=ndmin, encoding=encoding, max_rows=batch_max_rows, syskwargs={ "grid_entry": grid_entry, "grid_shape": grid.grid_shape }) return result
def write_meta_s3(filename: AnyStr, grid_meta: Dict): sa: StoredArrayS3 = StoredArrayS3(filename, ArrayGrid.from_meta(grid_meta)) return np.array(sa.put_grid(sa.grid), dtype=dict)
def astype(self, dtype): grid = ArrayGrid(self.shape, self.block_shape, dtype.__name__) result = BlockArray(grid, self.system) for grid_entry in result.grid.get_entry_iterator(): result.blocks[grid_entry] = self.blocks[grid_entry].astype(dtype) return result
def reduce_axis(self, op_name, axis, keepdims=False): if not (axis is None or isinstance(axis, (int, np.int32, np.int64))): raise NotImplementedError("Only integer axis is currently supported.") result_blocks = np.empty_like(self.blocks, dtype=Block) for grid_entry in self.grid.get_entry_iterator(): result_blocks[grid_entry] = self.blocks[grid_entry].reduce_axis(op_name, axis, keepdims=keepdims) result_shape = [] result_block_shape = [] for curr_axis in range(len(self.shape)): axis_size, axis_block_size = self.shape[curr_axis], self.block_shape[curr_axis] if curr_axis == axis or axis is None: if keepdims: axis_size, axis_block_size = 1, 1 else: continue result_shape.append(axis_size) result_block_shape.append(axis_block_size) result_shape = tuple(result_shape) result_block_shape = tuple(result_block_shape) result_dtype = array_utils.get_reduce_output_type(op_name, self.dtype) result_grid = ArrayGrid(shape=result_shape, block_shape=result_block_shape, dtype=result_dtype.__name__) result = BlockArray(result_grid, self.system) if op_name in settings.np_pairwise_reduction_map: # Do a pairwise reduction with the pairwise reduction op. pairwise_op_name = settings.np_pairwise_reduction_map.get(op_name, op_name) if axis is None: reduced_block: Block = None for grid_entry in self.grid.get_entry_iterator(): if reduced_block is None: reduced_block = result_blocks[grid_entry] continue next_block = result_blocks[grid_entry] reduced_block = reduced_block.bop(pairwise_op_name, next_block, {}) if result.shape == (): result.blocks[()] = reduced_block else: result.blocks[:] = reduced_block else: for result_grid_entry in result_grid.get_entry_iterator(): reduced_block: Block = None for sum_dim in range(self.grid.grid_shape[axis]): grid_entry = list(result_grid_entry) if keepdims: grid_entry[axis] = sum_dim else: grid_entry = grid_entry[:axis] + [sum_dim] + grid_entry[axis:] grid_entry = tuple(grid_entry) next_block: Block = result_blocks[grid_entry] if reduced_block is None: reduced_block = next_block else: reduced_block = reduced_block.bop(pairwise_op_name, next_block, {}) result.blocks[result_grid_entry] = reduced_block else: op_func = np.__getattribute__(op_name) if result.shape == (): result.blocks[()] = op_func(result_blocks, axis=axis, keepdims=keepdims) else: result.blocks = op_func(result_blocks, axis=axis, keepdims=keepdims) return result
def reshape(self, shape=None, block_shape=None): # TODO (hme): Add support for arbitrary reshape. if shape is None: shape = self.shape if block_shape is None: block_shape = self.block_shape if shape == self.shape and block_shape == self.block_shape: return self temp_shape = shape temp_block_shape = block_shape shape = [] block_shape = [] negative_one = False for i, dim in enumerate(temp_shape): if dim == -1: assert len(self.shape) == 1 if negative_one: raise Exception("Only one -1 permitted in reshape.") negative_one = True shape.append(self.shape[i]) assert temp_block_shape[i] == -1 block_shape.append(self.block_shape[0]) else: shape.append(dim) block_shape.append(temp_block_shape[i]) del temp_shape shape = tuple(shape) block_shape = tuple(block_shape) assert np.product(shape) == np.product(self.shape) # Make sure the difference is either a preceding or succeeding one. if len(shape) > len(self.shape): if shape[0] == 1: grid_entry_op = "shift" assert shape[1:] == self.shape elif shape[-1] == 1: grid_entry_op = "pop" assert shape[:-1] == self.shape else: raise Exception() elif len(shape) < len(self.shape): if self.shape[0] == 1: grid_entry_op = "prep" assert self.shape[1:] == shape elif self.shape[-1] == 1: grid_entry_op = "app" assert self.shape[:-1] == shape else: raise Exception() else: grid_entry_op = "none" assert self.shape == shape grid = ArrayGrid(shape=shape, block_shape=block_shape, dtype=self.grid.dtype.__name__) grid_meta = grid.to_meta() rarr = BlockArray(grid, self.system) for grid_entry in grid.get_entry_iterator(): rarr.blocks[grid_entry].oid = self.system.empty(grid_entry, grid_meta, syskwargs={ "grid_entry": grid_entry, "grid_shape": grid.grid_shape }) grid_entry_slice = grid.get_slice(grid_entry) if grid_entry_op == "shift": grid_entry_slice = tuple([0] + list(grid_entry_slice)[1:]) self_grid_entry_slice = self.grid.get_slice(grid_entry[1:]) elif grid_entry_op == "pop": grid_entry_slice = tuple(list(grid_entry_slice)[:-1] + [0]) self_grid_entry_slice = self.grid.get_slice(grid_entry[:-1]) elif grid_entry_op == "prep": self_grid_entry_slice = self.grid.get_slice(tuple([0] + list(grid_entry))) elif grid_entry_op == "prep": self_grid_entry_slice = self.grid.get_slice(tuple(list(grid_entry) + [0])) else: assert grid_entry_op == "none" self_grid_entry_slice = grid_entry_slice # TODO (hme): This is costly. rarr[grid_entry_slice] = self[self_grid_entry_slice] return rarr
def empty(self, grid_entry, grid_meta): grid = ArrayGrid.from_meta(grid_meta) block_shape = grid.get_block_shape(grid_entry) return np.empty(block_shape, dtype=grid.dtype)
def test_block_shape(nps_app_inst): app = nps_app_inst dtype = np.float64 shape = (10**9, 250) cluster_shape = (1, 1) num_cores = 64 block_shape = app.compute_block_shape(shape=shape, dtype=dtype, cluster_shape=cluster_shape, num_cores=num_cores) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype.__name__) assert grid.grid_shape == (num_cores, 1) cluster_shape = (16, 1) num_cores = 64 * np.product(cluster_shape) block_shape = app.compute_block_shape(shape=shape, dtype=dtype, cluster_shape=cluster_shape, num_cores=num_cores) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype.__name__) assert grid.grid_shape == (num_cores, 1) shape = (250, 10**9) cluster_shape = (1, 16) block_shape = app.compute_block_shape(shape=shape, dtype=dtype, cluster_shape=cluster_shape, num_cores=num_cores) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype.__name__) assert grid.grid_shape == (1, num_cores) shape = (10**4, 10**4) cluster_shape = (1, 1) num_cores = 64 block_shape = app.compute_block_shape(shape=shape, dtype=dtype, cluster_shape=cluster_shape, num_cores=num_cores) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype.__name__) assert grid.grid_shape == (int(num_cores**.5), int(num_cores**.5)) shape = (10**4, 10**4 // dtype(0).nbytes) block_shape = app.compute_block_shape(shape=shape, dtype=dtype, cluster_shape=cluster_shape, num_cores=num_cores) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype.__name__) assert grid.grid_shape != (1, 1) shape = (10**4, 10**4 // dtype(0).nbytes - 1) block_shape = app.compute_block_shape(shape=shape, dtype=dtype, cluster_shape=cluster_shape, num_cores=num_cores) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype.__name__) assert grid.grid_shape == (1, 1) shape = (10**4, 10**4) block_shape = app.compute_block_shape(shape=shape, dtype=dtype, num_cores=num_cores) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype.__name__) assert grid.grid_shape == (int(num_cores**.5), int(num_cores**.5)) cluster_shape = (12, 1) num_cores = systems_utils.get_num_cores() block_shape = app.compute_block_shape(shape=shape, dtype=dtype, cluster_shape=cluster_shape) grid: ArrayGrid = ArrayGrid(shape, block_shape, dtype.__name__) assert grid.grid_shape == (num_cores, 1)
def create_basic_single_step(self, concrete_cls) -> BlockArrayBase: array_cls = BlockArrayBase if concrete_cls is None else concrete_cls dst_ba: BlockArrayBase = array_cls(self.grid, self._system) if 0 in self.shape: return dst_ba src_sel_arr: np.ndarray = selection.BasicSelection.block_selection( self._source.shape, self._source.block_shape) src_sel_clipped: np.ndarray = src_sel_arr & self.sel assert src_sel_clipped.shape == self._source.grid.grid_shape broadcast_shape = self.sel.get_broadcastable_shape() broadcast_block_shape = self.sel.get_broadcastable_block_shape( dst_ba.block_shape) dst_grid_bc: ArrayGrid = ArrayGrid(broadcast_shape, broadcast_block_shape, self.grid.dtype.__name__) dst_sel_arr: np.ndarray = selection.BasicSelection.block_selection( broadcast_shape, broadcast_block_shape) dst_sel_offset: np.ndarray = dst_sel_arr + self.sel.position() dst_entry_iterator = list(dst_ba.grid.get_entry_iterator()) for dst_index, dst_grid_entry_bc in enumerate( dst_grid_bc.get_entry_iterator()): dst_sel_offset_block: BasicSelection = dst_sel_offset[ dst_grid_entry_bc] if dst_sel_offset_block.is_empty(): continue src_dst_intersection_arr = src_sel_clipped & dst_sel_offset_block sys: System = self._system src_oids = [] src_params = [] dst_params = [] for _, src_grid_entry in enumerate( self._source.grid.get_entry_iterator()): src_dst_intersection_block: BasicSelection = src_dst_intersection_arr[ src_grid_entry] if src_dst_intersection_block.is_empty(): continue src_block: Block = self._source.blocks[src_grid_entry] src_oids.append(src_block.oid) src_sel_block: BasicSelection = src_sel_arr[src_grid_entry] src_dep_sel_loc = src_dst_intersection_block - src_sel_block.position( ) src_params.append( (src_dep_sel_loc.selector(), src_block.transposed)) dst_block_sel_loc = src_dst_intersection_block - dst_sel_offset_block.position( ) dst_params.append((dst_block_sel_loc.selector(), False)) dst_block: Block = dst_ba.blocks.reshape( dst_grid_bc.grid_shape)[dst_grid_entry_bc] dst_block.oid = sys.create_block( *src_oids, src_params=src_params, dst_params=dst_params, dst_shape=dst_block.shape, dst_shape_bc=dst_sel_offset_block.get_output_shape(), syskwargs={ "grid_entry": dst_entry_iterator[dst_index], "grid_shape": self.grid.grid_shape }) return dst_ba
def _vecdot(self, other): assert self.shape[-1] == other.shape[0], str( (self.shape[1], other.shape[0])) result_shape = tuple(self.shape[:-1] + other.shape[1:]) result_block_shape = tuple(self.block_shape[:-1] + other.block_shape[1:]) result_grid = ArrayGrid(shape=result_shape, block_shape=result_block_shape, dtype=self.dtype.__name__) result = BlockArray(result_grid, self.system) self_num_axes = len(self.grid.grid_shape) other_num_axes = len(other.grid.grid_shape) oids = [] for i in range(self.grid.grid_shape[-1]): self_grid_entry = tuple(i if axis == self_num_axes - 1 else 0 for axis in range(self_num_axes)) other_grid_entry = tuple(i if axis == 0 else 0 for axis in range(other_num_axes)) self_block: Block = self.blocks[self_grid_entry] other_block: Block = other.blocks[other_grid_entry] if self_block.transposed != other_block.transposed: # The vectors are aligned if their transpositions satisfy the xor relation. if self_block.transposed: # Use other grid entry for dot, # because physically, # other block is located on same node as self block. sch_grid_entry = other_grid_entry sch_grid_shape = other.grid.grid_shape elif other_block.transposed: # Use self grid entry for dot. sch_grid_entry = self_grid_entry sch_grid_shape = self.grid.grid_shape else: raise Exception("Impossible.") else: # They're either both transposed or not. # Either way, one will need to be transmitted, so transmit other. sch_grid_entry = self_grid_entry sch_grid_shape = self.grid.grid_shape dot_oid = self.system.bop("tensordot", a1=self_block.oid, a2=other_block.oid, a1_shape=self_block.shape, a2_shape=other_block.shape, a1_T=self_block.transposed, a2_T=other_block.transposed, axes=1, syskwargs={ "grid_entry": sch_grid_entry, "grid_shape": sch_grid_shape }) oids.append(dot_oid) result_grid_entry = tuple(0 for _ in range(len(result.grid.grid_shape))) result_oid = self.system.sum_reduce(*oids, syskwargs={ "grid_entry": result_grid_entry, "grid_shape": result.grid.grid_shape }) result.blocks[result_grid_entry].oid = result_oid return result
def test_grid_copy(): grid = ArrayGrid(shape=(1, 2), block_shape=(1, 2), dtype=np.float64.__name__) assert grid.copy() is not grid
def basic_assign_single_step(self, dst_sel: BasicSelection, value): assert isinstance(value, (ArrayView, BlockArrayBase)) dst_ba: BlockArrayBase = self._source dst_sel_arr: np.ndarray = selection.BasicSelection.block_selection(dst_ba.shape, dst_ba.block_shape) dst_sel_clipped: np.ndarray = dst_sel_arr & dst_sel assert dst_sel_clipped.shape == self._source.grid.grid_shape # We create value's block array, in case we need to broadcast. # This may not be necessary, but alternative solutions are extremely tedious. # The result is a block array with replicated blocks, # which match the output shape of dst_sel. if isinstance(value, ArrayView): src_ba_bc: BlockArrayBase = value.create().broadcast_to(dst_sel.get_output_shape()) elif isinstance(value, BlockArrayBase): src_ba_bc: BlockArrayBase = value.broadcast_to(dst_sel.get_output_shape()) else: raise Exception("Unexpected value type %s." % type(value)) # Different lengths occur when an index is used to perform # a selection on an axis. Numpy semantics drops such axes. To allow operations # between source and destination selections, dropped axes are restored with dimension 1 # so that selections are of equal length. # We restore the dropped dimensions of the destination selection, because # the source selection must be broadcastable to the destination selection # for the assignment to be valid. src_inflated_shape = dst_sel.get_broadcastable_shape() # The block shapes need not be equal, but the broadcast source block shape must # match the block shape we obtain below, so that there's a 1-to-1 correspondence # between the grid entries. src_inflated_block_shape = dst_sel.get_broadcastable_block_shape(src_ba_bc.block_shape) src_inflated_grid: ArrayGrid = ArrayGrid(src_inflated_shape, src_inflated_block_shape, self.grid.dtype.__name__) src_sel_arr: np.ndarray = selection.BasicSelection.block_selection(src_inflated_shape, src_inflated_block_shape) src_sel_offset: np.ndarray = src_sel_arr + dst_sel.position() # The enumeration of grid entries is identical if the broadcast source grid and # inflated grid have the same number of blocks. src_grid_entry_iterator = list(src_ba_bc.grid.get_entry_iterator()) for dst_grid_entry in dst_ba.grid.get_entry_iterator(): dst_sel_block: BasicSelection = dst_sel_arr[dst_grid_entry] dst_sel_block_clipped: BasicSelection = dst_sel_clipped[dst_grid_entry] if dst_sel_block_clipped.is_empty(): continue src_intersection_arr = src_sel_offset & dst_sel_block_clipped src_oids = [] src_params = [] dst_params = [] dst_block: Block = dst_ba.blocks[dst_grid_entry] for src_index, src_grid_entry_bc in enumerate(src_inflated_grid.get_entry_iterator()): src_intersection_block: BasicSelection = src_intersection_arr[src_grid_entry_bc] if src_intersection_block.is_empty(): continue src_grid_entry = src_grid_entry_iterator[src_index] src_block: Block = src_ba_bc.blocks[src_grid_entry] src_oids.append(src_block.oid) src_sel_block_offset: BasicSelection = src_sel_offset[src_grid_entry_bc] src_dep_sel_loc = src_intersection_block - src_sel_block_offset.position() src_params.append((src_dep_sel_loc.selector(), src_sel_block_offset.get_output_shape(), src_block.transposed)) # We're looking at intersection of dst block and src block, so the # location to which we assign must be offset by dst_sel_block. dst_block_sel_loc: BasicSelection = (src_intersection_block - dst_sel_block.position()) dst_params.append((dst_block_sel_loc.selector(), dst_block.transposed)) if len(src_oids) == 0: continue dst_block.oid = self._system.update_block(dst_block.oid, *src_oids, src_params=src_params, dst_params=dst_params, syskwargs={ "grid_entry": dst_block.grid_entry, "grid_shape": dst_block.grid_shape })