Skip to content

Commit e117396

Browse files
authored
Merge pull request scipy#22156 from jorenham/special.lpn-lpmn-clpmn-deprecation-warning
DEP: deprecation warnings for ``special.lpn`` and ``[c]lpmn``
2 parents 2b73948 + 5bcbae2 commit e117396

File tree

5 files changed

+120
-52
lines changed

5 files changed

+120
-52
lines changed

scipy/special/_basic.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from ._comb import _comb_int
2222
from ._multiufuncs import (assoc_legendre_p_all,
2323
legendre_p_all)
24+
from scipy._lib.deprecation import _deprecated
2425

2526

2627
__all__ = [
@@ -87,6 +88,11 @@
8788
]
8889

8990

91+
__DEPRECATION_MSG_1_15 = (
92+
"`scipy.special.{}` is deprecated as of SciPy 1.15.0 and will be "
93+
"removed in SciPy 1.17.0. Please use `scipy.special.{}` instead."
94+
)
95+
9096
# mapping k to last n such that factorialk(n, k) < np.iinfo(np.int64).max
9197
_FACTORIALK_LIMITS_64BITS = {1: 20, 2: 33, 3: 44, 4: 54, 5: 65,
9298
6: 74, 7: 84, 8: 93, 9: 101}
@@ -1703,6 +1709,7 @@ def mathieu_odd_coef(m, q):
17031709
return fc[:km]
17041710

17051711

1712+
@_deprecated(__DEPRECATION_MSG_1_15.format("lpmn", "assoc_legendre_p_all"))
17061713
def lpmn(m, n, z):
17071714
"""Sequence of associated Legendre functions of the first kind.
17081715
@@ -1715,8 +1722,8 @@ def lpmn(m, n, z):
17151722
use clpmn instead.
17161723
17171724
.. deprecated:: 1.15.0
1718-
This function is deprecated and will be removed in a future version.
1719-
Use `scipy.special.assoc_legendre_p_all` instead.
1725+
This function is deprecated and will be removed in SciPy 1.17.0.
1726+
Please `scipy.special.assoc_legendre_p_all` instead.
17201727
17211728
Parameters
17221729
----------
@@ -1782,6 +1789,7 @@ def lpmn(m, n, z):
17821789
return p, pd
17831790

17841791

1792+
@_deprecated(__DEPRECATION_MSG_1_15.format("clpmn", "assoc_legendre_p_all"))
17851793
def clpmn(m, n, z, type=3):
17861794
"""Associated Legendre function of the first kind for complex arguments.
17871795
@@ -1791,8 +1799,8 @@ def clpmn(m, n, z, type=3):
17911799
``Pmn'(z)`` for all orders from ``0..m`` and degrees from ``0..n``.
17921800
17931801
.. deprecated:: 1.15.0
1794-
This function is deprecated and will be removed in a future version.
1795-
Use `scipy.special.assoc_legendre_p_all` instead.
1802+
This function is deprecated and will be removed in SciPy 1.17.0.
1803+
Please use `scipy.special.assoc_legendre_p_all` instead.
17961804
17971805
Parameters
17981806
----------
@@ -2033,6 +2041,7 @@ def euler(n):
20332041
return _specfun.eulerb(n1)[:(n+1)]
20342042

20352043

