scipy 1.16.1__cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl → 1.16.2__cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. scipy/__config__.py +8 -8
  2. scipy/_cyutility.cpython-314-aarch64-linux-gnu.so +0 -0
  3. scipy/_lib/_ccallback_c.cpython-314-aarch64-linux-gnu.so +0 -0
  4. scipy/_lib/_test_deprecation_call.cpython-314-aarch64-linux-gnu.so +0 -0
  5. scipy/_lib/messagestream.cpython-314-aarch64-linux-gnu.so +0 -0
  6. scipy/cluster/_hierarchy.cpython-314-aarch64-linux-gnu.so +0 -0
  7. scipy/cluster/_optimal_leaf_ordering.cpython-314-aarch64-linux-gnu.so +0 -0
  8. scipy/cluster/_vq.cpython-314-aarch64-linux-gnu.so +0 -0
  9. scipy/fft/_pocketfft/pypocketfft.cpython-314-aarch64-linux-gnu.so +0 -0
  10. scipy/fftpack/convolve.cpython-314-aarch64-linux-gnu.so +0 -0
  11. scipy/integrate/_dop.cpython-314-aarch64-linux-gnu.so +0 -0
  12. scipy/integrate/_lsoda.cpython-314-aarch64-linux-gnu.so +0 -0
  13. scipy/integrate/_odepack.cpython-314-aarch64-linux-gnu.so +0 -0
  14. scipy/integrate/_test_odeint_banded.cpython-314-aarch64-linux-gnu.so +0 -0
  15. scipy/integrate/_vode.cpython-314-aarch64-linux-gnu.so +0 -0
  16. scipy/interpolate/_dfitpack.cpython-314-aarch64-linux-gnu.so +0 -0
  17. scipy/interpolate/_dierckx.cpython-314-aarch64-linux-gnu.so +0 -0
  18. scipy/interpolate/_interpnd.cpython-314-aarch64-linux-gnu.so +0 -0
  19. scipy/interpolate/_ppoly.cpython-314-aarch64-linux-gnu.so +0 -0
  20. scipy/interpolate/_rgi_cython.cpython-314-aarch64-linux-gnu.so +0 -0
  21. scipy/io/_fast_matrix_market/_fmm_core.cpython-314-aarch64-linux-gnu.so +0 -0
  22. scipy/io/_test_fortran.cpython-314-aarch64-linux-gnu.so +0 -0
  23. scipy/io/matlab/_mio5_utils.cpython-314-aarch64-linux-gnu.so +0 -0
  24. scipy/io/matlab/_mio_utils.cpython-314-aarch64-linux-gnu.so +0 -0
  25. scipy/io/matlab/_streams.cpython-314-aarch64-linux-gnu.so +0 -0
  26. scipy/linalg/_cythonized_array_utils.cpython-314-aarch64-linux-gnu.so +0 -0
  27. scipy/linalg/_decomp_interpolative.cpython-314-aarch64-linux-gnu.so +0 -0
  28. scipy/linalg/_decomp_lu_cython.cpython-314-aarch64-linux-gnu.so +0 -0
  29. scipy/linalg/_decomp_update.cpython-314-aarch64-linux-gnu.so +0 -0
  30. scipy/linalg/_fblas.cpython-314-aarch64-linux-gnu.so +0 -0
  31. scipy/linalg/_flapack.cpython-314-aarch64-linux-gnu.so +0 -0
  32. scipy/linalg/_matfuncs_expm.cpython-314-aarch64-linux-gnu.so +0 -0
  33. scipy/linalg/_matfuncs_schur_sqrtm.cpython-314-aarch64-linux-gnu.so +0 -0
  34. scipy/linalg/_matfuncs_sqrtm_triu.cpython-314-aarch64-linux-gnu.so +0 -0
  35. scipy/linalg/_solve_toeplitz.cpython-314-aarch64-linux-gnu.so +0 -0
  36. scipy/linalg/cython_blas.cpython-314-aarch64-linux-gnu.so +0 -0
  37. scipy/linalg/cython_lapack.cpython-314-aarch64-linux-gnu.so +0 -0
  38. scipy/linalg/tests/test_lapack.py +5 -1
  39. scipy/ndimage/_cytest.cpython-314-aarch64-linux-gnu.so +0 -0
  40. scipy/ndimage/_ni_label.cpython-314-aarch64-linux-gnu.so +0 -0
  41. scipy/odr/__odrpack.cpython-314-aarch64-linux-gnu.so +0 -0
  42. scipy/optimize/_bglu_dense.cpython-314-aarch64-linux-gnu.so +0 -0
  43. scipy/optimize/_highspy/_core.cpython-314-aarch64-linux-gnu.so +0 -0
  44. scipy/optimize/_highspy/_highs_options.cpython-314-aarch64-linux-gnu.so +0 -0
  45. scipy/optimize/_lbfgsb.cpython-314-aarch64-linux-gnu.so +0 -0
  46. scipy/optimize/_lbfgsb_py.py +2 -2
  47. scipy/optimize/_lsq/givens_elimination.cpython-314-aarch64-linux-gnu.so +0 -0
  48. scipy/optimize/_moduleTNC.cpython-314-aarch64-linux-gnu.so +0 -0
  49. scipy/optimize/_pava_pybind.cpython-314-aarch64-linux-gnu.so +0 -0
  50. scipy/optimize/_slsqp_py.py +5 -5
  51. scipy/optimize/_slsqplib.cpython-314-aarch64-linux-gnu.so +0 -0
  52. scipy/optimize/_trlib/_trlib.cpython-314-aarch64-linux-gnu.so +0 -0
  53. scipy/optimize/cython_optimize/_zeros.cpython-314-aarch64-linux-gnu.so +0 -0
  54. scipy/optimize/tests/test_optimize.py +9 -0
  55. scipy/signal/_peak_finding_utils.cpython-314-aarch64-linux-gnu.so +0 -0
  56. scipy/signal/_short_time_fft.py +74 -33
  57. scipy/signal/_sosfilt.cpython-314-aarch64-linux-gnu.so +0 -0
  58. scipy/signal/_spectral_py.py +2 -2
  59. scipy/signal/_upfirdn_apply.cpython-314-aarch64-linux-gnu.so +0 -0
  60. scipy/signal/tests/test_short_time_fft.py +9 -0
  61. scipy/signal/tests/test_signaltools.py +8 -2
  62. scipy/signal/tests/test_spectral.py +39 -1
  63. scipy/sparse/_csparsetools.cpython-314-aarch64-linux-gnu.so +0 -0
  64. scipy/sparse/csgraph/_flow.cpython-314-aarch64-linux-gnu.so +0 -0
  65. scipy/sparse/csgraph/_matching.cpython-314-aarch64-linux-gnu.so +0 -0
  66. scipy/sparse/csgraph/_min_spanning_tree.cpython-314-aarch64-linux-gnu.so +0 -0
  67. scipy/sparse/csgraph/_reordering.cpython-314-aarch64-linux-gnu.so +0 -0
  68. scipy/sparse/csgraph/_shortest_path.cpython-314-aarch64-linux-gnu.so +0 -0
  69. scipy/sparse/csgraph/_tools.cpython-314-aarch64-linux-gnu.so +0 -0
  70. scipy/sparse/csgraph/_traversal.cpython-314-aarch64-linux-gnu.so +0 -0
  71. scipy/sparse/linalg/_dsolve/_superlu.cpython-314-aarch64-linux-gnu.so +0 -0
  72. scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-314-aarch64-linux-gnu.so +0 -0
  73. scipy/sparse/linalg/_propack/_cpropack.cpython-314-aarch64-linux-gnu.so +0 -0
  74. scipy/sparse/linalg/_propack/_dpropack.cpython-314-aarch64-linux-gnu.so +0 -0
  75. scipy/sparse/linalg/_propack/_spropack.cpython-314-aarch64-linux-gnu.so +0 -0
  76. scipy/sparse/linalg/_propack/_zpropack.cpython-314-aarch64-linux-gnu.so +0 -0
  77. scipy/spatial/_ckdtree.cpython-314-aarch64-linux-gnu.so +0 -0
  78. scipy/spatial/_distance_pybind.cpython-314-aarch64-linux-gnu.so +0 -0
  79. scipy/spatial/_hausdorff.cpython-314-aarch64-linux-gnu.so +0 -0
  80. scipy/spatial/_qhull.cpython-314-aarch64-linux-gnu.so +0 -0
  81. scipy/spatial/_voronoi.cpython-314-aarch64-linux-gnu.so +0 -0
  82. scipy/spatial/qhull_src/COPYING_QHULL.txt +39 -0
  83. scipy/spatial/tests/test_distance.py +5 -4
  84. scipy/spatial/transform/_rigid_transform.cpython-314-aarch64-linux-gnu.so +0 -0
  85. scipy/spatial/transform/_rotation.cpython-314-aarch64-linux-gnu.so +0 -0
  86. scipy/special/_comb.cpython-314-aarch64-linux-gnu.so +0 -0
  87. scipy/special/_ellip_harm_2.cpython-314-aarch64-linux-gnu.so +0 -0
  88. scipy/special/_specfun.cpython-314-aarch64-linux-gnu.so +0 -0
  89. scipy/special/_test_internal.cpython-314-aarch64-linux-gnu.so +0 -0
  90. scipy/special/_ufuncs.cpython-314-aarch64-linux-gnu.so +0 -0
  91. scipy/special/_ufuncs_cxx.cpython-314-aarch64-linux-gnu.so +0 -0
  92. scipy/special/cython_special.cpython-314-aarch64-linux-gnu.so +0 -0
  93. scipy/stats/_ansari_swilk_statistics.cpython-314-aarch64-linux-gnu.so +0 -0
  94. scipy/stats/_biasedurn.cpython-314-aarch64-linux-gnu.so +0 -0
  95. scipy/stats/_levy_stable/levyst.cpython-314-aarch64-linux-gnu.so +0 -0
  96. scipy/stats/_qmc_cy.cpython-314-aarch64-linux-gnu.so +0 -0
  97. scipy/stats/_qmvnt_cy.cpython-314-aarch64-linux-gnu.so +0 -0
  98. scipy/stats/_rcont/rcont.cpython-314-aarch64-linux-gnu.so +0 -0
  99. scipy/stats/_sobol.cpython-314-aarch64-linux-gnu.so +0 -0
  100. scipy/stats/_stats.cpython-314-aarch64-linux-gnu.so +0 -0
  101. scipy/stats/_unuran/unuran_wrapper.cpython-314-aarch64-linux-gnu.so +0 -0
  102. scipy/version.py +2 -2
  103. {scipy-1.16.1.dist-info → scipy-1.16.2.dist-info}/METADATA +2 -2
  104. {scipy-1.16.1.dist-info → scipy-1.16.2.dist-info}/RECORD +107 -106
  105. scipy.libs/{libscipy_openblas-9778f98e.so → libscipy_openblas-d651f195.so} +0 -0
  106. {scipy-1.16.1.dist-info → scipy-1.16.2.dist-info}/LICENSE.txt +0 -0
  107. {scipy-1.16.1.dist-info → scipy-1.16.2.dist-info}/WHEEL +0 -0
