Check for output_padding <= stride/dilation in ConvTranspose1d
Labels: module: convolution, triaged, module: padding

### 🚀 The feature, motivation and pitch

I'm working on a network that should output the exact same sequence length (L) as the input has. I use a CNN to extract feature information and shorten the sequence length, feed the results into an LSTM and use transpose convolutional layers to get the original sequence length again. For that I'm using two custom formulas for padding for each convolutional and transposed convolutional layer. This basically works like ``padding="same"``, but also for strides > 1 and for ConvTranspose1d, which doesn't have this option. The input sequence length then needs to be divisible by the stride to the power of the number of layers chosen.  Everything works as intended **except** for using stride=1 and an even kernel_size when using transpose convolutional layers. The output padding in this case would need to be 1 and it would work flawlessly, but Pytorch doesn't allow that: ``RuntimeError: output padding must be smaller than either stride or dilation, but got output_padding_height: 0 output_padding_width: 1 stride_height: 1 stride_width: 1 dilation_height: 1 dilation_width: 1``. All other combinations of even and uneven kernel size and stride work as I intend. Would it be possible to check for ``output_padding <= stride`` instead of ``output_padding < stride``?            

Code to test on:    
```python
import torch
import math

def xpadding(l_in, l_out, stride, dilation, kernel_size):
    """Padding formula for Conv1d.

    The formula is adapted the formula for calculating the output sequence length (L_out)
    from the Pytorch documentation. The desired Lout is: L_out = L_in/stride.

        Args:
            l_in: 'int', input sequence length
            l_out: 'int', output sequence length
            stride: 'int', strides/steps of kernel points
            dilation: 'int', spacing between kernel points
            kernel_size: 'int'

        Returns:
            padding: 'int', padding needed to achieve division without remainder
                of sequence length, e.g. input seq_len: 10, stride: 2, expected/
                necessary output seq_len: 5
    """
    padding = math.ceil(((l_out - 1) * stride - l_in + dilation * (kernel_size - 1) + 1) / 2)
    # math.ceil to  avoid rounding half to even/bankers rounding, only needed for even L_in
    return padding


def trans_padding(l_in, l_out, stride, dilation, kernel_size, output_pad):
    """Padding formula for TransposeConv1d.

    The formula is adapted the formula for calculating the output sequence length (L_out)
    from the Pytorch documentation. The desired Lout is: L_out = L_in * stride. The output
    padding equals zero, if the stride is uneven, and one otherwise.

        Args:
            l_in: 'int', input sequence length
            l_out: 'int', output sequence length
            stride: 'int', strides/steps of kernel points
            dilation: 'int', spacing between kernel points
            kernel_size: 'int'
            output_pad: 'int', additional size added to one side of the output shape

        Returns:
            padding: 'int', padding needed to achieve multiplication "without remainder"
                of sequence length, e.g. input seq_len: 5, stride: 2, expected/
                necessary output seq_len: 10
    """
    padding = ((l_in - 1) * stride - l_out + dilation * (kernel_size - 1) + output_pad + 1) / 2
    return int(padding)


seq_len=21384
stride=1
layers = 2
x_in = seq_len / (stride ** layers)
print(x_in)
assert seq_len % stride ** layers == 0, "sequence length is not divisible by the stride to the power of layers"
x = torch.rand(2,4,seq_len)
kernel_size = 9
filter_size = 24
input_size=4
dilation=1

# CNN
# --------------
print("CNN\n---------------")
for layer in range(layers):
    l_in = seq_len / stride ** layer
    l_out = seq_len / stride ** (layer + 1)
    print("input size:", l_in, "desired output size", l_out)
    xpad = xpadding(l_in, l_out, stride, dilation, kernel_size)
    conv = torch.nn.Conv1d(input_size, filter_size, kernel_size, stride=stride, padding=xpad, dilation=dilation)
    x = conv(x)
    input_size = filter_size
    print(x.size())

# Transposed CNN
# ----------------
print("Transposed CNN\n---------------")
out_pad = 0 if (kernel_size+stride) % 2 == 0 else 1
print("output padding:", out_pad)

for layer in range(layers, 0, -1):
    l_in = seq_len / stride ** layer
    l_out = l_in * stride
    tpad = trans_padding(l_in, l_out, stride, dilation, kernel_size, out_pad)
    print("input size:", l_in, "desired output size", l_out)
    transconv = torch.nn.ConvTranspose1d(24, 24, kernel_size, stride=stride, padding=tpad,
                                         dilation=dilation, output_padding=out_pad)
    x = transconv(x)
    print(x.size())
```

### Alternatives

_No response_

### Additional context

_No response_