<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def launch(self, args, ctx, grid_dims, block_dims, shared_mem=0):
 """Launch cuda kernel. Parameters args : tuple of NDArray or numbers List of arguments for kernel. NDArrays are expected for pointer types (e.g. `float*`, `double*`) while numbers are expected for non-pointer types (e.g. `int`, `float`). ctx : Context The context to launch kernel on. Must be GPU context. grid_dims : tuple of 3 integers Grid dimensions for CUDA kernel. block_dims : tuple of 3 integers Block dimensions for CUDA kernel. shared_mem : integer, optional Size of dynamically allocated shared memory. Defaults to 0. """

        assert ctx.device_type == 'gpu', "Cuda kernel can only be launched on GPU"
        assert len(grid_dims) == 3, "grid_dims must be a tuple of 3 integers"
        assert len(block_dims) == 3, "grid_dims must be a tuple of 3 integers"
        assert len(args) == len(self._dtypes), \
            "CudaKernel(%s) expects %d arguments but got %d"%(
                self._name, len(self._dtypes), len(args))
        void_args = []
        ref_holder = []
        for i, (arg, is_nd, dtype) in enumerate(zip(args, self._is_ndarray, self._dtypes)):
            if is_nd:
                assert isinstance(arg, NDArray), \
                    "The %d-th argument is expected to be a NDArray but got %s"%(
                        i, type(arg))
                void_args.append(arg.handle)
            else:
                assert isinstance(arg, numeric_types), \
                    "The %d-th argument is expected to be a number, but got %s"%(
                        i, type(arg))
                ref_holder.append(np.array(arg, dtype=dtype))
                void_args.append(ref_holder[-1].ctypes.data_as(ctypes.c_void_p))

        check_call(_LIB.MXRtcCudaKernelCall(
            self.handle,
            ctx.device_id,
            c_array(ctypes.c_void_p, void_args),
            mx_uint(grid_dims[0]), mx_uint(grid_dims[1]), mx_uint(grid_dims[2]),
            mx_uint(block_dims[0]), mx_uint(block_dims[1]), mx_uint(block_dims[2]),
            mx_uint(shared_mem)))