scipy/__config__.py CHANGED
@@ -36,7 +36,7 @@ CONFIG = _cleanup(
36
36
  "cython": {
37
37
  "name": r"cython",
38
38
  "linker": r"cython",
39
- "version": r"3.1.2",
39
+ "version": r"3.1.3",
40
40
  "commands": r"cython",
41
41
  "args": r"",
42
42
  "linker args": r"",
@@ -59,7 +59,7 @@ CONFIG = _cleanup(
59
59
  },
60
60
  "pythran": {
61
61
  "version": r"0.18.0",
62
- "include directory": r"../../tmp/build-env-stjxhef6/lib/python3.14/site-packages/pythran"
62
+ "include directory": r"../../tmp/build-env-98ospmy4/lib/python3.14/site-packages/pythran"
63
63
  },
64
64
  },
65
65
  "Machine Information": {
@@ -81,32 +81,32 @@ CONFIG = _cleanup(
81
81
  "blas": {
82
82
  "name": "scipy-openblas",
83
83
  "found": bool("True".lower().replace('false', '')),
84
- "version": "0.3.28",
84
+ "version": "0.3.29.dev",
85
85
  "detection method": "pkgconfig",
86
86
  "include directory": r"/opt/_internal/cpython-3.14.0rc1/lib/python3.14/site-packages/scipy_openblas32/include",
87
87
  "lib directory": r"/opt/_internal/cpython-3.14.0rc1/lib/python3.14/site-packages/scipy_openblas32/lib",
88
- "openblas configuration": r"OpenBLAS 0.3.28 DYNAMIC_ARCH NO_AFFINITY neoversen2 MAX_THREADS=64",
88
+ "openblas configuration": r"OpenBLAS 0.3.29.dev DYNAMIC_ARCH NO_AFFINITY neoversev2 MAX_THREADS=64",
89
89
  "pc file directory": r"/project",
90
90
  },
91
91
  "lapack": {
92
92
  "name": "scipy-openblas",
93
93
  "found": bool("True".lower().replace('false', '')),
94
- "version": "0.3.28",
94
+ "version": "0.3.29.dev",
95
95
  "detection method": "pkgconfig",
96
96
  "include directory": r"/opt/_internal/cpython-3.14.0rc1/lib/python3.14/site-packages/scipy_openblas32/include",
97
97
  "lib directory": r"/opt/_internal/cpython-3.14.0rc1/lib/python3.14/site-packages/scipy_openblas32/lib",
98
- "openblas configuration": r"OpenBLAS 0.3.28 DYNAMIC_ARCH NO_AFFINITY neoversen2 MAX_THREADS=64",
98
+ "openblas configuration": r"OpenBLAS 0.3.29.dev DYNAMIC_ARCH NO_AFFINITY neoversev2 MAX_THREADS=64",
99
99
  "pc file directory": r"/project",
100
100
  },
101
101
  "pybind11": {
102
102
  "name": "pybind11",
103
- "version": "3.0.0",
103
+ "version": "3.0.1",
104
104
  "detection method": "config-tool",
105
105
  "include directory": r"unknown",
106
106
  },
107
107
  },
108
108
  "Python Information": {
109
- "path": r"/tmp/build-env-stjxhef6/bin/python",
109
+ "path": r"/tmp/build-env-98ospmy4/bin/python",
110
110
  "version": "3.14",
111
111
  },
112
112
  }
@@ -4,6 +4,7 @@
4
4
 
5
5
  from functools import reduce
6
6
  import random
7
+ import sysconfig
7
8
 
8
9
  from numpy.testing import (assert_equal, assert_array_almost_equal, assert_,
9
10
  assert_allclose, assert_almost_equal,
@@ -503,7 +504,7 @@ class TestDlasd4:
503
504
  roots.append(res[1])
504
505
 
505
506
  assert_(
506
- (res[3] <= 0),
507
+ (res[3] <= 0),
507
508
  f"LAPACK root finding dlasd4 failed to find the singular value {i}"
508
509
  )
509
510
  roots = np.array(roots)[::-1]
@@ -3495,6 +3496,9 @@ def test_sy_hetrs(mtype, dtype, lower):
3495
3496
  def test_sy_he_tri(dtype, lower, mtype):
3496
3497
  if mtype == 'he' and dtype in REAL_DTYPES:
3497
3498
  pytest.skip("hetri not for real dtypes.")
3499
+ if sysconfig.get_platform() == 'win-arm64' and dtype in COMPLEX_DTYPES:
3500
+ pytest.skip("Test segfaulting on win-arm64 in CI, see gh-23133")
3501
+
3498
3502
  rng = np.random.default_rng(1723059677121834)
3499
3503
  n = 20
3500
3504
  A = rng.random((n, n)) + rng.random((n, n))*1j
@@ -440,8 +440,8 @@ def _minimize_lbfgsb(fun, x0, args=(), jac=None, bounds=None,
440
440
  raise ValueError('maxls must be positive.')
441
441
 
442
442
  x = array(x0, dtype=np.float64)
443
- f = array(0.0, dtype=np.int32)
444
- g = zeros((n,), dtype=np.int32)
443
+ f = array(0.0, dtype=np.float64)
444
+ g = zeros((n,), dtype=np.float64)
445
445
  wa = zeros(2*m*n + 5*n + 11*m*m + 8*m, float64)
446
446
  iwa = zeros(3*n, dtype=np.int32)
447
447
  task = zeros(2, dtype=np.int32)
@@ -234,8 +234,8 @@ def _minimize_slsqp(func, x0, args=(), jac=None, bounds=None,
234
234
  disp : bool
235
235
  Set to True to print convergence messages. If False,
236
236
  `verbosity` is ignored and set to 0.
237
- maxiter : int
238
- Maximum number of iterations.
237
+ maxiter : int, optional
238
+ Maximum number of iterations. Default value is 100.
239
239
  finite_diff_rel_step : None or array_like, optional
240
240
  If ``jac in ['2-point', '3-point', 'cs']`` the relative step size to
241
241
  use for numerical approximation of `jac`. The absolute step
@@ -268,8 +268,8 @@ def _minimize_slsqp(func, x0, args=(), jac=None, bounds=None,
268
268
  attribute as a NumPy array. Denoting the dimension of the equality constraints
269
269
  with ``meq``, and of inequality constraints with ``mineq``, then the returned
270
270
  array slice ``m[:meq]`` contains the multipliers for the equality constraints,
271
- and the remaining ``m[meq:meq + mineq]`` contains the multipliers for the
272
- inequality constraints. The multipliers corresponding to bound inequalities
271
+ and the remaining ``m[meq:meq + mineq]`` contains the multipliers for the
272
+ inequality constraints. The multipliers corresponding to bound inequalities
273
273
  are not returned. See [1]_ pp. 321 or [2]_ for an explanation of how to interpret
274
274
  these multipliers. The internal QP problem is solved using the methods given
275
275
  in [3]_ Chapter 25.
@@ -461,7 +461,7 @@ def _minimize_slsqp(func, x0, args=(), jac=None, bounds=None,
461
461
  "inconsistent": 0,
462
462
  "reset": 0,
463
463
  "iter": 0,
464
- "itermax": maxiter,
464
+ "itermax": int(maxiter),
465
465
  "line": 0,
466
466
  "m": m,
467
467
  "meq": meq,
@@ -3167,6 +3167,15 @@ def test_bounds_with_list():
3167
3167
  )
3168
3168
 
3169
3169
 
3170
+ @pytest.mark.parametrize('method', (
3171
+ 'slsqp', 'cg', 'cobyqa', 'powell','nelder-mead', 'bfgs', 'l-bfgs-b',
3172
+ 'trust-constr'))
3173
+ def test_minimize_maxiter_noninteger(method):
3174
+ # Regression test for gh-23430
3175
+ x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])
3176
+ optimize.minimize(rosen, x0, method=method, options={'maxiter': 100.1})
3177
+
3178
+
3170
3179
  def test_x_overwritten_user_function():
3171
3180
  # if the user overwrites the x-array in the user function it's likely
3172
3181
  # that the minimizer stops working properly.
@@ -1,11 +1,7 @@
1
1
  """Implementation of an FFT-based Short-time Fourier Transform. """
2
2
 
3
- # Implementation Notes for this file (as of 2023-07)
3
+ # Implementation Notes for this file (as of 2025-08)
4
4
  # --------------------------------------------------
5
- # * MyPy version 1.1.1 does not seem to support decorated property methods
6
- # properly. Hence, applying ``@property`` to methods decorated with `@cache``
7
- # (as tried with the ``lower_border_end`` method) causes a mypy error when
8
- # accessing it as an index (e.g., ``SFT.lower_border_end[0]``).
9
5
  # * Since the method `stft` and `istft` have identical names as the legacy
10
6
  # functions in the signal module, referencing them as HTML link in the
11
7
  # docstrings has to be done by an explicit `~ShortTimeFFT.stft` instead of an
@@ -17,10 +13,9 @@
17
13
  # (currently 0.9). Consult Issue 18512 and PR 16660 for further details.
18
14
 
19
15
 
20
- # Provides typing union operator ``|`` in Python 3.9:
21
16
  # Linter does not allow to import ``Generator`` from ``typing`` module:
22
17
  from collections.abc import Generator, Callable
23
- from functools import cache, lru_cache, partial
18
+ from functools import partial, cached_property
24
19
  from typing import get_args, Literal
25
20
 
26
21
  import numpy as np
@@ -419,6 +414,15 @@ class ShortTimeFFT:
419
414
  _fac_mag: float | None = None
420
415
  _fac_psd: float | None = None
421
416
  _lower_border_end: tuple[int, int] | None = None
417
+ # The following tuples store parameter(s) and return value(s) of methods for caching
418
+ # (initialized with invalid parameters; should only be accessed by atomic
419
+ # read/writes to alleviate potential multithreading issues):
420
+ _cache_post_padding: tuple[int, tuple[int, int]] = -1, (0, 0)
421
+ _cache_upper_border_begin: tuple[int, tuple[int, int]] = -1, (0, 0)
422
+ _cache_t: tuple[tuple[int, int | None, int | None, int, float], np.ndarray] = \
423
+ (-1, None, None, 0, 0.), np.ndarray([])
424
+ _cache_f: tuple[tuple[FFT_MODE_TYPE, int, float], np.ndarray] = \
425
+ ('onesided', -1, 1.), np.ndarray([])
422
426
 
423
427
  def __init__(self, win: np.ndarray, hop: int, fs: float, *,
424
428
  fft_mode: FFT_MODE_TYPE = 'onesided',
@@ -1607,7 +1611,7 @@ class ShortTimeFFT:
1607
1611
  """
1608
1612
  return self.m_num // 2
1609
1613
 
1610
- @cache
1614
+ @cached_property
1611
1615
  def _pre_padding(self) -> tuple[int, int]:
1612
1616
  """Smallest signal index and slice index due to padding.
1613
1617
 
@@ -1617,13 +1621,12 @@ class ShortTimeFFT:
1617
1621
  w2 = self.win.real**2 + self.win.imag**2
1618
1622
  # move window to the left until the overlap with t >= 0 vanishes:
1619
1623
  n0 = -self.m_num_mid
1620
- for q_, n_ in enumerate(range(n0, n0-self.m_num-1, -self.hop)):
1624
+ for p_, n_ in enumerate(range(n0, n0-self.m_num-1, -self.hop)):
1621
1625
  n_next = n_ - self.hop
1622
1626
  if n_next + self.m_num <= 0 or all(w2[n_next:] == 0):
1623
- return n_, -q_
1624
- raise RuntimeError("This is code line should not have been reached!")
1625
- # If this case is reached, it probably means the first slice should be
1626
- # returned, i.e.: return n0, 0
1627
+ return n_, -p_
1628
+ # Make the linter happy:
1629
+ raise RuntimeError("This code line should never run! Please file a bug.")
1627
1630
 
1628
1631
  @property
1629
1632
  def k_min(self) -> int:
@@ -1646,7 +1649,7 @@ class ShortTimeFFT:
1646
1649
  upper_border_begin: Where post-padding effects start.
1647
1650
  ShortTimeFFT: Class this property belongs to.
1648
1651
  """
1649
- return self._pre_padding()[0]
1652
+ return self._pre_padding[0]
1650
1653
 
1651
1654
  @property
1652
1655
  def p_min(self) -> int:
@@ -1671,9 +1674,8 @@ class ShortTimeFFT:
1671
1674
  p_range: Determine and validate slice index range.
1672
1675
  ShortTimeFFT: Class this property belongs to.
1673
1676
  """
1674
- return self._pre_padding()[1]
1677
+ return self._pre_padding[1]
1675
1678
 
1676
- @lru_cache(maxsize=256)
1677
1679
  def _post_padding(self, n: int) -> tuple[int, int]:
1678
1680
  """Largest signal index and slice index due to padding.
1679
1681
 
@@ -1681,9 +1683,17 @@ class ShortTimeFFT:
1681
1683
  ----------
1682
1684
  n : int
1683
1685
  Number of samples of input signal (must be ≥ half of the window length).
1686
+
1687
+ Notes
1688
+ -----
1689
+ Note that the return values are cached together with the parameter `n` to avoid
1690
+ unnecessary recalculations.
1684
1691
  """
1685
1692
  if not (n >= (m2p := self.m_num - self.m_num_mid)):
1686
1693
  raise ValueError(f"Parameter n must be >= ceil(m_num/2) = {m2p}!")
1694
+ last_arg, last_return_value = self._cache_post_padding
1695
+ if n == last_arg: # use cached value:
1696
+ return last_return_value
1687
1697
  w2 = self.win.real**2 + self.win.imag**2
1688
1698
  # move window to the right until the overlap for t < t[n] vanishes:
1689
1699
  q1 = n // self.hop # last slice index with t[p1] <= t[n]
@@ -1691,15 +1701,17 @@ class ShortTimeFFT:
1691
1701
  for q_, k_ in enumerate(range(k1, n+self.m_num, self.hop), start=q1):
1692
1702
  n_next = k_ + self.hop
1693
1703
  if n_next >= n or all(w2[:n-n_next] == 0):
1694
- return k_ + self.m_num, q_ + 1
1695
- raise RuntimeError("This is code line should not have been reached!")
1704
+ return_value = k_ + self.m_num, q_ + 1
1705
+ self._cache_post_padding = n, return_value
1706
+ return return_value
1707
+ raise RuntimeError("This code line should never run! Please file a bug.")
1696
1708
  # If this case is reached, it probably means the last slice should be
1697
1709
  # returned, i.e.: return k1 + self.m_num - self.m_num_mid, q1 + 1
1698
1710
 
1699
1711
  def k_max(self, n: int) -> int:
1700
1712
  """First sample index after signal end not touched by a time slice.
1701
1713
 
1702
- `k_max` - 1 is the largest sample index of the slice `p_max` for a
1714
+ `k_max` - 1 is the largest sample index of the slice `p_max` - 1 for a
1703
1715
  given input signal of `n` samples.
1704
1716
  A detailed example is provided in the :ref:`tutorial_stft_sliding_win`
1705
1717
  section of the :ref:`user_guide`.
@@ -1785,7 +1797,6 @@ class ShortTimeFFT:
1785
1797
  upper_border_begin: Where post-padding effects start.
1786
1798
  ShortTimeFFT: Class this property belongs to.
1787
1799
  """
1788
- # not using @cache decorator due to MyPy limitations
1789
1800
  if self._lower_border_end is not None:
1790
1801
  return self._lower_border_end
1791
1802
 
@@ -1801,7 +1812,6 @@ class ShortTimeFFT:
1801
1812
  self._lower_border_end = (0, max(self.p_min, 0)) # ends at first slice
1802
1813
  return self._lower_border_end
1803
1814
 
1804
- @lru_cache(maxsize=256)
1805
1815
  def upper_border_begin(self, n: int) -> tuple[int, int]:
1806
1816
  """First signal index and first slice index affected by post-padding.
1807
1817
 
@@ -1823,6 +1833,11 @@ class ShortTimeFFT:
1823
1833
  p_ub : int
1824
1834
  Lowest index of time slice of which the end sticks out past the signal end.
1825
1835
 
1836
+ Notes
1837
+ -----
1838
+ Note that the return values are cached together with the parameter `n` to avoid
1839
+ unnecessary recalculations.
1840
+
1826
1841
  See Also
1827
1842
  --------
1828
1843
  k_min: The smallest possible signal index.
@@ -1836,6 +1851,9 @@ class ShortTimeFFT:
1836
1851
  """
1837
1852
  if not (n >= (m2p := self.m_num - self.m_num_mid)):
1838
1853
  raise ValueError(f"Parameter n must be >= ceil(m_num/2) = {m2p}!")
1854
+ last_arg, last_return_value = self._cache_upper_border_begin
1855
+ if n == last_arg: # use cached value:
1856
+ return last_return_value
1839
1857
  w2 = self.win.real**2 + self.win.imag**2
1840
1858
  q2 = n // self.hop + 1 # first t[q] >= t[n]
1841
1859
  q1 = max((n-self.m_num) // self.hop - 1, -1)
@@ -1843,8 +1861,11 @@ class ShortTimeFFT:
1843
1861
  for q_ in range(q2, q1, -1):
1844
1862
  k_ = q_ * self.hop + (self.m_num - self.m_num_mid)
1845
1863
  if k_ <= n or all(w2[n-k_:] == 0):
1846
- return (q_ + 1) * self.hop - self.m_num_mid, q_ + 1
1847
- return 0, 0 # border starts at first slice
1864
+ return_value = (q_ + 1) * self.hop - self.m_num_mid, q_ + 1
1865
+ self. _cache_upper_border_begin = n, return_value
1866
+ return return_value
1867
+ # make linter happy:
1868
+ raise RuntimeError("This code line should never run! Please file a bug.")
1848
1869
 
1849
1870
  @property
1850
1871
  def delta_t(self) -> float:
@@ -1912,7 +1933,6 @@ class ShortTimeFFT:
1912
1933
  f"does not hold for signal length {n=}!")
1913
1934
  return p0_, p1_
1914
1935
 
1915
- @lru_cache(maxsize=1)
1916
1936
  def t(self, n: int, p0: int | None = None, p1: int | None = None,
1917
1937
  k_offset: int = 0) -> np.ndarray:
1918
1938
  """Times of STFT for an input signal with `n` samples.
@@ -1934,6 +1954,10 @@ class ShortTimeFFT:
1934
1954
  k_offset
1935
1955
  Index of first sample (t = 0) in `x`.
1936
1956
 
1957
+ Notes
1958
+ -----
1959
+ Note that the returned array is cached together with the method's call
1960
+ parameters to avoid unnecessary recalculations.
1937
1961
 
1938
1962
  See Also
1939
1963
  --------
@@ -1944,8 +1968,18 @@ class ShortTimeFFT:
1944
1968
  fs: Sampling frequency (being ``1/T``)
1945
1969
  ShortTimeFFT: Class this method belongs to.
1946
1970
  """
1971
+ if not (n > 0 and isinstance(n, int | np.integer)):
1972
+ raise ValueError(f"Parameter {n=} is not a positive integer!")
1973
+ args = n, p0, p1, k_offset, self.T # since `self.T` is mutable, it's needed too
1974
+ last_args, last_return_value = self._cache_t
1975
+ if args == last_args: # use cached value:
1976
+ return last_return_value
1977
+
1947
1978
  p0, p1 = self.p_range(n, p0, p1)
1948
- return np.arange(p0, p1) * self.delta_t + k_offset * self.T
1979
+ return_value = np.arange(p0, p1) * self.delta_t + k_offset * self.T
1980
+
1981
+ self._cache_t = args, return_value
1982
+ return return_value
1949
1983
 
1950
1984
  def nearest_k_p(self, k: int, left: bool = True) -> int:
1951
1985
  """Return nearest sample index k_p for which t[k_p] == t[p] holds.
@@ -2022,6 +2056,7 @@ class ShortTimeFFT:
2022
2056
  """Frequencies values of the STFT.
2023
2057
 
2024
2058
  A 1d array of length `f_pts` with `delta_f` spaced entries is returned.
2059
+ This array is calculated lazily.
2025
2060
 
2026
2061
  See Also
2027
2062
  --------
@@ -2030,15 +2065,22 @@ class ShortTimeFFT:
2030
2065
  mfft: Length of the input for FFT used.
2031
2066
  ShortTimeFFT: Class this property belongs to.
2032
2067
  """
2068
+ last_state, last_return_value = self._cache_f
2069
+ current_state = self.fft_mode, self.mfft, self.T
2070
+ if current_state == last_state: # use cached value:
2071
+ return last_return_value
2072
+
2033
2073
  if self.fft_mode in {'onesided', 'onesided2X'}:
2034
- return fft_lib.rfftfreq(self.mfft, self.T)
2074
+ return_value = fft_lib.rfftfreq(self.mfft, self.T)
2035
2075
  elif self.fft_mode == 'twosided':
2036
- return fft_lib.fftfreq(self.mfft, self.T)
2076
+ return_value = fft_lib.fftfreq(self.mfft, self.T)
2037
2077
  elif self.fft_mode == 'centered':
2038
- return fft_lib.fftshift(fft_lib.fftfreq(self.mfft, self.T))
2039
- # This should never happen but makes the Linters happy:
2040
- fft_modes = get_args(FFT_MODE_TYPE)
2041
- raise RuntimeError(f"{self.fft_mode=} not in {fft_modes}!")
2078
+ return_value = fft_lib.fftshift(fft_lib.fftfreq(self.mfft, self.T))
2079
+ else: # This should never happen but makes the Linters happy:
2080
+ fft_modes = get_args(FFT_MODE_TYPE)
2081
+ raise RuntimeError(f"{self.fft_mode=} not in {fft_modes}!")
2082
+ self._cache_f = current_state, return_value
2083
+ return return_value
2042
2084
 
2043
2085
  def _fft_func(self, x: np.ndarray) -> np.ndarray:
2044
2086
  """FFT based on the `fft_mode`, `mfft`, `scaling` and `phase_shift`
@@ -2095,8 +2137,7 @@ class ShortTimeFFT:
2095
2137
  Xc[..., 1:q1] /= fac
2096
2138
  x = fft_lib.irfft(Xc, n=self.mfft, axis=-1)
2097
2139
  else: # This should never happen but makes the Linter happy:
2098
- error_str = f"{self.fft_mode=} not in {get_args(FFT_MODE_TYPE)}!"
2099
- raise RuntimeError(error_str)
2140
+ raise RuntimeError(f"{self.fft_mode=} not in {get_args(FFT_MODE_TYPE)}!")
2100
2141
 
2101
2142
  if self.phase_shift is None:
2102
2143
  return x[..., :self.m_num]
@@ -243,11 +243,11 @@ def lombscargle(
243
243
  )
244
244
 
245
245
  # weight vector must sum to 1
246
- weights *= 1.0 / weights.sum()
246
+ weights = weights * (1.0 / weights.sum())
247
247
 
248
248
  # if requested, perform precenter
249
249
  if precenter:
250
- y -= y.mean()
250
+ y = y - y.mean()
251
251
 
252
252
  # transform arrays
253
253
  # row vector
@@ -531,11 +531,15 @@ def test_border_values():
531
531
  assert SFT.p_max(10) == 4
532
532
  assert SFT.k_max(10) == 16
533
533
  assert SFT.upper_border_begin(10) == (4, 2)
534
+ assert SFT.upper_border_begin(10) == (4, 2) # needed to test caching
534
535
  # Raise exceptions:
535
536
  with pytest.raises(ValueError, match="^Parameter n must be"):
536
537
  SFT.upper_border_begin(3)
537
538
  with pytest.raises(ValueError, match="^Parameter n must be"):
538
539
  SFT._post_padding(3)
540
+ with pytest.raises(RuntimeError):
541
+ SFT._hop = -1 # illegal hop interval
542
+ SFT.upper_border_begin(8)
539
543
 
540
544
  def test_border_values_exotic():
541
545
  """Ensure that the border calculations are correct for windows with
@@ -573,6 +577,11 @@ def test_t():
573
577
  SFT.fs = 1/8
574
578
  assert SFT.fs == 1/8
575
579
  assert SFT.T == 8
580
+ with pytest.raises(ValueError):
581
+ # noinspection PyTypeChecker
582
+ SFT.t(1.5) # only integers allowed
583
+ with pytest.raises(ValueError):
584
+ SFT.t(-1) # only positive `n` allowed
576
585
 
577
586
 
578
587
  @pytest.mark.parametrize('fft_mode, f',
@@ -2084,7 +2084,9 @@ class _TestLinearFilter:
2084
2084
  a = self.convert_dtype(a, xp)
2085
2085
  x = self.convert_dtype(x, xp)
2086
2086
  zi = self.convert_dtype(zi, xp)
2087
- assert_raises(ValueError, lfilter, b, a, x, axis, zi)
2087
+ # NOTE: MemoryError is currently allowed below because of:
2088
+ # https://github.com/numpy/numpy/issues/29721
2089
+ assert_raises((ValueError, MemoryError), lfilter, b, a, x, axis, zi)
2088
2090
 
2089
2091
  @skip_xp_backends('cupy', reason='cupy does not raise')
2090
2092
  def test_bad_size_zi(self, xp):
@@ -2134,7 +2136,11 @@ class _TestLinearFilter:
2134
2136
  self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[0, 1, 2, 3], [4, 5, 6, 7]], xp)
2135
2137
 
2136
2138
  self.base_bad_size_zi([1], [1, 1], x2, 0, [0, 1, 2], xp)
2137
- self.base_bad_size_zi([1], [1, 1], x2, 0, [[[0, 1, 2]]], xp)
2139
+ # this case is disabled on the release branch
2140
+ # because of:
2141
+ # https://github.com/scipy/scipy/pull/23543#issuecomment-3276286172
2142
+ # https://github.com/numpy/numpy/issues/29721
2143
+ #self.base_bad_size_zi([1], [1, 1], x2, 0, [[[0, 1, 2]]], xp)
2138
2144
  self.base_bad_size_zi([1], [1, 1], x2, 0, [[0], [1], [2]], xp)
2139
2145
  self.base_bad_size_zi([1], [1, 1], x2, 0, [[0, 1]], xp)
2140
2146
  self.base_bad_size_zi([1], [1, 1], x2, 0, [[0, 1, 2, 3]], xp)