scipy 1.15.2__cp312-cp312-macosx_14_0_arm64.whl → 1.16.0__cp312-cp312-macosx_14_0_arm64.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 (642) hide show
  1. scipy/.dylibs/libgcc_s.1.1.dylib +0 -0
  2. scipy/.dylibs/libgfortran.5.dylib +0 -0
  3. scipy/.dylibs/libquadmath.0.dylib +0 -0
  4. scipy/__config__.py +5 -5
  5. scipy/__init__.py +3 -6
  6. scipy/_cyutility.cpython-312-darwin.so +0 -0
  7. scipy/_lib/_array_api.py +497 -161
  8. scipy/_lib/_array_api_compat_vendor.py +9 -0
  9. scipy/_lib/_bunch.py +4 -0
  10. scipy/_lib/_ccallback_c.cpython-312-darwin.so +0 -0
  11. scipy/_lib/_docscrape.py +1 -1
  12. scipy/_lib/_elementwise_iterative_method.py +15 -26
  13. scipy/_lib/_sparse.py +41 -0
  14. scipy/_lib/_test_ccallback.cpython-312-darwin.so +0 -0
  15. scipy/_lib/_test_deprecation_call.cpython-312-darwin.so +0 -0
  16. scipy/_lib/_test_deprecation_def.cpython-312-darwin.so +0 -0
  17. scipy/_lib/_testutils.py +6 -2
  18. scipy/_lib/_util.py +222 -125
  19. scipy/_lib/array_api_compat/__init__.py +4 -4
  20. scipy/_lib/array_api_compat/_internal.py +19 -6
  21. scipy/_lib/array_api_compat/common/__init__.py +1 -1
  22. scipy/_lib/array_api_compat/common/_aliases.py +365 -193
  23. scipy/_lib/array_api_compat/common/_fft.py +94 -64
  24. scipy/_lib/array_api_compat/common/_helpers.py +413 -180
  25. scipy/_lib/array_api_compat/common/_linalg.py +116 -40
  26. scipy/_lib/array_api_compat/common/_typing.py +179 -10
  27. scipy/_lib/array_api_compat/cupy/__init__.py +1 -4
  28. scipy/_lib/array_api_compat/cupy/_aliases.py +61 -41
  29. scipy/_lib/array_api_compat/cupy/_info.py +16 -6
  30. scipy/_lib/array_api_compat/cupy/_typing.py +24 -39
  31. scipy/_lib/array_api_compat/dask/array/__init__.py +6 -3
  32. scipy/_lib/array_api_compat/dask/array/_aliases.py +267 -108
  33. scipy/_lib/array_api_compat/dask/array/_info.py +105 -34
  34. scipy/_lib/array_api_compat/dask/array/fft.py +5 -8
  35. scipy/_lib/array_api_compat/dask/array/linalg.py +21 -22
  36. scipy/_lib/array_api_compat/numpy/__init__.py +13 -15
  37. scipy/_lib/array_api_compat/numpy/_aliases.py +98 -49
  38. scipy/_lib/array_api_compat/numpy/_info.py +36 -16
  39. scipy/_lib/array_api_compat/numpy/_typing.py +27 -43
  40. scipy/_lib/array_api_compat/numpy/fft.py +11 -5
  41. scipy/_lib/array_api_compat/numpy/linalg.py +75 -22
  42. scipy/_lib/array_api_compat/torch/__init__.py +3 -5
  43. scipy/_lib/array_api_compat/torch/_aliases.py +262 -159
  44. scipy/_lib/array_api_compat/torch/_info.py +27 -16
  45. scipy/_lib/array_api_compat/torch/_typing.py +3 -0
  46. scipy/_lib/array_api_compat/torch/fft.py +17 -18
  47. scipy/_lib/array_api_compat/torch/linalg.py +16 -16
  48. scipy/_lib/array_api_extra/__init__.py +26 -3
  49. scipy/_lib/array_api_extra/_delegation.py +171 -0
  50. scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
  51. scipy/_lib/array_api_extra/_lib/_at.py +463 -0
  52. scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
  53. scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
  54. scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
  55. scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
  56. scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
  57. scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
  58. scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
  59. scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
  60. scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
  61. scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
  62. scipy/_lib/array_api_extra/testing.py +359 -0
  63. scipy/_lib/decorator.py +2 -2
  64. scipy/_lib/doccer.py +1 -7
  65. scipy/_lib/messagestream.cpython-312-darwin.so +0 -0
  66. scipy/_lib/pyprima/__init__.py +212 -0
  67. scipy/_lib/pyprima/cobyla/__init__.py +0 -0
  68. scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
  69. scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
  70. scipy/_lib/pyprima/cobyla/geometry.py +226 -0
  71. scipy/_lib/pyprima/cobyla/initialize.py +215 -0
  72. scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
  73. scipy/_lib/pyprima/cobyla/update.py +289 -0
  74. scipy/_lib/pyprima/common/__init__.py +0 -0
  75. scipy/_lib/pyprima/common/_bounds.py +34 -0
  76. scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
  77. scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
  78. scipy/_lib/pyprima/common/_project.py +173 -0
  79. scipy/_lib/pyprima/common/checkbreak.py +93 -0
  80. scipy/_lib/pyprima/common/consts.py +47 -0
  81. scipy/_lib/pyprima/common/evaluate.py +99 -0
  82. scipy/_lib/pyprima/common/history.py +38 -0
  83. scipy/_lib/pyprima/common/infos.py +30 -0
  84. scipy/_lib/pyprima/common/linalg.py +435 -0
  85. scipy/_lib/pyprima/common/message.py +290 -0
  86. scipy/_lib/pyprima/common/powalg.py +131 -0
  87. scipy/_lib/pyprima/common/preproc.py +277 -0
  88. scipy/_lib/pyprima/common/present.py +5 -0
  89. scipy/_lib/pyprima/common/ratio.py +54 -0
  90. scipy/_lib/pyprima/common/redrho.py +47 -0
  91. scipy/_lib/pyprima/common/selectx.py +296 -0
  92. scipy/_lib/tests/test__util.py +105 -121
  93. scipy/_lib/tests/test_array_api.py +169 -34
  94. scipy/_lib/tests/test_bunch.py +7 -0
  95. scipy/_lib/tests/test_ccallback.py +2 -10
  96. scipy/_lib/tests/test_public_api.py +13 -0
  97. scipy/cluster/_hierarchy.cpython-312-darwin.so +0 -0
  98. scipy/cluster/_optimal_leaf_ordering.cpython-312-darwin.so +0 -0
  99. scipy/cluster/_vq.cpython-312-darwin.so +0 -0
  100. scipy/cluster/hierarchy.py +393 -223
  101. scipy/cluster/tests/test_hierarchy.py +273 -335
  102. scipy/cluster/tests/test_vq.py +45 -61
  103. scipy/cluster/vq.py +39 -35
  104. scipy/conftest.py +282 -151
  105. scipy/constants/_constants.py +4 -1
  106. scipy/constants/tests/test_codata.py +2 -2
  107. scipy/constants/tests/test_constants.py +11 -18
  108. scipy/datasets/_download_all.py +15 -1
  109. scipy/datasets/_fetchers.py +7 -1
  110. scipy/datasets/_utils.py +1 -1
  111. scipy/differentiate/_differentiate.py +25 -25
  112. scipy/differentiate/tests/test_differentiate.py +24 -25
  113. scipy/fft/_basic.py +20 -0
  114. scipy/fft/_helper.py +3 -34
  115. scipy/fft/_pocketfft/helper.py +29 -1
  116. scipy/fft/_pocketfft/tests/test_basic.py +2 -4
  117. scipy/fft/_pocketfft/tests/test_real_transforms.py +4 -4
  118. scipy/fft/_realtransforms.py +13 -0
  119. scipy/fft/tests/test_basic.py +27 -25
  120. scipy/fft/tests/test_fftlog.py +16 -7
  121. scipy/fft/tests/test_helper.py +18 -34
  122. scipy/fft/tests/test_real_transforms.py +8 -10
  123. scipy/fftpack/convolve.cpython-312-darwin.so +0 -0
  124. scipy/fftpack/tests/test_basic.py +2 -4
  125. scipy/fftpack/tests/test_real_transforms.py +8 -9
  126. scipy/integrate/_bvp.py +9 -3
  127. scipy/integrate/_cubature.py +3 -2
  128. scipy/integrate/_dop.cpython-312-darwin.so +0 -0
  129. scipy/integrate/_ivp/common.py +3 -3
  130. scipy/integrate/_ivp/ivp.py +9 -2
  131. scipy/integrate/_ivp/tests/test_ivp.py +19 -0
  132. scipy/integrate/_lsoda.cpython-312-darwin.so +0 -0
  133. scipy/integrate/_ode.py +9 -2
  134. scipy/integrate/_odepack.cpython-312-darwin.so +0 -0
  135. scipy/integrate/_quad_vec.py +21 -29
  136. scipy/integrate/_quadpack.cpython-312-darwin.so +0 -0
  137. scipy/integrate/_quadpack_py.py +11 -7
  138. scipy/integrate/_quadrature.py +3 -3
  139. scipy/integrate/_rules/_base.py +2 -2
  140. scipy/integrate/_tanhsinh.py +57 -54
  141. scipy/integrate/_test_odeint_banded.cpython-312-darwin.so +0 -0
  142. scipy/integrate/_vode.cpython-312-darwin.so +0 -0
  143. scipy/integrate/tests/test__quad_vec.py +0 -6
  144. scipy/integrate/tests/test_banded_ode_solvers.py +85 -0
  145. scipy/integrate/tests/test_cubature.py +21 -35
  146. scipy/integrate/tests/test_quadrature.py +6 -8
  147. scipy/integrate/tests/test_tanhsinh.py +61 -43
  148. scipy/interpolate/__init__.py +70 -58
  149. scipy/interpolate/_bary_rational.py +22 -22
  150. scipy/interpolate/_bsplines.py +119 -66
  151. scipy/interpolate/_cubic.py +65 -50
  152. scipy/interpolate/_dfitpack.cpython-312-darwin.so +0 -0
  153. scipy/interpolate/_dierckx.cpython-312-darwin.so +0 -0
  154. scipy/interpolate/_fitpack.cpython-312-darwin.so +0 -0
  155. scipy/interpolate/_fitpack2.py +9 -6
  156. scipy/interpolate/_fitpack_impl.py +32 -26
  157. scipy/interpolate/_fitpack_repro.py +23 -19
  158. scipy/interpolate/_interpnd.cpython-312-darwin.so +0 -0
  159. scipy/interpolate/_interpolate.py +30 -12
  160. scipy/interpolate/_ndbspline.py +13 -18
  161. scipy/interpolate/_ndgriddata.py +5 -8
  162. scipy/interpolate/_polyint.py +95 -31
  163. scipy/interpolate/_ppoly.cpython-312-darwin.so +0 -0
  164. scipy/interpolate/_rbf.py +2 -2
  165. scipy/interpolate/_rbfinterp.py +1 -1
  166. scipy/interpolate/_rbfinterp_pythran.cpython-312-darwin.so +0 -0
  167. scipy/interpolate/_rgi.py +31 -26
  168. scipy/interpolate/_rgi_cython.cpython-312-darwin.so +0 -0
  169. scipy/interpolate/dfitpack.py +0 -20
  170. scipy/interpolate/interpnd.py +1 -2
  171. scipy/interpolate/tests/test_bary_rational.py +2 -2
  172. scipy/interpolate/tests/test_bsplines.py +97 -1
  173. scipy/interpolate/tests/test_fitpack2.py +39 -1
  174. scipy/interpolate/tests/test_interpnd.py +32 -20
  175. scipy/interpolate/tests/test_interpolate.py +48 -4
  176. scipy/interpolate/tests/test_rgi.py +2 -1
  177. scipy/io/_fast_matrix_market/__init__.py +2 -0
  178. scipy/io/_harwell_boeing/_fortran_format_parser.py +19 -16
  179. scipy/io/_harwell_boeing/hb.py +7 -11
  180. scipy/io/_idl.py +5 -7
  181. scipy/io/_netcdf.py +15 -5
  182. scipy/io/_test_fortran.cpython-312-darwin.so +0 -0
  183. scipy/io/arff/tests/test_arffread.py +3 -3
  184. scipy/io/matlab/__init__.py +5 -3
  185. scipy/io/matlab/_mio.py +4 -1
  186. scipy/io/matlab/_mio5.py +19 -13
  187. scipy/io/matlab/_mio5_utils.cpython-312-darwin.so +0 -0
  188. scipy/io/matlab/_mio_utils.cpython-312-darwin.so +0 -0
  189. scipy/io/matlab/_miobase.py +4 -1
  190. scipy/io/matlab/_streams.cpython-312-darwin.so +0 -0
  191. scipy/io/matlab/tests/test_mio.py +46 -18
  192. scipy/io/matlab/tests/test_mio_funcs.py +1 -1
  193. scipy/io/tests/test_mmio.py +7 -1
  194. scipy/io/tests/test_wavfile.py +41 -0
  195. scipy/io/wavfile.py +57 -10
  196. scipy/linalg/_basic.py +113 -86
  197. scipy/linalg/_cythonized_array_utils.cpython-312-darwin.so +0 -0
  198. scipy/linalg/_decomp.py +22 -9
  199. scipy/linalg/_decomp_cholesky.py +28 -13
  200. scipy/linalg/_decomp_cossin.py +45 -30
  201. scipy/linalg/_decomp_interpolative.cpython-312-darwin.so +0 -0
  202. scipy/linalg/_decomp_ldl.py +4 -1
  203. scipy/linalg/_decomp_lu.py +18 -6
  204. scipy/linalg/_decomp_lu_cython.cpython-312-darwin.so +0 -0
  205. scipy/linalg/_decomp_polar.py +2 -0
  206. scipy/linalg/_decomp_qr.py +6 -2
  207. scipy/linalg/_decomp_qz.py +3 -0
  208. scipy/linalg/_decomp_schur.py +3 -1
  209. scipy/linalg/_decomp_svd.py +13 -2
  210. scipy/linalg/_decomp_update.cpython-312-darwin.so +0 -0
  211. scipy/linalg/_expm_frechet.py +4 -0
  212. scipy/linalg/_fblas.cpython-312-darwin.so +0 -0
  213. scipy/linalg/_flapack.cpython-312-darwin.so +0 -0
  214. scipy/linalg/_linalg_pythran.cpython-312-darwin.so +0 -0
  215. scipy/linalg/_matfuncs.py +187 -4
  216. scipy/linalg/_matfuncs_expm.cpython-312-darwin.so +0 -0
  217. scipy/linalg/_matfuncs_schur_sqrtm.cpython-312-darwin.so +0 -0
  218. scipy/linalg/_matfuncs_sqrtm.py +1 -99
  219. scipy/linalg/_matfuncs_sqrtm_triu.cpython-312-darwin.so +0 -0
  220. scipy/linalg/_procrustes.py +2 -0
  221. scipy/linalg/_sketches.py +17 -6
  222. scipy/linalg/_solve_toeplitz.cpython-312-darwin.so +0 -0
  223. scipy/linalg/_solvers.py +7 -2
  224. scipy/linalg/_special_matrices.py +26 -36
  225. scipy/linalg/blas.py +35 -24
  226. scipy/linalg/cython_blas.cpython-312-darwin.so +0 -0
  227. scipy/linalg/cython_lapack.cpython-312-darwin.so +0 -0
  228. scipy/linalg/lapack.py +22 -2
  229. scipy/linalg/tests/_cython_examples/meson.build +7 -0
  230. scipy/linalg/tests/test_basic.py +31 -16
  231. scipy/linalg/tests/test_batch.py +588 -0
  232. scipy/linalg/tests/test_cythonized_array_utils.py +0 -2
  233. scipy/linalg/tests/test_decomp.py +40 -3
  234. scipy/linalg/tests/test_decomp_cossin.py +14 -0
  235. scipy/linalg/tests/test_decomp_ldl.py +1 -1
  236. scipy/linalg/tests/test_interpolative.py +17 -0
  237. scipy/linalg/tests/test_lapack.py +115 -7
  238. scipy/linalg/tests/test_matfuncs.py +157 -102
  239. scipy/linalg/tests/test_procrustes.py +0 -7
  240. scipy/linalg/tests/test_solve_toeplitz.py +1 -1
  241. scipy/linalg/tests/test_special_matrices.py +1 -5
  242. scipy/ndimage/__init__.py +1 -0
  243. scipy/ndimage/_cytest.cpython-312-darwin.so +0 -0
  244. scipy/ndimage/_delegators.py +8 -2
  245. scipy/ndimage/_filters.py +453 -5
  246. scipy/ndimage/_interpolation.py +36 -6
  247. scipy/ndimage/_measurements.py +4 -2
  248. scipy/ndimage/_morphology.py +5 -0
  249. scipy/ndimage/_nd_image.cpython-312-darwin.so +0 -0
  250. scipy/ndimage/_ndimage_api.py +2 -1
  251. scipy/ndimage/_ni_docstrings.py +5 -1
  252. scipy/ndimage/_ni_label.cpython-312-darwin.so +0 -0
  253. scipy/ndimage/_ni_support.py +1 -5
  254. scipy/ndimage/_rank_filter_1d.cpython-312-darwin.so +0 -0
  255. scipy/ndimage/_support_alternative_backends.py +18 -6
  256. scipy/ndimage/tests/test_filters.py +384 -259
  257. scipy/ndimage/tests/test_fourier.py +7 -9
  258. scipy/ndimage/tests/test_interpolation.py +68 -61
  259. scipy/ndimage/tests/test_measurements.py +18 -35
  260. scipy/ndimage/tests/test_morphology.py +143 -131
  261. scipy/ndimage/tests/test_splines.py +1 -3
  262. scipy/odr/__odrpack.cpython-312-darwin.so +0 -0
  263. scipy/optimize/_basinhopping.py +13 -7
  264. scipy/optimize/_bglu_dense.cpython-312-darwin.so +0 -0
  265. scipy/optimize/_bracket.py +46 -26
  266. scipy/optimize/_chandrupatla.py +9 -10
  267. scipy/optimize/_cobyla_py.py +104 -123
  268. scipy/optimize/_constraints.py +14 -10
  269. scipy/optimize/_differentiable_functions.py +371 -230
  270. scipy/optimize/_differentialevolution.py +4 -3
  271. scipy/optimize/_direct.cpython-312-darwin.so +0 -0
  272. scipy/optimize/_dual_annealing.py +1 -1
  273. scipy/optimize/_elementwise.py +1 -4
  274. scipy/optimize/_group_columns.cpython-312-darwin.so +0 -0
  275. scipy/optimize/_highspy/_highs_wrapper.py +6 -4
  276. scipy/optimize/_lbfgsb.cpython-312-darwin.so +0 -0
  277. scipy/optimize/_lbfgsb_py.py +80 -24
  278. scipy/optimize/_linprog_doc.py +2 -2
  279. scipy/optimize/_linprog_highs.py +11 -11
  280. scipy/optimize/_linprog_ip.py +25 -10
  281. scipy/optimize/_linprog_util.py +18 -19
  282. scipy/optimize/_lsap.cpython-312-darwin.so +0 -0
  283. scipy/optimize/_lsq/common.py +3 -3
  284. scipy/optimize/_lsq/dogbox.py +16 -2
  285. scipy/optimize/_lsq/givens_elimination.cpython-312-darwin.so +0 -0
  286. scipy/optimize/_lsq/least_squares.py +198 -126
  287. scipy/optimize/_lsq/lsq_linear.py +6 -6
  288. scipy/optimize/_lsq/trf.py +35 -8
  289. scipy/optimize/_milp.py +3 -1
  290. scipy/optimize/_minimize.py +105 -36
  291. scipy/optimize/_minpack.cpython-312-darwin.so +0 -0
  292. scipy/optimize/_minpack_py.py +21 -14
  293. scipy/optimize/_moduleTNC.cpython-312-darwin.so +0 -0
  294. scipy/optimize/_nnls.py +20 -21
  295. scipy/optimize/_nonlin.py +34 -3
  296. scipy/optimize/_numdiff.py +288 -110
  297. scipy/optimize/_optimize.py +86 -48
  298. scipy/optimize/_pava_pybind.cpython-312-darwin.so +0 -0
  299. scipy/optimize/_remove_redundancy.py +5 -5
  300. scipy/optimize/_root_scalar.py +1 -1
  301. scipy/optimize/_shgo.py +6 -0
  302. scipy/optimize/_shgo_lib/_complex.py +1 -1
  303. scipy/optimize/_slsqp_py.py +216 -124
  304. scipy/optimize/_slsqplib.cpython-312-darwin.so +0 -0
  305. scipy/optimize/_spectral.py +1 -1
  306. scipy/optimize/_tnc.py +8 -1
  307. scipy/optimize/_trlib/_trlib.cpython-312-darwin.so +0 -0
  308. scipy/optimize/_trustregion.py +20 -6
  309. scipy/optimize/_trustregion_constr/canonical_constraint.py +7 -7
  310. scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +1 -1
  311. scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +11 -3
  312. scipy/optimize/_trustregion_constr/projections.py +12 -8
  313. scipy/optimize/_trustregion_constr/qp_subproblem.py +9 -9
  314. scipy/optimize/_trustregion_constr/tests/test_projections.py +7 -7
  315. scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +77 -77
  316. scipy/optimize/_trustregion_constr/tr_interior_point.py +5 -5
  317. scipy/optimize/_trustregion_exact.py +0 -1
  318. scipy/optimize/_zeros.cpython-312-darwin.so +0 -0
  319. scipy/optimize/_zeros_py.py +97 -17
  320. scipy/optimize/cython_optimize/_zeros.cpython-312-darwin.so +0 -0
  321. scipy/optimize/slsqp.py +0 -1
  322. scipy/optimize/tests/test__basinhopping.py +1 -1
  323. scipy/optimize/tests/test__differential_evolution.py +4 -4
  324. scipy/optimize/tests/test__linprog_clean_inputs.py +5 -3
  325. scipy/optimize/tests/test__numdiff.py +66 -22
  326. scipy/optimize/tests/test__remove_redundancy.py +2 -2
  327. scipy/optimize/tests/test__shgo.py +9 -1
  328. scipy/optimize/tests/test_bracket.py +71 -46
  329. scipy/optimize/tests/test_chandrupatla.py +133 -135
  330. scipy/optimize/tests/test_cobyla.py +74 -45
  331. scipy/optimize/tests/test_constraints.py +1 -1
  332. scipy/optimize/tests/test_differentiable_functions.py +226 -6
  333. scipy/optimize/tests/test_lbfgsb_hessinv.py +22 -0
  334. scipy/optimize/tests/test_least_squares.py +125 -13
  335. scipy/optimize/tests/test_linear_assignment.py +3 -3
  336. scipy/optimize/tests/test_linprog.py +3 -3
  337. scipy/optimize/tests/test_lsq_linear.py +6 -6
  338. scipy/optimize/tests/test_minimize_constrained.py +2 -2
  339. scipy/optimize/tests/test_minpack.py +4 -4
  340. scipy/optimize/tests/test_nnls.py +43 -3
  341. scipy/optimize/tests/test_nonlin.py +36 -0
  342. scipy/optimize/tests/test_optimize.py +98 -20
  343. scipy/optimize/tests/test_slsqp.py +36 -4
  344. scipy/optimize/tests/test_zeros.py +34 -1
  345. scipy/signal/__init__.py +12 -23
  346. scipy/signal/_delegators.py +568 -0
  347. scipy/signal/_filter_design.py +459 -241
  348. scipy/signal/_fir_filter_design.py +262 -90
  349. scipy/signal/_lti_conversion.py +3 -2
  350. scipy/signal/_ltisys.py +118 -91
  351. scipy/signal/_max_len_seq_inner.cpython-312-darwin.so +0 -0
  352. scipy/signal/_peak_finding_utils.cpython-312-darwin.so +0 -0
  353. scipy/signal/_polyutils.py +172 -0
  354. scipy/signal/_short_time_fft.py +553 -76
  355. scipy/signal/_signal_api.py +30 -0
  356. scipy/signal/_signaltools.py +719 -396
  357. scipy/signal/_sigtools.cpython-312-darwin.so +0 -0
  358. scipy/signal/_sosfilt.cpython-312-darwin.so +0 -0
  359. scipy/signal/_spectral_py.py +230 -50
  360. scipy/signal/_spline.cpython-312-darwin.so +0 -0
  361. scipy/signal/_spline_filters.py +108 -68
  362. scipy/signal/_support_alternative_backends.py +73 -0
  363. scipy/signal/_upfirdn.py +4 -1
  364. scipy/signal/_upfirdn_apply.cpython-312-darwin.so +0 -0
  365. scipy/signal/_waveforms.py +2 -11
  366. scipy/signal/_wavelets.py +1 -1
  367. scipy/signal/fir_filter_design.py +1 -0
  368. scipy/signal/spline.py +4 -11
  369. scipy/signal/tests/_scipy_spectral_test_shim.py +5 -182
  370. scipy/signal/tests/test_bsplines.py +114 -79
  371. scipy/signal/tests/test_cont2discrete.py +9 -2
  372. scipy/signal/tests/test_filter_design.py +721 -481
  373. scipy/signal/tests/test_fir_filter_design.py +332 -140
  374. scipy/signal/tests/test_savitzky_golay.py +4 -3
  375. scipy/signal/tests/test_short_time_fft.py +231 -5
  376. scipy/signal/tests/test_signaltools.py +2150 -1349
  377. scipy/signal/tests/test_spectral.py +50 -6
  378. scipy/signal/tests/test_splines.py +161 -96
  379. scipy/signal/tests/test_upfirdn.py +84 -50
  380. scipy/signal/tests/test_waveforms.py +20 -0
  381. scipy/signal/tests/test_windows.py +607 -466
  382. scipy/signal/windows/_windows.py +287 -148
  383. scipy/sparse/__init__.py +23 -4
  384. scipy/sparse/_base.py +269 -120
  385. scipy/sparse/_bsr.py +7 -4
  386. scipy/sparse/_compressed.py +59 -234
  387. scipy/sparse/_construct.py +90 -38
  388. scipy/sparse/_coo.py +115 -181
  389. scipy/sparse/_csc.py +4 -4
  390. scipy/sparse/_csparsetools.cpython-312-darwin.so +0 -0
  391. scipy/sparse/_csr.py +2 -2
  392. scipy/sparse/_data.py +48 -48
  393. scipy/sparse/_dia.py +105 -21
  394. scipy/sparse/_dok.py +0 -23
  395. scipy/sparse/_index.py +4 -4
  396. scipy/sparse/_matrix.py +23 -0
  397. scipy/sparse/_sparsetools.cpython-312-darwin.so +0 -0
  398. scipy/sparse/_sputils.py +37 -22
  399. scipy/sparse/base.py +0 -9
  400. scipy/sparse/bsr.py +0 -14
  401. scipy/sparse/compressed.py +0 -23
  402. scipy/sparse/construct.py +0 -6
  403. scipy/sparse/coo.py +0 -14
  404. scipy/sparse/csc.py +0 -3
  405. scipy/sparse/csgraph/_flow.cpython-312-darwin.so +0 -0
  406. scipy/sparse/csgraph/_matching.cpython-312-darwin.so +0 -0
  407. scipy/sparse/csgraph/_min_spanning_tree.cpython-312-darwin.so +0 -0
  408. scipy/sparse/csgraph/_reordering.cpython-312-darwin.so +0 -0
  409. scipy/sparse/csgraph/_shortest_path.cpython-312-darwin.so +0 -0
  410. scipy/sparse/csgraph/_tools.cpython-312-darwin.so +0 -0
  411. scipy/sparse/csgraph/_traversal.cpython-312-darwin.so +0 -0
  412. scipy/sparse/csgraph/tests/test_matching.py +14 -2
  413. scipy/sparse/csgraph/tests/test_pydata_sparse.py +4 -1
  414. scipy/sparse/csgraph/tests/test_shortest_path.py +83 -27
  415. scipy/sparse/csr.py +0 -5
  416. scipy/sparse/data.py +1 -6
  417. scipy/sparse/dia.py +0 -7
  418. scipy/sparse/dok.py +0 -10
  419. scipy/sparse/linalg/_dsolve/_superlu.cpython-312-darwin.so +0 -0
  420. scipy/sparse/linalg/_dsolve/linsolve.py +9 -0
  421. scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +35 -28
  422. scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-312-darwin.so +0 -0
  423. scipy/sparse/linalg/_eigen/arpack/arpack.py +28 -20
  424. scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +6 -6
  425. scipy/sparse/linalg/_expm_multiply.py +8 -3
  426. scipy/sparse/linalg/_interface.py +29 -26
  427. scipy/sparse/linalg/_isolve/_gcrotmk.py +6 -5
  428. scipy/sparse/linalg/_isolve/iterative.py +51 -45
  429. scipy/sparse/linalg/_isolve/lgmres.py +6 -6
  430. scipy/sparse/linalg/_isolve/minres.py +5 -5
  431. scipy/sparse/linalg/_isolve/tfqmr.py +7 -7
  432. scipy/sparse/linalg/_isolve/utils.py +2 -8
  433. scipy/sparse/linalg/_matfuncs.py +1 -1
  434. scipy/sparse/linalg/_norm.py +1 -1
  435. scipy/sparse/linalg/_propack/_cpropack.cpython-312-darwin.so +0 -0
  436. scipy/sparse/linalg/_propack/_dpropack.cpython-312-darwin.so +0 -0
  437. scipy/sparse/linalg/_propack/_spropack.cpython-312-darwin.so +0 -0
  438. scipy/sparse/linalg/_propack/_zpropack.cpython-312-darwin.so +0 -0
  439. scipy/sparse/linalg/_special_sparse_arrays.py +39 -38
  440. scipy/sparse/linalg/tests/test_expm_multiply.py +10 -0
  441. scipy/sparse/linalg/tests/test_interface.py +35 -0
  442. scipy/sparse/linalg/tests/test_pydata_sparse.py +18 -0
  443. scipy/sparse/tests/test_arithmetic1d.py +5 -2
  444. scipy/sparse/tests/test_base.py +224 -40
  445. scipy/sparse/tests/test_common1d.py +17 -12
  446. scipy/sparse/tests/test_construct.py +1 -1
  447. scipy/sparse/tests/test_coo.py +272 -4
  448. scipy/sparse/tests/test_sparsetools.py +5 -0
  449. scipy/sparse/tests/test_sputils.py +36 -7
  450. scipy/spatial/_ckdtree.cpython-312-darwin.so +0 -0
  451. scipy/spatial/_distance_pybind.cpython-312-darwin.so +0 -0
  452. scipy/spatial/_distance_wrap.cpython-312-darwin.so +0 -0
  453. scipy/spatial/_hausdorff.cpython-312-darwin.so +0 -0
  454. scipy/spatial/_qhull.cpython-312-darwin.so +0 -0
  455. scipy/spatial/_voronoi.cpython-312-darwin.so +0 -0
  456. scipy/spatial/distance.py +49 -42
  457. scipy/spatial/tests/test_distance.py +15 -1
  458. scipy/spatial/tests/test_kdtree.py +1 -0
  459. scipy/spatial/tests/test_qhull.py +106 -2
  460. scipy/spatial/transform/__init__.py +5 -3
  461. scipy/spatial/transform/_rigid_transform.cpython-312-darwin.so +0 -0
  462. scipy/spatial/transform/_rotation.cpython-312-darwin.so +0 -0
  463. scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
  464. scipy/spatial/transform/tests/test_rotation.py +1342 -790
  465. scipy/spatial/transform/tests/test_rotation_groups.py +3 -3
  466. scipy/spatial/transform/tests/test_rotation_spline.py +29 -8
  467. scipy/special/__init__.py +1 -47
  468. scipy/special/_add_newdocs.py +34 -772
  469. scipy/special/_basic.py +22 -25
  470. scipy/special/_comb.cpython-312-darwin.so +0 -0
  471. scipy/special/_ellip_harm_2.cpython-312-darwin.so +0 -0
  472. scipy/special/_gufuncs.cpython-312-darwin.so +0 -0
  473. scipy/special/_logsumexp.py +83 -69
  474. scipy/special/_orthogonal.pyi +1 -1
  475. scipy/special/_specfun.cpython-312-darwin.so +0 -0
  476. scipy/special/_special_ufuncs.cpython-312-darwin.so +0 -0
  477. scipy/special/_spherical_bessel.py +4 -4
  478. scipy/special/_support_alternative_backends.py +212 -119
  479. scipy/special/_test_internal.cpython-312-darwin.so +0 -0
  480. scipy/special/_testutils.py +4 -4
  481. scipy/special/_ufuncs.cpython-312-darwin.so +0 -0
  482. scipy/special/_ufuncs.pyi +1 -0
  483. scipy/special/_ufuncs.pyx +215 -1400
  484. scipy/special/_ufuncs_cxx.cpython-312-darwin.so +0 -0
  485. scipy/special/_ufuncs_cxx.pxd +2 -15
  486. scipy/special/_ufuncs_cxx.pyx +5 -44
  487. scipy/special/_ufuncs_cxx_defs.h +2 -16
  488. scipy/special/_ufuncs_defs.h +0 -8
  489. scipy/special/cython_special.cpython-312-darwin.so +0 -0
  490. scipy/special/cython_special.pxd +1 -1
  491. scipy/special/tests/_cython_examples/meson.build +10 -1
  492. scipy/special/tests/test_basic.py +153 -20
  493. scipy/special/tests/test_boost_ufuncs.py +3 -0
  494. scipy/special/tests/test_cdflib.py +35 -11
  495. scipy/special/tests/test_gammainc.py +16 -0
  496. scipy/special/tests/test_hyp2f1.py +23 -2
  497. scipy/special/tests/test_log1mexp.py +85 -0
  498. scipy/special/tests/test_logsumexp.py +220 -64
  499. scipy/special/tests/test_mpmath.py +1 -0
  500. scipy/special/tests/test_nan_inputs.py +1 -1
  501. scipy/special/tests/test_orthogonal.py +17 -18
  502. scipy/special/tests/test_sf_error.py +3 -2
  503. scipy/special/tests/test_sph_harm.py +6 -7
  504. scipy/special/tests/test_support_alternative_backends.py +211 -76
  505. scipy/stats/__init__.py +4 -1
  506. scipy/stats/_ansari_swilk_statistics.cpython-312-darwin.so +0 -0
  507. scipy/stats/_axis_nan_policy.py +5 -12
  508. scipy/stats/_biasedurn.cpython-312-darwin.so +0 -0
  509. scipy/stats/_continued_fraction.py +387 -0
  510. scipy/stats/_continuous_distns.py +296 -319
  511. scipy/stats/_correlation.py +1 -1
  512. scipy/stats/_covariance.py +6 -3
  513. scipy/stats/_discrete_distns.py +39 -32
  514. scipy/stats/_distn_infrastructure.py +39 -12
  515. scipy/stats/_distribution_infrastructure.py +920 -238
  516. scipy/stats/_entropy.py +9 -10
  517. scipy/{_lib → stats}/_finite_differences.py +1 -1
  518. scipy/stats/_hypotests.py +83 -50
  519. scipy/stats/_kde.py +53 -49
  520. scipy/stats/_ksstats.py +1 -1
  521. scipy/stats/_levy_stable/__init__.py +7 -15
  522. scipy/stats/_levy_stable/levyst.cpython-312-darwin.so +0 -0
  523. scipy/stats/_morestats.py +118 -73
  524. scipy/stats/_mstats_basic.py +13 -17
  525. scipy/stats/_mstats_extras.py +8 -8
  526. scipy/stats/_multivariate.py +89 -113
  527. scipy/stats/_new_distributions.py +97 -20
  528. scipy/stats/_page_trend_test.py +12 -5
  529. scipy/stats/_probability_distribution.py +265 -43
  530. scipy/stats/_qmc.py +14 -9
  531. scipy/stats/_qmc_cy.cpython-312-darwin.so +0 -0
  532. scipy/stats/_qmvnt.py +16 -95
  533. scipy/stats/_qmvnt_cy.cpython-312-darwin.so +0 -0
  534. scipy/stats/_quantile.py +335 -0
  535. scipy/stats/_rcont/rcont.cpython-312-darwin.so +0 -0
  536. scipy/stats/_resampling.py +5 -30
  537. scipy/stats/_sampling.py +1 -1
  538. scipy/stats/_sobol.cpython-312-darwin.so +0 -0
  539. scipy/stats/_stats.cpython-312-darwin.so +0 -0
  540. scipy/stats/_stats_mstats_common.py +21 -2
  541. scipy/stats/_stats_py.py +551 -477
  542. scipy/stats/_stats_pythran.cpython-312-darwin.so +0 -0
  543. scipy/stats/_unuran/unuran_wrapper.cpython-312-darwin.so +0 -0
  544. scipy/stats/_unuran/unuran_wrapper.pyi +2 -1
  545. scipy/stats/_variation.py +6 -8
  546. scipy/stats/_wilcoxon.py +13 -7
  547. scipy/stats/tests/common_tests.py +6 -4
  548. scipy/stats/tests/test_axis_nan_policy.py +62 -24
  549. scipy/stats/tests/test_continued_fraction.py +173 -0
  550. scipy/stats/tests/test_continuous.py +379 -60
  551. scipy/stats/tests/test_continuous_basic.py +18 -12
  552. scipy/stats/tests/test_discrete_basic.py +14 -8
  553. scipy/stats/tests/test_discrete_distns.py +16 -16
  554. scipy/stats/tests/test_distributions.py +117 -75
  555. scipy/stats/tests/test_entropy.py +40 -48
  556. scipy/stats/tests/test_fit.py +4 -3
  557. scipy/stats/tests/test_hypotests.py +153 -24
  558. scipy/stats/tests/test_kdeoth.py +109 -41
  559. scipy/stats/tests/test_marray.py +289 -0
  560. scipy/stats/tests/test_morestats.py +81 -49
  561. scipy/stats/tests/test_mstats_basic.py +3 -3
  562. scipy/stats/tests/test_multivariate.py +434 -83
  563. scipy/stats/tests/test_qmc.py +13 -10
  564. scipy/stats/tests/test_quantile.py +199 -0
  565. scipy/stats/tests/test_rank.py +119 -112
  566. scipy/stats/tests/test_resampling.py +47 -56
  567. scipy/stats/tests/test_sampling.py +9 -4
  568. scipy/stats/tests/test_stats.py +799 -939
  569. scipy/stats/tests/test_variation.py +8 -6
  570. scipy/version.py +2 -2
  571. {scipy-1.15.2.dist-info → scipy-1.16.0.dist-info}/LICENSE.txt +4 -4
  572. {scipy-1.15.2.dist-info → scipy-1.16.0.dist-info}/METADATA +12 -12
  573. {scipy-1.15.2.dist-info → scipy-1.16.0.dist-info}/RECORD +574 -581
  574. scipy-1.16.0.dist-info/WHEEL +6 -0
  575. scipy/_lib/array_api_extra/_funcs.py +0 -484
  576. scipy/_lib/array_api_extra/_typing.py +0 -8
  577. scipy/interpolate/_bspl.cpython-312-darwin.so +0 -0
  578. scipy/optimize/_cobyla.cpython-312-darwin.so +0 -0
  579. scipy/optimize/_cython_nnls.cpython-312-darwin.so +0 -0
  580. scipy/optimize/_slsqp.cpython-312-darwin.so +0 -0
  581. scipy/spatial/qhull_src/COPYING.txt +0 -38
  582. scipy/special/libsf_error_state.dylib +0 -0
  583. scipy/special/tests/test_log_softmax.py +0 -109
  584. scipy/special/tests/test_xsf_cuda.py +0 -114
  585. scipy/special/xsf/binom.h +0 -89
  586. scipy/special/xsf/cdflib.h +0 -100
  587. scipy/special/xsf/cephes/airy.h +0 -307
  588. scipy/special/xsf/cephes/besselpoly.h +0 -51
  589. scipy/special/xsf/cephes/beta.h +0 -257
  590. scipy/special/xsf/cephes/cbrt.h +0 -131
  591. scipy/special/xsf/cephes/chbevl.h +0 -85
  592. scipy/special/xsf/cephes/chdtr.h +0 -193
  593. scipy/special/xsf/cephes/const.h +0 -87
  594. scipy/special/xsf/cephes/ellie.h +0 -293
  595. scipy/special/xsf/cephes/ellik.h +0 -251
  596. scipy/special/xsf/cephes/ellpe.h +0 -107
  597. scipy/special/xsf/cephes/ellpk.h +0 -117
  598. scipy/special/xsf/cephes/expn.h +0 -260
  599. scipy/special/xsf/cephes/gamma.h +0 -398
  600. scipy/special/xsf/cephes/hyp2f1.h +0 -596
  601. scipy/special/xsf/cephes/hyperg.h +0 -361
  602. scipy/special/xsf/cephes/i0.h +0 -149
  603. scipy/special/xsf/cephes/i1.h +0 -158
  604. scipy/special/xsf/cephes/igam.h +0 -421
  605. scipy/special/xsf/cephes/igam_asymp_coeff.h +0 -195
  606. scipy/special/xsf/cephes/igami.h +0 -313
  607. scipy/special/xsf/cephes/j0.h +0 -225
  608. scipy/special/xsf/cephes/j1.h +0 -198
  609. scipy/special/xsf/cephes/jv.h +0 -715
  610. scipy/special/xsf/cephes/k0.h +0 -164
  611. scipy/special/xsf/cephes/k1.h +0 -163
  612. scipy/special/xsf/cephes/kn.h +0 -243
  613. scipy/special/xsf/cephes/lanczos.h +0 -112
  614. scipy/special/xsf/cephes/ndtr.h +0 -275
  615. scipy/special/xsf/cephes/poch.h +0 -85
  616. scipy/special/xsf/cephes/polevl.h +0 -167
  617. scipy/special/xsf/cephes/psi.h +0 -194
  618. scipy/special/xsf/cephes/rgamma.h +0 -111
  619. scipy/special/xsf/cephes/scipy_iv.h +0 -811
  620. scipy/special/xsf/cephes/shichi.h +0 -248
  621. scipy/special/xsf/cephes/sici.h +0 -224
  622. scipy/special/xsf/cephes/sindg.h +0 -221
  623. scipy/special/xsf/cephes/tandg.h +0 -139
  624. scipy/special/xsf/cephes/trig.h +0 -58
  625. scipy/special/xsf/cephes/unity.h +0 -186
  626. scipy/special/xsf/cephes/zeta.h +0 -172
  627. scipy/special/xsf/config.h +0 -304
  628. scipy/special/xsf/digamma.h +0 -205
  629. scipy/special/xsf/error.h +0 -57
  630. scipy/special/xsf/evalpoly.h +0 -47
  631. scipy/special/xsf/expint.h +0 -266
  632. scipy/special/xsf/hyp2f1.h +0 -694
  633. scipy/special/xsf/iv_ratio.h +0 -173
  634. scipy/special/xsf/lambertw.h +0 -150
  635. scipy/special/xsf/loggamma.h +0 -163
  636. scipy/special/xsf/sici.h +0 -200
  637. scipy/special/xsf/tools.h +0 -427
  638. scipy/special/xsf/trig.h +0 -164
  639. scipy/special/xsf/wright_bessel.h +0 -843
  640. scipy/special/xsf/zlog1.h +0 -35
  641. scipy/stats/_mvn.cpython-312-darwin.so +0 -0
  642. scipy-1.15.2.dist-info/WHEEL +0 -4