2044+
@_deprecated(__DEPRECATION_MSG_1_15.format("lpn", "legendre_p_all"))
20362045
def lpn(n, z):
20372046
"""Legendre function of the first kind.
20382047
@@ -2042,8 +2051,8 @@ def lpn(n, z):
20422051
See also special.legendre for polynomial class.
20432052
20442053
.. deprecated:: 1.15.0
2045-
This function is deprecated and will be removed in a future version.
2046-
Use `scipy.special.legendre_p_all` instead.
2054+
This function is deprecated and will be removed in SciPy 1.17.0.
2055+
Please use `scipy.special.legendre_p_all` instead.
20472056
20482057
References
20492058
----------
@@ -3464,7 +3473,7 @@ def zeta(x, q=None, out=None):
34643473
``None``, complex inputs `x` are supported. If `q` is not ``None``,
34653474
then currently only real inputs `x` with ``x >= 1`` are supported,
34663475
even when ``q = 1.0`` (corresponding to the Riemann zeta function).
3467-
3476+
34683477
out : ndarray, optional
34693478
Output array for the computed values.
34703479
@@ -3530,7 +3539,7 @@ def zeta(x, q=None, out=None):
35303539
else:
35313540
return _ufuncs._zeta(x, q, out)
35323541

3533-
3542+
35343543
def softplus(x, **kwargs):
35353544
r"""
35363545
Compute the softplus function element-wise.
@@ -3554,7 +3563,7 @@ def softplus(x, **kwargs):
35543563
Examples
35553564
--------
35563565
>>> from scipy import special
3557-
3566+
35583567
>>> special.softplus(0)
35593568
0.6931471805599453
35603569

scipy/special/tests/test_basic.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from scipy.special import elliprc, elliprd, elliprf, elliprg, elliprj
4242
from scipy.special import softplus
4343
from scipy.special import mathieu_odd_coef, mathieu_even_coef, stirling2
44+
from scipy.special import lpn, lpmn, clpmn
4445
from scipy._lib._util import np_long, np_ulong
4546
from scipy._lib._array_api import xp_assert_close, xp_assert_equal, SCIPY_ARRAY_API
4647

@@ -4633,3 +4634,17 @@ def test_temme_rel_max_error(self):
46334634
denom = stirling2([n], k_entries, exact=True)
46344635
num = denom - stirling2([n], k_entries, exact=False)
46354636
assert np.max(np.abs(num / denom)) < 2e-5
4637+
4638+
4639+
class TestLegendreDeprecation:
4640+
4641+
def test_warn_lpn(self):
4642+
msg = "`scipy.special.lpn` is deprecated..."
4643+
with pytest.deprecated_call(match=msg):
4644+
_ = lpn(1, 0)
4645+
4646+
@pytest.mark.parametrize("xlpmn", [lpmn, clpmn])
4647+
def test_warn_xlpmn(self, xlpmn):
4648+
message = f"`scipy.special.{xlpmn.__name__}` is deprecated..."
4649+
with pytest.deprecated_call(match=message):
4650+
_ = xlpmn(1, 1, 0)

scipy/special/tests/test_data.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,17 @@ def legendre_p_via_assoc_(nu, x):
8484
return lpmv(0, nu, x)
8585

8686
def lpn_(n, x):
87-
return lpn(n.astype('l'), x)[0][-1]
87+
with suppress_warnings() as sup:
88+
sup.filter(category=DeprecationWarning)
89+
return lpn(n.astype('l'), x)[0][-1]
8890

8991
def lqn_(n, x):
9092
return lqn(n.astype('l'), x)[0][-1]
9193

9294
def legendre_p_via_lpmn(n, x):
93-
return lpmn(0, n, x)[0][0,-1]
95+
with suppress_warnings() as sup:
96+
sup.filter(category=DeprecationWarning)
97+
return lpmn(0, n, x)[0][0,-1]
9498

9599
def legendre_q_via_lqmn(n, x):
96100
return lqmn(0, n, x)[0][0,-1]

scipy/special/tests/test_legendre.py

Lines changed: 70 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66
from numpy.testing import (assert_equal, assert_almost_equal, assert_array_almost_equal,
7-
assert_allclose)
7+
assert_allclose, suppress_warnings)
88

