Repository: cornellius-gp/gpytorch
Pull request #222 (2018-08-06 19:19:42)

Zero mean should produce a 1D output
This addresses an issue pointed out in #218 

cc/ @Balandat @jacobrgardner 

gpleiss: @Balandat added unit tests, removed variable
jacobrgardner: I've been running `black -l 120` for what it's worth, I much prefer 120 chars per line to 80
Balandat: yeah `black -l 120` makes sense, this with clause just comes out looking pretty weird...
Balandat: test failure seems unrelated, but can you still lock the seed to a different value so this passes?

Patch:
diff --git a/gpytorch/means/zero_mean.py b/gpytorch/means/zero_mean.py
index bffe7e24..ab50fc20 100644
--- a/gpytorch/means/zero_mean.py
+++ b/gpytorch/means/zero_mean.py
@@ -9,4 +9,6 @@ from gpytorch.means import Mean
 
 class ZeroMean(Mean):
     def forward(self, input):
-        return torch.zeros_like(input)
+        return torch.zeros(
+            (input.size(0), input.size(1)), dtype=input.dtype, device=input.device
+        )


Test patch:
diff --git a/test/examples/test_kissgp_additive_regression.py b/test/examples/test_kissgp_additive_regression.py
index e4a88cbc..97057729 100644
--- a/test/examples/test_kissgp_additive_regression.py
+++ b/test/examples/test_kissgp_additive_regression.py
@@ -11,10 +11,9 @@ import torch
 import unittest
 import gpytorch
 from torch import optim
-from torch.autograd import Variable
 from gpytorch.kernels import RBFKernel, AdditiveGridInterpolationKernel
 from gpytorch.likelihoods import GaussianLikelihood
-from gpytorch.means import ConstantMean
+from gpytorch.means import ZeroMean
 from gpytorch.priors import SmoothedBoxPrior
 from gpytorch.random_variables import GaussianRandomVariable
 
@@ -26,8 +25,7 @@ for i in range(n):
     for j in range(n):
         train_x[i * n + j][0] = float(i) / (n - 1)
         train_x[i * n + j][1] = float(j) / (n - 1)
-train_x = Variable(train_x)
-train_y = Variable((torch.sin(train_x.data[:, 0]) + torch.cos(train_x.data[:, 1])) * (2 * pi))
+train_y = (torch.sin(train_x.data[:, 0]) + torch.cos(train_x.data[:, 1])) * (2 * pi)
 
 m = 10
 test_x = torch.zeros(pow(m, 2), 2)
@@ -35,17 +33,18 @@ for i in range(m):
     for j in range(m):
         test_x[i * m + j][0] = float(i) / (m - 1)
         test_x[i * m + j][1] = float(j) / (m - 1)
-test_x = Variable(test_x)
-test_y = Variable((torch.sin(test_x.data[:, 0]) + torch.cos(test_x.data[:, 1])) * (2 * pi))
+test_y = (torch.sin(test_x.data[:, 0]) + torch.cos(test_x.data[:, 1])) * (2 * pi)
 
 
 # All tests that pass with the exact kernel should pass with the interpolated kernel.
 class GPRegressionModel(gpytorch.models.ExactGP):
     def __init__(self, train_x, train_y, likelihood):
         super(GPRegressionModel, self).__init__(train_x, train_y, likelihood)
-        self.mean_module = ConstantMean(prior=SmoothedBoxPrior(-1, 1))
+        self.mean_module = ZeroMean()
         self.base_covar_module = RBFKernel(
-            log_lengthscale_prior=SmoothedBoxPrior(exp(-3), exp(3), sigma=0.1, log_transform=True)
+            log_lengthscale_prior=SmoothedBoxPrior(
+                exp(-3), exp(3), sigma=0.1, log_transform=True
+            )
         )
         self.covar_module = AdditiveGridInterpolationKernel(
             self.base_covar_module, grid_size=100, grid_bounds=[(0, 1)], n_components=2
@@ -59,9 +58,12 @@ class GPRegressionModel(gpytorch.models.ExactGP):
 
 class TestKISSGPAdditiveRegression(unittest.TestCase):
     def setUp(self):
-        if os.getenv("UNLOCK_SEED") is None or os.getenv("UNLOCK_SEED").lower() == "false":
+        if (
+            os.getenv("UNLOCK_SEED") is None
+            or os.getenv("UNLOCK_SEED").lower() == "false"
+        ):
             self.rng_state = torch.get_rng_state()
-            torch.manual_seed(0)
+            torch.manual_seed(1)
             if torch.cuda.is_available():
                 torch.cuda.manual_seed_all(0)
             random.seed(0)
@@ -75,12 +77,16 @@ class TestKISSGPAdditiveRegression(unittest.TestCase):
         gp_model = GPRegressionModel(train_x.data, train_y.data, likelihood)
         mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, gp_model)
 
-        with gpytorch.settings.max_preconditioner_size(10), gpytorch.settings.max_cg_iterations(30):
+        with gpytorch.settings.max_preconditioner_size(
+            10
+        ), gpytorch.settings.max_cg_iterations(30):
             # Optimize the model
             gp_model.train()
             likelihood.train()
 
-            optimizer = optim.Adam(list(gp_model.parameters()) + list(likelihood.parameters()), lr=0.2)
+            optimizer = optim.Adam(
+                list(gp_model.parameters()) + list(likelihood.parameters()), lr=0.2
+            )
             optimizer.n_iter = 0
             for _ in range(20):
                 optimizer.zero_grad()