@@ -0,0 +1,937 @@
1
+ """Array-agnostic implementations for the public API."""
2
+
3
+ import math
4
+ import warnings
5
+ from collections.abc import Callable, Sequence
6
+ from types import ModuleType, NoneType
7
+ from typing import cast, overload
8
+
9
+ from ._at import at
10
+ from ._utils import _compat, _helpers
11
+ from ._utils._compat import array_namespace, is_dask_namespace, is_jax_array
12
+ from ._utils._helpers import (
13
+ asarrays,
14
+ capabilities,
15
+ eager_shape,
16
+ meta_namespace,
17
+ ndindex,
18
+ )
19
+ from ._utils._typing import Array
20
+
21
+ __all__ = [
22
+ "apply_where",
23
+ "atleast_nd",
24
+ "broadcast_shapes",
25
+ "cov",
26
+ "create_diagonal",
27
+ "expand_dims",
28
+ "kron",
29
+ "nunique",
30
+ "pad",
31
+ "setdiff1d",
32
+ "sinc",
33
+ ]
34
+
35
+
36
+ @overload
37
+ def apply_where( # type: ignore[explicit-any,decorated-any] # numpydoc ignore=GL08
38
+ cond: Array,
39
+ args: Array | tuple[Array, ...],
40
+ f1: Callable[..., Array],
41
+ f2: Callable[..., Array],
42
+ /,
43
+ *,
44
+ xp: ModuleType | None = None,
45
+ ) -> Array: ...
46
+
47
+
48
+ @overload
49
+ def apply_where( # type: ignore[explicit-any,decorated-any] # numpydoc ignore=GL08
50
+ cond: Array,
51
+ args: Array | tuple[Array, ...],
52
+ f1: Callable[..., Array],
53
+ /,
54
+ *,
55
+ fill_value: Array | complex,
56
+ xp: ModuleType | None = None,
57
+ ) -> Array: ...
58
+
59
+
60
+ def apply_where( # type: ignore[explicit-any] # numpydoc ignore=PR01,PR02
61
+ cond: Array,
62
+ args: Array | tuple[Array, ...],
63
+ f1: Callable[..., Array],
64
+ f2: Callable[..., Array] | None = None,
65
+ /,
66
+ *,
67
+ fill_value: Array | complex | None = None,
68
+ xp: ModuleType | None = None,
69
+ ) -> Array:
70
+ """
71
+ Run one of two elementwise functions depending on a condition.
72
+
73
+ Equivalent to ``f1(*args) if cond else fill_value`` performed elementwise
74
+ when `fill_value` is defined, otherwise to ``f1(*args) if cond else f2(*args)``.
75
+
76
+ Parameters
77
+ ----------
78
+ cond : array
79
+ The condition, expressed as a boolean array.
80
+ args : Array or tuple of Arrays
81
+ Argument(s) to `f1` (and `f2`). Must be broadcastable with `cond`.
82
+ f1 : callable
83
+ Elementwise function of `args`, returning a single array.
84
+ Where `cond` is True, output will be ``f1(arg0[cond], arg1[cond], ...)``.
85
+ f2 : callable, optional
86
+ Elementwise function of `args`, returning a single array.
87
+ Where `cond` is False, output will be ``f2(arg0[cond], arg1[cond], ...)``.
88
+ Mutually exclusive with `fill_value`.
89
+ fill_value : Array or scalar, optional
90
+ If provided, value with which to fill output array where `cond` is False.
91
+ It does not need to be scalar; it needs however to be broadcastable with
92
+ `cond` and `args`.
93
+ Mutually exclusive with `f2`. You must provide one or the other.
94
+ xp : array_namespace, optional
95
+ The standard-compatible namespace for `cond` and `args`. Default: infer.
96
+
97
+ Returns
98
+ -------
99
+ Array
100
+ An array with elements from the output of `f1` where `cond` is True and either
101
+ the output of `f2` or `fill_value` where `cond` is False. The returned array has
102
+ data type determined by type promotion rules between the output of `f1` and
103
+ either `fill_value` or the output of `f2`.
104
+
105
+ Notes
106
+ -----
107
+ ``xp.where(cond, f1(*args), f2(*args))`` requires explicitly evaluating `f1` even
108
+ when `cond` is False, and `f2` when cond is True. This function evaluates each
109
+ function only for their matching condition, if the backend allows for it.
110
+
111
+ On Dask, `f1` and `f2` are applied to the individual chunks and should use functions
112
+ from the namespace of the chunks.
113
+
114
+ Examples
115
+ --------
116
+ >>> import array_api_strict as xp
117
+ >>> import array_api_extra as xpx
118
+ >>> a = xp.asarray([5, 4, 3])
119
+ >>> b = xp.asarray([0, 2, 2])
120
+ >>> def f(a, b):
121
+ ... return a // b
122
+ >>> xpx.apply_where(b != 0, (a, b), f, fill_value=xp.nan)
123
+ array([ nan, 2., 1.])
124
+ """
125
+ # Parse and normalize arguments
126
+ if (f2 is None) == (fill_value is None):
127
+ msg = "Exactly one of `fill_value` or `f2` must be given."
128
+ raise TypeError(msg)
129
+ args_ = list(args) if isinstance(args, tuple) else [args]
130
+ del args
131
+
132
+ xp = array_namespace(cond, fill_value, *args_) if xp is None else xp
133
+
134
+ if isinstance(fill_value, int | float | complex | NoneType):
135
+ cond, *args_ = xp.broadcast_arrays(cond, *args_)
136
+ else:
137
+ cond, fill_value, *args_ = xp.broadcast_arrays(cond, fill_value, *args_)
138
+
139
+ if is_dask_namespace(xp):
140
+ meta_xp = meta_namespace(cond, fill_value, *args_, xp=xp)
141
+ # map_blocks doesn't descend into tuples of Arrays
142
+ return xp.map_blocks(_apply_where, cond, f1, f2, fill_value, *args_, xp=meta_xp)
143
+ return _apply_where(cond, f1, f2, fill_value, *args_, xp=xp)
144
+
145
+
146
+ def _apply_where( # type: ignore[explicit-any] # numpydoc ignore=PR01,RT01
147
+ cond: Array,
148
+ f1: Callable[..., Array],
149
+ f2: Callable[..., Array] | None,
150
+ fill_value: Array | int | float | complex | bool | None,
151
+ *args: Array,
152
+ xp: ModuleType,
153
+ ) -> Array:
154
+ """Helper of `apply_where`. On Dask, this runs on a single chunk."""
155
+
156
+ if not capabilities(xp)["boolean indexing"]:
157
+ # jax.jit does not support assignment by boolean mask
158
+ return xp.where(cond, f1(*args), f2(*args) if f2 is not None else fill_value)
159
+
160
+ temp1 = f1(*(arr[cond] for arr in args))
161
+
162
+ if f2 is None:
163
+ dtype = xp.result_type(temp1, fill_value)
164
+ if isinstance(fill_value, int | float | complex):
165
+ out = xp.full_like(cond, dtype=dtype, fill_value=fill_value)
166
+ else:
167
+ out = xp.astype(fill_value, dtype, copy=True)
168
+ else:
169
+ ncond = ~cond
170
+ temp2 = f2(*(arr[ncond] for arr in args))
171
+ dtype = xp.result_type(temp1, temp2)
172
+ out = xp.empty_like(cond, dtype=dtype)
173
+ out = at(out, ncond).set(temp2)
174
+
175
+ return at(out, cond).set(temp1)
176
+
177
+
178
+ def atleast_nd(x: Array, /, *, ndim: int, xp: ModuleType | None = None) -> Array:
179
+ """
180
+ Recursively expand the dimension of an array to at least `ndim`.
181
+
182
+ Parameters
183
+ ----------
184
+ x : array
185
+ Input array.
186
+ ndim : int
187
+ The minimum number of dimensions for the result.
188
+ xp : array_namespace, optional
189
+ The standard-compatible namespace for `x`. Default: infer.
190
+
191
+ Returns
192
+ -------
193
+ array
194
+ An array with ``res.ndim`` >= `ndim`.
195
+ If ``x.ndim`` >= `ndim`, `x` is returned.
196
+ If ``x.ndim`` < `ndim`, `x` is expanded by prepending new axes
197
+ until ``res.ndim`` equals `ndim`.
198
+
199
+ Examples
200
+ --------
201
+ >>> import array_api_strict as xp
202
+ >>> import array_api_extra as xpx
203
+ >>> x = xp.asarray([1])
204
+ >>> xpx.atleast_nd(x, ndim=3, xp=xp)
205
+ Array([[[1]]], dtype=array_api_strict.int64)
206
+
207
+ >>> x = xp.asarray([[[1, 2],
208
+ ... [3, 4]]])
209
+ >>> xpx.atleast_nd(x, ndim=1, xp=xp) is x
210
+ True
211
+ """
212
+ if xp is None:
213
+ xp = array_namespace(x)
214
+
215
+ if x.ndim < ndim:
216
+ x = xp.expand_dims(x, axis=0)
217
+ x = atleast_nd(x, ndim=ndim, xp=xp)
218
+ return x
219
+
220
+
221
+ # `float` in signature to accept `math.nan` for Dask.
222
+ # `int`s are still accepted as `float` is a superclass of `int` in typing
223
+ def broadcast_shapes(*shapes: tuple[float | None, ...]) -> tuple[int | None, ...]:
224
+ """
225
+ Compute the shape of the broadcasted arrays.
226
+
227
+ Duplicates :func:`numpy.broadcast_shapes`, with additional support for
228
+ None and NaN sizes.
229
+
230
+ This is equivalent to ``xp.broadcast_arrays(arr1, arr2, ...)[0].shape``
231
+ without needing to worry about the backend potentially deep copying
232
+ the arrays.
233
+
234
+ Parameters
235
+ ----------
236
+ *shapes : tuple[int | None, ...]
237
+ Shapes of the arrays to broadcast.
238
+
239
+ Returns
240
+ -------
241
+ tuple[int | None, ...]
242
+ The shape of the broadcasted arrays.
243
+
244
+ See Also
245
+ --------
246
+ numpy.broadcast_shapes : Equivalent NumPy function.
247
+ array_api.broadcast_arrays : Function to broadcast actual arrays.
248
+
249
+ Notes
250
+ -----
251
+ This function accepts the Array API's ``None`` for unknown sizes,
252
+ as well as Dask's non-standard ``math.nan``.
253
+ Regardless of input, the output always contains ``None`` for unknown sizes.
254
+
255
+ Examples
256
+ --------
257
+ >>> import array_api_extra as xpx
258
+ >>> xpx.broadcast_shapes((2, 3), (2, 1))
259
+ (2, 3)
260
+ >>> xpx.broadcast_shapes((4, 2, 3), (2, 1), (1, 3))
261
+ (4, 2, 3)
262
+ """
263
+ if not shapes:
264
+ return () # Match NumPy output
265
+
266
+ ndim = max(len(shape) for shape in shapes)
267
+ out: list[int | None] = []
268
+ for axis in range(-ndim, 0):
269
+ sizes = {shape[axis] for shape in shapes if axis >= -len(shape)}
270
+ # Dask uses NaN for unknown shape, which predates the Array API spec for None
271
+ none_size = None in sizes or math.nan in sizes
272
+ sizes -= {1, None, math.nan}
273
+ if len(sizes) > 1:
274
+ msg = (
275
+ "shape mismatch: objects cannot be broadcast to a single shape: "
276
+ f"{shapes}."
277
+ )
278
+ raise ValueError(msg)
279
+ out.append(None if none_size else cast(int, sizes.pop()) if sizes else 1)
280
+
281
+ return tuple(out)
282
+
283
+
284
+ def cov(m: Array, /, *, xp: ModuleType | None = None) -> Array:
285
+ """
286
+ Estimate a covariance matrix.
287
+
288
+ Covariance indicates the level to which two variables vary together.
289
+ If we examine N-dimensional samples, :math:`X = [x_1, x_2, ... x_N]^T`,
290
+ then the covariance matrix element :math:`C_{ij}` is the covariance of
291
+ :math:`x_i` and :math:`x_j`. The element :math:`C_{ii}` is the variance
292
+ of :math:`x_i`.
293
+
294
+ This provides a subset of the functionality of ``numpy.cov``.
295
+
296
+ Parameters
297
+ ----------
298
+ m : array
299
+ A 1-D or 2-D array containing multiple variables and observations.
300
+ Each row of `m` represents a variable, and each column a single
301
+ observation of all those variables.
302
+ xp : array_namespace, optional
303
+ The standard-compatible namespace for `m`. Default: infer.
304
+
305
+ Returns
306
+ -------
307
+ array
308
+ The covariance matrix of the variables.
309
+
310
+ Examples
311
+ --------
312
+ >>> import array_api_strict as xp
313
+ >>> import array_api_extra as xpx
314
+
315
+ Consider two variables, :math:`x_0` and :math:`x_1`, which
316
+ correlate perfectly, but in opposite directions:
317
+
318
+ >>> x = xp.asarray([[0, 2], [1, 1], [2, 0]]).T
319
+ >>> x
320
+ Array([[0, 1, 2],
321
+ [2, 1, 0]], dtype=array_api_strict.int64)
322
+
323
+ Note how :math:`x_0` increases while :math:`x_1` decreases. The covariance
324
+ matrix shows this clearly:
325
+
326
+ >>> xpx.cov(x, xp=xp)
327
+ Array([[ 1., -1.],
328
+ [-1., 1.]], dtype=array_api_strict.float64)
329
+
330
+ Note that element :math:`C_{0,1}`, which shows the correlation between
331
+ :math:`x_0` and :math:`x_1`, is negative.
332
+
333
+ Further, note how `x` and `y` are combined:
334
+
335
+ >>> x = xp.asarray([-2.1, -1, 4.3])
336
+ >>> y = xp.asarray([3, 1.1, 0.12])
337
+ >>> X = xp.stack((x, y), axis=0)
338
+ >>> xpx.cov(X, xp=xp)
339
+ Array([[11.71 , -4.286 ],
340
+ [-4.286 , 2.14413333]], dtype=array_api_strict.float64)
341
+
342
+ >>> xpx.cov(x, xp=xp)
343
+ Array(11.71, dtype=array_api_strict.float64)
344
+
345
+ >>> xpx.cov(y, xp=xp)
346
+ Array(2.14413333, dtype=array_api_strict.float64)
347
+ """
348
+ if xp is None:
349
+ xp = array_namespace(m)
350
+
351
+ m = xp.asarray(m, copy=True)
352
+ dtype = (
353
+ xp.float64 if xp.isdtype(m.dtype, "integral") else xp.result_type(m, xp.float64)
354
+ )
355
+
356
+ m = atleast_nd(m, ndim=2, xp=xp)
357
+ m = xp.astype(m, dtype)
358
+
359
+ avg = _helpers.mean(m, axis=1, xp=xp)
360
+
361
+ m_shape = eager_shape(m)
362
+ fact = m_shape[1] - 1
363
+
364
+ if fact <= 0:
365
+ warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning, stacklevel=2)
366
+ fact = 0
367
+
368
+ m -= avg[:, None]
369
+ m_transpose = m.T
370
+ if xp.isdtype(m_transpose.dtype, "complex floating"):
371
+ m_transpose = xp.conj(m_transpose)
372
+ c = m @ m_transpose
373
+ c /= fact
374
+ axes = tuple(axis for axis, length in enumerate(c.shape) if length == 1)
375
+ return xp.squeeze(c, axis=axes)
376
+
377
+
378
+ def create_diagonal(
379
+ x: Array, /, *, offset: int = 0, xp: ModuleType | None = None
380
+ ) -> Array:
381
+ """
382
+ Construct a diagonal array.
383
+
384
+ Parameters
385
+ ----------
386
+ x : array
387
+ An array having shape ``(*batch_dims, k)``.
388
+ offset : int, optional
389
+ Offset from the leading diagonal (default is ``0``).
390
+ Use positive ints for diagonals above the leading diagonal,
391
+ and negative ints for diagonals below the leading diagonal.
392
+ xp : array_namespace, optional
393
+ The standard-compatible namespace for `x`. Default: infer.
394
+
395
+ Returns
396
+ -------
397
+ array
398
+ An array having shape ``(*batch_dims, k+abs(offset), k+abs(offset))`` with `x`
399
+ on the diagonal (offset by `offset`).
400
+
401
+ Examples
402
+ --------
403
+ >>> import array_api_strict as xp
404
+ >>> import array_api_extra as xpx
405
+ >>> x = xp.asarray([2, 4, 8])
406
+
407
+ >>> xpx.create_diagonal(x, xp=xp)
408
+ Array([[2, 0, 0],
409
+ [0, 4, 0],
410
+ [0, 0, 8]], dtype=array_api_strict.int64)
411
+
412
+ >>> xpx.create_diagonal(x, offset=-2, xp=xp)
413
+ Array([[0, 0, 0, 0, 0],
414
+ [0, 0, 0, 0, 0],
415
+ [2, 0, 0, 0, 0],
416
+ [0, 4, 0, 0, 0],
417
+ [0, 0, 8, 0, 0]], dtype=array_api_strict.int64)
418
+ """
419
+ if xp is None:
420
+ xp = array_namespace(x)
421
+
422
+ if x.ndim == 0:
423
+ err_msg = "`x` must be at least 1-dimensional."
424
+ raise ValueError(err_msg)
425
+
426
+ x_shape = eager_shape(x)
427
+ batch_dims = x_shape[:-1]
428
+ n = x_shape[-1] + abs(offset)
429
+ diag = xp.zeros((*batch_dims, n**2), dtype=x.dtype, device=_compat.device(x))
430
+
431
+ target_slice = slice(
432
+ offset if offset >= 0 else abs(offset) * n,
433
+ min(n * (n - offset), diag.shape[-1]),
434
+ n + 1,
435
+ )
436
+ for index in ndindex(*batch_dims):
437
+ diag = at(diag)[(*index, target_slice)].set(x[(*index, slice(None))])
438
+ return xp.reshape(diag, (*batch_dims, n, n))
439
+
440
+
441
+ def expand_dims(
442
+ a: Array, /, *, axis: int | tuple[int, ...] = (0,), xp: ModuleType | None = None
443
+ ) -> Array:
444
+ """
445
+ Expand the shape of an array.
446
+
447
+ Insert (a) new axis/axes that will appear at the position(s) specified by
448
+ `axis` in the expanded array shape.
449
+
450
+ This is ``xp.expand_dims`` for `axis` an int *or a tuple of ints*.
451
+ Roughly equivalent to ``numpy.expand_dims`` for NumPy arrays.
452
+
453
+ Parameters
454
+ ----------
455
+ a : array
456
+ Array to have its shape expanded.
457
+ axis : int or tuple of ints, optional
458
+ Position(s) in the expanded axes where the new axis (or axes) is/are placed.
459
+ If multiple positions are provided, they should be unique (note that a position
460
+ given by a positive index could also be referred to by a negative index -
461
+ that will also result in an error).
462
+ Default: ``(0,)``.
463
+ xp : array_namespace, optional
464
+ The standard-compatible namespace for `a`. Default: infer.
465
+
466
+ Returns
467
+ -------
468
+ array
469
+ `a` with an expanded shape.
470
+
471
+ Examples
472
+ --------
473
+ >>> import array_api_strict as xp
474
+ >>> import array_api_extra as xpx
475
+ >>> x = xp.asarray([1, 2])
476
+ >>> x.shape
477
+ (2,)
478
+
479
+ The following is equivalent to ``x[xp.newaxis, :]`` or ``x[xp.newaxis]``:
480
+
481
+ >>> y = xpx.expand_dims(x, axis=0, xp=xp)
482
+ >>> y
483
+ Array([[1, 2]], dtype=array_api_strict.int64)
484
+ >>> y.shape
485
+ (1, 2)
486
+
487
+ The following is equivalent to ``x[:, xp.newaxis]``:
488
+
489
+ >>> y = xpx.expand_dims(x, axis=1, xp=xp)
490
+ >>> y
491
+ Array([[1],
492
+ [2]], dtype=array_api_strict.int64)
493
+ >>> y.shape
494
+ (2, 1)
495
+
496
+ ``axis`` may also be a tuple:
497
+
498
+ >>> y = xpx.expand_dims(x, axis=(0, 1), xp=xp)
499
+ >>> y
500
+ Array([[[1, 2]]], dtype=array_api_strict.int64)
501
+
502
+ >>> y = xpx.expand_dims(x, axis=(2, 0), xp=xp)
503
+ >>> y
504
+ Array([[[1],
505
+ [2]]], dtype=array_api_strict.int64)
506
+ """
507
+ if xp is None:
508
+ xp = array_namespace(a)
509
+
510
+ if not isinstance(axis, tuple):
511
+ axis = (axis,)
512
+ ndim = a.ndim + len(axis)
513
+ if axis != () and (min(axis) < -ndim or max(axis) >= ndim):
514
+ err_msg = (
515
+ f"a provided axis position is out of bounds for array of dimension {a.ndim}"
516
+ )
517
+ raise IndexError(err_msg)
518
+ axis = tuple(dim % ndim for dim in axis)
519
+ if len(set(axis)) != len(axis):
520
+ err_msg = "Duplicate dimensions specified in `axis`."
521
+ raise ValueError(err_msg)
522
+ for i in sorted(axis):
523
+ a = xp.expand_dims(a, axis=i)
524
+ return a
525
+
526
+
527
+ def isclose(
528
+ a: Array | complex,
529
+ b: Array | complex,
530
+ *,
531
+ rtol: float = 1e-05,
532
+ atol: float = 1e-08,
533
+ equal_nan: bool = False,
534
+ xp: ModuleType,
535
+ ) -> Array: # numpydoc ignore=PR01,RT01
536
+ """See docstring in array_api_extra._delegation."""
537
+ a, b = asarrays(a, b, xp=xp)
538
+
539
+ a_inexact = xp.isdtype(a.dtype, ("real floating", "complex floating"))
540
+ b_inexact = xp.isdtype(b.dtype, ("real floating", "complex floating"))
541
+ if a_inexact or b_inexact:
542
+ # prevent warnings on NumPy and Dask on inf - inf
543
+ mxp = meta_namespace(a, b, xp=xp)
544
+ out = apply_where(
545
+ xp.isinf(a) | xp.isinf(b),
546
+ (a, b),
547
+ lambda a, b: mxp.isinf(a) & mxp.isinf(b) & (mxp.sign(a) == mxp.sign(b)), # pyright: ignore[reportUnknownArgumentType]
548
+ # Note: inf <= inf is True!
549
+ lambda a, b: mxp.abs(a - b) <= (atol + rtol * mxp.abs(b)), # pyright: ignore[reportUnknownArgumentType]
550
+ xp=xp,
551
+ )
552
+ if equal_nan:
553
+ out = xp.where(xp.isnan(a) & xp.isnan(b), True, out)
554
+ return out
555
+
556
+ if xp.isdtype(a.dtype, "bool") or xp.isdtype(b.dtype, "bool"):
557
+ if atol >= 1 or rtol >= 1:
558
+ return xp.ones_like(a == b)
559
+ return a == b
560
+
561
+ # integer types
562
+ atol = int(atol)
563
+ if rtol == 0:
564
+ return xp.abs(a - b) <= atol
565
+
566
+ # Don't rely on OverflowError, as it is not guaranteed by the Array API.
567
+ nrtol = int(1.0 / rtol)
568
+ if nrtol > xp.iinfo(b.dtype).max:
569
+ # rtol * max_int < 1, so it's inconsequential
570
+ return xp.abs(a - b) <= atol
571
+ return xp.abs(a - b) <= (atol + xp.abs(b) // nrtol)
572
+
573
+
574
+ def kron(
575
+ a: Array | complex,
576
+ b: Array | complex,
577
+ /,
578
+ *,
579
+ xp: ModuleType | None = None,
580
+ ) -> Array:
581
+ """
582
+ Kronecker product of two arrays.
583
+
584
+ Computes the Kronecker product, a composite array made of blocks of the
585
+ second array scaled by the first.
586
+
587
+ Equivalent to ``numpy.kron`` for NumPy arrays.
588
+
589
+ Parameters
590
+ ----------
591
+ a, b : Array | int | float | complex
592
+ Input arrays or scalars. At least one must be an array.
593
+ xp : array_namespace, optional
594
+ The standard-compatible namespace for `a` and `b`. Default: infer.
595
+
596
+ Returns
597
+ -------
598
+ array
599
+ The Kronecker product of `a` and `b`.
600
+
601
+ Notes
602
+ -----
603
+ The function assumes that the number of dimensions of `a` and `b`
604
+ are the same, if necessary prepending the smallest with ones.
605
+ If ``a.shape = (r0,r1,..,rN)`` and ``b.shape = (s0,s1,...,sN)``,
606
+ the Kronecker product has shape ``(r0*s0, r1*s1, ..., rN*SN)``.
607
+ The elements are products of elements from `a` and `b`, organized
608
+ explicitly by::
609
+
610
+ kron(a,b)[k0,k1,...,kN] = a[i0,i1,...,iN] * b[j0,j1,...,jN]
611
+
612
+ where::
613
+
614
+ kt = it * st + jt, t = 0,...,N
615
+
616
+ In the common 2-D case (N=1), the block structure can be visualized::
617
+
618
+ [[ a[0,0]*b, a[0,1]*b, ... , a[0,-1]*b ],
619
+ [ ... ... ],
620
+ [ a[-1,0]*b, a[-1,1]*b, ... , a[-1,-1]*b ]]
621
+
622
+ Examples
623
+ --------
624
+ >>> import array_api_strict as xp
625
+ >>> import array_api_extra as xpx
626
+ >>> xpx.kron(xp.asarray([1, 10, 100]), xp.asarray([5, 6, 7]), xp=xp)
627
+ Array([ 5, 6, 7, 50, 60, 70, 500,
628
+ 600, 700], dtype=array_api_strict.int64)
629
+
630
+ >>> xpx.kron(xp.asarray([5, 6, 7]), xp.asarray([1, 10, 100]), xp=xp)
631
+ Array([ 5, 50, 500, 6, 60, 600, 7,
632
+ 70, 700], dtype=array_api_strict.int64)
633
+
634
+ >>> xpx.kron(xp.eye(2), xp.ones((2, 2)), xp=xp)
635
+ Array([[1., 1., 0., 0.],
636
+ [1., 1., 0., 0.],
637
+ [0., 0., 1., 1.],
638
+ [0., 0., 1., 1.]], dtype=array_api_strict.float64)
639
+
640
+ >>> a = xp.reshape(xp.arange(100), (2, 5, 2, 5))
641
+ >>> b = xp.reshape(xp.arange(24), (2, 3, 4))
642
+ >>> c = xpx.kron(a, b, xp=xp)
643
+ >>> c.shape
644
+ (2, 10, 6, 20)
645
+ >>> I = (1, 3, 0, 2)
646
+ >>> J = (0, 2, 1)
647
+ >>> J1 = (0,) + J # extend to ndim=4
648
+ >>> S1 = (1,) + b.shape
649
+ >>> K = tuple(xp.asarray(I) * xp.asarray(S1) + xp.asarray(J1))
650
+ >>> c[K] == a[I]*b[J]
651
+ Array(True, dtype=array_api_strict.bool)
652
+ """
653
+ if xp is None:
654
+ xp = array_namespace(a, b)
655
+ a, b = asarrays(a, b, xp=xp)
656
+
657
+ singletons = (1,) * (b.ndim - a.ndim)
658
+ a = cast(Array, xp.broadcast_to(a, singletons + a.shape))
659
+
660
+ nd_b, nd_a = b.ndim, a.ndim
661
+ nd_max = max(nd_b, nd_a)
662
+ if nd_a == 0 or nd_b == 0:
663
+ return xp.multiply(a, b)
664
+
665
+ a_shape = eager_shape(a)
666
+ b_shape = eager_shape(b)
667
+
668
+ # Equalise the shapes by prepending smaller one with 1s
669
+ a_shape = (1,) * max(0, nd_b - nd_a) + a_shape
670
+ b_shape = (1,) * max(0, nd_a - nd_b) + b_shape
671
+
672
+ # Insert empty dimensions
673
+ a_arr = expand_dims(a, axis=tuple(range(nd_b - nd_a)), xp=xp)
674
+ b_arr = expand_dims(b, axis=tuple(range(nd_a - nd_b)), xp=xp)
675
+
676
+ # Compute the product
677
+ a_arr = expand_dims(a_arr, axis=tuple(range(1, nd_max * 2, 2)), xp=xp)
678
+ b_arr = expand_dims(b_arr, axis=tuple(range(0, nd_max * 2, 2)), xp=xp)
679
+ result = xp.multiply(a_arr, b_arr)
680
+
681
+ # Reshape back and return
682
+ res_shape = tuple(a_s * b_s for a_s, b_s in zip(a_shape, b_shape, strict=True))
683
+ return xp.reshape(result, res_shape)
684
+
685
+
686
+ def nunique(x: Array, /, *, xp: ModuleType | None = None) -> Array:
687
+ """
688
+ Count the number of unique elements in an array.
689
+
690
+ Compatible with JAX and Dask, whose laziness would be otherwise
691
+ problematic.
692
+
693
+ Parameters
694
+ ----------
695
+ x : Array
696
+ Input array.
697
+ xp : array_namespace, optional
698
+ The standard-compatible namespace for `x`. Default: infer.
699
+
700
+ Returns
701
+ -------
702
+ array: 0-dimensional integer array
703
+ The number of unique elements in `x`. It can be lazy.
704
+ """
705
+ if xp is None:
706
+ xp = array_namespace(x)
707
+
708
+ if is_jax_array(x):
709
+ # size= is JAX-specific
710
+ # https://github.com/data-apis/array-api/issues/883
711
+ _, counts = xp.unique_counts(x, size=_compat.size(x))
712
+ return (counts > 0).sum()
713
+
714
+ # There are 3 general use cases:
715
+ # 1. backend has unique_counts and it returns an array with known shape
716
+ # 2. backend has unique_counts and it returns a None-sized array;
717
+ # e.g. Dask, ndonnx
718
+ # 3. backend does not have unique_counts; e.g. wrapped JAX
719
+ if capabilities(xp)["data-dependent shapes"]:
720
+ # xp has unique_counts; O(n) complexity
721
+ _, counts = xp.unique_counts(x)
722
+ n = _compat.size(counts)
723
+ if n is None:
724
+ return xp.sum(xp.ones_like(counts))
725
+ return xp.asarray(n, device=_compat.device(x))
726
+
727
+ # xp does not have unique_counts; O(n*logn) complexity
728
+ x = xp.reshape(x, (-1,))
729
+ x = xp.sort(x)
730
+ mask = x != xp.roll(x, -1)
731
+ default_int = xp.__array_namespace_info__().default_dtypes(
732
+ device=_compat.device(x)
733
+ )["integral"]
734
+ return xp.maximum(
735
+ # Special cases:
736
+ # - array is size 0
737
+ # - array has all elements equal to each other
738
+ xp.astype(xp.any(~mask), default_int),
739
+ xp.sum(xp.astype(mask, default_int)),
740
+ )
741
+
742
+
743
+ def pad(
744
+ x: Array,
745
+ pad_width: int | tuple[int, int] | Sequence[tuple[int, int]],
746
+ *,
747
+ constant_values: complex = 0,
748
+ xp: ModuleType,
749
+ ) -> Array: # numpydoc ignore=PR01,RT01
750
+ """See docstring in `array_api_extra._delegation.py`."""
751
+ # make pad_width a list of length-2 tuples of ints
752
+ if isinstance(pad_width, int):
753
+ pad_width_seq = [(pad_width, pad_width)] * x.ndim
754
+ elif (
755
+ isinstance(pad_width, tuple)
756
+ and len(pad_width) == 2
757
+ and all(isinstance(i, int) for i in pad_width)
758
+ ):
759
+ pad_width_seq = [cast(tuple[int, int], pad_width)] * x.ndim
760
+ else:
761
+ pad_width_seq = cast(list[tuple[int, int]], list(pad_width))
762
+
763
+ # https://github.com/python/typeshed/issues/13376
764
+ slices: list[slice] = [] # type: ignore[explicit-any]
765
+ newshape: list[int] = []
766
+ for ax, w_tpl in enumerate(pad_width_seq):
767
+ if len(w_tpl) != 2:
768
+ msg = f"expect a 2-tuple (before, after), got {w_tpl}."
769
+ raise ValueError(msg)
770
+
771
+ sh = eager_shape(x)[ax]
772
+
773
+ if w_tpl[0] == 0 and w_tpl[1] == 0:
774
+ sl = slice(None, None, None)
775
+ else:
776
+ start, stop = w_tpl
777
+ stop = None if stop == 0 else -stop
778
+
779
+ sl = slice(start, stop, None)
780
+ sh += w_tpl[0] + w_tpl[1]
781
+
782
+ newshape.append(sh)
783
+ slices.append(sl)
784
+
785
+ padded = xp.full(
786
+ tuple(newshape),
787
+ fill_value=constant_values,
788
+ dtype=x.dtype,
789
+ device=_compat.device(x),
790
+ )
791
+ return at(padded, tuple(slices)).set(x)
792
+
793
+
794
+ def setdiff1d(
795
+ x1: Array | complex,
796
+ x2: Array | complex,
797
+ /,
798
+ *,
799
+ assume_unique: bool = False,
800
+ xp: ModuleType | None = None,
801
+ ) -> Array:
802
+ """
803
+ Find the set difference of two arrays.
804
+
805
+ Return the unique values in `x1` that are not in `x2`.
806
+
807
+ Parameters
808
+ ----------
809
+ x1 : array | int | float | complex | bool
810
+ Input array.
811
+ x2 : array
812
+ Input comparison array.
813
+ assume_unique : bool
814
+ If ``True``, the input arrays are both assumed to be unique, which
815
+ can speed up the calculation. Default is ``False``.
816
+ xp : array_namespace, optional
817
+ The standard-compatible namespace for `x1` and `x2`. Default: infer.
818
+
819
+ Returns
820
+ -------
821
+ array
822
+ 1D array of values in `x1` that are not in `x2`. The result
823
+ is sorted when `assume_unique` is ``False``, but otherwise only sorted
824
+ if the input is sorted.
825
+
826
+ Examples
827
+ --------
828
+ >>> import array_api_strict as xp
829
+ >>> import array_api_extra as xpx
830
+
831
+ >>> x1 = xp.asarray([1, 2, 3, 2, 4, 1])
832
+ >>> x2 = xp.asarray([3, 4, 5, 6])
833
+ >>> xpx.setdiff1d(x1, x2, xp=xp)
834
+ Array([1, 2], dtype=array_api_strict.int64)
835
+ """
836
+ if xp is None:
837
+ xp = array_namespace(x1, x2)
838
+ # https://github.com/microsoft/pyright/issues/10103
839
+ x1_, x2_ = asarrays(x1, x2, xp=xp)
840
+
841
+ if assume_unique:
842
+ x1_ = xp.reshape(x1_, (-1,))
843
+ x2_ = xp.reshape(x2_, (-1,))
844
+ else:
845
+ x1_ = xp.unique_values(x1_)
846
+ x2_ = xp.unique_values(x2_)
847
+
848
+ return x1_[_helpers.in1d(x1_, x2_, assume_unique=True, invert=True, xp=xp)]
849
+
850
+
851
+ def sinc(x: Array, /, *, xp: ModuleType | None = None) -> Array:
852
+ r"""
853
+ Return the normalized sinc function.
854
+
855
+ The sinc function is equal to :math:`\sin(\pi x)/(\pi x)` for any argument
856
+ :math:`x\ne 0`. ``sinc(0)`` takes the limit value 1, making ``sinc`` not
857
+ only everywhere continuous but also infinitely differentiable.
858
+
859
+ .. note::
860
+
861
+ Note the normalization factor of ``pi`` used in the definition.
862
+ This is the most commonly used definition in signal processing.
863
+ Use ``sinc(x / xp.pi)`` to obtain the unnormalized sinc function
864
+ :math:`\sin(x)/x` that is more common in mathematics.
865
+
866
+ Parameters
867
+ ----------
868
+ x : array
869
+ Array (possibly multi-dimensional) of values for which to calculate
870
+ ``sinc(x)``. Must have a real floating point dtype.
871
+ xp : array_namespace, optional
872
+ The standard-compatible namespace for `x`. Default: infer.
873
+
874
+ Returns
875
+ -------
876
+ array
877
+ ``sinc(x)`` calculated elementwise, which has the same shape as the input.
878
+
879
+ Notes
880
+ -----
881
+ The name sinc is short for "sine cardinal" or "sinus cardinalis".
882
+
883
+ The sinc function is used in various signal processing applications,
884
+ including in anti-aliasing, in the construction of a Lanczos resampling
885
+ filter, and in interpolation.
886
+
887
+ For bandlimited interpolation of discrete-time signals, the ideal
888
+ interpolation kernel is proportional to the sinc function.
889
+
890
+ References
891
+ ----------
892
+ #. Weisstein, Eric W. "Sinc Function." From MathWorld--A Wolfram Web
893
+ Resource. https://mathworld.wolfram.com/SincFunction.html
894
+ #. Wikipedia, "Sinc function",
895
+ https://en.wikipedia.org/wiki/Sinc_function
896
+
897
+ Examples
898
+ --------
899
+ >>> import array_api_strict as xp
900
+ >>> import array_api_extra as xpx
901
+ >>> x = xp.linspace(-4, 4, 41)
902
+ >>> xpx.sinc(x, xp=xp)
903
+ Array([-3.89817183e-17, -4.92362781e-02,
904
+ -8.40918587e-02, -8.90384387e-02,
905
+ -5.84680802e-02, 3.89817183e-17,
906
+ 6.68206631e-02, 1.16434881e-01,
907
+ 1.26137788e-01, 8.50444803e-02,
908
+ -3.89817183e-17, -1.03943254e-01,
909
+ -1.89206682e-01, -2.16236208e-01,
910
+ -1.55914881e-01, 3.89817183e-17,
911
+ 2.33872321e-01, 5.04551152e-01,
912
+ 7.56826729e-01, 9.35489284e-01,
913
+ 1.00000000e+00, 9.35489284e-01,
914
+ 7.56826729e-01, 5.04551152e-01,
915
+ 2.33872321e-01, 3.89817183e-17,
916
+ -1.55914881e-01, -2.16236208e-01,
917
+ -1.89206682e-01, -1.03943254e-01,
918
+ -3.89817183e-17, 8.50444803e-02,
919
+ 1.26137788e-01, 1.16434881e-01,
920
+ 6.68206631e-02, 3.89817183e-17,
921
+ -5.84680802e-02, -8.90384387e-02,
922
+ -8.40918587e-02, -4.92362781e-02,
923
+ -3.89817183e-17], dtype=array_api_strict.float64)
924
+ """
925
+ if xp is None:
926
+ xp = array_namespace(x)
927
+
928
+ if not xp.isdtype(x.dtype, "real floating"):
929
+ err_msg = "`x` must have a real floating data type."
930
+ raise ValueError(err_msg)
931
+ # no scalars in `where` - array-api#807
932
+ y = xp.pi * xp.where(
933
+ xp.astype(x, xp.bool),
934
+ x,
935
+ xp.asarray(xp.finfo(x.dtype).eps, dtype=x.dtype, device=_compat.device(x)),
936
+ )
937
+ return xp.sin(y) / y