scipy 1.15.3__cp312-cp312-musllinux_1_2_aarch64.whl → 1.16.0rc2__cp312-cp312-musllinux_1_2_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.
- scipy/__config__.py +10 -10
- scipy/__init__.py +3 -6
- scipy/_cyutility.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_array_api.py +486 -161
- scipy/_lib/_array_api_compat_vendor.py +9 -0
- scipy/_lib/_bunch.py +4 -0
- scipy/_lib/_ccallback_c.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_docscrape.py +1 -1
- scipy/_lib/_elementwise_iterative_method.py +15 -26
- scipy/_lib/_fpumode.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_sparse.py +41 -0
- scipy/_lib/_test_ccallback.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_test_deprecation_call.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_test_deprecation_def.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_testutils.py +6 -2
- scipy/_lib/_uarray/_uarray.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_util.py +222 -125
- scipy/_lib/array_api_compat/__init__.py +4 -4
- scipy/_lib/array_api_compat/_internal.py +19 -6
- scipy/_lib/array_api_compat/common/__init__.py +1 -1
- scipy/_lib/array_api_compat/common/_aliases.py +365 -193
- scipy/_lib/array_api_compat/common/_fft.py +94 -64
- scipy/_lib/array_api_compat/common/_helpers.py +413 -180
- scipy/_lib/array_api_compat/common/_linalg.py +116 -40
- scipy/_lib/array_api_compat/common/_typing.py +179 -10
- scipy/_lib/array_api_compat/cupy/__init__.py +1 -4
- scipy/_lib/array_api_compat/cupy/_aliases.py +61 -41
- scipy/_lib/array_api_compat/cupy/_info.py +16 -6
- scipy/_lib/array_api_compat/cupy/_typing.py +24 -39
- scipy/_lib/array_api_compat/dask/array/__init__.py +6 -3
- scipy/_lib/array_api_compat/dask/array/_aliases.py +267 -108
- scipy/_lib/array_api_compat/dask/array/_info.py +105 -34
- scipy/_lib/array_api_compat/dask/array/fft.py +5 -8
- scipy/_lib/array_api_compat/dask/array/linalg.py +21 -22
- scipy/_lib/array_api_compat/numpy/__init__.py +13 -15
- scipy/_lib/array_api_compat/numpy/_aliases.py +98 -49
- scipy/_lib/array_api_compat/numpy/_info.py +36 -16
- scipy/_lib/array_api_compat/numpy/_typing.py +27 -43
- scipy/_lib/array_api_compat/numpy/fft.py +11 -5
- scipy/_lib/array_api_compat/numpy/linalg.py +75 -22
- scipy/_lib/array_api_compat/torch/__init__.py +3 -5
- scipy/_lib/array_api_compat/torch/_aliases.py +262 -159
- scipy/_lib/array_api_compat/torch/_info.py +27 -16
- scipy/_lib/array_api_compat/torch/_typing.py +3 -0
- scipy/_lib/array_api_compat/torch/fft.py +17 -18
- scipy/_lib/array_api_compat/torch/linalg.py +16 -16
- scipy/_lib/array_api_extra/__init__.py +26 -3
- scipy/_lib/array_api_extra/_delegation.py +171 -0
- scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_at.py +463 -0
- scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
- scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
- scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
- scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
- scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
- scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
- scipy/_lib/array_api_extra/testing.py +359 -0
- scipy/_lib/decorator.py +2 -2
- scipy/_lib/doccer.py +1 -7
- scipy/_lib/messagestream.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/pyprima/__init__.py +212 -0
- scipy/_lib/pyprima/cobyla/__init__.py +0 -0
- scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
- scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
- scipy/_lib/pyprima/cobyla/geometry.py +226 -0
- scipy/_lib/pyprima/cobyla/initialize.py +215 -0
- scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
- scipy/_lib/pyprima/cobyla/update.py +289 -0
- scipy/_lib/pyprima/common/__init__.py +0 -0
- scipy/_lib/pyprima/common/_bounds.py +34 -0
- scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
- scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
- scipy/_lib/pyprima/common/_project.py +173 -0
- scipy/_lib/pyprima/common/checkbreak.py +93 -0
- scipy/_lib/pyprima/common/consts.py +47 -0
- scipy/_lib/pyprima/common/evaluate.py +99 -0
- scipy/_lib/pyprima/common/history.py +38 -0
- scipy/_lib/pyprima/common/infos.py +30 -0
- scipy/_lib/pyprima/common/linalg.py +435 -0
- scipy/_lib/pyprima/common/message.py +290 -0
- scipy/_lib/pyprima/common/powalg.py +131 -0
- scipy/_lib/pyprima/common/preproc.py +277 -0
- scipy/_lib/pyprima/common/present.py +5 -0
- scipy/_lib/pyprima/common/ratio.py +54 -0
- scipy/_lib/pyprima/common/redrho.py +47 -0
- scipy/_lib/pyprima/common/selectx.py +296 -0
- scipy/_lib/tests/test__util.py +105 -121
- scipy/_lib/tests/test_array_api.py +166 -35
- scipy/_lib/tests/test_bunch.py +7 -0
- scipy/_lib/tests/test_ccallback.py +2 -10
- scipy/_lib/tests/test_public_api.py +13 -0
- scipy/cluster/_hierarchy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/_optimal_leaf_ordering.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/_vq.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/hierarchy.py +393 -223
- scipy/cluster/tests/test_hierarchy.py +273 -335
- scipy/cluster/tests/test_vq.py +45 -61
- scipy/cluster/vq.py +39 -35
- scipy/conftest.py +263 -157
- scipy/constants/_constants.py +4 -1
- scipy/constants/tests/test_codata.py +2 -2
- scipy/constants/tests/test_constants.py +11 -18
- scipy/datasets/_download_all.py +15 -1
- scipy/datasets/_fetchers.py +7 -1
- scipy/datasets/_utils.py +1 -1
- scipy/differentiate/_differentiate.py +25 -25
- scipy/differentiate/tests/test_differentiate.py +24 -25
- scipy/fft/_basic.py +20 -0
- scipy/fft/_helper.py +3 -34
- scipy/fft/_pocketfft/helper.py +29 -1
- scipy/fft/_pocketfft/pypocketfft.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/fft/_pocketfft/tests/test_basic.py +2 -4
- scipy/fft/_pocketfft/tests/test_real_transforms.py +4 -4
- scipy/fft/_realtransforms.py +13 -0
- scipy/fft/tests/test_basic.py +27 -25
- scipy/fft/tests/test_fftlog.py +16 -7
- scipy/fft/tests/test_helper.py +18 -34
- scipy/fft/tests/test_real_transforms.py +8 -10
- scipy/fftpack/convolve.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/fftpack/tests/test_basic.py +2 -4
- scipy/fftpack/tests/test_real_transforms.py +8 -9
- scipy/integrate/_bvp.py +9 -3
- scipy/integrate/_cubature.py +3 -2
- scipy/integrate/_dop.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_lsoda.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_ode.py +9 -2
- scipy/integrate/_odepack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_quad_vec.py +21 -29
- scipy/integrate/_quadpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_quadpack_py.py +11 -7
- scipy/integrate/_quadrature.py +3 -3
- scipy/integrate/_rules/_base.py +2 -2
- scipy/integrate/_tanhsinh.py +48 -47
- scipy/integrate/_test_multivariate.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_test_odeint_banded.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_vode.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/tests/test__quad_vec.py +0 -6
- scipy/integrate/tests/test_banded_ode_solvers.py +85 -0
- scipy/integrate/tests/test_cubature.py +21 -35
- scipy/integrate/tests/test_quadrature.py +6 -8
- scipy/integrate/tests/test_tanhsinh.py +56 -48
- scipy/interpolate/__init__.py +70 -58
- scipy/interpolate/_bary_rational.py +22 -22
- scipy/interpolate/_bsplines.py +119 -66
- scipy/interpolate/_cubic.py +65 -50
- scipy/interpolate/_dfitpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_dierckx.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_fitpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_fitpack2.py +9 -6
- scipy/interpolate/_fitpack_impl.py +32 -26
- scipy/interpolate/_fitpack_repro.py +23 -19
- scipy/interpolate/_interpnd.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_interpolate.py +30 -12
- scipy/interpolate/_ndbspline.py +13 -18
- scipy/interpolate/_ndgriddata.py +5 -8
- scipy/interpolate/_polyint.py +95 -31
- scipy/interpolate/_ppoly.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_rbf.py +2 -2
- scipy/interpolate/_rbfinterp.py +1 -1
- scipy/interpolate/_rbfinterp_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_rgi.py +31 -26
- scipy/interpolate/_rgi_cython.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/dfitpack.py +0 -20
- scipy/interpolate/interpnd.py +1 -2
- scipy/interpolate/tests/test_bary_rational.py +2 -2
- scipy/interpolate/tests/test_bsplines.py +97 -1
- scipy/interpolate/tests/test_fitpack2.py +39 -1
- scipy/interpolate/tests/test_interpnd.py +32 -20
- scipy/interpolate/tests/test_interpolate.py +48 -4
- scipy/interpolate/tests/test_rgi.py +2 -1
- scipy/io/_fast_matrix_market/__init__.py +2 -0
- scipy/io/_fast_matrix_market/_fmm_core.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/_harwell_boeing/_fortran_format_parser.py +19 -16
- scipy/io/_harwell_boeing/hb.py +7 -11
- scipy/io/_idl.py +5 -7
- scipy/io/_netcdf.py +15 -5
- scipy/io/_test_fortran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/arff/tests/test_arffread.py +3 -3
- scipy/io/matlab/__init__.py +5 -3
- scipy/io/matlab/_mio.py +4 -1
- scipy/io/matlab/_mio5.py +19 -13
- scipy/io/matlab/_mio5_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/_mio_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/_miobase.py +4 -1
- scipy/io/matlab/_streams.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/tests/test_mio.py +46 -18
- scipy/io/matlab/tests/test_mio_funcs.py +1 -1
- scipy/io/tests/test_mmio.py +7 -1
- scipy/io/tests/test_wavfile.py +41 -0
- scipy/io/wavfile.py +57 -10
- scipy/linalg/_basic.py +113 -86
- scipy/linalg/_cythonized_array_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp.py +22 -9
- scipy/linalg/_decomp_cholesky.py +28 -13
- scipy/linalg/_decomp_cossin.py +45 -30
- scipy/linalg/_decomp_interpolative.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp_ldl.py +4 -1
- scipy/linalg/_decomp_lu.py +18 -6
- scipy/linalg/_decomp_lu_cython.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp_polar.py +2 -0
- scipy/linalg/_decomp_qr.py +6 -2
- scipy/linalg/_decomp_qz.py +3 -0
- scipy/linalg/_decomp_schur.py +3 -1
- scipy/linalg/_decomp_svd.py +13 -2
- scipy/linalg/_decomp_update.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_expm_frechet.py +4 -0
- scipy/linalg/_fblas.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_flapack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_linalg_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs.py +187 -4
- scipy/linalg/_matfuncs_expm.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs_sqrtm.py +1 -99
- scipy/linalg/_matfuncs_sqrtm_triu.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_procrustes.py +2 -0
- scipy/linalg/_sketches.py +17 -6
- scipy/linalg/_solve_toeplitz.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_solvers.py +7 -2
- scipy/linalg/_special_matrices.py +26 -36
- scipy/linalg/cython_blas.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/cython_lapack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/lapack.py +22 -2
- scipy/linalg/tests/_cython_examples/meson.build +7 -0
- scipy/linalg/tests/test_basic.py +31 -16
- scipy/linalg/tests/test_batch.py +588 -0
- scipy/linalg/tests/test_cythonized_array_utils.py +0 -2
- scipy/linalg/tests/test_decomp.py +40 -3
- scipy/linalg/tests/test_decomp_cossin.py +14 -0
- scipy/linalg/tests/test_decomp_ldl.py +1 -1
- scipy/linalg/tests/test_lapack.py +115 -7
- scipy/linalg/tests/test_matfuncs.py +157 -102
- scipy/linalg/tests/test_procrustes.py +0 -7
- scipy/linalg/tests/test_solve_toeplitz.py +1 -1
- scipy/linalg/tests/test_special_matrices.py +1 -5
- scipy/ndimage/__init__.py +1 -0
- scipy/ndimage/_ctest.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_cytest.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_delegators.py +8 -2
- scipy/ndimage/_filters.py +453 -5
- scipy/ndimage/_interpolation.py +36 -6
- scipy/ndimage/_measurements.py +4 -2
- scipy/ndimage/_morphology.py +5 -0
- scipy/ndimage/_nd_image.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_ni_docstrings.py +5 -1
- scipy/ndimage/_ni_label.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_ni_support.py +1 -5
- scipy/ndimage/_rank_filter_1d.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_support_alternative_backends.py +18 -6
- scipy/ndimage/tests/test_filters.py +370 -259
- scipy/ndimage/tests/test_fourier.py +7 -9
- scipy/ndimage/tests/test_interpolation.py +68 -61
- scipy/ndimage/tests/test_measurements.py +18 -35
- scipy/ndimage/tests/test_morphology.py +143 -131
- scipy/ndimage/tests/test_splines.py +1 -3
- scipy/odr/__odrpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_basinhopping.py +13 -7
- scipy/optimize/_bglu_dense.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_bracket.py +17 -24
- scipy/optimize/_chandrupatla.py +9 -10
- scipy/optimize/_cobyla_py.py +104 -123
- scipy/optimize/_constraints.py +14 -10
- scipy/optimize/_differentiable_functions.py +371 -230
- scipy/optimize/_differentialevolution.py +4 -3
- scipy/optimize/_direct.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_dual_annealing.py +1 -1
- scipy/optimize/_elementwise.py +1 -4
- scipy/optimize/_group_columns.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_highspy/_core.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_highspy/_highs_options.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lbfgsb.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lbfgsb_py.py +57 -16
- scipy/optimize/_linprog_doc.py +2 -2
- scipy/optimize/_linprog_highs.py +2 -2
- scipy/optimize/_linprog_ip.py +25 -10
- scipy/optimize/_linprog_util.py +14 -16
- scipy/optimize/_lsap.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lsq/common.py +3 -3
- scipy/optimize/_lsq/dogbox.py +16 -2
- scipy/optimize/_lsq/givens_elimination.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lsq/least_squares.py +198 -126
- scipy/optimize/_lsq/lsq_linear.py +6 -6
- scipy/optimize/_lsq/trf.py +35 -8
- scipy/optimize/_milp.py +3 -1
- scipy/optimize/_minimize.py +105 -36
- scipy/optimize/_minpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_minpack_py.py +21 -14
- scipy/optimize/_moduleTNC.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_nnls.py +20 -21
- scipy/optimize/_nonlin.py +34 -3
- scipy/optimize/_numdiff.py +288 -110
- scipy/optimize/_optimize.py +86 -48
- scipy/optimize/_pava_pybind.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_remove_redundancy.py +5 -5
- scipy/optimize/_root_scalar.py +1 -1
- scipy/optimize/_shgo.py +6 -0
- scipy/optimize/_shgo_lib/_complex.py +1 -1
- scipy/optimize/_slsqp_py.py +216 -124
- scipy/optimize/_slsqplib.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_spectral.py +1 -1
- scipy/optimize/_tnc.py +8 -1
- scipy/optimize/_trlib/_trlib.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_trustregion.py +20 -6
- scipy/optimize/_trustregion_constr/canonical_constraint.py +7 -7
- scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +1 -1
- scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +11 -3
- scipy/optimize/_trustregion_constr/projections.py +12 -8
- scipy/optimize/_trustregion_constr/qp_subproblem.py +9 -9
- scipy/optimize/_trustregion_constr/tests/test_projections.py +7 -7
- scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +77 -77
- scipy/optimize/_trustregion_constr/tr_interior_point.py +5 -5
- scipy/optimize/_trustregion_exact.py +0 -1
- scipy/optimize/_zeros.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_zeros_py.py +97 -17
- scipy/optimize/cython_optimize/_zeros.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/slsqp.py +0 -1
- scipy/optimize/tests/test__basinhopping.py +1 -1
- scipy/optimize/tests/test__differential_evolution.py +4 -4
- scipy/optimize/tests/test__linprog_clean_inputs.py +5 -3
- scipy/optimize/tests/test__numdiff.py +66 -22
- scipy/optimize/tests/test__remove_redundancy.py +2 -2
- scipy/optimize/tests/test__shgo.py +9 -1
- scipy/optimize/tests/test_bracket.py +36 -46
- scipy/optimize/tests/test_chandrupatla.py +133 -135
- scipy/optimize/tests/test_cobyla.py +74 -45
- scipy/optimize/tests/test_constraints.py +1 -1
- scipy/optimize/tests/test_differentiable_functions.py +226 -6
- scipy/optimize/tests/test_lbfgsb_hessinv.py +22 -0
- scipy/optimize/tests/test_least_squares.py +125 -13
- scipy/optimize/tests/test_linear_assignment.py +3 -3
- scipy/optimize/tests/test_linprog.py +3 -3
- scipy/optimize/tests/test_lsq_linear.py +6 -6
- scipy/optimize/tests/test_minimize_constrained.py +2 -2
- scipy/optimize/tests/test_minpack.py +4 -4
- scipy/optimize/tests/test_nnls.py +43 -3
- scipy/optimize/tests/test_nonlin.py +36 -0
- scipy/optimize/tests/test_optimize.py +95 -17
- scipy/optimize/tests/test_slsqp.py +36 -4
- scipy/optimize/tests/test_zeros.py +34 -1
- scipy/signal/__init__.py +12 -23
- scipy/signal/_delegators.py +568 -0
- scipy/signal/_filter_design.py +459 -241
- scipy/signal/_fir_filter_design.py +262 -90
- scipy/signal/_lti_conversion.py +3 -2
- scipy/signal/_ltisys.py +118 -91
- scipy/signal/_max_len_seq_inner.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_peak_finding_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_polyutils.py +172 -0
- scipy/signal/_short_time_fft.py +519 -70
- scipy/signal/_signal_api.py +30 -0
- scipy/signal/_signaltools.py +719 -399
- scipy/signal/_sigtools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_sosfilt.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_spectral_py.py +230 -50
- scipy/signal/_spline.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_spline_filters.py +108 -68
- scipy/signal/_support_alternative_backends.py +73 -0
- scipy/signal/_upfirdn.py +4 -1
- scipy/signal/_upfirdn_apply.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_waveforms.py +2 -11
- scipy/signal/_wavelets.py +1 -1
- scipy/signal/fir_filter_design.py +1 -0
- scipy/signal/spline.py +4 -11
- scipy/signal/tests/_scipy_spectral_test_shim.py +2 -171
- scipy/signal/tests/test_bsplines.py +114 -79
- scipy/signal/tests/test_cont2discrete.py +9 -2
- scipy/signal/tests/test_filter_design.py +721 -481
- scipy/signal/tests/test_fir_filter_design.py +332 -140
- scipy/signal/tests/test_savitzky_golay.py +4 -3
- scipy/signal/tests/test_short_time_fft.py +221 -3
- scipy/signal/tests/test_signaltools.py +2144 -1348
- scipy/signal/tests/test_spectral.py +50 -6
- scipy/signal/tests/test_splines.py +161 -96
- scipy/signal/tests/test_upfirdn.py +84 -50
- scipy/signal/tests/test_waveforms.py +20 -0
- scipy/signal/tests/test_windows.py +607 -466
- scipy/signal/windows/_windows.py +287 -148
- scipy/sparse/__init__.py +23 -4
- scipy/sparse/_base.py +270 -108
- scipy/sparse/_bsr.py +7 -4
- scipy/sparse/_compressed.py +59 -231
- scipy/sparse/_construct.py +90 -38
- scipy/sparse/_coo.py +115 -181
- scipy/sparse/_csc.py +4 -4
- scipy/sparse/_csparsetools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/_csr.py +2 -2
- scipy/sparse/_data.py +48 -48
- scipy/sparse/_dia.py +105 -18
- scipy/sparse/_dok.py +0 -23
- scipy/sparse/_index.py +4 -4
- scipy/sparse/_matrix.py +23 -0
- scipy/sparse/_sparsetools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/_sputils.py +37 -22
- scipy/sparse/base.py +0 -9
- scipy/sparse/bsr.py +0 -14
- scipy/sparse/compressed.py +0 -23
- scipy/sparse/construct.py +0 -6
- scipy/sparse/coo.py +0 -14
- scipy/sparse/csc.py +0 -3
- scipy/sparse/csgraph/_flow.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_matching.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_reordering.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_shortest_path.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_tools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_traversal.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/tests/test_matching.py +14 -2
- scipy/sparse/csgraph/tests/test_pydata_sparse.py +4 -1
- scipy/sparse/csgraph/tests/test_shortest_path.py +83 -27
- scipy/sparse/csr.py +0 -5
- scipy/sparse/data.py +1 -6
- scipy/sparse/dia.py +0 -7
- scipy/sparse/dok.py +0 -10
- scipy/sparse/linalg/_dsolve/_superlu.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_dsolve/linsolve.py +9 -0
- scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +35 -28
- scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_eigen/arpack/arpack.py +23 -17
- scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +6 -6
- scipy/sparse/linalg/_interface.py +17 -18
- scipy/sparse/linalg/_isolve/_gcrotmk.py +4 -4
- scipy/sparse/linalg/_isolve/iterative.py +51 -45
- scipy/sparse/linalg/_isolve/lgmres.py +6 -6
- scipy/sparse/linalg/_isolve/minres.py +5 -5
- scipy/sparse/linalg/_isolve/tfqmr.py +7 -7
- scipy/sparse/linalg/_isolve/utils.py +2 -8
- scipy/sparse/linalg/_matfuncs.py +1 -1
- scipy/sparse/linalg/_norm.py +1 -1
- scipy/sparse/linalg/_propack/_cpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_spropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_special_sparse_arrays.py +39 -38
- scipy/sparse/linalg/tests/test_pydata_sparse.py +14 -0
- scipy/sparse/tests/test_arithmetic1d.py +5 -2
- scipy/sparse/tests/test_base.py +214 -42
- scipy/sparse/tests/test_common1d.py +7 -7
- scipy/sparse/tests/test_construct.py +1 -1
- scipy/sparse/tests/test_coo.py +272 -4
- scipy/sparse/tests/test_sparsetools.py +5 -0
- scipy/sparse/tests/test_sputils.py +36 -7
- scipy/spatial/_ckdtree.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_distance_pybind.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_distance_wrap.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_hausdorff.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_qhull.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_voronoi.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/distance.py +49 -42
- scipy/spatial/tests/test_distance.py +15 -1
- scipy/spatial/tests/test_kdtree.py +1 -0
- scipy/spatial/tests/test_qhull.py +7 -2
- scipy/spatial/transform/__init__.py +5 -3
- scipy/spatial/transform/_rigid_transform.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/transform/_rotation.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
- scipy/spatial/transform/tests/test_rotation.py +1213 -832
- scipy/spatial/transform/tests/test_rotation_groups.py +3 -3
- scipy/spatial/transform/tests/test_rotation_spline.py +29 -8
- scipy/special/__init__.py +1 -47
- scipy/special/_add_newdocs.py +34 -772
- scipy/special/_basic.py +22 -25
- scipy/special/_comb.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ellip_harm_2.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_gufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_logsumexp.py +67 -58
- scipy/special/_orthogonal.pyi +1 -1
- scipy/special/_specfun.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_special_ufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_spherical_bessel.py +4 -4
- scipy/special/_support_alternative_backends.py +212 -119
- scipy/special/_test_internal.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_testutils.py +4 -4
- scipy/special/_ufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ufuncs.pyi +1 -0
- scipy/special/_ufuncs.pyx +215 -1400
- scipy/special/_ufuncs_cxx.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ufuncs_cxx.pxd +2 -15
- scipy/special/_ufuncs_cxx.pyx +5 -44
- scipy/special/_ufuncs_cxx_defs.h +2 -16
- scipy/special/_ufuncs_defs.h +0 -8
- scipy/special/cython_special.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/cython_special.pxd +1 -1
- scipy/special/tests/_cython_examples/meson.build +10 -1
- scipy/special/tests/test_basic.py +153 -20
- scipy/special/tests/test_boost_ufuncs.py +3 -0
- scipy/special/tests/test_cdflib.py +35 -11
- scipy/special/tests/test_gammainc.py +16 -0
- scipy/special/tests/test_hyp2f1.py +2 -2
- scipy/special/tests/test_log1mexp.py +85 -0
- scipy/special/tests/test_logsumexp.py +206 -64
- scipy/special/tests/test_mpmath.py +1 -0
- scipy/special/tests/test_nan_inputs.py +1 -1
- scipy/special/tests/test_orthogonal.py +17 -18
- scipy/special/tests/test_sf_error.py +3 -2
- scipy/special/tests/test_sph_harm.py +6 -7
- scipy/special/tests/test_support_alternative_backends.py +211 -76
- scipy/stats/__init__.py +4 -1
- scipy/stats/_ansari_swilk_statistics.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_axis_nan_policy.py +5 -12
- scipy/stats/_biasedurn.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_continued_fraction.py +387 -0
- scipy/stats/_continuous_distns.py +277 -310
- scipy/stats/_correlation.py +1 -1
- scipy/stats/_covariance.py +6 -3
- scipy/stats/_discrete_distns.py +39 -32
- scipy/stats/_distn_infrastructure.py +39 -12
- scipy/stats/_distribution_infrastructure.py +900 -238
- scipy/stats/_entropy.py +9 -10
- scipy/{_lib → stats}/_finite_differences.py +1 -1
- scipy/stats/_hypotests.py +83 -50
- scipy/stats/_kde.py +53 -49
- scipy/stats/_ksstats.py +1 -1
- scipy/stats/_levy_stable/__init__.py +7 -15
- scipy/stats/_levy_stable/levyst.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_morestats.py +118 -73
- scipy/stats/_mstats_basic.py +13 -17
- scipy/stats/_mstats_extras.py +8 -8
- scipy/stats/_multivariate.py +89 -113
- scipy/stats/_new_distributions.py +97 -20
- scipy/stats/_page_trend_test.py +12 -5
- scipy/stats/_probability_distribution.py +265 -43
- scipy/stats/_qmc.py +14 -9
- scipy/stats/_qmc_cy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_qmvnt.py +16 -95
- scipy/stats/_qmvnt_cy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_quantile.py +335 -0
- scipy/stats/_rcont/rcont.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_resampling.py +4 -29
- scipy/stats/_sampling.py +1 -1
- scipy/stats/_sobol.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_stats.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_stats_mstats_common.py +21 -2
- scipy/stats/_stats_py.py +550 -476
- scipy/stats/_stats_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.pyi +2 -1
- scipy/stats/_variation.py +6 -8
- scipy/stats/_wilcoxon.py +13 -7
- scipy/stats/tests/common_tests.py +6 -4
- scipy/stats/tests/test_axis_nan_policy.py +62 -24
- scipy/stats/tests/test_continued_fraction.py +173 -0
- scipy/stats/tests/test_continuous.py +379 -60
- scipy/stats/tests/test_continuous_basic.py +18 -12
- scipy/stats/tests/test_discrete_basic.py +14 -8
- scipy/stats/tests/test_discrete_distns.py +16 -16
- scipy/stats/tests/test_distributions.py +95 -75
- scipy/stats/tests/test_entropy.py +40 -48
- scipy/stats/tests/test_fit.py +4 -3
- scipy/stats/tests/test_hypotests.py +153 -24
- scipy/stats/tests/test_kdeoth.py +109 -41
- scipy/stats/tests/test_marray.py +289 -0
- scipy/stats/tests/test_morestats.py +79 -47
- scipy/stats/tests/test_mstats_basic.py +3 -3
- scipy/stats/tests/test_multivariate.py +434 -83
- scipy/stats/tests/test_qmc.py +13 -10
- scipy/stats/tests/test_quantile.py +199 -0
- scipy/stats/tests/test_rank.py +119 -112
- scipy/stats/tests/test_resampling.py +47 -56
- scipy/stats/tests/test_sampling.py +9 -4
- scipy/stats/tests/test_stats.py +799 -939
- scipy/stats/tests/test_variation.py +8 -6
- scipy/version.py +2 -2
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/LICENSE.txt +4 -4
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/METADATA +11 -11
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/RECORD +1262 -1269
- scipy.libs/libgcc_s-69c45f16.so.1 +0 -0
- scipy.libs/libgfortran-db0b6589.so.5.0.0 +0 -0
- scipy.libs/{libstdc++-1b614e01.so.6.0.32 → libstdc++-1f1a71be.so.6.0.33} +0 -0
- scipy/_lib/array_api_extra/_funcs.py +0 -484
- scipy/_lib/array_api_extra/_typing.py +0 -8
- scipy/interpolate/_bspl.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_cobyla.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_cython_nnls.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_slsqp.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/qhull_src/COPYING.txt +0 -38
- scipy/special/libsf_error_state.so +0 -0
- scipy/special/tests/test_log_softmax.py +0 -109
- scipy/special/tests/test_xsf_cuda.py +0 -114
- scipy/special/xsf/binom.h +0 -89
- scipy/special/xsf/cdflib.h +0 -100
- scipy/special/xsf/cephes/airy.h +0 -307
- scipy/special/xsf/cephes/besselpoly.h +0 -51
- scipy/special/xsf/cephes/beta.h +0 -257
- scipy/special/xsf/cephes/cbrt.h +0 -131
- scipy/special/xsf/cephes/chbevl.h +0 -85
- scipy/special/xsf/cephes/chdtr.h +0 -193
- scipy/special/xsf/cephes/const.h +0 -87
- scipy/special/xsf/cephes/ellie.h +0 -293
- scipy/special/xsf/cephes/ellik.h +0 -251
- scipy/special/xsf/cephes/ellpe.h +0 -107
- scipy/special/xsf/cephes/ellpk.h +0 -117
- scipy/special/xsf/cephes/expn.h +0 -260
- scipy/special/xsf/cephes/gamma.h +0 -398
- scipy/special/xsf/cephes/hyp2f1.h +0 -596
- scipy/special/xsf/cephes/hyperg.h +0 -361
- scipy/special/xsf/cephes/i0.h +0 -149
- scipy/special/xsf/cephes/i1.h +0 -158
- scipy/special/xsf/cephes/igam.h +0 -421
- scipy/special/xsf/cephes/igam_asymp_coeff.h +0 -195
- scipy/special/xsf/cephes/igami.h +0 -313
- scipy/special/xsf/cephes/j0.h +0 -225
- scipy/special/xsf/cephes/j1.h +0 -198
- scipy/special/xsf/cephes/jv.h +0 -715
- scipy/special/xsf/cephes/k0.h +0 -164
- scipy/special/xsf/cephes/k1.h +0 -163
- scipy/special/xsf/cephes/kn.h +0 -243
- scipy/special/xsf/cephes/lanczos.h +0 -112
- scipy/special/xsf/cephes/ndtr.h +0 -275
- scipy/special/xsf/cephes/poch.h +0 -85
- scipy/special/xsf/cephes/polevl.h +0 -167
- scipy/special/xsf/cephes/psi.h +0 -194
- scipy/special/xsf/cephes/rgamma.h +0 -111
- scipy/special/xsf/cephes/scipy_iv.h +0 -811
- scipy/special/xsf/cephes/shichi.h +0 -248
- scipy/special/xsf/cephes/sici.h +0 -224
- scipy/special/xsf/cephes/sindg.h +0 -221
- scipy/special/xsf/cephes/tandg.h +0 -139
- scipy/special/xsf/cephes/trig.h +0 -58
- scipy/special/xsf/cephes/unity.h +0 -186
- scipy/special/xsf/cephes/zeta.h +0 -172
- scipy/special/xsf/config.h +0 -304
- scipy/special/xsf/digamma.h +0 -205
- scipy/special/xsf/error.h +0 -57
- scipy/special/xsf/evalpoly.h +0 -47
- scipy/special/xsf/expint.h +0 -266
- scipy/special/xsf/hyp2f1.h +0 -694
- scipy/special/xsf/iv_ratio.h +0 -173
- scipy/special/xsf/lambertw.h +0 -150
- scipy/special/xsf/loggamma.h +0 -163
- scipy/special/xsf/sici.h +0 -200
- scipy/special/xsf/tools.h +0 -427
- scipy/special/xsf/trig.h +0 -164
- scipy/special/xsf/wright_bessel.h +0 -843
- scipy/special/xsf/zlog1.h +0 -35
- scipy/stats/_mvn.cpython-312-aarch64-linux-musl.so +0 -0
- scipy.libs/libgcc_s-7393e603.so.1 +0 -0
- scipy.libs/libgfortran-eb933d8e.so.5.0.0 +0 -0
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/WHEEL +0 -0
@@ -32,29 +32,34 @@
|
|
32
32
|
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
33
33
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
34
|
import numpy as np
|
35
|
-
from numpy.testing import
|
36
|
-
assert_warns)
|
35
|
+
from numpy.testing import assert_allclose, assert_equal, assert_array_equal, assert_
|
37
36
|
import pytest
|
38
37
|
from pytest import raises as assert_raises
|
39
38
|
|
40
|
-
import scipy.cluster.hierarchy
|
41
39
|
from scipy.cluster.hierarchy import (
|
42
40
|
ClusterWarning, linkage, from_mlab_linkage, to_mlab_linkage,
|
43
41
|
num_obs_linkage, inconsistent, cophenet, fclusterdata, fcluster,
|
44
|
-
is_isomorphic, single, leaders,
|
42
|
+
is_isomorphic, single, ward, leaders,
|
45
43
|
correspond, is_monotonic, maxdists, maxinconsts, maxRstat,
|
46
44
|
is_valid_linkage, is_valid_im, to_tree, leaves_list, dendrogram,
|
47
45
|
set_link_color_palette, cut_tree, optimal_leaf_ordering,
|
48
|
-
_order_cluster_tree, _hierarchy, _LINKAGE_METHODS)
|
49
|
-
from scipy.spatial.distance import pdist
|
46
|
+
_order_cluster_tree, _hierarchy, _EUCLIDEAN_METHODS, _LINKAGE_METHODS)
|
50
47
|
from scipy.cluster._hierarchy import Heap
|
51
|
-
from scipy.
|
52
|
-
from scipy._lib._array_api import
|
48
|
+
from scipy.spatial.distance import pdist
|
49
|
+
from scipy._lib._array_api import (eager_warns, make_xp_test_case,
|
50
|
+
xp_assert_close, xp_assert_equal)
|
51
|
+
import scipy._lib.array_api_extra as xpx
|
53
52
|
|
54
53
|
from threading import Lock
|
55
54
|
|
56
55
|
from . import hierarchy_test_data
|
57
56
|
|
57
|
+
class eager:
|
58
|
+
# Bypass xpx.testing.lazy_xp_function when calling
|
59
|
+
# these functions from this namespace
|
60
|
+
is_valid_im = is_valid_im
|
61
|
+
is_valid_linkage = is_valid_linkage
|
62
|
+
|
58
63
|
|
59
64
|
# Matplotlib is not a scipy dependency but is optionally used in dendrogram, so
|
60
65
|
# check if it's available
|
@@ -68,27 +73,24 @@ try:
|
|
68
73
|
except Exception:
|
69
74
|
have_matplotlib = False
|
70
75
|
|
71
|
-
|
72
|
-
pytestmark = [array_api_compatible, pytest.mark.usefixtures("skip_xp_backends")]
|
73
76
|
skip_xp_backends = pytest.mark.skip_xp_backends
|
74
77
|
|
75
78
|
|
79
|
+
@make_xp_test_case(linkage)
|
76
80
|
class TestLinkage:
|
77
81
|
|
78
|
-
@skip_xp_backends(
|
82
|
+
@skip_xp_backends("jax.numpy", reason="Can't raise inside jax.pure_callback")
|
79
83
|
def test_linkage_non_finite_elements_in_distance_matrix(self, xp):
|
80
84
|
# Tests linkage(Y) where Y contains a non-finite element (e.g. NaN or Inf).
|
81
85
|
# Exception expected.
|
82
86
|
y = xp.asarray([xp.nan] + [0.0]*5)
|
83
87
|
assert_raises(ValueError, linkage, y)
|
84
88
|
|
85
|
-
@skip_xp_backends(cpu_only=True)
|
86
89
|
def test_linkage_empty_distance_matrix(self, xp):
|
87
90
|
# Tests linkage(Y) where Y is a 0x4 linkage matrix. Exception expected.
|
88
91
|
y = xp.zeros((0,))
|
89
92
|
assert_raises(ValueError, linkage, y)
|
90
93
|
|
91
|
-
@skip_xp_backends(cpu_only=True)
|
92
94
|
def test_linkage_tdist(self, xp):
|
93
95
|
for method in ['single', 'complete', 'average', 'weighted']:
|
94
96
|
self.check_linkage_tdist(method, xp)
|
@@ -99,7 +101,6 @@ class TestLinkage:
|
|
99
101
|
expectedZ = getattr(hierarchy_test_data, 'linkage_ytdist_' + method)
|
100
102
|
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
|
101
103
|
|
102
|
-
@skip_xp_backends(cpu_only=True)
|
103
104
|
def test_linkage_X(self, xp):
|
104
105
|
for method in ['centroid', 'median', 'ward']:
|
105
106
|
self.check_linkage_q(method, xp)
|
@@ -110,12 +111,11 @@ class TestLinkage:
|
|
110
111
|
expectedZ = getattr(hierarchy_test_data, 'linkage_X_' + method)
|
111
112
|
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
|
112
113
|
|
113
|
-
|
114
|
-
|
115
|
-
Z = linkage(
|
114
|
+
X = xp.asarray(hierarchy_test_data.X)
|
115
|
+
y = pdist(X, metric="euclidean")
|
116
|
+
Z = linkage(y, method)
|
116
117
|
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
|
117
118
|
|
118
|
-
@skip_xp_backends(cpu_only=True)
|
119
119
|
def test_compare_with_trivial(self, xp):
|
120
120
|
rng = np.random.RandomState(0)
|
121
121
|
n = 20
|
@@ -127,46 +127,59 @@ class TestLinkage:
|
|
127
127
|
Z = linkage(xp.asarray(d), method)
|
128
128
|
xp_assert_close(Z, xp.asarray(Z_trivial), rtol=1e-14, atol=1e-15)
|
129
129
|
|
130
|
-
@skip_xp_backends(cpu_only=True)
|
131
130
|
def test_optimal_leaf_ordering(self, xp):
|
132
131
|
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), optimal_ordering=True)
|
133
132
|
expectedZ = getattr(hierarchy_test_data, 'linkage_ytdist_single_olo')
|
134
133
|
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
|
135
134
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
'
|
142
|
-
|
143
|
-
'
|
144
|
-
|
145
|
-
'
|
146
|
-
|
147
|
-
'
|
148
|
-
|
149
|
-
'
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
'ward': np.array([[0, 1, 1.41421356, 2],
|
154
|
-
[2, 3, 2.44948974, 3]]),
|
155
|
-
}
|
156
|
-
|
157
|
-
def test_linkage_ties(self, xp):
|
158
|
-
for method in ['single', 'complete', 'average', 'weighted',
|
159
|
-
'centroid', 'median', 'ward']:
|
160
|
-
self.check_linkage_ties(method, xp)
|
161
|
-
|
162
|
-
def check_linkage_ties(self, method, xp):
|
135
|
+
@pytest.mark.parametrize("method,expect", [
|
136
|
+
('single', [[0, 1, 1.41421356, 2],
|
137
|
+
[2, 3, 1.41421356, 3]]),
|
138
|
+
('complete', [[0, 1, 1.41421356, 2],
|
139
|
+
[2, 3, 2.82842712, 3]]),
|
140
|
+
('average', [[0, 1, 1.41421356, 2],
|
141
|
+
[2, 3, 2.12132034, 3]]),
|
142
|
+
('weighted', [[0, 1, 1.41421356, 2],
|
143
|
+
[2, 3, 2.12132034, 3]]),
|
144
|
+
('centroid', [[0, 1, 1.41421356, 2],
|
145
|
+
[2, 3, 2.12132034, 3]]),
|
146
|
+
('median', [[0, 1, 1.41421356, 2],
|
147
|
+
[2, 3, 2.12132034, 3]]),
|
148
|
+
('ward', [[0, 1, 1.41421356, 2],
|
149
|
+
[2, 3, 2.44948974, 3]]),
|
150
|
+
])
|
151
|
+
def test_linkage_ties(self, method, expect, xp):
|
163
152
|
X = xp.asarray([[-1, -1], [0, 0], [1, 1]])
|
164
153
|
Z = linkage(X, method=method)
|
165
|
-
|
166
|
-
xp_assert_close(Z,
|
167
|
-
|
168
|
-
|
169
|
-
|
154
|
+
expect = xp.asarray(expect, dtype=xp.float64)
|
155
|
+
xp_assert_close(Z, expect, atol=1e-06)
|
156
|
+
|
157
|
+
def test_unsupported_uncondensed_distance_matrix_linkage_warning(self, xp):
|
158
|
+
X = xp.asarray([[0, 1], [1, 0]])
|
159
|
+
with eager_warns(X, ClusterWarning):
|
160
|
+
linkage(X)
|
161
|
+
|
162
|
+
@pytest.mark.parametrize("method", _EUCLIDEAN_METHODS)
|
163
|
+
def test_euclidean_linkage_value_error(self, method, xp):
|
164
|
+
X = xp.asarray([[1, 1], [1, 1]])
|
165
|
+
with pytest.raises(ValueError):
|
166
|
+
linkage(X, method=method, metric='cityblock')
|
167
|
+
|
168
|
+
def test_2x2_linkage(self, xp):
|
169
|
+
Z1 = linkage(xp.asarray([1]), method='single', metric='euclidean')
|
170
|
+
Z2 = linkage(xp.asarray([[0, 1], [0, 0]]), method='single', metric='euclidean')
|
171
|
+
xp_assert_close(Z1, Z2, rtol=1e-15)
|
172
|
+
|
173
|
+
@skip_xp_backends("jax.numpy", reason="Can't raise inside jax.pure_callback")
|
174
|
+
def test_centroid_neg_distance(self, xp):
|
175
|
+
# gh-21011
|
176
|
+
values = xp.asarray([0, 0, -1])
|
177
|
+
with pytest.raises(ValueError):
|
178
|
+
# This is just checking that this doesn't crash
|
179
|
+
linkage(values, method='centroid')
|
180
|
+
|
181
|
+
|
182
|
+
@make_xp_test_case(inconsistent)
|
170
183
|
class TestInconsistent:
|
171
184
|
|
172
185
|
def test_inconsistent_tdist(self, xp):
|
@@ -179,7 +192,7 @@ class TestInconsistent:
|
|
179
192
|
xp.asarray(hierarchy_test_data.inconsistent_ytdist[depth]))
|
180
193
|
|
181
194
|
|
182
|
-
@
|
195
|
+
@make_xp_test_case(cophenet)
|
183
196
|
class TestCopheneticDistance:
|
184
197
|
|
185
198
|
def test_linkage_cophenet_tdist_Z(self, xp):
|
@@ -200,6 +213,7 @@ class TestCopheneticDistance:
|
|
200
213
|
xp_assert_close(c, expectedc, atol=1e-10)
|
201
214
|
xp_assert_close(M, expectedM, atol=1e-10)
|
202
215
|
|
216
|
+
@skip_xp_backends("jax.numpy", reason="Can't raise inside jax.pure_callback")
|
203
217
|
def test_gh_22183(self, xp):
|
204
218
|
# check for lack of segfault
|
205
219
|
# (out of bounds memory access)
|
@@ -220,6 +234,7 @@ class TestCopheneticDistance:
|
|
220
234
|
cophenet(xp.asarray(arr))
|
221
235
|
|
222
236
|
|
237
|
+
@make_xp_test_case(from_mlab_linkage, to_mlab_linkage)
|
223
238
|
class TestMLabLinkageConversion:
|
224
239
|
|
225
240
|
def test_mlab_linkage_conversion_empty(self, xp):
|
@@ -228,7 +243,6 @@ class TestMLabLinkageConversion:
|
|
228
243
|
xp_assert_equal(from_mlab_linkage(X), X)
|
229
244
|
xp_assert_equal(to_mlab_linkage(X), X)
|
230
245
|
|
231
|
-
@skip_xp_backends(cpu_only=True)
|
232
246
|
def test_mlab_linkage_conversion_single_row(self, xp):
|
233
247
|
# Tests from/to_mlab_linkage on linkage array with single row.
|
234
248
|
Z = xp.asarray([[0., 1., 3., 2.]])
|
@@ -238,7 +252,6 @@ class TestMLabLinkageConversion:
|
|
238
252
|
xp_assert_close(to_mlab_linkage(Z), xp.asarray(Zm, dtype=xp.float64),
|
239
253
|
rtol=1e-15)
|
240
254
|
|
241
|
-
@skip_xp_backends(cpu_only=True)
|
242
255
|
def test_mlab_linkage_conversion_multiple_rows(self, xp):
|
243
256
|
# Tests from/to_mlab_linkage on linkage array with multiple rows.
|
244
257
|
Zm = xp.asarray([[3, 6, 138], [4, 5, 219],
|
@@ -254,57 +267,56 @@ class TestMLabLinkageConversion:
|
|
254
267
|
rtol=1e-15)
|
255
268
|
|
256
269
|
|
257
|
-
@
|
258
|
-
class
|
270
|
+
@make_xp_test_case(fclusterdata)
|
271
|
+
class TestFclusterData:
|
259
272
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
for t in hierarchy_test_data.fcluster_distance
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
def check_fclusterdata(self, t, criterion, xp):
|
273
|
+
@make_xp_test_case(is_isomorphic)
|
274
|
+
@pytest.mark.parametrize("criterion,t",
|
275
|
+
[("inconsistent", t) for t in hierarchy_test_data.fcluster_inconsistent]
|
276
|
+
+ [("distance", t) for t in hierarchy_test_data.fcluster_distance]
|
277
|
+
+ [("maxclust", t) for t in hierarchy_test_data.fcluster_maxclust]
|
278
|
+
)
|
279
|
+
def test_fclusterdata(self, t, criterion, xp):
|
269
280
|
# Tests fclusterdata(X, criterion=criterion, t=t) on a random 3-cluster data set
|
270
281
|
expectedT = xp.asarray(getattr(hierarchy_test_data, 'fcluster_' + criterion)[t])
|
271
282
|
X = xp.asarray(hierarchy_test_data.Q_X)
|
272
283
|
T = fclusterdata(X, criterion=criterion, t=t)
|
273
|
-
|
284
|
+
assert is_isomorphic(T, expectedT)
|
285
|
+
|
274
286
|
|
275
|
-
|
276
|
-
|
277
|
-
self.check_fcluster(t, 'inconsistent', xp)
|
278
|
-
for t in hierarchy_test_data.fcluster_distance:
|
279
|
-
self.check_fcluster(t, 'distance', xp)
|
280
|
-
for t in hierarchy_test_data.fcluster_maxclust:
|
281
|
-
self.check_fcluster(t, 'maxclust', xp)
|
287
|
+
@make_xp_test_case(fcluster)
|
288
|
+
class TestFcluster:
|
282
289
|
|
283
|
-
|
290
|
+
@make_xp_test_case(single, is_isomorphic)
|
291
|
+
@pytest.mark.parametrize("criterion,t",
|
292
|
+
[("inconsistent", t) for t in hierarchy_test_data.fcluster_inconsistent]
|
293
|
+
+ [("distance", t) for t in hierarchy_test_data.fcluster_distance]
|
294
|
+
+ [("maxclust", t) for t in hierarchy_test_data.fcluster_maxclust]
|
295
|
+
)
|
296
|
+
def test_fcluster(self, t, criterion, xp):
|
284
297
|
# Tests fcluster(Z, criterion=criterion, t=t) on a random 3-cluster data set.
|
285
298
|
expectedT = xp.asarray(getattr(hierarchy_test_data, 'fcluster_' + criterion)[t])
|
286
299
|
Z = single(xp.asarray(hierarchy_test_data.Q_X))
|
287
300
|
T = fcluster(Z, criterion=criterion, t=t)
|
288
301
|
assert_(is_isomorphic(T, expectedT))
|
289
302
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
for t in hierarchy_test_data.fcluster_maxclust:
|
294
|
-
self.check_fcluster_maxclust_monocrit(t, xp)
|
295
|
-
|
296
|
-
def check_fcluster_monocrit(self, t, xp):
|
303
|
+
@make_xp_test_case(single, is_isomorphic, maxdists)
|
304
|
+
@pytest.mark.parametrize("t", hierarchy_test_data.fcluster_distance)
|
305
|
+
def test_fcluster_monocrit(self, t, xp):
|
297
306
|
expectedT = xp.asarray(hierarchy_test_data.fcluster_distance[t])
|
298
307
|
Z = single(xp.asarray(hierarchy_test_data.Q_X))
|
299
308
|
T = fcluster(Z, t, criterion='monocrit', monocrit=maxdists(Z))
|
300
309
|
assert_(is_isomorphic(T, expectedT))
|
301
310
|
|
302
|
-
|
311
|
+
@make_xp_test_case(single, is_isomorphic, maxdists)
|
312
|
+
@pytest.mark.parametrize("t", hierarchy_test_data.fcluster_maxclust)
|
313
|
+
def test_fcluster_maxclust_monocrit(self, t, xp):
|
303
314
|
expectedT = xp.asarray(hierarchy_test_data.fcluster_maxclust[t])
|
304
315
|
Z = single(xp.asarray(hierarchy_test_data.Q_X))
|
305
316
|
T = fcluster(Z, t, criterion='maxclust_monocrit', monocrit=maxdists(Z))
|
306
317
|
assert_(is_isomorphic(T, expectedT))
|
307
318
|
|
319
|
+
@make_xp_test_case(single)
|
308
320
|
def test_fcluster_maxclust_gh_12651(self, xp):
|
309
321
|
y = xp.asarray([[1], [4], [5]])
|
310
322
|
Z = single(y)
|
@@ -318,29 +330,26 @@ class TestFcluster:
|
|
318
330
|
xp.asarray([1, 2, 3]))
|
319
331
|
|
320
332
|
|
321
|
-
@
|
333
|
+
@make_xp_test_case(leaders)
|
322
334
|
class TestLeaders:
|
323
335
|
|
324
336
|
def test_leaders_single(self, xp):
|
325
337
|
# Tests leaders using a flat clustering generated by single linkage.
|
326
338
|
X = hierarchy_test_data.Q_X
|
327
339
|
Y = pdist(X)
|
328
|
-
Y = xp.asarray(Y)
|
329
340
|
Z = linkage(Y)
|
330
341
|
T = fcluster(Z, criterion='maxclust', t=3)
|
331
|
-
|
342
|
+
Z = xp.asarray(Z)
|
332
343
|
T = xp.asarray(T, dtype=xp.int32)
|
333
344
|
L = leaders(Z, T)
|
334
|
-
|
345
|
+
expect = xp.asarray([53, 55, 56, 2, 3, 1], dtype=xp.int32)
|
346
|
+
xp_assert_close(xp.concat(L), expect, rtol=1e-15)
|
335
347
|
|
336
348
|
|
337
|
-
@
|
338
|
-
reason='`is_isomorphic` only supports NumPy backend')
|
349
|
+
@make_xp_test_case(is_isomorphic)
|
339
350
|
class TestIsIsomorphic:
|
340
351
|
|
341
|
-
|
342
|
-
reason='array-likes only supported for NumPy backend')
|
343
|
-
def test_array_like(self, xp):
|
352
|
+
def test_array_like(self):
|
344
353
|
assert is_isomorphic([1, 1, 1], [2, 2, 2])
|
345
354
|
assert is_isomorphic([], [])
|
346
355
|
|
@@ -377,8 +386,8 @@ class TestIsIsomorphic:
|
|
377
386
|
# (3 flat clusters, different labelings, nonisomorphic)
|
378
387
|
a = xp.asarray([1, 2, 3, 3])
|
379
388
|
b = xp.asarray([1, 3, 2, 3])
|
380
|
-
assert is_isomorphic(a, b)
|
381
|
-
assert is_isomorphic(b, a)
|
389
|
+
assert not is_isomorphic(a, b)
|
390
|
+
assert not is_isomorphic(b, a)
|
382
391
|
|
383
392
|
def test_is_isomorphic_4C(self, xp):
|
384
393
|
# Tests is_isomorphic on test case #4C
|
@@ -388,18 +397,18 @@ class TestIsIsomorphic:
|
|
388
397
|
assert is_isomorphic(a, b)
|
389
398
|
assert is_isomorphic(b, a)
|
390
399
|
|
391
|
-
|
400
|
+
@pytest.mark.parametrize("nclusters", [2, 3, 5])
|
401
|
+
def test_is_isomorphic_5(self, nclusters, xp):
|
392
402
|
# Tests is_isomorphic on test case #5 (1000 observations, 2/3/5 random
|
393
403
|
# clusters, random permutation of the labeling).
|
394
|
-
|
395
|
-
self.help_is_isomorphic_randperm(1000, nc, xp=xp)
|
404
|
+
self.is_isomorphic_randperm(1000, nclusters, xp=xp)
|
396
405
|
|
397
|
-
|
406
|
+
@pytest.mark.parametrize("nclusters", [2, 3, 5])
|
407
|
+
def test_is_isomorphic_6(self, nclusters, xp):
|
398
408
|
# Tests is_isomorphic on test case #5A (1000 observations, 2/3/5 random
|
399
409
|
# clusters, random permutation of the labeling, slightly
|
400
410
|
# nonisomorphic.)
|
401
|
-
|
402
|
-
self.help_is_isomorphic_randperm(1000, nc, True, 5, xp=xp)
|
411
|
+
self.is_isomorphic_randperm(1000, nclusters, True, 5, xp=xp)
|
403
412
|
|
404
413
|
def test_is_isomorphic_7(self, xp):
|
405
414
|
# Regression test for gh-6271
|
@@ -407,9 +416,8 @@ class TestIsIsomorphic:
|
|
407
416
|
b = xp.asarray([1, 1, 1])
|
408
417
|
assert not is_isomorphic(a, b)
|
409
418
|
|
410
|
-
def
|
411
|
-
|
412
|
-
for k in range(3):
|
419
|
+
def is_isomorphic_randperm(self, nobs, nclusters, noniso=False, nerrors=0, *, xp):
|
420
|
+
for _ in range(3):
|
413
421
|
a = (np.random.rand(nobs) * nclusters).astype(int)
|
414
422
|
b = np.zeros(a.size, dtype=int)
|
415
423
|
P = np.random.permutation(nclusters)
|
@@ -425,20 +433,17 @@ class TestIsIsomorphic:
|
|
425
433
|
assert is_isomorphic(b, a) == (not noniso)
|
426
434
|
|
427
435
|
|
428
|
-
@
|
436
|
+
@make_xp_test_case(is_valid_linkage)
|
429
437
|
class TestIsValidLinkage:
|
430
438
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
self.check_is_valid_linkage_various_size(nrow, ncol, valid, xp)
|
435
|
-
|
436
|
-
def check_is_valid_linkage_various_size(self, nrow, ncol, valid, xp):
|
439
|
+
@pytest.mark.parametrize("nrow, ncol, valid", [(2, 5, False), (2, 3, False),
|
440
|
+
(1, 4, True), (2, 4, True)])
|
441
|
+
def test_is_valid_linkage_various_size(self, nrow, ncol, valid, xp):
|
437
442
|
# Tests is_valid_linkage(Z) with linkage matrices of various sizes
|
438
443
|
Z = xp.asarray([[0, 1, 3.0, 2, 5],
|
439
444
|
[3, 2, 4.0, 3, 3]], dtype=xp.float64)
|
440
445
|
Z = Z[:nrow, :ncol]
|
441
|
-
|
446
|
+
xp_assert_equal(is_valid_linkage(Z), valid, check_namespace=False)
|
442
447
|
if not valid:
|
443
448
|
assert_raises(ValueError, is_valid_linkage, Z, throw=True)
|
444
449
|
|
@@ -446,13 +451,13 @@ class TestIsValidLinkage:
|
|
446
451
|
# Tests is_valid_linkage(Z) with integer type.
|
447
452
|
Z = xp.asarray([[0, 1, 3.0, 2],
|
448
453
|
[3, 2, 4.0, 3]], dtype=xp.int64)
|
449
|
-
|
454
|
+
xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
|
450
455
|
assert_raises(TypeError, is_valid_linkage, Z, throw=True)
|
451
456
|
|
452
457
|
def test_is_valid_linkage_empty(self, xp):
|
453
458
|
# Tests is_valid_linkage(Z) with empty linkage.
|
454
459
|
Z = xp.zeros((0, 4), dtype=xp.float64)
|
455
|
-
|
460
|
+
xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
|
456
461
|
assert_raises(ValueError, is_valid_linkage, Z, throw=True)
|
457
462
|
|
458
463
|
def test_is_valid_linkage_4_and_up(self, xp):
|
@@ -460,91 +465,84 @@ class TestIsValidLinkage:
|
|
460
465
|
# sizes 4 and 15 (step size 3).
|
461
466
|
for i in range(4, 15, 3):
|
462
467
|
y = np.random.rand(i*(i-1)//2)
|
468
|
+
Z = xp.asarray(linkage(y))
|
463
469
|
y = xp.asarray(y)
|
464
|
-
Z =
|
465
|
-
assert_(is_valid_linkage(Z) is True)
|
470
|
+
xp_assert_equal(is_valid_linkage(Z), True, check_namespace=False)
|
466
471
|
|
467
|
-
@skip_xp_backends('jax.numpy',
|
468
|
-
reason='jax arrays do not support item assignment')
|
469
472
|
def test_is_valid_linkage_4_and_up_neg_index_left(self, xp):
|
470
473
|
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
471
474
|
# sizes 4 and 15 (step size 3) with negative indices (left).
|
472
475
|
for i in range(4, 15, 3):
|
473
476
|
y = np.random.rand(i*(i-1)//2)
|
477
|
+
Z = xp.asarray(linkage(y))
|
474
478
|
y = xp.asarray(y)
|
475
|
-
Z =
|
476
|
-
Z
|
477
|
-
|
478
|
-
|
479
|
+
Z = xpx.at(Z)[i//2, 0].set(-2)
|
480
|
+
xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
|
481
|
+
with pytest.raises(ValueError):
|
482
|
+
eager.is_valid_linkage(Z, throw=True)
|
479
483
|
|
480
|
-
@skip_xp_backends('jax.numpy',
|
481
|
-
reason='jax arrays do not support item assignment')
|
482
484
|
def test_is_valid_linkage_4_and_up_neg_index_right(self, xp):
|
483
485
|
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
484
486
|
# sizes 4 and 15 (step size 3) with negative indices (right).
|
485
487
|
for i in range(4, 15, 3):
|
486
488
|
y = np.random.rand(i*(i-1)//2)
|
489
|
+
Z = xp.asarray(linkage(y))
|
487
490
|
y = xp.asarray(y)
|
488
|
-
Z =
|
489
|
-
Z
|
490
|
-
|
491
|
-
|
491
|
+
Z = xpx.at(Z)[i//2, 1].set(-2)
|
492
|
+
xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
|
493
|
+
with pytest.raises(ValueError):
|
494
|
+
eager.is_valid_linkage(Z, throw=True)
|
492
495
|
|
493
|
-
@skip_xp_backends('jax.numpy',
|
494
|
-
reason='jax arrays do not support item assignment')
|
495
496
|
def test_is_valid_linkage_4_and_up_neg_dist(self, xp):
|
496
497
|
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
497
498
|
# sizes 4 and 15 (step size 3) with negative distances.
|
498
499
|
for i in range(4, 15, 3):
|
499
500
|
y = np.random.rand(i*(i-1)//2)
|
501
|
+
Z = xp.asarray(linkage(y))
|
500
502
|
y = xp.asarray(y)
|
501
|
-
Z =
|
502
|
-
Z
|
503
|
-
|
504
|
-
|
503
|
+
Z = xpx.at(Z)[i//2, 2].set(-0.5)
|
504
|
+
xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
|
505
|
+
with pytest.raises(ValueError):
|
506
|
+
eager.is_valid_linkage(Z, throw=True)
|
505
507
|
|
506
|
-
@skip_xp_backends('jax.numpy',
|
507
|
-
reason='jax arrays do not support item assignment')
|
508
508
|
def test_is_valid_linkage_4_and_up_neg_counts(self, xp):
|
509
509
|
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
510
510
|
# sizes 4 and 15 (step size 3) with negative counts.
|
511
511
|
for i in range(4, 15, 3):
|
512
512
|
y = np.random.rand(i*(i-1)//2)
|
513
|
+
Z = xp.asarray(linkage(y))
|
513
514
|
y = xp.asarray(y)
|
514
|
-
Z =
|
515
|
-
Z
|
516
|
-
|
517
|
-
|
515
|
+
Z = xpx.at(Z)[i//2, 3].set(-2)
|
516
|
+
xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
|
517
|
+
with pytest.raises(ValueError):
|
518
|
+
eager.is_valid_linkage(Z, throw=True)
|
518
519
|
|
519
520
|
|
520
|
-
@
|
521
|
+
@make_xp_test_case(is_valid_im)
|
521
522
|
class TestIsValidInconsistent:
|
522
523
|
|
523
524
|
def test_is_valid_im_int_type(self, xp):
|
524
525
|
# Tests is_valid_im(R) with integer type.
|
525
526
|
R = xp.asarray([[0, 1, 3.0, 2],
|
526
527
|
[3, 2, 4.0, 3]], dtype=xp.int64)
|
527
|
-
|
528
|
+
xp_assert_equal(is_valid_im(R), False, check_namespace=False)
|
528
529
|
assert_raises(TypeError, is_valid_im, R, throw=True)
|
529
530
|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
self.check_is_valid_im_various_size(nrow, ncol, valid, xp)
|
534
|
-
|
535
|
-
def check_is_valid_im_various_size(self, nrow, ncol, valid, xp):
|
531
|
+
@pytest.mark.parametrize("nrow, ncol, valid", [(2, 5, False), (2, 3, False),
|
532
|
+
(1, 4, True), (2, 4, True)])
|
533
|
+
def test_is_valid_im_various_size(self, nrow, ncol, valid, xp):
|
536
534
|
# Tests is_valid_im(R) with linkage matrices of various sizes
|
537
535
|
R = xp.asarray([[0, 1, 3.0, 2, 5],
|
538
536
|
[3, 2, 4.0, 3, 3]], dtype=xp.float64)
|
539
537
|
R = R[:nrow, :ncol]
|
540
|
-
|
538
|
+
xp_assert_equal(is_valid_im(R), valid, check_namespace=False)
|
541
539
|
if not valid:
|
542
540
|
assert_raises(ValueError, is_valid_im, R, throw=True)
|
543
541
|
|
544
542
|
def test_is_valid_im_empty(self, xp):
|
545
543
|
# Tests is_valid_im(R) with empty inconsistency matrix.
|
546
544
|
R = xp.zeros((0, 4), dtype=xp.float64)
|
547
|
-
|
545
|
+
xp_assert_equal(is_valid_im(R), False, check_namespace=False)
|
548
546
|
assert_raises(ValueError, is_valid_im, R, throw=True)
|
549
547
|
|
550
548
|
def test_is_valid_im_4_and_up(self, xp):
|
@@ -552,54 +550,53 @@ class TestIsValidInconsistent:
|
|
552
550
|
# (step size 3).
|
553
551
|
for i in range(4, 15, 3):
|
554
552
|
y = np.random.rand(i*(i-1)//2)
|
555
|
-
y = xp.asarray(y)
|
556
553
|
Z = linkage(y)
|
557
554
|
R = inconsistent(Z)
|
558
|
-
|
555
|
+
R = xp.asarray(R)
|
556
|
+
xp_assert_equal(is_valid_im(R), True, check_namespace=False)
|
559
557
|
|
560
|
-
@skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
|
561
558
|
def test_is_valid_im_4_and_up_neg_index_left(self, xp):
|
562
559
|
# Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
|
563
560
|
# (step size 3) with negative link height means.
|
564
561
|
for i in range(4, 15, 3):
|
565
562
|
y = np.random.rand(i*(i-1)//2)
|
566
|
-
y = xp.asarray(y)
|
567
563
|
Z = linkage(y)
|
568
564
|
R = inconsistent(Z)
|
569
|
-
R[i//2,0]
|
570
|
-
|
571
|
-
|
565
|
+
R = xpx.at(R)[i//2 , 0].set(-2.0)
|
566
|
+
R = xp.asarray(R)
|
567
|
+
xp_assert_equal(is_valid_im(R), False, check_namespace=False)
|
568
|
+
with pytest.raises(ValueError):
|
569
|
+
eager.is_valid_im(R, throw=True)
|
572
570
|
|
573
|
-
@skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
|
574
571
|
def test_is_valid_im_4_and_up_neg_index_right(self, xp):
|
575
572
|
# Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
|
576
573
|
# (step size 3) with negative link height standard deviations.
|
577
574
|
for i in range(4, 15, 3):
|
578
575
|
y = np.random.rand(i*(i-1)//2)
|
579
|
-
y = xp.asarray(y)
|
580
576
|
Z = linkage(y)
|
581
577
|
R = inconsistent(Z)
|
582
|
-
R[i//2,1]
|
583
|
-
|
584
|
-
|
578
|
+
R = xpx.at(R)[i//2 , 1].set(-2.0)
|
579
|
+
R = xp.asarray(R)
|
580
|
+
xp_assert_equal(is_valid_im(R), False, check_namespace=False)
|
581
|
+
with pytest.raises(ValueError):
|
582
|
+
eager.is_valid_im(R, throw=True)
|
585
583
|
|
586
|
-
@skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
|
587
584
|
def test_is_valid_im_4_and_up_neg_dist(self, xp):
|
588
585
|
# Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
|
589
586
|
# (step size 3) with negative link counts.
|
590
587
|
for i in range(4, 15, 3):
|
591
588
|
y = np.random.rand(i*(i-1)//2)
|
592
|
-
y = xp.asarray(y)
|
593
589
|
Z = linkage(y)
|
594
590
|
R = inconsistent(Z)
|
595
|
-
R[i//2,2]
|
596
|
-
|
597
|
-
|
591
|
+
R = xpx.at(R)[i//2, 2].set(-0.5)
|
592
|
+
R = xp.asarray(R)
|
593
|
+
xp_assert_equal(is_valid_im(R), False, check_namespace=False)
|
594
|
+
with pytest.raises(ValueError):
|
595
|
+
eager.is_valid_im(R, throw=True)
|
598
596
|
|
599
597
|
|
600
598
|
class TestNumObsLinkage:
|
601
599
|
|
602
|
-
@skip_xp_backends(cpu_only=True)
|
603
600
|
def test_num_obs_linkage_empty(self, xp):
|
604
601
|
# Tests num_obs_linkage(Z) with empty linkage.
|
605
602
|
Z = xp.zeros((0, 4), dtype=xp.float64)
|
@@ -608,26 +605,32 @@ class TestNumObsLinkage:
|
|
608
605
|
def test_num_obs_linkage_1x4(self, xp):
|
609
606
|
# Tests num_obs_linkage(Z) on linkage over 2 observations.
|
610
607
|
Z = xp.asarray([[0, 1, 3.0, 2]], dtype=xp.float64)
|
611
|
-
|
608
|
+
assert num_obs_linkage(Z) == 2
|
612
609
|
|
613
610
|
def test_num_obs_linkage_2x4(self, xp):
|
614
611
|
# Tests num_obs_linkage(Z) on linkage over 3 observations.
|
615
612
|
Z = xp.asarray([[0, 1, 3.0, 2],
|
616
613
|
[3, 2, 4.0, 3]], dtype=xp.float64)
|
617
|
-
|
614
|
+
assert num_obs_linkage(Z) == 3
|
618
615
|
|
619
|
-
@skip_xp_backends(cpu_only=True)
|
620
616
|
def test_num_obs_linkage_4_and_up(self, xp):
|
621
617
|
# Tests num_obs_linkage(Z) on linkage on observation sets between sizes
|
622
618
|
# 4 and 15 (step size 3).
|
623
619
|
for i in range(4, 15, 3):
|
624
620
|
y = np.random.rand(i*(i-1)//2)
|
625
|
-
|
626
|
-
Z
|
627
|
-
assert_equal(num_obs_linkage(Z), i)
|
621
|
+
Z = xp.asarray(linkage(y))
|
622
|
+
assert num_obs_linkage(Z) == i
|
628
623
|
|
624
|
+
def test_num_obs_linkage_multi_matrix(self, xp):
|
625
|
+
# Tests num_obs_linkage with observation matrices of multiple sizes.
|
626
|
+
for n in range(2, 10):
|
627
|
+
X = np.random.rand(n, 4)
|
628
|
+
Y = pdist(X)
|
629
|
+
Z = xp.asarray(linkage(Y))
|
630
|
+
assert num_obs_linkage(Z) == n
|
629
631
|
|
630
|
-
|
632
|
+
|
633
|
+
@make_xp_test_case(leaves_list, to_tree)
|
631
634
|
class TestLeavesList:
|
632
635
|
|
633
636
|
def test_leaves_list_1x4(self, xp):
|
@@ -643,29 +646,26 @@ class TestLeavesList:
|
|
643
646
|
to_tree(Z)
|
644
647
|
assert_allclose(leaves_list(Z), [0, 1, 2], rtol=1e-15)
|
645
648
|
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
self.check_leaves_list_Q(method, xp)
|
650
|
-
|
651
|
-
def check_leaves_list_Q(self, method, xp):
|
649
|
+
@pytest.mark.parametrize("method",
|
650
|
+
['single', 'complete', 'average', 'weighted', 'centroid', 'median', 'ward'])
|
651
|
+
def test_leaves_list_Q(self, method, xp):
|
652
652
|
# Tests leaves_list(Z) on the Q data set
|
653
|
-
X =
|
654
|
-
Z = linkage(X, method)
|
653
|
+
X = hierarchy_test_data.Q_X
|
654
|
+
Z = xp.asarray(linkage(X, method))
|
655
655
|
node = to_tree(Z)
|
656
656
|
assert_allclose(node.pre_order(), leaves_list(Z), rtol=1e-15)
|
657
657
|
|
658
658
|
def test_Q_subtree_pre_order(self, xp):
|
659
659
|
# Tests that pre_order() works when called on sub-trees.
|
660
|
-
X =
|
661
|
-
Z = linkage(X, 'single')
|
660
|
+
X = hierarchy_test_data.Q_X
|
661
|
+
Z = xp.asarray(linkage(X, 'single'))
|
662
662
|
node = to_tree(Z)
|
663
|
-
assert_allclose(node.pre_order(),
|
664
|
-
|
663
|
+
assert_allclose(node.pre_order(),
|
664
|
+
(node.get_left().pre_order() + node.get_right().pre_order()),
|
665
665
|
rtol=1e-15)
|
666
666
|
|
667
667
|
|
668
|
-
@
|
668
|
+
@make_xp_test_case(correspond)
|
669
669
|
class TestCorrespond:
|
670
670
|
|
671
671
|
def test_correspond_empty(self, xp):
|
@@ -679,13 +679,13 @@ class TestCorrespond:
|
|
679
679
|
# different sizes.
|
680
680
|
for i in range(2, 4):
|
681
681
|
y = np.random.rand(i*(i-1)//2)
|
682
|
+
Z = xp.asarray(linkage(y))
|
682
683
|
y = xp.asarray(y)
|
683
|
-
Z = linkage(y)
|
684
684
|
assert_(correspond(Z, y))
|
685
685
|
for i in range(4, 15, 3):
|
686
686
|
y = np.random.rand(i*(i-1)//2)
|
687
|
+
Z = xp.asarray(linkage(y))
|
687
688
|
y = xp.asarray(y)
|
688
|
-
Z = linkage(y)
|
689
689
|
assert_(correspond(Z, y))
|
690
690
|
|
691
691
|
def test_correspond_4_and_up(self, xp):
|
@@ -695,10 +695,10 @@ class TestCorrespond:
|
|
695
695
|
list(zip(list(range(3, 5)), list(range(2, 4))))):
|
696
696
|
y = np.random.rand(i*(i-1)//2)
|
697
697
|
y2 = np.random.rand(j*(j-1)//2)
|
698
|
+
Z = xp.asarray(linkage(y))
|
699
|
+
Z2 = xp.asarray(linkage(y2))
|
698
700
|
y = xp.asarray(y)
|
699
701
|
y2 = xp.asarray(y2)
|
700
|
-
Z = linkage(y)
|
701
|
-
Z2 = linkage(y2)
|
702
702
|
assert not correspond(Z, y2)
|
703
703
|
assert not correspond(Z2, y)
|
704
704
|
|
@@ -709,24 +709,15 @@ class TestCorrespond:
|
|
709
709
|
list(zip(list(range(2, 7)), list(range(16, 21))))):
|
710
710
|
y = np.random.rand(i*(i-1)//2)
|
711
711
|
y2 = np.random.rand(j*(j-1)//2)
|
712
|
+
Z = xp.asarray(linkage(y))
|
713
|
+
Z2 = xp.asarray(linkage(y2))
|
712
714
|
y = xp.asarray(y)
|
713
715
|
y2 = xp.asarray(y2)
|
714
|
-
Z = linkage(y)
|
715
|
-
Z2 = linkage(y2)
|
716
716
|
assert not correspond(Z, y2)
|
717
717
|
assert not correspond(Z2, y)
|
718
718
|
|
719
|
-
def test_num_obs_linkage_multi_matrix(self, xp):
|
720
|
-
# Tests num_obs_linkage with observation matrices of multiple sizes.
|
721
|
-
for n in range(2, 10):
|
722
|
-
X = np.random.rand(n, 4)
|
723
|
-
Y = pdist(X)
|
724
|
-
Y = xp.asarray(Y)
|
725
|
-
Z = linkage(Y)
|
726
|
-
assert_equal(num_obs_linkage(Z), n)
|
727
|
-
|
728
719
|
|
729
|
-
@
|
720
|
+
@make_xp_test_case(is_monotonic)
|
730
721
|
class TestIsMonotonic:
|
731
722
|
|
732
723
|
def test_is_monotonic_empty(self, xp):
|
@@ -782,26 +773,25 @@ class TestIsMonotonic:
|
|
782
773
|
def test_is_monotonic_tdist_linkage1(self, xp):
|
783
774
|
# Tests is_monotonic(Z) on clustering generated by single linkage on
|
784
775
|
# tdist data set. Expecting True.
|
785
|
-
Z =
|
776
|
+
Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
|
786
777
|
assert is_monotonic(Z)
|
787
778
|
|
788
|
-
@skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
|
789
779
|
def test_is_monotonic_tdist_linkage2(self, xp):
|
790
780
|
# Tests is_monotonic(Z) on clustering generated by single linkage on
|
791
781
|
# tdist data set. Perturbing. Expecting False.
|
792
|
-
Z =
|
793
|
-
Z[2,2]
|
782
|
+
Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
|
783
|
+
Z = xpx.at(Z)[2, 2].set(0.0)
|
794
784
|
assert not is_monotonic(Z)
|
795
785
|
|
796
786
|
def test_is_monotonic_Q_linkage(self, xp):
|
797
787
|
# Tests is_monotonic(Z) on clustering generated by single linkage on
|
798
788
|
# Q data set. Expecting True.
|
799
|
-
X =
|
800
|
-
Z = linkage(X, 'single')
|
789
|
+
X = hierarchy_test_data.Q_X
|
790
|
+
Z = xp.asarray(linkage(X, 'single'))
|
801
791
|
assert is_monotonic(Z)
|
802
792
|
|
803
793
|
|
804
|
-
@
|
794
|
+
@make_xp_test_case(maxdists)
|
805
795
|
class TestMaxDists:
|
806
796
|
|
807
797
|
def test_maxdists_empty_linkage(self, xp):
|
@@ -809,7 +799,6 @@ class TestMaxDists:
|
|
809
799
|
Z = xp.zeros((0, 4), dtype=xp.float64)
|
810
800
|
assert_raises(ValueError, maxdists, Z)
|
811
801
|
|
812
|
-
@skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
|
813
802
|
def test_maxdists_one_cluster_linkage(self, xp):
|
814
803
|
# Tests maxdists(Z) on linkage with one cluster.
|
815
804
|
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
@@ -817,23 +806,20 @@ class TestMaxDists:
|
|
817
806
|
expectedMD = calculate_maximum_distances(Z, xp)
|
818
807
|
xp_assert_close(MD, expectedMD, atol=1e-15)
|
819
808
|
|
820
|
-
@
|
821
|
-
|
822
|
-
|
823
|
-
self.check_maxdists_Q_linkage(method, xp)
|
824
|
-
|
825
|
-
def check_maxdists_Q_linkage(self, method, xp):
|
809
|
+
@pytest.mark.parametrize(
|
810
|
+
"method", ['single', 'complete', 'ward', 'centroid', 'median'])
|
811
|
+
def test_maxdists_Q_linkage(self, method, xp):
|
826
812
|
# Tests maxdists(Z) on the Q data set
|
827
|
-
X =
|
828
|
-
Z = linkage(X, method)
|
813
|
+
X = hierarchy_test_data.Q_X
|
814
|
+
Z = xp.asarray(linkage(X, method))
|
829
815
|
MD = maxdists(Z)
|
830
816
|
expectedMD = calculate_maximum_distances(Z, xp)
|
831
817
|
xp_assert_close(MD, expectedMD, atol=1e-15)
|
832
818
|
|
833
819
|
|
820
|
+
@make_xp_test_case(maxinconsts)
|
834
821
|
class TestMaxInconsts:
|
835
822
|
|
836
|
-
@skip_xp_backends(cpu_only=True)
|
837
823
|
def test_maxinconsts_empty_linkage(self, xp):
|
838
824
|
# Tests maxinconsts(Z, R) on empty linkage. Expecting exception.
|
839
825
|
Z = xp.zeros((0, 4), dtype=xp.float64)
|
@@ -848,8 +834,6 @@ class TestMaxInconsts:
|
|
848
834
|
R = xp.asarray(R)
|
849
835
|
assert_raises(ValueError, maxinconsts, Z, R)
|
850
836
|
|
851
|
-
@skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment',
|
852
|
-
cpu_only=True)
|
853
837
|
def test_maxinconsts_one_cluster_linkage(self, xp):
|
854
838
|
# Tests maxinconsts(Z, R) on linkage with one cluster.
|
855
839
|
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
@@ -858,53 +842,42 @@ class TestMaxInconsts:
|
|
858
842
|
expectedMD = calculate_maximum_inconsistencies(Z, R, xp=xp)
|
859
843
|
xp_assert_close(MD, expectedMD, atol=1e-15)
|
860
844
|
|
861
|
-
@
|
862
|
-
|
863
|
-
def test_maxinconsts_Q_linkage(self, xp):
|
864
|
-
for method in ['single', 'complete', 'ward', 'centroid', 'median']:
|
865
|
-
self.check_maxinconsts_Q_linkage(method, xp)
|
866
|
-
|
867
|
-
def check_maxinconsts_Q_linkage(self, method, xp):
|
845
|
+
@pytest.mark.parametrize(
|
846
|
+
"method", ['single', 'complete', 'ward', 'centroid', 'median'])
|
847
|
+
def test_maxinconsts_Q_linkage(self, method, xp):
|
868
848
|
# Tests maxinconsts(Z, R) on the Q data set
|
869
|
-
X =
|
849
|
+
X = hierarchy_test_data.Q_X
|
870
850
|
Z = linkage(X, method)
|
871
|
-
R = inconsistent(Z)
|
851
|
+
R = xp.asarray(inconsistent(Z))
|
852
|
+
Z = xp.asarray(Z)
|
872
853
|
MD = maxinconsts(Z, R)
|
873
854
|
expectedMD = calculate_maximum_inconsistencies(Z, R, xp=xp)
|
874
855
|
xp_assert_close(MD, expectedMD, atol=1e-15)
|
875
856
|
|
876
857
|
|
858
|
+
@make_xp_test_case(maxRstat)
|
877
859
|
class TestMaxRStat:
|
878
860
|
|
879
861
|
def test_maxRstat_invalid_index(self, xp):
|
880
|
-
for i in [3.3, -1, 4]:
|
881
|
-
self.check_maxRstat_invalid_index(i, xp)
|
882
|
-
|
883
|
-
def check_maxRstat_invalid_index(self, i, xp):
|
884
862
|
# Tests maxRstat(Z, R, i). Expecting exception.
|
885
863
|
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
886
864
|
R = xp.asarray([[0, 0, 0, 0.3]], dtype=xp.float64)
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
def check_maxRstat_empty_linkage(self, i, xp):
|
865
|
+
with pytest.raises(TypeError):
|
866
|
+
maxRstat(Z, R, 3.3)
|
867
|
+
with pytest.raises(ValueError):
|
868
|
+
maxRstat(Z, R, -1)
|
869
|
+
with pytest.raises(ValueError):
|
870
|
+
maxRstat(Z, R, 4)
|
871
|
+
|
872
|
+
@pytest.mark.parametrize("i", range(4))
|
873
|
+
def test_maxRstat_empty_linkage(self, i, xp):
|
898
874
|
# Tests maxRstat(Z, R, i) on empty linkage. Expecting exception.
|
899
875
|
Z = xp.zeros((0, 4), dtype=xp.float64)
|
900
876
|
R = xp.zeros((0, 4), dtype=xp.float64)
|
901
877
|
assert_raises(ValueError, maxRstat, Z, R, i)
|
902
878
|
|
903
|
-
|
904
|
-
|
905
|
-
self.check_maxRstat_difrow_linkage(i, xp)
|
906
|
-
|
907
|
-
def check_maxRstat_difrow_linkage(self, i, xp):
|
879
|
+
@pytest.mark.parametrize("i", range(4))
|
880
|
+
def test_maxRstat_difrow_linkage(self, i, xp):
|
908
881
|
# Tests maxRstat(Z, R, i) on linkage and inconsistency matrices with
|
909
882
|
# different numbers of clusters. Expecting exception.
|
910
883
|
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
@@ -912,13 +885,7 @@ class TestMaxRStat:
|
|
912
885
|
R = xp.asarray(R)
|
913
886
|
assert_raises(ValueError, maxRstat, Z, R, i)
|
914
887
|
|
915
|
-
@skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment',
|
916
|
-
cpu_only=True)
|
917
888
|
def test_maxRstat_one_cluster_linkage(self, xp):
|
918
|
-
for i in range(4):
|
919
|
-
self.check_maxRstat_one_cluster_linkage(i, xp)
|
920
|
-
|
921
|
-
def check_maxRstat_one_cluster_linkage(self, i, xp):
|
922
889
|
# Tests maxRstat(Z, R, i) on linkage with one cluster.
|
923
890
|
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
924
891
|
R = xp.asarray([[0, 0, 0, 0.3]], dtype=xp.float64)
|
@@ -926,40 +893,36 @@ class TestMaxRStat:
|
|
926
893
|
expectedMD = calculate_maximum_inconsistencies(Z, R, 1, xp)
|
927
894
|
xp_assert_close(MD, expectedMD, atol=1e-15)
|
928
895
|
|
929
|
-
@
|
930
|
-
|
931
|
-
def test_maxRstat_Q_linkage(self, xp):
|
932
|
-
|
933
|
-
|
934
|
-
self.check_maxRstat_Q_linkage(method, i, xp)
|
935
|
-
|
936
|
-
def check_maxRstat_Q_linkage(self, method, i, xp):
|
937
|
-
# Tests maxRstat(Z, R, i) on the Q data set
|
938
|
-
X = xp.asarray(hierarchy_test_data.Q_X)
|
896
|
+
@pytest.mark.parametrize(
|
897
|
+
"method", ['single', 'complete', 'ward', 'centroid', 'median'])
|
898
|
+
def test_maxRstat_Q_linkage(self, method, xp):
|
899
|
+
# Tests maxRstat(Z, R, 1) on the Q data set
|
900
|
+
X = hierarchy_test_data.Q_X
|
939
901
|
Z = linkage(X, method)
|
940
|
-
R = inconsistent(Z)
|
902
|
+
R = xp.asarray(inconsistent(Z))
|
903
|
+
Z = xp.asarray(Z)
|
941
904
|
MD = maxRstat(Z, R, 1)
|
942
905
|
expectedMD = calculate_maximum_inconsistencies(Z, R, 1, xp)
|
943
906
|
xp_assert_close(MD, expectedMD, atol=1e-15)
|
944
907
|
|
945
908
|
|
946
|
-
@
|
909
|
+
@make_xp_test_case(dendrogram)
|
947
910
|
class TestDendrogram:
|
948
911
|
|
949
912
|
def test_dendrogram_single_linkage_tdist(self, xp):
|
950
913
|
# Tests dendrogram calculation on single linkage of the tdist data set.
|
951
|
-
Z =
|
914
|
+
Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
|
952
915
|
R = dendrogram(Z, no_plot=True)
|
953
916
|
leaves = R["leaves"]
|
954
917
|
assert_equal(leaves, [2, 5, 1, 0, 3, 4])
|
955
918
|
|
956
919
|
def test_valid_orientation(self, xp):
|
957
|
-
Z =
|
920
|
+
Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
|
958
921
|
assert_raises(ValueError, dendrogram, Z, orientation="foo")
|
959
922
|
|
960
923
|
def test_labels_as_array_or_list(self, xp):
|
961
924
|
# test for gh-12418
|
962
|
-
Z =
|
925
|
+
Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
|
963
926
|
labels = [1, 3, 2, 6, 4, 5]
|
964
927
|
result1 = dendrogram(Z, labels=xp.asarray(labels), no_plot=True)
|
965
928
|
result2 = dendrogram(Z, labels=labels, no_plot=True)
|
@@ -989,14 +952,14 @@ class TestDendrogram:
|
|
989
952
|
reason='MPL 3.9.2 & torch DeprecationWarning from __array_wrap__'
|
990
953
|
' and NumPy 2.0'
|
991
954
|
)
|
955
|
+
@skip_xp_backends('dask.array',
|
956
|
+
reason='dask.array has bad interaction with matplotlib'
|
957
|
+
)
|
992
958
|
@pytest.mark.skipif(not have_matplotlib, reason="no matplotlib")
|
993
|
-
|
994
|
-
|
995
|
-
self.check_dendrogram_plot(orientation, xp)
|
996
|
-
|
997
|
-
def check_dendrogram_plot(self, orientation, xp):
|
959
|
+
@pytest.mark.parametrize("orientation", ['top', 'bottom', 'left', 'right'])
|
960
|
+
def test_dendrogram_plot(self, orientation, xp):
|
998
961
|
# Tests dendrogram plotting.
|
999
|
-
Z =
|
962
|
+
Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
|
1000
963
|
expected = {'color_list': ['C1', 'C0', 'C0', 'C0', 'C0'],
|
1001
964
|
'dcoord': [[0.0, 138.0, 138.0, 0.0],
|
1002
965
|
[0.0, 219.0, 219.0, 0.0],
|
@@ -1060,9 +1023,12 @@ class TestDendrogram:
|
|
1060
1023
|
reason='MPL 3.9.2 & torch DeprecationWarning from __array_wrap__'
|
1061
1024
|
' and NumPy 2.0'
|
1062
1025
|
)
|
1026
|
+
@skip_xp_backends('dask.array',
|
1027
|
+
reason='dask.array has bad interaction with matplotlib'
|
1028
|
+
)
|
1063
1029
|
@pytest.mark.skipif(not have_matplotlib, reason="no matplotlib")
|
1064
1030
|
def test_dendrogram_truncate_mode(self, xp):
|
1065
|
-
Z =
|
1031
|
+
Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
|
1066
1032
|
|
1067
1033
|
R = dendrogram(Z, 2, 'lastp', show_contracted=True)
|
1068
1034
|
plt.close()
|
@@ -1098,13 +1064,13 @@ class TestDendrogram:
|
|
1098
1064
|
|
1099
1065
|
def test_dendrogram_colors(self, xp, dendrogram_lock):
|
1100
1066
|
# Tests dendrogram plots with alternate colors
|
1101
|
-
Z =
|
1067
|
+
Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
|
1102
1068
|
|
1103
1069
|
with dendrogram_lock:
|
1104
1070
|
# Global color palette might be changed concurrently
|
1105
1071
|
set_link_color_palette(['c', 'm', 'y', 'k'])
|
1106
1072
|
R = dendrogram(Z, no_plot=True,
|
1107
|
-
|
1073
|
+
above_threshold_color='g', color_threshold=250)
|
1108
1074
|
set_link_color_palette(['g', 'r', 'c', 'm', 'y', 'k'])
|
1109
1075
|
|
1110
1076
|
color_list = R['color_list']
|
@@ -1116,14 +1082,14 @@ class TestDendrogram:
|
|
1116
1082
|
def test_dendrogram_leaf_colors_zero_dist(self, xp):
|
1117
1083
|
# tests that the colors of leafs are correct for tree
|
1118
1084
|
# with two identical points
|
1119
|
-
|
1085
|
+
X = np.asarray([[1, 0, 0],
|
1120
1086
|
[0, 0, 1],
|
1121
1087
|
[0, 2, 0],
|
1122
1088
|
[0, 0, 1],
|
1123
1089
|
[0, 1, 0],
|
1124
1090
|
[0, 1, 0]])
|
1125
|
-
|
1126
|
-
d = dendrogram(
|
1091
|
+
Z = xp.asarray(linkage(X, "single"))
|
1092
|
+
d = dendrogram(Z, no_plot=True)
|
1127
1093
|
exp_colors = ['C0', 'C1', 'C1', 'C0', 'C2', 'C2']
|
1128
1094
|
colors = d["leaves_color_list"]
|
1129
1095
|
assert_equal(colors, exp_colors)
|
@@ -1131,14 +1097,14 @@ class TestDendrogram:
|
|
1131
1097
|
def test_dendrogram_leaf_colors(self, xp):
|
1132
1098
|
# tests that the colors are correct for a tree
|
1133
1099
|
# with two near points ((0, 0, 1.1) and (0, 0, 1))
|
1134
|
-
|
1100
|
+
X = np.asarray([[1, 0, 0],
|
1135
1101
|
[0, 0, 1.1],
|
1136
1102
|
[0, 2, 0],
|
1137
1103
|
[0, 0, 1],
|
1138
1104
|
[0, 1, 0],
|
1139
1105
|
[0, 1, 0]])
|
1140
|
-
|
1141
|
-
d = dendrogram(
|
1106
|
+
Z = xp.asarray(linkage(X, "single"))
|
1107
|
+
d = dendrogram(Z, no_plot=True)
|
1142
1108
|
exp_colors = ['C0', 'C1', 'C1', 'C0', 'C2', 'C2']
|
1143
1109
|
colors = d["leaves_color_list"]
|
1144
1110
|
assert_equal(colors, exp_colors)
|
@@ -1148,17 +1114,18 @@ def calculate_maximum_distances(Z, xp):
|
|
1148
1114
|
# Used for testing correctness of maxdists.
|
1149
1115
|
n = Z.shape[0] + 1
|
1150
1116
|
B = xp.zeros((n-1,), dtype=Z.dtype)
|
1151
|
-
q = xp.zeros((3,))
|
1152
1117
|
for i in range(0, n - 1):
|
1153
|
-
q
|
1118
|
+
q = xp.zeros((3,))
|
1154
1119
|
left = Z[i, 0]
|
1155
1120
|
right = Z[i, 1]
|
1156
1121
|
if left >= n:
|
1157
|
-
|
1122
|
+
b_left = B[xp.asarray(left, dtype=xp.int64) - n]
|
1123
|
+
q = xpx.at(q, 0).set(b_left)
|
1158
1124
|
if right >= n:
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1125
|
+
b_right = B[xp.asarray(right, dtype=xp.int64) - n]
|
1126
|
+
q = xpx.at(q, 1).set(b_right)
|
1127
|
+
q = xpx.at(q, 2).set(Z[i, 2])
|
1128
|
+
B = xpx.at(B, i).set(xp.max(q))
|
1162
1129
|
return B
|
1163
1130
|
|
1164
1131
|
|
@@ -1167,46 +1134,27 @@ def calculate_maximum_inconsistencies(Z, R, k=3, xp=np):
|
|
1167
1134
|
n = Z.shape[0] + 1
|
1168
1135
|
dtype = xp.result_type(Z, R)
|
1169
1136
|
B = xp.zeros((n-1,), dtype=dtype)
|
1170
|
-
q = xp.zeros((3,))
|
1171
1137
|
for i in range(0, n - 1):
|
1172
|
-
q
|
1138
|
+
q = xp.zeros((3,))
|
1173
1139
|
left = Z[i, 0]
|
1174
1140
|
right = Z[i, 1]
|
1175
1141
|
if left >= n:
|
1176
|
-
|
1142
|
+
b_left = B[xp.asarray(left, dtype=xp.int64) - n]
|
1143
|
+
q = xpx.at(q, 0).set(b_left)
|
1177
1144
|
if right >= n:
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1145
|
+
b_right = B[xp.asarray(right, dtype=xp.int64) - n]
|
1146
|
+
q = xpx.at(q, 1).set(b_right)
|
1147
|
+
q = xpx.at(q, 2).set(R[i, k])
|
1148
|
+
B = xpx.at(B, i).set(xp.max(q))
|
1181
1149
|
return B
|
1182
1150
|
|
1183
1151
|
|
1184
|
-
@
|
1185
|
-
@skip_xp_backends(cpu_only=True)
|
1186
|
-
def test_unsupported_uncondensed_distance_matrix_linkage_warning(xp):
|
1187
|
-
assert_warns(ClusterWarning, linkage, xp.asarray([[0, 1], [1, 0]]))
|
1188
|
-
|
1189
|
-
|
1190
|
-
def test_euclidean_linkage_value_error(xp):
|
1191
|
-
for method in scipy.cluster.hierarchy._EUCLIDEAN_METHODS:
|
1192
|
-
assert_raises(ValueError, linkage, xp.asarray([[1, 1], [1, 1]]),
|
1193
|
-
method=method, metric='cityblock')
|
1194
|
-
|
1195
|
-
|
1196
|
-
@skip_xp_backends(cpu_only=True)
|
1197
|
-
def test_2x2_linkage(xp):
|
1198
|
-
Z1 = linkage(xp.asarray([1]), method='single', metric='euclidean')
|
1199
|
-
Z2 = linkage(xp.asarray([[0, 1], [0, 0]]), method='single', metric='euclidean')
|
1200
|
-
xp_assert_close(Z1, Z2, rtol=1e-15)
|
1201
|
-
|
1202
|
-
|
1203
|
-
@skip_xp_backends(cpu_only=True)
|
1152
|
+
@make_xp_test_case(to_tree)
|
1204
1153
|
def test_node_compare(xp):
|
1205
1154
|
np.random.seed(23)
|
1206
1155
|
nobs = 50
|
1207
1156
|
X = np.random.randn(nobs, 4)
|
1208
|
-
|
1209
|
-
Z = scipy.cluster.hierarchy.ward(X)
|
1157
|
+
Z = xp.asarray(ward(X))
|
1210
1158
|
tree = to_tree(Z)
|
1211
1159
|
assert_(tree > tree.get_left())
|
1212
1160
|
assert_(tree.get_right() > tree.get_left())
|
@@ -1214,13 +1162,12 @@ def test_node_compare(xp):
|
|
1214
1162
|
assert_(tree.get_right() != tree.get_left())
|
1215
1163
|
|
1216
1164
|
|
1217
|
-
@
|
1165
|
+
@make_xp_test_case(cut_tree)
|
1218
1166
|
def test_cut_tree(xp):
|
1219
1167
|
np.random.seed(23)
|
1220
1168
|
nobs = 50
|
1221
1169
|
X = np.random.randn(nobs, 4)
|
1222
|
-
|
1223
|
-
Z = scipy.cluster.hierarchy.ward(X)
|
1170
|
+
Z = xp.asarray(ward(X))
|
1224
1171
|
cutree = cut_tree(Z)
|
1225
1172
|
|
1226
1173
|
# cutree.dtype varies between int32 and int64 over platforms
|
@@ -1243,16 +1190,16 @@ def test_cut_tree(xp):
|
|
1243
1190
|
cut_tree(Z, height=[10, 5]), rtol=1e-15)
|
1244
1191
|
|
1245
1192
|
|
1246
|
-
@
|
1193
|
+
@make_xp_test_case(optimal_leaf_ordering)
|
1247
1194
|
def test_optimal_leaf_ordering(xp):
|
1248
1195
|
# test with the distance vector y
|
1249
|
-
Z = optimal_leaf_ordering(
|
1196
|
+
Z = optimal_leaf_ordering(xp.asarray(linkage(hierarchy_test_data.ytdist)),
|
1250
1197
|
xp.asarray(hierarchy_test_data.ytdist))
|
1251
1198
|
expectedZ = hierarchy_test_data.linkage_ytdist_single_olo
|
1252
1199
|
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
|
1253
1200
|
|
1254
1201
|
# test with the observation matrix X
|
1255
|
-
Z = optimal_leaf_ordering(
|
1202
|
+
Z = optimal_leaf_ordering(xp.asarray(linkage(hierarchy_test_data.X, 'ward')),
|
1256
1203
|
xp.asarray(hierarchy_test_data.X))
|
1257
1204
|
expectedZ = hierarchy_test_data.linkage_X_ward_olo
|
1258
1205
|
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
|
@@ -1289,12 +1236,3 @@ def test_Heap(xp):
|
|
1289
1236
|
pair = heap.get_min()
|
1290
1237
|
assert_equal(pair['key'], 1)
|
1291
1238
|
assert_equal(pair['value'], 10)
|
1292
|
-
|
1293
|
-
|
1294
|
-
@skip_xp_backends(cpu_only=True)
|
1295
|
-
def test_centroid_neg_distance(xp):
|
1296
|
-
# gh-21011
|
1297
|
-
values = xp.asarray([0, 0, -1])
|
1298
|
-
with pytest.raises(ValueError):
|
1299
|
-
# This is just checking that this doesn't crash
|
1300
|
-
linkage(values, method='centroid')
|