# Save_for_backward convertion to tuple

Hello,

I am a pytorch beginner and wanted to implement a really simple module (autograd and module extension) reprensenting an affine transformation.

I am however struggling with my code which returns the following error:
‘Variable’ object has no attribute ‘numpy’

It comes from the fact (I guess) that the type of input is tuple whereas I am expecting input to be a tuple.

I am pretty sure I am doing many stupid things and if somebody can help me / guide me it will be greatly appreciate. Here is the implementation I made (get prepared … sorry)

``````import torch

import numpy as np
from scipy.signal import convolve2d, correlate2d
from torch.nn.modules.module import Module
from torch.nn.parameter import Parameter
##

## Inherit from Function
class RigidTransformationFunction(Function):
# Both forward and backward are @staticmethods

def forward(self, input, param):
# Affine transformation
# rotation parametrised by 3 rotation angles
R_ = Rotation3D()
R_.rot3D(param[0,0], param[0,1], param[0,2])
t = torch.Tensor(3, 1)
t[0,0] = param[0,3]
t[1,0] = param[0,4]
t[2,0] = param[0,5]
r = torch.from_numpy(R_.R)
xyz_ = r @ input.double() + t.double()
self.save_for_backward(input)

# derived wrt affine parameters
input = self.saved_tensors
# rotation parametrised by 3 rotation angles
R_ = Rotation3D()
r = torch.from_numpy(R_.R_x)
r = torch.from_numpy(R_.R_y)
r = torch.from_numpy(R_.R_z)

class RigidTransformation(Module):
def __init__(self):
super(RigidTransformation, self).__init__()
self.form = 'rotation_angle'
self.param = Parameter(torch.randn(1, 6))

def forward(self, input):
return RigidTransformationFunction()(input, self.param)

class Rotation3D:
def __init__(self):
self.R = []
self.R_x = []
self.R_y = []
self.R_z = []

def rot3D(self,rx,ry,rz):
Rx = np.array( [ [ 1 ,      0       ,     0         ] ,
[ 0 , np.cos(rx) , -np.sin( rx ) ] ,
[ 0 , np.sin( rx ) ,  np.cos( rx ) ] ] )

Ry = np.array( [ [ np.cos( ry )  ,     0       ,     np.sin( ry ) ] ,
[ 	0 		  , 	1 		, 		  0 	   ] ,
[ -np.sin( ry ) , 	0 		,  	  np.cos( ry ) ] ] )

Rz = np.array( [ [ np.cos( rz )  ,  -np.sin( rz )  ,   0 ] ,
[ np.sin( rz )  ,   np.cos( rz )  , 	0 ] ,
[ 	0 		  , 	 	0 		,   1 ] ] )

self.R = Rz.dot(Ry.dot(Rx))

Rx = np.matrix( [ [ 0 ,      0        ,     0         ] ,
[ 0 , -np.sin( rx ) , -np.cos( rx ) ] ,
[ 0 ,  np.cos( rx ) , -np.sin( rx ) ] ] )
Ry = np.matrix( [ [ np.cos( ry )  ,     0       ,     np.sin( ry ) ] ,
[ 	0 		  , 	1 		, 		  0 	   ] ,
[ -np.sin( ry ) , 	0 		,  	  np.cos( ry ) ] ] )
Rz = np.matrix( [ [ np.cos( rz )  ,  -np.sin( rz )  ,   0 ] ,
[ np.sin( rz )  ,   np.cos( rz )  , 	0 ] ,
[ 	0 		  , 	 	0 		,   1 ] ] )
self.R_x = Rz.dot(Ry.dot(Rx))

Rx = np.matrix( [ [ 1 ,      0       ,     0         ] ,
[ 0 , np.cos( rx ) , -np.sin( rx ) ] ,
[ 0 , np.sin( rx ) ,  np.cos( rx ) ] ] )
Ry = np.matrix( [ [ -np.sin( ry )  ,     0       ,     np.cos( ry ) ] ,
[ 	0 		   , 	 0 		 , 		  0 	    ] ,
[ -np.cos( ry )  , 	 0 		 ,    -np.sin( ry ) ] ] )
Rz = np.matrix( [ [ np.cos( rz )  ,  -np.sin( rz )  ,   0 ] ,
[ np.sin( rz )  ,   np.cos( rz )  , 	0 ] ,
[ 	0 		  , 	 	0 		,   1 ] ] )
self.R_y = Rz.dot(Ry.dot(Rx))

Rx = np.matrix( [ [ 1 ,      0       ,     0         ] ,
[ 0 , np.cos( rx ) , -np.sin( rx ) ] ,
[ 0 , np.sin( rx ) ,  np.cos( rx ) ] ] )
Ry = np.matrix( [ [ np.cos( ry )  ,     0       ,     np.sin( ry ) ] ,
[ 	0 		  , 	1 		, 		  0 	   ] ,
[ -np.sin( ry ) , 	0 		,  	  np.cos( ry ) ] ] )
Rz = np.matrix( [ [ -np.sin( rz )  ,  -np.cos( rz )  ,   0 ] ,
[  np.cos( rz )  ,  -np.sin( rz )  , 	 0 ] ,
[ 	0 		   , 	 	0 		 ,   0 ] ] )
self.R_z = Rz.dot(Ry.dot(Rx))

rT = RigidTransformation()
print(list(rT.parameters()))
print(input)
output = rT(input)
print(output)
print('______________')
output.backward(torch.randn(8, 8))
``````@staticmethod
Variables dont have `.numpy()` defined on them. If `x` is a Variable, then `x.data.numpy()` will exist instead.