99
from scipy import special
1010
from scipy.special import (legendre_p, legendre_p_all, assoc_legendre_p,
@@ -32,8 +32,11 @@ def test_legendre(self):
3232
@pytest.mark.parametrize('zi', [9.766818, 0.2999083, 8.24726, -22.84843,
3333
-0.8792666])
3434
def test_lpn_against_clpmn(self, n, zr, zi):
35-
reslpn = special.lpn(n, zr + zi*1j)
36-
resclpmn = special.clpmn(0, n, zr+zi*1j)
35+
with suppress_warnings() as sup:
36+
sup.filter(category=DeprecationWarning)
37+
reslpn = special.lpn(n, zr + zi*1j)
38+
resclpmn = special.clpmn(0, n, zr+zi*1j)
39+
3740
assert_allclose(reslpn[0], resclpmn[0][0])
3841
assert_allclose(reslpn[1], resclpmn[1][0])
3942

@@ -73,7 +76,10 @@ def test_all_ode(self, n_max, x_shape):
7376
np.testing.assert_allclose(err, 0, atol=1e-10)
7477

7578
def test_legacy(self):
76-
p, pd = special.lpn(2, 0.5)
79+
with suppress_warnings() as sup:
80+
sup.filter(category=DeprecationWarning)
81+
p, pd = special.lpn(2, 0.5)
82+
7783
assert_array_almost_equal(p, [1.00000, 0.50000, -0.12500], 4)
7884
assert_array_almost_equal(pd, [0.00000, 1.00000, 1.50000], 4)
7985

@@ -327,13 +333,16 @@ def test_legacy(self, m_max, n_max):
327333
x = 0.5
328334
p, p_jac = assoc_legendre_p_all(n_max, m_max, x, diff_n=1)
329335

330-
p_legacy, p_jac_legacy = special.lpmn(m_max, n_max, x)
331-
for m in range(m_max + 1):
332-
np.testing.assert_allclose(p_legacy[m], p[:, m])
336+
with suppress_warnings() as sup:
337+
sup.filter(category=DeprecationWarning)
333338

334-
p_legacy, p_jac_legacy = special.lpmn(-m_max, n_max, x)
335-
for m in range(m_max + 1):
336-
np.testing.assert_allclose(p_legacy[m], p[:, -m])
339+
p_legacy, p_jac_legacy = special.lpmn(m_max, n_max, x)
340+
for m in range(m_max + 1):
341+
np.testing.assert_allclose(p_legacy[m], p[:, m])
342+
343+
p_legacy, p_jac_legacy = special.lpmn(-m_max, n_max, x)
344+
for m in range(m_max + 1):
345+
np.testing.assert_allclose(p_legacy[m], p[:, -m])
337346

338347
class TestMultiAssocLegendreP:
339348
@pytest.mark.parametrize("shape", [(1000,), (4, 9), (3, 5, 7)])
@@ -388,7 +397,7 @@ def test_specific(self, shape, branch_cut, z_min, z_max, norm):
388397
assoc_legendre_p_2_m2(z, branch_cut=branch_cut, norm=norm))
389398
np.testing.assert_allclose(p[2, -1],
390399
assoc_legendre_p_2_m1(z, branch_cut=branch_cut, norm=norm))
391-
400+
392401
np.testing.assert_allclose(p[3, 0],
393402
assoc_legendre_p_3_0(z, branch_cut=branch_cut, norm=norm))
394403
np.testing.assert_allclose(p[3, 1],
@@ -678,7 +687,11 @@ def test_ode(self, shape):
678687
class TestLegendreFunctions:
679688
def test_clpmn(self):
680689
z = 0.5+0.3j
681-
clp = special.clpmn(2, 2, z, 3)
690+
691+
with suppress_warnings() as sup:
692+
sup.filter(category=DeprecationWarning)
693+
clp = special.clpmn(2, 2, z, 3)
694+
682695
assert_array_almost_equal(clp,
683696
(np.array([[1.0000, z, 0.5*(3*z*z-1)],
684697
[0.0000, np.sqrt(z*z-1), 3*z*np.sqrt(z*z-1)],
@@ -693,8 +706,12 @@ def test_clpmn_close_to_real_2(self):
693706
m = 1
694707
n = 3
695708
x = 0.5
696-
clp_plus = special.clpmn(m, n, x+1j*eps, 2)[0][m, n]
697-
clp_minus = special.clpmn(m, n, x-1j*eps, 2)[0][m, n]
709+
710+
with suppress_warnings() as sup:
711+
sup.filter(category=DeprecationWarning)
712+
clp_plus = special.clpmn(m, n, x+1j*eps, 2)[0][m, n]
713+
clp_minus = special.clpmn(m, n, x-1j*eps, 2)[0][m, n]
714+
698715
assert_array_almost_equal(np.array([clp_plus, clp_minus]),
699716
np.array([special.lpmv(m, n, x),
700717
special.lpmv(m, n, x)]),
@@ -705,8 +722,12 @@ def test_clpmn_close_to_real_3(self):
705722
m = 1
706723
n = 3
707724
x = 0.5
708-
clp_plus = special.clpmn(m, n, x+1j*eps, 3)[0][m, n]
709-
clp_minus = special.clpmn(m, n, x-1j*eps, 3)[0][m, n]
725+
726+
with suppress_warnings() as sup:
727+
sup.filter(category=DeprecationWarning)
728+
clp_plus = special.clpmn(m, n, x+1j*eps, 3)[0][m, n]
729+
clp_minus = special.clpmn(m, n, x-1j*eps, 3)[0][m, n]
730+
710731
assert_array_almost_equal(np.array([clp_plus, clp_minus]),
711732
np.array([special.lpmv(m, n, x)*np.exp(-0.5j*m*np.pi),
712733
special.lpmv(m, n, x)*np.exp(0.5j*m*np.pi)]),
@@ -717,33 +738,41 @@ def test_clpmn_across_unit_circle(self):
717738
m = 1
718739
n = 1
719740
x = 1j
720-
for type in [2, 3]:
721-
assert_almost_equal(special.clpmn(m, n, x+1j*eps, type)[0][m, n],
722-
special.clpmn(m, n, x-1j*eps, type)[0][m, n], 6)
741+
742+
with suppress_warnings() as sup:
743+
sup.filter(category=DeprecationWarning)
744+
for type in [2, 3]:
745+
assert_almost_equal(special.clpmn(m, n, x+1j*eps, type)[0][m, n],
746+
special.clpmn(m, n, x-1j*eps, type)[0][m, n], 6)
723747

724748
def test_inf(self):
725-
for z in (1, -1):
726-
for n in range(4):
727-
for m in range(1, n):
728-
lp = special.clpmn(m, n, z)
729-
assert np.isinf(lp[1][1,1:]).all()
730-
lp = special.lpmn(m, n, z)
731-
assert np.isinf(lp[1][1,1:]).all()
749+
with suppress_warnings() as sup:
750+
sup.filter(category=DeprecationWarning)
751+
for z in (1, -1):
752+
for n in range(4):
753+
for m in range(1, n):
754+
lp = special.clpmn(m, n, z)
755+
assert np.isinf(lp[1][1,1:]).all()
756+
lp = special.lpmn(m, n, z)
757+
assert np.isinf(lp[1][1,1:]).all()
732758

733759
def test_deriv_clpmn(self):
734760
# data inside and outside of the unit circle
735761
zvals = [0.5+0.5j, -0.5+0.5j, -0.5-0.5j, 0.5-0.5j,
736762
1+1j, -1+1j, -1-1j, 1-1j]
737763
m = 2
738764
n = 3
739-
for type in [2, 3]:
740-
for z in zvals:
741-
for h in [1e-3, 1e-3j]:
742-
approx_derivative = (special.clpmn(m, n, z+0.5*h, type)[0]
743-
- special.clpmn(m, n, z-0.5*h, type)[0])/h
744-
assert_allclose(special.clpmn(m, n, z, type)[1],
745-
approx_derivative,
746-
rtol=1e-4)
765+
766+
with suppress_warnings() as sup:
767+
sup.filter(category=DeprecationWarning)
768+
for type in [2, 3]:
769+
for z in zvals:
770+
for h in [1e-3, 1e-3j]:
771+
approx_derivative = (special.clpmn(m, n, z+0.5*h, type)[0]
772+
- special.clpmn(m, n, z-0.5*h, type)[0])/h
773+
assert_allclose(special.clpmn(m, n, z, type)[1],
774+
approx_derivative,
775+
rtol=1e-4)
747776

748777
"""
749778
@pytest.mark.parametrize("m_max", [3])
@@ -834,7 +863,9 @@ def test_array_inputs_lxn(self, function, n, z_complex, z_inexact, input_shape):
834863
if z_complex:
835864
z = 1j * z + 0.5j * z
836865

837-
P_z, P_d_z = function(n, z)
866+
with suppress_warnings() as sup:
867+
sup.filter(category=DeprecationWarning)
868+
P_z, P_d_z = function(n, z)
838869
assert P_z.shape == (n + 1, ) + input_shape
839870
assert P_d_z.shape == (n + 1, ) + input_shape
840871

@@ -877,7 +908,10 @@ def test_array_inputs_clxmn(self, function, m, n, input_shape):
877908
z = rng.uniform(-1, 1, size=input_shape)
878909
z = 1j * z + 0.5j * z
879910

880-
P_z, P_d_z = function(m, n, z)
911+
with suppress_warnings() as sup:
912+
sup.filter(category=DeprecationWarning)
913+
P_z, P_d_z = function(m, n, z)
914+
881915
assert P_z.shape == (m + 1, n + 1) + input_shape
882916
assert P_d_z.shape == (m + 1, n + 1) + input_shape
883917

scipy/special/tests/test_mpmath.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
"""
55
import numpy as np
6-
from numpy.testing import assert_, assert_allclose
6+
from numpy.testing import assert_, assert_allclose, suppress_warnings
77
from numpy import pi
88
import pytest
99
import itertools
@@ -1321,7 +1321,7 @@ def test_gamma_complex(self):
13211321
assert_mpmath_equal(
13221322
sc.gamma,
13231323
exception_to_nan(mpmath.gamma),
1324-
[ComplexArg()],
1324+
[ComplexArg()],
13251325
rtol=5e-13,
13261326
)
13271327

@@ -1659,7 +1659,9 @@ def test_legendre_int(self):
16591659
def test_legenp(self):
16601660
def lpnm(n, m, z):
16611661
try:
1662-
v = sc.lpmn(m, n, z)[0][-1,-1]
1662+
with suppress_warnings() as sup:
1663+
sup.filter(category=DeprecationWarning)
1664+
v = sc.lpmn(m, n, z)[0][-1,-1]
16631665
except ValueError:
16641666
return np.nan
16651667
if abs(v) > 1e306:
@@ -1710,7 +1712,9 @@ def legenp(n, m, z):
17101712
def test_legenp_complex_2(self):
17111713
def clpnm(n, m, z):
17121714
try:
1713-
return sc.clpmn(m.real, n.real, z, type=2)[0][-1,-1]
1715+
with suppress_warnings() as sup:
1716+
sup.filter(category=DeprecationWarning)
1717+
return sc.clpmn(m.real, n.real, z, type=2)[0][-1,-1]
17141718
except ValueError:
17151719
return np.nan
17161720

@@ -1738,7 +1742,9 @@ def legenp(n, m, z):
17381742
def test_legenp_complex_3(self):
17391743
def clpnm(n, m, z):
17401744
try:
1741-
return sc.clpmn(m.real, n.real, z, type=3)[0][-1,-1]
1745+
with suppress_warnings() as sup:
1746+
sup.filter(category=DeprecationWarning)
1747+
return sc.clpmn(m.real, n.real, z, type=3)[0][-1,-1]
17421748
except ValueError:
17431749
return np.nan
17441750

0 commit comments

Comments
 (0)