Repository: scipy/scipy

Issue:
SciPy 1.9 exception in LSQSphereBivariateSpline
The issue is an exception in `SciPy` 1.9.0 which did not occur in earlier versions of `SciPy` ...

```
File "~/PyGeodesy/pygeodesy/heights.py", line ...
    self._ev = scipy.interpolate.LSQSphereBivariateSpline(..., eps=EPS, w=w).ev
 File "~/Library/Python/3.8/lib/python/site-packages/scipy/interpolate/fitpack2.py", line 1549, in __init__
    if w is not None and not (w >= 0.0).all():
TypeError: '>=' not supported between instances of 'list' and 'float'
```
Variable `w` is a list of `float`s ...
 ```
w = [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0]
```
The exception is due to ...
```
(w >= 0.0).all()
```



Discussion:
@mrJean1 thanks for reporting the issue.

It looks like the problem was introduced in https://github.com/scipy/scipy/pull/11736, where additional input validation was added.  The code in `LSQSphereBivariateSpline.__init__()` needs to wrap the inputs with `np.asarray` (e.g. `theta = np.asarray(theta)`) before an expression such as `(0.0 <= theta).all()` is evaluated.
It is a regression test in the [PyGeodesy](https://github.com/mrJean1/PyGeodesy) package upgraded to SciPy 1.9.  
I'm sorry it looks like that my PR #11736 has problems. I will try to fix it. 

Resolving patch:
diff --git a/scipy/interpolate/fitpack2.py b/scipy/interpolate/fitpack2.py
--- a/scipy/interpolate/fitpack2.py
+++ b/scipy/interpolate/fitpack2.py
@@ -1424,13 +1424,17 @@ class SmoothSphereBivariateSpline(SphereBivariateSpline):
 
     def __init__(self, theta, phi, r, w=None, s=0., eps=1E-16):
 
+        theta, phi, r = np.asarray(theta), np.asarray(phi), np.asarray(r)
+
         # input validation
         if not ((0.0 <= theta).all() and (theta <= np.pi).all()):
             raise ValueError('theta should be between [0, pi]')
         if not ((0.0 <= phi).all() and (phi <= 2.0 * np.pi).all()):
             raise ValueError('phi should be between [0, 2pi]')
-        if w is not None and not (w >= 0.0).all():
-            raise ValueError('w should be positive')
+        if w is not None:
+            w = np.asarray(w)
+            if not (w >= 0.0).all():
+                raise ValueError('w should be positive')
         if not s >= 0.0:
             raise ValueError('s should be positive')
         if not 0.0 < eps < 1.0:
@@ -1538,6 +1542,9 @@ class LSQSphereBivariateSpline(SphereBivariateSpline):
 
     def __init__(self, theta, phi, r, tt, tp, w=None, eps=1E-16):
 
+        theta, phi, r = np.asarray(theta), np.asarray(phi), np.asarray(r)
+        tt, tp = np.asarray(tt), np.asarray(tp)
+
         if not ((0.0 <= theta).all() and (theta <= np.pi).all()):
             raise ValueError('theta should be between [0, pi]')
         if not ((0.0 <= phi).all() and (phi <= 2*np.pi).all()):
@@ -1546,8 +1553,10 @@ def __init__(self, theta, phi, r, tt, tp, w=None, eps=1E-16):
             raise ValueError('tt should be between (0, pi)')
         if not ((0.0 < tp).all() and (tp < 2*np.pi).all()):
             raise ValueError('tp should be between (0, 2pi)')
-        if w is not None and not (w >= 0.0).all():
-            raise ValueError('w should be positive')
+        if w is not None:
+            w = np.asarray(w)
+            if not (w >= 0.0).all():
+                raise ValueError('w should be positive')
         if not 0.0 < eps < 1.0:
             raise ValueError('eps should be between (0, 1)')
 
@@ -1763,6 +1772,7 @@ def __init__(self, u, v, r, s=0., pole_continuity=False, pole_values=None,
         ider[1], ider[3] = pole_flat
 
         u, v = np.ravel(u), np.ravel(v)
+        r = np.asarray(r)
 
         if not ((0.0 <= u).all() and (u <= np.pi).all()):
             raise ValueError('u should be between [0, pi]')


Test patch:
diff --git a/scipy/interpolate/tests/test_fitpack2.py b/scipy/interpolate/tests/test_fitpack2.py
--- a/scipy/interpolate/tests/test_fitpack2.py
+++ b/scipy/interpolate/tests/test_fitpack2.py
@@ -309,6 +309,21 @@ def test_invalid_input_for_lsq_univariate_spline(self):
             LSQUnivariateSpline(x_values, y_values, t_values, k=6)
         assert "k should be 1 <= k <= 5" in str(info.value)
 
+    def test_array_like_input(self):
+        x_values = np.array([1, 2, 4, 6, 8.5])
+        y_values = np.array([0.5, 0.8, 1.3, 2.5, 2.8])
+        w_values = np.array([1.0, 1.0, 1.0, 1.0, 1.0])
+        bbox = np.array([-100, 100])
+        # np.array input
+        spl1 = UnivariateSpline(x=x_values, y=y_values, w=w_values,
+                                bbox=bbox)
+        # list input
+        spl2 = UnivariateSpline(x=x_values.tolist(), y=y_values.tolist(),
+                                w=w_values.tolist(), bbox=bbox.tolist())
+
+        assert_allclose(spl1([0.1, 0.5, 0.9, 0.99]),
+                        spl2([0.1, 0.5, 0.9, 0.99]))
+
 
 class TestLSQBivariateSpline(object):
     # NOTE: The systems in this test class are rank-deficient
@@ -430,6 +445,27 @@ def test_invalid_input(self):
             LSQBivariateSpline(x, y, z, tx, ty, eps=1.0)
         assert "eps should be between (0, 1)" in str(exc_info.value)
 
+    def test_array_like_input(self):
+        s = 0.1
+        tx = np.array([1 + s, 3 - s])
+        ty = np.array([1 + s, 3 - s])
+        x = np.linspace(1.0, 10.0)
+        y = np.linspace(1.0, 10.0)
+        z = np.linspace(1.0, 10.0)
+        w = np.linspace(1.0, 10.0)
+        bbox = np.array([1.0, 10.0, 1.0, 10.0])
+
+        with suppress_warnings() as sup:
+            r = sup.record(UserWarning, "\nThe coefficients of the spline")
+            # np.array input
+            spl1 = LSQBivariateSpline(x, y, z, tx, ty, w=w, bbox=bbox)
+            # list input
+            spl2 = LSQBivariateSpline(x.tolist(), y.tolist(), z.tolist(),
+                                      tx.tolist(), ty.tolist(), w=w.tolist(),
+                                      bbox=bbox)
+            assert_allclose(spl1(2.0, 2.0), spl2(2.0, 2.0))
+            assert_equal(len(r), 2)
+
 
 class TestSmoothBivariateSpline(object):
     def test_linear_constant(self):
@@ -538,6 +574,20 @@ def test_invalid_input(self):
             SmoothBivariateSpline(x, y, z, eps=1.0)
         assert "eps should be between (0, 1)" in str(exc_info.value)
 
+    def test_array_like_input(self):
+        x = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
+        y = np.array([1, 2, 3, 1, 2, 3, 1, 2, 3])
+        z = np.array([3, 3, 3, 3, 3, 3, 3, 3, 3])
+        w = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1])
+        bbox = np.array([1.0, 3.0, 1.0, 3.0])
+        # np.array input
+        spl1 = SmoothBivariateSpline(x, y, z, w=w, bbox=bbox, kx=1, ky=1)
+        # list input
+        spl2 = SmoothBivariateSpline(x.tolist(), y.tolist(), z.tolist(),
+                                     bbox=bbox.tolist(), w=w.tolist(),
+                                     kx=1, ky=1)
+        assert_allclose(spl1(0.1, 0.5), spl2(0.1, 0.5))
+
 
 class TestLSQSphereBivariateSpline(object):
     def setup_method(self):
