Hi Thomas,
Sorry to bother you again. I have tried to code Relationship 14 for the 2D case but I can’t make it work due to a PyTorch restriction ( output_pading < stride). Do you think you have the time to have a look over it?
import torch


def calculate_deconv_params(
    input_shape, kernel_shape, padding_shape, stride_shape
):
    '''
    Calculates the padding and output_padding for a torch.nn.ConvTransposeXD to 
    invert the effect of a torch.nn.ConvXD

    Args:
        input_shape: The input shape of tensor GIVEN TO torch.nn.ConvXD
        kernel_shape: The kernel shape passed to the torch.nn.ConvXD's kernel_size
        padding_shape: The padding shape passed to the torch.nn.ConvXD's padding
        stride_shape: The stride shape passed ot the torch.nn.ConvXD's stride

    Returns:
        A dictionary of with the following format:
            {
                "kernel_size": k',
                "stride": s',
                "output_padding": a,
                "padding": p',
            }

    References:
        Dumoulin, V. and Visin, F., 2016.
        A guide to convolution arithmetic for deep learning.
        arXiv preprint arXiv:1603.07285.
    '''
    # k' = k
    deconv_kernel_shape = kernel_shape

    # This deviates from Relationship 14 as I assume that the torch.nn.ConvTransposeXD
    # uses the stride to actually insert zeroes in the input and then continues with a
    # stride of 1
    deconv_stride_shape = stride_shape

    # p' = k - p - 1
    deconv_padding_shape = [
        kernel_shape[i] - padding_shape[i] - 1 for i in range(len(kernel_shape))
    ]

    # a = (i + 2 * p - k) % s
    deconv_output_padding_shape = [
        (input_shape[i + 2] + 2 * padding_shape[i] - kernel_shape[i])
        % stride_shape[i]
        for i in range(len(kernel_shape))
    ]

    return {
        "kernel_size": deconv_kernel_shape,
        "stride": deconv_stride_shape,
        "output_padding": deconv_output_padding_shape,
        "padding": deconv_padding_shape,
    }


x = torch.rand(1, 1, 6, 6)

print("Working example")
kernel_size = (3, 3)
padding = (1, 1)
stride = (2, 2)

print(x.shape)

c1 = torch.nn.Conv2d(
    in_channels=1,
    out_channels=1,
    kernel_size=kernel_size,
    stride=stride,
    padding=padding,
)
print(c1(x).shape)

c2_params = calculate_deconv_params(x.shape, kernel_size, padding, stride)
c2_params.update({"in_channels": 1, "out_channels": 1})
print(c2_params)

c2 = torch.nn.ConvTranspose2d(**c2_params)
print(c2(c1(x)).shape)


print("Breaking example")
kernel_size = (2, 2)
padding = (1, 1)
stride = (2, 2)

print(x.shape)

c1 = torch.nn.Conv2d(
    in_channels=1,
    out_channels=1,
    kernel_size=kernel_size,
    stride=stride,
    padding=padding,
)
print(c1(x).shape)

c2_params = calculate_deconv_params(x.shape, kernel_size, padding, stride)
c2_params.update({"in_channels": 1, "out_channels": 1})
print(c2_params)

c2 = torch.nn.ConvTranspose2d(**c2_params)
print(c2(c1(x)).shape)

Thanks a lot and stay safe!
cc @ptrblck