Hello Andreas!



 kof123:

I really need the adoint operator of c, i.e. it has to fulfill
<cx,y> = <x,c*y> for any x and y.


ConvTranspose2d 2 should do what you want (at least in the plain-vanilla,
2d case – I didn’t check 3d or various stride / padding combinations).
Here is an example script:
import torch
torch.__version__
torch.random.manual_seed (2020)

cnva = torch.nn.Conv2d (1, 1, 3, padding = 1, bias = False)
cnvt = torch.nn.ConvTranspose2d (1, 1, 3, padding = 1, bias = False)
with torch.no_grad():
    _ = cnvt.weight.copy_ (cnva.weight)

s = torch.randn ((1, 1, 7, 7))
t = torch.randn ((1, 1, 7, 7))
(cnva (s) * t).sum()
(s * cnvt (t)).sum()

Note the use of the same weight matrix for ConvTranspose2d as for
Conv2d.
And here is its output:
>>> import torch
>>> torch.__version__
'1.6.0'
>>> torch.random.manual_seed (2020)
<torch._C.Generator object at 0x7efc1f81c6f0>
>>>
>>> cnva = torch.nn.Conv2d (1, 1, 3, padding = 1, bias = False)
>>> cnvt = torch.nn.ConvTranspose2d (1, 1, 3, padding = 1, bias = False)
>>> with torch.no_grad():
...     _ = cnvt.weight.copy_ (cnva.weight)
...
>>> s = torch.randn ((1, 1, 7, 7))
>>> t = torch.randn ((1, 1, 7, 7))
>>> (cnva (s) * t).sum()
tensor(3.5478, grad_fn=<SumBackward0>)
>>> (s * cnvt (t)).sum()
tensor(3.5478, grad_fn=<SumBackward0>)

To understand the adjoint, think of a 2d 100x100 image as being a
vector of length 1,000,000.  The 2d convolution can be represented
(inefficiently) as a 1,000,000 x 1,000,000 matrix with a particular
banded structure.  Because it’s real, its adjoint is just its transpose.
ConvTranspose2d represents (algorithmically) the appropriate
banded structure for that transposed 1,000,000 x 1,000,000 matrix.
(If pytorch were to support convolutions with complex weights, you
would presumably use the complex conjugate of Conv2d's weights
for ConvTranspose2d.)
Good luck.
K. Frank