Thanks for the code.
Note that you are currently using a global beta and cutoff in ANN_Pytorch.forward, since you didn’t set them as attributes.
Also Variables are deprecated since PyTorch 0.4.0, so you can just use torch.tensors instead.
After fixing these small issues and using some random arguments, I get valid gradients for eta:
class ANN_Pytorch(torch.nn.Module):
    def __init__(self, D_in, H1, H2, D_out, cutoff, beta, device):
        super(ANN_Pytorch, self).__init__()
        self.w0 = ((15 - 2.5) * torch.rand(1, D_in, device=device, requires_grad=True) + 2.5).double().unsqueeze(0).permute(2, 0, 1)
        self.eta = nn.Parameter(self.w0)
        self.linear1 = torch.nn.Linear(D_in, H1)
        self.linear2 = torch.nn.Linear(H1, H2)
        self.linear3 = torch.nn.Linear(H2, D_out)
        self.beta = beta
        self.cutoff = cutoff


    def forward(self, X):
        output = torch.zeros(X.shape[0], X.shape[1])
        for i in range(X.shape[0]):
            output[i,:] = RigidityExp.apply(X[i,:].reshape(1,-1).double(), self.eta, self.beta, self.cutoff)
        z1 = self.linear1(output)
        f1 = F.sigmoid(z1)
        z2 = self.linear2(f1)
        f2 = F.sigmoid(z2)
        y_hat = self.linear3(f2)
        return y_hat

model = ANN_Pytorch(5, 10, 10, 10, 1, 1, 'cpu')
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr = 100, momentum = 0.9, weight_decay = 1e-2)
data = torch.randn(1, 5)
ytrain = torch.randn(1, 10)

model.train()
y_pred  = model(data)
loss = criterion(y_pred, ytrain)
optimizer.zero_grad()
loss.backward()
print(model.eta.grad)
> tensor([[[ 7.7592511500e-04]],

        [[-7.3158718795e-05]],

        [[-1.6567345016e-04]],

        [[ 3.0475339896e-04]],

        [[-1.9967394943e-04]]], dtype=torch.float64)
optimizer.step()