diff --git a/test/means/__init__.py b/test/means/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/means/test_constant_mean.py b/test/means/test_constant_mean.py
new file mode 100644
index 00000000..63dd3573
--- /dev/null
+++ b/test/means/test_constant_mean.py
@@ -0,0 +1,26 @@
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import torch
+import unittest
+from gpytorch.means import ConstantMean
+
+
+class TestConstantMean(unittest.TestCase):
+    def setUp(self):
+        self.mean = ConstantMean()
+        self.mean.constant.data.fill_(5)
+
+    def test_forward(self):
+        a = torch.Tensor([[1, 2], [2, 4]])
+        res = self.mean(a)
+        self.assertEqual(tuple(res.size()), (2,))
+        self.assertTrue(res.eq(5).all())
+
+    def test_forward_batch(self):
+        a = torch.Tensor([[[1, 2], [1, 2], [2, 4]], [[2, 3], [2, 3], [1, 3]]])
+        res = self.mean(a)
+        self.assertEqual(tuple(res.size()), (2, 3))
+        self.assertTrue(res.eq(5).all())
diff --git a/test/means/test_multitask_mean.py b/test/means/test_multitask_mean.py
new file mode 100644
index 00000000..19f06c6b
--- /dev/null
+++ b/test/means/test_multitask_mean.py
@@ -0,0 +1,60 @@
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import torch
+import unittest
+from gpytorch.means import ConstantMean, ZeroMean, MultitaskMean
+
+
+class TestMultitaskMean(unittest.TestCase):
+    def setUp(self):
+        self.mean = MultitaskMean([ConstantMean(), ZeroMean(), ZeroMean(), ConstantMean()], n_tasks=4)
+        self.mean.base_means[0].constant.data.fill_(5)
+        self.mean.base_means[3].constant.data.fill_(7)
+
+    def test_forward(self):
+        a = torch.Tensor([[1, 2], [2, 4]])
+        res = self.mean(a)
+        self.assertEqual(tuple(res.size()), (2, 4))
+        self.assertTrue(res[:, 0].eq(5).all())
+        self.assertTrue(res[:, 1].eq(0).all())
+        self.assertTrue(res[:, 2].eq(0).all())
+        self.assertTrue(res[:, 3].eq(7).all())
+
+    def test_forward_batch(self):
+        a = torch.Tensor([[[1, 2], [1, 2], [2, 4]], [[2, 3], [2, 3], [1, 3]]])
+        res = self.mean(a)
+        self.assertEqual(tuple(res.size()), (2, 3, 4))
+        self.assertTrue(res[:, :, 0].eq(5).all())
+        self.assertTrue(res[:, :, 1].eq(0).all())
+        self.assertTrue(res[:, :, 2].eq(0).all())
+        self.assertTrue(res[:, :, 3].eq(7).all())
+
+
+class TestMultitaskMeanSameMean(unittest.TestCase):
+    def setUp(self):
+        self.mean = MultitaskMean(ConstantMean(), n_tasks=4)
+        self.mean.base_means[0].constant.data.fill_(0)
+        self.mean.base_means[1].constant.data.fill_(1)
+        self.mean.base_means[2].constant.data.fill_(2)
+        self.mean.base_means[3].constant.data.fill_(3)
+
+    def test_forward(self):
+        a = torch.Tensor([[1, 2], [2, 4]])
+        res = self.mean(a)
+        self.assertEqual(tuple(res.size()), (2, 4))
+        self.assertTrue(res[:, 0].eq(0).all())
+        self.assertTrue(res[:, 1].eq(1).all())
+        self.assertTrue(res[:, 2].eq(2).all())
+        self.assertTrue(res[:, 3].eq(3).all())
+
+    def test_forward_batch(self):
+        a = torch.Tensor([[[1, 2], [1, 2], [2, 4]], [[2, 3], [2, 3], [1, 3]]])
+        res = self.mean(a)
+        self.assertEqual(tuple(res.size()), (2, 3, 4))
+        self.assertTrue(res[:, :, 0].eq(0).all())
+        self.assertTrue(res[:, :, 1].eq(1).all())
+        self.assertTrue(res[:, :, 2].eq(2).all())
+        self.assertTrue(res[:, :, 3].eq(3).all())
diff --git a/test/means/test_zero_mean.py b/test/means/test_zero_mean.py
new file mode 100644
index 00000000..165bd597
--- /dev/null
+++ b/test/means/test_zero_mean.py
@@ -0,0 +1,25 @@
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import torch
+import unittest
+from gpytorch.means import ZeroMean
+
+
+class TestZeroMean(unittest.TestCase):
+    def setUp(self):
+        self.mean = ZeroMean()
+
+    def test_forward(self):
+        a = torch.Tensor([[1, 2], [2, 4]])
+        res = self.mean(a)
+        self.assertEqual(tuple(res.size()), (2,))
+        self.assertTrue(res.eq(0).all())
+
+    def test_forward_batch(self):
+        a = torch.Tensor([[[1, 2], [1, 2], [2, 4]], [[2, 3], [2, 3], [1, 3]]])
+        res = self.mean(a)
+        self.assertEqual(tuple(res.size()), (2, 3))
+        self.assertTrue(res.eq(0).all())