Repository: scipy/scipy

Issue:
DOC: scipy.interpolate.RectSphereBivariateSpline
A couple of doc issues here:

v has to be a "1-D array of latitude coordinates in strictly ascending order. Coordinates must be given in radians and lie within the interval (0, pi)". But actually, a v in this range is a colatitude, not a latitude (latitudes go from -pi/2 to pi/2).

Also, it should be emphasized that (0, pi) really does mean an open interval, and 0 and pi are not allowed for v. If you put them in, the error message is completely cryptic.


Discussion:

Resolving patch:
diff --git a/scipy/interpolate/fitpack2.py b/scipy/interpolate/fitpack2.py
--- a/scipy/interpolate/fitpack2.py
+++ b/scipy/interpolate/fitpack2.py
@@ -86,8 +86,8 @@ class UnivariateSpline(object):
         2-sequence specifying the boundary of the approximation interval. If
         None (default), ``bbox=[x[0], x[-1]]``.
     k : int, optional
-        Degree of the smoothing spline.  Must be <= 5.
-        Default is k=3, a cubic spline.
+        Degree of the smoothing spline.  Must be 1 <= `k` <= 5.
+        Default is `k` = 3, a cubic spline.
     s : float or None, optional
         Positive smoothing factor used to choose the number of knots.  Number
         of knots will be increased until the smoothing condition is satisfied::
@@ -166,6 +166,23 @@ class UnivariateSpline(object):
     def __init__(self, x, y, w=None, bbox=[None]*2, k=3, s=None,
                  ext=0, check_finite=False):
 
