def col2im(context, node):
    """
    Combines an array of sliding local blocks into a large containing tensor.

    torch.nn.functional.fold aims to be the general version:
    col2im is the "2 output spatial dimensions" case of fold.

    PyTorch currently only supports col2im: torch.nn.functional.fold redispatches to at::col2im,
    which is why coremltools needs col2im to convert torch.nn.functional.fold.

    We currently only support col2im (consistent with PyTorch) and:
    * dilation set to 1
    * padding set to 0
    * stride >= kernel_size
    * output_size is divisible by kernel_size
    by gathering columns to form an image. More flexbible support will be added in the future.

    Reference https://pytorch.org/docs/stable/generated/torch.nn.Fold.html
    """

    def _parse_positional_args(context, node) -> Tuple[Var]:
        inputs = _get_inputs(context, node, expected=6)

        x = inputs[0]
        output_size = inputs[1]
        kernel_size = inputs[2]
        dilation = inputs[3]
        padding = inputs[4]
        stride = inputs[5]

        return x, output_size, kernel_size, dilation, padding, stride

    def _translate_torch_args(
        output_size: Var, kernel_size: Var, dilation: Var, padding: Var, stride: Var
    ) -> Tuple[np.ndarray]:
        if isinstance(output_size, Var):
            output_size = output_size.val
        if isinstance(kernel_size, Var):
            kernel_size = kernel_size.val
        if isinstance(dilation, Var):
            dilation = dilation.val
        if isinstance(padding, Var):
            padding = padding.val
        if isinstance(stride, Var):
            stride = stride.val

        if len(output_size) != 2:
            raise ValueError("Only supports 2 output spatial dimensions for col2im (fold).")
        if any(dilation > 1):
            raise ValueError("Only supports dilation=1 for col2im (fold).")
        # In Pytorch, if an image entry unfolds to multiple columns,
        # then during folding those columns are accumulated in that image entry
        # In Core ML, however, gather op does not accumulate,
        # so we cowardly refuse to convert if accumulation happens
        # TODO: We may be able to support accumulation by translating to Core ML scatter op,
        #       i.e. rather than directly gather columns to form an image,
        #       we may create an empty image then scatter columns into image entries.
        #       The con is scatter op falls off ANE, while gather op resides
        if any(stride < kernel_size):
            raise ValueError("Only supports stride >= kernel_size for col2im (fold).")
        # We implement fold as an inverse to unfold
        # i.e. a gather with indices that are inverse to unfold gather indices
        # This works only if there is no edge leftover
        if any((output_size + 2 * padding) % stride != 0):
            raise ValueError(
                "Only supports (output_size + 2 * padding) % stride = 0 for col2im (fold)."
            )

        return output_size, kernel_size, padding, stride

    x, output_size, kernel_size, dilation, padding, stride = _parse_positional_args(context, node)
    output_size, kernel_size, padding, stride = _translate_torch_args(
        output_size, kernel_size, dilation, padding, stride
    )
    N, block_size, block_count = x.shape
    C = int(block_size / np.prod(kernel_size))
    H, W = output_size
    H_padded, W_padded = output_size + 2 * padding

    """
    The implementation below assumes `x` to be contiguous
    """
    # inverse unfold indices
    indices_unfold = _construct_unfold_indices(N, C, H_padded, W_padded, kernel_size, stride)
    indices = np.empty(indices_unfold.shape, dtype=np.int32)
    for i in range(indices.shape[0]):
        indices[indices_unfold[i]] = i

    # perform gather with fold indices
    x_flatten = mb.reshape(x=x, shape=(-1,))
    y_flatten = mb.gather_along_axis(x=x_flatten, indices=indices)
    expected_numel = N * C * H_padded * W_padded
    if np.prod(y_flatten.shape) > expected_numel:
        y_flatten = y_flatten = mb.slice_by_size(x=y_flatten, begin=(0,), size=(expected_numel,))
    if all(padding == 0):
        y = mb.reshape(x=y_flatten, shape=(N, C, H, W), name=node.name)
    else:
        y_padded = mb.reshape(x=y_flatten, shape=(N, C, H_padded, W_padded))
        # cut off the padded outer rim
        y = mb.slice_by_size(
            x=y_padded, begin=(0, 0, padding[0], padding[1]), size=(N, C, H, W), name=node.name
        )
    context.add(y)

    Combines an array of sliding local blocks into a large containing tensor.

    torch.nn.functional.fold aims to be the general version:
    col2im is the "2 output spatial dimensions" case of fold.

    PyTorch currently only supports col2im: torch.nn.functional.fold redispatches to at::col2im,
    which is why coremltools needs col2im to convert torch.nn.functional.fold.

    We currently only support col2im (consistent with PyTorch) and:
    * dilation set to 1
    * padding set to 0
    * stride >= kernel_size
    * output_size is divisible by kernel_size
    by gathering columns to form an image. More flexbible support will be added in the future.

    Reference https://pytorch.org/docs/stable/generated/torch.nn.Fold.html
    
coremltools/converters/mil/frontend/torch/ops.py