@@ -651,6 +701,30 @@ def test_invalid_input(self):
                                      knotst, knotsp, eps=1.0)
         assert "eps should be between (0, 1)" in str(exc_info.value)
 
+    def test_array_like_input(self):
+        ntheta, nphi = 70, 90
+        theta = linspace(0.5 / (ntheta - 1), 1 - 0.5 / (ntheta - 1),
+                         ntheta) * pi
+        phi = linspace(0.5 / (nphi - 1), 1 - 0.5 / (nphi - 1),
+                       nphi) * 2. * pi
+        lats, lons = meshgrid(theta, phi)
+        data = ones((theta.shape[0], phi.shape[0]))
+        # define knots and extract data values at the knots
+        knotst = theta[::5]
+        knotsp = phi[::5]
+        w = ones((lats.ravel().shape[0]))
+
+        # np.array input
+        spl1 = LSQSphereBivariateSpline(lats.ravel(), lons.ravel(),
+                                        data.T.ravel(), knotst, knotsp, w=w)
+        # list input
+        spl2 = LSQSphereBivariateSpline(lats.ravel().tolist(),
+                                        lons.ravel().tolist(),
+                                        data.T.ravel().tolist(),
+                                        knotst.tolist(),
+                                        knotsp.tolist(), w=w.tolist())
+        assert_array_almost_equal(spl1(1.0, 1.0), spl2(1.0, 1.0))
+
 
 class TestSmoothSphereBivariateSpline(object):
     def setup_method(self):