+        x, y, w, bbox, self.ext = self.validate_input(x, y, w, bbox, k, s, ext,
+                                                      check_finite)
+
+        # _data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier
+        data = dfitpack.fpcurf0(x, y, k, w=w, xb=bbox[0],
+                                xe=bbox[1], s=s)
+        if data[-1] == 1:
+            # nest too small, setting to maximum bound
+            data = self._reset_nest(data)
+        self._data = data
+        self._reset_class()
+
+    @staticmethod
+    def validate_input(x, y, w, bbox, k, s, ext, check_finite):
+        x, y, bbox = np.asarray(x), np.asarray(y), np.asarray(bbox)
+        if w is not None:
+            w = np.asarray(w)
         if check_finite:
             w_finite = np.isfinite(w).all() if w is not None else True
             if (not np.isfinite(x).all() or not np.isfinite(y).all() or
@@ -178,20 +195,23 @@ def __init__(self, x, y, w=None, bbox=[None]*2, k=3, s=None,
         else:
             if not np.all(diff(x) > 0.0):
                 raise ValueError("x must be strictly increasing if s = 0")
+        if x.size != y.size:
+            raise ValueError("x and y should have a same length")
+        elif w is not None and not x.size == y.size == w.size:
+            raise ValueError("x, y, and w should have a same length")
+        elif bbox.shape != (2,):
+            raise ValueError("bbox shape should be (2,)")
+        elif not (1 <= k <= 5):
+            raise ValueError("k should be 1 <= k <= 5")
+        elif s is not None and not s >= 0.0:
+            raise ValueError("s should be s >= 0.0")
 
-        # _data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier
         try:
-            self.ext = _extrap_modes[ext]
+            ext = _extrap_modes[ext]
         except KeyError:
             raise ValueError("Unknown extrapolation mode %s." % ext)
 
-        data = dfitpack.fpcurf0(x, y, k, w=w, xb=bbox[0],
-                                xe=bbox[1], s=s)
-        if data[-1] == 1:
-            # nest too small, setting to maximum bound
-            data = self._reset_nest(data)
-        self._data = data
-        self._reset_class()
+        return x, y, w, bbox, ext
 
     @classmethod
     def _from_tck(cls, tck, ext=0):
@@ -281,12 +301,12 @@ def __call__(self, x, nu=0, ext=None):
         ----------
         x : array_like
             A 1-D array of points at which to return the value of the smoothed
-            spline or its derivatives. Note: x can be unordered but the
-            evaluation is more efficient if x is (partially) ordered.
+            spline or its derivatives. Note: `x` can be unordered but the
+            evaluation is more efficient if `x` is (partially) ordered.
         nu  : int
             The order of derivative of the spline to compute.
         ext : int
-            Controls the value returned for elements of ``x`` not in the
+            Controls the value returned for elements of `x` not in the
             interval defined by the knot sequence.
 
             * if ext=0 or 'extrapolate', return the extrapolated value.
@@ -302,9 +322,6 @@ def __call__(self, x, nu=0, ext=None):
         # empty input yields empty output
         if x.size == 0:
             return array([])
-#        if nu is None:
-#            return dfitpack.splev(*(self._eval_args+(x,)))
-#        return dfitpack.splder(nu=nu,*(self._eval_args+(x,)))
         if ext is None:
             ext = self.ext
         else:
@@ -597,11 +614,8 @@ class InterpolatedUnivariateSpline(UnivariateSpline):
     def __init__(self, x, y, w=None, bbox=[None]*2, k=3,
                  ext=0, check_finite=False):
 
-        if check_finite:
-            w_finite = np.isfinite(w).all() if w is not None else True
-            if (not np.isfinite(x).all() or not np.isfinite(y).all() or
-                    not w_finite):
-                raise ValueError("Input must not contain NaNs or infs.")
+        x, y, w, bbox, self.ext = self.validate_input(x, y, w, bbox, k, None,
+                                            ext, check_finite)
         if not np.all(diff(x) > 0.0):
             raise ValueError('x must be strictly increasing')
 
@@ -610,11 +624,6 @@ def __init__(self, x, y, w=None, bbox=[None]*2, k=3,
                                       xe=bbox[1], s=0)
         self._reset_class()
 
-        try:
-            self.ext = _extrap_modes[ext]
-        except KeyError:
-            raise ValueError("Unknown extrapolation mode %s." % ext)
-
 
 _fpchec_error_string = """The input parameters have been rejected by fpchec. \
 This means that at least one of the following conditions is violated:
@@ -657,7 +666,7 @@ class LSQUnivariateSpline(UnivariateSpline):
         None (default), ``bbox = [x[0], x[-1]]``.
     k : int, optional
         Degree of the smoothing spline.  Must be 1 <= `k` <= 5.
-        Default is k=3, a cubic spline.
+        Default is `k` = 3, a cubic spline.
     ext : int or str, optional
         Controls the extrapolation mode for elements
         not in the interval defined by the knot sequence.
@@ -736,11 +745,8 @@ class LSQUnivariateSpline(UnivariateSpline):
     def __init__(self, x, y, t, w=None, bbox=[None]*2, k=3,
                  ext=0, check_finite=False):
 
-        if check_finite:
-            w_finite = np.isfinite(w).all() if w is not None else True
-            if (not np.isfinite(x).all() or not np.isfinite(y).all() or
-                    not w_finite or not np.isfinite(t).all()):
-                raise ValueError("Input(s) must not contain NaNs or infs.")
+        x, y, w, bbox, self.ext = self.validate_input(x, y, w, bbox, k, None,
+                                                      ext, check_finite)
         if not np.all(diff(x) >= 0.0):
             raise ValueError('x must be increasing')
 
@@ -762,11 +768,6 @@ def __init__(self, x, y, t, w=None, bbox=[None]*2, k=3,
         self._data = data[:-3] + (None, None, data[-1])
         self._reset_class()
 
-        try:
-            self.ext = _extrap_modes[ext]
-        except KeyError:
-            raise ValueError("Unknown extrapolation mode %s." % ext)
-
 
 # ############### Bivariate spline ####################
 
@@ -937,12 +938,16 @@ class BivariateSpline(_BivariateSplineBase):
     UnivariateSpline :
         a similar class for univariate spline interpolation
     SmoothBivariateSpline :
-        to create a BivariateSpline through the given points
+        to create a bivariate spline through the given points
     LSQBivariateSpline :
-        to create a BivariateSpline using weighted least-squares fitting
-    RectSphereBivariateSpline
+        to create a bivariate spline using weighted least-squares fitting
+    RectSphereBivariateSpline :
+        to create a bivariate spline over a rectangular mesh on a sphere
     SmoothSphereBivariateSpline :
-    LSQSphereBivariateSpline
+        to create a smooth bivariate spline in spherical coordinates
+    LSQSphereBivariateSpline :
+        to create a bivariate spline in spherical coordinates using
+        weighted least-squares fitting
     bisplrep : older wrapping of FITPACK
     bisplev : older wrapping of FITPACK
     """
@@ -1001,6 +1006,25 @@ def integral(self, xa, xb, ya, yb):
         kx, ky = self.degrees
         return dfitpack.dblint(tx, ty, c, kx, ky, xa, xb, ya, yb)
 
+    @staticmethod
+    def _validate_input(x, y, z, w, kx, ky, eps):
+        x, y, z = np.asarray(x), np.asarray(y), np.asarray(z)
+        if not x.size == y.size == z.size:
+            raise ValueError('x, y, and z should have a same length')
+
+        if w is not None:
+            w = np.asarray(w)
+            if x.size != w.size:
+                raise ValueError('x, y, z, and w should have a same length')
+            elif not np.all(w >= 0.0):
+                raise ValueError('w should be positive')
+        if (eps is not None) and (not 0.0 < eps < 1.0):
+            raise ValueError('eps should be between (0, 1)')
+        if not x.size >= (kx + 1) * (ky + 1):
+            raise ValueError('The length of x, y and z should be at least'
+                             ' (kx+1) * (ky+1)')
+        return x, y, z, w
+
 
 class SmoothBivariateSpline(BivariateSpline):
     """
@@ -1025,8 +1049,8 @@ class SmoothBivariateSpline(BivariateSpline):
         estimate of the standard deviation of ``z[i]``.
     eps : float, optional
         A threshold for determining the effective rank of an over-determined
-        linear system of equations. `eps` should have a value between 0 and 1,
-        the default is 1e-16.
+        linear system of equations. `eps` should have a value within the open
+        interval ``(0, 1)``, the default is 1e-16.
 
     See Also
     --------
@@ -1042,7 +1066,15 @@ class SmoothBivariateSpline(BivariateSpline):
     """
 
     def __init__(self, x, y, z, w=None, bbox=[None] * 4, kx=3, ky=3, s=None,
-                 eps=None):
+                 eps=1e-16):
+
+        x, y, z, w = self._validate_input(x, y, z, w, kx, ky, eps)
+        bbox = ravel(bbox)
+        if not bbox.shape == (4,):
+            raise ValueError('bbox shape should be (4,)')
+        if s is not None and not s >= 0.0:
+            raise ValueError("s should be s >= 0.0")
+
         xb, xe, yb, ye = bbox
         nx, tx, ny, ty, c, fp, wrk1, ier = dfitpack.surfit_smth(x, y, z, w,
                                                                 xb, xe, yb,
@@ -1087,8 +1119,8 @@ class LSQBivariateSpline(BivariateSpline):
         Degrees of the bivariate spline. Default is 3.
     eps : float, optional
         A threshold for determining the effective rank of an over-determined
-        linear system of equations. `eps` should have a value between 0 and 1,
-        the default is 1e-16.
+        linear system of equations. `eps` should have a value within the open
+        interval ``(0, 1)``, the default is 1e-16.
 
     See Also
     --------
@@ -1105,6 +1137,12 @@ class LSQBivariateSpline(BivariateSpline):
 
     def __init__(self, x, y, z, tx, ty, w=None, bbox=[None]*4, kx=3, ky=3,
                  eps=None):
+
+        x, y, z, w = self._validate_input(x, y, z, w, kx, ky, eps)
+        bbox = ravel(bbox)
+        if not bbox.shape == (4,):
+            raise ValueError('bbox shape should be (4,)')
+
         nx = 2*kx+2+len(tx)
         ny = 2*ky+2+len(ty)
         tx1 = zeros((nx,), float)
@@ -1167,21 +1205,23 @@ class RectBivariateSpline(BivariateSpline):
     """
 
     def __init__(self, x, y, z, bbox=[None] * 4, kx=3, ky=3, s=0):
-        x, y = ravel(x), ravel(y)
+        x, y, bbox = ravel(x), ravel(y), ravel(bbox)
+        z = np.asarray(z)
         if not np.all(diff(x) > 0.0):
             raise ValueError('x must be strictly increasing')
         if not np.all(diff(y) > 0.0):
             raise ValueError('y must be strictly increasing')
-        if not ((x.min() == x[0]) and (x.max() == x[-1])):
-            raise ValueError('x must be strictly ascending')
-        if not ((y.min() == y[0]) and (y.max() == y[-1])):
-            raise ValueError('y must be strictly ascending')
         if not x.size == z.shape[0]:
             raise ValueError('x dimension of z must have same number of '
                              'elements as x')
         if not y.size == z.shape[1]:
             raise ValueError('y dimension of z must have same number of '
                              'elements as y')
+        if not bbox.shape == (4,):
+            raise ValueError('bbox shape should be (4,)')
+        if s is not None and not s >= 0.0:
+            raise ValueError("s should be s >= 0.0")
+
         z = ravel(z)
         xb, xe, yb, ye = bbox
         nx, tx, ny, ty, c, fp, ier = dfitpack.regrid_smth(x, y, z, xb, xe, yb,
@@ -1312,19 +1352,19 @@ class SmoothSphereBivariateSpline(SphereBivariateSpline):
     ----------
     theta, phi, r : array_like
         1-D sequences of data points (order is not important). Coordinates
-        must be given in radians. Theta must lie within the interval (0, pi),
-        and phi must lie within the interval (0, 2pi).
+        must be given in radians. Theta must lie within the interval
+        ``[0, pi]``, and phi must lie within the interval ``[0, 2pi]``.
     w : array_like, optional
         Positive 1-D sequence of weights.
     s : float, optional
         Positive smoothing factor defined for estimation condition:
         ``sum((w(i)*(r(i) - s(theta(i), phi(i))))**2, axis=0) <= s``
-        Default ``s=len(w)`` which should be a good value if 1/w[i] is an
-        estimate of the standard deviation of r[i].
+        Default ``s=len(w)`` which should be a good value if ``1/w[i]`` is an
+        estimate of the standard deviation of ``r[i]``.
     eps : float, optional
         A threshold for determining the effective rank of an over-determined
-        linear system of equations. `eps` should have a value between 0 and 1,
-        the default is 1e-16.
+        linear system of equations. `eps` should have a value within the open
+        interval ``(0, 1)``, the default is 1e-16.
 
     Notes
     -----
@@ -1380,6 +1420,19 @@ class SmoothSphereBivariateSpline(SphereBivariateSpline):
     """
 
     def __init__(self, theta, phi, r, w=None, s=0., eps=1E-16):
+
+        # 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 not s >= 0.0:
+            raise ValueError('s should be positive')
+        if not 0.0 < eps < 1.0:
+            raise ValueError('eps should be between (0, 1)')
+
         if np.issubclass_(w, float):
             w = ones(len(theta)) * w
         nt_, tt_, np_, tp_, c, fp, ier = dfitpack.spherfit_smth(theta, phi,
@@ -1408,8 +1461,8 @@ class LSQSphereBivariateSpline(SphereBivariateSpline):
     ----------
     theta, phi, r : array_like
         1-D sequences of data points (order is not important). Coordinates
-        must be given in radians. Theta must lie within the interval (0, pi),
-        and phi must lie within the interval (0, 2pi).
+        must be given in radians. Theta must lie within the interval
+        ``[0, pi]``, and phi must lie within the interval ``[0, 2pi]``.
     tt, tp : array_like
         Strictly ordered 1-D sequences of knots coordinates.
         Coordinates must satisfy ``0 < tt[i] < pi``, ``0 < tp[i] < 2*pi``.
@@ -1418,8 +1471,8 @@ class LSQSphereBivariateSpline(SphereBivariateSpline):
         and `r`.
     eps : float, optional
         A threshold for determining the effective rank of an over-determined
-        linear system of equations. `eps` should have a value between 0 and 1,
-        the default is 1e-16.
+        linear system of equations. `eps` should have a value within the
+        open interval ``(0, 1)``, the default is 1e-16.
 
     Notes
     -----
@@ -1481,6 +1534,20 @@ class LSQSphereBivariateSpline(SphereBivariateSpline):
     """
 
     def __init__(self, theta, phi, r, tt, tp, w=None, eps=1E-16):
+
+        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()):
+            raise ValueError('phi should be between [0, 2pi]')
+        if not ((0.0 < tt).all() and (tt < np.pi).all()):
+            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 not 0.0 < eps < 1.0:
+            raise ValueError('eps should be between (0, 1)')
+
         if np.issubclass_(w, float):
             w = ones(len(theta)) * w
         nt_, np_ = 8 + len(tt), 8 + len(tp)
@@ -1542,14 +1609,14 @@ class RectSphereBivariateSpline(SphereBivariateSpline):
     Parameters
     ----------
     u : array_like
-        1-D array of latitude coordinates in strictly ascending order.
+        1-D array of colatitude coordinates in strictly ascending order.
         Coordinates must be given in radians and lie within the interval
-        (0, pi).
+        ``[0, pi]``.
     v : array_like
         1-D array of longitude coordinates in strictly ascending order.
-        Coordinates must be given in radians. First element (v[0]) must lie
-        within the interval [-pi, pi). Last element (v[-1]) must satisfy
-        v[-1] <= v[0] + 2*pi.
+        Coordinates must be given in radians. First element (``v[0]``) must lie
+        within the interval ``[-pi, pi)``. Last element (``v[-1]``) must satisfy
+        ``v[-1] <= v[0] + 2*pi``.
     r : array_like
         2-D array of data with shape ``(u.size, v.size)``.
     s : float, optional
@@ -1693,6 +1760,14 @@ 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)
+
+        if not ((0.0 <= u).all() and (u <= np.pi).all()):
+            raise ValueError('u should be between [0, pi]')
+        if not -np.pi <= v[0] < np.pi:
+            raise ValueError('v[0] should be between [-pi, pi)')
+        if not v[-1] <= v[0] + 2*np.pi:
+            raise ValueError('v[-1] should be v[0] + 2pi or less ')
+
         if not np.all(np.diff(u) > 0.0):
             raise ValueError('u must be strictly increasing')
         if not np.all(np.diff(v) > 0.0):
@@ -1712,9 +1787,15 @@ def __init__(self, u, v, r, s=0., pole_continuity=False, pole_values=None,
             raise ValueError('if pole_continuity is False, so must be '
                              'pole_flat')
 
+        if not s >= 0.0:
+            raise ValueError('s should be positive')
+
         r = np.ravel(r)
         nu, tu, nv, tv, c, fp, ier = dfitpack.regrid_smth_spher(iopt, ider,
-                                       u.copy(), v.copy(), r.copy(), r0, r1, s)
+                                                                u.copy(),
+                                                                v.copy(),
+                                                                r.copy(),
+                                                                r0, r1, s)
 
         if ier not in [0, -1, -2]:
             msg = _spfit_messages.get(ier, 'ier=%s' % (ier))


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
@@ -223,6 +223,92 @@ def test_increasing_x(self):
         assert_raises(ValueError, LSQUnivariateSpline,
                 **dict(x=x, y=y, t=t, w=w, check_finite=True))
 
+    def test_invalid_input_for_univariate_spline(self):
+
+        with assert_raises(ValueError) as info:
+            x_values = [1, 2, 4, 6, 8.5]
+            y_values = [0.5, 0.8, 1.3, 2.5]
+            UnivariateSpline(x_values, y_values)
+        assert "x and y should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            x_values = [1, 2, 4, 6, 8.5]
+            y_values = [0.5, 0.8, 1.3, 2.5, 2.8]
+            w_values = [-1.0, 1.0, 1.0, 1.0]
+            UnivariateSpline(x_values, y_values, w=w_values)
+        assert "x, y, and w should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            bbox = (-1)
+            UnivariateSpline(x_values, y_values, bbox=bbox)
+        assert "bbox shape should be (2,)" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            UnivariateSpline(x_values, y_values, k=6)
+        assert "k should be 1 <= k <= 5" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            UnivariateSpline(x_values, y_values, s=-1.0)
+        assert "s should be s >= 0.0" in str(info.value)
+
+    def test_invalid_input_for_interpolated_univariate_spline(self):
+
+        with assert_raises(ValueError) as info:
+            x_values = [1, 2, 4, 6, 8.5]
+            y_values = [0.5, 0.8, 1.3, 2.5]
+            InterpolatedUnivariateSpline(x_values, y_values)
+        assert "x and y should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            x_values = [1, 2, 4, 6, 8.5]
+            y_values = [0.5, 0.8, 1.3, 2.5, 2.8]
+            w_values = [-1.0, 1.0, 1.0, 1.0]
+            InterpolatedUnivariateSpline(x_values, y_values, w=w_values)
+        assert "x, y, and w should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            bbox = (-1)
+            InterpolatedUnivariateSpline(x_values, y_values, bbox=bbox)
+        assert "bbox shape should be (2,)" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            InterpolatedUnivariateSpline(x_values, y_values, k=6)
+        assert "k should be 1 <= k <= 5" in str(info.value)
+
+    def test_invalid_input_for_lsq_univariate_spline(self):
+
+        x_values = [1, 2, 4, 6, 8.5]
+        y_values = [0.5, 0.8, 1.3, 2.5, 2.8]
+        spl = UnivariateSpline(x_values, y_values, check_finite=True)
+        t_values = spl.get_knots()[3:4]  # interior knots w/ default k=3
+
+        with assert_raises(ValueError) as info:
+            x_values = [1, 2, 4, 6, 8.5]
+            y_values = [0.5, 0.8, 1.3, 2.5]
+            LSQUnivariateSpline(x_values, y_values, t_values)
+        assert "x and y should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            x_values = [1, 2, 4, 6, 8.5]
+            y_values = [0.5, 0.8, 1.3, 2.5, 2.8]
+            w_values = [1.0, 1.0, 1.0, 1.0]
+            LSQUnivariateSpline(x_values, y_values, t_values, w=w_values)
+        assert "x, y, and w should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            bbox = (100, -100)
+            LSQUnivariateSpline(x_values, y_values, t_values, bbox=bbox)
+        assert "Interior knots t must satisfy Schoenberg-Whitney conditions" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            bbox = (-1)
+            LSQUnivariateSpline(x_values, y_values, t_values, bbox=bbox)
+        assert "bbox shape should be (2,)" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            LSQUnivariateSpline(x_values, y_values, t_values, k=6)
+        assert "k should be 1 <= k <= 5" in str(info.value)
+
 
 class TestLSQBivariateSpline(object):
     # NOTE: The systems in this test class are rank-deficient
@@ -301,6 +387,49 @@ def test_empty_input(self):
         assert_array_equal(lut([], []), np.zeros((0,0)))
         assert_array_equal(lut([], [], grid=False), np.zeros((0,)))
 
+    def test_invalid_input(self):
+        s = 0.1
+        tx = [1 + s, 3 - s]
+        ty = [1 + s, 3 - s]
+
+        with assert_raises(ValueError) as info:
+            x = np.linspace(1.0, 10.0)
+            y = np.linspace(1.0, 10.0)
+            z = np.linspace(1.0, 10.0, num=10)
+            LSQBivariateSpline(x, y, z, tx, ty)
+        assert "x, y, and z should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            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, num=20)
+            LSQBivariateSpline(x, y, z, tx, ty, w=w)
+        assert "x, y, z, and w should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            w = np.linspace(-1.0, 10.0)
+            LSQBivariateSpline(x, y, z, tx, ty, w=w)
+        assert "w should be positive" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            bbox = (-100, 100, -100)
+            LSQBivariateSpline(x, y, z, tx, ty, bbox=bbox)
+        assert "bbox shape should be (4,)" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            LSQBivariateSpline(x, y, z, tx, ty, kx=10, ky=10)
+        assert "The length of x, y and z should be at least (kx+1) * (ky+1)" in \
+               str(info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            LSQBivariateSpline(x, y, z, tx, ty, eps=0.0)
+        assert "eps should be between (0, 1)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            LSQBivariateSpline(x, y, z, tx, ty, eps=1.0)
+        assert "eps should be between (0, 1)" in str(exc_info.value)
+
 
 class TestSmoothBivariateSpline(object):
     def test_linear_constant(self):
@@ -365,6 +494,50 @@ def test_rerun_lwrk2_too_small(self):
         res2 = interp_(xi, yi)
         assert_almost_equal(res1, res2)
 
+    def test_invalid_input(self):
+
+        with assert_raises(ValueError) as info:
+            x = np.linspace(1.0, 10.0)
+            y = np.linspace(1.0, 10.0)
+            z = np.linspace(1.0, 10.0, num=10)
+            SmoothBivariateSpline(x, y, z)
+        assert "x, y, and z should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            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, num=20)
+            SmoothBivariateSpline(x, y, z, w=w)
+        assert "x, y, z, and w should have a same length" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            w = np.linspace(-1.0, 10.0)
+            SmoothBivariateSpline(x, y, z, w=w)
+        assert "w should be positive" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            bbox = (-100, 100, -100)
+            SmoothBivariateSpline(x, y, z, bbox=bbox)
+        assert "bbox shape should be (4,)" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            SmoothBivariateSpline(x, y, z, kx=10, ky=10)
+        assert "The length of x, y and z should be at least (kx+1) * (ky+1)" in\
+               str(info.value)
+
+        with assert_raises(ValueError) as info:
+            SmoothBivariateSpline(x, y, z, s=-1.0)
+        assert "s should be s >= 0.0" in str(info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            SmoothBivariateSpline(x, y, z, eps=0.0)
+        assert "eps should be between (0, 1)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            SmoothBivariateSpline(x, y, z, eps=1.0)
+        assert "eps should be between (0, 1)" in str(exc_info.value)
+
 
 class TestLSQSphereBivariateSpline(object):
     def setup_method(self):
@@ -394,6 +567,90 @@ def test_empty_input(self):
         assert_array_almost_equal(self.lut_lsq([], []), np.zeros((0,0)))
         assert_array_almost_equal(self.lut_lsq([], [], grid=False), np.zeros((0,)))
 
+    def test_invalid_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
+        data = ones((theta.shape[0], phi.shape[0]))
+        # define knots and extract data values at the knots
+        knotst = theta[::5]
+        knotsp = phi[::5]
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_theta = linspace(-0.1, 1.0, num=ntheta) * pi
+            invalid_lats, lons = meshgrid(invalid_theta, phi)
+            LSQSphereBivariateSpline(invalid_lats.ravel(), lons.ravel(),
+                                     data.T.ravel(), knotst, knotsp)
+        assert "theta should be between [0, pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_theta = linspace(0.1, 1.1, num=ntheta) * pi
+            invalid_lats, lons = meshgrid(invalid_theta, phi)
+            LSQSphereBivariateSpline(invalid_lats.ravel(), lons.ravel(),
+                                     data.T.ravel(), knotst, knotsp)
+        assert "theta should be between [0, pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_phi = linspace(-0.1, 1.0, num=ntheta) * 2.0 * pi
+            lats, invalid_lons = meshgrid(theta, invalid_phi)
+            LSQSphereBivariateSpline(lats.ravel(), invalid_lons.ravel(),
+                                     data.T.ravel(), knotst, knotsp)
+        assert "phi should be between [0, 2pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_phi = linspace(0.0, 1.1, num=ntheta) * 2.0 * pi
+            lats, invalid_lons = meshgrid(theta, invalid_phi)
+            LSQSphereBivariateSpline(lats.ravel(), invalid_lons.ravel(),
+                                     data.T.ravel(), knotst, knotsp)
+        assert "phi should be between [0, 2pi]" in str(exc_info.value)
+
+        lats, lons = meshgrid(theta, phi)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_knotst = np.copy(knotst)
+            invalid_knotst[0] = -0.1
+            LSQSphereBivariateSpline(lats.ravel(), lons.ravel(),
+                                     data.T.ravel(), invalid_knotst, knotsp)
+        assert "tt should be between (0, pi)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_knotst = np.copy(knotst)
+            invalid_knotst[0] = pi
+            LSQSphereBivariateSpline(lats.ravel(), lons.ravel(),
+                                     data.T.ravel(), invalid_knotst, knotsp)
+        assert "tt should be between (0, pi)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_knotsp = np.copy(knotsp)
+            invalid_knotsp[0] = -0.1
+            LSQSphereBivariateSpline(lats.ravel(), lons.ravel(),
+                                     data.T.ravel(), knotst, invalid_knotsp)
+        assert "tp should be between (0, 2pi)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_knotsp = np.copy(knotsp)
+            invalid_knotsp[0] = 2 * pi
+            LSQSphereBivariateSpline(lats.ravel(), lons.ravel(),
+                                     data.T.ravel(), knotst, invalid_knotsp)
+        assert "tp should be between (0, 2pi)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_w = array([-1.0, 1.0, 1.5, 0.5, 1.0, 1.5, 0.5, 1.0, 1.0])
+            LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), data.T.ravel(),
+                                     knotst, knotsp, w=invalid_w)
+        assert "w should be positive" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), data.T.ravel(),
+                                     knotst, knotsp, eps=0.0)
+        assert "eps should be between (0, 1)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            LSQSphereBivariateSpline(lats.ravel(), lons.ravel(), data.T.ravel(),
+                                     knotst, knotsp, eps=1.0)
+        assert "eps should be between (0, 1)" in str(exc_info.value)
+
 
 class TestSmoothSphereBivariateSpline(object):
     def setup_method(self):
@@ -413,6 +670,56 @@ def test_empty_input(self):
         assert_array_almost_equal(self.lut([], []), np.zeros((0,0)))
         assert_array_almost_equal(self.lut([], [], grid=False), np.zeros((0,)))
 
+    def test_invalid_input(self):
+        theta = array([.25 * pi, .25 * pi, .25 * pi, .5 * pi, .5 * pi, .5 * pi,
+                       .75 * pi, .75 * pi, .75 * pi])
+        phi = array([.5 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi, .5 * pi, pi,
+                     1.5 * pi])
+        r = array([3, 3, 3, 3, 3, 3, 3, 3, 3])
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_theta = array([-0.1 * pi, .25 * pi, .25 * pi, .5 * pi,
+                                   .5 * pi, .5 * pi, .75 * pi, .75 * pi,
+                                   .75 * pi])
+            SmoothSphereBivariateSpline(invalid_theta, phi, r, s=1E10)
+        assert "theta should be between [0, pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_theta = array([.25 * pi, .25 * pi, .25 * pi, .5 * pi,
+                                   .5 * pi, .5 * pi, .75 * pi, .75 * pi,
+                                   1.1 * pi])
+            SmoothSphereBivariateSpline(invalid_theta, phi, r, s=1E10)
+        assert "theta should be between [0, pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_phi = array([-.1 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi,
+                                 .5 * pi, pi, 1.5 * pi])
+            SmoothSphereBivariateSpline(theta, invalid_phi, r, s=1E10)
+        assert "phi should be between [0, 2pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_phi = array([1.0 * pi, pi, 1.5 * pi, .5 * pi, pi, 1.5 * pi,
+                                 .5 * pi, pi, 2.1 * pi])
+            SmoothSphereBivariateSpline(theta, invalid_phi, r, s=1E10)
+        assert "phi should be between [0, 2pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            invalid_w = array([-1.0, 1.0, 1.5, 0.5, 1.0, 1.5, 0.5, 1.0, 1.0])
+            SmoothSphereBivariateSpline(theta, phi, r, w=invalid_w, s=1E10)
+        assert "w should be positive" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            SmoothSphereBivariateSpline(theta, phi, r, s=-1.0)
+        assert "s should be positive" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            SmoothSphereBivariateSpline(theta, phi, r, eps=-1.0)
+        assert "eps should be between (0, 1)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            SmoothSphereBivariateSpline(theta, phi, r, eps=1.0)
+        assert "eps should be between (0, 1)" in str(exc_info.value)
+
 
 class TestRectBivariateSpline(object):
     def test_defaults(self):
@@ -469,6 +776,55 @@ def test_broadcast(self):
         lut = RectBivariateSpline(x,y,z)
         assert_allclose(lut(x, y), lut(x[:,None], y[None,:], grid=False))
 
+    def test_invalid_input(self):
+
+        with assert_raises(ValueError) as info:
+            x = array([6, 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]])
+            RectBivariateSpline(x, y, z)
+        assert "x must be strictly increasing" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            x = array([1, 2, 3, 4, 5])
+            y = array([2, 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]])
+            RectBivariateSpline(x, y, z)
+        assert "y must be strictly increasing" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            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]])
+            RectBivariateSpline(x, y, z)
+        assert "x dimension of z must have same number of elements as x"\
+               in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            x = array([1, 2, 3, 4, 5])
+            y = array([1, 2, 3, 4, 5])
+            z = array([[1, 2, 1, 2], [1, 2, 1, 2], [1, 2, 3, 2],
+                       [1, 2, 2, 2], [1, 2, 1, 2]])
+            RectBivariateSpline(x, y, z)
+        assert "y dimension of z must have same number of elements as y"\
+               in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            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 = (-100, 100, -100)
+            RectBivariateSpline(x, y, z, bbox=bbox)
+        assert "bbox shape should be (4,)" in str(info.value)
+
+        with assert_raises(ValueError) as info:
+            RectBivariateSpline(x, y, z, s=-1.0)
+        assert "s should be s >= 0.0" in str(info.value)
+
 
 class TestRectSphereBivariateSpline(object):
     def test_defaults(self):
@@ -535,6 +891,40 @@ def test_derivatives(self):
                         _numdiff_2d(lambda x,y: lut(x,y,grid=False), x, y, dx=1, dy=1, eps=1e-6),
                         rtol=1e-3, atol=1e-3)
 
+    def test_invalid_input(self):
+        data = np.dot(np.atleast_2d(90. - np.linspace(-80., 80., 18)).T,
+                      np.atleast_2d(180. - np.abs(np.linspace(0., 350., 9)))).T
+
+        with assert_raises(ValueError) as exc_info:
+            lats = np.linspace(-1, 170, 9) * np.pi / 180.
+            lons = np.linspace(0, 350, 18) * np.pi / 180.
+            RectSphereBivariateSpline(lats, lons, data)
+        assert "u should be between [0, pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            lats = np.linspace(10, 181, 9) * np.pi / 180.
+            lons = np.linspace(0, 350, 18) * np.pi / 180.
+            RectSphereBivariateSpline(lats, lons, data)
+        assert "u should be between [0, pi]" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            lats = np.linspace(10, 170, 9) * np.pi / 180.
+            lons = np.linspace(-181, 10, 18) * np.pi / 180.
+            RectSphereBivariateSpline(lats, lons, data)
+        assert "v[0] should be between [-pi, pi)" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            lats = np.linspace(10, 170, 9) * np.pi / 180.
+            lons = np.linspace(-10, 360, 18) * np.pi / 180.
+            RectSphereBivariateSpline(lats, lons, data)
+        assert "v[-1] should be v[0] + 2pi or less" in str(exc_info.value)
+
+        with assert_raises(ValueError) as exc_info:
+            lats = np.linspace(10, 170, 9) * np.pi / 180.
+            lons = np.linspace(10, 350, 18) * np.pi / 180.
+            RectSphereBivariateSpline(lats, lons, data, s=-1)
+        assert "s should be positive" in str(exc_info.value)
+
 
 def _numdiff_2d(func, x, y, dx=0, dy=0, eps=1e-8):
     if dx == 0 and dy == 0: