from __future__ import division, absolute_import, print_function
import numpy as np
from numpy.testing import assert_equal, assert_allclose, assert_
from scipy._lib._numpy_compat import suppress_warnings
from pytest import raises as assert_raises
import pytest
from scipy.interpolate import (BSpline, BPoly, PPoly, make_interp_spline,
make_lsq_spline, _bspl, splev, splrep, splprep, splder, splantider,
sproot, splint, insert)
import scipy.linalg as sl
from scipy._lib._version import NumpyVersion
from scipy.interpolate._bsplines import _not_a_knot, _augknt
import scipy.interpolate._fitpack_impl as _impl
from scipy.interpolate._fitpack import _splint
class TestBSpline(object):
def test_ctor(self):
# knots should be an ordered 1D array of finite real numbers
assert_raises((TypeError, ValueError), BSpline,
**dict(t=[1, 1.j], c=[1.], k=0))
with np.errstate(invalid='ignore'):
assert_raises(ValueError, BSpline, **dict(t=[1, np.nan], c=[1.], k=0))
assert_raises(ValueError, BSpline, **dict(t=[1, np.inf], c=[1.], k=0))
assert_raises(ValueError, BSpline, **dict(t=[1, -1], c=[1.], k=0))
assert_raises(ValueError, BSpline, **dict(t=[[1], [1]], c=[1.], k=0))
# for n+k+1 knots and degree k need at least n coefficients
assert_raises(ValueError, BSpline, **dict(t=[0, 1, 2], c=[1], k=0))
assert_raises(ValueError, BSpline,
**dict(t=[0, 1, 2, 3, 4], c=[1., 1.], k=2))
# non-integer orders
assert_raises(TypeError, BSpline,
**dict(t=[0., 0., 1., 2., 3., 4.], c=[1., 1., 1.], k="cubic"))
assert_raises(TypeError, BSpline,
**dict(t=[0., 0., 1., 2., 3., 4.], c=[1., 1., 1.], k=2.5))
# basic interval cannot have measure zero (here: [1..1])
assert_raises(ValueError, BSpline,
**dict(t=[0., 0, 1, 1, 2, 3], c=[1., 1, 1], k=2))
# tck vs self.tck
n, k = 11, 3
t = np.arange(n+k+1)
c = np.random.random(n)
b = BSpline(t, c, k)
assert_allclose(t, b.t)
assert_allclose(c, b.c)
assert_equal(k, b.k)
def test_tck(self):
b = _make_random_spline()
tck = b.tck
assert_allclose(b.t, tck[0], atol=1e-15, rtol=1e-15)
assert_allclose(b.c, tck[1], atol=1e-15, rtol=1e-15)
assert_equal(b.k, tck[2])
# b.tck is read-only
with pytest.raises(AttributeError):
b.tck = 'foo'
def test_degree_0(self):
xx = np.linspace(0, 1, 10)
b = BSpline(t=[0, 1], c=[3.], k=0)
assert_allclose(b(xx), 3)
b = BSpline(t=[0, 0.35, 1], c=[3, 4], k=0)
assert_allclose(b(xx), np.where(xx < 0.35, 3, 4))
def test_degree_1(self):
t = [0, 1, 2, 3, 4]
c = [1, 2, 3]
k = 1
b = BSpline(t, c, k)
x = np.linspace(1, 3, 50)
assert_allclose(c[0]*B_012(x) + c[1]*B_012(x-1) + c[2]*B_012(x-2),
b(x), atol=1e-14)
assert_allclose(splev(x, (t, c, k)), b(x), atol=1e-14)
def test_bernstein(self):
# a special knot vector: Bernstein polynomials
k = 3
t = np.asarray([0]*(k+1) + [1]*(k+1))
c = np.asarray([1., 2., 3., 4.])
bp = BPoly(c.reshape(-1, 1), [0, 1])
bspl = BSpline(t, c, k)
xx = np.linspace(-1., 2., 10)
assert_allclose(bp(xx, extrapolate=True),
bspl(xx, extrapolate=True), atol=1e-14)
assert_allclose(splev(xx, (t, c, k)),
bspl(xx), atol=1e-14)
def test_rndm_naive_eval(self):
# test random coefficient spline *on the base interval*,
# t[k] <= x < t[-k-1]
b = _make_random_spline()
t, c, k = b.tck
xx = np.linspace(t[k], t[-k-1], 50)
y_b = b(xx)
y_n = [_naive_eval(x, t, c, k) for x in xx]
assert_allclose(y_b, y_n, atol=1e-14)
y_n2 = [_naive_eval_2(x, t, c, k) for x in xx]
assert_allclose(y_b, y_n2, atol=1e-14)
def test_rndm_splev(self):
b = _make_random_spline()
t, c, k = b.tck
xx = np.linspace(t[k], t[-k-1], 50)
assert_allclose(b(xx), splev(xx, (t, c, k)), atol=1e-14)
def test_rndm_splrep(self):
np.random.seed(1234)
x = np.sort(np.random.random(20))
y = np.random.random(20)
tck = splrep(x, y)
b = BSpline(*tck)
t, k = b.t, b.k
xx = np.linspace(t[k], t[-k-1], 80)
assert_allclose(b(xx), splev(xx, tck), atol=1e-14)
def test_rndm_unity(self):
b = _make_random_spline()
b.c = np.ones_like(b.c)
xx = np.linspace(b.t[b.k], b.t[-b.k-1], 100)
assert_allclose(b(xx), 1.)
def test_vectorization(self):
n, k = 22, 3
t = np.sort(np.random.random(n))
c = np.random.random(size=(n, 6, 7))
b = BSpline(t, c, k)
tm, tp = t[k], t[-k-1]
xx = tm + (tp - tm) * np.random.random((3, 4, 5))
assert_equal(b(xx).shape, (3, 4, 5, 6, 7))
def test_len_c(self):
# for n+k+1 knots, only first n coefs are used.
# and BTW this is consistent with FITPACK
n, k = 33, 3
t = np.sort(np.random.random(n+k+1))
c = np.random.random(n)
# pad coefficients with random garbage
c_pad = np.r_[c, np.random.random(k+1)]
b, b_pad = BSpline(t, c, k), BSpline(t, c_pad, k)
dt = t[-1] - t[0]
xx = np.linspace(t[0] - dt, t[-1] + dt, 50)
assert_allclose(b(xx), b_pad(xx), atol=1e-14)
assert_allclose(b(xx), splev(xx, (t, c, k)), atol=1e-14)
assert_allclose(b(xx), splev(xx, (t, c_pad, k)), atol=1e-14)
def test_endpoints(self):
# base interval is closed
b = _make_random_spline()
t, _, k = b.tck
tm, tp = t[k], t[-k-1]
for extrap in (True, False):
assert_allclose(b([tm, tp], extrap),
b([tm + 1e-10, tp - 1e-10], extrap), atol=1e-9)
def test_continuity(self):
# assert continuity at internal knots
b = _make_random_spline()
t, _, k = b.tck
assert_allclose(b(t[k+1:-k-1] - 1e-10), b(t[k+1:-k-1] + 1e-10),
atol=1e-9)
def test_extrap(self):
b = _make_random_spline()
t, c, k = b.tck
dt = t[-1] - t[0]
xx = np.linspace(t[k] - dt, t[-k-1] + dt, 50)
mask = (t[k] < xx) & (xx < t[-k-1])
# extrap has no effect within the base interval
assert_allclose(b(xx[mask], extrapolate=True),
b(xx[mask], extrapolate=False))
# extrapolated values agree with FITPACK
assert_allclose(b(xx, extrapolate=True),
splev(xx, (t, c, k), ext=0))
def test_default_extrap(self):
# BSpline defaults to extrapolate=True
b = _make_random_spline()
t, _, k = b.tck
xx = [t[0] - 1, t[-1] + 1]
yy = b(xx)
assert_(not np.all(np.isnan(yy)))
def test_periodic_extrap(self):
np.random.seed(1234)
t = np.sort(np.random.random(8))
c = np.random.random(4)
k = 3
b = BSpline(t, c, k, extrapolate='periodic')
n = t.size - (k + 1)
dt = t[-1] - t[0]
xx = np.linspace(t[k] - dt, t[n] + dt, 50)
xy = t[k] + (xx - t[k]) % (t[n] - t[k])
assert_allclose(b(xx), splev(xy, (t, c, k)))
# Direct check
xx = [-1, 0, 0.5, 1]
xy = t[k] + (xx - t[k]) % (t[n] - t[k])
assert_equal(b(xx, extrapolate='periodic'), b(xy, extrapolate=True))
def test_ppoly(self):
b = _make_random_spline()
t, c, k = b.tck
pp = PPoly.from_spline((t, c, k))
xx = np.linspace(t[k], t[-k], 100)
assert_allclose(b(xx), pp(xx), atol=1e-14, rtol=1e-14)
def test_derivative_rndm(self):
b = _make_random_spline()
t, c, k = b.tck
xx = np.linspace(t[0], t[-1], 50)
xx = np.r_[xx, t]
for der in range(1, k+1):
yd = splev(xx, (t, c, k), der=der)
assert_allclose(yd, b(xx, nu=der), atol=1e-14)
# higher derivatives all vanish
assert_allclose(b(xx, nu=k+1), 0, atol=1e-14)
def test_derivative_jumps(self):
# example from de Boor, Chap IX, example (24)
# NB: knots augmented & corresp coefs are zeroed out
# in agreement with the convention (29)
k = 2
t = [-1, -1, 0, 1, 1, 3, 4, 6, 6, 6, 7, 7]
np.random.seed(1234)
c = np.r_[0, 0, np.random.random(5), 0, 0]
b = BSpline(t, c, k)
# b is continuous at x != 6 (triple knot)
x = np.asarray([1, 3, 4, 6])
assert_allclose(b(x[x != 6] - 1e-10),
b(x[x != 6] + 1e-10))
assert_(not np.allclose(b(6.-1e-10), b(6+1e-10)))
# 1st derivative jumps at double knots, 1 & 6:
x0 = np.asarray([3, 4])
assert_allclose(b(x0 - 1e-10, nu=1),
b(x0 + 1e-10, nu=1))
x1 = np.asarray([1, 6])
assert_(not np.all(np.allclose(b(x1 - 1e-10, nu=1),
b(x1 + 1e-10, nu=1))))
# 2nd derivative is not guaranteed to be continuous either
assert_(not np.all(np.allclose(b(x - 1e-10, nu=2),
b(x + 1e-10, nu=2))))
def test_basis_element_quadratic(self):
xx = np.linspace(-1, 4, 20)
b = BSpline.basis_element(t=[0, 1, 2, 3])
assert_allclose(b(xx),
splev(xx, (b.t, b.c, b.k)), atol=1e-14)
assert_allclose(b(xx),
B_0123(xx), atol=1e-14)
b = BSpline.basis_element(t=[0, 1, 1, 2])
xx = np.linspace(0, 2, 10)
assert_allclose(b(xx),
np.where(xx < 1, xx*xx, (2.-xx)**2), atol=1e-14)
def test_basis_element_rndm(self):
b = _make_random_spline()
t, c, k = b.tck
xx = np.linspace(t[k], t[-k-1], 20)
assert_allclose(b(xx), _sum_basis_elements(xx, t, c, k), atol=1e-14)
def test_cmplx(self):
b = _make_random_spline()
t, c, k = b.tck
cc = c * (1. + 3.j)
b = BSpline(t, cc, k)
b_re = BSpline(t, b.c.real, k)
b_im = BSpline(t, b.c.imag, k)
xx = np.linspace(t[k], t[-k-1], 20)
assert_allclose(b(xx).real, b_re(xx), atol=1e-14)
assert_allclose(b(xx).imag, b_im(xx), atol=1e-14)
def test_nan(self):
# nan in, nan out.
b = BSpline.basis_element([0, 1, 1, 2])
assert_(np.isnan(b(np.nan)))
def test_derivative_method(self):
b = _make_random_spline(k=5)
t, c, k = b.tck
b0 = BSpline(t, c, k)
xx = np.linspace(t[k], t[-k-1], 20)
for j in range(1, k):
b = b.derivative()
assert_allclose(b0(xx, j), b(xx), atol=1e-12, rtol=1e-12)
def test_antiderivative_method(self):
b = _make_random_spline()
t, c, k = b.tck
xx = np.linspace(t[k], t[-k-1], 20)
assert_allclose(b.antiderivative().derivative()(xx),
b(xx), atol=1e-14, rtol=1e-14)
# repeat with n-D array for c
c = np.c_[c, c, c]
c = np.dstack((c, c))
b = BSpline(t, c, k)
assert_allclose(b.antiderivative().derivative()(xx),
b(xx), atol=1e-14, rtol=1e-14)
def test_integral(self):
b = BSpline.basis_element([0, 1, 2]) # x for x < 1 else 2 - x
assert_allclose(b.integrate(0, 1), 0.5)
assert_allclose(b.integrate(1, 0), -1 * 0.5)
assert_allclose(b.integrate(1, 0), -0.5)
# extrapolate or zeros outside of [0, 2]; default is yes
assert_allclose(b.integrate(-1, 1), 0)
assert_allclose(b.integrate(-1, 1, extrapolate=True), 0)
assert_allclose(b.integrate(-1, 1, extrapolate=False), 0.5)
assert_allclose(b.integrate(1, -1, extrapolate=False), -1 * 0.5)
Loading ...