Repository: GPflow/GPflow (python)

Issue:
Set a prior over a linear mean function
<!-- Lines like this are comments and will be invisible -->

Bug: I set a Gaussian prior over a linear mean function and when a I see the monitor, gpflow only assigns a prior over the b parameter, and no for the A parameter.

## To reproduce

**Minimal, reproducible example**
I just put the code for that part of the model, for simplicity:
```python
import gpflow
import numpy as np
import tensorflow_probability as tfp
import tensorflow as tf

#trivial example, from [here](https://gpflow.github.io/GPflow/2.5.2/notebooks/advanced/mcmc.html) 
rng = np.random.RandomState(14)
X = np.linspace(-3, 3, 20).reshape(1,20)
y = rng.exponential(np.sin(X) ** 2)

A = gpflow.Parameter(np.zeros(X.shape[1]),dtype=np.float64)
A.prior=tfp.distributions.Normal((np.zeros(X.shape[1])),np.ones(X.shape[1]))
b = gpflow.Parameter(np.zeros((1,)),dtype=np.float64)
b.prior=tfp.distributions.Normal(np.zeros((1,)),np.ones((1,)))
mf = gpflow.functions.Linear(A,b)

k = gpflow.kernels.SquaredExponential()
k.lengthscales.prior = tfp.distributions.Normal(np.zeros(X.shape[1]),np.ones(X.shape[1]))
k.variance.prior = tfp.distributions.Normal(np.zeros(1),np.ones(1))

lik = gpflow.likelihoods.Gaussian()
lik.variance.prior = tfp.distributions.Normal(np.zeros(1),np.ones(1))

model = gpflow.models.GPMC((X, y), kernel=k, mean_function=mf, likelihood=lik)
model
```
So, there is no warning, but, as can be seen from the monitor, gpflow doesn't put the prior over A:

![imagen](https://github.com/GPflow/GPflow/assets/112404457/1fb5d981-0e00-4e07-8a5a-e68c8735fec1)


System information

* GPflow version: 2.9.0
* GPflow installed from: "pip install gpflow"
* TensorFlow version: 2.10.1
* Python version: 3.10.9 | packaged by conda-forge | (main, Jan 11 2023, 15:15:40) [MSC v.1916 64 bit (AMD64)]
* Operating system: win11/Ubuntu 18.046 LTS (I probed it in both os)

I guess the setting is correct, but I don't understand why gpflow set the prior over b and not over A.




Pull request description:
fix bug of issue #2091 by type checking A
<!-- (Lines like this are comments and will be invisible - you can leave them as is, or remove them) -->

<!-- Thank you very much for spending time on contributing to GPflow!
This template exists to simplify communicating basic information that is required to understand your contribution.
Please fill it in as far as possible; if anything about this template is unclear, please do mention it! -->

**PR type:** bugfix

**Related issue(s)/PRs:** resolves #2091

## Summary

**Proposed changes**
<!-- Large PRs should ideally be preceded by a design discussion on a separate issue! -->

<!-- A clear and concise description of the contents of this pull request. -->
* type check A
* throws error if the Parameter does not have the correct shape


**What alternatives have you considered?**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->

### Minimal working example

* refer to https://github.com/GPflow/GPflow/issues/2091

### Release notes
<!-- leave blank if unsure -->

**Fully backwards compatible:** yes

## PR checklist
<!-- tick off [X] as applicable -->
- [x] The bug case / new feature is covered by unit tests
- [x] Code has type annotations
- [x] Build checks
  - [x] I ran the black+isort formatter (`make format`)
  - [x] I locally tested that the tests pass (`make check-all`)
- [x] Release management
  - [x] RELEASE.md updated with entry for this change
  - [x] New contributors: I've added myself to CONTRIBUTORS.md



Patch:
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 673a72ca..18572a42 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -89,5 +89,6 @@ Because GitHub's [graph of contributors](http://github.com/GPflow/GPflow/graphs/
 [@khurram-ghani](https://github.com/khurram-ghani)
 [@partev](https://github.com/partev)
 [@uri-granta](https://github.com/uri-granta)
+[@jschmiegel](https://github.com/jschmiegel)
 
 Add yourself when you first contribute to GPflow's code, tests, or documentation!
diff --git a/RELEASE.md b/RELEASE.md
index 98c75e23..2f19f265 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -56,6 +56,7 @@ This release contains contributions from:
 ## Bug Fixes and Other Changes
 
 * Don't round small values in kernel summary printout
+* allowing to set a prior for A in the Linear() mean function
 
 ## Thanks to our Contributors
 
diff --git a/gpflow/functions.py b/gpflow/functions.py
index 02cf05b3..209817a4 100644
--- a/gpflow/functions.py
+++ b/gpflow/functions.py
@@ -110,7 +110,15 @@ class Linear(MeanFunction, Function):
         MeanFunction.__init__(self)
         A = np.ones((1, 1), dtype=default_float()) if A is None else A
         b = np.zeros(1, dtype=default_float()) if b is None else b
-        self.A = Parameter(np.atleast_2d(A))
+        if isinstance(A, Parameter):
+            if len(A._shape) >= 2:
+                self.A = A
+            else:
+                raise ValueError(
+                    "Error 'gpflow.funcitons.Linear()' mean function. A has not the correct shape (at least 2d)."
+                )
+        else:
+            self.A = Parameter(np.atleast_2d(A))
         self.b = Parameter(b)
 
     @inherit_check_shapes


Test patch:
diff --git a/tests/gpflow/test_functions.py b/tests/gpflow/test_functions.py
index 59400fac..911770b8 100644
--- a/tests/gpflow/test_functions.py
+++ b/tests/gpflow/test_functions.py
@@ -16,6 +16,7 @@ from typing import Any, Sequence, Tuple, Type
 
 import numpy as np
 import pytest
+import tensorflow_probability as tfp
 from check_shapes import check_shapes
 from numpy.testing import assert_allclose
 
@@ -371,3 +372,25 @@ def test_models_with_mean_functions_changes(model_class: Type[Any]) -> None:
     assert np.all(var_zero.numpy() == var_non_zero.numpy())
     # predictive mean changes after modifying mean function
     assert not np.all(mu_zero.numpy() == mu_non_zero.numpy())
+
+
+class TestIssue2091EnsureParameterTypeForLinearMeanFunction:
+    """
+    See github issue #2091. These are tests to ensure that the prior is kept if A is already given as Parameter.
+    """
+
+    # test_parameter_with_correct_shape
+    def test_parameter_with_correct_shape(self) -> None:
+        "Check that Linear copies the prior from a correctly shaped A Parameter"
+        A = gpflow.Parameter(
+            np.ones((1, 1)), dtype=np.float64, prior=tfp.distributions.Normal(0.0, 1.0)
+        )
+        linear_function = Linear(A, 1)
+        assert linear_function.A.prior is not None
+
+    # test_parameter_with_incorrect_shape
+    def test_parameter_with_incorrect_shape(self) -> None:
+        "Check that Linear throws an error when the A Parameter is not correctly shaped"
+        A = gpflow.Parameter(np.zeros(20), dtype=np.float64)
+        with pytest.raises(ValueError):
+            Linear(A, 1)