We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug: Predictions with full_cov=True
The functions `model.predict_y()` and `model.predict_density()` do not work nicely with `full_cov=True`. For example, take `GPR`, where the `Gaussian` likelihood is used. In `predict_y()` the likelihood function `predict_mean_and_var()` is called. Here a scalar value of the likelihood variance is added to the covariance matrix. For `full_cov=True`, this should be added only to the diagonal.

I'm not sure what the right resolution for this is. In the short term, maybe throwing an error for `full_cov=True` would be helpful to show that this is unsupported.
Bug: Predictions with full_cov=True
The functions `model.predict_y()` and `model.predict_density()` do not work nicely with `full_cov=True`. For example, take `GPR`, where the `Gaussian` likelihood is used. In `predict_y()` the likelihood function `predict_mean_and_var()` is called. Here a scalar value of the likelihood variance is added to the covariance matrix. For `full_cov=True`, this should be added only to the diagonal.

I'm not sure what the right resolution for this is. In the short term, maybe throwing an error for `full_cov=True` would be helpful to show that this is unsupported.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gpflow/models/model.py`
Content:
```
1 # Copyright 2016-2020 The GPflow Contributors. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 import abc
16 from typing import Optional, Tuple
17 
18 import tensorflow as tf
19 
20 from ..base import Module
21 from ..conditionals.util import sample_mvn
22 from ..kernels import Kernel, MultioutputKernel
23 from ..likelihoods import Likelihood, SwitchedLikelihood
24 from ..mean_functions import MeanFunction, Zero
25 from ..utilities import to_default_float
26 from .training_mixins import InputData, RegressionData
27 
28 MeanAndVariance = Tuple[tf.Tensor, tf.Tensor]
29 
30 
31 class BayesianModel(Module, metaclass=abc.ABCMeta):
32     """ Bayesian model. """
33 
34     def log_prior_density(self) -> tf.Tensor:
35         """
36         Sum of the log prior probability densities of all (constrained) variables in this model.
37         """
38         if self.trainable_parameters:
39             return tf.add_n([p.log_prior_density() for p in self.trainable_parameters])
40         else:
41             return to_default_float(0.0)
42 
43     def log_posterior_density(self, *args, **kwargs) -> tf.Tensor:
44         """
45         This may be the posterior with respect to the hyperparameters (e.g. for
46         GPR) or the posterior with respect to the function (e.g. for GPMC and
47         SGPMC). It assumes that maximum_log_likelihood_objective() is defined
48         sensibly.
49         """
50         return self.maximum_log_likelihood_objective(*args, **kwargs) + self.log_prior_density()
51 
52     def _training_loss(self, *args, **kwargs) -> tf.Tensor:
53         """
54         Training loss definition. To allow MAP (maximum a-posteriori) estimation,
55         adds the log density of all priors to maximum_log_likelihood_objective().
56         """
57         return -(self.maximum_log_likelihood_objective(*args, **kwargs) + self.log_prior_density())
58 
59     @abc.abstractmethod
60     def maximum_log_likelihood_objective(self, *args, **kwargs) -> tf.Tensor:
61         """
62         Objective for maximum likelihood estimation. Should be maximized. E.g.
63         log-marginal likelihood (hyperparameter likelihood) for GPR, or lower
64         bound to the log-marginal likelihood (ELBO) for sparse and variational
65         GPs.
66         """
67         raise NotImplementedError
68 
69 
70 class GPModel(BayesianModel):
71     r"""
72     A stateless base class for Gaussian process models, that is, those of the
73     form
74 
75     .. math::
76        :nowrap:
77 
78        \begin{align}
79            \theta        & \sim p(\theta) \\
80            f             & \sim \mathcal{GP}(m(x), k(x, x'; \theta)) \\
81            f_i           & = f(x_i) \\
82            y_i \,|\, f_i & \sim p(y_i|f_i)
83        \end{align}
84 
85     This class mostly adds functionality for predictions. To use it, inheriting
86     classes must define a predict_f function, which computes the means and
87     variances of the latent function.
88 
89     These predictions are then pushed through the likelihood to obtain means
90     and variances of held out data, self.predict_y.
91 
92     The predictions can also be used to compute the (log) density of held-out
93     data via self.predict_log_density.
94 
95     It is also possible to draw samples from the latent GPs using
96     self.predict_f_samples.
97     """
98 
99     def __init__(
100         self,
101         kernel: Kernel,
102         likelihood: Likelihood,
103         mean_function: Optional[MeanFunction] = None,
104         num_latent_gps: int = None,
105     ):
106         super().__init__()
107         assert num_latent_gps is not None, "GPModel requires specification of num_latent_gps"
108         self.num_latent_gps = num_latent_gps
109         if mean_function is None:
110             mean_function = Zero()
111         self.mean_function = mean_function
112         self.kernel = kernel
113         self.likelihood = likelihood
114 
115     @staticmethod
116     def calc_num_latent_gps_from_data(data, kernel: Kernel, likelihood: Likelihood) -> int:
117         """
118         Calculates the number of latent GPs required based on the data as well
119         as the type of kernel and likelihood.
120         """
121         _, Y = data
122         output_dim = Y.shape[-1]
123         return GPModel.calc_num_latent_gps(kernel, likelihood, output_dim)
124 
125     @staticmethod
126     def calc_num_latent_gps(kernel: Kernel, likelihood: Likelihood, output_dim: int) -> int:
127         """
128         Calculates the number of latent GPs required given the number of
129         outputs `output_dim` and the type of likelihood and kernel.
130 
131         Note: It's not nice for `GPModel` to need to be aware of specific
132         likelihoods as here. However, `num_latent_gps` is a bit more broken in
133         general, we should fix this in the future. There are also some slightly
134         problematic assumptions re the output dimensions of mean_function.
135         See https://github.com/GPflow/GPflow/issues/1343
136         """
137         if isinstance(kernel, MultioutputKernel):
138             # MultioutputKernels already have num_latent_gps attributes
139             num_latent_gps = kernel.num_latent_gps
140         elif isinstance(likelihood, SwitchedLikelihood):
141             # the SwitchedLikelihood partitions/stitches based on the last
142             # column in Y, but we should not add a separate latent GP for this!
143             # hence decrement by 1
144             num_latent_gps = output_dim - 1
145             assert num_latent_gps > 0
146         else:
147             num_latent_gps = output_dim
148 
149         return num_latent_gps
150 
151     @abc.abstractmethod
152     def predict_f(
153         self, Xnew: InputData, full_cov: bool = False, full_output_cov: bool = False
154     ) -> MeanAndVariance:
155         raise NotImplementedError
156 
157     def predict_f_samples(
158         self,
159         Xnew: InputData,
160         num_samples: Optional[int] = None,
161         full_cov: bool = True,
162         full_output_cov: bool = False,
163     ) -> tf.Tensor:
164         """
165         Produce samples from the posterior latent function(s) at the input points.
166 
167         :param Xnew: InputData
168             Input locations at which to draw samples, shape [..., N, D]
169             where N is the number of rows and D is the input dimension of each point.
170         :param num_samples:
171             Number of samples to draw.
172             If `None`, a single sample is drawn and the return shape is [..., N, P],
173             for any positive integer the return shape contains an extra batch
174             dimension, [..., S, N, P], with S = num_samples and P is the number of outputs.
175         :param full_cov:
176             If True, draw correlated samples over the inputs. Computes the Cholesky over the
177             dense covariance matrix of size [num_data, num_data].
178             If False, draw samples that are uncorrelated over the inputs.
179         :param full_output_cov:
180             If True, draw correlated samples over the outputs.
181             If False, draw samples that are uncorrelated over the outputs.
182 
183         Currently, the method does not support `full_output_cov=True` and `full_cov=True`.
184         """
185         if full_cov and full_output_cov:
186             raise NotImplementedError(
187                 "The combination of both `full_cov` and `full_output_cov` is not supported."
188             )
189 
190         # check below for shape info
191         mean, cov = self.predict_f(Xnew, full_cov=full_cov, full_output_cov=full_output_cov)
192         if full_cov:
193             # mean: [..., N, P]
194             # cov: [..., P, N, N]
195             mean_for_sample = tf.linalg.adjoint(mean)  # [..., P, N]
196             samples = sample_mvn(
197                 mean_for_sample, cov, full_cov, num_samples=num_samples
198             )  # [..., (S), P, N]
199             samples = tf.linalg.adjoint(samples)  # [..., (S), N, P]
200         else:
201             # mean: [..., N, P]
202             # cov: [..., N, P] or [..., N, P, P]
203             samples = sample_mvn(
204                 mean, cov, full_output_cov, num_samples=num_samples
205             )  # [..., (S), N, P]
206         return samples  # [..., (S), N, P]
207 
208     def predict_y(
209         self, Xnew: InputData, full_cov: bool = False, full_output_cov: bool = False
210     ) -> MeanAndVariance:
211         """
212         Compute the mean and variance of the held-out data at the input points.
213         """
214         f_mean, f_var = self.predict_f(Xnew, full_cov=full_cov, full_output_cov=full_output_cov)
215         return self.likelihood.predict_mean_and_var(f_mean, f_var)
216 
217     def predict_log_density(
218         self, data: RegressionData, full_cov: bool = False, full_output_cov: bool = False
219     ) -> tf.Tensor:
220         """
221         Compute the log density of the data at the new data points.
222         """
223         X, Y = data
224         f_mean, f_var = self.predict_f(X, full_cov=full_cov, full_output_cov=full_output_cov)
225         return self.likelihood.predict_log_density(f_mean, f_var, Y)
226
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gpflow/models/model.py b/gpflow/models/model.py
--- a/gpflow/models/model.py
+++ b/gpflow/models/model.py
@@ -211,6 +211,12 @@
         """
         Compute the mean and variance of the held-out data at the input points.
         """
+        if full_cov or full_output_cov:
+            # See https://github.com/GPflow/GPflow/issues/1461
+            raise NotImplementedError(
+                "The predict_y method currently supports only the argument values full_cov=False and full_output_cov=False"
+            )
+
         f_mean, f_var = self.predict_f(Xnew, full_cov=full_cov, full_output_cov=full_output_cov)
         return self.likelihood.predict_mean_and_var(f_mean, f_var)
 
@@ -220,6 +226,12 @@
         """
         Compute the log density of the data at the new data points.
         """
+        if full_cov or full_output_cov:
+            # See https://github.com/GPflow/GPflow/issues/1461
+            raise NotImplementedError(
+                "The predict_log_density method currently supports only the argument values full_cov=False and full_output_cov=False"
+            )
+
         X, Y = data
         f_mean, f_var = self.predict_f(X, full_cov=full_cov, full_output_cov=full_output_cov)
         return self.likelihood.predict_log_density(f_mean, f_var, Y)

{"golden_diff": "diff --git a/gpflow/models/model.py b/gpflow/models/model.py\n--- a/gpflow/models/model.py\n+++ b/gpflow/models/model.py\n@@ -211,6 +211,12 @@\n         \"\"\"\n         Compute the mean and variance of the held-out data at the input points.\n         \"\"\"\n+        if full_cov or full_output_cov:\n+            # See https://github.com/GPflow/GPflow/issues/1461\n+            raise NotImplementedError(\n+                \"The predict_y method currently supports only the argument values full_cov=False and full_output_cov=False\"\n+            )\n+\n         f_mean, f_var = self.predict_f(Xnew, full_cov=full_cov, full_output_cov=full_output_cov)\n         return self.likelihood.predict_mean_and_var(f_mean, f_var)\n \n@@ -220,6 +226,12 @@\n         \"\"\"\n         Compute the log density of the data at the new data points.\n         \"\"\"\n+        if full_cov or full_output_cov:\n+            # See https://github.com/GPflow/GPflow/issues/1461\n+            raise NotImplementedError(\n+                \"The predict_log_density method currently supports only the argument values full_cov=False and full_output_cov=False\"\n+            )\n+\n         X, Y = data\n         f_mean, f_var = self.predict_f(X, full_cov=full_cov, full_output_cov=full_output_cov)\n         return self.likelihood.predict_log_density(f_mean, f_var, Y)\n", "issue": "Bug: Predictions with full_cov=True\nThe functions `model.predict_y()` and `model.predict_density()` do not work nicely with `full_cov=True`. For example, take `GPR`, where the `Gaussian` likelihood is used. In `predict_y()` the likelihood function `predict_mean_and_var()` is called. Here a scalar value of the likelihood variance is added to the covariance matrix. For `full_cov=True`, this should be added only to the diagonal.\r\n\r\nI'm not sure what the right resolution for this is. In the short term, maybe throwing an error for `full_cov=True` would be helpful to show that this is unsupported.\nBug: Predictions with full_cov=True\nThe functions `model.predict_y()` and `model.predict_density()` do not work nicely with `full_cov=True`. For example, take `GPR`, where the `Gaussian` likelihood is used. In `predict_y()` the likelihood function `predict_mean_and_var()` is called. Here a scalar value of the likelihood variance is added to the covariance matrix. For `full_cov=True`, this should be added only to the diagonal.\r\n\r\nI'm not sure what the right resolution for this is. In the short term, maybe throwing an error for `full_cov=True` would be helpful to show that this is unsupported.\n", "before_files": [{"content": "# Copyright 2016-2020 The GPflow Contributors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport abc\nfrom typing import Optional, Tuple\n\nimport tensorflow as tf\n\nfrom ..base import Module\nfrom ..conditionals.util import sample_mvn\nfrom ..kernels import Kernel, MultioutputKernel\nfrom ..likelihoods import Likelihood, SwitchedLikelihood\nfrom ..mean_functions import MeanFunction, Zero\nfrom ..utilities import to_default_float\nfrom .training_mixins import InputData, RegressionData\n\nMeanAndVariance = Tuple[tf.Tensor, tf.Tensor]\n\n\nclass BayesianModel(Module, metaclass=abc.ABCMeta):\n    \"\"\" Bayesian model. \"\"\"\n\n    def log_prior_density(self) -> tf.Tensor:\n        \"\"\"\n        Sum of the log prior probability densities of all (constrained) variables in this model.\n        \"\"\"\n        if self.trainable_parameters:\n            return tf.add_n([p.log_prior_density() for p in self.trainable_parameters])\n        else:\n            return to_default_float(0.0)\n\n    def log_posterior_density(self, *args, **kwargs) -> tf.Tensor:\n        \"\"\"\n        This may be the posterior with respect to the hyperparameters (e.g. for\n        GPR) or the posterior with respect to the function (e.g. for GPMC and\n        SGPMC). It assumes that maximum_log_likelihood_objective() is defined\n        sensibly.\n        \"\"\"\n        return self.maximum_log_likelihood_objective(*args, **kwargs) + self.log_prior_density()\n\n    def _training_loss(self, *args, **kwargs) -> tf.Tensor:\n        \"\"\"\n        Training loss definition. To allow MAP (maximum a-posteriori) estimation,\n        adds the log density of all priors to maximum_log_likelihood_objective().\n        \"\"\"\n        return -(self.maximum_log_likelihood_objective(*args, **kwargs) + self.log_prior_density())\n\n    @abc.abstractmethod\n    def maximum_log_likelihood_objective(self, *args, **kwargs) -> tf.Tensor:\n        \"\"\"\n        Objective for maximum likelihood estimation. Should be maximized. E.g.\n        log-marginal likelihood (hyperparameter likelihood) for GPR, or lower\n        bound to the log-marginal likelihood (ELBO) for sparse and variational\n        GPs.\n        \"\"\"\n        raise NotImplementedError\n\n\nclass GPModel(BayesianModel):\n    r\"\"\"\n    A stateless base class for Gaussian process models, that is, those of the\n    form\n\n    .. math::\n       :nowrap:\n\n       \\begin{align}\n           \\theta        & \\sim p(\\theta) \\\\\n           f             & \\sim \\mathcal{GP}(m(x), k(x, x'; \\theta)) \\\\\n           f_i           & = f(x_i) \\\\\n           y_i \\,|\\, f_i & \\sim p(y_i|f_i)\n       \\end{align}\n\n    This class mostly adds functionality for predictions. To use it, inheriting\n    classes must define a predict_f function, which computes the means and\n    variances of the latent function.\n\n    These predictions are then pushed through the likelihood to obtain means\n    and variances of held out data, self.predict_y.\n\n    The predictions can also be used to compute the (log) density of held-out\n    data via self.predict_log_density.\n\n    It is also possible to draw samples from the latent GPs using\n    self.predict_f_samples.\n    \"\"\"\n\n    def __init__(\n        self,\n        kernel: Kernel,\n        likelihood: Likelihood,\n        mean_function: Optional[MeanFunction] = None,\n        num_latent_gps: int = None,\n    ):\n        super().__init__()\n        assert num_latent_gps is not None, \"GPModel requires specification of num_latent_gps\"\n        self.num_latent_gps = num_latent_gps\n        if mean_function is None:\n            mean_function = Zero()\n        self.mean_function = mean_function\n        self.kernel = kernel\n        self.likelihood = likelihood\n\n    @staticmethod\n    def calc_num_latent_gps_from_data(data, kernel: Kernel, likelihood: Likelihood) -> int:\n        \"\"\"\n        Calculates the number of latent GPs required based on the data as well\n        as the type of kernel and likelihood.\n        \"\"\"\n        _, Y = data\n        output_dim = Y.shape[-1]\n        return GPModel.calc_num_latent_gps(kernel, likelihood, output_dim)\n\n    @staticmethod\n    def calc_num_latent_gps(kernel: Kernel, likelihood: Likelihood, output_dim: int) -> int:\n        \"\"\"\n        Calculates the number of latent GPs required given the number of\n        outputs `output_dim` and the type of likelihood and kernel.\n\n        Note: It's not nice for `GPModel` to need to be aware of specific\n        likelihoods as here. However, `num_latent_gps` is a bit more broken in\n        general, we should fix this in the future. There are also some slightly\n        problematic assumptions re the output dimensions of mean_function.\n        See https://github.com/GPflow/GPflow/issues/1343\n        \"\"\"\n        if isinstance(kernel, MultioutputKernel):\n            # MultioutputKernels already have num_latent_gps attributes\n            num_latent_gps = kernel.num_latent_gps\n        elif isinstance(likelihood, SwitchedLikelihood):\n            # the SwitchedLikelihood partitions/stitches based on the last\n            # column in Y, but we should not add a separate latent GP for this!\n            # hence decrement by 1\n            num_latent_gps = output_dim - 1\n            assert num_latent_gps > 0\n        else:\n            num_latent_gps = output_dim\n\n        return num_latent_gps\n\n    @abc.abstractmethod\n    def predict_f(\n        self, Xnew: InputData, full_cov: bool = False, full_output_cov: bool = False\n    ) -> MeanAndVariance:\n        raise NotImplementedError\n\n    def predict_f_samples(\n        self,\n        Xnew: InputData,\n        num_samples: Optional[int] = None,\n        full_cov: bool = True,\n        full_output_cov: bool = False,\n    ) -> tf.Tensor:\n        \"\"\"\n        Produce samples from the posterior latent function(s) at the input points.\n\n        :param Xnew: InputData\n            Input locations at which to draw samples, shape [..., N, D]\n            where N is the number of rows and D is the input dimension of each point.\n        :param num_samples:\n            Number of samples to draw.\n            If `None`, a single sample is drawn and the return shape is [..., N, P],\n            for any positive integer the return shape contains an extra batch\n            dimension, [..., S, N, P], with S = num_samples and P is the number of outputs.\n        :param full_cov:\n            If True, draw correlated samples over the inputs. Computes the Cholesky over the\n            dense covariance matrix of size [num_data, num_data].\n            If False, draw samples that are uncorrelated over the inputs.\n        :param full_output_cov:\n            If True, draw correlated samples over the outputs.\n            If False, draw samples that are uncorrelated over the outputs.\n\n        Currently, the method does not support `full_output_cov=True` and `full_cov=True`.\n        \"\"\"\n        if full_cov and full_output_cov:\n            raise NotImplementedError(\n                \"The combination of both `full_cov` and `full_output_cov` is not supported.\"\n            )\n\n        # check below for shape info\n        mean, cov = self.predict_f(Xnew, full_cov=full_cov, full_output_cov=full_output_cov)\n        if full_cov:\n            # mean: [..., N, P]\n            # cov: [..., P, N, N]\n            mean_for_sample = tf.linalg.adjoint(mean)  # [..., P, N]\n            samples = sample_mvn(\n                mean_for_sample, cov, full_cov, num_samples=num_samples\n            )  # [..., (S), P, N]\n            samples = tf.linalg.adjoint(samples)  # [..., (S), N, P]\n        else:\n            # mean: [..., N, P]\n            # cov: [..., N, P] or [..., N, P, P]\n            samples = sample_mvn(\n                mean, cov, full_output_cov, num_samples=num_samples\n            )  # [..., (S), N, P]\n        return samples  # [..., (S), N, P]\n\n    def predict_y(\n        self, Xnew: InputData, full_cov: bool = False, full_output_cov: bool = False\n    ) -> MeanAndVariance:\n        \"\"\"\n        Compute the mean and variance of the held-out data at the input points.\n        \"\"\"\n        f_mean, f_var = self.predict_f(Xnew, full_cov=full_cov, full_output_cov=full_output_cov)\n        return self.likelihood.predict_mean_and_var(f_mean, f_var)\n\n    def predict_log_density(\n        self, data: RegressionData, full_cov: bool = False, full_output_cov: bool = False\n    ) -> tf.Tensor:\n        \"\"\"\n        Compute the log density of the data at the new data points.\n        \"\"\"\n        X, Y = data\n        f_mean, f_var = self.predict_f(X, full_cov=full_cov, full_output_cov=full_output_cov)\n        return self.likelihood.predict_log_density(f_mean, f_var, Y)\n", "path": "gpflow/models/model.py"}], "after_files": [{"content": "# Copyright 2016-2020 The GPflow Contributors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport abc\nfrom typing import Optional, Tuple\n\nimport tensorflow as tf\n\nfrom ..base import Module\nfrom ..conditionals.util import sample_mvn\nfrom ..kernels import Kernel, MultioutputKernel\nfrom ..likelihoods import Likelihood, SwitchedLikelihood\nfrom ..mean_functions import MeanFunction, Zero\nfrom ..utilities import to_default_float\nfrom .training_mixins import InputData, RegressionData\n\nMeanAndVariance = Tuple[tf.Tensor, tf.Tensor]\n\n\nclass BayesianModel(Module, metaclass=abc.ABCMeta):\n    \"\"\" Bayesian model. \"\"\"\n\n    def log_prior_density(self) -> tf.Tensor:\n        \"\"\"\n        Sum of the log prior probability densities of all (constrained) variables in this model.\n        \"\"\"\n        if self.trainable_parameters:\n            return tf.add_n([p.log_prior_density() for p in self.trainable_parameters])\n        else:\n            return to_default_float(0.0)\n\n    def log_posterior_density(self, *args, **kwargs) -> tf.Tensor:\n        \"\"\"\n        This may be the posterior with respect to the hyperparameters (e.g. for\n        GPR) or the posterior with respect to the function (e.g. for GPMC and\n        SGPMC). It assumes that maximum_log_likelihood_objective() is defined\n        sensibly.\n        \"\"\"\n        return self.maximum_log_likelihood_objective(*args, **kwargs) + self.log_prior_density()\n\n    def _training_loss(self, *args, **kwargs) -> tf.Tensor:\n        \"\"\"\n        Training loss definition. To allow MAP (maximum a-posteriori) estimation,\n        adds the log density of all priors to maximum_log_likelihood_objective().\n        \"\"\"\n        return -(self.maximum_log_likelihood_objective(*args, **kwargs) + self.log_prior_density())\n\n    @abc.abstractmethod\n    def maximum_log_likelihood_objective(self, *args, **kwargs) -> tf.Tensor:\n        \"\"\"\n        Objective for maximum likelihood estimation. Should be maximized. E.g.\n        log-marginal likelihood (hyperparameter likelihood) for GPR, or lower\n        bound to the log-marginal likelihood (ELBO) for sparse and variational\n        GPs.\n        \"\"\"\n        raise NotImplementedError\n\n\nclass GPModel(BayesianModel):\n    r\"\"\"\n    A stateless base class for Gaussian process models, that is, those of the\n    form\n\n    .. math::\n       :nowrap:\n\n       \\begin{align}\n           \\theta        & \\sim p(\\theta) \\\\\n           f             & \\sim \\mathcal{GP}(m(x), k(x, x'; \\theta)) \\\\\n           f_i           & = f(x_i) \\\\\n           y_i \\,|\\, f_i & \\sim p(y_i|f_i)\n       \\end{align}\n\n    This class mostly adds functionality for predictions. To use it, inheriting\n    classes must define a predict_f function, which computes the means and\n    variances of the latent function.\n\n    These predictions are then pushed through the likelihood to obtain means\n    and variances of held out data, self.predict_y.\n\n    The predictions can also be used to compute the (log) density of held-out\n    data via self.predict_log_density.\n\n    It is also possible to draw samples from the latent GPs using\n    self.predict_f_samples.\n    \"\"\"\n\n    def __init__(\n        self,\n        kernel: Kernel,\n        likelihood: Likelihood,\n        mean_function: Optional[MeanFunction] = None,\n        num_latent_gps: int = None,\n    ):\n        super().__init__()\n        assert num_latent_gps is not None, \"GPModel requires specification of num_latent_gps\"\n        self.num_latent_gps = num_latent_gps\n        if mean_function is None:\n            mean_function = Zero()\n        self.mean_function = mean_function\n        self.kernel = kernel\n        self.likelihood = likelihood\n\n    @staticmethod\n    def calc_num_latent_gps_from_data(data, kernel: Kernel, likelihood: Likelihood) -> int:\n        \"\"\"\n        Calculates the number of latent GPs required based on the data as well\n        as the type of kernel and likelihood.\n        \"\"\"\n        _, Y = data\n        output_dim = Y.shape[-1]\n        return GPModel.calc_num_latent_gps(kernel, likelihood, output_dim)\n\n    @staticmethod\n    def calc_num_latent_gps(kernel: Kernel, likelihood: Likelihood, output_dim: int) -> int:\n        \"\"\"\n        Calculates the number of latent GPs required given the number of\n        outputs `output_dim` and the type of likelihood and kernel.\n\n        Note: It's not nice for `GPModel` to need to be aware of specific\n        likelihoods as here. However, `num_latent_gps` is a bit more broken in\n        general, we should fix this in the future. There are also some slightly\n        problematic assumptions re the output dimensions of mean_function.\n        See https://github.com/GPflow/GPflow/issues/1343\n        \"\"\"\n        if isinstance(kernel, MultioutputKernel):\n            # MultioutputKernels already have num_latent_gps attributes\n            num_latent_gps = kernel.num_latent_gps\n        elif isinstance(likelihood, SwitchedLikelihood):\n            # the SwitchedLikelihood partitions/stitches based on the last\n            # column in Y, but we should not add a separate latent GP for this!\n            # hence decrement by 1\n            num_latent_gps = output_dim - 1\n            assert num_latent_gps > 0\n        else:\n            num_latent_gps = output_dim\n\n        return num_latent_gps\n\n    @abc.abstractmethod\n    def predict_f(\n        self, Xnew: InputData, full_cov: bool = False, full_output_cov: bool = False\n    ) -> MeanAndVariance:\n        raise NotImplementedError\n\n    def predict_f_samples(\n        self,\n        Xnew: InputData,\n        num_samples: Optional[int] = None,\n        full_cov: bool = True,\n        full_output_cov: bool = False,\n    ) -> tf.Tensor:\n        \"\"\"\n        Produce samples from the posterior latent function(s) at the input points.\n\n        :param Xnew: InputData\n            Input locations at which to draw samples, shape [..., N, D]\n            where N is the number of rows and D is the input dimension of each point.\n        :param num_samples:\n            Number of samples to draw.\n            If `None`, a single sample is drawn and the return shape is [..., N, P],\n            for any positive integer the return shape contains an extra batch\n            dimension, [..., S, N, P], with S = num_samples and P is the number of outputs.\n        :param full_cov:\n            If True, draw correlated samples over the inputs. Computes the Cholesky over the\n            dense covariance matrix of size [num_data, num_data].\n            If False, draw samples that are uncorrelated over the inputs.\n        :param full_output_cov:\n            If True, draw correlated samples over the outputs.\n            If False, draw samples that are uncorrelated over the outputs.\n\n        Currently, the method does not support `full_output_cov=True` and `full_cov=True`.\n        \"\"\"\n        if full_cov and full_output_cov:\n            raise NotImplementedError(\n                \"The combination of both `full_cov` and `full_output_cov` is not supported.\"\n            )\n\n        # check below for shape info\n        mean, cov = self.predict_f(Xnew, full_cov=full_cov, full_output_cov=full_output_cov)\n        if full_cov:\n            # mean: [..., N, P]\n            # cov: [..., P, N, N]\n            mean_for_sample = tf.linalg.adjoint(mean)  # [..., P, N]\n            samples = sample_mvn(\n                mean_for_sample, cov, full_cov, num_samples=num_samples\n            )  # [..., (S), P, N]\n            samples = tf.linalg.adjoint(samples)  # [..., (S), N, P]\n        else:\n            # mean: [..., N, P]\n            # cov: [..., N, P] or [..., N, P, P]\n            samples = sample_mvn(\n                mean, cov, full_output_cov, num_samples=num_samples\n            )  # [..., (S), N, P]\n        return samples  # [..., (S), N, P]\n\n    def predict_y(\n        self, Xnew: InputData, full_cov: bool = False, full_output_cov: bool = False\n    ) -> MeanAndVariance:\n        \"\"\"\n        Compute the mean and variance of the held-out data at the input points.\n        \"\"\"\n        if full_cov or full_output_cov:\n            # See https://github.com/GPflow/GPflow/issues/1461\n            raise NotImplementedError(\n                \"The predict_y method currently supports only the argument values full_cov=False and full_output_cov=False\"\n            )\n\n        f_mean, f_var = self.predict_f(Xnew, full_cov=full_cov, full_output_cov=full_output_cov)\n        return self.likelihood.predict_mean_and_var(f_mean, f_var)\n\n    def predict_log_density(\n        self, data: RegressionData, full_cov: bool = False, full_output_cov: bool = False\n    ) -> tf.Tensor:\n        \"\"\"\n        Compute the log density of the data at the new data points.\n        \"\"\"\n        if full_cov or full_output_cov:\n            # See https://github.com/GPflow/GPflow/issues/1461\n            raise NotImplementedError(\n                \"The predict_log_density method currently supports only the argument values full_cov=False and full_output_cov=False\"\n            )\n\n        X, Y = data\n        f_mean, f_var = self.predict_f(X, full_cov=full_cov, full_output_cov=full_output_cov)\n        return self.likelihood.predict_log_density(f_mean, f_var, Y)\n", "path": "gpflow/models/model.py"}]}