@@ -720,6 +794,22 @@ def test_invalid_input(self):
             SmoothSphereBivariateSpline(theta, phi, r, eps=1.0)
         assert "eps should be between (0, 1)" in str(exc_info.value)
 
+    def test_array_like_input(self):
+        theta = np.array([.25 * pi, .25 * pi, .25 * pi, .5 * pi, .5 * pi,
+                          .5 * pi, .75 * pi, .75 * pi, .75 * pi])
+        phi = np.array([.5 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi, .5 * pi,
+                        pi, 1.5 * pi])
+        r = np.array([3, 3, 3, 3, 3, 3, 3, 3, 3])
+        w = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0])
+
+        # np.array input
+        spl1 = SmoothSphereBivariateSpline(theta, phi, r, w=w, s=1E10)
+
+        # list input
+        spl2 = SmoothSphereBivariateSpline(theta.tolist(), phi.tolist(),
+                                           r.tolist(), w=w.tolist(), s=1E10)
+        assert_array_almost_equal(spl1(1.0, 1.0), spl2(1.0, 1.0))
+
 
 class TestRectBivariateSpline(object):
     def test_defaults(self):
@@ -825,6 +915,18 @@ def test_invalid_input(self):
             RectBivariateSpline(x, y, z, s=-1.0)
         assert "s should be s >= 0.0" in str(info.value)
 
+    def test_array_like_input(self):
+        x = array([1, 2, 3, 4, 5])
+        y = array([1, 2, 3, 4, 5])
+        z = array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1],
+                   [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]])
+        bbox = array([1, 5, 1, 5])
+
+        spl1 = RectBivariateSpline(x, y, z, bbox=bbox)
+        spl2 = RectBivariateSpline(x.tolist(), y.tolist(), z.tolist(),
+                                   bbox=bbox.tolist())
+        assert_array_almost_equal(spl1(1.0, 1.0), spl2(1.0, 1.0))
+
 
 class TestRectSphereBivariateSpline(object):
     def test_defaults(self):
@@ -925,6 +1027,20 @@ def test_invalid_input(self):
             RectSphereBivariateSpline(lats, lons, data, s=-1)
         assert "s should be positive" in str(exc_info.value)
 
+    def test_array_like_input(self):
+        y = linspace(0.01, 2 * pi - 0.01, 7)
+        x = linspace(0.01, pi - 0.01, 7)
+        z = array([[1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1],
+                   [1, 2, 3, 2, 1, 2, 1],
+                   [1, 2, 2, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1],
+                   [1, 2, 2, 2, 1, 2, 1],
+                   [1, 2, 1, 2, 1, 2, 1]])
+        # np.array input
+        spl1 = RectSphereBivariateSpline(x, y, z)
+        # list input
+        spl2 = RectSphereBivariateSpline(x.tolist(), y.tolist(), z.tolist())
+        assert_array_almost_equal(spl1(x, y), spl2(x, y))
+
 
 def _numdiff_2d(func, x, y, dx=0, dy=0, eps=1e-8):
     if dx == 0 and dy == 0: