Repository: pyro-ppl/pyro

Issue:
Incorrect result from Delta's batch_log_pdf
It looks like there may be a bug in Delta's `batch_log_pdf` method. When the value we're computing the log prob of doesn't match the parameter I expect `batch_log_pdf` to return `-inf` but it doesn't. For example:

```
x = Variable(torch.Tensor([[1.0]]))
y = Variable(torch.Tensor([[2.0]]))

# This is OK, it returns zero as expected:
print(Delta(x).batch_log_pdf(x))

# Here I expect -inf, but get 2.5500e+08
print(Delta(x).batch_log_pdf(y))
```

`log_pdf` works as expected.

(This isn't high priority for me.)


Discussion:
Thanks for pointing it out. Added to #177. 

Resolving patch:
diff --git a/pyro/distributions/__init__.py b/pyro/distributions/__init__.py
--- a/pyro/distributions/__init__.py
+++ b/pyro/distributions/__init__.py
@@ -23,7 +23,7 @@
 categorical = Categorical()
 bernoulli = RandomPrimitive(Bernoulli)
 beta = RandomPrimitive(Beta)
-delta = Delta()
+delta = RandomPrimitive(Delta)
 exponential = RandomPrimitive(Exponential)
 gamma = RandomPrimitive(Gamma)
 multinomial = RandomPrimitive(Multinomial)
diff --git a/pyro/distributions/delta.py b/pyro/distributions/delta.py
--- a/pyro/distributions/delta.py
+++ b/pyro/distributions/delta.py
@@ -14,44 +14,40 @@ class Delta(Distribution):
     """
     enumerable = True
 
-    def _sanitize_input(self, v):
-        if v is not None:
-            # stateless distribution
-            return v
-        elif self.v is not None:
-            # stateful distribution
-            return self.v
-        else:
-            raise ValueError("Parameter(s) were None")
-
-    def __init__(self, v=None, batch_size=1, *args, **kwargs):
+    def __init__(self, v, batch_size=None, *args, **kwargs):
         """
         Params:
           `v` - value
         """
         self.v = v
-        if v is not None:
-            if v.dim() == 1 and batch_size > 1:
-                self.v = v.expand(v, v.size(0))
+        if not isinstance(self.v, Variable):
+            self.v = Variable(self.v)
+        if v.dim() == 1 and batch_size is not None:
+            self.v = v.expand(v, v.size(0))
         super(Delta, self).__init__(*args, **kwargs)
 
-    def sample(self, v=None):
-        v = self._sanitize_input(v)
-        if isinstance(v, Variable):
-            return v
-        return Variable(v)
+    def batch_shape(self, x=None):
+        event_dim = 1
+        v = self.v
+        if x is not None and x.size() != v.size():
+            v = self.v.expand_as(x)
+        return v.size()[:-event_dim]
+
+    def event_shape(self):
+        event_dim = 1
+        return self.v.size()[-event_dim:]
+
+    def shape(self, x=None):
+        return self.batch_shape(x) + self.event_shape()
 
-    def batch_log_pdf(self, x, v=None, batch_size=1):
-        v = self._sanitize_input(v)
-        if x.dim == 1:
-            x = x.expand(batch_size, x.size(0))
-        return (torch.eq(x, v.expand_as(x)) - 1).float() * 999999
+    def sample(self):
+        return self.v
 
-    def log_pdf(self, x, v=None, *args, **kwargs):
-        v = self._sanitize_input(v)
-        if torch.equal(x.data, v.data.expand_as(x.data)):
-            return Variable(torch.zeros(1).type_as(v.data))
-        return Variable(torch.Tensor([-float("inf")]).type_as(v.data))
+    def batch_log_pdf(self, x):
+        v = self.v
+        if x.size() != v.size():
+            v = v.expand_as(x)
+        return torch.sum(torch.eq(x, v).float().log(), -1)
 
     def support(self, v=None):
         """
@@ -62,6 +58,4 @@ def support(self, v=None):
         :return: torch variable enumerating the support of the delta distribution.
         :rtype: torch.autograd.Variable.
         """
-        v = self._sanitize_input(v)
-        # univariate case
-        return Variable(v.data)
+        return Variable(self.v.data)


Test patch:
diff --git a/tests/distributions/test_delta.py b/tests/distributions/test_delta.py
--- a/tests/distributions/test_delta.py
+++ b/tests/distributions/test_delta.py
@@ -11,7 +11,8 @@ def setUp(self):
         self.v = Variable(torch.Tensor([3]))
         self.vs = Variable(torch.Tensor([[0], [1], [2], [3]]))
         self.test_data = Variable(torch.Tensor([3, 3, 3]))
-        self.batch_test_data = Variable(torch.arange(0, 4).unsqueeze(1).expand(4, 3))
+        self.batch_test_data_1 = Variable(torch.arange(0, 4).unsqueeze(1).expand(4, 3))
+        self.batch_test_data_2 = Variable(torch.arange(4, 8).unsqueeze(1).expand(4, 3))
         self.expected_support = [[0], [1], [2], [3]]
         self.expected_support_non_vec = [3]
         self.analytic_mean = 3
@@ -23,8 +24,10 @@ def test_log_pdf(self):
         self.assertEqual(torch.sum(log_px_torch), 0)
 
     def test_batch_log_pdf(self):
-        log_px_torch = dist.delta.batch_log_pdf(self.batch_test_data, self.vs, batch_size=2).data
+        log_px_torch = dist.delta.batch_log_pdf(self.batch_test_data_1, self.vs).data
         self.assertEqual(torch.sum(log_px_torch), 0)
+        log_px_torch = dist.delta.batch_log_pdf(self.batch_test_data_2, self.vs).data
+        self.assertEqual(torch.sum(log_px_torch), float('-inf'))
 
     def test_mean_and_var(self):
         torch_samples = [dist.delta(self.v).data.cpu().numpy()