Repository: pytorch/pytorch

Issue:
nn.Conv2d errors with bias=False and groups=2
    x = Variable(torch.rand((16, 64, 8, 8)))
    res = nn.Conv2d(16, 16, kernel_size=3, groups=2, bias=False)(x)

results in:
> File ".../torch/nn/_functions/conv.py", line 88, in _update_output
    return self._thnn('update_output', input, weight, bias)
  File ".../torch/nn/_functions/conv.py", line 158, in _thnn
    grouped_args += [group(t) for t in args]
  File ".../torch/nn/_functions/conv.py", line 153, in group
    dim = 0 if tensor.dim() == 1 else 1
AttributeError: 'NoneType' object has no attribute 'dim'

The `t` in line 158 is `None` because there is no bias.   I am running on CPU so this is in the `_thnn` code.




Discussion:

Resolving patch:
diff --git a/torch/nn/_functions/conv.py b/torch/nn/_functions/conv.py
--- a/torch/nn/_functions/conv.py
+++ b/torch/nn/_functions/conv.py
@@ -149,6 +149,8 @@ def _thnn(self, fn_name, input, weight, *args):
             res = []
             for g in range(self.groups):
                 def group(tensor, dim=None):
+                    if tensor is None:
+                        return None
                     if dim is None:
                         dim = 0 if tensor.dim() == 1 else 1
                     n = tensor.size(dim) // self.groups
@@ -158,7 +160,8 @@ def group(tensor, dim=None):
                 grouped_args += [group(t) for t in args]
                 res.append(impl[fn_name](self, self._bufs[g], *grouped_args))
             if fn_name == 'grad_params':
-                return [torch.cat(t, 0) for t in zip(*res)]
+                return [torch.cat(t, 0) if t[0] is not None else None
+                                        for t in zip(*res)]
             else:
                 return torch.cat(res, 1)
 
@@ -178,8 +181,11 @@ def _view3d(*tensors):
     # view 4d tensor as 3d
     output = []
     for t in tensors:
-        assert t.dim() == 4 and t.size(2) == 1
-        output += [t.squeeze(2)]
+        if t is None:
+            output += [None]
+        else:
+            assert t.dim() == 4 and t.size(2) == 1
+            output += [t.squeeze(2)]
     return output
 
 
diff --git a/torch/nn/parameter.py b/torch/nn/parameter.py
--- a/torch/nn/parameter.py
+++ b/torch/nn/parameter.py
@@ -2,6 +2,25 @@
 
 
 class Parameter(Variable):
+    """A kind of Variable that is to be considered a module parameter.
+
+    Parameters are :class:`~torch.autograd.Variable` subclasses, that have a
+    very special property when used with :class:`Module` s - when they're
+    assigned as Module attributes they are automatically added to the list of
+    its parameters, and will appear e.g. in :meth:`~Module.parameters` iterator.
+    Assigning a Variable doesn't have such effect. This is because one might
+    want to cache some temporary state, like last hidden state of the RNN, in
+    the model. If there was no such class as :class:`Parameter`, these
+    temporaries would get registered too.
+
+    Another difference is that parameters can't be volatile and that they
+    require gradient by default.
+
+    Arguments:
+        data (Tensor): parameter tensor.
+        requires_grad (bool, optional): if the parameter requires gradient. See
+            :ref:`excluding-subgraphs` for more details.
+    """
 
     def __init__(self, data, requires_grad=True):
         super(Parameter, self).__init__(data, requires_grad=requires_grad)
diff --git a/torch/optim/optimizer.py b/torch/optim/optimizer.py
--- a/torch/optim/optimizer.py
+++ b/torch/optim/optimizer.py
@@ -24,6 +24,8 @@ def __init__(self, params, defaults):
 
         self.state = defaultdict(dict)
         self.param_groups = list(params)
+        if len(self.param_groups) == 0:
+            raise ValueError("optimizer got an empty parameter list")
         if not isinstance(self.param_groups[0], dict):
             self.param_groups = [{'params': self.param_groups}]
 
@@ -50,6 +52,8 @@ def __init__(self, params, defaults):
                 if not param.requires_grad:
                     raise ValueError("optimizing a parameter that doesn't "
                         "require gradients")
+                if param.creator is not None:
+                    raise ValueError("can't optimize a non-leaf Variable")
 
     def __getstate__(self):
         return {


Test patch:
diff --git a/test/test_nn.py b/test/test_nn.py
--- a/test/test_nn.py
+++ b/test/test_nn.py
@@ -1308,6 +1308,11 @@ def add_test(test):
         input_size=(2, 4, 6, 5),
         cudnn=True,
     ),
+    dict(
+        fullname='Conv2d_groups_thnn',
+        constructor=lambda: nn.Conv2d(4, 6, (3, 2), groups=2),
+        input_size=(2, 4, 6, 5),
+    ),
     dict(
         module_name='ConvTranspose2d',
         constructor_args=(3, 4, 3, (3, 2), 1, (1, 1)),