Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/__pycache__/_pep440.cpython-310.pyc +0 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/__pycache__/_testutils.cpython-310.pyc +0 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/_ccallback_c.cpython-310-x86_64-linux-gnu.so +3 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/LICENSE +29 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/__init__.py +116 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/_backend.py +704 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/__init__.py +22 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/_internal.py +43 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/__init__.py +1 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_aliases.py +530 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_helpers.py +234 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_linalg.py +155 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_typing.py +20 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/__init__.py +16 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/_aliases.py +69 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/_typing.py +46 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/linalg.py +41 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/__init__.py +22 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/_aliases.py +69 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/_typing.py +46 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/linalg.py +34 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/__init__.py +22 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/_aliases.py +704 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/linalg.py +55 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/__init__.py +0 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__gcutils.py +101 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__pep440.py +67 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__testutils.py +32 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__threadsafety.py +51 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__util.py +408 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_array_api.py +109 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_bunch.py +162 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_ccallback.py +204 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_deprecation.py +10 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_import_cycles.py +14 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_public_api.py +474 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_scipy_version.py +18 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_tmpdirs.py +42 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_warnings.py +135 -0
- rtme/lib/python3.10/site-packages/scipy/_lib/uarray.py +31 -0
- rtme/lib/python3.10/site-packages/scipy/cluster/__init__.py +31 -0
- rtme/lib/python3.10/site-packages/scipy/cluster/hierarchy.py +0 -0
- rtme/lib/python3.10/site-packages/scipy/cluster/tests/__init__.py +0 -0
- rtme/lib/python3.10/site-packages/scipy/cluster/tests/hierarchy_test_data.py +145 -0
- rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_disjoint_set.py +202 -0
- rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_hierarchy.py +1349 -0
- rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_vq.py +416 -0
- rtme/lib/python3.10/site-packages/scipy/cluster/vq.py +821 -0
- rtme/lib/python3.10/site-packages/scipy/constants/__init__.py +347 -0
.gitattributes
CHANGED
|
@@ -5351,3 +5351,4 @@ rtme/lib/python3.10/site-packages/torch/lib/libtorch_cuda_linalg.so filter=lfs d
|
|
| 5351 |
rtme/lib/python3.10/site-packages/scipy.libs/libgfortran-040039e1.so.5.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 5352 |
rtme/lib/python3.10/site-packages/scipy.libs/libquadmath-96973f99.so.0.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 5353 |
rtme/lib/python3.10/site-packages/scipy.libs/libopenblasp-r0-23e5df77.3.21.dev.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 5351 |
rtme/lib/python3.10/site-packages/scipy.libs/libgfortran-040039e1.so.5.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 5352 |
rtme/lib/python3.10/site-packages/scipy.libs/libquadmath-96973f99.so.0.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 5353 |
rtme/lib/python3.10/site-packages/scipy.libs/libopenblasp-r0-23e5df77.3.21.dev.so filter=lfs diff=lfs merge=lfs -text
|
| 5354 |
+
rtme/lib/python3.10/site-packages/scipy/_lib/_ccallback_c.cpython-310-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
rtme/lib/python3.10/site-packages/scipy/_lib/__pycache__/_pep440.cpython-310.pyc
ADDED
|
Binary file (12.7 kB). View file
|
|
|
rtme/lib/python3.10/site-packages/scipy/_lib/__pycache__/_testutils.cpython-310.pyc
ADDED
|
Binary file (7.98 kB). View file
|
|
|
rtme/lib/python3.10/site-packages/scipy/_lib/_ccallback_c.cpython-310-x86_64-linux-gnu.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e61284afa31024197dd093b6cc02b6ea889dfaafe0a8c500ec95ab63535f9f2b
|
| 3 |
+
size 109960
|
rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
BSD 3-Clause License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2018, Quansight-Labs
|
| 4 |
+
All rights reserved.
|
| 5 |
+
|
| 6 |
+
Redistribution and use in source and binary forms, with or without
|
| 7 |
+
modification, are permitted provided that the following conditions are met:
|
| 8 |
+
|
| 9 |
+
* Redistributions of source code must retain the above copyright notice, this
|
| 10 |
+
list of conditions and the following disclaimer.
|
| 11 |
+
|
| 12 |
+
* Redistributions in binary form must reproduce the above copyright notice,
|
| 13 |
+
this list of conditions and the following disclaimer in the documentation
|
| 14 |
+
and/or other materials provided with the distribution.
|
| 15 |
+
|
| 16 |
+
* Neither the name of the copyright holder nor the names of its
|
| 17 |
+
contributors may be used to endorse or promote products derived from
|
| 18 |
+
this software without specific prior written permission.
|
| 19 |
+
|
| 20 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
| 21 |
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
| 22 |
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 23 |
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
| 24 |
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
| 25 |
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
| 26 |
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 27 |
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
| 28 |
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 29 |
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/__init__.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
.. note:
|
| 3 |
+
If you are looking for overrides for NumPy-specific methods, see the
|
| 4 |
+
documentation for :obj:`unumpy`. This page explains how to write
|
| 5 |
+
back-ends and multimethods.
|
| 6 |
+
|
| 7 |
+
``uarray`` is built around a back-end protocol, and overridable multimethods.
|
| 8 |
+
It is necessary to define multimethods for back-ends to be able to override them.
|
| 9 |
+
See the documentation of :obj:`generate_multimethod` on how to write multimethods.
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
Let's start with the simplest:
|
| 14 |
+
|
| 15 |
+
``__ua_domain__`` defines the back-end *domain*. The domain consists of period-
|
| 16 |
+
separated string consisting of the modules you extend plus the submodule. For
|
| 17 |
+
example, if a submodule ``module2.submodule`` extends ``module1``
|
| 18 |
+
(i.e., it exposes dispatchables marked as types available in ``module1``),
|
| 19 |
+
then the domain string should be ``"module1.module2.submodule"``.
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
For the purpose of this demonstration, we'll be creating an object and setting
|
| 23 |
+
its attributes directly. However, note that you can use a module or your own type
|
| 24 |
+
as a backend as well.
|
| 25 |
+
|
| 26 |
+
>>> class Backend: pass
|
| 27 |
+
>>> be = Backend()
|
| 28 |
+
>>> be.__ua_domain__ = "ua_examples"
|
| 29 |
+
|
| 30 |
+
It might be useful at this point to sidetrack to the documentation of
|
| 31 |
+
:obj:`generate_multimethod` to find out how to generate a multimethod
|
| 32 |
+
overridable by :obj:`uarray`. Needless to say, writing a backend and
|
| 33 |
+
creating multimethods are mostly orthogonal activities, and knowing
|
| 34 |
+
one doesn't necessarily require knowledge of the other, although it
|
| 35 |
+
is certainly helpful. We expect core API designers/specifiers to write the
|
| 36 |
+
multimethods, and implementors to override them. But, as is often the case,
|
| 37 |
+
similar people write both.
|
| 38 |
+
|
| 39 |
+
Without further ado, here's an example multimethod:
|
| 40 |
+
|
| 41 |
+
>>> import uarray as ua
|
| 42 |
+
>>> from uarray import Dispatchable
|
| 43 |
+
>>> def override_me(a, b):
|
| 44 |
+
... return Dispatchable(a, int),
|
| 45 |
+
>>> def override_replacer(args, kwargs, dispatchables):
|
| 46 |
+
... return (dispatchables[0], args[1]), {}
|
| 47 |
+
>>> overridden_me = ua.generate_multimethod(
|
| 48 |
+
... override_me, override_replacer, "ua_examples"
|
| 49 |
+
... )
|
| 50 |
+
|
| 51 |
+
Next comes the part about overriding the multimethod. This requires
|
| 52 |
+
the ``__ua_function__`` protocol, and the ``__ua_convert__``
|
| 53 |
+
protocol. The ``__ua_function__`` protocol has the signature
|
| 54 |
+
``(method, args, kwargs)`` where ``method`` is the passed
|
| 55 |
+
multimethod, ``args``/``kwargs`` specify the arguments and ``dispatchables``
|
| 56 |
+
is the list of converted dispatchables passed in.
|
| 57 |
+
|
| 58 |
+
>>> def __ua_function__(method, args, kwargs):
|
| 59 |
+
... return method.__name__, args, kwargs
|
| 60 |
+
>>> be.__ua_function__ = __ua_function__
|
| 61 |
+
|
| 62 |
+
The other protocol of interest is the ``__ua_convert__`` protocol. It has the
|
| 63 |
+
signature ``(dispatchables, coerce)``. When ``coerce`` is ``False``, conversion
|
| 64 |
+
between the formats should ideally be an ``O(1)`` operation, but it means that
|
| 65 |
+
no memory copying should be involved, only views of the existing data.
|
| 66 |
+
|
| 67 |
+
>>> def __ua_convert__(dispatchables, coerce):
|
| 68 |
+
... for d in dispatchables:
|
| 69 |
+
... if d.type is int:
|
| 70 |
+
... if coerce and d.coercible:
|
| 71 |
+
... yield str(d.value)
|
| 72 |
+
... else:
|
| 73 |
+
... yield d.value
|
| 74 |
+
>>> be.__ua_convert__ = __ua_convert__
|
| 75 |
+
|
| 76 |
+
Now that we have defined the backend, the next thing to do is to call the multimethod.
|
| 77 |
+
|
| 78 |
+
>>> with ua.set_backend(be):
|
| 79 |
+
... overridden_me(1, "2")
|
| 80 |
+
('override_me', (1, '2'), {})
|
| 81 |
+
|
| 82 |
+
Note that the marked type has no effect on the actual type of the passed object.
|
| 83 |
+
We can also coerce the type of the input.
|
| 84 |
+
|
| 85 |
+
>>> with ua.set_backend(be, coerce=True):
|
| 86 |
+
... overridden_me(1, "2")
|
| 87 |
+
... overridden_me(1.0, "2")
|
| 88 |
+
('override_me', ('1', '2'), {})
|
| 89 |
+
('override_me', ('1.0', '2'), {})
|
| 90 |
+
|
| 91 |
+
Another feature is that if you remove ``__ua_convert__``, the arguments are not
|
| 92 |
+
converted at all and it's up to the backend to handle that.
|
| 93 |
+
|
| 94 |
+
>>> del be.__ua_convert__
|
| 95 |
+
>>> with ua.set_backend(be):
|
| 96 |
+
... overridden_me(1, "2")
|
| 97 |
+
('override_me', (1, '2'), {})
|
| 98 |
+
|
| 99 |
+
You also have the option to return ``NotImplemented``, in which case processing moves on
|
| 100 |
+
to the next back-end, which in this case, doesn't exist. The same applies to
|
| 101 |
+
``__ua_convert__``.
|
| 102 |
+
|
| 103 |
+
>>> be.__ua_function__ = lambda *a, **kw: NotImplemented
|
| 104 |
+
>>> with ua.set_backend(be):
|
| 105 |
+
... overridden_me(1, "2")
|
| 106 |
+
Traceback (most recent call last):
|
| 107 |
+
...
|
| 108 |
+
uarray.BackendNotImplementedError: ...
|
| 109 |
+
|
| 110 |
+
The last possibility is if we don't have ``__ua_convert__``, in which case the job is
|
| 111 |
+
left up to ``__ua_function__``, but putting things back into arrays after conversion
|
| 112 |
+
will not be possible.
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
from ._backend import *
|
| 116 |
+
__version__ = '0.8.8.dev0+aa94c5a4.scipy'
|
rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/_backend.py
ADDED
|
@@ -0,0 +1,704 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import typing
|
| 2 |
+
import types
|
| 3 |
+
import inspect
|
| 4 |
+
import functools
|
| 5 |
+
from . import _uarray
|
| 6 |
+
import copyreg
|
| 7 |
+
import pickle
|
| 8 |
+
import contextlib
|
| 9 |
+
|
| 10 |
+
from ._uarray import ( # type: ignore
|
| 11 |
+
BackendNotImplementedError,
|
| 12 |
+
_Function,
|
| 13 |
+
_SkipBackendContext,
|
| 14 |
+
_SetBackendContext,
|
| 15 |
+
_BackendState,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
__all__ = [
|
| 19 |
+
"set_backend",
|
| 20 |
+
"set_global_backend",
|
| 21 |
+
"skip_backend",
|
| 22 |
+
"register_backend",
|
| 23 |
+
"determine_backend",
|
| 24 |
+
"determine_backend_multi",
|
| 25 |
+
"clear_backends",
|
| 26 |
+
"create_multimethod",
|
| 27 |
+
"generate_multimethod",
|
| 28 |
+
"_Function",
|
| 29 |
+
"BackendNotImplementedError",
|
| 30 |
+
"Dispatchable",
|
| 31 |
+
"wrap_single_convertor",
|
| 32 |
+
"wrap_single_convertor_instance",
|
| 33 |
+
"all_of_type",
|
| 34 |
+
"mark_as",
|
| 35 |
+
"set_state",
|
| 36 |
+
"get_state",
|
| 37 |
+
"reset_state",
|
| 38 |
+
"_BackendState",
|
| 39 |
+
"_SkipBackendContext",
|
| 40 |
+
"_SetBackendContext",
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
ArgumentExtractorType = typing.Callable[..., tuple["Dispatchable", ...]]
|
| 44 |
+
ArgumentReplacerType = typing.Callable[
|
| 45 |
+
[tuple, dict, tuple], tuple[tuple, dict]
|
| 46 |
+
]
|
| 47 |
+
|
| 48 |
+
def unpickle_function(mod_name, qname, self_):
|
| 49 |
+
import importlib
|
| 50 |
+
|
| 51 |
+
try:
|
| 52 |
+
module = importlib.import_module(mod_name)
|
| 53 |
+
qname = qname.split(".")
|
| 54 |
+
func = module
|
| 55 |
+
for q in qname:
|
| 56 |
+
func = getattr(func, q)
|
| 57 |
+
|
| 58 |
+
if self_ is not None:
|
| 59 |
+
func = types.MethodType(func, self_)
|
| 60 |
+
|
| 61 |
+
return func
|
| 62 |
+
except (ImportError, AttributeError) as e:
|
| 63 |
+
from pickle import UnpicklingError
|
| 64 |
+
|
| 65 |
+
raise UnpicklingError from e
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def pickle_function(func):
|
| 69 |
+
mod_name = getattr(func, "__module__", None)
|
| 70 |
+
qname = getattr(func, "__qualname__", None)
|
| 71 |
+
self_ = getattr(func, "__self__", None)
|
| 72 |
+
|
| 73 |
+
try:
|
| 74 |
+
test = unpickle_function(mod_name, qname, self_)
|
| 75 |
+
except pickle.UnpicklingError:
|
| 76 |
+
test = None
|
| 77 |
+
|
| 78 |
+
if test is not func:
|
| 79 |
+
raise pickle.PicklingError(
|
| 80 |
+
f"Can't pickle {func}: it's not the same object as {test}"
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
return unpickle_function, (mod_name, qname, self_)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def pickle_state(state):
|
| 87 |
+
return _uarray._BackendState._unpickle, state._pickle()
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def pickle_set_backend_context(ctx):
|
| 91 |
+
return _SetBackendContext, ctx._pickle()
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def pickle_skip_backend_context(ctx):
|
| 95 |
+
return _SkipBackendContext, ctx._pickle()
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
copyreg.pickle(_Function, pickle_function)
|
| 99 |
+
copyreg.pickle(_uarray._BackendState, pickle_state)
|
| 100 |
+
copyreg.pickle(_SetBackendContext, pickle_set_backend_context)
|
| 101 |
+
copyreg.pickle(_SkipBackendContext, pickle_skip_backend_context)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def get_state():
|
| 105 |
+
"""
|
| 106 |
+
Returns an opaque object containing the current state of all the backends.
|
| 107 |
+
|
| 108 |
+
Can be used for synchronization between threads/processes.
|
| 109 |
+
|
| 110 |
+
See Also
|
| 111 |
+
--------
|
| 112 |
+
set_state
|
| 113 |
+
Sets the state returned by this function.
|
| 114 |
+
"""
|
| 115 |
+
return _uarray.get_state()
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
@contextlib.contextmanager
|
| 119 |
+
def reset_state():
|
| 120 |
+
"""
|
| 121 |
+
Returns a context manager that resets all state once exited.
|
| 122 |
+
|
| 123 |
+
See Also
|
| 124 |
+
--------
|
| 125 |
+
set_state
|
| 126 |
+
Context manager that sets the backend state.
|
| 127 |
+
get_state
|
| 128 |
+
Gets a state to be set by this context manager.
|
| 129 |
+
"""
|
| 130 |
+
with set_state(get_state()):
|
| 131 |
+
yield
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
@contextlib.contextmanager
|
| 135 |
+
def set_state(state):
|
| 136 |
+
"""
|
| 137 |
+
A context manager that sets the state of the backends to one returned by :obj:`get_state`.
|
| 138 |
+
|
| 139 |
+
See Also
|
| 140 |
+
--------
|
| 141 |
+
get_state
|
| 142 |
+
Gets a state to be set by this context manager.
|
| 143 |
+
""" # noqa: E501
|
| 144 |
+
old_state = get_state()
|
| 145 |
+
_uarray.set_state(state)
|
| 146 |
+
try:
|
| 147 |
+
yield
|
| 148 |
+
finally:
|
| 149 |
+
_uarray.set_state(old_state, True)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def create_multimethod(*args, **kwargs):
|
| 153 |
+
"""
|
| 154 |
+
Creates a decorator for generating multimethods.
|
| 155 |
+
|
| 156 |
+
This function creates a decorator that can be used with an argument
|
| 157 |
+
extractor in order to generate a multimethod. Other than for the
|
| 158 |
+
argument extractor, all arguments are passed on to
|
| 159 |
+
:obj:`generate_multimethod`.
|
| 160 |
+
|
| 161 |
+
See Also
|
| 162 |
+
--------
|
| 163 |
+
generate_multimethod
|
| 164 |
+
Generates a multimethod.
|
| 165 |
+
"""
|
| 166 |
+
|
| 167 |
+
def wrapper(a):
|
| 168 |
+
return generate_multimethod(a, *args, **kwargs)
|
| 169 |
+
|
| 170 |
+
return wrapper
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def generate_multimethod(
|
| 174 |
+
argument_extractor: ArgumentExtractorType,
|
| 175 |
+
argument_replacer: ArgumentReplacerType,
|
| 176 |
+
domain: str,
|
| 177 |
+
default: typing.Optional[typing.Callable] = None,
|
| 178 |
+
):
|
| 179 |
+
"""
|
| 180 |
+
Generates a multimethod.
|
| 181 |
+
|
| 182 |
+
Parameters
|
| 183 |
+
----------
|
| 184 |
+
argument_extractor : ArgumentExtractorType
|
| 185 |
+
A callable which extracts the dispatchable arguments. Extracted arguments
|
| 186 |
+
should be marked by the :obj:`Dispatchable` class. It has the same signature
|
| 187 |
+
as the desired multimethod.
|
| 188 |
+
argument_replacer : ArgumentReplacerType
|
| 189 |
+
A callable with the signature (args, kwargs, dispatchables), which should also
|
| 190 |
+
return an (args, kwargs) pair with the dispatchables replaced inside the
|
| 191 |
+
args/kwargs.
|
| 192 |
+
domain : str
|
| 193 |
+
A string value indicating the domain of this multimethod.
|
| 194 |
+
default: Optional[Callable], optional
|
| 195 |
+
The default implementation of this multimethod, where ``None`` (the default)
|
| 196 |
+
specifies there is no default implementation.
|
| 197 |
+
|
| 198 |
+
Examples
|
| 199 |
+
--------
|
| 200 |
+
In this example, ``a`` is to be dispatched over, so we return it, while marking it
|
| 201 |
+
as an ``int``.
|
| 202 |
+
The trailing comma is needed because the args have to be returned as an iterable.
|
| 203 |
+
|
| 204 |
+
>>> def override_me(a, b):
|
| 205 |
+
... return Dispatchable(a, int),
|
| 206 |
+
|
| 207 |
+
Next, we define the argument replacer that replaces the dispatchables inside
|
| 208 |
+
args/kwargs with the supplied ones.
|
| 209 |
+
|
| 210 |
+
>>> def override_replacer(args, kwargs, dispatchables):
|
| 211 |
+
... return (dispatchables[0], args[1]), {}
|
| 212 |
+
|
| 213 |
+
Next, we define the multimethod.
|
| 214 |
+
|
| 215 |
+
>>> overridden_me = generate_multimethod(
|
| 216 |
+
... override_me, override_replacer, "ua_examples"
|
| 217 |
+
... )
|
| 218 |
+
|
| 219 |
+
Notice that there's no default implementation, unless you supply one.
|
| 220 |
+
|
| 221 |
+
>>> overridden_me(1, "a")
|
| 222 |
+
Traceback (most recent call last):
|
| 223 |
+
...
|
| 224 |
+
uarray.BackendNotImplementedError: ...
|
| 225 |
+
|
| 226 |
+
>>> overridden_me2 = generate_multimethod(
|
| 227 |
+
... override_me, override_replacer, "ua_examples", default=lambda x, y: (x, y)
|
| 228 |
+
... )
|
| 229 |
+
>>> overridden_me2(1, "a")
|
| 230 |
+
(1, 'a')
|
| 231 |
+
|
| 232 |
+
See Also
|
| 233 |
+
--------
|
| 234 |
+
uarray
|
| 235 |
+
See the module documentation for how to override the method by creating
|
| 236 |
+
backends.
|
| 237 |
+
"""
|
| 238 |
+
kw_defaults, arg_defaults, opts = get_defaults(argument_extractor)
|
| 239 |
+
ua_func = _Function(
|
| 240 |
+
argument_extractor,
|
| 241 |
+
argument_replacer,
|
| 242 |
+
domain,
|
| 243 |
+
arg_defaults,
|
| 244 |
+
kw_defaults,
|
| 245 |
+
default,
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
return functools.update_wrapper(ua_func, argument_extractor)
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
def set_backend(backend, coerce=False, only=False):
|
| 252 |
+
"""
|
| 253 |
+
A context manager that sets the preferred backend.
|
| 254 |
+
|
| 255 |
+
Parameters
|
| 256 |
+
----------
|
| 257 |
+
backend
|
| 258 |
+
The backend to set.
|
| 259 |
+
coerce
|
| 260 |
+
Whether or not to coerce to a specific backend's types. Implies ``only``.
|
| 261 |
+
only
|
| 262 |
+
Whether or not this should be the last backend to try.
|
| 263 |
+
|
| 264 |
+
See Also
|
| 265 |
+
--------
|
| 266 |
+
skip_backend: A context manager that allows skipping of backends.
|
| 267 |
+
set_global_backend: Set a single, global backend for a domain.
|
| 268 |
+
"""
|
| 269 |
+
try:
|
| 270 |
+
return backend.__ua_cache__["set", coerce, only]
|
| 271 |
+
except AttributeError:
|
| 272 |
+
backend.__ua_cache__ = {}
|
| 273 |
+
except KeyError:
|
| 274 |
+
pass
|
| 275 |
+
|
| 276 |
+
ctx = _SetBackendContext(backend, coerce, only)
|
| 277 |
+
backend.__ua_cache__["set", coerce, only] = ctx
|
| 278 |
+
return ctx
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def skip_backend(backend):
|
| 282 |
+
"""
|
| 283 |
+
A context manager that allows one to skip a given backend from processing
|
| 284 |
+
entirely. This allows one to use another backend's code in a library that
|
| 285 |
+
is also a consumer of the same backend.
|
| 286 |
+
|
| 287 |
+
Parameters
|
| 288 |
+
----------
|
| 289 |
+
backend
|
| 290 |
+
The backend to skip.
|
| 291 |
+
|
| 292 |
+
See Also
|
| 293 |
+
--------
|
| 294 |
+
set_backend: A context manager that allows setting of backends.
|
| 295 |
+
set_global_backend: Set a single, global backend for a domain.
|
| 296 |
+
"""
|
| 297 |
+
try:
|
| 298 |
+
return backend.__ua_cache__["skip"]
|
| 299 |
+
except AttributeError:
|
| 300 |
+
backend.__ua_cache__ = {}
|
| 301 |
+
except KeyError:
|
| 302 |
+
pass
|
| 303 |
+
|
| 304 |
+
ctx = _SkipBackendContext(backend)
|
| 305 |
+
backend.__ua_cache__["skip"] = ctx
|
| 306 |
+
return ctx
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def get_defaults(f):
|
| 310 |
+
sig = inspect.signature(f)
|
| 311 |
+
kw_defaults = {}
|
| 312 |
+
arg_defaults = []
|
| 313 |
+
opts = set()
|
| 314 |
+
for k, v in sig.parameters.items():
|
| 315 |
+
if v.default is not inspect.Parameter.empty:
|
| 316 |
+
kw_defaults[k] = v.default
|
| 317 |
+
if v.kind in (
|
| 318 |
+
inspect.Parameter.POSITIONAL_ONLY,
|
| 319 |
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
| 320 |
+
):
|
| 321 |
+
arg_defaults.append(v.default)
|
| 322 |
+
opts.add(k)
|
| 323 |
+
|
| 324 |
+
return kw_defaults, tuple(arg_defaults), opts
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
def set_global_backend(backend, coerce=False, only=False, *, try_last=False):
|
| 328 |
+
"""
|
| 329 |
+
This utility method replaces the default backend for permanent use. It
|
| 330 |
+
will be tried in the list of backends automatically, unless the
|
| 331 |
+
``only`` flag is set on a backend. This will be the first tried
|
| 332 |
+
backend outside the :obj:`set_backend` context manager.
|
| 333 |
+
|
| 334 |
+
Note that this method is not thread-safe.
|
| 335 |
+
|
| 336 |
+
.. warning::
|
| 337 |
+
We caution library authors against using this function in
|
| 338 |
+
their code. We do *not* support this use-case. This function
|
| 339 |
+
is meant to be used only by users themselves, or by a reference
|
| 340 |
+
implementation, if one exists.
|
| 341 |
+
|
| 342 |
+
Parameters
|
| 343 |
+
----------
|
| 344 |
+
backend
|
| 345 |
+
The backend to register.
|
| 346 |
+
coerce : bool
|
| 347 |
+
Whether to coerce input types when trying this backend.
|
| 348 |
+
only : bool
|
| 349 |
+
If ``True``, no more backends will be tried if this fails.
|
| 350 |
+
Implied by ``coerce=True``.
|
| 351 |
+
try_last : bool
|
| 352 |
+
If ``True``, the global backend is tried after registered backends.
|
| 353 |
+
|
| 354 |
+
See Also
|
| 355 |
+
--------
|
| 356 |
+
set_backend: A context manager that allows setting of backends.
|
| 357 |
+
skip_backend: A context manager that allows skipping of backends.
|
| 358 |
+
"""
|
| 359 |
+
_uarray.set_global_backend(backend, coerce, only, try_last)
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
def register_backend(backend):
|
| 363 |
+
"""
|
| 364 |
+
This utility method sets registers backend for permanent use. It
|
| 365 |
+
will be tried in the list of backends automatically, unless the
|
| 366 |
+
``only`` flag is set on a backend.
|
| 367 |
+
|
| 368 |
+
Note that this method is not thread-safe.
|
| 369 |
+
|
| 370 |
+
Parameters
|
| 371 |
+
----------
|
| 372 |
+
backend
|
| 373 |
+
The backend to register.
|
| 374 |
+
"""
|
| 375 |
+
_uarray.register_backend(backend)
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
def clear_backends(domain, registered=True, globals=False):
|
| 379 |
+
"""
|
| 380 |
+
This utility method clears registered backends.
|
| 381 |
+
|
| 382 |
+
.. warning::
|
| 383 |
+
We caution library authors against using this function in
|
| 384 |
+
their code. We do *not* support this use-case. This function
|
| 385 |
+
is meant to be used only by users themselves.
|
| 386 |
+
|
| 387 |
+
.. warning::
|
| 388 |
+
Do NOT use this method inside a multimethod call, or the
|
| 389 |
+
program is likely to crash.
|
| 390 |
+
|
| 391 |
+
Parameters
|
| 392 |
+
----------
|
| 393 |
+
domain : Optional[str]
|
| 394 |
+
The domain for which to de-register backends. ``None`` means
|
| 395 |
+
de-register for all domains.
|
| 396 |
+
registered : bool
|
| 397 |
+
Whether or not to clear registered backends. See :obj:`register_backend`.
|
| 398 |
+
globals : bool
|
| 399 |
+
Whether or not to clear global backends. See :obj:`set_global_backend`.
|
| 400 |
+
|
| 401 |
+
See Also
|
| 402 |
+
--------
|
| 403 |
+
register_backend : Register a backend globally.
|
| 404 |
+
set_global_backend : Set a global backend.
|
| 405 |
+
"""
|
| 406 |
+
_uarray.clear_backends(domain, registered, globals)
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
class Dispatchable:
|
| 410 |
+
"""
|
| 411 |
+
A utility class which marks an argument with a specific dispatch type.
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
Attributes
|
| 415 |
+
----------
|
| 416 |
+
value
|
| 417 |
+
The value of the Dispatchable.
|
| 418 |
+
|
| 419 |
+
type
|
| 420 |
+
The type of the Dispatchable.
|
| 421 |
+
|
| 422 |
+
Examples
|
| 423 |
+
--------
|
| 424 |
+
>>> x = Dispatchable(1, str)
|
| 425 |
+
>>> x
|
| 426 |
+
<Dispatchable: type=<class 'str'>, value=1>
|
| 427 |
+
|
| 428 |
+
See Also
|
| 429 |
+
--------
|
| 430 |
+
all_of_type
|
| 431 |
+
Marks all unmarked parameters of a function.
|
| 432 |
+
|
| 433 |
+
mark_as
|
| 434 |
+
Allows one to create a utility function to mark as a given type.
|
| 435 |
+
"""
|
| 436 |
+
|
| 437 |
+
def __init__(self, value, dispatch_type, coercible=True):
|
| 438 |
+
self.value = value
|
| 439 |
+
self.type = dispatch_type
|
| 440 |
+
self.coercible = coercible
|
| 441 |
+
|
| 442 |
+
def __getitem__(self, index):
|
| 443 |
+
return (self.type, self.value)[index]
|
| 444 |
+
|
| 445 |
+
def __str__(self):
|
| 446 |
+
return f"<{type(self).__name__}: type={self.type!r}, value={self.value!r}>"
|
| 447 |
+
|
| 448 |
+
__repr__ = __str__
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
def mark_as(dispatch_type):
|
| 452 |
+
"""
|
| 453 |
+
Creates a utility function to mark something as a specific type.
|
| 454 |
+
|
| 455 |
+
Examples
|
| 456 |
+
--------
|
| 457 |
+
>>> mark_int = mark_as(int)
|
| 458 |
+
>>> mark_int(1)
|
| 459 |
+
<Dispatchable: type=<class 'int'>, value=1>
|
| 460 |
+
"""
|
| 461 |
+
return functools.partial(Dispatchable, dispatch_type=dispatch_type)
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
def all_of_type(arg_type):
|
| 465 |
+
"""
|
| 466 |
+
Marks all unmarked arguments as a given type.
|
| 467 |
+
|
| 468 |
+
Examples
|
| 469 |
+
--------
|
| 470 |
+
>>> @all_of_type(str)
|
| 471 |
+
... def f(a, b):
|
| 472 |
+
... return a, Dispatchable(b, int)
|
| 473 |
+
>>> f('a', 1)
|
| 474 |
+
(<Dispatchable: type=<class 'str'>, value='a'>,
|
| 475 |
+
<Dispatchable: type=<class 'int'>, value=1>)
|
| 476 |
+
"""
|
| 477 |
+
|
| 478 |
+
def outer(func):
|
| 479 |
+
@functools.wraps(func)
|
| 480 |
+
def inner(*args, **kwargs):
|
| 481 |
+
extracted_args = func(*args, **kwargs)
|
| 482 |
+
return tuple(
|
| 483 |
+
Dispatchable(arg, arg_type)
|
| 484 |
+
if not isinstance(arg, Dispatchable)
|
| 485 |
+
else arg
|
| 486 |
+
for arg in extracted_args
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
return inner
|
| 490 |
+
|
| 491 |
+
return outer
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
def wrap_single_convertor(convert_single):
|
| 495 |
+
"""
|
| 496 |
+
Wraps a ``__ua_convert__`` defined for a single element to all elements.
|
| 497 |
+
If any of them return ``NotImplemented``, the operation is assumed to be
|
| 498 |
+
undefined.
|
| 499 |
+
|
| 500 |
+
Accepts a signature of (value, type, coerce).
|
| 501 |
+
"""
|
| 502 |
+
|
| 503 |
+
@functools.wraps(convert_single)
|
| 504 |
+
def __ua_convert__(dispatchables, coerce):
|
| 505 |
+
converted = []
|
| 506 |
+
for d in dispatchables:
|
| 507 |
+
c = convert_single(d.value, d.type, coerce and d.coercible)
|
| 508 |
+
|
| 509 |
+
if c is NotImplemented:
|
| 510 |
+
return NotImplemented
|
| 511 |
+
|
| 512 |
+
converted.append(c)
|
| 513 |
+
|
| 514 |
+
return converted
|
| 515 |
+
|
| 516 |
+
return __ua_convert__
|
| 517 |
+
|
| 518 |
+
|
| 519 |
+
def wrap_single_convertor_instance(convert_single):
|
| 520 |
+
"""
|
| 521 |
+
Wraps a ``__ua_convert__`` defined for a single element to all elements.
|
| 522 |
+
If any of them return ``NotImplemented``, the operation is assumed to be
|
| 523 |
+
undefined.
|
| 524 |
+
|
| 525 |
+
Accepts a signature of (value, type, coerce).
|
| 526 |
+
"""
|
| 527 |
+
|
| 528 |
+
@functools.wraps(convert_single)
|
| 529 |
+
def __ua_convert__(self, dispatchables, coerce):
|
| 530 |
+
converted = []
|
| 531 |
+
for d in dispatchables:
|
| 532 |
+
c = convert_single(self, d.value, d.type, coerce and d.coercible)
|
| 533 |
+
|
| 534 |
+
if c is NotImplemented:
|
| 535 |
+
return NotImplemented
|
| 536 |
+
|
| 537 |
+
converted.append(c)
|
| 538 |
+
|
| 539 |
+
return converted
|
| 540 |
+
|
| 541 |
+
return __ua_convert__
|
| 542 |
+
|
| 543 |
+
|
| 544 |
+
def determine_backend(value, dispatch_type, *, domain, only=True, coerce=False):
|
| 545 |
+
"""Set the backend to the first active backend that supports ``value``
|
| 546 |
+
|
| 547 |
+
This is useful for functions that call multimethods without any dispatchable
|
| 548 |
+
arguments. You can use :func:`determine_backend` to ensure the same backend
|
| 549 |
+
is used everywhere in a block of multimethod calls.
|
| 550 |
+
|
| 551 |
+
Parameters
|
| 552 |
+
----------
|
| 553 |
+
value
|
| 554 |
+
The value being tested
|
| 555 |
+
dispatch_type
|
| 556 |
+
The dispatch type associated with ``value``, aka
|
| 557 |
+
":ref:`marking <MarkingGlossary>`".
|
| 558 |
+
domain: string
|
| 559 |
+
The domain to query for backends and set.
|
| 560 |
+
coerce: bool
|
| 561 |
+
Whether or not to allow coercion to the backend's types. Implies ``only``.
|
| 562 |
+
only: bool
|
| 563 |
+
Whether or not this should be the last backend to try.
|
| 564 |
+
|
| 565 |
+
See Also
|
| 566 |
+
--------
|
| 567 |
+
set_backend: For when you know which backend to set
|
| 568 |
+
|
| 569 |
+
Notes
|
| 570 |
+
-----
|
| 571 |
+
|
| 572 |
+
Support is determined by the ``__ua_convert__`` protocol. Backends not
|
| 573 |
+
supporting the type must return ``NotImplemented`` from their
|
| 574 |
+
``__ua_convert__`` if they don't support input of that type.
|
| 575 |
+
|
| 576 |
+
Examples
|
| 577 |
+
--------
|
| 578 |
+
|
| 579 |
+
Suppose we have two backends ``BackendA`` and ``BackendB`` each supporting
|
| 580 |
+
different types, ``TypeA`` and ``TypeB``. Neither supporting the other type:
|
| 581 |
+
|
| 582 |
+
>>> with ua.set_backend(ex.BackendA):
|
| 583 |
+
... ex.call_multimethod(ex.TypeB(), ex.TypeB())
|
| 584 |
+
Traceback (most recent call last):
|
| 585 |
+
...
|
| 586 |
+
uarray.BackendNotImplementedError: ...
|
| 587 |
+
|
| 588 |
+
Now consider a multimethod that creates a new object of ``TypeA``, or
|
| 589 |
+
``TypeB`` depending on the active backend.
|
| 590 |
+
|
| 591 |
+
>>> with ua.set_backend(ex.BackendA), ua.set_backend(ex.BackendB):
|
| 592 |
+
... res = ex.creation_multimethod()
|
| 593 |
+
... ex.call_multimethod(res, ex.TypeA())
|
| 594 |
+
Traceback (most recent call last):
|
| 595 |
+
...
|
| 596 |
+
uarray.BackendNotImplementedError: ...
|
| 597 |
+
|
| 598 |
+
``res`` is an object of ``TypeB`` because ``BackendB`` is set in the
|
| 599 |
+
innermost with statement. So, ``call_multimethod`` fails since the types
|
| 600 |
+
don't match.
|
| 601 |
+
|
| 602 |
+
Instead, we need to first find a backend suitable for all of our objects.
|
| 603 |
+
|
| 604 |
+
>>> with ua.set_backend(ex.BackendA), ua.set_backend(ex.BackendB):
|
| 605 |
+
... x = ex.TypeA()
|
| 606 |
+
... with ua.determine_backend(x, "mark", domain="ua_examples"):
|
| 607 |
+
... res = ex.creation_multimethod()
|
| 608 |
+
... ex.call_multimethod(res, x)
|
| 609 |
+
TypeA
|
| 610 |
+
|
| 611 |
+
"""
|
| 612 |
+
dispatchables = (Dispatchable(value, dispatch_type, coerce),)
|
| 613 |
+
backend = _uarray.determine_backend(domain, dispatchables, coerce)
|
| 614 |
+
|
| 615 |
+
return set_backend(backend, coerce=coerce, only=only)
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
def determine_backend_multi(
|
| 619 |
+
dispatchables, *, domain, only=True, coerce=False, **kwargs
|
| 620 |
+
):
|
| 621 |
+
"""Set a backend supporting all ``dispatchables``
|
| 622 |
+
|
| 623 |
+
This is useful for functions that call multimethods without any dispatchable
|
| 624 |
+
arguments. You can use :func:`determine_backend_multi` to ensure the same
|
| 625 |
+
backend is used everywhere in a block of multimethod calls involving
|
| 626 |
+
multiple arrays.
|
| 627 |
+
|
| 628 |
+
Parameters
|
| 629 |
+
----------
|
| 630 |
+
dispatchables: Sequence[Union[uarray.Dispatchable, Any]]
|
| 631 |
+
The dispatchables that must be supported
|
| 632 |
+
domain: string
|
| 633 |
+
The domain to query for backends and set.
|
| 634 |
+
coerce: bool
|
| 635 |
+
Whether or not to allow coercion to the backend's types. Implies ``only``.
|
| 636 |
+
only: bool
|
| 637 |
+
Whether or not this should be the last backend to try.
|
| 638 |
+
dispatch_type: Optional[Any]
|
| 639 |
+
The default dispatch type associated with ``dispatchables``, aka
|
| 640 |
+
":ref:`marking <MarkingGlossary>`".
|
| 641 |
+
|
| 642 |
+
See Also
|
| 643 |
+
--------
|
| 644 |
+
determine_backend: For a single dispatch value
|
| 645 |
+
set_backend: For when you know which backend to set
|
| 646 |
+
|
| 647 |
+
Notes
|
| 648 |
+
-----
|
| 649 |
+
|
| 650 |
+
Support is determined by the ``__ua_convert__`` protocol. Backends not
|
| 651 |
+
supporting the type must return ``NotImplemented`` from their
|
| 652 |
+
``__ua_convert__`` if they don't support input of that type.
|
| 653 |
+
|
| 654 |
+
Examples
|
| 655 |
+
--------
|
| 656 |
+
|
| 657 |
+
:func:`determine_backend` allows the backend to be set from a single
|
| 658 |
+
object. :func:`determine_backend_multi` allows multiple objects to be
|
| 659 |
+
checked simultaneously for support in the backend. Suppose we have a
|
| 660 |
+
``BackendAB`` which supports ``TypeA`` and ``TypeB`` in the same call,
|
| 661 |
+
and a ``BackendBC`` that doesn't support ``TypeA``.
|
| 662 |
+
|
| 663 |
+
>>> with ua.set_backend(ex.BackendAB), ua.set_backend(ex.BackendBC):
|
| 664 |
+
... a, b = ex.TypeA(), ex.TypeB()
|
| 665 |
+
... with ua.determine_backend_multi(
|
| 666 |
+
... [ua.Dispatchable(a, "mark"), ua.Dispatchable(b, "mark")],
|
| 667 |
+
... domain="ua_examples"
|
| 668 |
+
... ):
|
| 669 |
+
... res = ex.creation_multimethod()
|
| 670 |
+
... ex.call_multimethod(res, a, b)
|
| 671 |
+
TypeA
|
| 672 |
+
|
| 673 |
+
This won't call ``BackendBC`` because it doesn't support ``TypeA``.
|
| 674 |
+
|
| 675 |
+
We can also use leave out the ``ua.Dispatchable`` if we specify the
|
| 676 |
+
default ``dispatch_type`` for the ``dispatchables`` argument.
|
| 677 |
+
|
| 678 |
+
>>> with ua.set_backend(ex.BackendAB), ua.set_backend(ex.BackendBC):
|
| 679 |
+
... a, b = ex.TypeA(), ex.TypeB()
|
| 680 |
+
... with ua.determine_backend_multi(
|
| 681 |
+
... [a, b], dispatch_type="mark", domain="ua_examples"
|
| 682 |
+
... ):
|
| 683 |
+
... res = ex.creation_multimethod()
|
| 684 |
+
... ex.call_multimethod(res, a, b)
|
| 685 |
+
TypeA
|
| 686 |
+
|
| 687 |
+
"""
|
| 688 |
+
if "dispatch_type" in kwargs:
|
| 689 |
+
disp_type = kwargs.pop("dispatch_type")
|
| 690 |
+
dispatchables = tuple(
|
| 691 |
+
d if isinstance(d, Dispatchable) else Dispatchable(d, disp_type)
|
| 692 |
+
for d in dispatchables
|
| 693 |
+
)
|
| 694 |
+
else:
|
| 695 |
+
dispatchables = tuple(dispatchables)
|
| 696 |
+
if not all(isinstance(d, Dispatchable) for d in dispatchables):
|
| 697 |
+
raise TypeError("dispatchables must be instances of uarray.Dispatchable")
|
| 698 |
+
|
| 699 |
+
if len(kwargs) != 0:
|
| 700 |
+
raise TypeError(f"Received unexpected keyword arguments: {kwargs}")
|
| 701 |
+
|
| 702 |
+
backend = _uarray.determine_backend(domain, dispatchables, coerce)
|
| 703 |
+
|
| 704 |
+
return set_backend(backend, coerce=coerce, only=only)
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
NumPy Array API compatibility library
|
| 3 |
+
|
| 4 |
+
This is a small wrapper around NumPy and CuPy that is compatible with the
|
| 5 |
+
Array API standard https://data-apis.org/array-api/latest/. See also NEP 47
|
| 6 |
+
https://numpy.org/neps/nep-0047-array-api-standard.html.
|
| 7 |
+
|
| 8 |
+
Unlike numpy.array_api, this is not a strict minimal implementation of the
|
| 9 |
+
Array API, but rather just an extension of the main NumPy namespace with
|
| 10 |
+
changes needed to be compliant with the Array API. See
|
| 11 |
+
https://numpy.org/doc/stable/reference/array_api.html for a full list of
|
| 12 |
+
changes. In particular, unlike numpy.array_api, this package does not use a
|
| 13 |
+
separate Array object, but rather just uses numpy.ndarray directly.
|
| 14 |
+
|
| 15 |
+
Library authors using the Array API may wish to test against numpy.array_api
|
| 16 |
+
to ensure they are not using functionality outside of the standard, but prefer
|
| 17 |
+
this implementation for the default when working with NumPy arrays.
|
| 18 |
+
|
| 19 |
+
"""
|
| 20 |
+
__version__ = '1.4'
|
| 21 |
+
|
| 22 |
+
from .common import *
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/_internal.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Internal helpers
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from functools import wraps
|
| 6 |
+
from inspect import signature
|
| 7 |
+
|
| 8 |
+
def get_xp(xp):
|
| 9 |
+
"""
|
| 10 |
+
Decorator to automatically replace xp with the corresponding array module.
|
| 11 |
+
|
| 12 |
+
Use like
|
| 13 |
+
|
| 14 |
+
import numpy as np
|
| 15 |
+
|
| 16 |
+
@get_xp(np)
|
| 17 |
+
def func(x, /, xp, kwarg=None):
|
| 18 |
+
return xp.func(x, kwarg=kwarg)
|
| 19 |
+
|
| 20 |
+
Note that xp must be a keyword argument and come after all non-keyword
|
| 21 |
+
arguments.
|
| 22 |
+
|
| 23 |
+
"""
|
| 24 |
+
def inner(f):
|
| 25 |
+
@wraps(f)
|
| 26 |
+
def wrapped_f(*args, **kwargs):
|
| 27 |
+
return f(*args, xp=xp, **kwargs)
|
| 28 |
+
|
| 29 |
+
sig = signature(f)
|
| 30 |
+
new_sig = sig.replace(parameters=[sig.parameters[i] for i in sig.parameters if i != 'xp'])
|
| 31 |
+
|
| 32 |
+
if wrapped_f.__doc__ is None:
|
| 33 |
+
wrapped_f.__doc__ = f"""\
|
| 34 |
+
Array API compatibility wrapper for {f.__name__}.
|
| 35 |
+
|
| 36 |
+
See the corresponding documentation in NumPy/CuPy and/or the array API
|
| 37 |
+
specification for more details.
|
| 38 |
+
|
| 39 |
+
"""
|
| 40 |
+
wrapped_f.__signature__ = new_sig
|
| 41 |
+
return wrapped_f
|
| 42 |
+
|
| 43 |
+
return inner
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from ._helpers import *
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_aliases.py
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
These are functions that are just aliases of existing functions in NumPy.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
if TYPE_CHECKING:
|
| 9 |
+
from typing import Optional, Sequence, Tuple, Union, List
|
| 10 |
+
from ._typing import ndarray, Device, Dtype, NestedSequence, SupportsBufferProtocol
|
| 11 |
+
|
| 12 |
+
from typing import NamedTuple
|
| 13 |
+
from types import ModuleType
|
| 14 |
+
import inspect
|
| 15 |
+
|
| 16 |
+
from ._helpers import _check_device, _is_numpy_array, array_namespace
|
| 17 |
+
|
| 18 |
+
# These functions are modified from the NumPy versions.
|
| 19 |
+
|
| 20 |
+
def arange(
|
| 21 |
+
start: Union[int, float],
|
| 22 |
+
/,
|
| 23 |
+
stop: Optional[Union[int, float]] = None,
|
| 24 |
+
step: Union[int, float] = 1,
|
| 25 |
+
*,
|
| 26 |
+
xp,
|
| 27 |
+
dtype: Optional[Dtype] = None,
|
| 28 |
+
device: Optional[Device] = None,
|
| 29 |
+
**kwargs
|
| 30 |
+
) -> ndarray:
|
| 31 |
+
_check_device(xp, device)
|
| 32 |
+
return xp.arange(start, stop=stop, step=step, dtype=dtype, **kwargs)
|
| 33 |
+
|
| 34 |
+
def empty(
|
| 35 |
+
shape: Union[int, Tuple[int, ...]],
|
| 36 |
+
xp,
|
| 37 |
+
*,
|
| 38 |
+
dtype: Optional[Dtype] = None,
|
| 39 |
+
device: Optional[Device] = None,
|
| 40 |
+
**kwargs
|
| 41 |
+
) -> ndarray:
|
| 42 |
+
_check_device(xp, device)
|
| 43 |
+
return xp.empty(shape, dtype=dtype, **kwargs)
|
| 44 |
+
|
| 45 |
+
def empty_like(
|
| 46 |
+
x: ndarray, /, xp, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None,
|
| 47 |
+
**kwargs
|
| 48 |
+
) -> ndarray:
|
| 49 |
+
_check_device(xp, device)
|
| 50 |
+
return xp.empty_like(x, dtype=dtype, **kwargs)
|
| 51 |
+
|
| 52 |
+
def eye(
|
| 53 |
+
n_rows: int,
|
| 54 |
+
n_cols: Optional[int] = None,
|
| 55 |
+
/,
|
| 56 |
+
*,
|
| 57 |
+
xp,
|
| 58 |
+
k: int = 0,
|
| 59 |
+
dtype: Optional[Dtype] = None,
|
| 60 |
+
device: Optional[Device] = None,
|
| 61 |
+
**kwargs,
|
| 62 |
+
) -> ndarray:
|
| 63 |
+
_check_device(xp, device)
|
| 64 |
+
return xp.eye(n_rows, M=n_cols, k=k, dtype=dtype, **kwargs)
|
| 65 |
+
|
| 66 |
+
def full(
|
| 67 |
+
shape: Union[int, Tuple[int, ...]],
|
| 68 |
+
fill_value: Union[int, float],
|
| 69 |
+
xp,
|
| 70 |
+
*,
|
| 71 |
+
dtype: Optional[Dtype] = None,
|
| 72 |
+
device: Optional[Device] = None,
|
| 73 |
+
**kwargs,
|
| 74 |
+
) -> ndarray:
|
| 75 |
+
_check_device(xp, device)
|
| 76 |
+
return xp.full(shape, fill_value, dtype=dtype, **kwargs)
|
| 77 |
+
|
| 78 |
+
def full_like(
|
| 79 |
+
x: ndarray,
|
| 80 |
+
/,
|
| 81 |
+
fill_value: Union[int, float],
|
| 82 |
+
*,
|
| 83 |
+
xp,
|
| 84 |
+
dtype: Optional[Dtype] = None,
|
| 85 |
+
device: Optional[Device] = None,
|
| 86 |
+
**kwargs,
|
| 87 |
+
) -> ndarray:
|
| 88 |
+
_check_device(xp, device)
|
| 89 |
+
return xp.full_like(x, fill_value, dtype=dtype, **kwargs)
|
| 90 |
+
|
| 91 |
+
def linspace(
|
| 92 |
+
start: Union[int, float],
|
| 93 |
+
stop: Union[int, float],
|
| 94 |
+
/,
|
| 95 |
+
num: int,
|
| 96 |
+
*,
|
| 97 |
+
xp,
|
| 98 |
+
dtype: Optional[Dtype] = None,
|
| 99 |
+
device: Optional[Device] = None,
|
| 100 |
+
endpoint: bool = True,
|
| 101 |
+
**kwargs,
|
| 102 |
+
) -> ndarray:
|
| 103 |
+
_check_device(xp, device)
|
| 104 |
+
return xp.linspace(start, stop, num, dtype=dtype, endpoint=endpoint, **kwargs)
|
| 105 |
+
|
| 106 |
+
def ones(
|
| 107 |
+
shape: Union[int, Tuple[int, ...]],
|
| 108 |
+
xp,
|
| 109 |
+
*,
|
| 110 |
+
dtype: Optional[Dtype] = None,
|
| 111 |
+
device: Optional[Device] = None,
|
| 112 |
+
**kwargs,
|
| 113 |
+
) -> ndarray:
|
| 114 |
+
_check_device(xp, device)
|
| 115 |
+
return xp.ones(shape, dtype=dtype, **kwargs)
|
| 116 |
+
|
| 117 |
+
def ones_like(
|
| 118 |
+
x: ndarray, /, xp, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None,
|
| 119 |
+
**kwargs,
|
| 120 |
+
) -> ndarray:
|
| 121 |
+
_check_device(xp, device)
|
| 122 |
+
return xp.ones_like(x, dtype=dtype, **kwargs)
|
| 123 |
+
|
| 124 |
+
def zeros(
|
| 125 |
+
shape: Union[int, Tuple[int, ...]],
|
| 126 |
+
xp,
|
| 127 |
+
*,
|
| 128 |
+
dtype: Optional[Dtype] = None,
|
| 129 |
+
device: Optional[Device] = None,
|
| 130 |
+
**kwargs,
|
| 131 |
+
) -> ndarray:
|
| 132 |
+
_check_device(xp, device)
|
| 133 |
+
return xp.zeros(shape, dtype=dtype, **kwargs)
|
| 134 |
+
|
| 135 |
+
def zeros_like(
|
| 136 |
+
x: ndarray, /, xp, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None,
|
| 137 |
+
**kwargs,
|
| 138 |
+
) -> ndarray:
|
| 139 |
+
_check_device(xp, device)
|
| 140 |
+
return xp.zeros_like(x, dtype=dtype, **kwargs)
|
| 141 |
+
|
| 142 |
+
# np.unique() is split into four functions in the array API:
|
| 143 |
+
# unique_all, unique_counts, unique_inverse, and unique_values (this is done
|
| 144 |
+
# to remove polymorphic return types).
|
| 145 |
+
|
| 146 |
+
# The functions here return namedtuples (np.unique() returns a normal
|
| 147 |
+
# tuple).
|
| 148 |
+
class UniqueAllResult(NamedTuple):
|
| 149 |
+
values: ndarray
|
| 150 |
+
indices: ndarray
|
| 151 |
+
inverse_indices: ndarray
|
| 152 |
+
counts: ndarray
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
class UniqueCountsResult(NamedTuple):
|
| 156 |
+
values: ndarray
|
| 157 |
+
counts: ndarray
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
class UniqueInverseResult(NamedTuple):
|
| 161 |
+
values: ndarray
|
| 162 |
+
inverse_indices: ndarray
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def _unique_kwargs(xp):
|
| 166 |
+
# Older versions of NumPy and CuPy do not have equal_nan. Rather than
|
| 167 |
+
# trying to parse version numbers, just check if equal_nan is in the
|
| 168 |
+
# signature.
|
| 169 |
+
s = inspect.signature(xp.unique)
|
| 170 |
+
if 'equal_nan' in s.parameters:
|
| 171 |
+
return {'equal_nan': False}
|
| 172 |
+
return {}
|
| 173 |
+
|
| 174 |
+
def unique_all(x: ndarray, /, xp) -> UniqueAllResult:
|
| 175 |
+
kwargs = _unique_kwargs(xp)
|
| 176 |
+
values, indices, inverse_indices, counts = xp.unique(
|
| 177 |
+
x,
|
| 178 |
+
return_counts=True,
|
| 179 |
+
return_index=True,
|
| 180 |
+
return_inverse=True,
|
| 181 |
+
**kwargs,
|
| 182 |
+
)
|
| 183 |
+
# np.unique() flattens inverse indices, but they need to share x's shape
|
| 184 |
+
# See https://github.com/numpy/numpy/issues/20638
|
| 185 |
+
inverse_indices = inverse_indices.reshape(x.shape)
|
| 186 |
+
return UniqueAllResult(
|
| 187 |
+
values,
|
| 188 |
+
indices,
|
| 189 |
+
inverse_indices,
|
| 190 |
+
counts,
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def unique_counts(x: ndarray, /, xp) -> UniqueCountsResult:
|
| 195 |
+
kwargs = _unique_kwargs(xp)
|
| 196 |
+
res = xp.unique(
|
| 197 |
+
x,
|
| 198 |
+
return_counts=True,
|
| 199 |
+
return_index=False,
|
| 200 |
+
return_inverse=False,
|
| 201 |
+
**kwargs
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
return UniqueCountsResult(*res)
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def unique_inverse(x: ndarray, /, xp) -> UniqueInverseResult:
|
| 208 |
+
kwargs = _unique_kwargs(xp)
|
| 209 |
+
values, inverse_indices = xp.unique(
|
| 210 |
+
x,
|
| 211 |
+
return_counts=False,
|
| 212 |
+
return_index=False,
|
| 213 |
+
return_inverse=True,
|
| 214 |
+
**kwargs,
|
| 215 |
+
)
|
| 216 |
+
# xp.unique() flattens inverse indices, but they need to share x's shape
|
| 217 |
+
# See https://github.com/numpy/numpy/issues/20638
|
| 218 |
+
inverse_indices = inverse_indices.reshape(x.shape)
|
| 219 |
+
return UniqueInverseResult(values, inverse_indices)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def unique_values(x: ndarray, /, xp) -> ndarray:
|
| 223 |
+
kwargs = _unique_kwargs(xp)
|
| 224 |
+
return xp.unique(
|
| 225 |
+
x,
|
| 226 |
+
return_counts=False,
|
| 227 |
+
return_index=False,
|
| 228 |
+
return_inverse=False,
|
| 229 |
+
**kwargs,
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
def astype(x: ndarray, dtype: Dtype, /, *, copy: bool = True) -> ndarray:
|
| 233 |
+
if not copy and dtype == x.dtype:
|
| 234 |
+
return x
|
| 235 |
+
return x.astype(dtype=dtype, copy=copy)
|
| 236 |
+
|
| 237 |
+
# These functions have different keyword argument names
|
| 238 |
+
|
| 239 |
+
def std(
|
| 240 |
+
x: ndarray,
|
| 241 |
+
/,
|
| 242 |
+
xp,
|
| 243 |
+
*,
|
| 244 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 245 |
+
correction: Union[int, float] = 0.0, # correction instead of ddof
|
| 246 |
+
keepdims: bool = False,
|
| 247 |
+
**kwargs,
|
| 248 |
+
) -> ndarray:
|
| 249 |
+
return xp.std(x, axis=axis, ddof=correction, keepdims=keepdims, **kwargs)
|
| 250 |
+
|
| 251 |
+
def var(
|
| 252 |
+
x: ndarray,
|
| 253 |
+
/,
|
| 254 |
+
xp,
|
| 255 |
+
*,
|
| 256 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 257 |
+
correction: Union[int, float] = 0.0, # correction instead of ddof
|
| 258 |
+
keepdims: bool = False,
|
| 259 |
+
**kwargs,
|
| 260 |
+
) -> ndarray:
|
| 261 |
+
return xp.var(x, axis=axis, ddof=correction, keepdims=keepdims, **kwargs)
|
| 262 |
+
|
| 263 |
+
# Unlike transpose(), the axes argument to permute_dims() is required.
|
| 264 |
+
def permute_dims(x: ndarray, /, axes: Tuple[int, ...], xp) -> ndarray:
|
| 265 |
+
return xp.transpose(x, axes)
|
| 266 |
+
|
| 267 |
+
# Creation functions add the device keyword (which does nothing for NumPy)
|
| 268 |
+
|
| 269 |
+
# asarray also adds the copy keyword
|
| 270 |
+
def _asarray(
|
| 271 |
+
obj: Union[
|
| 272 |
+
ndarray,
|
| 273 |
+
bool,
|
| 274 |
+
int,
|
| 275 |
+
float,
|
| 276 |
+
NestedSequence[bool | int | float],
|
| 277 |
+
SupportsBufferProtocol,
|
| 278 |
+
],
|
| 279 |
+
/,
|
| 280 |
+
*,
|
| 281 |
+
dtype: Optional[Dtype] = None,
|
| 282 |
+
device: Optional[Device] = None,
|
| 283 |
+
copy: "Optional[Union[bool, np._CopyMode]]" = None,
|
| 284 |
+
namespace = None,
|
| 285 |
+
**kwargs,
|
| 286 |
+
) -> ndarray:
|
| 287 |
+
"""
|
| 288 |
+
Array API compatibility wrapper for asarray().
|
| 289 |
+
|
| 290 |
+
See the corresponding documentation in NumPy/CuPy and/or the array API
|
| 291 |
+
specification for more details.
|
| 292 |
+
|
| 293 |
+
"""
|
| 294 |
+
if namespace is None:
|
| 295 |
+
try:
|
| 296 |
+
xp = array_namespace(obj, _use_compat=False)
|
| 297 |
+
except ValueError:
|
| 298 |
+
# TODO: What about lists of arrays?
|
| 299 |
+
raise ValueError("A namespace must be specified for asarray() with non-array input")
|
| 300 |
+
elif isinstance(namespace, ModuleType):
|
| 301 |
+
xp = namespace
|
| 302 |
+
elif namespace == 'numpy':
|
| 303 |
+
import numpy as xp
|
| 304 |
+
elif namespace == 'cupy':
|
| 305 |
+
import cupy as xp
|
| 306 |
+
else:
|
| 307 |
+
raise ValueError("Unrecognized namespace argument to asarray()")
|
| 308 |
+
|
| 309 |
+
_check_device(xp, device)
|
| 310 |
+
if _is_numpy_array(obj):
|
| 311 |
+
import numpy as np
|
| 312 |
+
if hasattr(np, '_CopyMode'):
|
| 313 |
+
# Not present in older NumPys
|
| 314 |
+
COPY_FALSE = (False, np._CopyMode.IF_NEEDED)
|
| 315 |
+
COPY_TRUE = (True, np._CopyMode.ALWAYS)
|
| 316 |
+
else:
|
| 317 |
+
COPY_FALSE = (False,)
|
| 318 |
+
COPY_TRUE = (True,)
|
| 319 |
+
else:
|
| 320 |
+
COPY_FALSE = (False,)
|
| 321 |
+
COPY_TRUE = (True,)
|
| 322 |
+
if copy in COPY_FALSE:
|
| 323 |
+
# copy=False is not yet implemented in xp.asarray
|
| 324 |
+
raise NotImplementedError("copy=False is not yet implemented")
|
| 325 |
+
if isinstance(obj, xp.ndarray):
|
| 326 |
+
if dtype is not None and obj.dtype != dtype:
|
| 327 |
+
copy = True
|
| 328 |
+
if copy in COPY_TRUE:
|
| 329 |
+
return xp.array(obj, copy=True, dtype=dtype)
|
| 330 |
+
return obj
|
| 331 |
+
|
| 332 |
+
return xp.asarray(obj, dtype=dtype, **kwargs)
|
| 333 |
+
|
| 334 |
+
# np.reshape calls the keyword argument 'newshape' instead of 'shape'
|
| 335 |
+
def reshape(x: ndarray,
|
| 336 |
+
/,
|
| 337 |
+
shape: Tuple[int, ...],
|
| 338 |
+
xp, copy: Optional[bool] = None,
|
| 339 |
+
**kwargs) -> ndarray:
|
| 340 |
+
if copy is True:
|
| 341 |
+
x = x.copy()
|
| 342 |
+
elif copy is False:
|
| 343 |
+
y = x.view()
|
| 344 |
+
y.shape = shape
|
| 345 |
+
return y
|
| 346 |
+
return xp.reshape(x, shape, **kwargs)
|
| 347 |
+
|
| 348 |
+
# The descending keyword is new in sort and argsort, and 'kind' replaced with
|
| 349 |
+
# 'stable'
|
| 350 |
+
def argsort(
|
| 351 |
+
x: ndarray, /, xp, *, axis: int = -1, descending: bool = False, stable: bool = True,
|
| 352 |
+
**kwargs,
|
| 353 |
+
) -> ndarray:
|
| 354 |
+
# Note: this keyword argument is different, and the default is different.
|
| 355 |
+
# We set it in kwargs like this because numpy.sort uses kind='quicksort'
|
| 356 |
+
# as the default whereas cupy.sort uses kind=None.
|
| 357 |
+
if stable:
|
| 358 |
+
kwargs['kind'] = "stable"
|
| 359 |
+
if not descending:
|
| 360 |
+
res = xp.argsort(x, axis=axis, **kwargs)
|
| 361 |
+
else:
|
| 362 |
+
# As NumPy has no native descending sort, we imitate it here. Note that
|
| 363 |
+
# simply flipping the results of xp.argsort(x, ...) would not
|
| 364 |
+
# respect the relative order like it would in native descending sorts.
|
| 365 |
+
res = xp.flip(
|
| 366 |
+
xp.argsort(xp.flip(x, axis=axis), axis=axis, **kwargs),
|
| 367 |
+
axis=axis,
|
| 368 |
+
)
|
| 369 |
+
# Rely on flip()/argsort() to validate axis
|
| 370 |
+
normalised_axis = axis if axis >= 0 else x.ndim + axis
|
| 371 |
+
max_i = x.shape[normalised_axis] - 1
|
| 372 |
+
res = max_i - res
|
| 373 |
+
return res
|
| 374 |
+
|
| 375 |
+
def sort(
|
| 376 |
+
x: ndarray, /, xp, *, axis: int = -1, descending: bool = False, stable: bool = True,
|
| 377 |
+
**kwargs,
|
| 378 |
+
) -> ndarray:
|
| 379 |
+
# Note: this keyword argument is different, and the default is different.
|
| 380 |
+
# We set it in kwargs like this because numpy.sort uses kind='quicksort'
|
| 381 |
+
# as the default whereas cupy.sort uses kind=None.
|
| 382 |
+
if stable:
|
| 383 |
+
kwargs['kind'] = "stable"
|
| 384 |
+
res = xp.sort(x, axis=axis, **kwargs)
|
| 385 |
+
if descending:
|
| 386 |
+
res = xp.flip(res, axis=axis)
|
| 387 |
+
return res
|
| 388 |
+
|
| 389 |
+
# sum() and prod() should always upcast when dtype=None
|
| 390 |
+
def sum(
|
| 391 |
+
x: ndarray,
|
| 392 |
+
/,
|
| 393 |
+
xp,
|
| 394 |
+
*,
|
| 395 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 396 |
+
dtype: Optional[Dtype] = None,
|
| 397 |
+
keepdims: bool = False,
|
| 398 |
+
**kwargs,
|
| 399 |
+
) -> ndarray:
|
| 400 |
+
# `xp.sum` already upcasts integers, but not floats or complexes
|
| 401 |
+
if dtype is None:
|
| 402 |
+
if x.dtype == xp.float32:
|
| 403 |
+
dtype = xp.float64
|
| 404 |
+
elif x.dtype == xp.complex64:
|
| 405 |
+
dtype = xp.complex128
|
| 406 |
+
return xp.sum(x, axis=axis, dtype=dtype, keepdims=keepdims, **kwargs)
|
| 407 |
+
|
| 408 |
+
def prod(
|
| 409 |
+
x: ndarray,
|
| 410 |
+
/,
|
| 411 |
+
xp,
|
| 412 |
+
*,
|
| 413 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 414 |
+
dtype: Optional[Dtype] = None,
|
| 415 |
+
keepdims: bool = False,
|
| 416 |
+
**kwargs,
|
| 417 |
+
) -> ndarray:
|
| 418 |
+
if dtype is None:
|
| 419 |
+
if x.dtype == xp.float32:
|
| 420 |
+
dtype = xp.float64
|
| 421 |
+
elif x.dtype == xp.complex64:
|
| 422 |
+
dtype = xp.complex128
|
| 423 |
+
return xp.prod(x, dtype=dtype, axis=axis, keepdims=keepdims, **kwargs)
|
| 424 |
+
|
| 425 |
+
# ceil, floor, and trunc return integers for integer inputs
|
| 426 |
+
|
| 427 |
+
def ceil(x: ndarray, /, xp, **kwargs) -> ndarray:
|
| 428 |
+
if xp.issubdtype(x.dtype, xp.integer):
|
| 429 |
+
return x
|
| 430 |
+
return xp.ceil(x, **kwargs)
|
| 431 |
+
|
| 432 |
+
def floor(x: ndarray, /, xp, **kwargs) -> ndarray:
|
| 433 |
+
if xp.issubdtype(x.dtype, xp.integer):
|
| 434 |
+
return x
|
| 435 |
+
return xp.floor(x, **kwargs)
|
| 436 |
+
|
| 437 |
+
def trunc(x: ndarray, /, xp, **kwargs) -> ndarray:
|
| 438 |
+
if xp.issubdtype(x.dtype, xp.integer):
|
| 439 |
+
return x
|
| 440 |
+
return xp.trunc(x, **kwargs)
|
| 441 |
+
|
| 442 |
+
# linear algebra functions
|
| 443 |
+
|
| 444 |
+
def matmul(x1: ndarray, x2: ndarray, /, xp, **kwargs) -> ndarray:
|
| 445 |
+
return xp.matmul(x1, x2, **kwargs)
|
| 446 |
+
|
| 447 |
+
# Unlike transpose, matrix_transpose only transposes the last two axes.
|
| 448 |
+
def matrix_transpose(x: ndarray, /, xp) -> ndarray:
|
| 449 |
+
if x.ndim < 2:
|
| 450 |
+
raise ValueError("x must be at least 2-dimensional for matrix_transpose")
|
| 451 |
+
return xp.swapaxes(x, -1, -2)
|
| 452 |
+
|
| 453 |
+
def tensordot(x1: ndarray,
|
| 454 |
+
x2: ndarray,
|
| 455 |
+
/,
|
| 456 |
+
xp,
|
| 457 |
+
*,
|
| 458 |
+
axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2,
|
| 459 |
+
**kwargs,
|
| 460 |
+
) -> ndarray:
|
| 461 |
+
return xp.tensordot(x1, x2, axes=axes, **kwargs)
|
| 462 |
+
|
| 463 |
+
def vecdot(x1: ndarray, x2: ndarray, /, xp, *, axis: int = -1) -> ndarray:
|
| 464 |
+
ndim = max(x1.ndim, x2.ndim)
|
| 465 |
+
x1_shape = (1,)*(ndim - x1.ndim) + tuple(x1.shape)
|
| 466 |
+
x2_shape = (1,)*(ndim - x2.ndim) + tuple(x2.shape)
|
| 467 |
+
if x1_shape[axis] != x2_shape[axis]:
|
| 468 |
+
raise ValueError("x1 and x2 must have the same size along the given axis")
|
| 469 |
+
|
| 470 |
+
if hasattr(xp, 'broadcast_tensors'):
|
| 471 |
+
_broadcast = xp.broadcast_tensors
|
| 472 |
+
else:
|
| 473 |
+
_broadcast = xp.broadcast_arrays
|
| 474 |
+
|
| 475 |
+
x1_, x2_ = _broadcast(x1, x2)
|
| 476 |
+
x1_ = xp.moveaxis(x1_, axis, -1)
|
| 477 |
+
x2_ = xp.moveaxis(x2_, axis, -1)
|
| 478 |
+
|
| 479 |
+
res = x1_[..., None, :] @ x2_[..., None]
|
| 480 |
+
return res[..., 0, 0]
|
| 481 |
+
|
| 482 |
+
# isdtype is a new function in the 2022.12 array API specification.
|
| 483 |
+
|
| 484 |
+
def isdtype(
|
| 485 |
+
dtype: Dtype, kind: Union[Dtype, str, Tuple[Union[Dtype, str], ...]], xp,
|
| 486 |
+
*, _tuple=True, # Disallow nested tuples
|
| 487 |
+
) -> bool:
|
| 488 |
+
"""
|
| 489 |
+
Returns a boolean indicating whether a provided dtype is of a specified data type ``kind``.
|
| 490 |
+
|
| 491 |
+
Note that outside of this function, this compat library does not yet fully
|
| 492 |
+
support complex numbers.
|
| 493 |
+
|
| 494 |
+
See
|
| 495 |
+
https://data-apis.org/array-api/latest/API_specification/generated/array_api.isdtype.html
|
| 496 |
+
for more details
|
| 497 |
+
"""
|
| 498 |
+
if isinstance(kind, tuple) and _tuple:
|
| 499 |
+
return any(isdtype(dtype, k, xp, _tuple=False) for k in kind)
|
| 500 |
+
elif isinstance(kind, str):
|
| 501 |
+
if kind == 'bool':
|
| 502 |
+
return dtype == xp.bool_
|
| 503 |
+
elif kind == 'signed integer':
|
| 504 |
+
return xp.issubdtype(dtype, xp.signedinteger)
|
| 505 |
+
elif kind == 'unsigned integer':
|
| 506 |
+
return xp.issubdtype(dtype, xp.unsignedinteger)
|
| 507 |
+
elif kind == 'integral':
|
| 508 |
+
return xp.issubdtype(dtype, xp.integer)
|
| 509 |
+
elif kind == 'real floating':
|
| 510 |
+
return xp.issubdtype(dtype, xp.floating)
|
| 511 |
+
elif kind == 'complex floating':
|
| 512 |
+
return xp.issubdtype(dtype, xp.complexfloating)
|
| 513 |
+
elif kind == 'numeric':
|
| 514 |
+
return xp.issubdtype(dtype, xp.number)
|
| 515 |
+
else:
|
| 516 |
+
raise ValueError(f"Unrecognized data type kind: {kind!r}")
|
| 517 |
+
else:
|
| 518 |
+
# This will allow things that aren't required by the spec, like
|
| 519 |
+
# isdtype(np.float64, float) or isdtype(np.int64, 'l'). Should we be
|
| 520 |
+
# more strict here to match the type annotation? Note that the
|
| 521 |
+
# numpy.array_api implementation will be very strict.
|
| 522 |
+
return dtype == kind
|
| 523 |
+
|
| 524 |
+
__all__ = ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like',
|
| 525 |
+
'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like',
|
| 526 |
+
'UniqueAllResult', 'UniqueCountsResult', 'UniqueInverseResult',
|
| 527 |
+
'unique_all', 'unique_counts', 'unique_inverse', 'unique_values',
|
| 528 |
+
'astype', 'std', 'var', 'permute_dims', 'reshape', 'argsort',
|
| 529 |
+
'sort', 'sum', 'prod', 'ceil', 'floor', 'trunc', 'matmul',
|
| 530 |
+
'matrix_transpose', 'tensordot', 'vecdot', 'isdtype']
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_helpers.py
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Various helper functions which are not part of the spec.
|
| 3 |
+
|
| 4 |
+
Functions which start with an underscore are for internal use only but helpers
|
| 5 |
+
that are in __all__ are intended as additional helper functions for use by end
|
| 6 |
+
users of the compat library.
|
| 7 |
+
"""
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import sys
|
| 11 |
+
import math
|
| 12 |
+
|
| 13 |
+
def _is_numpy_array(x):
|
| 14 |
+
# Avoid importing NumPy if it isn't already
|
| 15 |
+
if 'numpy' not in sys.modules:
|
| 16 |
+
return False
|
| 17 |
+
|
| 18 |
+
import numpy as np
|
| 19 |
+
|
| 20 |
+
# TODO: Should we reject ndarray subclasses?
|
| 21 |
+
return isinstance(x, (np.ndarray, np.generic))
|
| 22 |
+
|
| 23 |
+
def _is_cupy_array(x):
|
| 24 |
+
# Avoid importing NumPy if it isn't already
|
| 25 |
+
if 'cupy' not in sys.modules:
|
| 26 |
+
return False
|
| 27 |
+
|
| 28 |
+
import cupy as cp
|
| 29 |
+
|
| 30 |
+
# TODO: Should we reject ndarray subclasses?
|
| 31 |
+
return isinstance(x, (cp.ndarray, cp.generic))
|
| 32 |
+
|
| 33 |
+
def _is_torch_array(x):
|
| 34 |
+
# Avoid importing torch if it isn't already
|
| 35 |
+
if 'torch' not in sys.modules:
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
import torch
|
| 39 |
+
|
| 40 |
+
# TODO: Should we reject ndarray subclasses?
|
| 41 |
+
return isinstance(x, torch.Tensor)
|
| 42 |
+
|
| 43 |
+
def is_array_api_obj(x):
|
| 44 |
+
"""
|
| 45 |
+
Check if x is an array API compatible array object.
|
| 46 |
+
"""
|
| 47 |
+
return _is_numpy_array(x) \
|
| 48 |
+
or _is_cupy_array(x) \
|
| 49 |
+
or _is_torch_array(x) \
|
| 50 |
+
or hasattr(x, '__array_namespace__')
|
| 51 |
+
|
| 52 |
+
def _check_api_version(api_version):
|
| 53 |
+
if api_version is not None and api_version != '2021.12':
|
| 54 |
+
raise ValueError("Only the 2021.12 version of the array API specification is currently supported")
|
| 55 |
+
|
| 56 |
+
def array_namespace(*xs, api_version=None, _use_compat=True):
|
| 57 |
+
"""
|
| 58 |
+
Get the array API compatible namespace for the arrays `xs`.
|
| 59 |
+
|
| 60 |
+
`xs` should contain one or more arrays.
|
| 61 |
+
|
| 62 |
+
Typical usage is
|
| 63 |
+
|
| 64 |
+
def your_function(x, y):
|
| 65 |
+
xp = array_api_compat.array_namespace(x, y)
|
| 66 |
+
# Now use xp as the array library namespace
|
| 67 |
+
return xp.mean(x, axis=0) + 2*xp.std(y, axis=0)
|
| 68 |
+
|
| 69 |
+
api_version should be the newest version of the spec that you need support
|
| 70 |
+
for (currently the compat library wrapped APIs only support v2021.12).
|
| 71 |
+
"""
|
| 72 |
+
namespaces = set()
|
| 73 |
+
for x in xs:
|
| 74 |
+
if isinstance(x, (tuple, list)):
|
| 75 |
+
namespaces.add(array_namespace(*x, _use_compat=_use_compat))
|
| 76 |
+
elif hasattr(x, '__array_namespace__'):
|
| 77 |
+
namespaces.add(x.__array_namespace__(api_version=api_version))
|
| 78 |
+
elif _is_numpy_array(x):
|
| 79 |
+
_check_api_version(api_version)
|
| 80 |
+
if _use_compat:
|
| 81 |
+
from .. import numpy as numpy_namespace
|
| 82 |
+
namespaces.add(numpy_namespace)
|
| 83 |
+
else:
|
| 84 |
+
import numpy as np
|
| 85 |
+
namespaces.add(np)
|
| 86 |
+
elif _is_cupy_array(x):
|
| 87 |
+
_check_api_version(api_version)
|
| 88 |
+
if _use_compat:
|
| 89 |
+
from .. import cupy as cupy_namespace
|
| 90 |
+
namespaces.add(cupy_namespace)
|
| 91 |
+
else:
|
| 92 |
+
import cupy as cp
|
| 93 |
+
namespaces.add(cp)
|
| 94 |
+
elif _is_torch_array(x):
|
| 95 |
+
_check_api_version(api_version)
|
| 96 |
+
if _use_compat:
|
| 97 |
+
from .. import torch as torch_namespace
|
| 98 |
+
namespaces.add(torch_namespace)
|
| 99 |
+
else:
|
| 100 |
+
import torch
|
| 101 |
+
namespaces.add(torch)
|
| 102 |
+
else:
|
| 103 |
+
# TODO: Support Python scalars?
|
| 104 |
+
raise TypeError("The input is not a supported array type")
|
| 105 |
+
|
| 106 |
+
if not namespaces:
|
| 107 |
+
raise TypeError("Unrecognized array input")
|
| 108 |
+
|
| 109 |
+
if len(namespaces) != 1:
|
| 110 |
+
raise TypeError(f"Multiple namespaces for array inputs: {namespaces}")
|
| 111 |
+
|
| 112 |
+
xp, = namespaces
|
| 113 |
+
|
| 114 |
+
return xp
|
| 115 |
+
|
| 116 |
+
# backwards compatibility alias
|
| 117 |
+
get_namespace = array_namespace
|
| 118 |
+
|
| 119 |
+
def _check_device(xp, device):
|
| 120 |
+
if xp == sys.modules.get('numpy'):
|
| 121 |
+
if device not in ["cpu", None]:
|
| 122 |
+
raise ValueError(f"Unsupported device for NumPy: {device!r}")
|
| 123 |
+
|
| 124 |
+
# device() is not on numpy.ndarray and and to_device() is not on numpy.ndarray
|
| 125 |
+
# or cupy.ndarray. They are not included in array objects of this library
|
| 126 |
+
# because this library just reuses the respective ndarray classes without
|
| 127 |
+
# wrapping or subclassing them. These helper functions can be used instead of
|
| 128 |
+
# the wrapper functions for libraries that need to support both NumPy/CuPy and
|
| 129 |
+
# other libraries that use devices.
|
| 130 |
+
def device(x: "Array", /) -> "Device":
|
| 131 |
+
"""
|
| 132 |
+
Hardware device the array data resides on.
|
| 133 |
+
|
| 134 |
+
Parameters
|
| 135 |
+
----------
|
| 136 |
+
x: array
|
| 137 |
+
array instance from NumPy or an array API compatible library.
|
| 138 |
+
|
| 139 |
+
Returns
|
| 140 |
+
-------
|
| 141 |
+
out: device
|
| 142 |
+
a ``device`` object (see the "Device Support" section of the array API specification).
|
| 143 |
+
"""
|
| 144 |
+
if _is_numpy_array(x):
|
| 145 |
+
return "cpu"
|
| 146 |
+
return x.device
|
| 147 |
+
|
| 148 |
+
# Based on cupy.array_api.Array.to_device
|
| 149 |
+
def _cupy_to_device(x, device, /, stream=None):
|
| 150 |
+
import cupy as cp
|
| 151 |
+
from cupy.cuda import Device as _Device
|
| 152 |
+
from cupy.cuda import stream as stream_module
|
| 153 |
+
from cupy_backends.cuda.api import runtime
|
| 154 |
+
|
| 155 |
+
if device == x.device:
|
| 156 |
+
return x
|
| 157 |
+
elif device == "cpu":
|
| 158 |
+
# allowing us to use `to_device(x, "cpu")`
|
| 159 |
+
# is useful for portable test swapping between
|
| 160 |
+
# host and device backends
|
| 161 |
+
return x.get()
|
| 162 |
+
elif not isinstance(device, _Device):
|
| 163 |
+
raise ValueError(f"Unsupported device {device!r}")
|
| 164 |
+
else:
|
| 165 |
+
# see cupy/cupy#5985 for the reason how we handle device/stream here
|
| 166 |
+
prev_device = runtime.getDevice()
|
| 167 |
+
prev_stream: stream_module.Stream = None
|
| 168 |
+
if stream is not None:
|
| 169 |
+
prev_stream = stream_module.get_current_stream()
|
| 170 |
+
# stream can be an int as specified in __dlpack__, or a CuPy stream
|
| 171 |
+
if isinstance(stream, int):
|
| 172 |
+
stream = cp.cuda.ExternalStream(stream)
|
| 173 |
+
elif isinstance(stream, cp.cuda.Stream):
|
| 174 |
+
pass
|
| 175 |
+
else:
|
| 176 |
+
raise ValueError('the input stream is not recognized')
|
| 177 |
+
stream.use()
|
| 178 |
+
try:
|
| 179 |
+
runtime.setDevice(device.id)
|
| 180 |
+
arr = x.copy()
|
| 181 |
+
finally:
|
| 182 |
+
runtime.setDevice(prev_device)
|
| 183 |
+
if stream is not None:
|
| 184 |
+
prev_stream.use()
|
| 185 |
+
return arr
|
| 186 |
+
|
| 187 |
+
def _torch_to_device(x, device, /, stream=None):
|
| 188 |
+
if stream is not None:
|
| 189 |
+
raise NotImplementedError
|
| 190 |
+
return x.to(device)
|
| 191 |
+
|
| 192 |
+
def to_device(x: "Array", device: "Device", /, *, stream: "Optional[Union[int, Any]]" = None) -> "Array":
|
| 193 |
+
"""
|
| 194 |
+
Copy the array from the device on which it currently resides to the specified ``device``.
|
| 195 |
+
|
| 196 |
+
Parameters
|
| 197 |
+
----------
|
| 198 |
+
x: array
|
| 199 |
+
array instance from NumPy or an array API compatible library.
|
| 200 |
+
device: device
|
| 201 |
+
a ``device`` object (see the "Device Support" section of the array API specification).
|
| 202 |
+
stream: Optional[Union[int, Any]]
|
| 203 |
+
stream object to use during copy. In addition to the types supported in ``array.__dlpack__``, implementations may choose to support any library-specific stream object with the caveat that any code using such an object would not be portable.
|
| 204 |
+
|
| 205 |
+
Returns
|
| 206 |
+
-------
|
| 207 |
+
out: array
|
| 208 |
+
an array with the same data and data type as ``x`` and located on the specified ``device``.
|
| 209 |
+
|
| 210 |
+
.. note::
|
| 211 |
+
If ``stream`` is given, the copy operation should be enqueued on the provided ``stream``; otherwise, the copy operation should be enqueued on the default stream/queue. Whether the copy is performed synchronously or asynchronously is implementation-dependent. Accordingly, if synchronization is required to guarantee data safety, this must be clearly explained in a conforming library's documentation.
|
| 212 |
+
"""
|
| 213 |
+
if _is_numpy_array(x):
|
| 214 |
+
if stream is not None:
|
| 215 |
+
raise ValueError("The stream argument to to_device() is not supported")
|
| 216 |
+
if device == 'cpu':
|
| 217 |
+
return x
|
| 218 |
+
raise ValueError(f"Unsupported device {device!r}")
|
| 219 |
+
elif _is_cupy_array(x):
|
| 220 |
+
# cupy does not yet have to_device
|
| 221 |
+
return _cupy_to_device(x, device, stream=stream)
|
| 222 |
+
elif _is_torch_array(x):
|
| 223 |
+
return _torch_to_device(x, device, stream=stream)
|
| 224 |
+
return x.to_device(device, stream=stream)
|
| 225 |
+
|
| 226 |
+
def size(x):
|
| 227 |
+
"""
|
| 228 |
+
Return the total number of elements of x
|
| 229 |
+
"""
|
| 230 |
+
if None in x.shape:
|
| 231 |
+
return None
|
| 232 |
+
return math.prod(x.shape)
|
| 233 |
+
|
| 234 |
+
__all__ = ['is_array_api_obj', 'array_namespace', 'get_namespace', 'device', 'to_device', 'size']
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_linalg.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import TYPE_CHECKING, NamedTuple
|
| 4 |
+
if TYPE_CHECKING:
|
| 5 |
+
from typing import Literal, Optional, Sequence, Tuple, Union
|
| 6 |
+
from ._typing import ndarray
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
if np.__version__[0] == "2":
|
| 10 |
+
from numpy.lib.array_utils import normalize_axis_tuple
|
| 11 |
+
else:
|
| 12 |
+
from numpy.core.numeric import normalize_axis_tuple
|
| 13 |
+
|
| 14 |
+
from ._aliases import matmul, matrix_transpose, tensordot, vecdot
|
| 15 |
+
from .._internal import get_xp
|
| 16 |
+
|
| 17 |
+
# These are in the main NumPy namespace but not in numpy.linalg
|
| 18 |
+
def cross(x1: ndarray, x2: ndarray, /, xp, *, axis: int = -1, **kwargs) -> ndarray:
|
| 19 |
+
return xp.cross(x1, x2, axis=axis, **kwargs)
|
| 20 |
+
|
| 21 |
+
def outer(x1: ndarray, x2: ndarray, /, xp, **kwargs) -> ndarray:
|
| 22 |
+
return xp.outer(x1, x2, **kwargs)
|
| 23 |
+
|
| 24 |
+
class EighResult(NamedTuple):
|
| 25 |
+
eigenvalues: ndarray
|
| 26 |
+
eigenvectors: ndarray
|
| 27 |
+
|
| 28 |
+
class QRResult(NamedTuple):
|
| 29 |
+
Q: ndarray
|
| 30 |
+
R: ndarray
|
| 31 |
+
|
| 32 |
+
class SlogdetResult(NamedTuple):
|
| 33 |
+
sign: ndarray
|
| 34 |
+
logabsdet: ndarray
|
| 35 |
+
|
| 36 |
+
class SVDResult(NamedTuple):
|
| 37 |
+
U: ndarray
|
| 38 |
+
S: ndarray
|
| 39 |
+
Vh: ndarray
|
| 40 |
+
|
| 41 |
+
# These functions are the same as their NumPy counterparts except they return
|
| 42 |
+
# a namedtuple.
|
| 43 |
+
def eigh(x: ndarray, /, xp, **kwargs) -> EighResult:
|
| 44 |
+
return EighResult(*xp.linalg.eigh(x, **kwargs))
|
| 45 |
+
|
| 46 |
+
def qr(x: ndarray, /, xp, *, mode: Literal['reduced', 'complete'] = 'reduced',
|
| 47 |
+
**kwargs) -> QRResult:
|
| 48 |
+
return QRResult(*xp.linalg.qr(x, mode=mode, **kwargs))
|
| 49 |
+
|
| 50 |
+
def slogdet(x: ndarray, /, xp, **kwargs) -> SlogdetResult:
|
| 51 |
+
return SlogdetResult(*xp.linalg.slogdet(x, **kwargs))
|
| 52 |
+
|
| 53 |
+
def svd(x: ndarray, /, xp, *, full_matrices: bool = True, **kwargs) -> SVDResult:
|
| 54 |
+
return SVDResult(*xp.linalg.svd(x, full_matrices=full_matrices, **kwargs))
|
| 55 |
+
|
| 56 |
+
# These functions have additional keyword arguments
|
| 57 |
+
|
| 58 |
+
# The upper keyword argument is new from NumPy
|
| 59 |
+
def cholesky(x: ndarray, /, xp, *, upper: bool = False, **kwargs) -> ndarray:
|
| 60 |
+
L = xp.linalg.cholesky(x, **kwargs)
|
| 61 |
+
if upper:
|
| 62 |
+
return get_xp(xp)(matrix_transpose)(L)
|
| 63 |
+
return L
|
| 64 |
+
|
| 65 |
+
# The rtol keyword argument of matrix_rank() and pinv() is new from NumPy.
|
| 66 |
+
# Note that it has a different semantic meaning from tol and rcond.
|
| 67 |
+
def matrix_rank(x: ndarray,
|
| 68 |
+
/,
|
| 69 |
+
xp,
|
| 70 |
+
*,
|
| 71 |
+
rtol: Optional[Union[float, ndarray]] = None,
|
| 72 |
+
**kwargs) -> ndarray:
|
| 73 |
+
# this is different from xp.linalg.matrix_rank, which supports 1
|
| 74 |
+
# dimensional arrays.
|
| 75 |
+
if x.ndim < 2:
|
| 76 |
+
raise xp.linalg.LinAlgError("1-dimensional array given. Array must be at least two-dimensional")
|
| 77 |
+
S = xp.linalg.svd(x, compute_uv=False, **kwargs)
|
| 78 |
+
if rtol is None:
|
| 79 |
+
tol = S.max(axis=-1, keepdims=True) * max(x.shape[-2:]) * xp.finfo(S.dtype).eps
|
| 80 |
+
else:
|
| 81 |
+
# this is different from xp.linalg.matrix_rank, which does not
|
| 82 |
+
# multiply the tolerance by the largest singular value.
|
| 83 |
+
tol = S.max(axis=-1, keepdims=True)*xp.asarray(rtol)[..., xp.newaxis]
|
| 84 |
+
return xp.count_nonzero(S > tol, axis=-1)
|
| 85 |
+
|
| 86 |
+
def pinv(x: ndarray, /, xp, *, rtol: Optional[Union[float, ndarray]] = None, **kwargs) -> ndarray:
|
| 87 |
+
# this is different from xp.linalg.pinv, which does not multiply the
|
| 88 |
+
# default tolerance by max(M, N).
|
| 89 |
+
if rtol is None:
|
| 90 |
+
rtol = max(x.shape[-2:]) * xp.finfo(x.dtype).eps
|
| 91 |
+
return xp.linalg.pinv(x, rcond=rtol, **kwargs)
|
| 92 |
+
|
| 93 |
+
# These functions are new in the array API spec
|
| 94 |
+
|
| 95 |
+
def matrix_norm(x: ndarray, /, xp, *, keepdims: bool = False, ord: Optional[Union[int, float, Literal['fro', 'nuc']]] = 'fro') -> ndarray:
|
| 96 |
+
return xp.linalg.norm(x, axis=(-2, -1), keepdims=keepdims, ord=ord)
|
| 97 |
+
|
| 98 |
+
# svdvals is not in NumPy (but it is in SciPy). It is equivalent to
|
| 99 |
+
# xp.linalg.svd(compute_uv=False).
|
| 100 |
+
def svdvals(x: ndarray, /, xp) -> Union[ndarray, Tuple[ndarray, ...]]:
|
| 101 |
+
return xp.linalg.svd(x, compute_uv=False)
|
| 102 |
+
|
| 103 |
+
def vector_norm(x: ndarray, /, xp, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False, ord: Optional[Union[int, float]] = 2) -> ndarray:
|
| 104 |
+
# xp.linalg.norm tries to do a matrix norm whenever axis is a 2-tuple or
|
| 105 |
+
# when axis=None and the input is 2-D, so to force a vector norm, we make
|
| 106 |
+
# it so the input is 1-D (for axis=None), or reshape so that norm is done
|
| 107 |
+
# on a single dimension.
|
| 108 |
+
if axis is None:
|
| 109 |
+
# Note: xp.linalg.norm() doesn't handle 0-D arrays
|
| 110 |
+
x = x.ravel()
|
| 111 |
+
_axis = 0
|
| 112 |
+
elif isinstance(axis, tuple):
|
| 113 |
+
# Note: The axis argument supports any number of axes, whereas
|
| 114 |
+
# xp.linalg.norm() only supports a single axis for vector norm.
|
| 115 |
+
normalized_axis = normalize_axis_tuple(axis, x.ndim)
|
| 116 |
+
rest = tuple(i for i in range(x.ndim) if i not in normalized_axis)
|
| 117 |
+
newshape = axis + rest
|
| 118 |
+
x = xp.transpose(x, newshape).reshape(
|
| 119 |
+
(xp.prod([x.shape[i] for i in axis], dtype=int), *[x.shape[i] for i in rest]))
|
| 120 |
+
_axis = 0
|
| 121 |
+
else:
|
| 122 |
+
_axis = axis
|
| 123 |
+
|
| 124 |
+
res = xp.linalg.norm(x, axis=_axis, ord=ord)
|
| 125 |
+
|
| 126 |
+
if keepdims:
|
| 127 |
+
# We can't reuse xp.linalg.norm(keepdims) because of the reshape hacks
|
| 128 |
+
# above to avoid matrix norm logic.
|
| 129 |
+
shape = list(x.shape)
|
| 130 |
+
_axis = normalize_axis_tuple(range(x.ndim) if axis is None else axis, x.ndim)
|
| 131 |
+
for i in _axis:
|
| 132 |
+
shape[i] = 1
|
| 133 |
+
res = xp.reshape(res, tuple(shape))
|
| 134 |
+
|
| 135 |
+
return res
|
| 136 |
+
|
| 137 |
+
# xp.diagonal and xp.trace operate on the first two axes whereas these
|
| 138 |
+
# operates on the last two
|
| 139 |
+
|
| 140 |
+
def diagonal(x: ndarray, /, xp, *, offset: int = 0, **kwargs) -> ndarray:
|
| 141 |
+
return xp.diagonal(x, offset=offset, axis1=-2, axis2=-1, **kwargs)
|
| 142 |
+
|
| 143 |
+
def trace(x: ndarray, /, xp, *, offset: int = 0, dtype=None, **kwargs) -> ndarray:
|
| 144 |
+
if dtype is None:
|
| 145 |
+
if x.dtype == xp.float32:
|
| 146 |
+
dtype = xp.float64
|
| 147 |
+
elif x.dtype == xp.complex64:
|
| 148 |
+
dtype = xp.complex128
|
| 149 |
+
return xp.asarray(xp.trace(x, offset=offset, dtype=dtype, axis1=-2, axis2=-1, **kwargs))
|
| 150 |
+
|
| 151 |
+
__all__ = ['cross', 'matmul', 'outer', 'tensordot', 'EighResult',
|
| 152 |
+
'QRResult', 'SlogdetResult', 'SVDResult', 'eigh', 'qr', 'slogdet',
|
| 153 |
+
'svd', 'cholesky', 'matrix_rank', 'pinv', 'matrix_norm',
|
| 154 |
+
'matrix_transpose', 'svdvals', 'vecdot', 'vector_norm', 'diagonal',
|
| 155 |
+
'trace']
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_typing.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
"NestedSequence",
|
| 5 |
+
"SupportsBufferProtocol",
|
| 6 |
+
]
|
| 7 |
+
|
| 8 |
+
from typing import (
|
| 9 |
+
Any,
|
| 10 |
+
TypeVar,
|
| 11 |
+
Protocol,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
_T_co = TypeVar("_T_co", covariant=True)
|
| 15 |
+
|
| 16 |
+
class NestedSequence(Protocol[_T_co]):
|
| 17 |
+
def __getitem__(self, key: int, /) -> _T_co | NestedSequence[_T_co]: ...
|
| 18 |
+
def __len__(self, /) -> int: ...
|
| 19 |
+
|
| 20 |
+
SupportsBufferProtocol = Any
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from cupy import *
|
| 2 |
+
|
| 3 |
+
# from cupy import * doesn't overwrite these builtin names
|
| 4 |
+
from cupy import abs, max, min, round
|
| 5 |
+
|
| 6 |
+
# These imports may overwrite names from the import * above.
|
| 7 |
+
from ._aliases import *
|
| 8 |
+
|
| 9 |
+
# See the comment in the numpy __init__.py
|
| 10 |
+
__import__(__package__ + '.linalg')
|
| 11 |
+
|
| 12 |
+
from .linalg import matrix_transpose, vecdot
|
| 13 |
+
|
| 14 |
+
from ..common._helpers import *
|
| 15 |
+
|
| 16 |
+
__array_api_version__ = '2022.12'
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/_aliases.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from functools import partial
|
| 4 |
+
|
| 5 |
+
from ..common import _aliases
|
| 6 |
+
|
| 7 |
+
from .._internal import get_xp
|
| 8 |
+
|
| 9 |
+
asarray = asarray_cupy = partial(_aliases._asarray, namespace='cupy')
|
| 10 |
+
asarray.__doc__ = _aliases._asarray.__doc__
|
| 11 |
+
del partial
|
| 12 |
+
|
| 13 |
+
import cupy as cp
|
| 14 |
+
bool = cp.bool_
|
| 15 |
+
|
| 16 |
+
# Basic renames
|
| 17 |
+
acos = cp.arccos
|
| 18 |
+
acosh = cp.arccosh
|
| 19 |
+
asin = cp.arcsin
|
| 20 |
+
asinh = cp.arcsinh
|
| 21 |
+
atan = cp.arctan
|
| 22 |
+
atan2 = cp.arctan2
|
| 23 |
+
atanh = cp.arctanh
|
| 24 |
+
bitwise_left_shift = cp.left_shift
|
| 25 |
+
bitwise_invert = cp.invert
|
| 26 |
+
bitwise_right_shift = cp.right_shift
|
| 27 |
+
concat = cp.concatenate
|
| 28 |
+
pow = cp.power
|
| 29 |
+
|
| 30 |
+
arange = get_xp(cp)(_aliases.arange)
|
| 31 |
+
empty = get_xp(cp)(_aliases.empty)
|
| 32 |
+
empty_like = get_xp(cp)(_aliases.empty_like)
|
| 33 |
+
eye = get_xp(cp)(_aliases.eye)
|
| 34 |
+
full = get_xp(cp)(_aliases.full)
|
| 35 |
+
full_like = get_xp(cp)(_aliases.full_like)
|
| 36 |
+
linspace = get_xp(cp)(_aliases.linspace)
|
| 37 |
+
ones = get_xp(cp)(_aliases.ones)
|
| 38 |
+
ones_like = get_xp(cp)(_aliases.ones_like)
|
| 39 |
+
zeros = get_xp(cp)(_aliases.zeros)
|
| 40 |
+
zeros_like = get_xp(cp)(_aliases.zeros_like)
|
| 41 |
+
UniqueAllResult = get_xp(cp)(_aliases.UniqueAllResult)
|
| 42 |
+
UniqueCountsResult = get_xp(cp)(_aliases.UniqueCountsResult)
|
| 43 |
+
UniqueInverseResult = get_xp(cp)(_aliases.UniqueInverseResult)
|
| 44 |
+
unique_all = get_xp(cp)(_aliases.unique_all)
|
| 45 |
+
unique_counts = get_xp(cp)(_aliases.unique_counts)
|
| 46 |
+
unique_inverse = get_xp(cp)(_aliases.unique_inverse)
|
| 47 |
+
unique_values = get_xp(cp)(_aliases.unique_values)
|
| 48 |
+
astype = _aliases.astype
|
| 49 |
+
std = get_xp(cp)(_aliases.std)
|
| 50 |
+
var = get_xp(cp)(_aliases.var)
|
| 51 |
+
permute_dims = get_xp(cp)(_aliases.permute_dims)
|
| 52 |
+
reshape = get_xp(cp)(_aliases.reshape)
|
| 53 |
+
argsort = get_xp(cp)(_aliases.argsort)
|
| 54 |
+
sort = get_xp(cp)(_aliases.sort)
|
| 55 |
+
sum = get_xp(cp)(_aliases.sum)
|
| 56 |
+
prod = get_xp(cp)(_aliases.prod)
|
| 57 |
+
ceil = get_xp(cp)(_aliases.ceil)
|
| 58 |
+
floor = get_xp(cp)(_aliases.floor)
|
| 59 |
+
trunc = get_xp(cp)(_aliases.trunc)
|
| 60 |
+
matmul = get_xp(cp)(_aliases.matmul)
|
| 61 |
+
matrix_transpose = get_xp(cp)(_aliases.matrix_transpose)
|
| 62 |
+
tensordot = get_xp(cp)(_aliases.tensordot)
|
| 63 |
+
vecdot = get_xp(cp)(_aliases.vecdot)
|
| 64 |
+
isdtype = get_xp(cp)(_aliases.isdtype)
|
| 65 |
+
|
| 66 |
+
__all__ = _aliases.__all__ + ['asarray', 'asarray_cupy', 'bool', 'acos',
|
| 67 |
+
'acosh', 'asin', 'asinh', 'atan', 'atan2',
|
| 68 |
+
'atanh', 'bitwise_left_shift', 'bitwise_invert',
|
| 69 |
+
'bitwise_right_shift', 'concat', 'pow']
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/_typing.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
"ndarray",
|
| 5 |
+
"Device",
|
| 6 |
+
"Dtype",
|
| 7 |
+
]
|
| 8 |
+
|
| 9 |
+
import sys
|
| 10 |
+
from typing import (
|
| 11 |
+
Union,
|
| 12 |
+
TYPE_CHECKING,
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
from cupy import (
|
| 16 |
+
ndarray,
|
| 17 |
+
dtype,
|
| 18 |
+
int8,
|
| 19 |
+
int16,
|
| 20 |
+
int32,
|
| 21 |
+
int64,
|
| 22 |
+
uint8,
|
| 23 |
+
uint16,
|
| 24 |
+
uint32,
|
| 25 |
+
uint64,
|
| 26 |
+
float32,
|
| 27 |
+
float64,
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
from cupy.cuda.device import Device
|
| 31 |
+
|
| 32 |
+
if TYPE_CHECKING or sys.version_info >= (3, 9):
|
| 33 |
+
Dtype = dtype[Union[
|
| 34 |
+
int8,
|
| 35 |
+
int16,
|
| 36 |
+
int32,
|
| 37 |
+
int64,
|
| 38 |
+
uint8,
|
| 39 |
+
uint16,
|
| 40 |
+
uint32,
|
| 41 |
+
uint64,
|
| 42 |
+
float32,
|
| 43 |
+
float64,
|
| 44 |
+
]]
|
| 45 |
+
else:
|
| 46 |
+
Dtype = dtype
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/linalg.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from cupy.linalg import *
|
| 2 |
+
# cupy.linalg doesn't have __all__. If it is added, replace this with
|
| 3 |
+
#
|
| 4 |
+
# from cupy.linalg import __all__ as linalg_all
|
| 5 |
+
_n = {}
|
| 6 |
+
exec('from cupy.linalg import *', _n)
|
| 7 |
+
del _n['__builtins__']
|
| 8 |
+
linalg_all = list(_n)
|
| 9 |
+
del _n
|
| 10 |
+
|
| 11 |
+
from ..common import _linalg
|
| 12 |
+
from .._internal import get_xp
|
| 13 |
+
from ._aliases import (matmul, matrix_transpose, tensordot, vecdot)
|
| 14 |
+
|
| 15 |
+
import cupy as cp
|
| 16 |
+
|
| 17 |
+
cross = get_xp(cp)(_linalg.cross)
|
| 18 |
+
outer = get_xp(cp)(_linalg.outer)
|
| 19 |
+
EighResult = _linalg.EighResult
|
| 20 |
+
QRResult = _linalg.QRResult
|
| 21 |
+
SlogdetResult = _linalg.SlogdetResult
|
| 22 |
+
SVDResult = _linalg.SVDResult
|
| 23 |
+
eigh = get_xp(cp)(_linalg.eigh)
|
| 24 |
+
qr = get_xp(cp)(_linalg.qr)
|
| 25 |
+
slogdet = get_xp(cp)(_linalg.slogdet)
|
| 26 |
+
svd = get_xp(cp)(_linalg.svd)
|
| 27 |
+
cholesky = get_xp(cp)(_linalg.cholesky)
|
| 28 |
+
matrix_rank = get_xp(cp)(_linalg.matrix_rank)
|
| 29 |
+
pinv = get_xp(cp)(_linalg.pinv)
|
| 30 |
+
matrix_norm = get_xp(cp)(_linalg.matrix_norm)
|
| 31 |
+
svdvals = get_xp(cp)(_linalg.svdvals)
|
| 32 |
+
vector_norm = get_xp(cp)(_linalg.vector_norm)
|
| 33 |
+
diagonal = get_xp(cp)(_linalg.diagonal)
|
| 34 |
+
trace = get_xp(cp)(_linalg.trace)
|
| 35 |
+
|
| 36 |
+
__all__ = linalg_all + _linalg.__all__
|
| 37 |
+
|
| 38 |
+
del get_xp
|
| 39 |
+
del cp
|
| 40 |
+
del linalg_all
|
| 41 |
+
del _linalg
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from numpy import *
|
| 2 |
+
|
| 3 |
+
# from numpy import * doesn't overwrite these builtin names
|
| 4 |
+
from numpy import abs, max, min, round
|
| 5 |
+
|
| 6 |
+
# These imports may overwrite names from the import * above.
|
| 7 |
+
from ._aliases import *
|
| 8 |
+
|
| 9 |
+
# Don't know why, but we have to do an absolute import to import linalg. If we
|
| 10 |
+
# instead do
|
| 11 |
+
#
|
| 12 |
+
# from . import linalg
|
| 13 |
+
#
|
| 14 |
+
# It doesn't overwrite np.linalg from above. The import is generated
|
| 15 |
+
# dynamically so that the library can be vendored.
|
| 16 |
+
__import__(__package__ + '.linalg')
|
| 17 |
+
|
| 18 |
+
from .linalg import matrix_transpose, vecdot
|
| 19 |
+
|
| 20 |
+
from ..common._helpers import *
|
| 21 |
+
|
| 22 |
+
__array_api_version__ = '2022.12'
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/_aliases.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from functools import partial
|
| 4 |
+
|
| 5 |
+
from ..common import _aliases
|
| 6 |
+
|
| 7 |
+
from .._internal import get_xp
|
| 8 |
+
|
| 9 |
+
asarray = asarray_numpy = partial(_aliases._asarray, namespace='numpy')
|
| 10 |
+
asarray.__doc__ = _aliases._asarray.__doc__
|
| 11 |
+
del partial
|
| 12 |
+
|
| 13 |
+
import numpy as np
|
| 14 |
+
bool = np.bool_
|
| 15 |
+
|
| 16 |
+
# Basic renames
|
| 17 |
+
acos = np.arccos
|
| 18 |
+
acosh = np.arccosh
|
| 19 |
+
asin = np.arcsin
|
| 20 |
+
asinh = np.arcsinh
|
| 21 |
+
atan = np.arctan
|
| 22 |
+
atan2 = np.arctan2
|
| 23 |
+
atanh = np.arctanh
|
| 24 |
+
bitwise_left_shift = np.left_shift
|
| 25 |
+
bitwise_invert = np.invert
|
| 26 |
+
bitwise_right_shift = np.right_shift
|
| 27 |
+
concat = np.concatenate
|
| 28 |
+
pow = np.power
|
| 29 |
+
|
| 30 |
+
arange = get_xp(np)(_aliases.arange)
|
| 31 |
+
empty = get_xp(np)(_aliases.empty)
|
| 32 |
+
empty_like = get_xp(np)(_aliases.empty_like)
|
| 33 |
+
eye = get_xp(np)(_aliases.eye)
|
| 34 |
+
full = get_xp(np)(_aliases.full)
|
| 35 |
+
full_like = get_xp(np)(_aliases.full_like)
|
| 36 |
+
linspace = get_xp(np)(_aliases.linspace)
|
| 37 |
+
ones = get_xp(np)(_aliases.ones)
|
| 38 |
+
ones_like = get_xp(np)(_aliases.ones_like)
|
| 39 |
+
zeros = get_xp(np)(_aliases.zeros)
|
| 40 |
+
zeros_like = get_xp(np)(_aliases.zeros_like)
|
| 41 |
+
UniqueAllResult = get_xp(np)(_aliases.UniqueAllResult)
|
| 42 |
+
UniqueCountsResult = get_xp(np)(_aliases.UniqueCountsResult)
|
| 43 |
+
UniqueInverseResult = get_xp(np)(_aliases.UniqueInverseResult)
|
| 44 |
+
unique_all = get_xp(np)(_aliases.unique_all)
|
| 45 |
+
unique_counts = get_xp(np)(_aliases.unique_counts)
|
| 46 |
+
unique_inverse = get_xp(np)(_aliases.unique_inverse)
|
| 47 |
+
unique_values = get_xp(np)(_aliases.unique_values)
|
| 48 |
+
astype = _aliases.astype
|
| 49 |
+
std = get_xp(np)(_aliases.std)
|
| 50 |
+
var = get_xp(np)(_aliases.var)
|
| 51 |
+
permute_dims = get_xp(np)(_aliases.permute_dims)
|
| 52 |
+
reshape = get_xp(np)(_aliases.reshape)
|
| 53 |
+
argsort = get_xp(np)(_aliases.argsort)
|
| 54 |
+
sort = get_xp(np)(_aliases.sort)
|
| 55 |
+
sum = get_xp(np)(_aliases.sum)
|
| 56 |
+
prod = get_xp(np)(_aliases.prod)
|
| 57 |
+
ceil = get_xp(np)(_aliases.ceil)
|
| 58 |
+
floor = get_xp(np)(_aliases.floor)
|
| 59 |
+
trunc = get_xp(np)(_aliases.trunc)
|
| 60 |
+
matmul = get_xp(np)(_aliases.matmul)
|
| 61 |
+
matrix_transpose = get_xp(np)(_aliases.matrix_transpose)
|
| 62 |
+
tensordot = get_xp(np)(_aliases.tensordot)
|
| 63 |
+
vecdot = get_xp(np)(_aliases.vecdot)
|
| 64 |
+
isdtype = get_xp(np)(_aliases.isdtype)
|
| 65 |
+
|
| 66 |
+
__all__ = _aliases.__all__ + ['asarray', 'asarray_numpy', 'bool', 'acos',
|
| 67 |
+
'acosh', 'asin', 'asinh', 'atan', 'atan2',
|
| 68 |
+
'atanh', 'bitwise_left_shift', 'bitwise_invert',
|
| 69 |
+
'bitwise_right_shift', 'concat', 'pow']
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/_typing.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
"ndarray",
|
| 5 |
+
"Device",
|
| 6 |
+
"Dtype",
|
| 7 |
+
]
|
| 8 |
+
|
| 9 |
+
import sys
|
| 10 |
+
from typing import (
|
| 11 |
+
Literal,
|
| 12 |
+
Union,
|
| 13 |
+
TYPE_CHECKING,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
from numpy import (
|
| 17 |
+
ndarray,
|
| 18 |
+
dtype,
|
| 19 |
+
int8,
|
| 20 |
+
int16,
|
| 21 |
+
int32,
|
| 22 |
+
int64,
|
| 23 |
+
uint8,
|
| 24 |
+
uint16,
|
| 25 |
+
uint32,
|
| 26 |
+
uint64,
|
| 27 |
+
float32,
|
| 28 |
+
float64,
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
Device = Literal["cpu"]
|
| 32 |
+
if TYPE_CHECKING or sys.version_info >= (3, 9):
|
| 33 |
+
Dtype = dtype[Union[
|
| 34 |
+
int8,
|
| 35 |
+
int16,
|
| 36 |
+
int32,
|
| 37 |
+
int64,
|
| 38 |
+
uint8,
|
| 39 |
+
uint16,
|
| 40 |
+
uint32,
|
| 41 |
+
uint64,
|
| 42 |
+
float32,
|
| 43 |
+
float64,
|
| 44 |
+
]]
|
| 45 |
+
else:
|
| 46 |
+
Dtype = dtype
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/linalg.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from numpy.linalg import *
|
| 2 |
+
from numpy.linalg import __all__ as linalg_all
|
| 3 |
+
|
| 4 |
+
from ..common import _linalg
|
| 5 |
+
from .._internal import get_xp
|
| 6 |
+
from ._aliases import (matmul, matrix_transpose, tensordot, vecdot)
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
cross = get_xp(np)(_linalg.cross)
|
| 11 |
+
outer = get_xp(np)(_linalg.outer)
|
| 12 |
+
EighResult = _linalg.EighResult
|
| 13 |
+
QRResult = _linalg.QRResult
|
| 14 |
+
SlogdetResult = _linalg.SlogdetResult
|
| 15 |
+
SVDResult = _linalg.SVDResult
|
| 16 |
+
eigh = get_xp(np)(_linalg.eigh)
|
| 17 |
+
qr = get_xp(np)(_linalg.qr)
|
| 18 |
+
slogdet = get_xp(np)(_linalg.slogdet)
|
| 19 |
+
svd = get_xp(np)(_linalg.svd)
|
| 20 |
+
cholesky = get_xp(np)(_linalg.cholesky)
|
| 21 |
+
matrix_rank = get_xp(np)(_linalg.matrix_rank)
|
| 22 |
+
pinv = get_xp(np)(_linalg.pinv)
|
| 23 |
+
matrix_norm = get_xp(np)(_linalg.matrix_norm)
|
| 24 |
+
svdvals = get_xp(np)(_linalg.svdvals)
|
| 25 |
+
vector_norm = get_xp(np)(_linalg.vector_norm)
|
| 26 |
+
diagonal = get_xp(np)(_linalg.diagonal)
|
| 27 |
+
trace = get_xp(np)(_linalg.trace)
|
| 28 |
+
|
| 29 |
+
__all__ = linalg_all + _linalg.__all__
|
| 30 |
+
|
| 31 |
+
del get_xp
|
| 32 |
+
del np
|
| 33 |
+
del linalg_all
|
| 34 |
+
del _linalg
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import *
|
| 2 |
+
|
| 3 |
+
# Several names are not included in the above import *
|
| 4 |
+
import torch
|
| 5 |
+
for n in dir(torch):
|
| 6 |
+
if (n.startswith('_')
|
| 7 |
+
or n.endswith('_')
|
| 8 |
+
or 'cuda' in n
|
| 9 |
+
or 'cpu' in n
|
| 10 |
+
or 'backward' in n):
|
| 11 |
+
continue
|
| 12 |
+
exec(n + ' = torch.' + n)
|
| 13 |
+
|
| 14 |
+
# These imports may overwrite names from the import * above.
|
| 15 |
+
from ._aliases import *
|
| 16 |
+
|
| 17 |
+
# See the comment in the numpy __init__.py
|
| 18 |
+
__import__(__package__ + '.linalg')
|
| 19 |
+
|
| 20 |
+
from ..common._helpers import *
|
| 21 |
+
|
| 22 |
+
__array_api_version__ = '2022.12'
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/_aliases.py
ADDED
|
@@ -0,0 +1,704 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from functools import wraps
|
| 4 |
+
from builtins import all as builtin_all, any as builtin_any
|
| 5 |
+
|
| 6 |
+
from ..common._aliases import (UniqueAllResult, UniqueCountsResult,
|
| 7 |
+
UniqueInverseResult,
|
| 8 |
+
matrix_transpose as _aliases_matrix_transpose,
|
| 9 |
+
vecdot as _aliases_vecdot)
|
| 10 |
+
from .._internal import get_xp
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
|
| 14 |
+
from typing import TYPE_CHECKING
|
| 15 |
+
if TYPE_CHECKING:
|
| 16 |
+
from typing import List, Optional, Sequence, Tuple, Union
|
| 17 |
+
from ..common._typing import Device
|
| 18 |
+
from torch import dtype as Dtype
|
| 19 |
+
|
| 20 |
+
array = torch.Tensor
|
| 21 |
+
|
| 22 |
+
_int_dtypes = {
|
| 23 |
+
torch.uint8,
|
| 24 |
+
torch.int8,
|
| 25 |
+
torch.int16,
|
| 26 |
+
torch.int32,
|
| 27 |
+
torch.int64,
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
_array_api_dtypes = {
|
| 31 |
+
torch.bool,
|
| 32 |
+
*_int_dtypes,
|
| 33 |
+
torch.float32,
|
| 34 |
+
torch.float64,
|
| 35 |
+
torch.complex64,
|
| 36 |
+
torch.complex128,
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
_promotion_table = {
|
| 40 |
+
# bool
|
| 41 |
+
(torch.bool, torch.bool): torch.bool,
|
| 42 |
+
# ints
|
| 43 |
+
(torch.int8, torch.int8): torch.int8,
|
| 44 |
+
(torch.int8, torch.int16): torch.int16,
|
| 45 |
+
(torch.int8, torch.int32): torch.int32,
|
| 46 |
+
(torch.int8, torch.int64): torch.int64,
|
| 47 |
+
(torch.int16, torch.int8): torch.int16,
|
| 48 |
+
(torch.int16, torch.int16): torch.int16,
|
| 49 |
+
(torch.int16, torch.int32): torch.int32,
|
| 50 |
+
(torch.int16, torch.int64): torch.int64,
|
| 51 |
+
(torch.int32, torch.int8): torch.int32,
|
| 52 |
+
(torch.int32, torch.int16): torch.int32,
|
| 53 |
+
(torch.int32, torch.int32): torch.int32,
|
| 54 |
+
(torch.int32, torch.int64): torch.int64,
|
| 55 |
+
(torch.int64, torch.int8): torch.int64,
|
| 56 |
+
(torch.int64, torch.int16): torch.int64,
|
| 57 |
+
(torch.int64, torch.int32): torch.int64,
|
| 58 |
+
(torch.int64, torch.int64): torch.int64,
|
| 59 |
+
# uints
|
| 60 |
+
(torch.uint8, torch.uint8): torch.uint8,
|
| 61 |
+
# ints and uints (mixed sign)
|
| 62 |
+
(torch.int8, torch.uint8): torch.int16,
|
| 63 |
+
(torch.int16, torch.uint8): torch.int16,
|
| 64 |
+
(torch.int32, torch.uint8): torch.int32,
|
| 65 |
+
(torch.int64, torch.uint8): torch.int64,
|
| 66 |
+
(torch.uint8, torch.int8): torch.int16,
|
| 67 |
+
(torch.uint8, torch.int16): torch.int16,
|
| 68 |
+
(torch.uint8, torch.int32): torch.int32,
|
| 69 |
+
(torch.uint8, torch.int64): torch.int64,
|
| 70 |
+
# floats
|
| 71 |
+
(torch.float32, torch.float32): torch.float32,
|
| 72 |
+
(torch.float32, torch.float64): torch.float64,
|
| 73 |
+
(torch.float64, torch.float32): torch.float64,
|
| 74 |
+
(torch.float64, torch.float64): torch.float64,
|
| 75 |
+
# complexes
|
| 76 |
+
(torch.complex64, torch.complex64): torch.complex64,
|
| 77 |
+
(torch.complex64, torch.complex128): torch.complex128,
|
| 78 |
+
(torch.complex128, torch.complex64): torch.complex128,
|
| 79 |
+
(torch.complex128, torch.complex128): torch.complex128,
|
| 80 |
+
# Mixed float and complex
|
| 81 |
+
(torch.float32, torch.complex64): torch.complex64,
|
| 82 |
+
(torch.float32, torch.complex128): torch.complex128,
|
| 83 |
+
(torch.float64, torch.complex64): torch.complex128,
|
| 84 |
+
(torch.float64, torch.complex128): torch.complex128,
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def _two_arg(f):
|
| 89 |
+
@wraps(f)
|
| 90 |
+
def _f(x1, x2, /, **kwargs):
|
| 91 |
+
x1, x2 = _fix_promotion(x1, x2)
|
| 92 |
+
return f(x1, x2, **kwargs)
|
| 93 |
+
if _f.__doc__ is None:
|
| 94 |
+
_f.__doc__ = f"""\
|
| 95 |
+
Array API compatibility wrapper for torch.{f.__name__}.
|
| 96 |
+
|
| 97 |
+
See the corresponding PyTorch documentation and/or the array API specification
|
| 98 |
+
for more details.
|
| 99 |
+
|
| 100 |
+
"""
|
| 101 |
+
return _f
|
| 102 |
+
|
| 103 |
+
def _fix_promotion(x1, x2, only_scalar=True):
|
| 104 |
+
if x1.dtype not in _array_api_dtypes or x2.dtype not in _array_api_dtypes:
|
| 105 |
+
return x1, x2
|
| 106 |
+
# If an argument is 0-D pytorch downcasts the other argument
|
| 107 |
+
if not only_scalar or x1.shape == ():
|
| 108 |
+
dtype = result_type(x1, x2)
|
| 109 |
+
x2 = x2.to(dtype)
|
| 110 |
+
if not only_scalar or x2.shape == ():
|
| 111 |
+
dtype = result_type(x1, x2)
|
| 112 |
+
x1 = x1.to(dtype)
|
| 113 |
+
return x1, x2
|
| 114 |
+
|
| 115 |
+
def result_type(*arrays_and_dtypes: Union[array, Dtype]) -> Dtype:
|
| 116 |
+
if len(arrays_and_dtypes) == 0:
|
| 117 |
+
raise TypeError("At least one array or dtype must be provided")
|
| 118 |
+
if len(arrays_and_dtypes) == 1:
|
| 119 |
+
x = arrays_and_dtypes[0]
|
| 120 |
+
if isinstance(x, torch.dtype):
|
| 121 |
+
return x
|
| 122 |
+
return x.dtype
|
| 123 |
+
if len(arrays_and_dtypes) > 2:
|
| 124 |
+
return result_type(arrays_and_dtypes[0], result_type(*arrays_and_dtypes[1:]))
|
| 125 |
+
|
| 126 |
+
x, y = arrays_and_dtypes
|
| 127 |
+
xdt = x.dtype if not isinstance(x, torch.dtype) else x
|
| 128 |
+
ydt = y.dtype if not isinstance(y, torch.dtype) else y
|
| 129 |
+
|
| 130 |
+
if (xdt, ydt) in _promotion_table:
|
| 131 |
+
return _promotion_table[xdt, ydt]
|
| 132 |
+
|
| 133 |
+
# This doesn't result_type(dtype, dtype) for non-array API dtypes
|
| 134 |
+
# because torch.result_type only accepts tensors. This does however, allow
|
| 135 |
+
# cross-kind promotion.
|
| 136 |
+
x = torch.tensor([], dtype=x) if isinstance(x, torch.dtype) else x
|
| 137 |
+
y = torch.tensor([], dtype=y) if isinstance(y, torch.dtype) else y
|
| 138 |
+
return torch.result_type(x, y)
|
| 139 |
+
|
| 140 |
+
def can_cast(from_: Union[Dtype, array], to: Dtype, /) -> bool:
|
| 141 |
+
if not isinstance(from_, torch.dtype):
|
| 142 |
+
from_ = from_.dtype
|
| 143 |
+
return torch.can_cast(from_, to)
|
| 144 |
+
|
| 145 |
+
# Basic renames
|
| 146 |
+
bitwise_invert = torch.bitwise_not
|
| 147 |
+
|
| 148 |
+
# Two-arg elementwise functions
|
| 149 |
+
# These require a wrapper to do the correct type promotion on 0-D tensors
|
| 150 |
+
add = _two_arg(torch.add)
|
| 151 |
+
atan2 = _two_arg(torch.atan2)
|
| 152 |
+
bitwise_and = _two_arg(torch.bitwise_and)
|
| 153 |
+
bitwise_left_shift = _two_arg(torch.bitwise_left_shift)
|
| 154 |
+
bitwise_or = _two_arg(torch.bitwise_or)
|
| 155 |
+
bitwise_right_shift = _two_arg(torch.bitwise_right_shift)
|
| 156 |
+
bitwise_xor = _two_arg(torch.bitwise_xor)
|
| 157 |
+
divide = _two_arg(torch.divide)
|
| 158 |
+
# Also a rename. torch.equal does not broadcast
|
| 159 |
+
equal = _two_arg(torch.eq)
|
| 160 |
+
floor_divide = _two_arg(torch.floor_divide)
|
| 161 |
+
greater = _two_arg(torch.greater)
|
| 162 |
+
greater_equal = _two_arg(torch.greater_equal)
|
| 163 |
+
less = _two_arg(torch.less)
|
| 164 |
+
less_equal = _two_arg(torch.less_equal)
|
| 165 |
+
logaddexp = _two_arg(torch.logaddexp)
|
| 166 |
+
# logical functions are not included here because they only accept bool in the
|
| 167 |
+
# spec, so type promotion is irrelevant.
|
| 168 |
+
multiply = _two_arg(torch.multiply)
|
| 169 |
+
not_equal = _two_arg(torch.not_equal)
|
| 170 |
+
pow = _two_arg(torch.pow)
|
| 171 |
+
remainder = _two_arg(torch.remainder)
|
| 172 |
+
subtract = _two_arg(torch.subtract)
|
| 173 |
+
|
| 174 |
+
# These wrappers are mostly based on the fact that pytorch uses 'dim' instead
|
| 175 |
+
# of 'axis'.
|
| 176 |
+
|
| 177 |
+
# torch.min and torch.max return a tuple and don't support multiple axes https://github.com/pytorch/pytorch/issues/58745
|
| 178 |
+
def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array:
|
| 179 |
+
# https://github.com/pytorch/pytorch/issues/29137
|
| 180 |
+
if axis == ():
|
| 181 |
+
return torch.clone(x)
|
| 182 |
+
return torch.amax(x, axis, keepdims=keepdims)
|
| 183 |
+
|
| 184 |
+
def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array:
|
| 185 |
+
# https://github.com/pytorch/pytorch/issues/29137
|
| 186 |
+
if axis == ():
|
| 187 |
+
return torch.clone(x)
|
| 188 |
+
return torch.amin(x, axis, keepdims=keepdims)
|
| 189 |
+
|
| 190 |
+
# torch.sort also returns a tuple
|
| 191 |
+
# https://github.com/pytorch/pytorch/issues/70921
|
| 192 |
+
def sort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True, **kwargs) -> array:
|
| 193 |
+
return torch.sort(x, dim=axis, descending=descending, stable=stable, **kwargs).values
|
| 194 |
+
|
| 195 |
+
def _normalize_axes(axis, ndim):
|
| 196 |
+
axes = []
|
| 197 |
+
if ndim == 0 and axis:
|
| 198 |
+
# Better error message in this case
|
| 199 |
+
raise IndexError(f"Dimension out of range: {axis[0]}")
|
| 200 |
+
lower, upper = -ndim, ndim - 1
|
| 201 |
+
for a in axis:
|
| 202 |
+
if a < lower or a > upper:
|
| 203 |
+
# Match torch error message (e.g., from sum())
|
| 204 |
+
raise IndexError(f"Dimension out of range (expected to be in range of [{lower}, {upper}], but got {a}")
|
| 205 |
+
if a < 0:
|
| 206 |
+
a = a + ndim
|
| 207 |
+
if a in axes:
|
| 208 |
+
# Use IndexError instead of RuntimeError, and "axis" instead of "dim"
|
| 209 |
+
raise IndexError(f"Axis {a} appears multiple times in the list of axes")
|
| 210 |
+
axes.append(a)
|
| 211 |
+
return sorted(axes)
|
| 212 |
+
|
| 213 |
+
def _axis_none_keepdims(x, ndim, keepdims):
|
| 214 |
+
# Apply keepdims when axis=None
|
| 215 |
+
# (https://github.com/pytorch/pytorch/issues/71209)
|
| 216 |
+
# Note that this is only valid for the axis=None case.
|
| 217 |
+
if keepdims:
|
| 218 |
+
for i in range(ndim):
|
| 219 |
+
x = torch.unsqueeze(x, 0)
|
| 220 |
+
return x
|
| 221 |
+
|
| 222 |
+
def _reduce_multiple_axes(f, x, axis, keepdims=False, **kwargs):
|
| 223 |
+
# Some reductions don't support multiple axes
|
| 224 |
+
# (https://github.com/pytorch/pytorch/issues/56586).
|
| 225 |
+
axes = _normalize_axes(axis, x.ndim)
|
| 226 |
+
for a in reversed(axes):
|
| 227 |
+
x = torch.movedim(x, a, -1)
|
| 228 |
+
x = torch.flatten(x, -len(axes))
|
| 229 |
+
|
| 230 |
+
out = f(x, -1, **kwargs)
|
| 231 |
+
|
| 232 |
+
if keepdims:
|
| 233 |
+
for a in axes:
|
| 234 |
+
out = torch.unsqueeze(out, a)
|
| 235 |
+
return out
|
| 236 |
+
|
| 237 |
+
def prod(x: array,
|
| 238 |
+
/,
|
| 239 |
+
*,
|
| 240 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 241 |
+
dtype: Optional[Dtype] = None,
|
| 242 |
+
keepdims: bool = False,
|
| 243 |
+
**kwargs) -> array:
|
| 244 |
+
x = torch.asarray(x)
|
| 245 |
+
ndim = x.ndim
|
| 246 |
+
|
| 247 |
+
# https://github.com/pytorch/pytorch/issues/29137. Separate from the logic
|
| 248 |
+
# below because it still needs to upcast.
|
| 249 |
+
if axis == ():
|
| 250 |
+
if dtype is None:
|
| 251 |
+
# We can't upcast uint8 according to the spec because there is no
|
| 252 |
+
# torch.uint64, so at least upcast to int64 which is what sum does
|
| 253 |
+
# when axis=None.
|
| 254 |
+
if x.dtype in [torch.int8, torch.int16, torch.int32, torch.uint8]:
|
| 255 |
+
return x.to(torch.int64)
|
| 256 |
+
return x.clone()
|
| 257 |
+
return x.to(dtype)
|
| 258 |
+
|
| 259 |
+
# torch.prod doesn't support multiple axes
|
| 260 |
+
# (https://github.com/pytorch/pytorch/issues/56586).
|
| 261 |
+
if isinstance(axis, tuple):
|
| 262 |
+
return _reduce_multiple_axes(torch.prod, x, axis, keepdims=keepdims, dtype=dtype, **kwargs)
|
| 263 |
+
if axis is None:
|
| 264 |
+
# torch doesn't support keepdims with axis=None
|
| 265 |
+
# (https://github.com/pytorch/pytorch/issues/71209)
|
| 266 |
+
res = torch.prod(x, dtype=dtype, **kwargs)
|
| 267 |
+
res = _axis_none_keepdims(res, ndim, keepdims)
|
| 268 |
+
return res
|
| 269 |
+
|
| 270 |
+
return torch.prod(x, axis, dtype=dtype, keepdims=keepdims, **kwargs)
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
def sum(x: array,
|
| 274 |
+
/,
|
| 275 |
+
*,
|
| 276 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 277 |
+
dtype: Optional[Dtype] = None,
|
| 278 |
+
keepdims: bool = False,
|
| 279 |
+
**kwargs) -> array:
|
| 280 |
+
x = torch.asarray(x)
|
| 281 |
+
ndim = x.ndim
|
| 282 |
+
|
| 283 |
+
# https://github.com/pytorch/pytorch/issues/29137.
|
| 284 |
+
# Make sure it upcasts.
|
| 285 |
+
if axis == ():
|
| 286 |
+
if dtype is None:
|
| 287 |
+
# We can't upcast uint8 according to the spec because there is no
|
| 288 |
+
# torch.uint64, so at least upcast to int64 which is what sum does
|
| 289 |
+
# when axis=None.
|
| 290 |
+
if x.dtype in [torch.int8, torch.int16, torch.int32, torch.uint8]:
|
| 291 |
+
return x.to(torch.int64)
|
| 292 |
+
return x.clone()
|
| 293 |
+
return x.to(dtype)
|
| 294 |
+
|
| 295 |
+
if axis is None:
|
| 296 |
+
# torch doesn't support keepdims with axis=None
|
| 297 |
+
# (https://github.com/pytorch/pytorch/issues/71209)
|
| 298 |
+
res = torch.sum(x, dtype=dtype, **kwargs)
|
| 299 |
+
res = _axis_none_keepdims(res, ndim, keepdims)
|
| 300 |
+
return res
|
| 301 |
+
|
| 302 |
+
return torch.sum(x, axis, dtype=dtype, keepdims=keepdims, **kwargs)
|
| 303 |
+
|
| 304 |
+
def any(x: array,
|
| 305 |
+
/,
|
| 306 |
+
*,
|
| 307 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 308 |
+
keepdims: bool = False,
|
| 309 |
+
**kwargs) -> array:
|
| 310 |
+
x = torch.asarray(x)
|
| 311 |
+
ndim = x.ndim
|
| 312 |
+
if axis == ():
|
| 313 |
+
return x.to(torch.bool)
|
| 314 |
+
# torch.any doesn't support multiple axes
|
| 315 |
+
# (https://github.com/pytorch/pytorch/issues/56586).
|
| 316 |
+
if isinstance(axis, tuple):
|
| 317 |
+
res = _reduce_multiple_axes(torch.any, x, axis, keepdims=keepdims, **kwargs)
|
| 318 |
+
return res.to(torch.bool)
|
| 319 |
+
if axis is None:
|
| 320 |
+
# torch doesn't support keepdims with axis=None
|
| 321 |
+
# (https://github.com/pytorch/pytorch/issues/71209)
|
| 322 |
+
res = torch.any(x, **kwargs)
|
| 323 |
+
res = _axis_none_keepdims(res, ndim, keepdims)
|
| 324 |
+
return res.to(torch.bool)
|
| 325 |
+
|
| 326 |
+
# torch.any doesn't return bool for uint8
|
| 327 |
+
return torch.any(x, axis, keepdims=keepdims).to(torch.bool)
|
| 328 |
+
|
| 329 |
+
def all(x: array,
|
| 330 |
+
/,
|
| 331 |
+
*,
|
| 332 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 333 |
+
keepdims: bool = False,
|
| 334 |
+
**kwargs) -> array:
|
| 335 |
+
x = torch.asarray(x)
|
| 336 |
+
ndim = x.ndim
|
| 337 |
+
if axis == ():
|
| 338 |
+
return x.to(torch.bool)
|
| 339 |
+
# torch.all doesn't support multiple axes
|
| 340 |
+
# (https://github.com/pytorch/pytorch/issues/56586).
|
| 341 |
+
if isinstance(axis, tuple):
|
| 342 |
+
res = _reduce_multiple_axes(torch.all, x, axis, keepdims=keepdims, **kwargs)
|
| 343 |
+
return res.to(torch.bool)
|
| 344 |
+
if axis is None:
|
| 345 |
+
# torch doesn't support keepdims with axis=None
|
| 346 |
+
# (https://github.com/pytorch/pytorch/issues/71209)
|
| 347 |
+
res = torch.all(x, **kwargs)
|
| 348 |
+
res = _axis_none_keepdims(res, ndim, keepdims)
|
| 349 |
+
return res.to(torch.bool)
|
| 350 |
+
|
| 351 |
+
# torch.all doesn't return bool for uint8
|
| 352 |
+
return torch.all(x, axis, keepdims=keepdims).to(torch.bool)
|
| 353 |
+
|
| 354 |
+
def mean(x: array,
|
| 355 |
+
/,
|
| 356 |
+
*,
|
| 357 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 358 |
+
keepdims: bool = False,
|
| 359 |
+
**kwargs) -> array:
|
| 360 |
+
# https://github.com/pytorch/pytorch/issues/29137
|
| 361 |
+
if axis == ():
|
| 362 |
+
return torch.clone(x)
|
| 363 |
+
if axis is None:
|
| 364 |
+
# torch doesn't support keepdims with axis=None
|
| 365 |
+
# (https://github.com/pytorch/pytorch/issues/71209)
|
| 366 |
+
res = torch.mean(x, **kwargs)
|
| 367 |
+
res = _axis_none_keepdims(res, x.ndim, keepdims)
|
| 368 |
+
return res
|
| 369 |
+
return torch.mean(x, axis, keepdims=keepdims, **kwargs)
|
| 370 |
+
|
| 371 |
+
def std(x: array,
|
| 372 |
+
/,
|
| 373 |
+
*,
|
| 374 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 375 |
+
correction: Union[int, float] = 0.0,
|
| 376 |
+
keepdims: bool = False,
|
| 377 |
+
**kwargs) -> array:
|
| 378 |
+
# Note, float correction is not supported
|
| 379 |
+
# https://github.com/pytorch/pytorch/issues/61492. We don't try to
|
| 380 |
+
# implement it here for now.
|
| 381 |
+
|
| 382 |
+
if isinstance(correction, float):
|
| 383 |
+
_correction = int(correction)
|
| 384 |
+
if correction != _correction:
|
| 385 |
+
raise NotImplementedError("float correction in torch std() is not yet supported")
|
| 386 |
+
|
| 387 |
+
# https://github.com/pytorch/pytorch/issues/29137
|
| 388 |
+
if axis == ():
|
| 389 |
+
return torch.zeros_like(x)
|
| 390 |
+
if isinstance(axis, int):
|
| 391 |
+
axis = (axis,)
|
| 392 |
+
if axis is None:
|
| 393 |
+
# torch doesn't support keepdims with axis=None
|
| 394 |
+
# (https://github.com/pytorch/pytorch/issues/71209)
|
| 395 |
+
res = torch.std(x, tuple(range(x.ndim)), correction=_correction, **kwargs)
|
| 396 |
+
res = _axis_none_keepdims(res, x.ndim, keepdims)
|
| 397 |
+
return res
|
| 398 |
+
return torch.std(x, axis, correction=_correction, keepdims=keepdims, **kwargs)
|
| 399 |
+
|
| 400 |
+
def var(x: array,
|
| 401 |
+
/,
|
| 402 |
+
*,
|
| 403 |
+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
|
| 404 |
+
correction: Union[int, float] = 0.0,
|
| 405 |
+
keepdims: bool = False,
|
| 406 |
+
**kwargs) -> array:
|
| 407 |
+
# Note, float correction is not supported
|
| 408 |
+
# https://github.com/pytorch/pytorch/issues/61492. We don't try to
|
| 409 |
+
# implement it here for now.
|
| 410 |
+
|
| 411 |
+
# if isinstance(correction, float):
|
| 412 |
+
# correction = int(correction)
|
| 413 |
+
|
| 414 |
+
# https://github.com/pytorch/pytorch/issues/29137
|
| 415 |
+
if axis == ():
|
| 416 |
+
return torch.zeros_like(x)
|
| 417 |
+
if isinstance(axis, int):
|
| 418 |
+
axis = (axis,)
|
| 419 |
+
if axis is None:
|
| 420 |
+
# torch doesn't support keepdims with axis=None
|
| 421 |
+
# (https://github.com/pytorch/pytorch/issues/71209)
|
| 422 |
+
res = torch.var(x, tuple(range(x.ndim)), correction=correction, **kwargs)
|
| 423 |
+
res = _axis_none_keepdims(res, x.ndim, keepdims)
|
| 424 |
+
return res
|
| 425 |
+
return torch.var(x, axis, correction=correction, keepdims=keepdims, **kwargs)
|
| 426 |
+
|
| 427 |
+
# torch.concat doesn't support dim=None
|
| 428 |
+
# https://github.com/pytorch/pytorch/issues/70925
|
| 429 |
+
def concat(arrays: Union[Tuple[array, ...], List[array]],
|
| 430 |
+
/,
|
| 431 |
+
*,
|
| 432 |
+
axis: Optional[int] = 0,
|
| 433 |
+
**kwargs) -> array:
|
| 434 |
+
if axis is None:
|
| 435 |
+
arrays = tuple(ar.flatten() for ar in arrays)
|
| 436 |
+
axis = 0
|
| 437 |
+
return torch.concat(arrays, axis, **kwargs)
|
| 438 |
+
|
| 439 |
+
# torch.squeeze only accepts int dim and doesn't require it
|
| 440 |
+
# https://github.com/pytorch/pytorch/issues/70924. Support for tuple dim was
|
| 441 |
+
# added at https://github.com/pytorch/pytorch/pull/89017.
|
| 442 |
+
def squeeze(x: array, /, axis: Union[int, Tuple[int, ...]]) -> array:
|
| 443 |
+
if isinstance(axis, int):
|
| 444 |
+
axis = (axis,)
|
| 445 |
+
for a in axis:
|
| 446 |
+
if x.shape[a] != 1:
|
| 447 |
+
raise ValueError("squeezed dimensions must be equal to 1")
|
| 448 |
+
axes = _normalize_axes(axis, x.ndim)
|
| 449 |
+
# Remove this once pytorch 1.14 is released with the above PR #89017.
|
| 450 |
+
sequence = [a - i for i, a in enumerate(axes)]
|
| 451 |
+
for a in sequence:
|
| 452 |
+
x = torch.squeeze(x, a)
|
| 453 |
+
return x
|
| 454 |
+
|
| 455 |
+
# torch.broadcast_to uses size instead of shape
|
| 456 |
+
def broadcast_to(x: array, /, shape: Tuple[int, ...], **kwargs) -> array:
|
| 457 |
+
return torch.broadcast_to(x, shape, **kwargs)
|
| 458 |
+
|
| 459 |
+
# torch.permute uses dims instead of axes
|
| 460 |
+
def permute_dims(x: array, /, axes: Tuple[int, ...]) -> array:
|
| 461 |
+
return torch.permute(x, axes)
|
| 462 |
+
|
| 463 |
+
# The axis parameter doesn't work for flip() and roll()
|
| 464 |
+
# https://github.com/pytorch/pytorch/issues/71210. Also torch.flip() doesn't
|
| 465 |
+
# accept axis=None
|
| 466 |
+
def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, **kwargs) -> array:
|
| 467 |
+
if axis is None:
|
| 468 |
+
axis = tuple(range(x.ndim))
|
| 469 |
+
# torch.flip doesn't accept dim as an int but the method does
|
| 470 |
+
# https://github.com/pytorch/pytorch/issues/18095
|
| 471 |
+
return x.flip(axis, **kwargs)
|
| 472 |
+
|
| 473 |
+
def roll(x: array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None, **kwargs) -> array:
|
| 474 |
+
return torch.roll(x, shift, axis, **kwargs)
|
| 475 |
+
|
| 476 |
+
def nonzero(x: array, /, **kwargs) -> Tuple[array, ...]:
|
| 477 |
+
return torch.nonzero(x, as_tuple=True, **kwargs)
|
| 478 |
+
|
| 479 |
+
def where(condition: array, x1: array, x2: array, /) -> array:
|
| 480 |
+
x1, x2 = _fix_promotion(x1, x2)
|
| 481 |
+
return torch.where(condition, x1, x2)
|
| 482 |
+
|
| 483 |
+
# torch.reshape doesn't have the copy keyword
|
| 484 |
+
def reshape(x: array,
|
| 485 |
+
/,
|
| 486 |
+
shape: Tuple[int, ...],
|
| 487 |
+
copy: Optional[bool] = None,
|
| 488 |
+
**kwargs) -> array:
|
| 489 |
+
if copy is not None:
|
| 490 |
+
raise NotImplementedError("torch.reshape doesn't yet support the copy keyword")
|
| 491 |
+
return torch.reshape(x, shape, **kwargs)
|
| 492 |
+
|
| 493 |
+
# torch.arange doesn't support returning empty arrays
|
| 494 |
+
# (https://github.com/pytorch/pytorch/issues/70915), and doesn't support some
|
| 495 |
+
# keyword argument combinations
|
| 496 |
+
# (https://github.com/pytorch/pytorch/issues/70914)
|
| 497 |
+
def arange(start: Union[int, float],
|
| 498 |
+
/,
|
| 499 |
+
stop: Optional[Union[int, float]] = None,
|
| 500 |
+
step: Union[int, float] = 1,
|
| 501 |
+
*,
|
| 502 |
+
dtype: Optional[Dtype] = None,
|
| 503 |
+
device: Optional[Device] = None,
|
| 504 |
+
**kwargs) -> array:
|
| 505 |
+
if stop is None:
|
| 506 |
+
start, stop = 0, start
|
| 507 |
+
if step > 0 and stop <= start or step < 0 and stop >= start:
|
| 508 |
+
if dtype is None:
|
| 509 |
+
if builtin_all(isinstance(i, int) for i in [start, stop, step]):
|
| 510 |
+
dtype = torch.int64
|
| 511 |
+
else:
|
| 512 |
+
dtype = torch.float32
|
| 513 |
+
return torch.empty(0, dtype=dtype, device=device, **kwargs)
|
| 514 |
+
return torch.arange(start, stop, step, dtype=dtype, device=device, **kwargs)
|
| 515 |
+
|
| 516 |
+
# torch.eye does not accept None as a default for the second argument and
|
| 517 |
+
# doesn't support off-diagonals (https://github.com/pytorch/pytorch/issues/70910)
|
| 518 |
+
def eye(n_rows: int,
|
| 519 |
+
n_cols: Optional[int] = None,
|
| 520 |
+
/,
|
| 521 |
+
*,
|
| 522 |
+
k: int = 0,
|
| 523 |
+
dtype: Optional[Dtype] = None,
|
| 524 |
+
device: Optional[Device] = None,
|
| 525 |
+
**kwargs) -> array:
|
| 526 |
+
if n_cols is None:
|
| 527 |
+
n_cols = n_rows
|
| 528 |
+
z = torch.zeros(n_rows, n_cols, dtype=dtype, device=device, **kwargs)
|
| 529 |
+
if abs(k) <= n_rows + n_cols:
|
| 530 |
+
z.diagonal(k).fill_(1)
|
| 531 |
+
return z
|
| 532 |
+
|
| 533 |
+
# torch.linspace doesn't have the endpoint parameter
|
| 534 |
+
def linspace(start: Union[int, float],
|
| 535 |
+
stop: Union[int, float],
|
| 536 |
+
/,
|
| 537 |
+
num: int,
|
| 538 |
+
*,
|
| 539 |
+
dtype: Optional[Dtype] = None,
|
| 540 |
+
device: Optional[Device] = None,
|
| 541 |
+
endpoint: bool = True,
|
| 542 |
+
**kwargs) -> array:
|
| 543 |
+
if not endpoint:
|
| 544 |
+
return torch.linspace(start, stop, num+1, dtype=dtype, device=device, **kwargs)[:-1]
|
| 545 |
+
return torch.linspace(start, stop, num, dtype=dtype, device=device, **kwargs)
|
| 546 |
+
|
| 547 |
+
# torch.full does not accept an int size
|
| 548 |
+
# https://github.com/pytorch/pytorch/issues/70906
|
| 549 |
+
def full(shape: Union[int, Tuple[int, ...]],
|
| 550 |
+
fill_value: Union[bool, int, float, complex],
|
| 551 |
+
*,
|
| 552 |
+
dtype: Optional[Dtype] = None,
|
| 553 |
+
device: Optional[Device] = None,
|
| 554 |
+
**kwargs) -> array:
|
| 555 |
+
if isinstance(shape, int):
|
| 556 |
+
shape = (shape,)
|
| 557 |
+
|
| 558 |
+
return torch.full(shape, fill_value, dtype=dtype, device=device, **kwargs)
|
| 559 |
+
|
| 560 |
+
# ones, zeros, and empty do not accept shape as a keyword argument
|
| 561 |
+
def ones(shape: Union[int, Tuple[int, ...]],
|
| 562 |
+
*,
|
| 563 |
+
dtype: Optional[Dtype] = None,
|
| 564 |
+
device: Optional[Device] = None,
|
| 565 |
+
**kwargs) -> array:
|
| 566 |
+
return torch.ones(shape, dtype=dtype, device=device, **kwargs)
|
| 567 |
+
|
| 568 |
+
def zeros(shape: Union[int, Tuple[int, ...]],
|
| 569 |
+
*,
|
| 570 |
+
dtype: Optional[Dtype] = None,
|
| 571 |
+
device: Optional[Device] = None,
|
| 572 |
+
**kwargs) -> array:
|
| 573 |
+
return torch.zeros(shape, dtype=dtype, device=device, **kwargs)
|
| 574 |
+
|
| 575 |
+
def empty(shape: Union[int, Tuple[int, ...]],
|
| 576 |
+
*,
|
| 577 |
+
dtype: Optional[Dtype] = None,
|
| 578 |
+
device: Optional[Device] = None,
|
| 579 |
+
**kwargs) -> array:
|
| 580 |
+
return torch.empty(shape, dtype=dtype, device=device, **kwargs)
|
| 581 |
+
|
| 582 |
+
# tril and triu do not call the keyword argument k
|
| 583 |
+
|
| 584 |
+
def tril(x: array, /, *, k: int = 0) -> array:
|
| 585 |
+
return torch.tril(x, k)
|
| 586 |
+
|
| 587 |
+
def triu(x: array, /, *, k: int = 0) -> array:
|
| 588 |
+
return torch.triu(x, k)
|
| 589 |
+
|
| 590 |
+
# Functions that aren't in torch https://github.com/pytorch/pytorch/issues/58742
|
| 591 |
+
def expand_dims(x: array, /, *, axis: int = 0) -> array:
|
| 592 |
+
return torch.unsqueeze(x, axis)
|
| 593 |
+
|
| 594 |
+
def astype(x: array, dtype: Dtype, /, *, copy: bool = True) -> array:
|
| 595 |
+
return x.to(dtype, copy=copy)
|
| 596 |
+
|
| 597 |
+
def broadcast_arrays(*arrays: array) -> List[array]:
|
| 598 |
+
shape = torch.broadcast_shapes(*[a.shape for a in arrays])
|
| 599 |
+
return [torch.broadcast_to(a, shape) for a in arrays]
|
| 600 |
+
|
| 601 |
+
# https://github.com/pytorch/pytorch/issues/70920
|
| 602 |
+
def unique_all(x: array) -> UniqueAllResult:
|
| 603 |
+
# torch.unique doesn't support returning indices.
|
| 604 |
+
# https://github.com/pytorch/pytorch/issues/36748. The workaround
|
| 605 |
+
# suggested in that issue doesn't actually function correctly (it relies
|
| 606 |
+
# on non-deterministic behavior of scatter()).
|
| 607 |
+
raise NotImplementedError("unique_all() not yet implemented for pytorch (see https://github.com/pytorch/pytorch/issues/36748)")
|
| 608 |
+
|
| 609 |
+
# values, inverse_indices, counts = torch.unique(x, return_counts=True, return_inverse=True)
|
| 610 |
+
# # torch.unique incorrectly gives a 0 count for nan values.
|
| 611 |
+
# # https://github.com/pytorch/pytorch/issues/94106
|
| 612 |
+
# counts[torch.isnan(values)] = 1
|
| 613 |
+
# return UniqueAllResult(values, indices, inverse_indices, counts)
|
| 614 |
+
|
| 615 |
+
def unique_counts(x: array) -> UniqueCountsResult:
|
| 616 |
+
values, counts = torch.unique(x, return_counts=True)
|
| 617 |
+
|
| 618 |
+
# torch.unique incorrectly gives a 0 count for nan values.
|
| 619 |
+
# https://github.com/pytorch/pytorch/issues/94106
|
| 620 |
+
counts[torch.isnan(values)] = 1
|
| 621 |
+
return UniqueCountsResult(values, counts)
|
| 622 |
+
|
| 623 |
+
def unique_inverse(x: array) -> UniqueInverseResult:
|
| 624 |
+
values, inverse = torch.unique(x, return_inverse=True)
|
| 625 |
+
return UniqueInverseResult(values, inverse)
|
| 626 |
+
|
| 627 |
+
def unique_values(x: array) -> array:
|
| 628 |
+
return torch.unique(x)
|
| 629 |
+
|
| 630 |
+
def matmul(x1: array, x2: array, /, **kwargs) -> array:
|
| 631 |
+
# torch.matmul doesn't type promote (but differently from _fix_promotion)
|
| 632 |
+
x1, x2 = _fix_promotion(x1, x2, only_scalar=False)
|
| 633 |
+
return torch.matmul(x1, x2, **kwargs)
|
| 634 |
+
|
| 635 |
+
matrix_transpose = get_xp(torch)(_aliases_matrix_transpose)
|
| 636 |
+
_vecdot = get_xp(torch)(_aliases_vecdot)
|
| 637 |
+
|
| 638 |
+
def vecdot(x1: array, x2: array, /, *, axis: int = -1) -> array:
|
| 639 |
+
x1, x2 = _fix_promotion(x1, x2, only_scalar=False)
|
| 640 |
+
return _vecdot(x1, x2, axis=axis)
|
| 641 |
+
|
| 642 |
+
# torch.tensordot uses dims instead of axes
|
| 643 |
+
def tensordot(x1: array, x2: array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2, **kwargs) -> array:
|
| 644 |
+
# Note: torch.tensordot fails with integer dtypes when there is only 1
|
| 645 |
+
# element in the axis (https://github.com/pytorch/pytorch/issues/84530).
|
| 646 |
+
x1, x2 = _fix_promotion(x1, x2, only_scalar=False)
|
| 647 |
+
return torch.tensordot(x1, x2, dims=axes, **kwargs)
|
| 648 |
+
|
| 649 |
+
|
| 650 |
+
def isdtype(
|
| 651 |
+
dtype: Dtype, kind: Union[Dtype, str, Tuple[Union[Dtype, str], ...]],
|
| 652 |
+
*, _tuple=True, # Disallow nested tuples
|
| 653 |
+
) -> bool:
|
| 654 |
+
"""
|
| 655 |
+
Returns a boolean indicating whether a provided dtype is of a specified data type ``kind``.
|
| 656 |
+
|
| 657 |
+
Note that outside of this function, this compat library does not yet fully
|
| 658 |
+
support complex numbers.
|
| 659 |
+
|
| 660 |
+
See
|
| 661 |
+
https://data-apis.org/array-api/latest/API_specification/generated/array_api.isdtype.html
|
| 662 |
+
for more details
|
| 663 |
+
"""
|
| 664 |
+
if isinstance(kind, tuple) and _tuple:
|
| 665 |
+
return builtin_any(isdtype(dtype, k, _tuple=False) for k in kind)
|
| 666 |
+
elif isinstance(kind, str):
|
| 667 |
+
if kind == 'bool':
|
| 668 |
+
return dtype == torch.bool
|
| 669 |
+
elif kind == 'signed integer':
|
| 670 |
+
return dtype in _int_dtypes and dtype.is_signed
|
| 671 |
+
elif kind == 'unsigned integer':
|
| 672 |
+
return dtype in _int_dtypes and not dtype.is_signed
|
| 673 |
+
elif kind == 'integral':
|
| 674 |
+
return dtype in _int_dtypes
|
| 675 |
+
elif kind == 'real floating':
|
| 676 |
+
return dtype.is_floating_point
|
| 677 |
+
elif kind == 'complex floating':
|
| 678 |
+
return dtype.is_complex
|
| 679 |
+
elif kind == 'numeric':
|
| 680 |
+
return isdtype(dtype, ('integral', 'real floating', 'complex floating'))
|
| 681 |
+
else:
|
| 682 |
+
raise ValueError(f"Unrecognized data type kind: {kind!r}")
|
| 683 |
+
else:
|
| 684 |
+
return dtype == kind
|
| 685 |
+
|
| 686 |
+
def take(x: array, indices: array, /, *, axis: Optional[int] = None, **kwargs) -> array:
|
| 687 |
+
if axis is None:
|
| 688 |
+
if x.ndim != 1:
|
| 689 |
+
raise ValueError("axis must be specified when ndim > 1")
|
| 690 |
+
axis = 0
|
| 691 |
+
return torch.index_select(x, axis, indices, **kwargs)
|
| 692 |
+
|
| 693 |
+
__all__ = ['result_type', 'can_cast', 'permute_dims', 'bitwise_invert', 'add',
|
| 694 |
+
'atan2', 'bitwise_and', 'bitwise_left_shift', 'bitwise_or',
|
| 695 |
+
'bitwise_right_shift', 'bitwise_xor', 'divide', 'equal',
|
| 696 |
+
'floor_divide', 'greater', 'greater_equal', 'less', 'less_equal',
|
| 697 |
+
'logaddexp', 'multiply', 'not_equal', 'pow', 'remainder',
|
| 698 |
+
'subtract', 'max', 'min', 'sort', 'prod', 'sum', 'any', 'all',
|
| 699 |
+
'mean', 'std', 'var', 'concat', 'squeeze', 'broadcast_to', 'flip', 'roll',
|
| 700 |
+
'nonzero', 'where', 'reshape', 'arange', 'eye', 'linspace', 'full',
|
| 701 |
+
'ones', 'zeros', 'empty', 'tril', 'triu', 'expand_dims', 'astype',
|
| 702 |
+
'broadcast_arrays', 'unique_all', 'unique_counts',
|
| 703 |
+
'unique_inverse', 'unique_values', 'matmul', 'matrix_transpose',
|
| 704 |
+
'vecdot', 'tensordot', 'isdtype', 'take']
|
rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/linalg.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import TYPE_CHECKING
|
| 4 |
+
if TYPE_CHECKING:
|
| 5 |
+
import torch
|
| 6 |
+
array = torch.Tensor
|
| 7 |
+
|
| 8 |
+
from torch.linalg import *
|
| 9 |
+
|
| 10 |
+
# torch.linalg doesn't define __all__
|
| 11 |
+
# from torch.linalg import __all__ as linalg_all
|
| 12 |
+
from torch import linalg as torch_linalg
|
| 13 |
+
linalg_all = [i for i in dir(torch_linalg) if not i.startswith('_')]
|
| 14 |
+
|
| 15 |
+
# These are implemented in torch but aren't in the linalg namespace
|
| 16 |
+
from torch import outer, trace
|
| 17 |
+
from ._aliases import _fix_promotion, matrix_transpose, tensordot
|
| 18 |
+
|
| 19 |
+
# Note: torch.linalg.cross does not default to axis=-1 (it defaults to the
|
| 20 |
+
# first axis with size 3), see https://github.com/pytorch/pytorch/issues/58743
|
| 21 |
+
def cross(x1: array, x2: array, /, *, axis: int = -1) -> array:
|
| 22 |
+
x1, x2 = _fix_promotion(x1, x2, only_scalar=False)
|
| 23 |
+
return torch_linalg.cross(x1, x2, dim=axis)
|
| 24 |
+
|
| 25 |
+
def vecdot(x1: array, x2: array, /, *, axis: int = -1, **kwargs) -> array:
|
| 26 |
+
from ._aliases import isdtype
|
| 27 |
+
|
| 28 |
+
x1, x2 = _fix_promotion(x1, x2, only_scalar=False)
|
| 29 |
+
|
| 30 |
+
# torch.linalg.vecdot doesn't support integer dtypes
|
| 31 |
+
if isdtype(x1.dtype, 'integral') or isdtype(x2.dtype, 'integral'):
|
| 32 |
+
if kwargs:
|
| 33 |
+
raise RuntimeError("vecdot kwargs not supported for integral dtypes")
|
| 34 |
+
ndim = max(x1.ndim, x2.ndim)
|
| 35 |
+
x1_shape = (1,)*(ndim - x1.ndim) + tuple(x1.shape)
|
| 36 |
+
x2_shape = (1,)*(ndim - x2.ndim) + tuple(x2.shape)
|
| 37 |
+
if x1_shape[axis] != x2_shape[axis]:
|
| 38 |
+
raise ValueError("x1 and x2 must have the same size along the given axis")
|
| 39 |
+
|
| 40 |
+
x1_, x2_ = torch.broadcast_tensors(x1, x2)
|
| 41 |
+
x1_ = torch.moveaxis(x1_, axis, -1)
|
| 42 |
+
x2_ = torch.moveaxis(x2_, axis, -1)
|
| 43 |
+
|
| 44 |
+
res = x1_[..., None, :] @ x2_[..., None]
|
| 45 |
+
return res[..., 0, 0]
|
| 46 |
+
return torch.linalg.vecdot(x1, x2, dim=axis, **kwargs)
|
| 47 |
+
|
| 48 |
+
def solve(x1: array, x2: array, /, **kwargs) -> array:
|
| 49 |
+
x1, x2 = _fix_promotion(x1, x2, only_scalar=False)
|
| 50 |
+
return torch.linalg.solve(x1, x2, **kwargs)
|
| 51 |
+
|
| 52 |
+
__all__ = linalg_all + ['outer', 'trace', 'matrix_transpose', 'tensordot',
|
| 53 |
+
'vecdot', 'solve']
|
| 54 |
+
|
| 55 |
+
del linalg_all
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/__init__.py
ADDED
|
File without changes
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__gcutils.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Test for assert_deallocated context manager and gc utilities
|
| 2 |
+
"""
|
| 3 |
+
import gc
|
| 4 |
+
|
| 5 |
+
from scipy._lib._gcutils import (set_gc_state, gc_state, assert_deallocated,
|
| 6 |
+
ReferenceError, IS_PYPY)
|
| 7 |
+
|
| 8 |
+
from numpy.testing import assert_equal
|
| 9 |
+
|
| 10 |
+
import pytest
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def test_set_gc_state():
|
| 14 |
+
gc_status = gc.isenabled()
|
| 15 |
+
try:
|
| 16 |
+
for state in (True, False):
|
| 17 |
+
gc.enable()
|
| 18 |
+
set_gc_state(state)
|
| 19 |
+
assert_equal(gc.isenabled(), state)
|
| 20 |
+
gc.disable()
|
| 21 |
+
set_gc_state(state)
|
| 22 |
+
assert_equal(gc.isenabled(), state)
|
| 23 |
+
finally:
|
| 24 |
+
if gc_status:
|
| 25 |
+
gc.enable()
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def test_gc_state():
|
| 29 |
+
# Test gc_state context manager
|
| 30 |
+
gc_status = gc.isenabled()
|
| 31 |
+
try:
|
| 32 |
+
for pre_state in (True, False):
|
| 33 |
+
set_gc_state(pre_state)
|
| 34 |
+
for with_state in (True, False):
|
| 35 |
+
# Check the gc state is with_state in with block
|
| 36 |
+
with gc_state(with_state):
|
| 37 |
+
assert_equal(gc.isenabled(), with_state)
|
| 38 |
+
# And returns to previous state outside block
|
| 39 |
+
assert_equal(gc.isenabled(), pre_state)
|
| 40 |
+
# Even if the gc state is set explicitly within the block
|
| 41 |
+
with gc_state(with_state):
|
| 42 |
+
assert_equal(gc.isenabled(), with_state)
|
| 43 |
+
set_gc_state(not with_state)
|
| 44 |
+
assert_equal(gc.isenabled(), pre_state)
|
| 45 |
+
finally:
|
| 46 |
+
if gc_status:
|
| 47 |
+
gc.enable()
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy")
|
| 51 |
+
def test_assert_deallocated():
|
| 52 |
+
# Ordinary use
|
| 53 |
+
class C:
|
| 54 |
+
def __init__(self, arg0, arg1, name='myname'):
|
| 55 |
+
self.name = name
|
| 56 |
+
for gc_current in (True, False):
|
| 57 |
+
with gc_state(gc_current):
|
| 58 |
+
# We are deleting from with-block context, so that's OK
|
| 59 |
+
with assert_deallocated(C, 0, 2, 'another name') as c:
|
| 60 |
+
assert_equal(c.name, 'another name')
|
| 61 |
+
del c
|
| 62 |
+
# Or not using the thing in with-block context, also OK
|
| 63 |
+
with assert_deallocated(C, 0, 2, name='third name'):
|
| 64 |
+
pass
|
| 65 |
+
assert_equal(gc.isenabled(), gc_current)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy")
|
| 69 |
+
def test_assert_deallocated_nodel():
|
| 70 |
+
class C:
|
| 71 |
+
pass
|
| 72 |
+
with pytest.raises(ReferenceError):
|
| 73 |
+
# Need to delete after using if in with-block context
|
| 74 |
+
# Note: assert_deallocated(C) needs to be assigned for the test
|
| 75 |
+
# to function correctly. It is assigned to _, but _ itself is
|
| 76 |
+
# not referenced in the body of the with, it is only there for
|
| 77 |
+
# the refcount.
|
| 78 |
+
with assert_deallocated(C) as _:
|
| 79 |
+
pass
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy")
|
| 83 |
+
def test_assert_deallocated_circular():
|
| 84 |
+
class C:
|
| 85 |
+
def __init__(self):
|
| 86 |
+
self._circular = self
|
| 87 |
+
with pytest.raises(ReferenceError):
|
| 88 |
+
# Circular reference, no automatic garbage collection
|
| 89 |
+
with assert_deallocated(C) as c:
|
| 90 |
+
del c
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy")
|
| 94 |
+
def test_assert_deallocated_circular2():
|
| 95 |
+
class C:
|
| 96 |
+
def __init__(self):
|
| 97 |
+
self._circular = self
|
| 98 |
+
with pytest.raises(ReferenceError):
|
| 99 |
+
# Still circular reference, no automatic garbage collection
|
| 100 |
+
with assert_deallocated(C):
|
| 101 |
+
pass
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__pep440.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pytest import raises as assert_raises
|
| 2 |
+
from scipy._lib._pep440 import Version, parse
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def test_main_versions():
|
| 6 |
+
assert Version('1.8.0') == Version('1.8.0')
|
| 7 |
+
for ver in ['1.9.0', '2.0.0', '1.8.1']:
|
| 8 |
+
assert Version('1.8.0') < Version(ver)
|
| 9 |
+
|
| 10 |
+
for ver in ['1.7.0', '1.7.1', '0.9.9']:
|
| 11 |
+
assert Version('1.8.0') > Version(ver)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_version_1_point_10():
|
| 15 |
+
# regression test for gh-2998.
|
| 16 |
+
assert Version('1.9.0') < Version('1.10.0')
|
| 17 |
+
assert Version('1.11.0') < Version('1.11.1')
|
| 18 |
+
assert Version('1.11.0') == Version('1.11.0')
|
| 19 |
+
assert Version('1.99.11') < Version('1.99.12')
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def test_alpha_beta_rc():
|
| 23 |
+
assert Version('1.8.0rc1') == Version('1.8.0rc1')
|
| 24 |
+
for ver in ['1.8.0', '1.8.0rc2']:
|
| 25 |
+
assert Version('1.8.0rc1') < Version(ver)
|
| 26 |
+
|
| 27 |
+
for ver in ['1.8.0a2', '1.8.0b3', '1.7.2rc4']:
|
| 28 |
+
assert Version('1.8.0rc1') > Version(ver)
|
| 29 |
+
|
| 30 |
+
assert Version('1.8.0b1') > Version('1.8.0a2')
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def test_dev_version():
|
| 34 |
+
assert Version('1.9.0.dev+Unknown') < Version('1.9.0')
|
| 35 |
+
for ver in ['1.9.0', '1.9.0a1', '1.9.0b2', '1.9.0b2.dev+ffffffff', '1.9.0.dev1']:
|
| 36 |
+
assert Version('1.9.0.dev+f16acvda') < Version(ver)
|
| 37 |
+
|
| 38 |
+
assert Version('1.9.0.dev+f16acvda') == Version('1.9.0.dev+f16acvda')
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def test_dev_a_b_rc_mixed():
|
| 42 |
+
assert Version('1.9.0a2.dev+f16acvda') == Version('1.9.0a2.dev+f16acvda')
|
| 43 |
+
assert Version('1.9.0a2.dev+6acvda54') < Version('1.9.0a2')
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def test_dev0_version():
|
| 47 |
+
assert Version('1.9.0.dev0+Unknown') < Version('1.9.0')
|
| 48 |
+
for ver in ['1.9.0', '1.9.0a1', '1.9.0b2', '1.9.0b2.dev0+ffffffff']:
|
| 49 |
+
assert Version('1.9.0.dev0+f16acvda') < Version(ver)
|
| 50 |
+
|
| 51 |
+
assert Version('1.9.0.dev0+f16acvda') == Version('1.9.0.dev0+f16acvda')
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def test_dev0_a_b_rc_mixed():
|
| 55 |
+
assert Version('1.9.0a2.dev0+f16acvda') == Version('1.9.0a2.dev0+f16acvda')
|
| 56 |
+
assert Version('1.9.0a2.dev0+6acvda54') < Version('1.9.0a2')
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def test_raises():
|
| 60 |
+
for ver in ['1,9.0', '1.7.x']:
|
| 61 |
+
assert_raises(ValueError, Version, ver)
|
| 62 |
+
|
| 63 |
+
def test_legacy_version():
|
| 64 |
+
# Non-PEP-440 version identifiers always compare less. For NumPy this only
|
| 65 |
+
# occurs on dev builds prior to 1.10.0 which are unsupported anyway.
|
| 66 |
+
assert parse('invalid') < Version('0.0.0')
|
| 67 |
+
assert parse('1.9.0-f16acvda') < Version('1.0.0')
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__testutils.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from scipy._lib._testutils import _parse_size, _get_mem_available
|
| 3 |
+
import pytest
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test__parse_size():
|
| 7 |
+
expected = {
|
| 8 |
+
'12': 12e6,
|
| 9 |
+
'12 b': 12,
|
| 10 |
+
'12k': 12e3,
|
| 11 |
+
' 12 M ': 12e6,
|
| 12 |
+
' 12 G ': 12e9,
|
| 13 |
+
' 12Tb ': 12e12,
|
| 14 |
+
'12 Mib ': 12 * 1024.0**2,
|
| 15 |
+
'12Tib': 12 * 1024.0**4,
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
for inp, outp in sorted(expected.items()):
|
| 19 |
+
if outp is None:
|
| 20 |
+
with pytest.raises(ValueError):
|
| 21 |
+
_parse_size(inp)
|
| 22 |
+
else:
|
| 23 |
+
assert _parse_size(inp) == outp
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def test__mem_available():
|
| 27 |
+
# May return None on non-Linux platforms
|
| 28 |
+
available = _get_mem_available()
|
| 29 |
+
if sys.platform.startswith('linux'):
|
| 30 |
+
assert available >= 0
|
| 31 |
+
else:
|
| 32 |
+
assert available is None or available >= 0
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__threadsafety.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import threading
|
| 2 |
+
import time
|
| 3 |
+
import traceback
|
| 4 |
+
|
| 5 |
+
from numpy.testing import assert_
|
| 6 |
+
from pytest import raises as assert_raises
|
| 7 |
+
|
| 8 |
+
from scipy._lib._threadsafety import ReentrancyLock, non_reentrant, ReentrancyError
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def test_parallel_threads():
|
| 12 |
+
# Check that ReentrancyLock serializes work in parallel threads.
|
| 13 |
+
#
|
| 14 |
+
# The test is not fully deterministic, and may succeed falsely if
|
| 15 |
+
# the timings go wrong.
|
| 16 |
+
|
| 17 |
+
lock = ReentrancyLock("failure")
|
| 18 |
+
|
| 19 |
+
failflag = [False]
|
| 20 |
+
exceptions_raised = []
|
| 21 |
+
|
| 22 |
+
def worker(k):
|
| 23 |
+
try:
|
| 24 |
+
with lock:
|
| 25 |
+
assert_(not failflag[0])
|
| 26 |
+
failflag[0] = True
|
| 27 |
+
time.sleep(0.1 * k)
|
| 28 |
+
assert_(failflag[0])
|
| 29 |
+
failflag[0] = False
|
| 30 |
+
except Exception:
|
| 31 |
+
exceptions_raised.append(traceback.format_exc(2))
|
| 32 |
+
|
| 33 |
+
threads = [threading.Thread(target=lambda k=k: worker(k))
|
| 34 |
+
for k in range(3)]
|
| 35 |
+
for t in threads:
|
| 36 |
+
t.start()
|
| 37 |
+
for t in threads:
|
| 38 |
+
t.join()
|
| 39 |
+
|
| 40 |
+
exceptions_raised = "\n".join(exceptions_raised)
|
| 41 |
+
assert_(not exceptions_raised, exceptions_raised)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def test_reentering():
|
| 45 |
+
# Check that ReentrancyLock prevents re-entering from the same thread.
|
| 46 |
+
|
| 47 |
+
@non_reentrant()
|
| 48 |
+
def func(x):
|
| 49 |
+
return func(x)
|
| 50 |
+
|
| 51 |
+
assert_raises(ReentrancyError, func, 0)
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__util.py
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from multiprocessing import Pool
|
| 2 |
+
from multiprocessing.pool import Pool as PWL
|
| 3 |
+
import re
|
| 4 |
+
import math
|
| 5 |
+
from fractions import Fraction
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
from numpy.testing import assert_equal, assert_
|
| 9 |
+
import pytest
|
| 10 |
+
from pytest import raises as assert_raises
|
| 11 |
+
import hypothesis.extra.numpy as npst
|
| 12 |
+
from hypothesis import given, strategies, reproduce_failure # noqa: F401
|
| 13 |
+
from scipy.conftest import array_api_compatible
|
| 14 |
+
|
| 15 |
+
from scipy._lib._array_api import xp_assert_equal
|
| 16 |
+
from scipy._lib._util import (_aligned_zeros, check_random_state, MapWrapper,
|
| 17 |
+
getfullargspec_no_self, FullArgSpec,
|
| 18 |
+
rng_integers, _validate_int, _rename_parameter,
|
| 19 |
+
_contains_nan, _rng_html_rewrite, _lazywhere)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def test__aligned_zeros():
|
| 23 |
+
niter = 10
|
| 24 |
+
|
| 25 |
+
def check(shape, dtype, order, align):
|
| 26 |
+
err_msg = repr((shape, dtype, order, align))
|
| 27 |
+
x = _aligned_zeros(shape, dtype, order, align=align)
|
| 28 |
+
if align is None:
|
| 29 |
+
align = np.dtype(dtype).alignment
|
| 30 |
+
assert_equal(x.__array_interface__['data'][0] % align, 0)
|
| 31 |
+
if hasattr(shape, '__len__'):
|
| 32 |
+
assert_equal(x.shape, shape, err_msg)
|
| 33 |
+
else:
|
| 34 |
+
assert_equal(x.shape, (shape,), err_msg)
|
| 35 |
+
assert_equal(x.dtype, dtype)
|
| 36 |
+
if order == "C":
|
| 37 |
+
assert_(x.flags.c_contiguous, err_msg)
|
| 38 |
+
elif order == "F":
|
| 39 |
+
if x.size > 0:
|
| 40 |
+
# Size-0 arrays get invalid flags on NumPy 1.5
|
| 41 |
+
assert_(x.flags.f_contiguous, err_msg)
|
| 42 |
+
elif order is None:
|
| 43 |
+
assert_(x.flags.c_contiguous, err_msg)
|
| 44 |
+
else:
|
| 45 |
+
raise ValueError()
|
| 46 |
+
|
| 47 |
+
# try various alignments
|
| 48 |
+
for align in [1, 2, 3, 4, 8, 16, 32, 64, None]:
|
| 49 |
+
for n in [0, 1, 3, 11]:
|
| 50 |
+
for order in ["C", "F", None]:
|
| 51 |
+
for dtype in [np.uint8, np.float64]:
|
| 52 |
+
for shape in [n, (1, 2, 3, n)]:
|
| 53 |
+
for j in range(niter):
|
| 54 |
+
check(shape, dtype, order, align)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def test_check_random_state():
|
| 58 |
+
# If seed is None, return the RandomState singleton used by np.random.
|
| 59 |
+
# If seed is an int, return a new RandomState instance seeded with seed.
|
| 60 |
+
# If seed is already a RandomState instance, return it.
|
| 61 |
+
# Otherwise raise ValueError.
|
| 62 |
+
rsi = check_random_state(1)
|
| 63 |
+
assert_equal(type(rsi), np.random.RandomState)
|
| 64 |
+
rsi = check_random_state(rsi)
|
| 65 |
+
assert_equal(type(rsi), np.random.RandomState)
|
| 66 |
+
rsi = check_random_state(None)
|
| 67 |
+
assert_equal(type(rsi), np.random.RandomState)
|
| 68 |
+
assert_raises(ValueError, check_random_state, 'a')
|
| 69 |
+
rg = np.random.Generator(np.random.PCG64())
|
| 70 |
+
rsi = check_random_state(rg)
|
| 71 |
+
assert_equal(type(rsi), np.random.Generator)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def test_getfullargspec_no_self():
|
| 75 |
+
p = MapWrapper(1)
|
| 76 |
+
argspec = getfullargspec_no_self(p.__init__)
|
| 77 |
+
assert_equal(argspec, FullArgSpec(['pool'], None, None, (1,), [],
|
| 78 |
+
None, {}))
|
| 79 |
+
argspec = getfullargspec_no_self(p.__call__)
|
| 80 |
+
assert_equal(argspec, FullArgSpec(['func', 'iterable'], None, None, None,
|
| 81 |
+
[], None, {}))
|
| 82 |
+
|
| 83 |
+
class _rv_generic:
|
| 84 |
+
def _rvs(self, a, b=2, c=3, *args, size=None, **kwargs):
|
| 85 |
+
return None
|
| 86 |
+
|
| 87 |
+
rv_obj = _rv_generic()
|
| 88 |
+
argspec = getfullargspec_no_self(rv_obj._rvs)
|
| 89 |
+
assert_equal(argspec, FullArgSpec(['a', 'b', 'c'], 'args', 'kwargs',
|
| 90 |
+
(2, 3), ['size'], {'size': None}, {}))
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def test_mapwrapper_serial():
|
| 94 |
+
in_arg = np.arange(10.)
|
| 95 |
+
out_arg = np.sin(in_arg)
|
| 96 |
+
|
| 97 |
+
p = MapWrapper(1)
|
| 98 |
+
assert_(p._mapfunc is map)
|
| 99 |
+
assert_(p.pool is None)
|
| 100 |
+
assert_(p._own_pool is False)
|
| 101 |
+
out = list(p(np.sin, in_arg))
|
| 102 |
+
assert_equal(out, out_arg)
|
| 103 |
+
|
| 104 |
+
with assert_raises(RuntimeError):
|
| 105 |
+
p = MapWrapper(0)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def test_pool():
|
| 109 |
+
with Pool(2) as p:
|
| 110 |
+
p.map(math.sin, [1, 2, 3, 4])
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def test_mapwrapper_parallel():
|
| 114 |
+
in_arg = np.arange(10.)
|
| 115 |
+
out_arg = np.sin(in_arg)
|
| 116 |
+
|
| 117 |
+
with MapWrapper(2) as p:
|
| 118 |
+
out = p(np.sin, in_arg)
|
| 119 |
+
assert_equal(list(out), out_arg)
|
| 120 |
+
|
| 121 |
+
assert_(p._own_pool is True)
|
| 122 |
+
assert_(isinstance(p.pool, PWL))
|
| 123 |
+
assert_(p._mapfunc is not None)
|
| 124 |
+
|
| 125 |
+
# the context manager should've closed the internal pool
|
| 126 |
+
# check that it has by asking it to calculate again.
|
| 127 |
+
with assert_raises(Exception) as excinfo:
|
| 128 |
+
p(np.sin, in_arg)
|
| 129 |
+
|
| 130 |
+
assert_(excinfo.type is ValueError)
|
| 131 |
+
|
| 132 |
+
# can also set a PoolWrapper up with a map-like callable instance
|
| 133 |
+
with Pool(2) as p:
|
| 134 |
+
q = MapWrapper(p.map)
|
| 135 |
+
|
| 136 |
+
assert_(q._own_pool is False)
|
| 137 |
+
q.close()
|
| 138 |
+
|
| 139 |
+
# closing the PoolWrapper shouldn't close the internal pool
|
| 140 |
+
# because it didn't create it
|
| 141 |
+
out = p.map(np.sin, in_arg)
|
| 142 |
+
assert_equal(list(out), out_arg)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def test_rng_integers():
|
| 146 |
+
rng = np.random.RandomState()
|
| 147 |
+
|
| 148 |
+
# test that numbers are inclusive of high point
|
| 149 |
+
arr = rng_integers(rng, low=2, high=5, size=100, endpoint=True)
|
| 150 |
+
assert np.max(arr) == 5
|
| 151 |
+
assert np.min(arr) == 2
|
| 152 |
+
assert arr.shape == (100, )
|
| 153 |
+
|
| 154 |
+
# test that numbers are inclusive of high point
|
| 155 |
+
arr = rng_integers(rng, low=5, size=100, endpoint=True)
|
| 156 |
+
assert np.max(arr) == 5
|
| 157 |
+
assert np.min(arr) == 0
|
| 158 |
+
assert arr.shape == (100, )
|
| 159 |
+
|
| 160 |
+
# test that numbers are exclusive of high point
|
| 161 |
+
arr = rng_integers(rng, low=2, high=5, size=100, endpoint=False)
|
| 162 |
+
assert np.max(arr) == 4
|
| 163 |
+
assert np.min(arr) == 2
|
| 164 |
+
assert arr.shape == (100, )
|
| 165 |
+
|
| 166 |
+
# test that numbers are exclusive of high point
|
| 167 |
+
arr = rng_integers(rng, low=5, size=100, endpoint=False)
|
| 168 |
+
assert np.max(arr) == 4
|
| 169 |
+
assert np.min(arr) == 0
|
| 170 |
+
assert arr.shape == (100, )
|
| 171 |
+
|
| 172 |
+
# now try with np.random.Generator
|
| 173 |
+
try:
|
| 174 |
+
rng = np.random.default_rng()
|
| 175 |
+
except AttributeError:
|
| 176 |
+
return
|
| 177 |
+
|
| 178 |
+
# test that numbers are inclusive of high point
|
| 179 |
+
arr = rng_integers(rng, low=2, high=5, size=100, endpoint=True)
|
| 180 |
+
assert np.max(arr) == 5
|
| 181 |
+
assert np.min(arr) == 2
|
| 182 |
+
assert arr.shape == (100, )
|
| 183 |
+
|
| 184 |
+
# test that numbers are inclusive of high point
|
| 185 |
+
arr = rng_integers(rng, low=5, size=100, endpoint=True)
|
| 186 |
+
assert np.max(arr) == 5
|
| 187 |
+
assert np.min(arr) == 0
|
| 188 |
+
assert arr.shape == (100, )
|
| 189 |
+
|
| 190 |
+
# test that numbers are exclusive of high point
|
| 191 |
+
arr = rng_integers(rng, low=2, high=5, size=100, endpoint=False)
|
| 192 |
+
assert np.max(arr) == 4
|
| 193 |
+
assert np.min(arr) == 2
|
| 194 |
+
assert arr.shape == (100, )
|
| 195 |
+
|
| 196 |
+
# test that numbers are exclusive of high point
|
| 197 |
+
arr = rng_integers(rng, low=5, size=100, endpoint=False)
|
| 198 |
+
assert np.max(arr) == 4
|
| 199 |
+
assert np.min(arr) == 0
|
| 200 |
+
assert arr.shape == (100, )
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
class TestValidateInt:
|
| 204 |
+
|
| 205 |
+
@pytest.mark.parametrize('n', [4, np.uint8(4), np.int16(4), np.array(4)])
|
| 206 |
+
def test_validate_int(self, n):
|
| 207 |
+
n = _validate_int(n, 'n')
|
| 208 |
+
assert n == 4
|
| 209 |
+
|
| 210 |
+
@pytest.mark.parametrize('n', [4.0, np.array([4]), Fraction(4, 1)])
|
| 211 |
+
def test_validate_int_bad(self, n):
|
| 212 |
+
with pytest.raises(TypeError, match='n must be an integer'):
|
| 213 |
+
_validate_int(n, 'n')
|
| 214 |
+
|
| 215 |
+
def test_validate_int_below_min(self):
|
| 216 |
+
with pytest.raises(ValueError, match='n must be an integer not '
|
| 217 |
+
'less than 0'):
|
| 218 |
+
_validate_int(-1, 'n', 0)
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
class TestRenameParameter:
|
| 222 |
+
# check that wrapper `_rename_parameter` for backward-compatible
|
| 223 |
+
# keyword renaming works correctly
|
| 224 |
+
|
| 225 |
+
# Example method/function that still accepts keyword `old`
|
| 226 |
+
@_rename_parameter("old", "new")
|
| 227 |
+
def old_keyword_still_accepted(self, new):
|
| 228 |
+
return new
|
| 229 |
+
|
| 230 |
+
# Example method/function for which keyword `old` is deprecated
|
| 231 |
+
@_rename_parameter("old", "new", dep_version="1.9.0")
|
| 232 |
+
def old_keyword_deprecated(self, new):
|
| 233 |
+
return new
|
| 234 |
+
|
| 235 |
+
def test_old_keyword_still_accepted(self):
|
| 236 |
+
# positional argument and both keyword work identically
|
| 237 |
+
res1 = self.old_keyword_still_accepted(10)
|
| 238 |
+
res2 = self.old_keyword_still_accepted(new=10)
|
| 239 |
+
res3 = self.old_keyword_still_accepted(old=10)
|
| 240 |
+
assert res1 == res2 == res3 == 10
|
| 241 |
+
|
| 242 |
+
# unexpected keyword raises an error
|
| 243 |
+
message = re.escape("old_keyword_still_accepted() got an unexpected")
|
| 244 |
+
with pytest.raises(TypeError, match=message):
|
| 245 |
+
self.old_keyword_still_accepted(unexpected=10)
|
| 246 |
+
|
| 247 |
+
# multiple values for the same parameter raises an error
|
| 248 |
+
message = re.escape("old_keyword_still_accepted() got multiple")
|
| 249 |
+
with pytest.raises(TypeError, match=message):
|
| 250 |
+
self.old_keyword_still_accepted(10, new=10)
|
| 251 |
+
with pytest.raises(TypeError, match=message):
|
| 252 |
+
self.old_keyword_still_accepted(10, old=10)
|
| 253 |
+
with pytest.raises(TypeError, match=message):
|
| 254 |
+
self.old_keyword_still_accepted(new=10, old=10)
|
| 255 |
+
|
| 256 |
+
def test_old_keyword_deprecated(self):
|
| 257 |
+
# positional argument and both keyword work identically,
|
| 258 |
+
# but use of old keyword results in DeprecationWarning
|
| 259 |
+
dep_msg = "Use of keyword argument `old` is deprecated"
|
| 260 |
+
res1 = self.old_keyword_deprecated(10)
|
| 261 |
+
res2 = self.old_keyword_deprecated(new=10)
|
| 262 |
+
with pytest.warns(DeprecationWarning, match=dep_msg):
|
| 263 |
+
res3 = self.old_keyword_deprecated(old=10)
|
| 264 |
+
assert res1 == res2 == res3 == 10
|
| 265 |
+
|
| 266 |
+
# unexpected keyword raises an error
|
| 267 |
+
message = re.escape("old_keyword_deprecated() got an unexpected")
|
| 268 |
+
with pytest.raises(TypeError, match=message):
|
| 269 |
+
self.old_keyword_deprecated(unexpected=10)
|
| 270 |
+
|
| 271 |
+
# multiple values for the same parameter raises an error and,
|
| 272 |
+
# if old keyword is used, results in DeprecationWarning
|
| 273 |
+
message = re.escape("old_keyword_deprecated() got multiple")
|
| 274 |
+
with pytest.raises(TypeError, match=message):
|
| 275 |
+
self.old_keyword_deprecated(10, new=10)
|
| 276 |
+
with pytest.raises(TypeError, match=message), \
|
| 277 |
+
pytest.warns(DeprecationWarning, match=dep_msg):
|
| 278 |
+
self.old_keyword_deprecated(10, old=10)
|
| 279 |
+
with pytest.raises(TypeError, match=message), \
|
| 280 |
+
pytest.warns(DeprecationWarning, match=dep_msg):
|
| 281 |
+
self.old_keyword_deprecated(new=10, old=10)
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
class TestContainsNaNTest:
|
| 285 |
+
|
| 286 |
+
def test_policy(self):
|
| 287 |
+
data = np.array([1, 2, 3, np.nan])
|
| 288 |
+
|
| 289 |
+
contains_nan, nan_policy = _contains_nan(data, nan_policy="propagate")
|
| 290 |
+
assert contains_nan
|
| 291 |
+
assert nan_policy == "propagate"
|
| 292 |
+
|
| 293 |
+
contains_nan, nan_policy = _contains_nan(data, nan_policy="omit")
|
| 294 |
+
assert contains_nan
|
| 295 |
+
assert nan_policy == "omit"
|
| 296 |
+
|
| 297 |
+
msg = "The input contains nan values"
|
| 298 |
+
with pytest.raises(ValueError, match=msg):
|
| 299 |
+
_contains_nan(data, nan_policy="raise")
|
| 300 |
+
|
| 301 |
+
msg = "nan_policy must be one of"
|
| 302 |
+
with pytest.raises(ValueError, match=msg):
|
| 303 |
+
_contains_nan(data, nan_policy="nan")
|
| 304 |
+
|
| 305 |
+
def test_contains_nan_1d(self):
|
| 306 |
+
data1 = np.array([1, 2, 3])
|
| 307 |
+
assert not _contains_nan(data1)[0]
|
| 308 |
+
|
| 309 |
+
data2 = np.array([1, 2, 3, np.nan])
|
| 310 |
+
assert _contains_nan(data2)[0]
|
| 311 |
+
|
| 312 |
+
data3 = np.array([np.nan, 2, 3, np.nan])
|
| 313 |
+
assert _contains_nan(data3)[0]
|
| 314 |
+
|
| 315 |
+
data4 = np.array([1, 2, "3", np.nan]) # converted to string "nan"
|
| 316 |
+
assert not _contains_nan(data4)[0]
|
| 317 |
+
|
| 318 |
+
data5 = np.array([1, 2, "3", np.nan], dtype='object')
|
| 319 |
+
assert _contains_nan(data5)[0]
|
| 320 |
+
|
| 321 |
+
def test_contains_nan_2d(self):
|
| 322 |
+
data1 = np.array([[1, 2], [3, 4]])
|
| 323 |
+
assert not _contains_nan(data1)[0]
|
| 324 |
+
|
| 325 |
+
data2 = np.array([[1, 2], [3, np.nan]])
|
| 326 |
+
assert _contains_nan(data2)[0]
|
| 327 |
+
|
| 328 |
+
data3 = np.array([["1", 2], [3, np.nan]]) # converted to string "nan"
|
| 329 |
+
assert not _contains_nan(data3)[0]
|
| 330 |
+
|
| 331 |
+
data4 = np.array([["1", 2], [3, np.nan]], dtype='object')
|
| 332 |
+
assert _contains_nan(data4)[0]
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
def test__rng_html_rewrite():
|
| 336 |
+
def mock_str():
|
| 337 |
+
lines = [
|
| 338 |
+
'np.random.default_rng(8989843)',
|
| 339 |
+
'np.random.default_rng(seed)',
|
| 340 |
+
'np.random.default_rng(0x9a71b21474694f919882289dc1559ca)',
|
| 341 |
+
' bob ',
|
| 342 |
+
]
|
| 343 |
+
return lines
|
| 344 |
+
|
| 345 |
+
res = _rng_html_rewrite(mock_str)()
|
| 346 |
+
ref = [
|
| 347 |
+
'np.random.default_rng()',
|
| 348 |
+
'np.random.default_rng(seed)',
|
| 349 |
+
'np.random.default_rng()',
|
| 350 |
+
' bob ',
|
| 351 |
+
]
|
| 352 |
+
|
| 353 |
+
assert res == ref
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
class TestLazywhere:
|
| 357 |
+
n_arrays = strategies.integers(min_value=1, max_value=3)
|
| 358 |
+
rng_seed = strategies.integers(min_value=1000000000, max_value=9999999999)
|
| 359 |
+
dtype = strategies.sampled_from((np.float32, np.float64))
|
| 360 |
+
p = strategies.floats(min_value=0, max_value=1)
|
| 361 |
+
data = strategies.data()
|
| 362 |
+
|
| 363 |
+
@pytest.mark.filterwarnings('ignore::RuntimeWarning') # overflows, etc.
|
| 364 |
+
@array_api_compatible
|
| 365 |
+
@given(n_arrays=n_arrays, rng_seed=rng_seed, dtype=dtype, p=p, data=data)
|
| 366 |
+
def test_basic(self, n_arrays, rng_seed, dtype, p, data, xp):
|
| 367 |
+
mbs = npst.mutually_broadcastable_shapes(num_shapes=n_arrays+1,
|
| 368 |
+
min_side=0)
|
| 369 |
+
input_shapes, result_shape = data.draw(mbs)
|
| 370 |
+
cond_shape, *shapes = input_shapes
|
| 371 |
+
fillvalue = xp.asarray(data.draw(npst.arrays(dtype=dtype, shape=tuple())))
|
| 372 |
+
arrays = [xp.asarray(data.draw(npst.arrays(dtype=dtype, shape=shape)))
|
| 373 |
+
for shape in shapes]
|
| 374 |
+
|
| 375 |
+
def f(*args):
|
| 376 |
+
return sum(arg for arg in args)
|
| 377 |
+
|
| 378 |
+
def f2(*args):
|
| 379 |
+
return sum(arg for arg in args) / 2
|
| 380 |
+
|
| 381 |
+
rng = np.random.default_rng(rng_seed)
|
| 382 |
+
cond = xp.asarray(rng.random(size=cond_shape) > p)
|
| 383 |
+
|
| 384 |
+
res1 = _lazywhere(cond, arrays, f, fillvalue)
|
| 385 |
+
res2 = _lazywhere(cond, arrays, f, f2=f2)
|
| 386 |
+
|
| 387 |
+
# Ensure arrays are at least 1d to follow sane type promotion rules.
|
| 388 |
+
if xp == np:
|
| 389 |
+
cond, fillvalue, *arrays = np.atleast_1d(cond, fillvalue, *arrays)
|
| 390 |
+
|
| 391 |
+
ref1 = xp.where(cond, f(*arrays), fillvalue)
|
| 392 |
+
ref2 = xp.where(cond, f(*arrays), f2(*arrays))
|
| 393 |
+
|
| 394 |
+
if xp == np:
|
| 395 |
+
ref1 = ref1.reshape(result_shape)
|
| 396 |
+
ref2 = ref2.reshape(result_shape)
|
| 397 |
+
res1 = xp.asarray(res1)[()]
|
| 398 |
+
res2 = xp.asarray(res2)[()]
|
| 399 |
+
|
| 400 |
+
isinstance(res1, type(xp.asarray([])))
|
| 401 |
+
xp_assert_equal(res1, ref1)
|
| 402 |
+
assert_equal(res1.shape, ref1.shape)
|
| 403 |
+
assert_equal(res1.dtype, ref1.dtype)
|
| 404 |
+
|
| 405 |
+
isinstance(res2, type(xp.asarray([])))
|
| 406 |
+
xp_assert_equal(res2, ref2)
|
| 407 |
+
assert_equal(res2.shape, ref2.shape)
|
| 408 |
+
assert_equal(res2.dtype, ref2.dtype)
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_array_api.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
from scipy.conftest import array_api_compatible
|
| 5 |
+
from scipy._lib._array_api import (
|
| 6 |
+
_GLOBAL_CONFIG, array_namespace, as_xparray, copy, xp_assert_equal, is_numpy
|
| 7 |
+
)
|
| 8 |
+
import scipy._lib.array_api_compat.array_api_compat.numpy as np_compat
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@pytest.mark.skipif(not _GLOBAL_CONFIG["SCIPY_ARRAY_API"],
|
| 12 |
+
reason="Array API test; set environment variable SCIPY_ARRAY_API=1 to run it")
|
| 13 |
+
class TestArrayAPI:
|
| 14 |
+
|
| 15 |
+
def test_array_namespace(self):
|
| 16 |
+
x, y = np.array([0, 1, 2]), np.array([0, 1, 2])
|
| 17 |
+
xp = array_namespace(x, y)
|
| 18 |
+
assert 'array_api_compat.numpy' in xp.__name__
|
| 19 |
+
|
| 20 |
+
_GLOBAL_CONFIG["SCIPY_ARRAY_API"] = False
|
| 21 |
+
xp = array_namespace(x, y)
|
| 22 |
+
assert 'array_api_compat.numpy' in xp.__name__
|
| 23 |
+
_GLOBAL_CONFIG["SCIPY_ARRAY_API"] = True
|
| 24 |
+
|
| 25 |
+
@array_api_compatible
|
| 26 |
+
def test_asarray(self, xp):
|
| 27 |
+
x, y = as_xparray([0, 1, 2], xp=xp), as_xparray(np.arange(3), xp=xp)
|
| 28 |
+
ref = xp.asarray([0, 1, 2])
|
| 29 |
+
xp_assert_equal(x, ref)
|
| 30 |
+
xp_assert_equal(y, ref)
|
| 31 |
+
|
| 32 |
+
@pytest.mark.filterwarnings("ignore: the matrix subclass")
|
| 33 |
+
def test_raises(self):
|
| 34 |
+
msg = "of type `numpy.ma.MaskedArray` are not supported"
|
| 35 |
+
with pytest.raises(TypeError, match=msg):
|
| 36 |
+
array_namespace(np.ma.array(1), np.array(1))
|
| 37 |
+
|
| 38 |
+
msg = "of type `numpy.matrix` are not supported"
|
| 39 |
+
with pytest.raises(TypeError, match=msg):
|
| 40 |
+
array_namespace(np.array(1), np.matrix(1))
|
| 41 |
+
|
| 42 |
+
msg = "only boolean and numerical dtypes are supported"
|
| 43 |
+
with pytest.raises(TypeError, match=msg):
|
| 44 |
+
array_namespace([object()])
|
| 45 |
+
with pytest.raises(TypeError, match=msg):
|
| 46 |
+
array_namespace('abc')
|
| 47 |
+
|
| 48 |
+
def test_array_likes(self):
|
| 49 |
+
# should be no exceptions
|
| 50 |
+
array_namespace([0, 1, 2])
|
| 51 |
+
array_namespace(1, 2, 3)
|
| 52 |
+
array_namespace(1)
|
| 53 |
+
|
| 54 |
+
@array_api_compatible
|
| 55 |
+
def test_copy(self, xp):
|
| 56 |
+
for _xp in [xp, None]:
|
| 57 |
+
x = xp.asarray([1, 2, 3])
|
| 58 |
+
y = copy(x, xp=_xp)
|
| 59 |
+
# with numpy we'd want to use np.shared_memory, but that's not specified
|
| 60 |
+
# in the array-api
|
| 61 |
+
x[0] = 10
|
| 62 |
+
x[1] = 11
|
| 63 |
+
x[2] = 12
|
| 64 |
+
|
| 65 |
+
assert x[0] != y[0]
|
| 66 |
+
assert x[1] != y[1]
|
| 67 |
+
assert x[2] != y[2]
|
| 68 |
+
assert id(x) != id(y)
|
| 69 |
+
|
| 70 |
+
@array_api_compatible
|
| 71 |
+
@pytest.mark.parametrize('dtype', ['int32', 'int64', 'float32', 'float64'])
|
| 72 |
+
@pytest.mark.parametrize('shape', [(), (3,)])
|
| 73 |
+
def test_strict_checks(self, xp, dtype, shape):
|
| 74 |
+
# Check that `_strict_check` behaves as expected
|
| 75 |
+
dtype = getattr(xp, dtype)
|
| 76 |
+
x = xp.broadcast_to(xp.asarray(1, dtype=dtype), shape)
|
| 77 |
+
x = x if shape else x[()]
|
| 78 |
+
y = np_compat.asarray(1)[()]
|
| 79 |
+
|
| 80 |
+
options = dict(check_namespace=True, check_dtype=False, check_shape=False)
|
| 81 |
+
if xp == np:
|
| 82 |
+
xp_assert_equal(x, y, **options)
|
| 83 |
+
else:
|
| 84 |
+
with pytest.raises(AssertionError, match="Namespaces do not match."):
|
| 85 |
+
xp_assert_equal(x, y, **options)
|
| 86 |
+
|
| 87 |
+
options = dict(check_namespace=False, check_dtype=True, check_shape=False)
|
| 88 |
+
if y.dtype.name in str(x.dtype):
|
| 89 |
+
xp_assert_equal(x, y, **options)
|
| 90 |
+
else:
|
| 91 |
+
with pytest.raises(AssertionError, match="dtypes do not match."):
|
| 92 |
+
xp_assert_equal(x, y, **options)
|
| 93 |
+
|
| 94 |
+
options = dict(check_namespace=False, check_dtype=False, check_shape=True)
|
| 95 |
+
if x.shape == y.shape:
|
| 96 |
+
xp_assert_equal(x, y, **options)
|
| 97 |
+
else:
|
| 98 |
+
with pytest.raises(AssertionError, match="Shapes do not match."):
|
| 99 |
+
xp_assert_equal(x, y, **options)
|
| 100 |
+
|
| 101 |
+
@array_api_compatible
|
| 102 |
+
def test_check_scalar(self, xp):
|
| 103 |
+
if not is_numpy(xp):
|
| 104 |
+
pytest.skip("Scalars only exist in NumPy")
|
| 105 |
+
|
| 106 |
+
if is_numpy(xp):
|
| 107 |
+
with pytest.raises(AssertionError, match="Types do not match."):
|
| 108 |
+
xp_assert_equal(xp.asarray(0.), xp.float64(0))
|
| 109 |
+
xp_assert_equal(xp.float64(0), xp.asarray(0.))
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_bunch.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
import pickle
|
| 3 |
+
from numpy.testing import assert_equal
|
| 4 |
+
from scipy._lib._bunch import _make_tuple_bunch
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
# `Result` is defined at the top level of the module so it can be
|
| 8 |
+
# used to test pickling.
|
| 9 |
+
Result = _make_tuple_bunch('Result', ['x', 'y', 'z'], ['w', 'beta'])
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class TestMakeTupleBunch:
|
| 13 |
+
|
| 14 |
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| 15 |
+
# Tests with Result
|
| 16 |
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| 17 |
+
|
| 18 |
+
def setup_method(self):
|
| 19 |
+
# Set up an instance of Result.
|
| 20 |
+
self.result = Result(x=1, y=2, z=3, w=99, beta=0.5)
|
| 21 |
+
|
| 22 |
+
def test_attribute_access(self):
|
| 23 |
+
assert_equal(self.result.x, 1)
|
| 24 |
+
assert_equal(self.result.y, 2)
|
| 25 |
+
assert_equal(self.result.z, 3)
|
| 26 |
+
assert_equal(self.result.w, 99)
|
| 27 |
+
assert_equal(self.result.beta, 0.5)
|
| 28 |
+
|
| 29 |
+
def test_indexing(self):
|
| 30 |
+
assert_equal(self.result[0], 1)
|
| 31 |
+
assert_equal(self.result[1], 2)
|
| 32 |
+
assert_equal(self.result[2], 3)
|
| 33 |
+
assert_equal(self.result[-1], 3)
|
| 34 |
+
with pytest.raises(IndexError, match='index out of range'):
|
| 35 |
+
self.result[3]
|
| 36 |
+
|
| 37 |
+
def test_unpacking(self):
|
| 38 |
+
x0, y0, z0 = self.result
|
| 39 |
+
assert_equal((x0, y0, z0), (1, 2, 3))
|
| 40 |
+
assert_equal(self.result, (1, 2, 3))
|
| 41 |
+
|
| 42 |
+
def test_slice(self):
|
| 43 |
+
assert_equal(self.result[1:], (2, 3))
|
| 44 |
+
assert_equal(self.result[::2], (1, 3))
|
| 45 |
+
assert_equal(self.result[::-1], (3, 2, 1))
|
| 46 |
+
|
| 47 |
+
def test_len(self):
|
| 48 |
+
assert_equal(len(self.result), 3)
|
| 49 |
+
|
| 50 |
+
def test_repr(self):
|
| 51 |
+
s = repr(self.result)
|
| 52 |
+
assert_equal(s, 'Result(x=1, y=2, z=3, w=99, beta=0.5)')
|
| 53 |
+
|
| 54 |
+
def test_hash(self):
|
| 55 |
+
assert_equal(hash(self.result), hash((1, 2, 3)))
|
| 56 |
+
|
| 57 |
+
def test_pickle(self):
|
| 58 |
+
s = pickle.dumps(self.result)
|
| 59 |
+
obj = pickle.loads(s)
|
| 60 |
+
assert isinstance(obj, Result)
|
| 61 |
+
assert_equal(obj.x, self.result.x)
|
| 62 |
+
assert_equal(obj.y, self.result.y)
|
| 63 |
+
assert_equal(obj.z, self.result.z)
|
| 64 |
+
assert_equal(obj.w, self.result.w)
|
| 65 |
+
assert_equal(obj.beta, self.result.beta)
|
| 66 |
+
|
| 67 |
+
def test_read_only_existing(self):
|
| 68 |
+
with pytest.raises(AttributeError, match="can't set attribute"):
|
| 69 |
+
self.result.x = -1
|
| 70 |
+
|
| 71 |
+
def test_read_only_new(self):
|
| 72 |
+
self.result.plate_of_shrimp = "lattice of coincidence"
|
| 73 |
+
assert self.result.plate_of_shrimp == "lattice of coincidence"
|
| 74 |
+
|
| 75 |
+
def test_constructor_missing_parameter(self):
|
| 76 |
+
with pytest.raises(TypeError, match='missing'):
|
| 77 |
+
# `w` is missing.
|
| 78 |
+
Result(x=1, y=2, z=3, beta=0.75)
|
| 79 |
+
|
| 80 |
+
def test_constructor_incorrect_parameter(self):
|
| 81 |
+
with pytest.raises(TypeError, match='unexpected'):
|
| 82 |
+
# `foo` is not an existing field.
|
| 83 |
+
Result(x=1, y=2, z=3, w=123, beta=0.75, foo=999)
|
| 84 |
+
|
| 85 |
+
def test_module(self):
|
| 86 |
+
m = 'scipy._lib.tests.test_bunch'
|
| 87 |
+
assert_equal(Result.__module__, m)
|
| 88 |
+
assert_equal(self.result.__module__, m)
|
| 89 |
+
|
| 90 |
+
def test_extra_fields_per_instance(self):
|
| 91 |
+
# This test exists to ensure that instances of the same class
|
| 92 |
+
# store their own values for the extra fields. That is, the values
|
| 93 |
+
# are stored per instance and not in the class.
|
| 94 |
+
result1 = Result(x=1, y=2, z=3, w=-1, beta=0.0)
|
| 95 |
+
result2 = Result(x=4, y=5, z=6, w=99, beta=1.0)
|
| 96 |
+
assert_equal(result1.w, -1)
|
| 97 |
+
assert_equal(result1.beta, 0.0)
|
| 98 |
+
# The rest of these checks aren't essential, but let's check
|
| 99 |
+
# them anyway.
|
| 100 |
+
assert_equal(result1[:], (1, 2, 3))
|
| 101 |
+
assert_equal(result2.w, 99)
|
| 102 |
+
assert_equal(result2.beta, 1.0)
|
| 103 |
+
assert_equal(result2[:], (4, 5, 6))
|
| 104 |
+
|
| 105 |
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| 106 |
+
# Other tests
|
| 107 |
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| 108 |
+
|
| 109 |
+
def test_extra_field_names_is_optional(self):
|
| 110 |
+
Square = _make_tuple_bunch('Square', ['width', 'height'])
|
| 111 |
+
sq = Square(width=1, height=2)
|
| 112 |
+
assert_equal(sq.width, 1)
|
| 113 |
+
assert_equal(sq.height, 2)
|
| 114 |
+
s = repr(sq)
|
| 115 |
+
assert_equal(s, 'Square(width=1, height=2)')
|
| 116 |
+
|
| 117 |
+
def test_tuple_like(self):
|
| 118 |
+
Tup = _make_tuple_bunch('Tup', ['a', 'b'])
|
| 119 |
+
tu = Tup(a=1, b=2)
|
| 120 |
+
assert isinstance(tu, tuple)
|
| 121 |
+
assert isinstance(tu + (1,), tuple)
|
| 122 |
+
|
| 123 |
+
def test_explicit_module(self):
|
| 124 |
+
m = 'some.module.name'
|
| 125 |
+
Foo = _make_tuple_bunch('Foo', ['x'], ['a', 'b'], module=m)
|
| 126 |
+
foo = Foo(x=1, a=355, b=113)
|
| 127 |
+
assert_equal(Foo.__module__, m)
|
| 128 |
+
assert_equal(foo.__module__, m)
|
| 129 |
+
|
| 130 |
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| 131 |
+
# Argument validation
|
| 132 |
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| 133 |
+
|
| 134 |
+
@pytest.mark.parametrize('args', [('123', ['a'], ['b']),
|
| 135 |
+
('Foo', ['-3'], ['x']),
|
| 136 |
+
('Foo', ['a'], ['+-*/'])])
|
| 137 |
+
def test_identifiers_not_allowed(self, args):
|
| 138 |
+
with pytest.raises(ValueError, match='identifiers'):
|
| 139 |
+
_make_tuple_bunch(*args)
|
| 140 |
+
|
| 141 |
+
@pytest.mark.parametrize('args', [('Foo', ['a', 'b', 'a'], ['x']),
|
| 142 |
+
('Foo', ['a', 'b'], ['b', 'x'])])
|
| 143 |
+
def test_repeated_field_names(self, args):
|
| 144 |
+
with pytest.raises(ValueError, match='Duplicate'):
|
| 145 |
+
_make_tuple_bunch(*args)
|
| 146 |
+
|
| 147 |
+
@pytest.mark.parametrize('args', [('Foo', ['_a'], ['x']),
|
| 148 |
+
('Foo', ['a'], ['_x'])])
|
| 149 |
+
def test_leading_underscore_not_allowed(self, args):
|
| 150 |
+
with pytest.raises(ValueError, match='underscore'):
|
| 151 |
+
_make_tuple_bunch(*args)
|
| 152 |
+
|
| 153 |
+
@pytest.mark.parametrize('args', [('Foo', ['def'], ['x']),
|
| 154 |
+
('Foo', ['a'], ['or']),
|
| 155 |
+
('and', ['a'], ['x'])])
|
| 156 |
+
def test_keyword_not_allowed_in_fields(self, args):
|
| 157 |
+
with pytest.raises(ValueError, match='keyword'):
|
| 158 |
+
_make_tuple_bunch(*args)
|
| 159 |
+
|
| 160 |
+
def test_at_least_one_field_name_required(self):
|
| 161 |
+
with pytest.raises(ValueError, match='at least one name'):
|
| 162 |
+
_make_tuple_bunch('Qwerty', [], ['a', 'b'])
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_ccallback.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from numpy.testing import assert_equal, assert_
|
| 2 |
+
from pytest import raises as assert_raises
|
| 3 |
+
|
| 4 |
+
import time
|
| 5 |
+
import pytest
|
| 6 |
+
import ctypes
|
| 7 |
+
import threading
|
| 8 |
+
from scipy._lib import _ccallback_c as _test_ccallback_cython
|
| 9 |
+
from scipy._lib import _test_ccallback
|
| 10 |
+
from scipy._lib._ccallback import LowLevelCallable
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
import cffi
|
| 14 |
+
HAVE_CFFI = True
|
| 15 |
+
except ImportError:
|
| 16 |
+
HAVE_CFFI = False
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
ERROR_VALUE = 2.0
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def callback_python(a, user_data=None):
|
| 23 |
+
if a == ERROR_VALUE:
|
| 24 |
+
raise ValueError("bad value")
|
| 25 |
+
|
| 26 |
+
if user_data is None:
|
| 27 |
+
return a + 1
|
| 28 |
+
else:
|
| 29 |
+
return a + user_data
|
| 30 |
+
|
| 31 |
+
def _get_cffi_func(base, signature):
|
| 32 |
+
if not HAVE_CFFI:
|
| 33 |
+
pytest.skip("cffi not installed")
|
| 34 |
+
|
| 35 |
+
# Get function address
|
| 36 |
+
voidp = ctypes.cast(base, ctypes.c_void_p)
|
| 37 |
+
address = voidp.value
|
| 38 |
+
|
| 39 |
+
# Create corresponding cffi handle
|
| 40 |
+
ffi = cffi.FFI()
|
| 41 |
+
func = ffi.cast(signature, address)
|
| 42 |
+
return func
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _get_ctypes_data():
|
| 46 |
+
value = ctypes.c_double(2.0)
|
| 47 |
+
return ctypes.cast(ctypes.pointer(value), ctypes.c_voidp)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _get_cffi_data():
|
| 51 |
+
if not HAVE_CFFI:
|
| 52 |
+
pytest.skip("cffi not installed")
|
| 53 |
+
ffi = cffi.FFI()
|
| 54 |
+
return ffi.new('double *', 2.0)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
CALLERS = {
|
| 58 |
+
'simple': _test_ccallback.test_call_simple,
|
| 59 |
+
'nodata': _test_ccallback.test_call_nodata,
|
| 60 |
+
'nonlocal': _test_ccallback.test_call_nonlocal,
|
| 61 |
+
'cython': _test_ccallback_cython.test_call_cython,
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
# These functions have signatures known to the callers
|
| 65 |
+
FUNCS = {
|
| 66 |
+
'python': lambda: callback_python,
|
| 67 |
+
'capsule': lambda: _test_ccallback.test_get_plus1_capsule(),
|
| 68 |
+
'cython': lambda: LowLevelCallable.from_cython(_test_ccallback_cython,
|
| 69 |
+
"plus1_cython"),
|
| 70 |
+
'ctypes': lambda: _test_ccallback_cython.plus1_ctypes,
|
| 71 |
+
'cffi': lambda: _get_cffi_func(_test_ccallback_cython.plus1_ctypes,
|
| 72 |
+
'double (*)(double, int *, void *)'),
|
| 73 |
+
'capsule_b': lambda: _test_ccallback.test_get_plus1b_capsule(),
|
| 74 |
+
'cython_b': lambda: LowLevelCallable.from_cython(_test_ccallback_cython,
|
| 75 |
+
"plus1b_cython"),
|
| 76 |
+
'ctypes_b': lambda: _test_ccallback_cython.plus1b_ctypes,
|
| 77 |
+
'cffi_b': lambda: _get_cffi_func(_test_ccallback_cython.plus1b_ctypes,
|
| 78 |
+
'double (*)(double, double, int *, void *)'),
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
# These functions have signatures the callers don't know
|
| 82 |
+
BAD_FUNCS = {
|
| 83 |
+
'capsule_bc': lambda: _test_ccallback.test_get_plus1bc_capsule(),
|
| 84 |
+
'cython_bc': lambda: LowLevelCallable.from_cython(_test_ccallback_cython,
|
| 85 |
+
"plus1bc_cython"),
|
| 86 |
+
'ctypes_bc': lambda: _test_ccallback_cython.plus1bc_ctypes,
|
| 87 |
+
'cffi_bc': lambda: _get_cffi_func(
|
| 88 |
+
_test_ccallback_cython.plus1bc_ctypes,
|
| 89 |
+
'double (*)(double, double, double, int *, void *)'
|
| 90 |
+
),
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
USER_DATAS = {
|
| 94 |
+
'ctypes': _get_ctypes_data,
|
| 95 |
+
'cffi': _get_cffi_data,
|
| 96 |
+
'capsule': _test_ccallback.test_get_data_capsule,
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def test_callbacks():
|
| 101 |
+
def check(caller, func, user_data):
|
| 102 |
+
caller = CALLERS[caller]
|
| 103 |
+
func = FUNCS[func]()
|
| 104 |
+
user_data = USER_DATAS[user_data]()
|
| 105 |
+
|
| 106 |
+
if func is callback_python:
|
| 107 |
+
def func2(x):
|
| 108 |
+
return func(x, 2.0)
|
| 109 |
+
else:
|
| 110 |
+
func2 = LowLevelCallable(func, user_data)
|
| 111 |
+
func = LowLevelCallable(func)
|
| 112 |
+
|
| 113 |
+
# Test basic call
|
| 114 |
+
assert_equal(caller(func, 1.0), 2.0)
|
| 115 |
+
|
| 116 |
+
# Test 'bad' value resulting to an error
|
| 117 |
+
assert_raises(ValueError, caller, func, ERROR_VALUE)
|
| 118 |
+
|
| 119 |
+
# Test passing in user_data
|
| 120 |
+
assert_equal(caller(func2, 1.0), 3.0)
|
| 121 |
+
|
| 122 |
+
for caller in sorted(CALLERS.keys()):
|
| 123 |
+
for func in sorted(FUNCS.keys()):
|
| 124 |
+
for user_data in sorted(USER_DATAS.keys()):
|
| 125 |
+
check(caller, func, user_data)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def test_bad_callbacks():
|
| 129 |
+
def check(caller, func, user_data):
|
| 130 |
+
caller = CALLERS[caller]
|
| 131 |
+
user_data = USER_DATAS[user_data]()
|
| 132 |
+
func = BAD_FUNCS[func]()
|
| 133 |
+
|
| 134 |
+
if func is callback_python:
|
| 135 |
+
def func2(x):
|
| 136 |
+
return func(x, 2.0)
|
| 137 |
+
else:
|
| 138 |
+
func2 = LowLevelCallable(func, user_data)
|
| 139 |
+
func = LowLevelCallable(func)
|
| 140 |
+
|
| 141 |
+
# Test that basic call fails
|
| 142 |
+
assert_raises(ValueError, caller, LowLevelCallable(func), 1.0)
|
| 143 |
+
|
| 144 |
+
# Test that passing in user_data also fails
|
| 145 |
+
assert_raises(ValueError, caller, func2, 1.0)
|
| 146 |
+
|
| 147 |
+
# Test error message
|
| 148 |
+
llfunc = LowLevelCallable(func)
|
| 149 |
+
try:
|
| 150 |
+
caller(llfunc, 1.0)
|
| 151 |
+
except ValueError as err:
|
| 152 |
+
msg = str(err)
|
| 153 |
+
assert_(llfunc.signature in msg, msg)
|
| 154 |
+
assert_('double (double, double, int *, void *)' in msg, msg)
|
| 155 |
+
|
| 156 |
+
for caller in sorted(CALLERS.keys()):
|
| 157 |
+
for func in sorted(BAD_FUNCS.keys()):
|
| 158 |
+
for user_data in sorted(USER_DATAS.keys()):
|
| 159 |
+
check(caller, func, user_data)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def test_signature_override():
|
| 163 |
+
caller = _test_ccallback.test_call_simple
|
| 164 |
+
func = _test_ccallback.test_get_plus1_capsule()
|
| 165 |
+
|
| 166 |
+
llcallable = LowLevelCallable(func, signature="bad signature")
|
| 167 |
+
assert_equal(llcallable.signature, "bad signature")
|
| 168 |
+
assert_raises(ValueError, caller, llcallable, 3)
|
| 169 |
+
|
| 170 |
+
llcallable = LowLevelCallable(func, signature="double (double, int *, void *)")
|
| 171 |
+
assert_equal(llcallable.signature, "double (double, int *, void *)")
|
| 172 |
+
assert_equal(caller(llcallable, 3), 4)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def test_threadsafety():
|
| 176 |
+
def callback(a, caller):
|
| 177 |
+
if a <= 0:
|
| 178 |
+
return 1
|
| 179 |
+
else:
|
| 180 |
+
res = caller(lambda x: callback(x, caller), a - 1)
|
| 181 |
+
return 2*res
|
| 182 |
+
|
| 183 |
+
def check(caller):
|
| 184 |
+
caller = CALLERS[caller]
|
| 185 |
+
|
| 186 |
+
results = []
|
| 187 |
+
|
| 188 |
+
count = 10
|
| 189 |
+
|
| 190 |
+
def run():
|
| 191 |
+
time.sleep(0.01)
|
| 192 |
+
r = caller(lambda x: callback(x, caller), count)
|
| 193 |
+
results.append(r)
|
| 194 |
+
|
| 195 |
+
threads = [threading.Thread(target=run) for j in range(20)]
|
| 196 |
+
for thread in threads:
|
| 197 |
+
thread.start()
|
| 198 |
+
for thread in threads:
|
| 199 |
+
thread.join()
|
| 200 |
+
|
| 201 |
+
assert_equal(results, [2.0**count]*len(threads))
|
| 202 |
+
|
| 203 |
+
for caller in CALLERS.keys():
|
| 204 |
+
check(caller)
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_deprecation.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_cython_api_deprecation():
|
| 5 |
+
match = ("`scipy._lib._test_deprecation_def.foo_deprecated` "
|
| 6 |
+
"is deprecated, use `foo` instead!\n"
|
| 7 |
+
"Deprecated in Scipy 42.0.0")
|
| 8 |
+
with pytest.warns(DeprecationWarning, match=match):
|
| 9 |
+
from .. import _test_deprecation_call
|
| 10 |
+
assert _test_deprecation_call.call() == (1, 1)
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_import_cycles.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import subprocess
|
| 3 |
+
|
| 4 |
+
from .test_public_api import PUBLIC_MODULES
|
| 5 |
+
|
| 6 |
+
# Regression tests for gh-6793.
|
| 7 |
+
# Check that all modules are importable in a new Python process.
|
| 8 |
+
# This is not necessarily true if there are import cycles present.
|
| 9 |
+
|
| 10 |
+
def test_public_modules_importable():
|
| 11 |
+
pids = [subprocess.Popen([sys.executable, '-c', f'import {module}'])
|
| 12 |
+
for module in PUBLIC_MODULES]
|
| 13 |
+
for i, pid in enumerate(pids):
|
| 14 |
+
assert pid.wait() == 0, f'Failed to import {PUBLIC_MODULES[i]}'
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_public_api.py
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This test script is adopted from:
|
| 3 |
+
https://github.com/numpy/numpy/blob/main/numpy/tests/test_public_api.py
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import pkgutil
|
| 7 |
+
import types
|
| 8 |
+
import importlib
|
| 9 |
+
import warnings
|
| 10 |
+
from importlib import import_module
|
| 11 |
+
|
| 12 |
+
import pytest
|
| 13 |
+
|
| 14 |
+
import scipy
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def test_dir_testing():
|
| 18 |
+
"""Assert that output of dir has only one "testing/tester"
|
| 19 |
+
attribute without duplicate"""
|
| 20 |
+
assert len(dir(scipy)) == len(set(dir(scipy)))
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
# Historically SciPy has not used leading underscores for private submodules
|
| 24 |
+
# much. This has resulted in lots of things that look like public modules
|
| 25 |
+
# (i.e. things that can be imported as `import scipy.somesubmodule.somefile`),
|
| 26 |
+
# but were never intended to be public. The PUBLIC_MODULES list contains
|
| 27 |
+
# modules that are either public because they were meant to be, or because they
|
| 28 |
+
# contain public functions/objects that aren't present in any other namespace
|
| 29 |
+
# for whatever reason and therefore should be treated as public.
|
| 30 |
+
PUBLIC_MODULES = ["scipy." + s for s in [
|
| 31 |
+
"cluster",
|
| 32 |
+
"cluster.vq",
|
| 33 |
+
"cluster.hierarchy",
|
| 34 |
+
"constants",
|
| 35 |
+
"datasets",
|
| 36 |
+
"fft",
|
| 37 |
+
"fftpack",
|
| 38 |
+
"integrate",
|
| 39 |
+
"interpolate",
|
| 40 |
+
"io",
|
| 41 |
+
"io.arff",
|
| 42 |
+
"io.matlab",
|
| 43 |
+
"io.wavfile",
|
| 44 |
+
"linalg",
|
| 45 |
+
"linalg.blas",
|
| 46 |
+
"linalg.cython_blas",
|
| 47 |
+
"linalg.lapack",
|
| 48 |
+
"linalg.cython_lapack",
|
| 49 |
+
"linalg.interpolative",
|
| 50 |
+
"misc",
|
| 51 |
+
"ndimage",
|
| 52 |
+
"odr",
|
| 53 |
+
"optimize",
|
| 54 |
+
"signal",
|
| 55 |
+
"signal.windows",
|
| 56 |
+
"sparse",
|
| 57 |
+
"sparse.linalg",
|
| 58 |
+
"sparse.csgraph",
|
| 59 |
+
"spatial",
|
| 60 |
+
"spatial.distance",
|
| 61 |
+
"spatial.transform",
|
| 62 |
+
"special",
|
| 63 |
+
"stats",
|
| 64 |
+
"stats.contingency",
|
| 65 |
+
"stats.distributions",
|
| 66 |
+
"stats.mstats",
|
| 67 |
+
"stats.qmc",
|
| 68 |
+
"stats.sampling"
|
| 69 |
+
]]
|
| 70 |
+
|
| 71 |
+
# The PRIVATE_BUT_PRESENT_MODULES list contains modules that lacked underscores
|
| 72 |
+
# in their name and hence looked public, but weren't meant to be. All these
|
| 73 |
+
# namespace were deprecated in the 1.8.0 release - see "clear split between
|
| 74 |
+
# public and private API" in the 1.8.0 release notes.
|
| 75 |
+
# These private modules support will be removed in SciPy v2.0.0, as the
|
| 76 |
+
# deprecation messages emitted by each of these modules say.
|
| 77 |
+
PRIVATE_BUT_PRESENT_MODULES = [
|
| 78 |
+
'scipy.constants.codata',
|
| 79 |
+
'scipy.constants.constants',
|
| 80 |
+
'scipy.fftpack.basic',
|
| 81 |
+
'scipy.fftpack.convolve',
|
| 82 |
+
'scipy.fftpack.helper',
|
| 83 |
+
'scipy.fftpack.pseudo_diffs',
|
| 84 |
+
'scipy.fftpack.realtransforms',
|
| 85 |
+
'scipy.integrate.dop',
|
| 86 |
+
'scipy.integrate.lsoda',
|
| 87 |
+
'scipy.integrate.odepack',
|
| 88 |
+
'scipy.integrate.quadpack',
|
| 89 |
+
'scipy.integrate.vode',
|
| 90 |
+
'scipy.interpolate.dfitpack',
|
| 91 |
+
'scipy.interpolate.fitpack',
|
| 92 |
+
'scipy.interpolate.fitpack2',
|
| 93 |
+
'scipy.interpolate.interpnd',
|
| 94 |
+
'scipy.interpolate.interpolate',
|
| 95 |
+
'scipy.interpolate.ndgriddata',
|
| 96 |
+
'scipy.interpolate.polyint',
|
| 97 |
+
'scipy.interpolate.rbf',
|
| 98 |
+
'scipy.io.arff.arffread',
|
| 99 |
+
'scipy.io.harwell_boeing',
|
| 100 |
+
'scipy.io.idl',
|
| 101 |
+
'scipy.io.matlab.byteordercodes',
|
| 102 |
+
'scipy.io.matlab.mio',
|
| 103 |
+
'scipy.io.matlab.mio4',
|
| 104 |
+
'scipy.io.matlab.mio5',
|
| 105 |
+
'scipy.io.matlab.mio5_params',
|
| 106 |
+
'scipy.io.matlab.mio5_utils',
|
| 107 |
+
'scipy.io.matlab.mio_utils',
|
| 108 |
+
'scipy.io.matlab.miobase',
|
| 109 |
+
'scipy.io.matlab.streams',
|
| 110 |
+
'scipy.io.mmio',
|
| 111 |
+
'scipy.io.netcdf',
|
| 112 |
+
'scipy.linalg.basic',
|
| 113 |
+
'scipy.linalg.decomp',
|
| 114 |
+
'scipy.linalg.decomp_cholesky',
|
| 115 |
+
'scipy.linalg.decomp_lu',
|
| 116 |
+
'scipy.linalg.decomp_qr',
|
| 117 |
+
'scipy.linalg.decomp_schur',
|
| 118 |
+
'scipy.linalg.decomp_svd',
|
| 119 |
+
'scipy.linalg.flinalg',
|
| 120 |
+
'scipy.linalg.matfuncs',
|
| 121 |
+
'scipy.linalg.misc',
|
| 122 |
+
'scipy.linalg.special_matrices',
|
| 123 |
+
'scipy.misc.common',
|
| 124 |
+
'scipy.misc.doccer',
|
| 125 |
+
'scipy.ndimage.filters',
|
| 126 |
+
'scipy.ndimage.fourier',
|
| 127 |
+
'scipy.ndimage.interpolation',
|
| 128 |
+
'scipy.ndimage.measurements',
|
| 129 |
+
'scipy.ndimage.morphology',
|
| 130 |
+
'scipy.odr.models',
|
| 131 |
+
'scipy.odr.odrpack',
|
| 132 |
+
'scipy.optimize.cobyla',
|
| 133 |
+
'scipy.optimize.cython_optimize',
|
| 134 |
+
'scipy.optimize.lbfgsb',
|
| 135 |
+
'scipy.optimize.linesearch',
|
| 136 |
+
'scipy.optimize.minpack',
|
| 137 |
+
'scipy.optimize.minpack2',
|
| 138 |
+
'scipy.optimize.moduleTNC',
|
| 139 |
+
'scipy.optimize.nonlin',
|
| 140 |
+
'scipy.optimize.optimize',
|
| 141 |
+
'scipy.optimize.slsqp',
|
| 142 |
+
'scipy.optimize.tnc',
|
| 143 |
+
'scipy.optimize.zeros',
|
| 144 |
+
'scipy.signal.bsplines',
|
| 145 |
+
'scipy.signal.filter_design',
|
| 146 |
+
'scipy.signal.fir_filter_design',
|
| 147 |
+
'scipy.signal.lti_conversion',
|
| 148 |
+
'scipy.signal.ltisys',
|
| 149 |
+
'scipy.signal.signaltools',
|
| 150 |
+
'scipy.signal.spectral',
|
| 151 |
+
'scipy.signal.spline',
|
| 152 |
+
'scipy.signal.waveforms',
|
| 153 |
+
'scipy.signal.wavelets',
|
| 154 |
+
'scipy.signal.windows.windows',
|
| 155 |
+
'scipy.sparse.base',
|
| 156 |
+
'scipy.sparse.bsr',
|
| 157 |
+
'scipy.sparse.compressed',
|
| 158 |
+
'scipy.sparse.construct',
|
| 159 |
+
'scipy.sparse.coo',
|
| 160 |
+
'scipy.sparse.csc',
|
| 161 |
+
'scipy.sparse.csr',
|
| 162 |
+
'scipy.sparse.data',
|
| 163 |
+
'scipy.sparse.dia',
|
| 164 |
+
'scipy.sparse.dok',
|
| 165 |
+
'scipy.sparse.extract',
|
| 166 |
+
'scipy.sparse.lil',
|
| 167 |
+
'scipy.sparse.linalg.dsolve',
|
| 168 |
+
'scipy.sparse.linalg.eigen',
|
| 169 |
+
'scipy.sparse.linalg.interface',
|
| 170 |
+
'scipy.sparse.linalg.isolve',
|
| 171 |
+
'scipy.sparse.linalg.matfuncs',
|
| 172 |
+
'scipy.sparse.sparsetools',
|
| 173 |
+
'scipy.sparse.spfuncs',
|
| 174 |
+
'scipy.sparse.sputils',
|
| 175 |
+
'scipy.spatial.ckdtree',
|
| 176 |
+
'scipy.spatial.kdtree',
|
| 177 |
+
'scipy.spatial.qhull',
|
| 178 |
+
'scipy.spatial.transform.rotation',
|
| 179 |
+
'scipy.special.add_newdocs',
|
| 180 |
+
'scipy.special.basic',
|
| 181 |
+
'scipy.special.cython_special',
|
| 182 |
+
'scipy.special.orthogonal',
|
| 183 |
+
'scipy.special.sf_error',
|
| 184 |
+
'scipy.special.specfun',
|
| 185 |
+
'scipy.special.spfun_stats',
|
| 186 |
+
'scipy.stats.biasedurn',
|
| 187 |
+
'scipy.stats.kde',
|
| 188 |
+
'scipy.stats.morestats',
|
| 189 |
+
'scipy.stats.mstats_basic',
|
| 190 |
+
'scipy.stats.mstats_extras',
|
| 191 |
+
'scipy.stats.mvn',
|
| 192 |
+
'scipy.stats.stats',
|
| 193 |
+
]
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
def is_unexpected(name):
|
| 197 |
+
"""Check if this needs to be considered."""
|
| 198 |
+
if '._' in name or '.tests' in name or '.setup' in name:
|
| 199 |
+
return False
|
| 200 |
+
|
| 201 |
+
if name in PUBLIC_MODULES:
|
| 202 |
+
return False
|
| 203 |
+
|
| 204 |
+
if name in PRIVATE_BUT_PRESENT_MODULES:
|
| 205 |
+
return False
|
| 206 |
+
|
| 207 |
+
return True
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
SKIP_LIST = [
|
| 211 |
+
'scipy.conftest',
|
| 212 |
+
'scipy.version',
|
| 213 |
+
]
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def test_all_modules_are_expected():
|
| 217 |
+
"""
|
| 218 |
+
Test that we don't add anything that looks like a new public module by
|
| 219 |
+
accident. Check is based on filenames.
|
| 220 |
+
"""
|
| 221 |
+
|
| 222 |
+
modnames = []
|
| 223 |
+
for _, modname, ispkg in pkgutil.walk_packages(path=scipy.__path__,
|
| 224 |
+
prefix=scipy.__name__ + '.',
|
| 225 |
+
onerror=None):
|
| 226 |
+
if is_unexpected(modname) and modname not in SKIP_LIST:
|
| 227 |
+
# We have a name that is new. If that's on purpose, add it to
|
| 228 |
+
# PUBLIC_MODULES. We don't expect to have to add anything to
|
| 229 |
+
# PRIVATE_BUT_PRESENT_MODULES. Use an underscore in the name!
|
| 230 |
+
modnames.append(modname)
|
| 231 |
+
|
| 232 |
+
if modnames:
|
| 233 |
+
raise AssertionError(f'Found unexpected modules: {modnames}')
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
# Stuff that clearly shouldn't be in the API and is detected by the next test
|
| 237 |
+
# below
|
| 238 |
+
SKIP_LIST_2 = [
|
| 239 |
+
'scipy.char',
|
| 240 |
+
'scipy.rec',
|
| 241 |
+
'scipy.emath',
|
| 242 |
+
'scipy.math',
|
| 243 |
+
'scipy.random',
|
| 244 |
+
'scipy.ctypeslib',
|
| 245 |
+
'scipy.ma'
|
| 246 |
+
]
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def test_all_modules_are_expected_2():
|
| 250 |
+
"""
|
| 251 |
+
Method checking all objects. The pkgutil-based method in
|
| 252 |
+
`test_all_modules_are_expected` does not catch imports into a namespace,
|
| 253 |
+
only filenames.
|
| 254 |
+
"""
|
| 255 |
+
|
| 256 |
+
def find_unexpected_members(mod_name):
|
| 257 |
+
members = []
|
| 258 |
+
module = importlib.import_module(mod_name)
|
| 259 |
+
if hasattr(module, '__all__'):
|
| 260 |
+
objnames = module.__all__
|
| 261 |
+
else:
|
| 262 |
+
objnames = dir(module)
|
| 263 |
+
|
| 264 |
+
for objname in objnames:
|
| 265 |
+
if not objname.startswith('_'):
|
| 266 |
+
fullobjname = mod_name + '.' + objname
|
| 267 |
+
if isinstance(getattr(module, objname), types.ModuleType):
|
| 268 |
+
if is_unexpected(fullobjname) and fullobjname not in SKIP_LIST_2:
|
| 269 |
+
members.append(fullobjname)
|
| 270 |
+
|
| 271 |
+
return members
|
| 272 |
+
|
| 273 |
+
unexpected_members = find_unexpected_members("scipy")
|
| 274 |
+
for modname in PUBLIC_MODULES:
|
| 275 |
+
unexpected_members.extend(find_unexpected_members(modname))
|
| 276 |
+
|
| 277 |
+
if unexpected_members:
|
| 278 |
+
raise AssertionError("Found unexpected object(s) that look like "
|
| 279 |
+
f"modules: {unexpected_members}")
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
def test_api_importable():
|
| 283 |
+
"""
|
| 284 |
+
Check that all submodules listed higher up in this file can be imported
|
| 285 |
+
Note that if a PRIVATE_BUT_PRESENT_MODULES entry goes missing, it may
|
| 286 |
+
simply need to be removed from the list (deprecation may or may not be
|
| 287 |
+
needed - apply common sense).
|
| 288 |
+
"""
|
| 289 |
+
def check_importable(module_name):
|
| 290 |
+
try:
|
| 291 |
+
importlib.import_module(module_name)
|
| 292 |
+
except (ImportError, AttributeError):
|
| 293 |
+
return False
|
| 294 |
+
|
| 295 |
+
return True
|
| 296 |
+
|
| 297 |
+
module_names = []
|
| 298 |
+
for module_name in PUBLIC_MODULES:
|
| 299 |
+
if not check_importable(module_name):
|
| 300 |
+
module_names.append(module_name)
|
| 301 |
+
|
| 302 |
+
if module_names:
|
| 303 |
+
raise AssertionError("Modules in the public API that cannot be "
|
| 304 |
+
f"imported: {module_names}")
|
| 305 |
+
|
| 306 |
+
with warnings.catch_warnings(record=True):
|
| 307 |
+
warnings.filterwarnings('always', category=DeprecationWarning)
|
| 308 |
+
warnings.filterwarnings('always', category=ImportWarning)
|
| 309 |
+
for module_name in PRIVATE_BUT_PRESENT_MODULES:
|
| 310 |
+
if not check_importable(module_name):
|
| 311 |
+
module_names.append(module_name)
|
| 312 |
+
|
| 313 |
+
if module_names:
|
| 314 |
+
raise AssertionError("Modules that are not really public but looked "
|
| 315 |
+
"public and can not be imported: "
|
| 316 |
+
f"{module_names}")
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
@pytest.mark.parametrize(("module_name", "correct_module"),
|
| 320 |
+
[('scipy.constants.codata', None),
|
| 321 |
+
('scipy.constants.constants', None),
|
| 322 |
+
('scipy.fftpack.basic', None),
|
| 323 |
+
('scipy.fftpack.helper', None),
|
| 324 |
+
('scipy.fftpack.pseudo_diffs', None),
|
| 325 |
+
('scipy.fftpack.realtransforms', None),
|
| 326 |
+
('scipy.integrate.dop', None),
|
| 327 |
+
('scipy.integrate.lsoda', None),
|
| 328 |
+
('scipy.integrate.odepack', None),
|
| 329 |
+
('scipy.integrate.quadpack', None),
|
| 330 |
+
('scipy.integrate.vode', None),
|
| 331 |
+
('scipy.interpolate.fitpack', None),
|
| 332 |
+
('scipy.interpolate.fitpack2', None),
|
| 333 |
+
('scipy.interpolate.interpolate', None),
|
| 334 |
+
('scipy.interpolate.ndgriddata', None),
|
| 335 |
+
('scipy.interpolate.polyint', None),
|
| 336 |
+
('scipy.interpolate.rbf', None),
|
| 337 |
+
('scipy.io.harwell_boeing', None),
|
| 338 |
+
('scipy.io.idl', None),
|
| 339 |
+
('scipy.io.mmio', None),
|
| 340 |
+
('scipy.io.netcdf', None),
|
| 341 |
+
('scipy.io.arff.arffread', 'arff'),
|
| 342 |
+
('scipy.io.matlab.byteordercodes', 'matlab'),
|
| 343 |
+
('scipy.io.matlab.mio_utils', 'matlab'),
|
| 344 |
+
('scipy.io.matlab.mio', 'matlab'),
|
| 345 |
+
('scipy.io.matlab.mio4', 'matlab'),
|
| 346 |
+
('scipy.io.matlab.mio5_params', 'matlab'),
|
| 347 |
+
('scipy.io.matlab.mio5_utils', 'matlab'),
|
| 348 |
+
('scipy.io.matlab.mio5', 'matlab'),
|
| 349 |
+
('scipy.io.matlab.miobase', 'matlab'),
|
| 350 |
+
('scipy.io.matlab.streams', 'matlab'),
|
| 351 |
+
('scipy.linalg.basic', None),
|
| 352 |
+
('scipy.linalg.decomp', None),
|
| 353 |
+
('scipy.linalg.decomp_cholesky', None),
|
| 354 |
+
('scipy.linalg.decomp_lu', None),
|
| 355 |
+
('scipy.linalg.decomp_qr', None),
|
| 356 |
+
('scipy.linalg.decomp_schur', None),
|
| 357 |
+
('scipy.linalg.decomp_svd', None),
|
| 358 |
+
('scipy.linalg.flinalg', None),
|
| 359 |
+
('scipy.linalg.matfuncs', None),
|
| 360 |
+
('scipy.linalg.misc', None),
|
| 361 |
+
('scipy.linalg.special_matrices', None),
|
| 362 |
+
('scipy.misc.common', None),
|
| 363 |
+
('scipy.ndimage.filters', None),
|
| 364 |
+
('scipy.ndimage.fourier', None),
|
| 365 |
+
('scipy.ndimage.interpolation', None),
|
| 366 |
+
('scipy.ndimage.measurements', None),
|
| 367 |
+
('scipy.ndimage.morphology', None),
|
| 368 |
+
('scipy.odr.models', None),
|
| 369 |
+
('scipy.odr.odrpack', None),
|
| 370 |
+
('scipy.optimize.cobyla', None),
|
| 371 |
+
('scipy.optimize.lbfgsb', None),
|
| 372 |
+
('scipy.optimize.linesearch', None),
|
| 373 |
+
('scipy.optimize.minpack', None),
|
| 374 |
+
('scipy.optimize.minpack2', None),
|
| 375 |
+
('scipy.optimize.moduleTNC', None),
|
| 376 |
+
('scipy.optimize.nonlin', None),
|
| 377 |
+
('scipy.optimize.optimize', None),
|
| 378 |
+
('scipy.optimize.slsqp', None),
|
| 379 |
+
('scipy.optimize.tnc', None),
|
| 380 |
+
('scipy.optimize.zeros', None),
|
| 381 |
+
('scipy.signal.bsplines', None),
|
| 382 |
+
('scipy.signal.filter_design', None),
|
| 383 |
+
('scipy.signal.fir_filter_design', None),
|
| 384 |
+
('scipy.signal.lti_conversion', None),
|
| 385 |
+
('scipy.signal.ltisys', None),
|
| 386 |
+
('scipy.signal.signaltools', None),
|
| 387 |
+
('scipy.signal.spectral', None),
|
| 388 |
+
('scipy.signal.waveforms', None),
|
| 389 |
+
('scipy.signal.wavelets', None),
|
| 390 |
+
('scipy.signal.windows.windows', 'windows'),
|
| 391 |
+
('scipy.sparse.lil', None),
|
| 392 |
+
('scipy.sparse.linalg.dsolve', 'linalg'),
|
| 393 |
+
('scipy.sparse.linalg.eigen', 'linalg'),
|
| 394 |
+
('scipy.sparse.linalg.interface', 'linalg'),
|
| 395 |
+
('scipy.sparse.linalg.isolve', 'linalg'),
|
| 396 |
+
('scipy.sparse.linalg.matfuncs', 'linalg'),
|
| 397 |
+
('scipy.sparse.sparsetools', None),
|
| 398 |
+
('scipy.sparse.spfuncs', None),
|
| 399 |
+
('scipy.sparse.sputils', None),
|
| 400 |
+
('scipy.spatial.ckdtree', None),
|
| 401 |
+
('scipy.spatial.kdtree', None),
|
| 402 |
+
('scipy.spatial.qhull', None),
|
| 403 |
+
('scipy.spatial.transform.rotation', 'transform'),
|
| 404 |
+
('scipy.special.add_newdocs', None),
|
| 405 |
+
('scipy.special.basic', None),
|
| 406 |
+
('scipy.special.orthogonal', None),
|
| 407 |
+
('scipy.special.sf_error', None),
|
| 408 |
+
('scipy.special.specfun', None),
|
| 409 |
+
('scipy.special.spfun_stats', None),
|
| 410 |
+
('scipy.stats.biasedurn', None),
|
| 411 |
+
('scipy.stats.kde', None),
|
| 412 |
+
('scipy.stats.morestats', None),
|
| 413 |
+
('scipy.stats.mstats_basic', 'mstats'),
|
| 414 |
+
('scipy.stats.mstats_extras', 'mstats'),
|
| 415 |
+
('scipy.stats.mvn', None),
|
| 416 |
+
('scipy.stats.stats', None)])
|
| 417 |
+
def test_private_but_present_deprecation(module_name, correct_module):
|
| 418 |
+
# gh-18279, gh-17572, gh-17771 noted that deprecation warnings
|
| 419 |
+
# for imports from private modules
|
| 420 |
+
# were misleading. Check that this is resolved.
|
| 421 |
+
module = import_module(module_name)
|
| 422 |
+
if correct_module is None:
|
| 423 |
+
import_name = f'scipy.{module_name.split(".")[1]}'
|
| 424 |
+
else:
|
| 425 |
+
import_name = f'scipy.{module_name.split(".")[1]}.{correct_module}'
|
| 426 |
+
|
| 427 |
+
correct_import = import_module(import_name)
|
| 428 |
+
|
| 429 |
+
# Attributes that were formerly in `module_name` can still be imported from
|
| 430 |
+
# `module_name`, albeit with a deprecation warning. The specific message
|
| 431 |
+
# depends on whether the attribute is public in `scipy.xxx` or not.
|
| 432 |
+
for attr_name in module.__all__:
|
| 433 |
+
attr = getattr(correct_import, attr_name, None)
|
| 434 |
+
if attr is None:
|
| 435 |
+
message = f"`{module_name}.{attr_name}` is deprecated..."
|
| 436 |
+
else:
|
| 437 |
+
message = f"Please import `{attr_name}` from the `{import_name}`..."
|
| 438 |
+
with pytest.deprecated_call(match=message):
|
| 439 |
+
getattr(module, attr_name)
|
| 440 |
+
|
| 441 |
+
# Attributes that were not in `module_name` get an error notifying the user
|
| 442 |
+
# that the attribute is not in `module_name` and that `module_name` is deprecated.
|
| 443 |
+
message = f"`{module_name}` is deprecated..."
|
| 444 |
+
with pytest.raises(AttributeError, match=message):
|
| 445 |
+
getattr(module, "ekki")
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def test_misc_doccer_deprecation():
|
| 449 |
+
# gh-18279, gh-17572, gh-17771 noted that deprecation warnings
|
| 450 |
+
# for imports from private modules were misleading.
|
| 451 |
+
# Check that this is resolved.
|
| 452 |
+
# `test_private_but_present_deprecation` cannot be used since `correct_import`
|
| 453 |
+
# is a different subpackage (`_lib` instead of `misc`).
|
| 454 |
+
module = import_module('scipy.misc.doccer')
|
| 455 |
+
correct_import = import_module('scipy._lib.doccer')
|
| 456 |
+
|
| 457 |
+
# Attributes that were formerly in `scipy.misc.doccer` can still be imported from
|
| 458 |
+
# `scipy.misc.doccer`, albeit with a deprecation warning. The specific message
|
| 459 |
+
# depends on whether the attribute is in `scipy._lib.doccer` or not.
|
| 460 |
+
for attr_name in module.__all__:
|
| 461 |
+
attr = getattr(correct_import, attr_name, None)
|
| 462 |
+
if attr is None:
|
| 463 |
+
message = f"`scipy.misc.{attr_name}` is deprecated..."
|
| 464 |
+
else:
|
| 465 |
+
message = f"Please import `{attr_name}` from the `scipy._lib.doccer`..."
|
| 466 |
+
with pytest.deprecated_call(match=message):
|
| 467 |
+
getattr(module, attr_name)
|
| 468 |
+
|
| 469 |
+
# Attributes that were not in `scipy.misc.doccer` get an error
|
| 470 |
+
# notifying the user that the attribute is not in `scipy.misc.doccer`
|
| 471 |
+
# and that `scipy.misc.doccer` is deprecated.
|
| 472 |
+
message = "`scipy.misc.doccer` is deprecated..."
|
| 473 |
+
with pytest.raises(AttributeError, match=message):
|
| 474 |
+
getattr(module, "ekki")
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_scipy_version.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
|
| 3 |
+
import scipy
|
| 4 |
+
from numpy.testing import assert_
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def test_valid_scipy_version():
|
| 8 |
+
# Verify that the SciPy version is a valid one (no .post suffix or other
|
| 9 |
+
# nonsense). See NumPy issue gh-6431 for an issue caused by an invalid
|
| 10 |
+
# version.
|
| 11 |
+
version_pattern = r"^[0-9]+\.[0-9]+\.[0-9]+(|a[0-9]|b[0-9]|rc[0-9])"
|
| 12 |
+
dev_suffix = r"(\.dev0\+.+([0-9a-f]{7}|Unknown))"
|
| 13 |
+
if scipy.version.release:
|
| 14 |
+
res = re.match(version_pattern, scipy.__version__)
|
| 15 |
+
else:
|
| 16 |
+
res = re.match(version_pattern + dev_suffix, scipy.__version__)
|
| 17 |
+
|
| 18 |
+
assert_(res is not None, scipy.__version__)
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_tmpdirs.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Test tmpdirs module """
|
| 2 |
+
from os import getcwd
|
| 3 |
+
from os.path import realpath, abspath, dirname, isfile, join as pjoin, exists
|
| 4 |
+
|
| 5 |
+
from scipy._lib._tmpdirs import tempdir, in_tempdir, in_dir
|
| 6 |
+
|
| 7 |
+
from numpy.testing import assert_, assert_equal
|
| 8 |
+
|
| 9 |
+
MY_PATH = abspath(__file__)
|
| 10 |
+
MY_DIR = dirname(MY_PATH)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def test_tempdir():
|
| 14 |
+
with tempdir() as tmpdir:
|
| 15 |
+
fname = pjoin(tmpdir, 'example_file.txt')
|
| 16 |
+
with open(fname, "w") as fobj:
|
| 17 |
+
fobj.write('a string\\n')
|
| 18 |
+
assert_(not exists(tmpdir))
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_in_tempdir():
|
| 22 |
+
my_cwd = getcwd()
|
| 23 |
+
with in_tempdir() as tmpdir:
|
| 24 |
+
with open('test.txt', "w") as f:
|
| 25 |
+
f.write('some text')
|
| 26 |
+
assert_(isfile('test.txt'))
|
| 27 |
+
assert_(isfile(pjoin(tmpdir, 'test.txt')))
|
| 28 |
+
assert_(not exists(tmpdir))
|
| 29 |
+
assert_equal(getcwd(), my_cwd)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def test_given_directory():
|
| 33 |
+
# Test InGivenDirectory
|
| 34 |
+
cwd = getcwd()
|
| 35 |
+
with in_dir() as tmpdir:
|
| 36 |
+
assert_equal(tmpdir, abspath(cwd))
|
| 37 |
+
assert_equal(tmpdir, abspath(getcwd()))
|
| 38 |
+
with in_dir(MY_DIR) as tmpdir:
|
| 39 |
+
assert_equal(tmpdir, MY_DIR)
|
| 40 |
+
assert_equal(realpath(MY_DIR), realpath(abspath(getcwd())))
|
| 41 |
+
# We were deleting the given directory! Check not so now.
|
| 42 |
+
assert_(isfile(MY_PATH))
|
rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_warnings.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Tests which scan for certain occurrences in the code, they may not find
|
| 3 |
+
all of these occurrences but should catch almost all. This file was adapted
|
| 4 |
+
from NumPy.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
import ast
|
| 11 |
+
import tokenize
|
| 12 |
+
|
| 13 |
+
import scipy
|
| 14 |
+
|
| 15 |
+
import pytest
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class ParseCall(ast.NodeVisitor):
|
| 19 |
+
def __init__(self):
|
| 20 |
+
self.ls = []
|
| 21 |
+
|
| 22 |
+
def visit_Attribute(self, node):
|
| 23 |
+
ast.NodeVisitor.generic_visit(self, node)
|
| 24 |
+
self.ls.append(node.attr)
|
| 25 |
+
|
| 26 |
+
def visit_Name(self, node):
|
| 27 |
+
self.ls.append(node.id)
|
| 28 |
+
|
| 29 |
+
class FindFuncs(ast.NodeVisitor):
|
| 30 |
+
def __init__(self, filename):
|
| 31 |
+
super().__init__()
|
| 32 |
+
self.__filename = filename
|
| 33 |
+
self.bad_filters = []
|
| 34 |
+
self.bad_stacklevels = []
|
| 35 |
+
|
| 36 |
+
def visit_Call(self, node):
|
| 37 |
+
p = ParseCall()
|
| 38 |
+
p.visit(node.func)
|
| 39 |
+
ast.NodeVisitor.generic_visit(self, node)
|
| 40 |
+
|
| 41 |
+
if p.ls[-1] == 'simplefilter' or p.ls[-1] == 'filterwarnings':
|
| 42 |
+
if node.args[0].value == "ignore":
|
| 43 |
+
self.bad_filters.append(
|
| 44 |
+
f"{self.__filename}:{node.lineno}")
|
| 45 |
+
|
| 46 |
+
if p.ls[-1] == 'warn' and (
|
| 47 |
+
len(p.ls) == 1 or p.ls[-2] == 'warnings'):
|
| 48 |
+
|
| 49 |
+
if self.__filename == "_lib/tests/test_warnings.py":
|
| 50 |
+
# This file
|
| 51 |
+
return
|
| 52 |
+
|
| 53 |
+
# See if stacklevel exists:
|
| 54 |
+
if len(node.args) == 3:
|
| 55 |
+
return
|
| 56 |
+
args = {kw.arg for kw in node.keywords}
|
| 57 |
+
if "stacklevel" not in args:
|
| 58 |
+
self.bad_stacklevels.append(
|
| 59 |
+
f"{self.__filename}:{node.lineno}")
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
@pytest.fixture(scope="session")
|
| 63 |
+
def warning_calls():
|
| 64 |
+
# combined "ignore" and stacklevel error
|
| 65 |
+
base = Path(scipy.__file__).parent
|
| 66 |
+
|
| 67 |
+
bad_filters = []
|
| 68 |
+
bad_stacklevels = []
|
| 69 |
+
|
| 70 |
+
for path in base.rglob("*.py"):
|
| 71 |
+
# use tokenize to auto-detect encoding on systems where no
|
| 72 |
+
# default encoding is defined (e.g., LANG='C')
|
| 73 |
+
with tokenize.open(str(path)) as file:
|
| 74 |
+
tree = ast.parse(file.read(), filename=str(path))
|
| 75 |
+
finder = FindFuncs(path.relative_to(base))
|
| 76 |
+
finder.visit(tree)
|
| 77 |
+
bad_filters.extend(finder.bad_filters)
|
| 78 |
+
bad_stacklevels.extend(finder.bad_stacklevels)
|
| 79 |
+
|
| 80 |
+
return bad_filters, bad_stacklevels
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
@pytest.mark.slow
|
| 84 |
+
def test_warning_calls_filters(warning_calls):
|
| 85 |
+
bad_filters, bad_stacklevels = warning_calls
|
| 86 |
+
|
| 87 |
+
# We try not to add filters in the code base, because those filters aren't
|
| 88 |
+
# thread-safe. We aim to only filter in tests with
|
| 89 |
+
# np.testing.suppress_warnings. However, in some cases it may prove
|
| 90 |
+
# necessary to filter out warnings, because we can't (easily) fix the root
|
| 91 |
+
# cause for them and we don't want users to see some warnings when they use
|
| 92 |
+
# SciPy correctly. So we list exceptions here. Add new entries only if
|
| 93 |
+
# there's a good reason.
|
| 94 |
+
allowed_filters = (
|
| 95 |
+
os.path.join('datasets', '_fetchers.py'),
|
| 96 |
+
os.path.join('datasets', '__init__.py'),
|
| 97 |
+
os.path.join('optimize', '_optimize.py'),
|
| 98 |
+
os.path.join('optimize', '_constraints.py'),
|
| 99 |
+
os.path.join('signal', '_ltisys.py'),
|
| 100 |
+
os.path.join('sparse', '__init__.py'), # np.matrix pending-deprecation
|
| 101 |
+
os.path.join('stats', '_discrete_distns.py'), # gh-14901
|
| 102 |
+
os.path.join('stats', '_continuous_distns.py'),
|
| 103 |
+
os.path.join('stats', '_binned_statistic.py'), # gh-19345
|
| 104 |
+
os.path.join('_lib', '_util.py'), # gh-19341
|
| 105 |
+
)
|
| 106 |
+
bad_filters = [item for item in bad_filters if item.split(':')[0] not in
|
| 107 |
+
allowed_filters]
|
| 108 |
+
|
| 109 |
+
if bad_filters:
|
| 110 |
+
raise AssertionError(
|
| 111 |
+
"warning ignore filter should not be used, instead, use\n"
|
| 112 |
+
"numpy.testing.suppress_warnings (in tests only);\n"
|
| 113 |
+
"found in:\n {}".format(
|
| 114 |
+
"\n ".join(bad_filters)))
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
@pytest.mark.slow
|
| 118 |
+
@pytest.mark.xfail(reason="stacklevels currently missing")
|
| 119 |
+
def test_warning_calls_stacklevels(warning_calls):
|
| 120 |
+
bad_filters, bad_stacklevels = warning_calls
|
| 121 |
+
|
| 122 |
+
msg = ""
|
| 123 |
+
|
| 124 |
+
if bad_filters:
|
| 125 |
+
msg += ("warning ignore filter should not be used, instead, use\n"
|
| 126 |
+
"numpy.testing.suppress_warnings (in tests only);\n"
|
| 127 |
+
"found in:\n {}".format("\n ".join(bad_filters)))
|
| 128 |
+
msg += "\n\n"
|
| 129 |
+
|
| 130 |
+
if bad_stacklevels:
|
| 131 |
+
msg += "warnings should have an appropriate stacklevel:\n {}".format(
|
| 132 |
+
"\n ".join(bad_stacklevels))
|
| 133 |
+
|
| 134 |
+
if msg:
|
| 135 |
+
raise AssertionError(msg)
|
rtme/lib/python3.10/site-packages/scipy/_lib/uarray.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""`uarray` provides functions for generating multimethods that dispatch to
|
| 2 |
+
multiple different backends
|
| 3 |
+
|
| 4 |
+
This should be imported, rather than `_uarray` so that an installed version could
|
| 5 |
+
be used instead, if available. This means that users can call
|
| 6 |
+
`uarray.set_backend` directly instead of going through SciPy.
|
| 7 |
+
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# Prefer an installed version of uarray, if available
|
| 12 |
+
try:
|
| 13 |
+
import uarray as _uarray
|
| 14 |
+
except ImportError:
|
| 15 |
+
_has_uarray = False
|
| 16 |
+
else:
|
| 17 |
+
from scipy._lib._pep440 import Version as _Version
|
| 18 |
+
|
| 19 |
+
_has_uarray = _Version(_uarray.__version__) >= _Version("0.8")
|
| 20 |
+
del _uarray
|
| 21 |
+
del _Version
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
if _has_uarray:
|
| 25 |
+
from uarray import * # noqa: F403
|
| 26 |
+
from uarray import _Function
|
| 27 |
+
else:
|
| 28 |
+
from ._uarray import * # noqa: F403
|
| 29 |
+
from ._uarray import _Function # noqa: F401
|
| 30 |
+
|
| 31 |
+
del _has_uarray
|
rtme/lib/python3.10/site-packages/scipy/cluster/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
=========================================
|
| 3 |
+
Clustering package (:mod:`scipy.cluster`)
|
| 4 |
+
=========================================
|
| 5 |
+
|
| 6 |
+
.. currentmodule:: scipy.cluster
|
| 7 |
+
|
| 8 |
+
.. toctree::
|
| 9 |
+
:hidden:
|
| 10 |
+
|
| 11 |
+
cluster.vq
|
| 12 |
+
cluster.hierarchy
|
| 13 |
+
|
| 14 |
+
Clustering algorithms are useful in information theory, target detection,
|
| 15 |
+
communications, compression, and other areas. The `vq` module only
|
| 16 |
+
supports vector quantization and the k-means algorithms.
|
| 17 |
+
|
| 18 |
+
The `hierarchy` module provides functions for hierarchical and
|
| 19 |
+
agglomerative clustering. Its features include generating hierarchical
|
| 20 |
+
clusters from distance matrices,
|
| 21 |
+
calculating statistics on clusters, cutting linkages
|
| 22 |
+
to generate flat clusters, and visualizing clusters with dendrograms.
|
| 23 |
+
|
| 24 |
+
"""
|
| 25 |
+
__all__ = ['vq', 'hierarchy']
|
| 26 |
+
|
| 27 |
+
from . import vq, hierarchy
|
| 28 |
+
|
| 29 |
+
from scipy._lib._testutils import PytestTester
|
| 30 |
+
test = PytestTester(__name__)
|
| 31 |
+
del PytestTester
|
rtme/lib/python3.10/site-packages/scipy/cluster/hierarchy.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
rtme/lib/python3.10/site-packages/scipy/cluster/tests/__init__.py
ADDED
|
File without changes
|
rtme/lib/python3.10/site-packages/scipy/cluster/tests/hierarchy_test_data.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from numpy import array
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
Q_X = array([[5.26563660e-01, 3.14160190e-01, 8.00656370e-02],
|
| 5 |
+
[7.50205180e-01, 4.60299830e-01, 8.98696460e-01],
|
| 6 |
+
[6.65461230e-01, 6.94011420e-01, 9.10465700e-01],
|
| 7 |
+
[9.64047590e-01, 1.43082200e-03, 7.39874220e-01],
|
| 8 |
+
[1.08159060e-01, 5.53028790e-01, 6.63804780e-02],
|
| 9 |
+
[9.31359130e-01, 8.25424910e-01, 9.52315440e-01],
|
| 10 |
+
[6.78086960e-01, 3.41903970e-01, 5.61481950e-01],
|
| 11 |
+
[9.82730940e-01, 7.04605210e-01, 8.70978630e-02],
|
| 12 |
+
[6.14691610e-01, 4.69989230e-02, 6.02406450e-01],
|
| 13 |
+
[5.80161260e-01, 9.17354970e-01, 5.88163850e-01],
|
| 14 |
+
[1.38246310e+00, 1.96358160e+00, 1.94437880e+00],
|
| 15 |
+
[2.10675860e+00, 1.67148730e+00, 1.34854480e+00],
|
| 16 |
+
[1.39880070e+00, 1.66142050e+00, 1.32224550e+00],
|
| 17 |
+
[1.71410460e+00, 1.49176380e+00, 1.45432170e+00],
|
| 18 |
+
[1.54102340e+00, 1.84374950e+00, 1.64658950e+00],
|
| 19 |
+
[2.08512480e+00, 1.84524350e+00, 2.17340850e+00],
|
| 20 |
+
[1.30748740e+00, 1.53801650e+00, 2.16007740e+00],
|
| 21 |
+
[1.41447700e+00, 1.99329070e+00, 1.99107420e+00],
|
| 22 |
+
[1.61943490e+00, 1.47703280e+00, 1.89788160e+00],
|
| 23 |
+
[1.59880600e+00, 1.54988980e+00, 1.57563350e+00],
|
| 24 |
+
[3.37247380e+00, 2.69635310e+00, 3.39981700e+00],
|
| 25 |
+
[3.13705120e+00, 3.36528090e+00, 3.06089070e+00],
|
| 26 |
+
[3.29413250e+00, 3.19619500e+00, 2.90700170e+00],
|
| 27 |
+
[2.65510510e+00, 3.06785900e+00, 2.97198540e+00],
|
| 28 |
+
[3.30941040e+00, 2.59283970e+00, 2.57714110e+00],
|
| 29 |
+
[2.59557220e+00, 3.33477370e+00, 3.08793190e+00],
|
| 30 |
+
[2.58206180e+00, 3.41615670e+00, 3.26441990e+00],
|
| 31 |
+
[2.71127000e+00, 2.77032450e+00, 2.63466500e+00],
|
| 32 |
+
[2.79617850e+00, 3.25473720e+00, 3.41801560e+00],
|
| 33 |
+
[2.64741750e+00, 2.54538040e+00, 3.25354110e+00]])
|
| 34 |
+
|
| 35 |
+
ytdist = array([662., 877., 255., 412., 996., 295., 468., 268., 400., 754.,
|
| 36 |
+
564., 138., 219., 869., 669.])
|
| 37 |
+
|
| 38 |
+
linkage_ytdist_single = array([[2., 5., 138., 2.],
|
| 39 |
+
[3., 4., 219., 2.],
|
| 40 |
+
[0., 7., 255., 3.],
|
| 41 |
+
[1., 8., 268., 4.],
|
| 42 |
+
[6., 9., 295., 6.]])
|
| 43 |
+
|
| 44 |
+
linkage_ytdist_complete = array([[2., 5., 138., 2.],
|
| 45 |
+
[3., 4., 219., 2.],
|
| 46 |
+
[1., 6., 400., 3.],
|
| 47 |
+
[0., 7., 412., 3.],
|
| 48 |
+
[8., 9., 996., 6.]])
|
| 49 |
+
|
| 50 |
+
linkage_ytdist_average = array([[2., 5., 138., 2.],
|
| 51 |
+
[3., 4., 219., 2.],
|
| 52 |
+
[0., 7., 333.5, 3.],
|
| 53 |
+
[1., 6., 347.5, 3.],
|
| 54 |
+
[8., 9., 680.77777778, 6.]])
|
| 55 |
+
|
| 56 |
+
linkage_ytdist_weighted = array([[2., 5., 138., 2.],
|
| 57 |
+
[3., 4., 219., 2.],
|
| 58 |
+
[0., 7., 333.5, 3.],
|
| 59 |
+
[1., 6., 347.5, 3.],
|
| 60 |
+
[8., 9., 670.125, 6.]])
|
| 61 |
+
|
| 62 |
+
# the optimal leaf ordering of linkage_ytdist_single
|
| 63 |
+
linkage_ytdist_single_olo = array([[5., 2., 138., 2.],
|
| 64 |
+
[4., 3., 219., 2.],
|
| 65 |
+
[7., 0., 255., 3.],
|
| 66 |
+
[1., 8., 268., 4.],
|
| 67 |
+
[6., 9., 295., 6.]])
|
| 68 |
+
|
| 69 |
+
X = array([[1.43054825, -7.5693489],
|
| 70 |
+
[6.95887839, 6.82293382],
|
| 71 |
+
[2.87137846, -9.68248579],
|
| 72 |
+
[7.87974764, -6.05485803],
|
| 73 |
+
[8.24018364, -6.09495602],
|
| 74 |
+
[7.39020262, 8.54004355]])
|
| 75 |
+
|
| 76 |
+
linkage_X_centroid = array([[3., 4., 0.36265956, 2.],
|
| 77 |
+
[1., 5., 1.77045373, 2.],
|
| 78 |
+
[0., 2., 2.55760419, 2.],
|
| 79 |
+
[6., 8., 6.43614494, 4.],
|
| 80 |
+
[7., 9., 15.17363237, 6.]])
|
| 81 |
+
|
| 82 |
+
linkage_X_median = array([[3., 4., 0.36265956, 2.],
|
| 83 |
+
[1., 5., 1.77045373, 2.],
|
| 84 |
+
[0., 2., 2.55760419, 2.],
|
| 85 |
+
[6., 8., 6.43614494, 4.],
|
| 86 |
+
[7., 9., 15.17363237, 6.]])
|
| 87 |
+
|
| 88 |
+
linkage_X_ward = array([[3., 4., 0.36265956, 2.],
|
| 89 |
+
[1., 5., 1.77045373, 2.],
|
| 90 |
+
[0., 2., 2.55760419, 2.],
|
| 91 |
+
[6., 8., 9.10208346, 4.],
|
| 92 |
+
[7., 9., 24.7784379, 6.]])
|
| 93 |
+
|
| 94 |
+
# the optimal leaf ordering of linkage_X_ward
|
| 95 |
+
linkage_X_ward_olo = array([[4., 3., 0.36265956, 2.],
|
| 96 |
+
[5., 1., 1.77045373, 2.],
|
| 97 |
+
[2., 0., 2.55760419, 2.],
|
| 98 |
+
[6., 8., 9.10208346, 4.],
|
| 99 |
+
[7., 9., 24.7784379, 6.]])
|
| 100 |
+
|
| 101 |
+
inconsistent_ytdist = {
|
| 102 |
+
1: array([[138., 0., 1., 0.],
|
| 103 |
+
[219., 0., 1., 0.],
|
| 104 |
+
[255., 0., 1., 0.],
|
| 105 |
+
[268., 0., 1., 0.],
|
| 106 |
+
[295., 0., 1., 0.]]),
|
| 107 |
+
2: array([[138., 0., 1., 0.],
|
| 108 |
+
[219., 0., 1., 0.],
|
| 109 |
+
[237., 25.45584412, 2., 0.70710678],
|
| 110 |
+
[261.5, 9.19238816, 2., 0.70710678],
|
| 111 |
+
[233.66666667, 83.9424406, 3., 0.7306594]]),
|
| 112 |
+
3: array([[138., 0., 1., 0.],
|
| 113 |
+
[219., 0., 1., 0.],
|
| 114 |
+
[237., 25.45584412, 2., 0.70710678],
|
| 115 |
+
[247.33333333, 25.38372182, 3., 0.81417007],
|
| 116 |
+
[239., 69.36377537, 4., 0.80733783]]),
|
| 117 |
+
4: array([[138., 0., 1., 0.],
|
| 118 |
+
[219., 0., 1., 0.],
|
| 119 |
+
[237., 25.45584412, 2., 0.70710678],
|
| 120 |
+
[247.33333333, 25.38372182, 3., 0.81417007],
|
| 121 |
+
[235., 60.73302232, 5., 0.98793042]])}
|
| 122 |
+
|
| 123 |
+
fcluster_inconsistent = {
|
| 124 |
+
0.8: array([6, 2, 2, 4, 6, 2, 3, 7, 3, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1,
|
| 125 |
+
1, 1, 1, 1, 1, 1, 1, 1, 1]),
|
| 126 |
+
1.0: array([6, 2, 2, 4, 6, 2, 3, 7, 3, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1,
|
| 127 |
+
1, 1, 1, 1, 1, 1, 1, 1, 1]),
|
| 128 |
+
2.0: array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
| 129 |
+
1, 1, 1, 1, 1, 1, 1, 1, 1])}
|
| 130 |
+
|
| 131 |
+
fcluster_distance = {
|
| 132 |
+
0.6: array([4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 3,
|
| 133 |
+
1, 1, 1, 2, 1, 1, 1, 1, 1]),
|
| 134 |
+
1.0: array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1,
|
| 135 |
+
1, 1, 1, 1, 1, 1, 1, 1, 1]),
|
| 136 |
+
2.0: array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
| 137 |
+
1, 1, 1, 1, 1, 1, 1, 1, 1])}
|
| 138 |
+
|
| 139 |
+
fcluster_maxclust = {
|
| 140 |
+
8.0: array([5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 7, 7, 7, 7, 7, 8, 7, 7, 7, 7, 4,
|
| 141 |
+
1, 1, 1, 3, 1, 1, 1, 1, 2]),
|
| 142 |
+
4.0: array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2,
|
| 143 |
+
1, 1, 1, 1, 1, 1, 1, 1, 1]),
|
| 144 |
+
1.0: array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
| 145 |
+
1, 1, 1, 1, 1, 1, 1, 1, 1])}
|
rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_disjoint_set.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
from pytest import raises as assert_raises
|
| 3 |
+
import numpy as np
|
| 4 |
+
from scipy.cluster.hierarchy import DisjointSet
|
| 5 |
+
import string
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def generate_random_token():
|
| 9 |
+
k = len(string.ascii_letters)
|
| 10 |
+
tokens = list(np.arange(k, dtype=int))
|
| 11 |
+
tokens += list(np.arange(k, dtype=float))
|
| 12 |
+
tokens += list(string.ascii_letters)
|
| 13 |
+
tokens += [None for i in range(k)]
|
| 14 |
+
tokens = np.array(tokens, dtype=object)
|
| 15 |
+
rng = np.random.RandomState(seed=0)
|
| 16 |
+
|
| 17 |
+
while 1:
|
| 18 |
+
size = rng.randint(1, 3)
|
| 19 |
+
element = rng.choice(tokens, size)
|
| 20 |
+
if size == 1:
|
| 21 |
+
yield element[0]
|
| 22 |
+
else:
|
| 23 |
+
yield tuple(element)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def get_elements(n):
|
| 27 |
+
# dict is deterministic without difficulty of comparing numpy ints
|
| 28 |
+
elements = {}
|
| 29 |
+
for element in generate_random_token():
|
| 30 |
+
if element not in elements:
|
| 31 |
+
elements[element] = len(elements)
|
| 32 |
+
if len(elements) >= n:
|
| 33 |
+
break
|
| 34 |
+
return list(elements.keys())
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def test_init():
|
| 38 |
+
n = 10
|
| 39 |
+
elements = get_elements(n)
|
| 40 |
+
dis = DisjointSet(elements)
|
| 41 |
+
assert dis.n_subsets == n
|
| 42 |
+
assert list(dis) == elements
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_len():
|
| 46 |
+
n = 10
|
| 47 |
+
elements = get_elements(n)
|
| 48 |
+
dis = DisjointSet(elements)
|
| 49 |
+
assert len(dis) == n
|
| 50 |
+
|
| 51 |
+
dis.add("dummy")
|
| 52 |
+
assert len(dis) == n + 1
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
@pytest.mark.parametrize("n", [10, 100])
|
| 56 |
+
def test_contains(n):
|
| 57 |
+
elements = get_elements(n)
|
| 58 |
+
dis = DisjointSet(elements)
|
| 59 |
+
for x in elements:
|
| 60 |
+
assert x in dis
|
| 61 |
+
|
| 62 |
+
assert "dummy" not in dis
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@pytest.mark.parametrize("n", [10, 100])
|
| 66 |
+
def test_add(n):
|
| 67 |
+
elements = get_elements(n)
|
| 68 |
+
dis1 = DisjointSet(elements)
|
| 69 |
+
|
| 70 |
+
dis2 = DisjointSet()
|
| 71 |
+
for i, x in enumerate(elements):
|
| 72 |
+
dis2.add(x)
|
| 73 |
+
assert len(dis2) == i + 1
|
| 74 |
+
|
| 75 |
+
# test idempotency by adding element again
|
| 76 |
+
dis2.add(x)
|
| 77 |
+
assert len(dis2) == i + 1
|
| 78 |
+
|
| 79 |
+
assert list(dis1) == list(dis2)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def test_element_not_present():
|
| 83 |
+
elements = get_elements(n=10)
|
| 84 |
+
dis = DisjointSet(elements)
|
| 85 |
+
|
| 86 |
+
with assert_raises(KeyError):
|
| 87 |
+
dis["dummy"]
|
| 88 |
+
|
| 89 |
+
with assert_raises(KeyError):
|
| 90 |
+
dis.merge(elements[0], "dummy")
|
| 91 |
+
|
| 92 |
+
with assert_raises(KeyError):
|
| 93 |
+
dis.connected(elements[0], "dummy")
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
@pytest.mark.parametrize("direction", ["forwards", "backwards"])
|
| 97 |
+
@pytest.mark.parametrize("n", [10, 100])
|
| 98 |
+
def test_linear_union_sequence(n, direction):
|
| 99 |
+
elements = get_elements(n)
|
| 100 |
+
dis = DisjointSet(elements)
|
| 101 |
+
assert elements == list(dis)
|
| 102 |
+
|
| 103 |
+
indices = list(range(n - 1))
|
| 104 |
+
if direction == "backwards":
|
| 105 |
+
indices = indices[::-1]
|
| 106 |
+
|
| 107 |
+
for it, i in enumerate(indices):
|
| 108 |
+
assert not dis.connected(elements[i], elements[i + 1])
|
| 109 |
+
assert dis.merge(elements[i], elements[i + 1])
|
| 110 |
+
assert dis.connected(elements[i], elements[i + 1])
|
| 111 |
+
assert dis.n_subsets == n - 1 - it
|
| 112 |
+
|
| 113 |
+
roots = [dis[i] for i in elements]
|
| 114 |
+
if direction == "forwards":
|
| 115 |
+
assert all(elements[0] == r for r in roots)
|
| 116 |
+
else:
|
| 117 |
+
assert all(elements[-2] == r for r in roots)
|
| 118 |
+
assert not dis.merge(elements[0], elements[-1])
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
@pytest.mark.parametrize("n", [10, 100])
|
| 122 |
+
def test_self_unions(n):
|
| 123 |
+
elements = get_elements(n)
|
| 124 |
+
dis = DisjointSet(elements)
|
| 125 |
+
|
| 126 |
+
for x in elements:
|
| 127 |
+
assert dis.connected(x, x)
|
| 128 |
+
assert not dis.merge(x, x)
|
| 129 |
+
assert dis.connected(x, x)
|
| 130 |
+
assert dis.n_subsets == len(elements)
|
| 131 |
+
|
| 132 |
+
assert elements == list(dis)
|
| 133 |
+
roots = [dis[x] for x in elements]
|
| 134 |
+
assert elements == roots
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
@pytest.mark.parametrize("order", ["ab", "ba"])
|
| 138 |
+
@pytest.mark.parametrize("n", [10, 100])
|
| 139 |
+
def test_equal_size_ordering(n, order):
|
| 140 |
+
elements = get_elements(n)
|
| 141 |
+
dis = DisjointSet(elements)
|
| 142 |
+
|
| 143 |
+
rng = np.random.RandomState(seed=0)
|
| 144 |
+
indices = np.arange(n)
|
| 145 |
+
rng.shuffle(indices)
|
| 146 |
+
|
| 147 |
+
for i in range(0, len(indices), 2):
|
| 148 |
+
a, b = elements[indices[i]], elements[indices[i + 1]]
|
| 149 |
+
if order == "ab":
|
| 150 |
+
assert dis.merge(a, b)
|
| 151 |
+
else:
|
| 152 |
+
assert dis.merge(b, a)
|
| 153 |
+
|
| 154 |
+
expected = elements[min(indices[i], indices[i + 1])]
|
| 155 |
+
assert dis[a] == expected
|
| 156 |
+
assert dis[b] == expected
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
@pytest.mark.parametrize("kmax", [5, 10])
|
| 160 |
+
def test_binary_tree(kmax):
|
| 161 |
+
n = 2**kmax
|
| 162 |
+
elements = get_elements(n)
|
| 163 |
+
dis = DisjointSet(elements)
|
| 164 |
+
rng = np.random.RandomState(seed=0)
|
| 165 |
+
|
| 166 |
+
for k in 2**np.arange(kmax):
|
| 167 |
+
for i in range(0, n, 2 * k):
|
| 168 |
+
r1, r2 = rng.randint(0, k, size=2)
|
| 169 |
+
a, b = elements[i + r1], elements[i + k + r2]
|
| 170 |
+
assert not dis.connected(a, b)
|
| 171 |
+
assert dis.merge(a, b)
|
| 172 |
+
assert dis.connected(a, b)
|
| 173 |
+
|
| 174 |
+
assert elements == list(dis)
|
| 175 |
+
roots = [dis[i] for i in elements]
|
| 176 |
+
expected_indices = np.arange(n) - np.arange(n) % (2 * k)
|
| 177 |
+
expected = [elements[i] for i in expected_indices]
|
| 178 |
+
assert roots == expected
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
@pytest.mark.parametrize("n", [10, 100])
|
| 182 |
+
def test_subsets(n):
|
| 183 |
+
elements = get_elements(n)
|
| 184 |
+
dis = DisjointSet(elements)
|
| 185 |
+
|
| 186 |
+
rng = np.random.RandomState(seed=0)
|
| 187 |
+
for i, j in rng.randint(0, n, (n, 2)):
|
| 188 |
+
x = elements[i]
|
| 189 |
+
y = elements[j]
|
| 190 |
+
|
| 191 |
+
expected = {element for element in dis if {dis[element]} == {dis[x]}}
|
| 192 |
+
assert dis.subset_size(x) == len(dis.subset(x))
|
| 193 |
+
assert expected == dis.subset(x)
|
| 194 |
+
|
| 195 |
+
expected = {dis[element]: set() for element in dis}
|
| 196 |
+
for element in dis:
|
| 197 |
+
expected[dis[element]].add(element)
|
| 198 |
+
expected = list(expected.values())
|
| 199 |
+
assert expected == dis.subsets()
|
| 200 |
+
|
| 201 |
+
dis.merge(x, y)
|
| 202 |
+
assert dis.subset(x) == dis.subset(y)
|
rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_hierarchy.py
ADDED
|
@@ -0,0 +1,1349 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# Author: Damian Eads
|
| 3 |
+
# Date: April 17, 2008
|
| 4 |
+
#
|
| 5 |
+
# Copyright (C) 2008 Damian Eads
|
| 6 |
+
#
|
| 7 |
+
# Redistribution and use in source and binary forms, with or without
|
| 8 |
+
# modification, are permitted provided that the following conditions
|
| 9 |
+
# are met:
|
| 10 |
+
#
|
| 11 |
+
# 1. Redistributions of source code must retain the above copyright
|
| 12 |
+
# notice, this list of conditions and the following disclaimer.
|
| 13 |
+
#
|
| 14 |
+
# 2. Redistributions in binary form must reproduce the above
|
| 15 |
+
# copyright notice, this list of conditions and the following
|
| 16 |
+
# disclaimer in the documentation and/or other materials provided
|
| 17 |
+
# with the distribution.
|
| 18 |
+
#
|
| 19 |
+
# 3. The name of the author may not be used to endorse or promote
|
| 20 |
+
# products derived from this software without specific prior
|
| 21 |
+
# written permission.
|
| 22 |
+
#
|
| 23 |
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
| 24 |
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 25 |
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
| 26 |
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
| 27 |
+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
| 28 |
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
| 29 |
+
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
| 30 |
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
| 31 |
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
| 32 |
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 33 |
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 34 |
+
import numpy as np
|
| 35 |
+
from numpy.testing import assert_allclose, assert_equal, assert_, assert_warns
|
| 36 |
+
import pytest
|
| 37 |
+
from pytest import raises as assert_raises
|
| 38 |
+
|
| 39 |
+
import scipy.cluster.hierarchy
|
| 40 |
+
from scipy.cluster.hierarchy import (
|
| 41 |
+
ClusterWarning, linkage, from_mlab_linkage, to_mlab_linkage,
|
| 42 |
+
num_obs_linkage, inconsistent, cophenet, fclusterdata, fcluster,
|
| 43 |
+
is_isomorphic, single, leaders,
|
| 44 |
+
correspond, is_monotonic, maxdists, maxinconsts, maxRstat,
|
| 45 |
+
is_valid_linkage, is_valid_im, to_tree, leaves_list, dendrogram,
|
| 46 |
+
set_link_color_palette, cut_tree, optimal_leaf_ordering,
|
| 47 |
+
_order_cluster_tree, _hierarchy, _LINKAGE_METHODS)
|
| 48 |
+
from scipy.spatial.distance import pdist
|
| 49 |
+
from scipy.cluster._hierarchy import Heap
|
| 50 |
+
from scipy.conftest import (
|
| 51 |
+
array_api_compatible,
|
| 52 |
+
skip_if_array_api,
|
| 53 |
+
skip_if_array_api_gpu,
|
| 54 |
+
skip_if_array_api_backend,
|
| 55 |
+
)
|
| 56 |
+
from scipy._lib._array_api import xp_assert_close
|
| 57 |
+
|
| 58 |
+
from . import hierarchy_test_data
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
# Matplotlib is not a scipy dependency but is optionally used in dendrogram, so
|
| 62 |
+
# check if it's available
|
| 63 |
+
try:
|
| 64 |
+
import matplotlib
|
| 65 |
+
# and set the backend to be Agg (no gui)
|
| 66 |
+
matplotlib.use('Agg')
|
| 67 |
+
# before importing pyplot
|
| 68 |
+
import matplotlib.pyplot as plt
|
| 69 |
+
have_matplotlib = True
|
| 70 |
+
except Exception:
|
| 71 |
+
have_matplotlib = False
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class TestLinkage:
|
| 75 |
+
|
| 76 |
+
@skip_if_array_api_gpu
|
| 77 |
+
@array_api_compatible
|
| 78 |
+
def test_linkage_non_finite_elements_in_distance_matrix(self, xp):
|
| 79 |
+
# Tests linkage(Y) where Y contains a non-finite element (e.g. NaN or Inf).
|
| 80 |
+
# Exception expected.
|
| 81 |
+
y = xp.zeros((6,))
|
| 82 |
+
y[0] = xp.nan
|
| 83 |
+
assert_raises(ValueError, linkage, y)
|
| 84 |
+
|
| 85 |
+
def test_linkage_empty_distance_matrix(self):
|
| 86 |
+
# Tests linkage(Y) where Y is a 0x4 linkage matrix. Exception expected.
|
| 87 |
+
y = np.zeros((0,))
|
| 88 |
+
assert_raises(ValueError, linkage, y)
|
| 89 |
+
|
| 90 |
+
@skip_if_array_api_gpu
|
| 91 |
+
@array_api_compatible
|
| 92 |
+
def test_linkage_tdist(self, xp):
|
| 93 |
+
for method in ['single', 'complete', 'average', 'weighted']:
|
| 94 |
+
self.check_linkage_tdist(method, xp)
|
| 95 |
+
|
| 96 |
+
def check_linkage_tdist(self, method, xp):
|
| 97 |
+
# Tests linkage(Y, method) on the tdist data set.
|
| 98 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), method)
|
| 99 |
+
expectedZ = getattr(hierarchy_test_data, 'linkage_ytdist_' + method)
|
| 100 |
+
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
|
| 101 |
+
|
| 102 |
+
@skip_if_array_api_gpu
|
| 103 |
+
@array_api_compatible
|
| 104 |
+
def test_linkage_X(self, xp):
|
| 105 |
+
for method in ['centroid', 'median', 'ward']:
|
| 106 |
+
self.check_linkage_q(method, xp)
|
| 107 |
+
|
| 108 |
+
def check_linkage_q(self, method, xp):
|
| 109 |
+
# Tests linkage(Y, method) on the Q data set.
|
| 110 |
+
Z = linkage(xp.asarray(hierarchy_test_data.X), method)
|
| 111 |
+
expectedZ = getattr(hierarchy_test_data, 'linkage_X_' + method)
|
| 112 |
+
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
|
| 113 |
+
|
| 114 |
+
y = scipy.spatial.distance.pdist(hierarchy_test_data.X,
|
| 115 |
+
metric="euclidean")
|
| 116 |
+
Z = linkage(xp.asarray(y), method)
|
| 117 |
+
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
|
| 118 |
+
|
| 119 |
+
@skip_if_array_api_gpu
|
| 120 |
+
@array_api_compatible
|
| 121 |
+
def test_compare_with_trivial(self, xp):
|
| 122 |
+
rng = np.random.RandomState(0)
|
| 123 |
+
n = 20
|
| 124 |
+
X = rng.rand(n, 2)
|
| 125 |
+
d = pdist(X)
|
| 126 |
+
|
| 127 |
+
for method, code in _LINKAGE_METHODS.items():
|
| 128 |
+
Z_trivial = _hierarchy.linkage(d, n, code)
|
| 129 |
+
Z = linkage(xp.asarray(d), method)
|
| 130 |
+
xp_assert_close(Z, xp.asarray(Z_trivial), rtol=1e-14, atol=1e-15)
|
| 131 |
+
|
| 132 |
+
@skip_if_array_api_gpu
|
| 133 |
+
@array_api_compatible
|
| 134 |
+
def test_optimal_leaf_ordering(self, xp):
|
| 135 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), optimal_ordering=True)
|
| 136 |
+
expectedZ = getattr(hierarchy_test_data, 'linkage_ytdist_single_olo')
|
| 137 |
+
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class TestLinkageTies:
|
| 141 |
+
|
| 142 |
+
_expectations = {
|
| 143 |
+
'single': np.array([[0, 1, 1.41421356, 2],
|
| 144 |
+
[2, 3, 1.41421356, 3]]),
|
| 145 |
+
'complete': np.array([[0, 1, 1.41421356, 2],
|
| 146 |
+
[2, 3, 2.82842712, 3]]),
|
| 147 |
+
'average': np.array([[0, 1, 1.41421356, 2],
|
| 148 |
+
[2, 3, 2.12132034, 3]]),
|
| 149 |
+
'weighted': np.array([[0, 1, 1.41421356, 2],
|
| 150 |
+
[2, 3, 2.12132034, 3]]),
|
| 151 |
+
'centroid': np.array([[0, 1, 1.41421356, 2],
|
| 152 |
+
[2, 3, 2.12132034, 3]]),
|
| 153 |
+
'median': np.array([[0, 1, 1.41421356, 2],
|
| 154 |
+
[2, 3, 2.12132034, 3]]),
|
| 155 |
+
'ward': np.array([[0, 1, 1.41421356, 2],
|
| 156 |
+
[2, 3, 2.44948974, 3]]),
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
@skip_if_array_api_gpu
|
| 160 |
+
@array_api_compatible
|
| 161 |
+
def test_linkage_ties(self, xp):
|
| 162 |
+
for method in ['single', 'complete', 'average', 'weighted',
|
| 163 |
+
'centroid', 'median', 'ward']:
|
| 164 |
+
self.check_linkage_ties(method, xp)
|
| 165 |
+
|
| 166 |
+
def check_linkage_ties(self, method, xp):
|
| 167 |
+
X = xp.asarray([[-1, -1], [0, 0], [1, 1]])
|
| 168 |
+
Z = linkage(X, method=method)
|
| 169 |
+
expectedZ = self._expectations[method]
|
| 170 |
+
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
class TestInconsistent:
|
| 174 |
+
|
| 175 |
+
@skip_if_array_api_gpu
|
| 176 |
+
@array_api_compatible
|
| 177 |
+
def test_inconsistent_tdist(self, xp):
|
| 178 |
+
for depth in hierarchy_test_data.inconsistent_ytdist:
|
| 179 |
+
self.check_inconsistent_tdist(depth, xp)
|
| 180 |
+
|
| 181 |
+
def check_inconsistent_tdist(self, depth, xp):
|
| 182 |
+
Z = xp.asarray(hierarchy_test_data.linkage_ytdist_single)
|
| 183 |
+
xp_assert_close(inconsistent(Z, depth),
|
| 184 |
+
xp.asarray(hierarchy_test_data.inconsistent_ytdist[depth]))
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
class TestCopheneticDistance:
|
| 188 |
+
|
| 189 |
+
@skip_if_array_api_gpu
|
| 190 |
+
@array_api_compatible
|
| 191 |
+
def test_linkage_cophenet_tdist_Z(self, xp):
|
| 192 |
+
# Tests cophenet(Z) on tdist data set.
|
| 193 |
+
expectedM = xp.asarray([268, 295, 255, 255, 295, 295, 268, 268, 295, 295,
|
| 194 |
+
295, 138, 219, 295, 295])
|
| 195 |
+
Z = xp.asarray(hierarchy_test_data.linkage_ytdist_single)
|
| 196 |
+
M = cophenet(Z)
|
| 197 |
+
xp_assert_close(M, xp.asarray(expectedM, dtype=xp.float64), atol=1e-10)
|
| 198 |
+
|
| 199 |
+
@skip_if_array_api_gpu
|
| 200 |
+
@array_api_compatible
|
| 201 |
+
def test_linkage_cophenet_tdist_Z_Y(self, xp):
|
| 202 |
+
# Tests cophenet(Z, Y) on tdist data set.
|
| 203 |
+
Z = xp.asarray(hierarchy_test_data.linkage_ytdist_single)
|
| 204 |
+
(c, M) = cophenet(Z, xp.asarray(hierarchy_test_data.ytdist))
|
| 205 |
+
expectedM = xp.asarray([268, 295, 255, 255, 295, 295, 268, 268, 295, 295,
|
| 206 |
+
295, 138, 219, 295, 295], dtype=xp.float64)
|
| 207 |
+
expectedc = xp.asarray(0.639931296433393415057366837573, dtype=xp.float64)[()]
|
| 208 |
+
xp_assert_close(c, expectedc, atol=1e-10)
|
| 209 |
+
xp_assert_close(M, expectedM, atol=1e-10)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
class TestMLabLinkageConversion:
|
| 213 |
+
|
| 214 |
+
@skip_if_array_api
|
| 215 |
+
def test_mlab_linkage_conversion_empty(self):
|
| 216 |
+
# Tests from/to_mlab_linkage on empty linkage array.
|
| 217 |
+
X = np.asarray([])
|
| 218 |
+
assert_equal(from_mlab_linkage([]), X)
|
| 219 |
+
assert_equal(to_mlab_linkage([]), X)
|
| 220 |
+
|
| 221 |
+
@skip_if_array_api_gpu
|
| 222 |
+
@array_api_compatible
|
| 223 |
+
def test_mlab_linkage_conversion_single_row(self, xp):
|
| 224 |
+
# Tests from/to_mlab_linkage on linkage array with single row.
|
| 225 |
+
Z = xp.asarray([[0., 1., 3., 2.]])
|
| 226 |
+
Zm = xp.asarray([[1, 2, 3]])
|
| 227 |
+
xp_assert_close(from_mlab_linkage(Zm), xp.asarray(Z, dtype=xp.float64),
|
| 228 |
+
rtol=1e-15)
|
| 229 |
+
xp_assert_close(to_mlab_linkage(Z), xp.asarray(Zm, dtype=xp.float64),
|
| 230 |
+
rtol=1e-15)
|
| 231 |
+
|
| 232 |
+
@skip_if_array_api_gpu
|
| 233 |
+
@array_api_compatible
|
| 234 |
+
def test_mlab_linkage_conversion_multiple_rows(self, xp):
|
| 235 |
+
# Tests from/to_mlab_linkage on linkage array with multiple rows.
|
| 236 |
+
Zm = xp.asarray([[3, 6, 138], [4, 5, 219],
|
| 237 |
+
[1, 8, 255], [2, 9, 268], [7, 10, 295]])
|
| 238 |
+
Z = xp.asarray([[2., 5., 138., 2.],
|
| 239 |
+
[3., 4., 219., 2.],
|
| 240 |
+
[0., 7., 255., 3.],
|
| 241 |
+
[1., 8., 268., 4.],
|
| 242 |
+
[6., 9., 295., 6.]],
|
| 243 |
+
dtype=xp.float64)
|
| 244 |
+
xp_assert_close(from_mlab_linkage(Zm), Z, rtol=1e-15)
|
| 245 |
+
xp_assert_close(to_mlab_linkage(Z), xp.asarray(Zm, dtype=xp.float64),
|
| 246 |
+
rtol=1e-15)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
class TestFcluster:
|
| 250 |
+
|
| 251 |
+
@skip_if_array_api_gpu
|
| 252 |
+
@array_api_compatible
|
| 253 |
+
def test_fclusterdata(self, xp):
|
| 254 |
+
for t in hierarchy_test_data.fcluster_inconsistent:
|
| 255 |
+
self.check_fclusterdata(t, 'inconsistent', xp)
|
| 256 |
+
for t in hierarchy_test_data.fcluster_distance:
|
| 257 |
+
self.check_fclusterdata(t, 'distance', xp)
|
| 258 |
+
for t in hierarchy_test_data.fcluster_maxclust:
|
| 259 |
+
self.check_fclusterdata(t, 'maxclust', xp)
|
| 260 |
+
|
| 261 |
+
def check_fclusterdata(self, t, criterion, xp):
|
| 262 |
+
# Tests fclusterdata(X, criterion=criterion, t=t) on a random 3-cluster data set
|
| 263 |
+
expectedT = xp.asarray(getattr(hierarchy_test_data, 'fcluster_' + criterion)[t])
|
| 264 |
+
X = xp.asarray(hierarchy_test_data.Q_X)
|
| 265 |
+
T = fclusterdata(X, criterion=criterion, t=t)
|
| 266 |
+
assert_(is_isomorphic(T, expectedT))
|
| 267 |
+
|
| 268 |
+
@skip_if_array_api_gpu
|
| 269 |
+
@array_api_compatible
|
| 270 |
+
def test_fcluster(self, xp):
|
| 271 |
+
for t in hierarchy_test_data.fcluster_inconsistent:
|
| 272 |
+
self.check_fcluster(t, 'inconsistent', xp)
|
| 273 |
+
for t in hierarchy_test_data.fcluster_distance:
|
| 274 |
+
self.check_fcluster(t, 'distance', xp)
|
| 275 |
+
for t in hierarchy_test_data.fcluster_maxclust:
|
| 276 |
+
self.check_fcluster(t, 'maxclust', xp)
|
| 277 |
+
|
| 278 |
+
def check_fcluster(self, t, criterion, xp):
|
| 279 |
+
# Tests fcluster(Z, criterion=criterion, t=t) on a random 3-cluster data set.
|
| 280 |
+
expectedT = xp.asarray(getattr(hierarchy_test_data, 'fcluster_' + criterion)[t])
|
| 281 |
+
Z = single(xp.asarray(hierarchy_test_data.Q_X))
|
| 282 |
+
T = fcluster(Z, criterion=criterion, t=t)
|
| 283 |
+
assert_(is_isomorphic(T, expectedT))
|
| 284 |
+
|
| 285 |
+
@skip_if_array_api_gpu
|
| 286 |
+
@array_api_compatible
|
| 287 |
+
def test_fcluster_monocrit(self, xp):
|
| 288 |
+
for t in hierarchy_test_data.fcluster_distance:
|
| 289 |
+
self.check_fcluster_monocrit(t, xp)
|
| 290 |
+
for t in hierarchy_test_data.fcluster_maxclust:
|
| 291 |
+
self.check_fcluster_maxclust_monocrit(t, xp)
|
| 292 |
+
|
| 293 |
+
def check_fcluster_monocrit(self, t, xp):
|
| 294 |
+
expectedT = xp.asarray(hierarchy_test_data.fcluster_distance[t])
|
| 295 |
+
Z = single(xp.asarray(hierarchy_test_data.Q_X))
|
| 296 |
+
T = fcluster(Z, t, criterion='monocrit', monocrit=maxdists(Z))
|
| 297 |
+
assert_(is_isomorphic(T, expectedT))
|
| 298 |
+
|
| 299 |
+
def check_fcluster_maxclust_monocrit(self, t, xp):
|
| 300 |
+
expectedT = xp.asarray(hierarchy_test_data.fcluster_maxclust[t])
|
| 301 |
+
Z = single(xp.asarray(hierarchy_test_data.Q_X))
|
| 302 |
+
T = fcluster(Z, t, criterion='maxclust_monocrit', monocrit=maxdists(Z))
|
| 303 |
+
assert_(is_isomorphic(T, expectedT))
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
class TestLeaders:
|
| 307 |
+
|
| 308 |
+
@skip_if_array_api_gpu
|
| 309 |
+
@array_api_compatible
|
| 310 |
+
def test_leaders_single(self, xp):
|
| 311 |
+
# Tests leaders using a flat clustering generated by single linkage.
|
| 312 |
+
X = hierarchy_test_data.Q_X
|
| 313 |
+
Y = pdist(X)
|
| 314 |
+
Y = xp.asarray(Y)
|
| 315 |
+
Z = linkage(Y)
|
| 316 |
+
T = fcluster(Z, criterion='maxclust', t=3)
|
| 317 |
+
Lright = (xp.asarray([53, 55, 56]), xp.asarray([2, 3, 1]))
|
| 318 |
+
T = xp.asarray(T, dtype=xp.int32)
|
| 319 |
+
L = leaders(Z, T)
|
| 320 |
+
assert_allclose(np.concatenate(L), np.concatenate(Lright), rtol=1e-15)
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
class TestIsIsomorphic:
|
| 324 |
+
|
| 325 |
+
@skip_if_array_api
|
| 326 |
+
def test_is_isomorphic_1(self):
|
| 327 |
+
# Tests is_isomorphic on test case #1 (one flat cluster, different labellings)
|
| 328 |
+
a = [1, 1, 1]
|
| 329 |
+
b = [2, 2, 2]
|
| 330 |
+
assert_(is_isomorphic(a, b))
|
| 331 |
+
assert_(is_isomorphic(b, a))
|
| 332 |
+
|
| 333 |
+
@skip_if_array_api
|
| 334 |
+
def test_is_isomorphic_2(self):
|
| 335 |
+
# Tests is_isomorphic on test case #2 (two flat clusters, different labelings)
|
| 336 |
+
a = np.asarray([1, 7, 1])
|
| 337 |
+
b = np.asarray([2, 3, 2])
|
| 338 |
+
assert_(is_isomorphic(a, b))
|
| 339 |
+
assert_(is_isomorphic(b, a))
|
| 340 |
+
|
| 341 |
+
@skip_if_array_api
|
| 342 |
+
def test_is_isomorphic_3(self):
|
| 343 |
+
# Tests is_isomorphic on test case #3 (no flat clusters)
|
| 344 |
+
a = np.asarray([])
|
| 345 |
+
b = np.asarray([])
|
| 346 |
+
assert_(is_isomorphic(a, b))
|
| 347 |
+
|
| 348 |
+
@skip_if_array_api
|
| 349 |
+
def test_is_isomorphic_4A(self):
|
| 350 |
+
# Tests is_isomorphic on test case #4A
|
| 351 |
+
# (3 flat clusters, different labelings, isomorphic)
|
| 352 |
+
a = np.asarray([1, 2, 3])
|
| 353 |
+
b = np.asarray([1, 3, 2])
|
| 354 |
+
assert_(is_isomorphic(a, b))
|
| 355 |
+
assert_(is_isomorphic(b, a))
|
| 356 |
+
|
| 357 |
+
@skip_if_array_api
|
| 358 |
+
def test_is_isomorphic_4B(self):
|
| 359 |
+
# Tests is_isomorphic on test case #4B
|
| 360 |
+
# (3 flat clusters, different labelings, nonisomorphic)
|
| 361 |
+
a = np.asarray([1, 2, 3, 3])
|
| 362 |
+
b = np.asarray([1, 3, 2, 3])
|
| 363 |
+
assert_(is_isomorphic(a, b) is False)
|
| 364 |
+
assert_(is_isomorphic(b, a) is False)
|
| 365 |
+
|
| 366 |
+
@skip_if_array_api
|
| 367 |
+
def test_is_isomorphic_4C(self):
|
| 368 |
+
# Tests is_isomorphic on test case #4C
|
| 369 |
+
# (3 flat clusters, different labelings, isomorphic)
|
| 370 |
+
a = np.asarray([7, 2, 3])
|
| 371 |
+
b = np.asarray([6, 3, 2])
|
| 372 |
+
assert_(is_isomorphic(a, b))
|
| 373 |
+
assert_(is_isomorphic(b, a))
|
| 374 |
+
|
| 375 |
+
@skip_if_array_api
|
| 376 |
+
def test_is_isomorphic_5(self):
|
| 377 |
+
# Tests is_isomorphic on test case #5 (1000 observations, 2/3/5 random
|
| 378 |
+
# clusters, random permutation of the labeling).
|
| 379 |
+
for nc in [2, 3, 5]:
|
| 380 |
+
self.help_is_isomorphic_randperm(1000, nc)
|
| 381 |
+
|
| 382 |
+
@skip_if_array_api
|
| 383 |
+
def test_is_isomorphic_6(self):
|
| 384 |
+
# Tests is_isomorphic on test case #5A (1000 observations, 2/3/5 random
|
| 385 |
+
# clusters, random permutation of the labeling, slightly
|
| 386 |
+
# nonisomorphic.)
|
| 387 |
+
for nc in [2, 3, 5]:
|
| 388 |
+
self.help_is_isomorphic_randperm(1000, nc, True, 5)
|
| 389 |
+
|
| 390 |
+
@skip_if_array_api
|
| 391 |
+
def test_is_isomorphic_7(self):
|
| 392 |
+
# Regression test for gh-6271
|
| 393 |
+
a = np.asarray([1, 2, 3])
|
| 394 |
+
b = np.asarray([1, 1, 1])
|
| 395 |
+
assert_(not is_isomorphic(a, b))
|
| 396 |
+
|
| 397 |
+
def help_is_isomorphic_randperm(self, nobs, nclusters, noniso=False, nerrors=0):
|
| 398 |
+
for k in range(3):
|
| 399 |
+
a = (np.random.rand(nobs) * nclusters).astype(int)
|
| 400 |
+
b = np.zeros(a.size, dtype=int)
|
| 401 |
+
P = np.random.permutation(nclusters)
|
| 402 |
+
for i in range(0, a.shape[0]):
|
| 403 |
+
b[i] = P[a[i]]
|
| 404 |
+
if noniso:
|
| 405 |
+
Q = np.random.permutation(nobs)
|
| 406 |
+
b[Q[0:nerrors]] += 1
|
| 407 |
+
b[Q[0:nerrors]] %= nclusters
|
| 408 |
+
assert_(is_isomorphic(a, b) == (not noniso))
|
| 409 |
+
assert_(is_isomorphic(b, a) == (not noniso))
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
class TestIsValidLinkage:
|
| 413 |
+
|
| 414 |
+
@skip_if_array_api_gpu
|
| 415 |
+
@array_api_compatible
|
| 416 |
+
def test_is_valid_linkage_various_size(self, xp):
|
| 417 |
+
for nrow, ncol, valid in [(2, 5, False), (2, 3, False),
|
| 418 |
+
(1, 4, True), (2, 4, True)]:
|
| 419 |
+
self.check_is_valid_linkage_various_size(nrow, ncol, valid, xp)
|
| 420 |
+
|
| 421 |
+
def check_is_valid_linkage_various_size(self, nrow, ncol, valid, xp):
|
| 422 |
+
# Tests is_valid_linkage(Z) with linkage matrices of various sizes
|
| 423 |
+
Z = xp.asarray([[0, 1, 3.0, 2, 5],
|
| 424 |
+
[3, 2, 4.0, 3, 3]], dtype=xp.float64)
|
| 425 |
+
Z = Z[:nrow, :ncol]
|
| 426 |
+
assert_(is_valid_linkage(Z) == valid)
|
| 427 |
+
if not valid:
|
| 428 |
+
assert_raises(ValueError, is_valid_linkage, Z, throw=True)
|
| 429 |
+
|
| 430 |
+
@skip_if_array_api_gpu
|
| 431 |
+
@array_api_compatible
|
| 432 |
+
def test_is_valid_linkage_int_type(self, xp):
|
| 433 |
+
# Tests is_valid_linkage(Z) with integer type.
|
| 434 |
+
Z = xp.asarray([[0, 1, 3.0, 2],
|
| 435 |
+
[3, 2, 4.0, 3]], dtype=xp.int64)
|
| 436 |
+
assert_(is_valid_linkage(Z) is False)
|
| 437 |
+
assert_raises(TypeError, is_valid_linkage, Z, throw=True)
|
| 438 |
+
|
| 439 |
+
@skip_if_array_api_gpu
|
| 440 |
+
@array_api_compatible
|
| 441 |
+
def test_is_valid_linkage_empty(self, xp):
|
| 442 |
+
# Tests is_valid_linkage(Z) with empty linkage.
|
| 443 |
+
Z = xp.zeros((0, 4), dtype=xp.float64)
|
| 444 |
+
assert_(is_valid_linkage(Z) is False)
|
| 445 |
+
assert_raises(ValueError, is_valid_linkage, Z, throw=True)
|
| 446 |
+
|
| 447 |
+
@skip_if_array_api_gpu
|
| 448 |
+
@array_api_compatible
|
| 449 |
+
def test_is_valid_linkage_4_and_up(self, xp):
|
| 450 |
+
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
| 451 |
+
# sizes 4 and 15 (step size 3).
|
| 452 |
+
for i in range(4, 15, 3):
|
| 453 |
+
y = np.random.rand(i*(i-1)//2)
|
| 454 |
+
y = xp.asarray(y)
|
| 455 |
+
Z = linkage(y)
|
| 456 |
+
assert_(is_valid_linkage(Z) is True)
|
| 457 |
+
|
| 458 |
+
@skip_if_array_api_gpu
|
| 459 |
+
@array_api_compatible
|
| 460 |
+
def test_is_valid_linkage_4_and_up_neg_index_left(self, xp):
|
| 461 |
+
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
| 462 |
+
# sizes 4 and 15 (step size 3) with negative indices (left).
|
| 463 |
+
for i in range(4, 15, 3):
|
| 464 |
+
y = np.random.rand(i*(i-1)//2)
|
| 465 |
+
y = xp.asarray(y)
|
| 466 |
+
Z = linkage(y)
|
| 467 |
+
Z[i//2,0] = -2
|
| 468 |
+
assert_(is_valid_linkage(Z) is False)
|
| 469 |
+
assert_raises(ValueError, is_valid_linkage, Z, throw=True)
|
| 470 |
+
|
| 471 |
+
@skip_if_array_api_gpu
|
| 472 |
+
@array_api_compatible
|
| 473 |
+
def test_is_valid_linkage_4_and_up_neg_index_right(self, xp):
|
| 474 |
+
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
| 475 |
+
# sizes 4 and 15 (step size 3) with negative indices (right).
|
| 476 |
+
for i in range(4, 15, 3):
|
| 477 |
+
y = np.random.rand(i*(i-1)//2)
|
| 478 |
+
y = xp.asarray(y)
|
| 479 |
+
Z = linkage(y)
|
| 480 |
+
Z[i//2,1] = -2
|
| 481 |
+
assert_(is_valid_linkage(Z) is False)
|
| 482 |
+
assert_raises(ValueError, is_valid_linkage, Z, throw=True)
|
| 483 |
+
|
| 484 |
+
@skip_if_array_api_gpu
|
| 485 |
+
@array_api_compatible
|
| 486 |
+
def test_is_valid_linkage_4_and_up_neg_dist(self, xp):
|
| 487 |
+
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
| 488 |
+
# sizes 4 and 15 (step size 3) with negative distances.
|
| 489 |
+
for i in range(4, 15, 3):
|
| 490 |
+
y = np.random.rand(i*(i-1)//2)
|
| 491 |
+
y = xp.asarray(y)
|
| 492 |
+
Z = linkage(y)
|
| 493 |
+
Z[i//2,2] = -0.5
|
| 494 |
+
assert_(is_valid_linkage(Z) is False)
|
| 495 |
+
assert_raises(ValueError, is_valid_linkage, Z, throw=True)
|
| 496 |
+
|
| 497 |
+
@skip_if_array_api_gpu
|
| 498 |
+
@array_api_compatible
|
| 499 |
+
def test_is_valid_linkage_4_and_up_neg_counts(self, xp):
|
| 500 |
+
# Tests is_valid_linkage(Z) on linkage on observation sets between
|
| 501 |
+
# sizes 4 and 15 (step size 3) with negative counts.
|
| 502 |
+
for i in range(4, 15, 3):
|
| 503 |
+
y = np.random.rand(i*(i-1)//2)
|
| 504 |
+
y = xp.asarray(y)
|
| 505 |
+
Z = linkage(y)
|
| 506 |
+
Z[i//2,3] = -2
|
| 507 |
+
assert_(is_valid_linkage(Z) is False)
|
| 508 |
+
assert_raises(ValueError, is_valid_linkage, Z, throw=True)
|
| 509 |
+
|
| 510 |
+
|
| 511 |
+
class TestIsValidInconsistent:
|
| 512 |
+
|
| 513 |
+
@skip_if_array_api_gpu
|
| 514 |
+
@array_api_compatible
|
| 515 |
+
def test_is_valid_im_int_type(self, xp):
|
| 516 |
+
# Tests is_valid_im(R) with integer type.
|
| 517 |
+
R = xp.asarray([[0, 1, 3.0, 2],
|
| 518 |
+
[3, 2, 4.0, 3]], dtype=xp.int64)
|
| 519 |
+
assert_(is_valid_im(R) is False)
|
| 520 |
+
assert_raises(TypeError, is_valid_im, R, throw=True)
|
| 521 |
+
|
| 522 |
+
@skip_if_array_api_gpu
|
| 523 |
+
@array_api_compatible
|
| 524 |
+
def test_is_valid_im_various_size(self, xp):
|
| 525 |
+
for nrow, ncol, valid in [(2, 5, False), (2, 3, False),
|
| 526 |
+
(1, 4, True), (2, 4, True)]:
|
| 527 |
+
self.check_is_valid_im_various_size(nrow, ncol, valid, xp)
|
| 528 |
+
|
| 529 |
+
def check_is_valid_im_various_size(self, nrow, ncol, valid, xp):
|
| 530 |
+
# Tests is_valid_im(R) with linkage matrices of various sizes
|
| 531 |
+
R = xp.asarray([[0, 1, 3.0, 2, 5],
|
| 532 |
+
[3, 2, 4.0, 3, 3]], dtype=xp.float64)
|
| 533 |
+
R = R[:nrow, :ncol]
|
| 534 |
+
assert_(is_valid_im(R) == valid)
|
| 535 |
+
if not valid:
|
| 536 |
+
assert_raises(ValueError, is_valid_im, R, throw=True)
|
| 537 |
+
|
| 538 |
+
@skip_if_array_api_gpu
|
| 539 |
+
@array_api_compatible
|
| 540 |
+
def test_is_valid_im_empty(self, xp):
|
| 541 |
+
# Tests is_valid_im(R) with empty inconsistency matrix.
|
| 542 |
+
R = xp.zeros((0, 4), dtype=xp.float64)
|
| 543 |
+
assert_(is_valid_im(R) is False)
|
| 544 |
+
assert_raises(ValueError, is_valid_im, R, throw=True)
|
| 545 |
+
|
| 546 |
+
@skip_if_array_api_gpu
|
| 547 |
+
@array_api_compatible
|
| 548 |
+
def test_is_valid_im_4_and_up(self, xp):
|
| 549 |
+
# Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
|
| 550 |
+
# (step size 3).
|
| 551 |
+
for i in range(4, 15, 3):
|
| 552 |
+
y = np.random.rand(i*(i-1)//2)
|
| 553 |
+
y = xp.asarray(y)
|
| 554 |
+
Z = linkage(y)
|
| 555 |
+
R = inconsistent(Z)
|
| 556 |
+
assert_(is_valid_im(R) is True)
|
| 557 |
+
|
| 558 |
+
@skip_if_array_api_gpu
|
| 559 |
+
@array_api_compatible
|
| 560 |
+
def test_is_valid_im_4_and_up_neg_index_left(self, xp):
|
| 561 |
+
# Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
|
| 562 |
+
# (step size 3) with negative link height means.
|
| 563 |
+
for i in range(4, 15, 3):
|
| 564 |
+
y = np.random.rand(i*(i-1)//2)
|
| 565 |
+
y = xp.asarray(y)
|
| 566 |
+
Z = linkage(y)
|
| 567 |
+
R = inconsistent(Z)
|
| 568 |
+
R[i//2,0] = -2.0
|
| 569 |
+
assert_(is_valid_im(R) is False)
|
| 570 |
+
assert_raises(ValueError, is_valid_im, R, throw=True)
|
| 571 |
+
|
| 572 |
+
@skip_if_array_api_gpu
|
| 573 |
+
@array_api_compatible
|
| 574 |
+
def test_is_valid_im_4_and_up_neg_index_right(self, xp):
|
| 575 |
+
# Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
|
| 576 |
+
# (step size 3) with negative link height standard deviations.
|
| 577 |
+
for i in range(4, 15, 3):
|
| 578 |
+
y = np.random.rand(i*(i-1)//2)
|
| 579 |
+
y = xp.asarray(y)
|
| 580 |
+
Z = linkage(y)
|
| 581 |
+
R = inconsistent(Z)
|
| 582 |
+
R[i//2,1] = -2.0
|
| 583 |
+
assert_(is_valid_im(R) is False)
|
| 584 |
+
assert_raises(ValueError, is_valid_im, R, throw=True)
|
| 585 |
+
|
| 586 |
+
@skip_if_array_api_gpu
|
| 587 |
+
@array_api_compatible
|
| 588 |
+
def test_is_valid_im_4_and_up_neg_dist(self, xp):
|
| 589 |
+
# Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
|
| 590 |
+
# (step size 3) with negative link counts.
|
| 591 |
+
for i in range(4, 15, 3):
|
| 592 |
+
y = np.random.rand(i*(i-1)//2)
|
| 593 |
+
y = xp.asarray(y)
|
| 594 |
+
Z = linkage(y)
|
| 595 |
+
R = inconsistent(Z)
|
| 596 |
+
R[i//2,2] = -0.5
|
| 597 |
+
assert_(is_valid_im(R) is False)
|
| 598 |
+
assert_raises(ValueError, is_valid_im, R, throw=True)
|
| 599 |
+
|
| 600 |
+
|
| 601 |
+
class TestNumObsLinkage:
|
| 602 |
+
|
| 603 |
+
@skip_if_array_api_gpu
|
| 604 |
+
@array_api_compatible
|
| 605 |
+
def test_num_obs_linkage_empty(self, xp):
|
| 606 |
+
# Tests num_obs_linkage(Z) with empty linkage.
|
| 607 |
+
Z = xp.zeros((0, 4), dtype=xp.float64)
|
| 608 |
+
assert_raises(ValueError, num_obs_linkage, Z)
|
| 609 |
+
|
| 610 |
+
@array_api_compatible
|
| 611 |
+
def test_num_obs_linkage_1x4(self, xp):
|
| 612 |
+
# Tests num_obs_linkage(Z) on linkage over 2 observations.
|
| 613 |
+
Z = xp.asarray([[0, 1, 3.0, 2]], dtype=xp.float64)
|
| 614 |
+
assert_equal(num_obs_linkage(Z), 2)
|
| 615 |
+
|
| 616 |
+
@array_api_compatible
|
| 617 |
+
def test_num_obs_linkage_2x4(self, xp):
|
| 618 |
+
# Tests num_obs_linkage(Z) on linkage over 3 observations.
|
| 619 |
+
Z = xp.asarray([[0, 1, 3.0, 2],
|
| 620 |
+
[3, 2, 4.0, 3]], dtype=xp.float64)
|
| 621 |
+
assert_equal(num_obs_linkage(Z), 3)
|
| 622 |
+
|
| 623 |
+
@skip_if_array_api_gpu
|
| 624 |
+
@array_api_compatible
|
| 625 |
+
def test_num_obs_linkage_4_and_up(self, xp):
|
| 626 |
+
# Tests num_obs_linkage(Z) on linkage on observation sets between sizes
|
| 627 |
+
# 4 and 15 (step size 3).
|
| 628 |
+
for i in range(4, 15, 3):
|
| 629 |
+
y = np.random.rand(i*(i-1)//2)
|
| 630 |
+
y = xp.asarray(y)
|
| 631 |
+
Z = linkage(y)
|
| 632 |
+
assert_equal(num_obs_linkage(Z), i)
|
| 633 |
+
|
| 634 |
+
|
| 635 |
+
class TestLeavesList:
|
| 636 |
+
|
| 637 |
+
@skip_if_array_api_gpu
|
| 638 |
+
@array_api_compatible
|
| 639 |
+
def test_leaves_list_1x4(self, xp):
|
| 640 |
+
# Tests leaves_list(Z) on a 1x4 linkage.
|
| 641 |
+
Z = xp.asarray([[0, 1, 3.0, 2]], dtype=xp.float64)
|
| 642 |
+
to_tree(Z)
|
| 643 |
+
assert_allclose(leaves_list(Z), [0, 1], rtol=1e-15)
|
| 644 |
+
|
| 645 |
+
@skip_if_array_api_gpu
|
| 646 |
+
@array_api_compatible
|
| 647 |
+
def test_leaves_list_2x4(self, xp):
|
| 648 |
+
# Tests leaves_list(Z) on a 2x4 linkage.
|
| 649 |
+
Z = xp.asarray([[0, 1, 3.0, 2],
|
| 650 |
+
[3, 2, 4.0, 3]], dtype=xp.float64)
|
| 651 |
+
to_tree(Z)
|
| 652 |
+
assert_allclose(leaves_list(Z), [0, 1, 2], rtol=1e-15)
|
| 653 |
+
|
| 654 |
+
@skip_if_array_api_gpu
|
| 655 |
+
@array_api_compatible
|
| 656 |
+
def test_leaves_list_Q(self, xp):
|
| 657 |
+
for method in ['single', 'complete', 'average', 'weighted', 'centroid',
|
| 658 |
+
'median', 'ward']:
|
| 659 |
+
self.check_leaves_list_Q(method, xp)
|
| 660 |
+
|
| 661 |
+
def check_leaves_list_Q(self, method, xp):
|
| 662 |
+
# Tests leaves_list(Z) on the Q data set
|
| 663 |
+
X = xp.asarray(hierarchy_test_data.Q_X)
|
| 664 |
+
Z = linkage(X, method)
|
| 665 |
+
node = to_tree(Z)
|
| 666 |
+
assert_allclose(node.pre_order(), leaves_list(Z), rtol=1e-15)
|
| 667 |
+
|
| 668 |
+
@skip_if_array_api_gpu
|
| 669 |
+
@array_api_compatible
|
| 670 |
+
def test_Q_subtree_pre_order(self, xp):
|
| 671 |
+
# Tests that pre_order() works when called on sub-trees.
|
| 672 |
+
X = xp.asarray(hierarchy_test_data.Q_X)
|
| 673 |
+
Z = linkage(X, 'single')
|
| 674 |
+
node = to_tree(Z)
|
| 675 |
+
assert_allclose(node.pre_order(), (node.get_left().pre_order()
|
| 676 |
+
+ node.get_right().pre_order()),
|
| 677 |
+
rtol=1e-15)
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
class TestCorrespond:
|
| 681 |
+
|
| 682 |
+
@skip_if_array_api_gpu
|
| 683 |
+
@array_api_compatible
|
| 684 |
+
def test_correspond_empty(self, xp):
|
| 685 |
+
# Tests correspond(Z, y) with empty linkage and condensed distance matrix.
|
| 686 |
+
y = xp.zeros((0,), dtype=xp.float64)
|
| 687 |
+
Z = xp.zeros((0,4), dtype=xp.float64)
|
| 688 |
+
assert_raises(ValueError, correspond, Z, y)
|
| 689 |
+
|
| 690 |
+
@skip_if_array_api_gpu
|
| 691 |
+
@array_api_compatible
|
| 692 |
+
def test_correspond_2_and_up(self, xp):
|
| 693 |
+
# Tests correspond(Z, y) on linkage and CDMs over observation sets of
|
| 694 |
+
# different sizes.
|
| 695 |
+
for i in range(2, 4):
|
| 696 |
+
y = np.random.rand(i*(i-1)//2)
|
| 697 |
+
y = xp.asarray(y)
|
| 698 |
+
Z = linkage(y)
|
| 699 |
+
assert_(correspond(Z, y))
|
| 700 |
+
for i in range(4, 15, 3):
|
| 701 |
+
y = np.random.rand(i*(i-1)//2)
|
| 702 |
+
y = xp.asarray(y)
|
| 703 |
+
Z = linkage(y)
|
| 704 |
+
assert_(correspond(Z, y))
|
| 705 |
+
|
| 706 |
+
@skip_if_array_api_gpu
|
| 707 |
+
@array_api_compatible
|
| 708 |
+
def test_correspond_4_and_up(self, xp):
|
| 709 |
+
# Tests correspond(Z, y) on linkage and CDMs over observation sets of
|
| 710 |
+
# different sizes. Correspondence should be false.
|
| 711 |
+
for (i, j) in (list(zip(list(range(2, 4)), list(range(3, 5)))) +
|
| 712 |
+
list(zip(list(range(3, 5)), list(range(2, 4))))):
|
| 713 |
+
y = np.random.rand(i*(i-1)//2)
|
| 714 |
+
y2 = np.random.rand(j*(j-1)//2)
|
| 715 |
+
y = xp.asarray(y)
|
| 716 |
+
y2 = xp.asarray(y2)
|
| 717 |
+
Z = linkage(y)
|
| 718 |
+
Z2 = linkage(y2)
|
| 719 |
+
assert not correspond(Z, y2)
|
| 720 |
+
assert not correspond(Z2, y)
|
| 721 |
+
|
| 722 |
+
@skip_if_array_api_gpu
|
| 723 |
+
@array_api_compatible
|
| 724 |
+
def test_correspond_4_and_up_2(self, xp):
|
| 725 |
+
# Tests correspond(Z, y) on linkage and CDMs over observation sets of
|
| 726 |
+
# different sizes. Correspondence should be false.
|
| 727 |
+
for (i, j) in (list(zip(list(range(2, 7)), list(range(16, 21)))) +
|
| 728 |
+
list(zip(list(range(2, 7)), list(range(16, 21))))):
|
| 729 |
+
y = np.random.rand(i*(i-1)//2)
|
| 730 |
+
y2 = np.random.rand(j*(j-1)//2)
|
| 731 |
+
y = xp.asarray(y)
|
| 732 |
+
y2 = xp.asarray(y2)
|
| 733 |
+
Z = linkage(y)
|
| 734 |
+
Z2 = linkage(y2)
|
| 735 |
+
assert not correspond(Z, y2)
|
| 736 |
+
assert not correspond(Z2, y)
|
| 737 |
+
|
| 738 |
+
@skip_if_array_api_gpu
|
| 739 |
+
@array_api_compatible
|
| 740 |
+
def test_num_obs_linkage_multi_matrix(self, xp):
|
| 741 |
+
# Tests num_obs_linkage with observation matrices of multiple sizes.
|
| 742 |
+
for n in range(2, 10):
|
| 743 |
+
X = np.random.rand(n, 4)
|
| 744 |
+
Y = pdist(X)
|
| 745 |
+
Y = xp.asarray(Y)
|
| 746 |
+
Z = linkage(Y)
|
| 747 |
+
assert_equal(num_obs_linkage(Z), n)
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
class TestIsMonotonic:
|
| 751 |
+
|
| 752 |
+
@skip_if_array_api_gpu
|
| 753 |
+
@array_api_compatible
|
| 754 |
+
def test_is_monotonic_empty(self, xp):
|
| 755 |
+
# Tests is_monotonic(Z) on an empty linkage.
|
| 756 |
+
Z = xp.zeros((0, 4), dtype=xp.float64)
|
| 757 |
+
assert_raises(ValueError, is_monotonic, Z)
|
| 758 |
+
|
| 759 |
+
@skip_if_array_api_gpu
|
| 760 |
+
@array_api_compatible
|
| 761 |
+
def test_is_monotonic_1x4(self, xp):
|
| 762 |
+
# Tests is_monotonic(Z) on 1x4 linkage. Expecting True.
|
| 763 |
+
Z = xp.asarray([[0, 1, 0.3, 2]], dtype=xp.float64)
|
| 764 |
+
assert is_monotonic(Z)
|
| 765 |
+
|
| 766 |
+
@skip_if_array_api_gpu
|
| 767 |
+
@array_api_compatible
|
| 768 |
+
def test_is_monotonic_2x4_T(self, xp):
|
| 769 |
+
# Tests is_monotonic(Z) on 2x4 linkage. Expecting True.
|
| 770 |
+
Z = xp.asarray([[0, 1, 0.3, 2],
|
| 771 |
+
[2, 3, 0.4, 3]], dtype=xp.float64)
|
| 772 |
+
assert is_monotonic(Z)
|
| 773 |
+
|
| 774 |
+
@skip_if_array_api_gpu
|
| 775 |
+
@array_api_compatible
|
| 776 |
+
def test_is_monotonic_2x4_F(self, xp):
|
| 777 |
+
# Tests is_monotonic(Z) on 2x4 linkage. Expecting False.
|
| 778 |
+
Z = xp.asarray([[0, 1, 0.4, 2],
|
| 779 |
+
[2, 3, 0.3, 3]], dtype=xp.float64)
|
| 780 |
+
assert not is_monotonic(Z)
|
| 781 |
+
|
| 782 |
+
@skip_if_array_api_gpu
|
| 783 |
+
@array_api_compatible
|
| 784 |
+
def test_is_monotonic_3x4_T(self, xp):
|
| 785 |
+
# Tests is_monotonic(Z) on 3x4 linkage. Expecting True.
|
| 786 |
+
Z = xp.asarray([[0, 1, 0.3, 2],
|
| 787 |
+
[2, 3, 0.4, 2],
|
| 788 |
+
[4, 5, 0.6, 4]], dtype=xp.float64)
|
| 789 |
+
assert is_monotonic(Z)
|
| 790 |
+
|
| 791 |
+
@skip_if_array_api_gpu
|
| 792 |
+
@array_api_compatible
|
| 793 |
+
def test_is_monotonic_3x4_F1(self, xp):
|
| 794 |
+
# Tests is_monotonic(Z) on 3x4 linkage (case 1). Expecting False.
|
| 795 |
+
Z = xp.asarray([[0, 1, 0.3, 2],
|
| 796 |
+
[2, 3, 0.2, 2],
|
| 797 |
+
[4, 5, 0.6, 4]], dtype=xp.float64)
|
| 798 |
+
assert not is_monotonic(Z)
|
| 799 |
+
|
| 800 |
+
@skip_if_array_api_gpu
|
| 801 |
+
@array_api_compatible
|
| 802 |
+
def test_is_monotonic_3x4_F2(self, xp):
|
| 803 |
+
# Tests is_monotonic(Z) on 3x4 linkage (case 2). Expecting False.
|
| 804 |
+
Z = xp.asarray([[0, 1, 0.8, 2],
|
| 805 |
+
[2, 3, 0.4, 2],
|
| 806 |
+
[4, 5, 0.6, 4]], dtype=xp.float64)
|
| 807 |
+
assert not is_monotonic(Z)
|
| 808 |
+
|
| 809 |
+
@skip_if_array_api_gpu
|
| 810 |
+
@array_api_compatible
|
| 811 |
+
def test_is_monotonic_3x4_F3(self, xp):
|
| 812 |
+
# Tests is_monotonic(Z) on 3x4 linkage (case 3). Expecting False
|
| 813 |
+
Z = xp.asarray([[0, 1, 0.3, 2],
|
| 814 |
+
[2, 3, 0.4, 2],
|
| 815 |
+
[4, 5, 0.2, 4]], dtype=xp.float64)
|
| 816 |
+
assert not is_monotonic(Z)
|
| 817 |
+
|
| 818 |
+
@skip_if_array_api_gpu
|
| 819 |
+
@array_api_compatible
|
| 820 |
+
def test_is_monotonic_tdist_linkage1(self, xp):
|
| 821 |
+
# Tests is_monotonic(Z) on clustering generated by single linkage on
|
| 822 |
+
# tdist data set. Expecting True.
|
| 823 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
|
| 824 |
+
assert is_monotonic(Z)
|
| 825 |
+
|
| 826 |
+
@skip_if_array_api_gpu
|
| 827 |
+
@array_api_compatible
|
| 828 |
+
def test_is_monotonic_tdist_linkage2(self, xp):
|
| 829 |
+
# Tests is_monotonic(Z) on clustering generated by single linkage on
|
| 830 |
+
# tdist data set. Perturbing. Expecting False.
|
| 831 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
|
| 832 |
+
Z[2,2] = 0.0
|
| 833 |
+
assert not is_monotonic(Z)
|
| 834 |
+
|
| 835 |
+
@skip_if_array_api_gpu
|
| 836 |
+
@array_api_compatible
|
| 837 |
+
def test_is_monotonic_Q_linkage(self, xp):
|
| 838 |
+
# Tests is_monotonic(Z) on clustering generated by single linkage on
|
| 839 |
+
# Q data set. Expecting True.
|
| 840 |
+
X = xp.asarray(hierarchy_test_data.Q_X)
|
| 841 |
+
Z = linkage(X, 'single')
|
| 842 |
+
assert is_monotonic(Z)
|
| 843 |
+
|
| 844 |
+
|
| 845 |
+
class TestMaxDists:
|
| 846 |
+
|
| 847 |
+
@skip_if_array_api_gpu
|
| 848 |
+
@array_api_compatible
|
| 849 |
+
def test_maxdists_empty_linkage(self, xp):
|
| 850 |
+
# Tests maxdists(Z) on empty linkage. Expecting exception.
|
| 851 |
+
Z = xp.zeros((0, 4), dtype=xp.float64)
|
| 852 |
+
assert_raises(ValueError, maxdists, Z)
|
| 853 |
+
|
| 854 |
+
@skip_if_array_api_gpu
|
| 855 |
+
@array_api_compatible
|
| 856 |
+
def test_maxdists_one_cluster_linkage(self, xp):
|
| 857 |
+
# Tests maxdists(Z) on linkage with one cluster.
|
| 858 |
+
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
| 859 |
+
MD = maxdists(Z)
|
| 860 |
+
expectedMD = calculate_maximum_distances(Z, xp)
|
| 861 |
+
xp_assert_close(MD, expectedMD, atol=1e-15)
|
| 862 |
+
|
| 863 |
+
@skip_if_array_api_gpu
|
| 864 |
+
@array_api_compatible
|
| 865 |
+
def test_maxdists_Q_linkage(self, xp):
|
| 866 |
+
for method in ['single', 'complete', 'ward', 'centroid', 'median']:
|
| 867 |
+
self.check_maxdists_Q_linkage(method, xp)
|
| 868 |
+
|
| 869 |
+
def check_maxdists_Q_linkage(self, method, xp):
|
| 870 |
+
# Tests maxdists(Z) on the Q data set
|
| 871 |
+
X = xp.asarray(hierarchy_test_data.Q_X)
|
| 872 |
+
Z = linkage(X, method)
|
| 873 |
+
MD = maxdists(Z)
|
| 874 |
+
expectedMD = calculate_maximum_distances(Z, xp)
|
| 875 |
+
xp_assert_close(MD, expectedMD, atol=1e-15)
|
| 876 |
+
|
| 877 |
+
|
| 878 |
+
class TestMaxInconsts:
|
| 879 |
+
|
| 880 |
+
@skip_if_array_api_gpu
|
| 881 |
+
@array_api_compatible
|
| 882 |
+
def test_maxinconsts_empty_linkage(self, xp):
|
| 883 |
+
# Tests maxinconsts(Z, R) on empty linkage. Expecting exception.
|
| 884 |
+
Z = xp.zeros((0, 4), dtype=xp.float64)
|
| 885 |
+
R = xp.zeros((0, 4), dtype=xp.float64)
|
| 886 |
+
assert_raises(ValueError, maxinconsts, Z, R)
|
| 887 |
+
|
| 888 |
+
@array_api_compatible
|
| 889 |
+
def test_maxinconsts_difrow_linkage(self, xp):
|
| 890 |
+
# Tests maxinconsts(Z, R) on linkage and inconsistency matrices with
|
| 891 |
+
# different numbers of clusters. Expecting exception.
|
| 892 |
+
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
| 893 |
+
R = np.random.rand(2, 4)
|
| 894 |
+
R = xp.asarray(R)
|
| 895 |
+
assert_raises(ValueError, maxinconsts, Z, R)
|
| 896 |
+
|
| 897 |
+
@skip_if_array_api_gpu
|
| 898 |
+
@array_api_compatible
|
| 899 |
+
def test_maxinconsts_one_cluster_linkage(self, xp):
|
| 900 |
+
# Tests maxinconsts(Z, R) on linkage with one cluster.
|
| 901 |
+
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
| 902 |
+
R = xp.asarray([[0, 0, 0, 0.3]], dtype=xp.float64)
|
| 903 |
+
MD = maxinconsts(Z, R)
|
| 904 |
+
expectedMD = calculate_maximum_inconsistencies(Z, R, xp=xp)
|
| 905 |
+
xp_assert_close(MD, expectedMD, atol=1e-15)
|
| 906 |
+
|
| 907 |
+
@skip_if_array_api_gpu
|
| 908 |
+
@array_api_compatible
|
| 909 |
+
def test_maxinconsts_Q_linkage(self, xp):
|
| 910 |
+
for method in ['single', 'complete', 'ward', 'centroid', 'median']:
|
| 911 |
+
self.check_maxinconsts_Q_linkage(method, xp)
|
| 912 |
+
|
| 913 |
+
def check_maxinconsts_Q_linkage(self, method, xp):
|
| 914 |
+
# Tests maxinconsts(Z, R) on the Q data set
|
| 915 |
+
X = xp.asarray(hierarchy_test_data.Q_X)
|
| 916 |
+
Z = linkage(X, method)
|
| 917 |
+
R = inconsistent(Z)
|
| 918 |
+
MD = maxinconsts(Z, R)
|
| 919 |
+
expectedMD = calculate_maximum_inconsistencies(Z, R, xp=xp)
|
| 920 |
+
xp_assert_close(MD, expectedMD, atol=1e-15)
|
| 921 |
+
|
| 922 |
+
|
| 923 |
+
class TestMaxRStat:
|
| 924 |
+
|
| 925 |
+
@array_api_compatible
|
| 926 |
+
def test_maxRstat_invalid_index(self, xp):
|
| 927 |
+
for i in [3.3, -1, 4]:
|
| 928 |
+
self.check_maxRstat_invalid_index(i, xp)
|
| 929 |
+
|
| 930 |
+
def check_maxRstat_invalid_index(self, i, xp):
|
| 931 |
+
# Tests maxRstat(Z, R, i). Expecting exception.
|
| 932 |
+
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
| 933 |
+
R = xp.asarray([[0, 0, 0, 0.3]], dtype=xp.float64)
|
| 934 |
+
if isinstance(i, int):
|
| 935 |
+
assert_raises(ValueError, maxRstat, Z, R, i)
|
| 936 |
+
else:
|
| 937 |
+
assert_raises(TypeError, maxRstat, Z, R, i)
|
| 938 |
+
|
| 939 |
+
@skip_if_array_api_gpu
|
| 940 |
+
@array_api_compatible
|
| 941 |
+
def test_maxRstat_empty_linkage(self, xp):
|
| 942 |
+
for i in range(4):
|
| 943 |
+
self.check_maxRstat_empty_linkage(i, xp)
|
| 944 |
+
|
| 945 |
+
def check_maxRstat_empty_linkage(self, i, xp):
|
| 946 |
+
# Tests maxRstat(Z, R, i) on empty linkage. Expecting exception.
|
| 947 |
+
Z = xp.zeros((0, 4), dtype=xp.float64)
|
| 948 |
+
R = xp.zeros((0, 4), dtype=xp.float64)
|
| 949 |
+
assert_raises(ValueError, maxRstat, Z, R, i)
|
| 950 |
+
|
| 951 |
+
@array_api_compatible
|
| 952 |
+
def test_maxRstat_difrow_linkage(self, xp):
|
| 953 |
+
for i in range(4):
|
| 954 |
+
self.check_maxRstat_difrow_linkage(i, xp)
|
| 955 |
+
|
| 956 |
+
def check_maxRstat_difrow_linkage(self, i, xp):
|
| 957 |
+
# Tests maxRstat(Z, R, i) on linkage and inconsistency matrices with
|
| 958 |
+
# different numbers of clusters. Expecting exception.
|
| 959 |
+
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
| 960 |
+
R = np.random.rand(2, 4)
|
| 961 |
+
R = xp.asarray(R)
|
| 962 |
+
assert_raises(ValueError, maxRstat, Z, R, i)
|
| 963 |
+
|
| 964 |
+
@skip_if_array_api_gpu
|
| 965 |
+
@array_api_compatible
|
| 966 |
+
def test_maxRstat_one_cluster_linkage(self, xp):
|
| 967 |
+
for i in range(4):
|
| 968 |
+
self.check_maxRstat_one_cluster_linkage(i, xp)
|
| 969 |
+
|
| 970 |
+
def check_maxRstat_one_cluster_linkage(self, i, xp):
|
| 971 |
+
# Tests maxRstat(Z, R, i) on linkage with one cluster.
|
| 972 |
+
Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
|
| 973 |
+
R = xp.asarray([[0, 0, 0, 0.3]], dtype=xp.float64)
|
| 974 |
+
MD = maxRstat(Z, R, 1)
|
| 975 |
+
expectedMD = calculate_maximum_inconsistencies(Z, R, 1, xp)
|
| 976 |
+
xp_assert_close(MD, expectedMD, atol=1e-15)
|
| 977 |
+
|
| 978 |
+
@skip_if_array_api_gpu
|
| 979 |
+
@array_api_compatible
|
| 980 |
+
def test_maxRstat_Q_linkage(self, xp):
|
| 981 |
+
for method in ['single', 'complete', 'ward', 'centroid', 'median']:
|
| 982 |
+
for i in range(4):
|
| 983 |
+
self.check_maxRstat_Q_linkage(method, i, xp)
|
| 984 |
+
|
| 985 |
+
def check_maxRstat_Q_linkage(self, method, i, xp):
|
| 986 |
+
# Tests maxRstat(Z, R, i) on the Q data set
|
| 987 |
+
X = xp.asarray(hierarchy_test_data.Q_X)
|
| 988 |
+
Z = linkage(X, method)
|
| 989 |
+
R = inconsistent(Z)
|
| 990 |
+
MD = maxRstat(Z, R, 1)
|
| 991 |
+
expectedMD = calculate_maximum_inconsistencies(Z, R, 1, xp)
|
| 992 |
+
xp_assert_close(MD, expectedMD, atol=1e-15)
|
| 993 |
+
|
| 994 |
+
|
| 995 |
+
class TestDendrogram:
|
| 996 |
+
|
| 997 |
+
@skip_if_array_api_gpu
|
| 998 |
+
@array_api_compatible
|
| 999 |
+
def test_dendrogram_single_linkage_tdist(self, xp):
|
| 1000 |
+
# Tests dendrogram calculation on single linkage of the tdist data set.
|
| 1001 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
|
| 1002 |
+
R = dendrogram(Z, no_plot=True)
|
| 1003 |
+
leaves = R["leaves"]
|
| 1004 |
+
assert_equal(leaves, [2, 5, 1, 0, 3, 4])
|
| 1005 |
+
|
| 1006 |
+
@skip_if_array_api_gpu
|
| 1007 |
+
@array_api_compatible
|
| 1008 |
+
def test_valid_orientation(self, xp):
|
| 1009 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
|
| 1010 |
+
assert_raises(ValueError, dendrogram, Z, orientation="foo")
|
| 1011 |
+
|
| 1012 |
+
@skip_if_array_api_gpu
|
| 1013 |
+
@array_api_compatible
|
| 1014 |
+
def test_labels_as_array_or_list(self, xp):
|
| 1015 |
+
# test for gh-12418
|
| 1016 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
|
| 1017 |
+
labels = xp.asarray([1, 3, 2, 6, 4, 5])
|
| 1018 |
+
result1 = dendrogram(Z, labels=labels, no_plot=True)
|
| 1019 |
+
result2 = dendrogram(Z, labels=list(labels), no_plot=True)
|
| 1020 |
+
assert result1 == result2
|
| 1021 |
+
|
| 1022 |
+
@skip_if_array_api_gpu
|
| 1023 |
+
@array_api_compatible
|
| 1024 |
+
@pytest.mark.skipif(not have_matplotlib, reason="no matplotlib")
|
| 1025 |
+
def test_valid_label_size(self, xp):
|
| 1026 |
+
link = xp.asarray([
|
| 1027 |
+
[0, 1, 1.0, 4],
|
| 1028 |
+
[2, 3, 1.0, 5],
|
| 1029 |
+
[4, 5, 2.0, 6],
|
| 1030 |
+
])
|
| 1031 |
+
plt.figure()
|
| 1032 |
+
with pytest.raises(ValueError) as exc_info:
|
| 1033 |
+
dendrogram(link, labels=list(range(100)))
|
| 1034 |
+
assert "Dimensions of Z and labels must be consistent."\
|
| 1035 |
+
in str(exc_info.value)
|
| 1036 |
+
|
| 1037 |
+
with pytest.raises(
|
| 1038 |
+
ValueError,
|
| 1039 |
+
match="Dimensions of Z and labels must be consistent."):
|
| 1040 |
+
dendrogram(link, labels=[])
|
| 1041 |
+
|
| 1042 |
+
plt.close()
|
| 1043 |
+
|
| 1044 |
+
@skip_if_array_api_gpu
|
| 1045 |
+
@array_api_compatible
|
| 1046 |
+
@pytest.mark.skipif(not have_matplotlib, reason="no matplotlib")
|
| 1047 |
+
def test_dendrogram_plot(self, xp):
|
| 1048 |
+
for orientation in ['top', 'bottom', 'left', 'right']:
|
| 1049 |
+
self.check_dendrogram_plot(orientation, xp)
|
| 1050 |
+
|
| 1051 |
+
def check_dendrogram_plot(self, orientation, xp):
|
| 1052 |
+
# Tests dendrogram plotting.
|
| 1053 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
|
| 1054 |
+
expected = {'color_list': ['C1', 'C0', 'C0', 'C0', 'C0'],
|
| 1055 |
+
'dcoord': [[0.0, 138.0, 138.0, 0.0],
|
| 1056 |
+
[0.0, 219.0, 219.0, 0.0],
|
| 1057 |
+
[0.0, 255.0, 255.0, 219.0],
|
| 1058 |
+
[0.0, 268.0, 268.0, 255.0],
|
| 1059 |
+
[138.0, 295.0, 295.0, 268.0]],
|
| 1060 |
+
'icoord': [[5.0, 5.0, 15.0, 15.0],
|
| 1061 |
+
[45.0, 45.0, 55.0, 55.0],
|
| 1062 |
+
[35.0, 35.0, 50.0, 50.0],
|
| 1063 |
+
[25.0, 25.0, 42.5, 42.5],
|
| 1064 |
+
[10.0, 10.0, 33.75, 33.75]],
|
| 1065 |
+
'ivl': ['2', '5', '1', '0', '3', '4'],
|
| 1066 |
+
'leaves': [2, 5, 1, 0, 3, 4],
|
| 1067 |
+
'leaves_color_list': ['C1', 'C1', 'C0', 'C0', 'C0', 'C0'],
|
| 1068 |
+
}
|
| 1069 |
+
|
| 1070 |
+
fig = plt.figure()
|
| 1071 |
+
ax = fig.add_subplot(221)
|
| 1072 |
+
|
| 1073 |
+
# test that dendrogram accepts ax keyword
|
| 1074 |
+
R1 = dendrogram(Z, ax=ax, orientation=orientation)
|
| 1075 |
+
R1['dcoord'] = np.asarray(R1['dcoord'])
|
| 1076 |
+
assert_equal(R1, expected)
|
| 1077 |
+
|
| 1078 |
+
# test that dendrogram accepts and handle the leaf_font_size and
|
| 1079 |
+
# leaf_rotation keywords
|
| 1080 |
+
dendrogram(Z, ax=ax, orientation=orientation,
|
| 1081 |
+
leaf_font_size=20, leaf_rotation=90)
|
| 1082 |
+
testlabel = (
|
| 1083 |
+
ax.get_xticklabels()[0]
|
| 1084 |
+
if orientation in ['top', 'bottom']
|
| 1085 |
+
else ax.get_yticklabels()[0]
|
| 1086 |
+
)
|
| 1087 |
+
assert_equal(testlabel.get_rotation(), 90)
|
| 1088 |
+
assert_equal(testlabel.get_size(), 20)
|
| 1089 |
+
dendrogram(Z, ax=ax, orientation=orientation,
|
| 1090 |
+
leaf_rotation=90)
|
| 1091 |
+
testlabel = (
|
| 1092 |
+
ax.get_xticklabels()[0]
|
| 1093 |
+
if orientation in ['top', 'bottom']
|
| 1094 |
+
else ax.get_yticklabels()[0]
|
| 1095 |
+
)
|
| 1096 |
+
assert_equal(testlabel.get_rotation(), 90)
|
| 1097 |
+
dendrogram(Z, ax=ax, orientation=orientation,
|
| 1098 |
+
leaf_font_size=20)
|
| 1099 |
+
testlabel = (
|
| 1100 |
+
ax.get_xticklabels()[0]
|
| 1101 |
+
if orientation in ['top', 'bottom']
|
| 1102 |
+
else ax.get_yticklabels()[0]
|
| 1103 |
+
)
|
| 1104 |
+
assert_equal(testlabel.get_size(), 20)
|
| 1105 |
+
plt.close()
|
| 1106 |
+
|
| 1107 |
+
# test plotting to gca (will import pylab)
|
| 1108 |
+
R2 = dendrogram(Z, orientation=orientation)
|
| 1109 |
+
plt.close()
|
| 1110 |
+
R2['dcoord'] = np.asarray(R2['dcoord'])
|
| 1111 |
+
assert_equal(R2, expected)
|
| 1112 |
+
|
| 1113 |
+
@skip_if_array_api_gpu
|
| 1114 |
+
@array_api_compatible
|
| 1115 |
+
@pytest.mark.skipif(not have_matplotlib, reason="no matplotlib")
|
| 1116 |
+
def test_dendrogram_truncate_mode(self, xp):
|
| 1117 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
|
| 1118 |
+
|
| 1119 |
+
R = dendrogram(Z, 2, 'lastp', show_contracted=True)
|
| 1120 |
+
plt.close()
|
| 1121 |
+
R['dcoord'] = np.asarray(R['dcoord'])
|
| 1122 |
+
assert_equal(R, {'color_list': ['C0'],
|
| 1123 |
+
'dcoord': [[0.0, 295.0, 295.0, 0.0]],
|
| 1124 |
+
'icoord': [[5.0, 5.0, 15.0, 15.0]],
|
| 1125 |
+
'ivl': ['(2)', '(4)'],
|
| 1126 |
+
'leaves': [6, 9],
|
| 1127 |
+
'leaves_color_list': ['C0', 'C0'],
|
| 1128 |
+
})
|
| 1129 |
+
|
| 1130 |
+
R = dendrogram(Z, 2, 'mtica', show_contracted=True)
|
| 1131 |
+
plt.close()
|
| 1132 |
+
R['dcoord'] = np.asarray(R['dcoord'])
|
| 1133 |
+
assert_equal(R, {'color_list': ['C1', 'C0', 'C0', 'C0'],
|
| 1134 |
+
'dcoord': [[0.0, 138.0, 138.0, 0.0],
|
| 1135 |
+
[0.0, 255.0, 255.0, 0.0],
|
| 1136 |
+
[0.0, 268.0, 268.0, 255.0],
|
| 1137 |
+
[138.0, 295.0, 295.0, 268.0]],
|
| 1138 |
+
'icoord': [[5.0, 5.0, 15.0, 15.0],
|
| 1139 |
+
[35.0, 35.0, 45.0, 45.0],
|
| 1140 |
+
[25.0, 25.0, 40.0, 40.0],
|
| 1141 |
+
[10.0, 10.0, 32.5, 32.5]],
|
| 1142 |
+
'ivl': ['2', '5', '1', '0', '(2)'],
|
| 1143 |
+
'leaves': [2, 5, 1, 0, 7],
|
| 1144 |
+
'leaves_color_list': ['C1', 'C1', 'C0', 'C0', 'C0'],
|
| 1145 |
+
})
|
| 1146 |
+
|
| 1147 |
+
@skip_if_array_api_gpu
|
| 1148 |
+
@array_api_compatible
|
| 1149 |
+
def test_dendrogram_colors(self, xp):
|
| 1150 |
+
# Tests dendrogram plots with alternate colors
|
| 1151 |
+
Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
|
| 1152 |
+
|
| 1153 |
+
set_link_color_palette(['c', 'm', 'y', 'k'])
|
| 1154 |
+
R = dendrogram(Z, no_plot=True,
|
| 1155 |
+
above_threshold_color='g', color_threshold=250)
|
| 1156 |
+
set_link_color_palette(['g', 'r', 'c', 'm', 'y', 'k'])
|
| 1157 |
+
|
| 1158 |
+
color_list = R['color_list']
|
| 1159 |
+
assert_equal(color_list, ['c', 'm', 'g', 'g', 'g'])
|
| 1160 |
+
|
| 1161 |
+
# reset color palette (global list)
|
| 1162 |
+
set_link_color_palette(None)
|
| 1163 |
+
|
| 1164 |
+
@skip_if_array_api_gpu
|
| 1165 |
+
@array_api_compatible
|
| 1166 |
+
def test_dendrogram_leaf_colors_zero_dist(self, xp):
|
| 1167 |
+
# tests that the colors of leafs are correct for tree
|
| 1168 |
+
# with two identical points
|
| 1169 |
+
x = xp.asarray([[1, 0, 0],
|
| 1170 |
+
[0, 0, 1],
|
| 1171 |
+
[0, 2, 0],
|
| 1172 |
+
[0, 0, 1],
|
| 1173 |
+
[0, 1, 0],
|
| 1174 |
+
[0, 1, 0]])
|
| 1175 |
+
z = linkage(x, "single")
|
| 1176 |
+
d = dendrogram(z, no_plot=True)
|
| 1177 |
+
exp_colors = ['C0', 'C1', 'C1', 'C0', 'C2', 'C2']
|
| 1178 |
+
colors = d["leaves_color_list"]
|
| 1179 |
+
assert_equal(colors, exp_colors)
|
| 1180 |
+
|
| 1181 |
+
@skip_if_array_api_gpu
|
| 1182 |
+
@array_api_compatible
|
| 1183 |
+
def test_dendrogram_leaf_colors(self, xp):
|
| 1184 |
+
# tests that the colors are correct for a tree
|
| 1185 |
+
# with two near points ((0, 0, 1.1) and (0, 0, 1))
|
| 1186 |
+
x = xp.asarray([[1, 0, 0],
|
| 1187 |
+
[0, 0, 1.1],
|
| 1188 |
+
[0, 2, 0],
|
| 1189 |
+
[0, 0, 1],
|
| 1190 |
+
[0, 1, 0],
|
| 1191 |
+
[0, 1, 0]])
|
| 1192 |
+
z = linkage(x, "single")
|
| 1193 |
+
d = dendrogram(z, no_plot=True)
|
| 1194 |
+
exp_colors = ['C0', 'C1', 'C1', 'C0', 'C2', 'C2']
|
| 1195 |
+
colors = d["leaves_color_list"]
|
| 1196 |
+
assert_equal(colors, exp_colors)
|
| 1197 |
+
|
| 1198 |
+
|
| 1199 |
+
def calculate_maximum_distances(Z, xp):
|
| 1200 |
+
# Used for testing correctness of maxdists.
|
| 1201 |
+
n = Z.shape[0] + 1
|
| 1202 |
+
B = xp.zeros((n-1,), dtype=Z.dtype)
|
| 1203 |
+
q = xp.zeros((3,))
|
| 1204 |
+
for i in range(0, n - 1):
|
| 1205 |
+
q[:] = 0.0
|
| 1206 |
+
left = Z[i, 0]
|
| 1207 |
+
right = Z[i, 1]
|
| 1208 |
+
if left >= n:
|
| 1209 |
+
q[0] = B[xp.asarray(left, dtype=xp.int64) - n]
|
| 1210 |
+
if right >= n:
|
| 1211 |
+
q[1] = B[xp.asarray(right, dtype=xp.int64) - n]
|
| 1212 |
+
q[2] = Z[i, 2]
|
| 1213 |
+
B[i] = xp.max(q)
|
| 1214 |
+
return B
|
| 1215 |
+
|
| 1216 |
+
|
| 1217 |
+
def calculate_maximum_inconsistencies(Z, R, k=3, xp=np):
|
| 1218 |
+
# Used for testing correctness of maxinconsts.
|
| 1219 |
+
n = Z.shape[0] + 1
|
| 1220 |
+
dtype = xp.result_type(Z, R)
|
| 1221 |
+
B = xp.zeros((n-1,), dtype=dtype)
|
| 1222 |
+
q = xp.zeros((3,))
|
| 1223 |
+
for i in range(0, n - 1):
|
| 1224 |
+
q[:] = 0.0
|
| 1225 |
+
left = Z[i, 0]
|
| 1226 |
+
right = Z[i, 1]
|
| 1227 |
+
if left >= n:
|
| 1228 |
+
q[0] = B[xp.asarray(left, dtype=xp.int64) - n]
|
| 1229 |
+
if right >= n:
|
| 1230 |
+
q[1] = B[xp.asarray(right, dtype=xp.int64) - n]
|
| 1231 |
+
q[2] = R[i, k]
|
| 1232 |
+
B[i] = xp.max(q)
|
| 1233 |
+
return B
|
| 1234 |
+
|
| 1235 |
+
|
| 1236 |
+
@skip_if_array_api_gpu
|
| 1237 |
+
@array_api_compatible
|
| 1238 |
+
def test_unsupported_uncondensed_distance_matrix_linkage_warning(xp):
|
| 1239 |
+
assert_warns(ClusterWarning, linkage, xp.asarray([[0, 1], [1, 0]]))
|
| 1240 |
+
|
| 1241 |
+
|
| 1242 |
+
@array_api_compatible
|
| 1243 |
+
def test_euclidean_linkage_value_error(xp):
|
| 1244 |
+
for method in scipy.cluster.hierarchy._EUCLIDEAN_METHODS:
|
| 1245 |
+
assert_raises(ValueError, linkage, xp.asarray([[1, 1], [1, 1]]),
|
| 1246 |
+
method=method, metric='cityblock')
|
| 1247 |
+
|
| 1248 |
+
|
| 1249 |
+
@skip_if_array_api_gpu
|
| 1250 |
+
@array_api_compatible
|
| 1251 |
+
def test_2x2_linkage(xp):
|
| 1252 |
+
Z1 = linkage(xp.asarray([1]), method='single', metric='euclidean')
|
| 1253 |
+
Z2 = linkage(xp.asarray([[0, 1], [0, 0]]), method='single', metric='euclidean')
|
| 1254 |
+
xp_assert_close(Z1, Z2, rtol=1e-15)
|
| 1255 |
+
|
| 1256 |
+
|
| 1257 |
+
@skip_if_array_api_gpu
|
| 1258 |
+
@array_api_compatible
|
| 1259 |
+
def test_node_compare(xp):
|
| 1260 |
+
np.random.seed(23)
|
| 1261 |
+
nobs = 50
|
| 1262 |
+
X = np.random.randn(nobs, 4)
|
| 1263 |
+
X = xp.asarray(X)
|
| 1264 |
+
Z = scipy.cluster.hierarchy.ward(X)
|
| 1265 |
+
tree = to_tree(Z)
|
| 1266 |
+
assert_(tree > tree.get_left())
|
| 1267 |
+
assert_(tree.get_right() > tree.get_left())
|
| 1268 |
+
assert_(tree.get_right() == tree.get_right())
|
| 1269 |
+
assert_(tree.get_right() != tree.get_left())
|
| 1270 |
+
|
| 1271 |
+
|
| 1272 |
+
@skip_if_array_api_gpu
|
| 1273 |
+
@array_api_compatible
|
| 1274 |
+
@skip_if_array_api_backend('numpy.array_api')
|
| 1275 |
+
def test_cut_tree(xp):
|
| 1276 |
+
np.random.seed(23)
|
| 1277 |
+
nobs = 50
|
| 1278 |
+
X = np.random.randn(nobs, 4)
|
| 1279 |
+
X = xp.asarray(X)
|
| 1280 |
+
Z = scipy.cluster.hierarchy.ward(X)
|
| 1281 |
+
cutree = cut_tree(Z)
|
| 1282 |
+
|
| 1283 |
+
# cutree.dtype varies between int32 and int64 over platforms
|
| 1284 |
+
xp_assert_close(cutree[:, 0], xp.arange(nobs), rtol=1e-15, check_dtype=False)
|
| 1285 |
+
xp_assert_close(cutree[:, -1], xp.zeros(nobs), rtol=1e-15, check_dtype=False)
|
| 1286 |
+
assert_equal(np.asarray(cutree).max(0), np.arange(nobs - 1, -1, -1))
|
| 1287 |
+
|
| 1288 |
+
xp_assert_close(cutree[:, [-5]], cut_tree(Z, n_clusters=5), rtol=1e-15)
|
| 1289 |
+
xp_assert_close(cutree[:, [-5, -10]], cut_tree(Z, n_clusters=[5, 10]), rtol=1e-15)
|
| 1290 |
+
xp_assert_close(cutree[:, [-10, -5]], cut_tree(Z, n_clusters=[10, 5]), rtol=1e-15)
|
| 1291 |
+
|
| 1292 |
+
nodes = _order_cluster_tree(Z)
|
| 1293 |
+
heights = xp.asarray([node.dist for node in nodes])
|
| 1294 |
+
|
| 1295 |
+
xp_assert_close(cutree[:, np.searchsorted(heights, [5])],
|
| 1296 |
+
cut_tree(Z, height=5), rtol=1e-15)
|
| 1297 |
+
xp_assert_close(cutree[:, np.searchsorted(heights, [5, 10])],
|
| 1298 |
+
cut_tree(Z, height=[5, 10]), rtol=1e-15)
|
| 1299 |
+
xp_assert_close(cutree[:, np.searchsorted(heights, [10, 5])],
|
| 1300 |
+
cut_tree(Z, height=[10, 5]), rtol=1e-15)
|
| 1301 |
+
|
| 1302 |
+
|
| 1303 |
+
@skip_if_array_api_gpu
|
| 1304 |
+
@array_api_compatible
|
| 1305 |
+
def test_optimal_leaf_ordering(xp):
|
| 1306 |
+
# test with the distance vector y
|
| 1307 |
+
Z = optimal_leaf_ordering(linkage(xp.asarray(hierarchy_test_data.ytdist)),
|
| 1308 |
+
xp.asarray(hierarchy_test_data.ytdist))
|
| 1309 |
+
expectedZ = hierarchy_test_data.linkage_ytdist_single_olo
|
| 1310 |
+
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
|
| 1311 |
+
|
| 1312 |
+
# test with the observation matrix X
|
| 1313 |
+
Z = optimal_leaf_ordering(linkage(xp.asarray(hierarchy_test_data.X), 'ward'),
|
| 1314 |
+
xp.asarray(hierarchy_test_data.X))
|
| 1315 |
+
expectedZ = hierarchy_test_data.linkage_X_ward_olo
|
| 1316 |
+
xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
|
| 1317 |
+
|
| 1318 |
+
|
| 1319 |
+
@skip_if_array_api
|
| 1320 |
+
def test_Heap():
|
| 1321 |
+
values = np.array([2, -1, 0, -1.5, 3])
|
| 1322 |
+
heap = Heap(values)
|
| 1323 |
+
|
| 1324 |
+
pair = heap.get_min()
|
| 1325 |
+
assert_equal(pair['key'], 3)
|
| 1326 |
+
assert_equal(pair['value'], -1.5)
|
| 1327 |
+
|
| 1328 |
+
heap.remove_min()
|
| 1329 |
+
pair = heap.get_min()
|
| 1330 |
+
assert_equal(pair['key'], 1)
|
| 1331 |
+
assert_equal(pair['value'], -1)
|
| 1332 |
+
|
| 1333 |
+
heap.change_value(1, 2.5)
|
| 1334 |
+
pair = heap.get_min()
|
| 1335 |
+
assert_equal(pair['key'], 2)
|
| 1336 |
+
assert_equal(pair['value'], 0)
|
| 1337 |
+
|
| 1338 |
+
heap.remove_min()
|
| 1339 |
+
heap.remove_min()
|
| 1340 |
+
|
| 1341 |
+
heap.change_value(1, 10)
|
| 1342 |
+
pair = heap.get_min()
|
| 1343 |
+
assert_equal(pair['key'], 4)
|
| 1344 |
+
assert_equal(pair['value'], 3)
|
| 1345 |
+
|
| 1346 |
+
heap.remove_min()
|
| 1347 |
+
pair = heap.get_min()
|
| 1348 |
+
assert_equal(pair['key'], 1)
|
| 1349 |
+
assert_equal(pair['value'], 10)
|
rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_vq.py
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
from numpy.testing import (
|
| 6 |
+
assert_array_equal, assert_allclose, assert_equal, assert_, suppress_warnings
|
| 7 |
+
)
|
| 8 |
+
import pytest
|
| 9 |
+
from pytest import raises as assert_raises
|
| 10 |
+
|
| 11 |
+
from scipy.cluster.vq import (kmeans, kmeans2, py_vq, vq, whiten,
|
| 12 |
+
ClusterError, _krandinit)
|
| 13 |
+
from scipy.cluster import _vq
|
| 14 |
+
from scipy.conftest import (
|
| 15 |
+
array_api_compatible,
|
| 16 |
+
skip_if_array_api,
|
| 17 |
+
skip_if_array_api_gpu
|
| 18 |
+
)
|
| 19 |
+
from scipy.sparse._sputils import matrix
|
| 20 |
+
|
| 21 |
+
from scipy._lib._array_api import (
|
| 22 |
+
SCIPY_ARRAY_API, copy, cov, xp_assert_close, xp_assert_equal
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
TESTDATA_2D = np.array([
|
| 26 |
+
-2.2, 1.17, -1.63, 1.69, -2.04, 4.38, -3.09, 0.95, -1.7, 4.79, -1.68, 0.68,
|
| 27 |
+
-2.26, 3.34, -2.29, 2.55, -1.72, -0.72, -1.99, 2.34, -2.75, 3.43, -2.45,
|
| 28 |
+
2.41, -4.26, 3.65, -1.57, 1.87, -1.96, 4.03, -3.01, 3.86, -2.53, 1.28,
|
| 29 |
+
-4.0, 3.95, -1.62, 1.25, -3.42, 3.17, -1.17, 0.12, -3.03, -0.27, -2.07,
|
| 30 |
+
-0.55, -1.17, 1.34, -2.82, 3.08, -2.44, 0.24, -1.71, 2.48, -5.23, 4.29,
|
| 31 |
+
-2.08, 3.69, -1.89, 3.62, -2.09, 0.26, -0.92, 1.07, -2.25, 0.88, -2.25,
|
| 32 |
+
2.02, -4.31, 3.86, -2.03, 3.42, -2.76, 0.3, -2.48, -0.29, -3.42, 3.21,
|
| 33 |
+
-2.3, 1.73, -2.84, 0.69, -1.81, 2.48, -5.24, 4.52, -2.8, 1.31, -1.67,
|
| 34 |
+
-2.34, -1.18, 2.17, -2.17, 2.82, -1.85, 2.25, -2.45, 1.86, -6.79, 3.94,
|
| 35 |
+
-2.33, 1.89, -1.55, 2.08, -1.36, 0.93, -2.51, 2.74, -2.39, 3.92, -3.33,
|
| 36 |
+
2.99, -2.06, -0.9, -2.83, 3.35, -2.59, 3.05, -2.36, 1.85, -1.69, 1.8,
|
| 37 |
+
-1.39, 0.66, -2.06, 0.38, -1.47, 0.44, -4.68, 3.77, -5.58, 3.44, -2.29,
|
| 38 |
+
2.24, -1.04, -0.38, -1.85, 4.23, -2.88, 0.73, -2.59, 1.39, -1.34, 1.75,
|
| 39 |
+
-1.95, 1.3, -2.45, 3.09, -1.99, 3.41, -5.55, 5.21, -1.73, 2.52, -2.17,
|
| 40 |
+
0.85, -2.06, 0.49, -2.54, 2.07, -2.03, 1.3, -3.23, 3.09, -1.55, 1.44,
|
| 41 |
+
-0.81, 1.1, -2.99, 2.92, -1.59, 2.18, -2.45, -0.73, -3.12, -1.3, -2.83,
|
| 42 |
+
0.2, -2.77, 3.24, -1.98, 1.6, -4.59, 3.39, -4.85, 3.75, -2.25, 1.71, -3.28,
|
| 43 |
+
3.38, -1.74, 0.88, -2.41, 1.92, -2.24, 1.19, -2.48, 1.06, -1.68, -0.62,
|
| 44 |
+
-1.3, 0.39, -1.78, 2.35, -3.54, 2.44, -1.32, 0.66, -2.38, 2.76, -2.35,
|
| 45 |
+
3.95, -1.86, 4.32, -2.01, -1.23, -1.79, 2.76, -2.13, -0.13, -5.25, 3.84,
|
| 46 |
+
-2.24, 1.59, -4.85, 2.96, -2.41, 0.01, -0.43, 0.13, -3.92, 2.91, -1.75,
|
| 47 |
+
-0.53, -1.69, 1.69, -1.09, 0.15, -2.11, 2.17, -1.53, 1.22, -2.1, -0.86,
|
| 48 |
+
-2.56, 2.28, -3.02, 3.33, -1.12, 3.86, -2.18, -1.19, -3.03, 0.79, -0.83,
|
| 49 |
+
0.97, -3.19, 1.45, -1.34, 1.28, -2.52, 4.22, -4.53, 3.22, -1.97, 1.75,
|
| 50 |
+
-2.36, 3.19, -0.83, 1.53, -1.59, 1.86, -2.17, 2.3, -1.63, 2.71, -2.03,
|
| 51 |
+
3.75, -2.57, -0.6, -1.47, 1.33, -1.95, 0.7, -1.65, 1.27, -1.42, 1.09, -3.0,
|
| 52 |
+
3.87, -2.51, 3.06, -2.6, 0.74, -1.08, -0.03, -2.44, 1.31, -2.65, 2.99,
|
| 53 |
+
-1.84, 1.65, -4.76, 3.75, -2.07, 3.98, -2.4, 2.67, -2.21, 1.49, -1.21,
|
| 54 |
+
1.22, -5.29, 2.38, -2.85, 2.28, -5.6, 3.78, -2.7, 0.8, -1.81, 3.5, -3.75,
|
| 55 |
+
4.17, -1.29, 2.99, -5.92, 3.43, -1.83, 1.23, -1.24, -1.04, -2.56, 2.37,
|
| 56 |
+
-3.26, 0.39, -4.63, 2.51, -4.52, 3.04, -1.7, 0.36, -1.41, 0.04, -2.1, 1.0,
|
| 57 |
+
-1.87, 3.78, -4.32, 3.59, -2.24, 1.38, -1.99, -0.22, -1.87, 1.95, -0.84,
|
| 58 |
+
2.17, -5.38, 3.56, -1.27, 2.9, -1.79, 3.31, -5.47, 3.85, -1.44, 3.69,
|
| 59 |
+
-2.02, 0.37, -1.29, 0.33, -2.34, 2.56, -1.74, -1.27, -1.97, 1.22, -2.51,
|
| 60 |
+
-0.16, -1.64, -0.96, -2.99, 1.4, -1.53, 3.31, -2.24, 0.45, -2.46, 1.71,
|
| 61 |
+
-2.88, 1.56, -1.63, 1.46, -1.41, 0.68, -1.96, 2.76, -1.61,
|
| 62 |
+
2.11]).reshape((200, 2))
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
# Global data
|
| 66 |
+
X = np.array([[3.0, 3], [4, 3], [4, 2],
|
| 67 |
+
[9, 2], [5, 1], [6, 2], [9, 4],
|
| 68 |
+
[5, 2], [5, 4], [7, 4], [6, 5]])
|
| 69 |
+
|
| 70 |
+
CODET1 = np.array([[3.0000, 3.0000],
|
| 71 |
+
[6.2000, 4.0000],
|
| 72 |
+
[5.8000, 1.8000]])
|
| 73 |
+
|
| 74 |
+
CODET2 = np.array([[11.0/3, 8.0/3],
|
| 75 |
+
[6.7500, 4.2500],
|
| 76 |
+
[6.2500, 1.7500]])
|
| 77 |
+
|
| 78 |
+
LABEL1 = np.array([0, 1, 2, 2, 2, 2, 1, 2, 1, 1, 1])
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class TestWhiten:
|
| 82 |
+
|
| 83 |
+
@array_api_compatible
|
| 84 |
+
def test_whiten(self, xp):
|
| 85 |
+
desired = xp.asarray([[5.08738849, 2.97091878],
|
| 86 |
+
[3.19909255, 0.69660580],
|
| 87 |
+
[4.51041982, 0.02640918],
|
| 88 |
+
[4.38567074, 0.95120889],
|
| 89 |
+
[2.32191480, 1.63195503]])
|
| 90 |
+
|
| 91 |
+
obs = xp.asarray([[0.98744510, 0.82766775],
|
| 92 |
+
[0.62093317, 0.19406729],
|
| 93 |
+
[0.87545741, 0.00735733],
|
| 94 |
+
[0.85124403, 0.26499712],
|
| 95 |
+
[0.45067590, 0.45464607]])
|
| 96 |
+
xp_assert_close(whiten(obs), desired, rtol=1e-5)
|
| 97 |
+
|
| 98 |
+
@array_api_compatible
|
| 99 |
+
def test_whiten_zero_std(self, xp):
|
| 100 |
+
desired = xp.asarray([[0., 1.0, 2.86666544],
|
| 101 |
+
[0., 1.0, 1.32460034],
|
| 102 |
+
[0., 1.0, 3.74382172]])
|
| 103 |
+
|
| 104 |
+
obs = xp.asarray([[0., 1., 0.74109533],
|
| 105 |
+
[0., 1., 0.34243798],
|
| 106 |
+
[0., 1., 0.96785929]])
|
| 107 |
+
with warnings.catch_warnings(record=True) as w:
|
| 108 |
+
warnings.simplefilter('always')
|
| 109 |
+
|
| 110 |
+
xp_assert_close(whiten(obs), desired, rtol=1e-5)
|
| 111 |
+
|
| 112 |
+
assert_equal(len(w), 1)
|
| 113 |
+
assert_(issubclass(w[-1].category, RuntimeWarning))
|
| 114 |
+
|
| 115 |
+
@array_api_compatible
|
| 116 |
+
def test_whiten_not_finite(self, xp):
|
| 117 |
+
arrays = [xp.asarray] if SCIPY_ARRAY_API else [np.asarray, matrix]
|
| 118 |
+
for tp in arrays:
|
| 119 |
+
for bad_value in xp.nan, xp.inf, -xp.inf:
|
| 120 |
+
obs = tp([[0.98744510, bad_value],
|
| 121 |
+
[0.62093317, 0.19406729],
|
| 122 |
+
[0.87545741, 0.00735733],
|
| 123 |
+
[0.85124403, 0.26499712],
|
| 124 |
+
[0.45067590, 0.45464607]])
|
| 125 |
+
assert_raises(ValueError, whiten, obs)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
class TestVq:
|
| 129 |
+
|
| 130 |
+
@skip_if_array_api_gpu
|
| 131 |
+
@array_api_compatible
|
| 132 |
+
def test_py_vq(self, xp):
|
| 133 |
+
initc = np.concatenate([[X[0]], [X[1]], [X[2]]])
|
| 134 |
+
arrays = [xp.asarray] if SCIPY_ARRAY_API else [np.asarray, matrix]
|
| 135 |
+
for tp in arrays:
|
| 136 |
+
# label1.dtype varies between int32 and int64 over platforms
|
| 137 |
+
label1 = py_vq(tp(X), tp(initc))[0]
|
| 138 |
+
xp_assert_equal(label1, xp.asarray(LABEL1, dtype=xp.int64),
|
| 139 |
+
check_dtype=False)
|
| 140 |
+
|
| 141 |
+
@skip_if_array_api
|
| 142 |
+
def test_vq(self):
|
| 143 |
+
initc = np.concatenate([[X[0]], [X[1]], [X[2]]])
|
| 144 |
+
for tp in [np.asarray, matrix]:
|
| 145 |
+
label1, dist = _vq.vq(tp(X), tp(initc))
|
| 146 |
+
assert_array_equal(label1, LABEL1)
|
| 147 |
+
tlabel1, tdist = vq(tp(X), tp(initc))
|
| 148 |
+
|
| 149 |
+
@skip_if_array_api_gpu
|
| 150 |
+
@array_api_compatible
|
| 151 |
+
def test_vq_1d(self, xp):
|
| 152 |
+
# Test special rank 1 vq algo, python implementation.
|
| 153 |
+
data = X[:, 0]
|
| 154 |
+
initc = data[:3]
|
| 155 |
+
a, b = _vq.vq(data, initc)
|
| 156 |
+
data = xp.asarray(data)
|
| 157 |
+
initc = xp.asarray(initc)
|
| 158 |
+
ta, tb = py_vq(data[:, np.newaxis], initc[:, np.newaxis])
|
| 159 |
+
# ta.dtype varies between int32 and int64 over platforms
|
| 160 |
+
xp_assert_equal(ta, xp.asarray(a, dtype=xp.int64), check_dtype=False)
|
| 161 |
+
xp_assert_equal(tb, xp.asarray(b))
|
| 162 |
+
|
| 163 |
+
@skip_if_array_api
|
| 164 |
+
def test__vq_sametype(self):
|
| 165 |
+
a = np.array([1.0, 2.0], dtype=np.float64)
|
| 166 |
+
b = a.astype(np.float32)
|
| 167 |
+
assert_raises(TypeError, _vq.vq, a, b)
|
| 168 |
+
|
| 169 |
+
@skip_if_array_api
|
| 170 |
+
def test__vq_invalid_type(self):
|
| 171 |
+
a = np.array([1, 2], dtype=int)
|
| 172 |
+
assert_raises(TypeError, _vq.vq, a, a)
|
| 173 |
+
|
| 174 |
+
@skip_if_array_api_gpu
|
| 175 |
+
@array_api_compatible
|
| 176 |
+
def test_vq_large_nfeat(self, xp):
|
| 177 |
+
X = np.random.rand(20, 20)
|
| 178 |
+
code_book = np.random.rand(3, 20)
|
| 179 |
+
|
| 180 |
+
codes0, dis0 = _vq.vq(X, code_book)
|
| 181 |
+
codes1, dis1 = py_vq(
|
| 182 |
+
xp.asarray(X), xp.asarray(code_book)
|
| 183 |
+
)
|
| 184 |
+
xp_assert_close(dis1, xp.asarray(dis0), rtol=1e-5)
|
| 185 |
+
# codes1.dtype varies between int32 and int64 over platforms
|
| 186 |
+
xp_assert_equal(codes1, xp.asarray(codes0, dtype=xp.int64), check_dtype=False)
|
| 187 |
+
|
| 188 |
+
X = X.astype(np.float32)
|
| 189 |
+
code_book = code_book.astype(np.float32)
|
| 190 |
+
|
| 191 |
+
codes0, dis0 = _vq.vq(X, code_book)
|
| 192 |
+
codes1, dis1 = py_vq(
|
| 193 |
+
xp.asarray(X), xp.asarray(code_book)
|
| 194 |
+
)
|
| 195 |
+
xp_assert_close(dis1, xp.asarray(dis0, dtype=xp.float64), rtol=1e-5)
|
| 196 |
+
# codes1.dtype varies between int32 and int64 over platforms
|
| 197 |
+
xp_assert_equal(codes1, xp.asarray(codes0, dtype=xp.int64), check_dtype=False)
|
| 198 |
+
|
| 199 |
+
@skip_if_array_api_gpu
|
| 200 |
+
@array_api_compatible
|
| 201 |
+
def test_vq_large_features(self, xp):
|
| 202 |
+
X = np.random.rand(10, 5) * 1000000
|
| 203 |
+
code_book = np.random.rand(2, 5) * 1000000
|
| 204 |
+
|
| 205 |
+
codes0, dis0 = _vq.vq(X, code_book)
|
| 206 |
+
codes1, dis1 = py_vq(
|
| 207 |
+
xp.asarray(X), xp.asarray(code_book)
|
| 208 |
+
)
|
| 209 |
+
xp_assert_close(dis1, xp.asarray(dis0), rtol=1e-5)
|
| 210 |
+
# codes1.dtype varies between int32 and int64 over platforms
|
| 211 |
+
xp_assert_equal(codes1, xp.asarray(codes0, dtype=xp.int64), check_dtype=False)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
# Whole class skipped on GPU for now;
|
| 215 |
+
# once pdist/cdist are hooked up for CuPy, more tests will work
|
| 216 |
+
class TestKMean:
|
| 217 |
+
|
| 218 |
+
@skip_if_array_api_gpu
|
| 219 |
+
@array_api_compatible
|
| 220 |
+
def test_large_features(self, xp):
|
| 221 |
+
# Generate a data set with large values, and run kmeans on it to
|
| 222 |
+
# (regression for 1077).
|
| 223 |
+
d = 300
|
| 224 |
+
n = 100
|
| 225 |
+
|
| 226 |
+
m1 = np.random.randn(d)
|
| 227 |
+
m2 = np.random.randn(d)
|
| 228 |
+
x = 10000 * np.random.randn(n, d) - 20000 * m1
|
| 229 |
+
y = 10000 * np.random.randn(n, d) + 20000 * m2
|
| 230 |
+
|
| 231 |
+
data = np.empty((x.shape[0] + y.shape[0], d), np.float64)
|
| 232 |
+
data[:x.shape[0]] = x
|
| 233 |
+
data[x.shape[0]:] = y
|
| 234 |
+
|
| 235 |
+
kmeans(xp.asarray(data), xp.asarray(2))
|
| 236 |
+
|
| 237 |
+
@skip_if_array_api_gpu
|
| 238 |
+
@array_api_compatible
|
| 239 |
+
def test_kmeans_simple(self, xp):
|
| 240 |
+
np.random.seed(54321)
|
| 241 |
+
initc = np.concatenate([[X[0]], [X[1]], [X[2]]])
|
| 242 |
+
arrays = [xp.asarray] if SCIPY_ARRAY_API else [np.asarray, matrix]
|
| 243 |
+
for tp in arrays:
|
| 244 |
+
code1 = kmeans(tp(X), tp(initc), iter=1)[0]
|
| 245 |
+
xp_assert_close(code1, xp.asarray(CODET2))
|
| 246 |
+
|
| 247 |
+
@skip_if_array_api_gpu
|
| 248 |
+
@array_api_compatible
|
| 249 |
+
def test_kmeans_lost_cluster(self, xp):
|
| 250 |
+
# This will cause kmeans to have a cluster with no points.
|
| 251 |
+
data = xp.asarray(TESTDATA_2D)
|
| 252 |
+
initk = xp.asarray([[-1.8127404, -0.67128041],
|
| 253 |
+
[2.04621601, 0.07401111],
|
| 254 |
+
[-2.31149087, -0.05160469]])
|
| 255 |
+
|
| 256 |
+
kmeans(data, initk)
|
| 257 |
+
with suppress_warnings() as sup:
|
| 258 |
+
sup.filter(UserWarning,
|
| 259 |
+
"One of the clusters is empty. Re-run kmeans with a "
|
| 260 |
+
"different initialization")
|
| 261 |
+
kmeans2(data, initk, missing='warn')
|
| 262 |
+
|
| 263 |
+
assert_raises(ClusterError, kmeans2, data, initk, missing='raise')
|
| 264 |
+
|
| 265 |
+
@skip_if_array_api_gpu
|
| 266 |
+
@array_api_compatible
|
| 267 |
+
def test_kmeans2_simple(self, xp):
|
| 268 |
+
np.random.seed(12345678)
|
| 269 |
+
initc = xp.asarray(np.concatenate([[X[0]], [X[1]], [X[2]]]))
|
| 270 |
+
arrays = [xp.asarray] if SCIPY_ARRAY_API else [np.asarray, matrix]
|
| 271 |
+
for tp in arrays:
|
| 272 |
+
code1 = kmeans2(tp(X), tp(initc), iter=1)[0]
|
| 273 |
+
code2 = kmeans2(tp(X), tp(initc), iter=2)[0]
|
| 274 |
+
|
| 275 |
+
xp_assert_close(code1, xp.asarray(CODET1))
|
| 276 |
+
xp_assert_close(code2, xp.asarray(CODET2))
|
| 277 |
+
|
| 278 |
+
@skip_if_array_api_gpu
|
| 279 |
+
@array_api_compatible
|
| 280 |
+
def test_kmeans2_rank1(self, xp):
|
| 281 |
+
data = xp.asarray(TESTDATA_2D)
|
| 282 |
+
data1 = data[:, 0]
|
| 283 |
+
|
| 284 |
+
initc = data1[:3]
|
| 285 |
+
code = copy(initc, xp=xp)
|
| 286 |
+
kmeans2(data1, code, iter=1)[0]
|
| 287 |
+
kmeans2(data1, code, iter=2)[0]
|
| 288 |
+
|
| 289 |
+
@skip_if_array_api_gpu
|
| 290 |
+
@array_api_compatible
|
| 291 |
+
def test_kmeans2_rank1_2(self, xp):
|
| 292 |
+
data = xp.asarray(TESTDATA_2D)
|
| 293 |
+
data1 = data[:, 0]
|
| 294 |
+
kmeans2(data1, xp.asarray(2), iter=1)
|
| 295 |
+
|
| 296 |
+
@skip_if_array_api_gpu
|
| 297 |
+
@array_api_compatible
|
| 298 |
+
def test_kmeans2_high_dim(self, xp):
|
| 299 |
+
# test kmeans2 when the number of dimensions exceeds the number
|
| 300 |
+
# of input points
|
| 301 |
+
data = xp.asarray(TESTDATA_2D)
|
| 302 |
+
data = xp.reshape(data, (20, 20))[:10, :]
|
| 303 |
+
kmeans2(data, xp.asarray(2))
|
| 304 |
+
|
| 305 |
+
@skip_if_array_api_gpu
|
| 306 |
+
@array_api_compatible
|
| 307 |
+
def test_kmeans2_init(self, xp):
|
| 308 |
+
np.random.seed(12345)
|
| 309 |
+
data = xp.asarray(TESTDATA_2D)
|
| 310 |
+
k = xp.asarray(3)
|
| 311 |
+
|
| 312 |
+
kmeans2(data, k, minit='points')
|
| 313 |
+
kmeans2(data[:, :1], k, minit='points') # special case (1-D)
|
| 314 |
+
|
| 315 |
+
kmeans2(data, k, minit='++')
|
| 316 |
+
kmeans2(data[:, :1], k, minit='++') # special case (1-D)
|
| 317 |
+
|
| 318 |
+
# minit='random' can give warnings, filter those
|
| 319 |
+
with suppress_warnings() as sup:
|
| 320 |
+
sup.filter(message="One of the clusters is empty. Re-run.")
|
| 321 |
+
kmeans2(data, k, minit='random')
|
| 322 |
+
kmeans2(data[:, :1], k, minit='random') # special case (1-D)
|
| 323 |
+
|
| 324 |
+
@skip_if_array_api_gpu
|
| 325 |
+
@array_api_compatible
|
| 326 |
+
@pytest.mark.skipif(sys.platform == 'win32',
|
| 327 |
+
reason='Fails with MemoryError in Wine.')
|
| 328 |
+
def test_krandinit(self, xp):
|
| 329 |
+
data = xp.asarray(TESTDATA_2D)
|
| 330 |
+
datas = [xp.reshape(data, (200, 2)),
|
| 331 |
+
xp.reshape(data, (20, 20))[:10, :]]
|
| 332 |
+
k = int(1e6)
|
| 333 |
+
for data in datas:
|
| 334 |
+
rng = np.random.default_rng(1234)
|
| 335 |
+
init = _krandinit(data, k, rng, xp)
|
| 336 |
+
orig_cov = cov(data.T)
|
| 337 |
+
init_cov = cov(init.T)
|
| 338 |
+
xp_assert_close(orig_cov, init_cov, atol=1e-2)
|
| 339 |
+
|
| 340 |
+
@skip_if_array_api_gpu
|
| 341 |
+
@array_api_compatible
|
| 342 |
+
def test_kmeans2_empty(self, xp):
|
| 343 |
+
# Regression test for gh-1032.
|
| 344 |
+
assert_raises(ValueError, kmeans2, xp.asarray([]), xp.asarray(2))
|
| 345 |
+
|
| 346 |
+
@skip_if_array_api
|
| 347 |
+
def test_kmeans_0k(self):
|
| 348 |
+
# Regression test for gh-1073: fail when k arg is 0.
|
| 349 |
+
assert_raises(ValueError, kmeans, X, 0)
|
| 350 |
+
assert_raises(ValueError, kmeans2, X, 0)
|
| 351 |
+
assert_raises(ValueError, kmeans2, X, np.array([]))
|
| 352 |
+
|
| 353 |
+
@skip_if_array_api_gpu
|
| 354 |
+
@array_api_compatible
|
| 355 |
+
def test_kmeans_large_thres(self, xp):
|
| 356 |
+
# Regression test for gh-1774
|
| 357 |
+
x = xp.asarray([1, 2, 3, 4, 10], dtype=xp.float64)
|
| 358 |
+
res = kmeans(x, xp.asarray(1), thresh=1e16)
|
| 359 |
+
xp_assert_close(res[0], xp.asarray([4.], dtype=xp.float64))
|
| 360 |
+
xp_assert_close(res[1], xp.asarray(2.3999999999999999, dtype=xp.float64)[()])
|
| 361 |
+
|
| 362 |
+
@skip_if_array_api_gpu
|
| 363 |
+
@array_api_compatible
|
| 364 |
+
def test_kmeans2_kpp_low_dim(self, xp):
|
| 365 |
+
# Regression test for gh-11462
|
| 366 |
+
prev_res = xp.asarray([[-1.95266667, 0.898],
|
| 367 |
+
[-3.153375, 3.3945]], dtype=xp.float64)
|
| 368 |
+
np.random.seed(42)
|
| 369 |
+
res, _ = kmeans2(xp.asarray(TESTDATA_2D), xp.asarray(2), minit='++')
|
| 370 |
+
xp_assert_close(res, prev_res)
|
| 371 |
+
|
| 372 |
+
@skip_if_array_api_gpu
|
| 373 |
+
@array_api_compatible
|
| 374 |
+
def test_kmeans2_kpp_high_dim(self, xp):
|
| 375 |
+
# Regression test for gh-11462
|
| 376 |
+
n_dim = 100
|
| 377 |
+
size = 10
|
| 378 |
+
centers = np.vstack([5 * np.ones(n_dim),
|
| 379 |
+
-5 * np.ones(n_dim)])
|
| 380 |
+
np.random.seed(42)
|
| 381 |
+
data = np.vstack([
|
| 382 |
+
np.random.multivariate_normal(centers[0], np.eye(n_dim), size=size),
|
| 383 |
+
np.random.multivariate_normal(centers[1], np.eye(n_dim), size=size)
|
| 384 |
+
])
|
| 385 |
+
|
| 386 |
+
data = xp.asarray(data)
|
| 387 |
+
res, _ = kmeans2(data, xp.asarray(2), minit='++')
|
| 388 |
+
xp_assert_equal(xp.sign(res), xp.sign(xp.asarray(centers)))
|
| 389 |
+
|
| 390 |
+
@skip_if_array_api_gpu
|
| 391 |
+
@array_api_compatible
|
| 392 |
+
def test_kmeans_diff_convergence(self, xp):
|
| 393 |
+
# Regression test for gh-8727
|
| 394 |
+
obs = xp.asarray([-3, -1, 0, 1, 1, 8], dtype=xp.float64)
|
| 395 |
+
res = kmeans(obs, xp.asarray([-3., 0.99]))
|
| 396 |
+
xp_assert_close(res[0], xp.asarray([-0.4, 8.], dtype=xp.float64))
|
| 397 |
+
xp_assert_close(res[1], xp.asarray(1.0666666666666667, dtype=xp.float64)[()])
|
| 398 |
+
|
| 399 |
+
@skip_if_array_api
|
| 400 |
+
def test_kmeans_and_kmeans2_random_seed(self):
|
| 401 |
+
|
| 402 |
+
seed_list = [
|
| 403 |
+
1234, np.random.RandomState(1234), np.random.default_rng(1234)
|
| 404 |
+
]
|
| 405 |
+
|
| 406 |
+
for seed in seed_list:
|
| 407 |
+
# test for kmeans
|
| 408 |
+
res1, _ = kmeans(TESTDATA_2D, 2, seed=seed)
|
| 409 |
+
res2, _ = kmeans(TESTDATA_2D, 2, seed=seed)
|
| 410 |
+
assert_allclose(res1, res1) # should be same results
|
| 411 |
+
|
| 412 |
+
# test for kmeans2
|
| 413 |
+
for minit in ["random", "points", "++"]:
|
| 414 |
+
res1, _ = kmeans2(TESTDATA_2D, 2, minit=minit, seed=seed)
|
| 415 |
+
res2, _ = kmeans2(TESTDATA_2D, 2, minit=minit, seed=seed)
|
| 416 |
+
assert_allclose(res1, res1) # should be same results
|
rtme/lib/python3.10/site-packages/scipy/cluster/vq.py
ADDED
|
@@ -0,0 +1,821 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
K-means clustering and vector quantization (:mod:`scipy.cluster.vq`)
|
| 3 |
+
====================================================================
|
| 4 |
+
|
| 5 |
+
Provides routines for k-means clustering, generating code books
|
| 6 |
+
from k-means models and quantizing vectors by comparing them with
|
| 7 |
+
centroids in a code book.
|
| 8 |
+
|
| 9 |
+
.. autosummary::
|
| 10 |
+
:toctree: generated/
|
| 11 |
+
|
| 12 |
+
whiten -- Normalize a group of observations so each feature has unit variance
|
| 13 |
+
vq -- Calculate code book membership of a set of observation vectors
|
| 14 |
+
kmeans -- Perform k-means on a set of observation vectors forming k clusters
|
| 15 |
+
kmeans2 -- A different implementation of k-means with more methods
|
| 16 |
+
-- for initializing centroids
|
| 17 |
+
|
| 18 |
+
Background information
|
| 19 |
+
----------------------
|
| 20 |
+
The k-means algorithm takes as input the number of clusters to
|
| 21 |
+
generate, k, and a set of observation vectors to cluster. It
|
| 22 |
+
returns a set of centroids, one for each of the k clusters. An
|
| 23 |
+
observation vector is classified with the cluster number or
|
| 24 |
+
centroid index of the centroid closest to it.
|
| 25 |
+
|
| 26 |
+
A vector v belongs to cluster i if it is closer to centroid i than
|
| 27 |
+
any other centroid. If v belongs to i, we say centroid i is the
|
| 28 |
+
dominating centroid of v. The k-means algorithm tries to
|
| 29 |
+
minimize distortion, which is defined as the sum of the squared distances
|
| 30 |
+
between each observation vector and its dominating centroid.
|
| 31 |
+
The minimization is achieved by iteratively reclassifying
|
| 32 |
+
the observations into clusters and recalculating the centroids until
|
| 33 |
+
a configuration is reached in which the centroids are stable. One can
|
| 34 |
+
also define a maximum number of iterations.
|
| 35 |
+
|
| 36 |
+
Since vector quantization is a natural application for k-means,
|
| 37 |
+
information theory terminology is often used. The centroid index
|
| 38 |
+
or cluster index is also referred to as a "code" and the table
|
| 39 |
+
mapping codes to centroids and, vice versa, is often referred to as a
|
| 40 |
+
"code book". The result of k-means, a set of centroids, can be
|
| 41 |
+
used to quantize vectors. Quantization aims to find an encoding of
|
| 42 |
+
vectors that reduces the expected distortion.
|
| 43 |
+
|
| 44 |
+
All routines expect obs to be an M by N array, where the rows are
|
| 45 |
+
the observation vectors. The codebook is a k by N array, where the
|
| 46 |
+
ith row is the centroid of code word i. The observation vectors
|
| 47 |
+
and centroids have the same feature dimension.
|
| 48 |
+
|
| 49 |
+
As an example, suppose we wish to compress a 24-bit color image
|
| 50 |
+
(each pixel is represented by one byte for red, one for blue, and
|
| 51 |
+
one for green) before sending it over the web. By using a smaller
|
| 52 |
+
8-bit encoding, we can reduce the amount of data by two
|
| 53 |
+
thirds. Ideally, the colors for each of the 256 possible 8-bit
|
| 54 |
+
encoding values should be chosen to minimize distortion of the
|
| 55 |
+
color. Running k-means with k=256 generates a code book of 256
|
| 56 |
+
codes, which fills up all possible 8-bit sequences. Instead of
|
| 57 |
+
sending a 3-byte value for each pixel, the 8-bit centroid index
|
| 58 |
+
(or code word) of the dominating centroid is transmitted. The code
|
| 59 |
+
book is also sent over the wire so each 8-bit code can be
|
| 60 |
+
translated back to a 24-bit pixel value representation. If the
|
| 61 |
+
image of interest was of an ocean, we would expect many 24-bit
|
| 62 |
+
blues to be represented by 8-bit codes. If it was an image of a
|
| 63 |
+
human face, more flesh-tone colors would be represented in the
|
| 64 |
+
code book.
|
| 65 |
+
|
| 66 |
+
"""
|
| 67 |
+
import warnings
|
| 68 |
+
import numpy as np
|
| 69 |
+
from collections import deque
|
| 70 |
+
from scipy._lib._array_api import (
|
| 71 |
+
as_xparray, array_namespace, size, atleast_nd, copy, cov
|
| 72 |
+
)
|
| 73 |
+
from scipy._lib._util import check_random_state, rng_integers
|
| 74 |
+
from scipy.spatial.distance import cdist
|
| 75 |
+
|
| 76 |
+
from . import _vq
|
| 77 |
+
|
| 78 |
+
__docformat__ = 'restructuredtext'
|
| 79 |
+
|
| 80 |
+
__all__ = ['whiten', 'vq', 'kmeans', 'kmeans2']
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class ClusterError(Exception):
|
| 84 |
+
pass
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def whiten(obs, check_finite=True):
|
| 88 |
+
"""
|
| 89 |
+
Normalize a group of observations on a per feature basis.
|
| 90 |
+
|
| 91 |
+
Before running k-means, it is beneficial to rescale each feature
|
| 92 |
+
dimension of the observation set by its standard deviation (i.e. "whiten"
|
| 93 |
+
it - as in "white noise" where each frequency has equal power).
|
| 94 |
+
Each feature is divided by its standard deviation across all observations
|
| 95 |
+
to give it unit variance.
|
| 96 |
+
|
| 97 |
+
Parameters
|
| 98 |
+
----------
|
| 99 |
+
obs : ndarray
|
| 100 |
+
Each row of the array is an observation. The
|
| 101 |
+
columns are the features seen during each observation.
|
| 102 |
+
|
| 103 |
+
>>> # f0 f1 f2
|
| 104 |
+
>>> obs = [[ 1., 1., 1.], #o0
|
| 105 |
+
... [ 2., 2., 2.], #o1
|
| 106 |
+
... [ 3., 3., 3.], #o2
|
| 107 |
+
... [ 4., 4., 4.]] #o3
|
| 108 |
+
|
| 109 |
+
check_finite : bool, optional
|
| 110 |
+
Whether to check that the input matrices contain only finite numbers.
|
| 111 |
+
Disabling may give a performance gain, but may result in problems
|
| 112 |
+
(crashes, non-termination) if the inputs do contain infinities or NaNs.
|
| 113 |
+
Default: True
|
| 114 |
+
|
| 115 |
+
Returns
|
| 116 |
+
-------
|
| 117 |
+
result : ndarray
|
| 118 |
+
Contains the values in `obs` scaled by the standard deviation
|
| 119 |
+
of each column.
|
| 120 |
+
|
| 121 |
+
Examples
|
| 122 |
+
--------
|
| 123 |
+
>>> import numpy as np
|
| 124 |
+
>>> from scipy.cluster.vq import whiten
|
| 125 |
+
>>> features = np.array([[1.9, 2.3, 1.7],
|
| 126 |
+
... [1.5, 2.5, 2.2],
|
| 127 |
+
... [0.8, 0.6, 1.7,]])
|
| 128 |
+
>>> whiten(features)
|
| 129 |
+
array([[ 4.17944278, 2.69811351, 7.21248917],
|
| 130 |
+
[ 3.29956009, 2.93273208, 9.33380951],
|
| 131 |
+
[ 1.75976538, 0.7038557 , 7.21248917]])
|
| 132 |
+
|
| 133 |
+
"""
|
| 134 |
+
xp = array_namespace(obs)
|
| 135 |
+
obs = as_xparray(obs, check_finite=check_finite, xp=xp)
|
| 136 |
+
std_dev = xp.std(obs, axis=0)
|
| 137 |
+
zero_std_mask = std_dev == 0
|
| 138 |
+
if xp.any(zero_std_mask):
|
| 139 |
+
std_dev[zero_std_mask] = 1.0
|
| 140 |
+
warnings.warn("Some columns have standard deviation zero. "
|
| 141 |
+
"The values of these columns will not change.",
|
| 142 |
+
RuntimeWarning, stacklevel=2)
|
| 143 |
+
return obs / std_dev
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def vq(obs, code_book, check_finite=True):
|
| 147 |
+
"""
|
| 148 |
+
Assign codes from a code book to observations.
|
| 149 |
+
|
| 150 |
+
Assigns a code from a code book to each observation. Each
|
| 151 |
+
observation vector in the 'M' by 'N' `obs` array is compared with the
|
| 152 |
+
centroids in the code book and assigned the code of the closest
|
| 153 |
+
centroid.
|
| 154 |
+
|
| 155 |
+
The features in `obs` should have unit variance, which can be
|
| 156 |
+
achieved by passing them through the whiten function. The code
|
| 157 |
+
book can be created with the k-means algorithm or a different
|
| 158 |
+
encoding algorithm.
|
| 159 |
+
|
| 160 |
+
Parameters
|
| 161 |
+
----------
|
| 162 |
+
obs : ndarray
|
| 163 |
+
Each row of the 'M' x 'N' array is an observation. The columns are
|
| 164 |
+
the "features" seen during each observation. The features must be
|
| 165 |
+
whitened first using the whiten function or something equivalent.
|
| 166 |
+
code_book : ndarray
|
| 167 |
+
The code book is usually generated using the k-means algorithm.
|
| 168 |
+
Each row of the array holds a different code, and the columns are
|
| 169 |
+
the features of the code.
|
| 170 |
+
|
| 171 |
+
>>> # f0 f1 f2 f3
|
| 172 |
+
>>> code_book = [
|
| 173 |
+
... [ 1., 2., 3., 4.], #c0
|
| 174 |
+
... [ 1., 2., 3., 4.], #c1
|
| 175 |
+
... [ 1., 2., 3., 4.]] #c2
|
| 176 |
+
|
| 177 |
+
check_finite : bool, optional
|
| 178 |
+
Whether to check that the input matrices contain only finite numbers.
|
| 179 |
+
Disabling may give a performance gain, but may result in problems
|
| 180 |
+
(crashes, non-termination) if the inputs do contain infinities or NaNs.
|
| 181 |
+
Default: True
|
| 182 |
+
|
| 183 |
+
Returns
|
| 184 |
+
-------
|
| 185 |
+
code : ndarray
|
| 186 |
+
A length M array holding the code book index for each observation.
|
| 187 |
+
dist : ndarray
|
| 188 |
+
The distortion (distance) between the observation and its nearest
|
| 189 |
+
code.
|
| 190 |
+
|
| 191 |
+
Examples
|
| 192 |
+
--------
|
| 193 |
+
>>> import numpy as np
|
| 194 |
+
>>> from scipy.cluster.vq import vq
|
| 195 |
+
>>> code_book = np.array([[1., 1., 1.],
|
| 196 |
+
... [2., 2., 2.]])
|
| 197 |
+
>>> features = np.array([[1.9, 2.3, 1.7],
|
| 198 |
+
... [1.5, 2.5, 2.2],
|
| 199 |
+
... [0.8, 0.6, 1.7]])
|
| 200 |
+
>>> vq(features, code_book)
|
| 201 |
+
(array([1, 1, 0], dtype=int32), array([0.43588989, 0.73484692, 0.83066239]))
|
| 202 |
+
|
| 203 |
+
"""
|
| 204 |
+
xp = array_namespace(obs, code_book)
|
| 205 |
+
obs = as_xparray(obs, xp=xp, check_finite=check_finite)
|
| 206 |
+
code_book = as_xparray(code_book, xp=xp, check_finite=check_finite)
|
| 207 |
+
ct = xp.result_type(obs, code_book)
|
| 208 |
+
|
| 209 |
+
c_obs = xp.astype(obs, ct, copy=False)
|
| 210 |
+
c_code_book = xp.astype(code_book, ct, copy=False)
|
| 211 |
+
|
| 212 |
+
if xp.isdtype(ct, kind='real floating'):
|
| 213 |
+
c_obs = np.asarray(c_obs)
|
| 214 |
+
c_code_book = np.asarray(c_code_book)
|
| 215 |
+
result = _vq.vq(c_obs, c_code_book)
|
| 216 |
+
return xp.asarray(result[0]), xp.asarray(result[1])
|
| 217 |
+
return py_vq(obs, code_book, check_finite=False)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def py_vq(obs, code_book, check_finite=True):
|
| 221 |
+
""" Python version of vq algorithm.
|
| 222 |
+
|
| 223 |
+
The algorithm computes the Euclidean distance between each
|
| 224 |
+
observation and every frame in the code_book.
|
| 225 |
+
|
| 226 |
+
Parameters
|
| 227 |
+
----------
|
| 228 |
+
obs : ndarray
|
| 229 |
+
Expects a rank 2 array. Each row is one observation.
|
| 230 |
+
code_book : ndarray
|
| 231 |
+
Code book to use. Same format than obs. Should have same number of
|
| 232 |
+
features (e.g., columns) than obs.
|
| 233 |
+
check_finite : bool, optional
|
| 234 |
+
Whether to check that the input matrices contain only finite numbers.
|
| 235 |
+
Disabling may give a performance gain, but may result in problems
|
| 236 |
+
(crashes, non-termination) if the inputs do contain infinities or NaNs.
|
| 237 |
+
Default: True
|
| 238 |
+
|
| 239 |
+
Returns
|
| 240 |
+
-------
|
| 241 |
+
code : ndarray
|
| 242 |
+
code[i] gives the label of the ith obversation; its code is
|
| 243 |
+
code_book[code[i]].
|
| 244 |
+
mind_dist : ndarray
|
| 245 |
+
min_dist[i] gives the distance between the ith observation and its
|
| 246 |
+
corresponding code.
|
| 247 |
+
|
| 248 |
+
Notes
|
| 249 |
+
-----
|
| 250 |
+
This function is slower than the C version but works for
|
| 251 |
+
all input types. If the inputs have the wrong types for the
|
| 252 |
+
C versions of the function, this one is called as a last resort.
|
| 253 |
+
|
| 254 |
+
It is about 20 times slower than the C version.
|
| 255 |
+
|
| 256 |
+
"""
|
| 257 |
+
xp = array_namespace(obs, code_book)
|
| 258 |
+
obs = as_xparray(obs, xp=xp, check_finite=check_finite)
|
| 259 |
+
code_book = as_xparray(code_book, xp=xp, check_finite=check_finite)
|
| 260 |
+
|
| 261 |
+
if obs.ndim != code_book.ndim:
|
| 262 |
+
raise ValueError("Observation and code_book should have the same rank")
|
| 263 |
+
|
| 264 |
+
if obs.ndim == 1:
|
| 265 |
+
obs = obs[:, xp.newaxis]
|
| 266 |
+
code_book = code_book[:, xp.newaxis]
|
| 267 |
+
|
| 268 |
+
# Once `cdist` has array API support, this `xp.asarray` call can be removed
|
| 269 |
+
dist = xp.asarray(cdist(obs, code_book))
|
| 270 |
+
code = xp.argmin(dist, axis=1)
|
| 271 |
+
min_dist = xp.min(dist, axis=1)
|
| 272 |
+
return code, min_dist
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
def _kmeans(obs, guess, thresh=1e-5, xp=None):
|
| 276 |
+
""" "raw" version of k-means.
|
| 277 |
+
|
| 278 |
+
Returns
|
| 279 |
+
-------
|
| 280 |
+
code_book
|
| 281 |
+
The lowest distortion codebook found.
|
| 282 |
+
avg_dist
|
| 283 |
+
The average distance a observation is from a code in the book.
|
| 284 |
+
Lower means the code_book matches the data better.
|
| 285 |
+
|
| 286 |
+
See Also
|
| 287 |
+
--------
|
| 288 |
+
kmeans : wrapper around k-means
|
| 289 |
+
|
| 290 |
+
Examples
|
| 291 |
+
--------
|
| 292 |
+
Note: not whitened in this example.
|
| 293 |
+
|
| 294 |
+
>>> import numpy as np
|
| 295 |
+
>>> from scipy.cluster.vq import _kmeans
|
| 296 |
+
>>> features = np.array([[ 1.9,2.3],
|
| 297 |
+
... [ 1.5,2.5],
|
| 298 |
+
... [ 0.8,0.6],
|
| 299 |
+
... [ 0.4,1.8],
|
| 300 |
+
... [ 1.0,1.0]])
|
| 301 |
+
>>> book = np.array((features[0],features[2]))
|
| 302 |
+
>>> _kmeans(features,book)
|
| 303 |
+
(array([[ 1.7 , 2.4 ],
|
| 304 |
+
[ 0.73333333, 1.13333333]]), 0.40563916697728591)
|
| 305 |
+
|
| 306 |
+
"""
|
| 307 |
+
xp = np if xp is None else xp
|
| 308 |
+
code_book = guess
|
| 309 |
+
diff = xp.inf
|
| 310 |
+
prev_avg_dists = deque([diff], maxlen=2)
|
| 311 |
+
while diff > thresh:
|
| 312 |
+
# compute membership and distances between obs and code_book
|
| 313 |
+
obs_code, distort = vq(obs, code_book, check_finite=False)
|
| 314 |
+
prev_avg_dists.append(xp.mean(distort, axis=-1))
|
| 315 |
+
# recalc code_book as centroids of associated obs
|
| 316 |
+
obs = np.asarray(obs)
|
| 317 |
+
obs_code = np.asarray(obs_code)
|
| 318 |
+
code_book, has_members = _vq.update_cluster_means(obs, obs_code,
|
| 319 |
+
code_book.shape[0])
|
| 320 |
+
obs = xp.asarray(obs)
|
| 321 |
+
obs_code = xp.asarray(obs_code)
|
| 322 |
+
code_book = xp.asarray(code_book)
|
| 323 |
+
has_members = xp.asarray(has_members)
|
| 324 |
+
code_book = code_book[has_members]
|
| 325 |
+
diff = xp.abs(prev_avg_dists[0] - prev_avg_dists[1])
|
| 326 |
+
|
| 327 |
+
return code_book, prev_avg_dists[1]
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def kmeans(obs, k_or_guess, iter=20, thresh=1e-5, check_finite=True,
|
| 331 |
+
*, seed=None):
|
| 332 |
+
"""
|
| 333 |
+
Performs k-means on a set of observation vectors forming k clusters.
|
| 334 |
+
|
| 335 |
+
The k-means algorithm adjusts the classification of the observations
|
| 336 |
+
into clusters and updates the cluster centroids until the position of
|
| 337 |
+
the centroids is stable over successive iterations. In this
|
| 338 |
+
implementation of the algorithm, the stability of the centroids is
|
| 339 |
+
determined by comparing the absolute value of the change in the average
|
| 340 |
+
Euclidean distance between the observations and their corresponding
|
| 341 |
+
centroids against a threshold. This yields
|
| 342 |
+
a code book mapping centroids to codes and vice versa.
|
| 343 |
+
|
| 344 |
+
Parameters
|
| 345 |
+
----------
|
| 346 |
+
obs : ndarray
|
| 347 |
+
Each row of the M by N array is an observation vector. The
|
| 348 |
+
columns are the features seen during each observation.
|
| 349 |
+
The features must be whitened first with the `whiten` function.
|
| 350 |
+
|
| 351 |
+
k_or_guess : int or ndarray
|
| 352 |
+
The number of centroids to generate. A code is assigned to
|
| 353 |
+
each centroid, which is also the row index of the centroid
|
| 354 |
+
in the code_book matrix generated.
|
| 355 |
+
|
| 356 |
+
The initial k centroids are chosen by randomly selecting
|
| 357 |
+
observations from the observation matrix. Alternatively,
|
| 358 |
+
passing a k by N array specifies the initial k centroids.
|
| 359 |
+
|
| 360 |
+
iter : int, optional
|
| 361 |
+
The number of times to run k-means, returning the codebook
|
| 362 |
+
with the lowest distortion. This argument is ignored if
|
| 363 |
+
initial centroids are specified with an array for the
|
| 364 |
+
``k_or_guess`` parameter. This parameter does not represent the
|
| 365 |
+
number of iterations of the k-means algorithm.
|
| 366 |
+
|
| 367 |
+
thresh : float, optional
|
| 368 |
+
Terminates the k-means algorithm if the change in
|
| 369 |
+
distortion since the last k-means iteration is less than
|
| 370 |
+
or equal to threshold.
|
| 371 |
+
|
| 372 |
+
check_finite : bool, optional
|
| 373 |
+
Whether to check that the input matrices contain only finite numbers.
|
| 374 |
+
Disabling may give a performance gain, but may result in problems
|
| 375 |
+
(crashes, non-termination) if the inputs do contain infinities or NaNs.
|
| 376 |
+
Default: True
|
| 377 |
+
|
| 378 |
+
seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional
|
| 379 |
+
Seed for initializing the pseudo-random number generator.
|
| 380 |
+
If `seed` is None (or `numpy.random`), the `numpy.random.RandomState`
|
| 381 |
+
singleton is used.
|
| 382 |
+
If `seed` is an int, a new ``RandomState`` instance is used,
|
| 383 |
+
seeded with `seed`.
|
| 384 |
+
If `seed` is already a ``Generator`` or ``RandomState`` instance then
|
| 385 |
+
that instance is used.
|
| 386 |
+
The default is None.
|
| 387 |
+
|
| 388 |
+
Returns
|
| 389 |
+
-------
|
| 390 |
+
codebook : ndarray
|
| 391 |
+
A k by N array of k centroids. The ith centroid
|
| 392 |
+
codebook[i] is represented with the code i. The centroids
|
| 393 |
+
and codes generated represent the lowest distortion seen,
|
| 394 |
+
not necessarily the globally minimal distortion.
|
| 395 |
+
Note that the number of centroids is not necessarily the same as the
|
| 396 |
+
``k_or_guess`` parameter, because centroids assigned to no observations
|
| 397 |
+
are removed during iterations.
|
| 398 |
+
|
| 399 |
+
distortion : float
|
| 400 |
+
The mean (non-squared) Euclidean distance between the observations
|
| 401 |
+
passed and the centroids generated. Note the difference to the standard
|
| 402 |
+
definition of distortion in the context of the k-means algorithm, which
|
| 403 |
+
is the sum of the squared distances.
|
| 404 |
+
|
| 405 |
+
See Also
|
| 406 |
+
--------
|
| 407 |
+
kmeans2 : a different implementation of k-means clustering
|
| 408 |
+
with more methods for generating initial centroids but without
|
| 409 |
+
using a distortion change threshold as a stopping criterion.
|
| 410 |
+
|
| 411 |
+
whiten : must be called prior to passing an observation matrix
|
| 412 |
+
to kmeans.
|
| 413 |
+
|
| 414 |
+
Notes
|
| 415 |
+
-----
|
| 416 |
+
For more functionalities or optimal performance, you can use
|
| 417 |
+
`sklearn.cluster.KMeans <https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html>`_.
|
| 418 |
+
`This <https://hdbscan.readthedocs.io/en/latest/performance_and_scalability.html#comparison-of-high-performance-implementations>`_
|
| 419 |
+
is a benchmark result of several implementations.
|
| 420 |
+
|
| 421 |
+
Examples
|
| 422 |
+
--------
|
| 423 |
+
>>> import numpy as np
|
| 424 |
+
>>> from scipy.cluster.vq import vq, kmeans, whiten
|
| 425 |
+
>>> import matplotlib.pyplot as plt
|
| 426 |
+
>>> features = np.array([[ 1.9,2.3],
|
| 427 |
+
... [ 1.5,2.5],
|
| 428 |
+
... [ 0.8,0.6],
|
| 429 |
+
... [ 0.4,1.8],
|
| 430 |
+
... [ 0.1,0.1],
|
| 431 |
+
... [ 0.2,1.8],
|
| 432 |
+
... [ 2.0,0.5],
|
| 433 |
+
... [ 0.3,1.5],
|
| 434 |
+
... [ 1.0,1.0]])
|
| 435 |
+
>>> whitened = whiten(features)
|
| 436 |
+
>>> book = np.array((whitened[0],whitened[2]))
|
| 437 |
+
>>> kmeans(whitened,book)
|
| 438 |
+
(array([[ 2.3110306 , 2.86287398], # random
|
| 439 |
+
[ 0.93218041, 1.24398691]]), 0.85684700941625547)
|
| 440 |
+
|
| 441 |
+
>>> codes = 3
|
| 442 |
+
>>> kmeans(whitened,codes)
|
| 443 |
+
(array([[ 2.3110306 , 2.86287398], # random
|
| 444 |
+
[ 1.32544402, 0.65607529],
|
| 445 |
+
[ 0.40782893, 2.02786907]]), 0.5196582527686241)
|
| 446 |
+
|
| 447 |
+
>>> # Create 50 datapoints in two clusters a and b
|
| 448 |
+
>>> pts = 50
|
| 449 |
+
>>> rng = np.random.default_rng()
|
| 450 |
+
>>> a = rng.multivariate_normal([0, 0], [[4, 1], [1, 4]], size=pts)
|
| 451 |
+
>>> b = rng.multivariate_normal([30, 10],
|
| 452 |
+
... [[10, 2], [2, 1]],
|
| 453 |
+
... size=pts)
|
| 454 |
+
>>> features = np.concatenate((a, b))
|
| 455 |
+
>>> # Whiten data
|
| 456 |
+
>>> whitened = whiten(features)
|
| 457 |
+
>>> # Find 2 clusters in the data
|
| 458 |
+
>>> codebook, distortion = kmeans(whitened, 2)
|
| 459 |
+
>>> # Plot whitened data and cluster centers in red
|
| 460 |
+
>>> plt.scatter(whitened[:, 0], whitened[:, 1])
|
| 461 |
+
>>> plt.scatter(codebook[:, 0], codebook[:, 1], c='r')
|
| 462 |
+
>>> plt.show()
|
| 463 |
+
|
| 464 |
+
"""
|
| 465 |
+
xp = array_namespace(obs, k_or_guess)
|
| 466 |
+
obs = as_xparray(obs, xp=xp, check_finite=check_finite)
|
| 467 |
+
guess = as_xparray(k_or_guess, xp=xp, check_finite=check_finite)
|
| 468 |
+
if iter < 1:
|
| 469 |
+
raise ValueError("iter must be at least 1, got %s" % iter)
|
| 470 |
+
|
| 471 |
+
# Determine whether a count (scalar) or an initial guess (array) was passed.
|
| 472 |
+
if size(guess) != 1:
|
| 473 |
+
if size(guess) < 1:
|
| 474 |
+
raise ValueError("Asked for 0 clusters. Initial book was %s" %
|
| 475 |
+
guess)
|
| 476 |
+
return _kmeans(obs, guess, thresh=thresh, xp=xp)
|
| 477 |
+
|
| 478 |
+
# k_or_guess is a scalar, now verify that it's an integer
|
| 479 |
+
k = int(guess)
|
| 480 |
+
if k != guess:
|
| 481 |
+
raise ValueError("If k_or_guess is a scalar, it must be an integer.")
|
| 482 |
+
if k < 1:
|
| 483 |
+
raise ValueError("Asked for %d clusters." % k)
|
| 484 |
+
|
| 485 |
+
rng = check_random_state(seed)
|
| 486 |
+
|
| 487 |
+
# initialize best distance value to a large value
|
| 488 |
+
best_dist = xp.inf
|
| 489 |
+
for i in range(iter):
|
| 490 |
+
# the initial code book is randomly selected from observations
|
| 491 |
+
guess = _kpoints(obs, k, rng, xp)
|
| 492 |
+
book, dist = _kmeans(obs, guess, thresh=thresh, xp=xp)
|
| 493 |
+
if dist < best_dist:
|
| 494 |
+
best_book = book
|
| 495 |
+
best_dist = dist
|
| 496 |
+
return best_book, best_dist
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
def _kpoints(data, k, rng, xp):
|
| 500 |
+
"""Pick k points at random in data (one row = one observation).
|
| 501 |
+
|
| 502 |
+
Parameters
|
| 503 |
+
----------
|
| 504 |
+
data : ndarray
|
| 505 |
+
Expect a rank 1 or 2 array. Rank 1 are assumed to describe one
|
| 506 |
+
dimensional data, rank 2 multidimensional data, in which case one
|
| 507 |
+
row is one observation.
|
| 508 |
+
k : int
|
| 509 |
+
Number of samples to generate.
|
| 510 |
+
rng : `numpy.random.Generator` or `numpy.random.RandomState`
|
| 511 |
+
Random number generator.
|
| 512 |
+
|
| 513 |
+
Returns
|
| 514 |
+
-------
|
| 515 |
+
x : ndarray
|
| 516 |
+
A 'k' by 'N' containing the initial centroids
|
| 517 |
+
|
| 518 |
+
"""
|
| 519 |
+
idx = rng.choice(data.shape[0], size=int(k), replace=False)
|
| 520 |
+
return data[idx, ...]
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
def _krandinit(data, k, rng, xp):
|
| 524 |
+
"""Returns k samples of a random variable whose parameters depend on data.
|
| 525 |
+
|
| 526 |
+
More precisely, it returns k observations sampled from a Gaussian random
|
| 527 |
+
variable whose mean and covariances are the ones estimated from the data.
|
| 528 |
+
|
| 529 |
+
Parameters
|
| 530 |
+
----------
|
| 531 |
+
data : ndarray
|
| 532 |
+
Expect a rank 1 or 2 array. Rank 1 is assumed to describe 1-D
|
| 533 |
+
data, rank 2 multidimensional data, in which case one
|
| 534 |
+
row is one observation.
|
| 535 |
+
k : int
|
| 536 |
+
Number of samples to generate.
|
| 537 |
+
rng : `numpy.random.Generator` or `numpy.random.RandomState`
|
| 538 |
+
Random number generator.
|
| 539 |
+
|
| 540 |
+
Returns
|
| 541 |
+
-------
|
| 542 |
+
x : ndarray
|
| 543 |
+
A 'k' by 'N' containing the initial centroids
|
| 544 |
+
|
| 545 |
+
"""
|
| 546 |
+
mu = xp.mean(data, axis=0)
|
| 547 |
+
k = np.asarray(k)
|
| 548 |
+
|
| 549 |
+
if data.ndim == 1:
|
| 550 |
+
_cov = cov(data)
|
| 551 |
+
x = rng.standard_normal(size=k)
|
| 552 |
+
x = xp.asarray(x)
|
| 553 |
+
x *= xp.sqrt(_cov)
|
| 554 |
+
elif data.shape[1] > data.shape[0]:
|
| 555 |
+
# initialize when the covariance matrix is rank deficient
|
| 556 |
+
_, s, vh = xp.linalg.svd(data - mu, full_matrices=False)
|
| 557 |
+
x = rng.standard_normal(size=(k, size(s)))
|
| 558 |
+
x = xp.asarray(x)
|
| 559 |
+
sVh = s[:, None] * vh / xp.sqrt(data.shape[0] - xp.asarray(1.))
|
| 560 |
+
x = x @ sVh
|
| 561 |
+
else:
|
| 562 |
+
_cov = atleast_nd(cov(data.T), ndim=2)
|
| 563 |
+
|
| 564 |
+
# k rows, d cols (one row = one obs)
|
| 565 |
+
# Generate k sample of a random variable ~ Gaussian(mu, cov)
|
| 566 |
+
x = rng.standard_normal(size=(k, size(mu)))
|
| 567 |
+
x = xp.asarray(x)
|
| 568 |
+
x = x @ xp.linalg.cholesky(_cov).T
|
| 569 |
+
|
| 570 |
+
x += mu
|
| 571 |
+
return x
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
def _kpp(data, k, rng, xp):
|
| 575 |
+
""" Picks k points in the data based on the kmeans++ method.
|
| 576 |
+
|
| 577 |
+
Parameters
|
| 578 |
+
----------
|
| 579 |
+
data : ndarray
|
| 580 |
+
Expect a rank 1 or 2 array. Rank 1 is assumed to describe 1-D
|
| 581 |
+
data, rank 2 multidimensional data, in which case one
|
| 582 |
+
row is one observation.
|
| 583 |
+
k : int
|
| 584 |
+
Number of samples to generate.
|
| 585 |
+
rng : `numpy.random.Generator` or `numpy.random.RandomState`
|
| 586 |
+
Random number generator.
|
| 587 |
+
|
| 588 |
+
Returns
|
| 589 |
+
-------
|
| 590 |
+
init : ndarray
|
| 591 |
+
A 'k' by 'N' containing the initial centroids.
|
| 592 |
+
|
| 593 |
+
References
|
| 594 |
+
----------
|
| 595 |
+
.. [1] D. Arthur and S. Vassilvitskii, "k-means++: the advantages of
|
| 596 |
+
careful seeding", Proceedings of the Eighteenth Annual ACM-SIAM Symposium
|
| 597 |
+
on Discrete Algorithms, 2007.
|
| 598 |
+
"""
|
| 599 |
+
|
| 600 |
+
dims = data.shape[1] if len(data.shape) > 1 else 1
|
| 601 |
+
|
| 602 |
+
init = xp.empty((int(k), dims))
|
| 603 |
+
|
| 604 |
+
for i in range(k):
|
| 605 |
+
if i == 0:
|
| 606 |
+
init[i, :] = data[rng_integers(rng, data.shape[0]), :]
|
| 607 |
+
|
| 608 |
+
else:
|
| 609 |
+
D2 = cdist(init[:i,:], data, metric='sqeuclidean').min(axis=0)
|
| 610 |
+
probs = D2/D2.sum()
|
| 611 |
+
cumprobs = probs.cumsum()
|
| 612 |
+
r = rng.uniform()
|
| 613 |
+
cumprobs = np.asarray(cumprobs)
|
| 614 |
+
init[i, :] = data[np.searchsorted(cumprobs, r), :]
|
| 615 |
+
|
| 616 |
+
return init
|
| 617 |
+
|
| 618 |
+
|
| 619 |
+
_valid_init_meth = {'random': _krandinit, 'points': _kpoints, '++': _kpp}
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
def _missing_warn():
|
| 623 |
+
"""Print a warning when called."""
|
| 624 |
+
warnings.warn("One of the clusters is empty. "
|
| 625 |
+
"Re-run kmeans with a different initialization.",
|
| 626 |
+
stacklevel=3)
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
def _missing_raise():
|
| 630 |
+
"""Raise a ClusterError when called."""
|
| 631 |
+
raise ClusterError("One of the clusters is empty. "
|
| 632 |
+
"Re-run kmeans with a different initialization.")
|
| 633 |
+
|
| 634 |
+
|
| 635 |
+
_valid_miss_meth = {'warn': _missing_warn, 'raise': _missing_raise}
|
| 636 |
+
|
| 637 |
+
|
| 638 |
+
def kmeans2(data, k, iter=10, thresh=1e-5, minit='random',
|
| 639 |
+
missing='warn', check_finite=True, *, seed=None):
|
| 640 |
+
"""
|
| 641 |
+
Classify a set of observations into k clusters using the k-means algorithm.
|
| 642 |
+
|
| 643 |
+
The algorithm attempts to minimize the Euclidean distance between
|
| 644 |
+
observations and centroids. Several initialization methods are
|
| 645 |
+
included.
|
| 646 |
+
|
| 647 |
+
Parameters
|
| 648 |
+
----------
|
| 649 |
+
data : ndarray
|
| 650 |
+
A 'M' by 'N' array of 'M' observations in 'N' dimensions or a length
|
| 651 |
+
'M' array of 'M' 1-D observations.
|
| 652 |
+
k : int or ndarray
|
| 653 |
+
The number of clusters to form as well as the number of
|
| 654 |
+
centroids to generate. If `minit` initialization string is
|
| 655 |
+
'matrix', or if a ndarray is given instead, it is
|
| 656 |
+
interpreted as initial cluster to use instead.
|
| 657 |
+
iter : int, optional
|
| 658 |
+
Number of iterations of the k-means algorithm to run. Note
|
| 659 |
+
that this differs in meaning from the iters parameter to
|
| 660 |
+
the kmeans function.
|
| 661 |
+
thresh : float, optional
|
| 662 |
+
(not used yet)
|
| 663 |
+
minit : str, optional
|
| 664 |
+
Method for initialization. Available methods are 'random',
|
| 665 |
+
'points', '++' and 'matrix':
|
| 666 |
+
|
| 667 |
+
'random': generate k centroids from a Gaussian with mean and
|
| 668 |
+
variance estimated from the data.
|
| 669 |
+
|
| 670 |
+
'points': choose k observations (rows) at random from data for
|
| 671 |
+
the initial centroids.
|
| 672 |
+
|
| 673 |
+
'++': choose k observations accordingly to the kmeans++ method
|
| 674 |
+
(careful seeding)
|
| 675 |
+
|
| 676 |
+
'matrix': interpret the k parameter as a k by M (or length k
|
| 677 |
+
array for 1-D data) array of initial centroids.
|
| 678 |
+
missing : str, optional
|
| 679 |
+
Method to deal with empty clusters. Available methods are
|
| 680 |
+
'warn' and 'raise':
|
| 681 |
+
|
| 682 |
+
'warn': give a warning and continue.
|
| 683 |
+
|
| 684 |
+
'raise': raise an ClusterError and terminate the algorithm.
|
| 685 |
+
check_finite : bool, optional
|
| 686 |
+
Whether to check that the input matrices contain only finite numbers.
|
| 687 |
+
Disabling may give a performance gain, but may result in problems
|
| 688 |
+
(crashes, non-termination) if the inputs do contain infinities or NaNs.
|
| 689 |
+
Default: True
|
| 690 |
+
seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional
|
| 691 |
+
Seed for initializing the pseudo-random number generator.
|
| 692 |
+
If `seed` is None (or `numpy.random`), the `numpy.random.RandomState`
|
| 693 |
+
singleton is used.
|
| 694 |
+
If `seed` is an int, a new ``RandomState`` instance is used,
|
| 695 |
+
seeded with `seed`.
|
| 696 |
+
If `seed` is already a ``Generator`` or ``RandomState`` instance then
|
| 697 |
+
that instance is used.
|
| 698 |
+
The default is None.
|
| 699 |
+
|
| 700 |
+
Returns
|
| 701 |
+
-------
|
| 702 |
+
centroid : ndarray
|
| 703 |
+
A 'k' by 'N' array of centroids found at the last iteration of
|
| 704 |
+
k-means.
|
| 705 |
+
label : ndarray
|
| 706 |
+
label[i] is the code or index of the centroid the
|
| 707 |
+
ith observation is closest to.
|
| 708 |
+
|
| 709 |
+
See Also
|
| 710 |
+
--------
|
| 711 |
+
kmeans
|
| 712 |
+
|
| 713 |
+
References
|
| 714 |
+
----------
|
| 715 |
+
.. [1] D. Arthur and S. Vassilvitskii, "k-means++: the advantages of
|
| 716 |
+
careful seeding", Proceedings of the Eighteenth Annual ACM-SIAM Symposium
|
| 717 |
+
on Discrete Algorithms, 2007.
|
| 718 |
+
|
| 719 |
+
Examples
|
| 720 |
+
--------
|
| 721 |
+
>>> from scipy.cluster.vq import kmeans2
|
| 722 |
+
>>> import matplotlib.pyplot as plt
|
| 723 |
+
>>> import numpy as np
|
| 724 |
+
|
| 725 |
+
Create z, an array with shape (100, 2) containing a mixture of samples
|
| 726 |
+
from three multivariate normal distributions.
|
| 727 |
+
|
| 728 |
+
>>> rng = np.random.default_rng()
|
| 729 |
+
>>> a = rng.multivariate_normal([0, 6], [[2, 1], [1, 1.5]], size=45)
|
| 730 |
+
>>> b = rng.multivariate_normal([2, 0], [[1, -1], [-1, 3]], size=30)
|
| 731 |
+
>>> c = rng.multivariate_normal([6, 4], [[5, 0], [0, 1.2]], size=25)
|
| 732 |
+
>>> z = np.concatenate((a, b, c))
|
| 733 |
+
>>> rng.shuffle(z)
|
| 734 |
+
|
| 735 |
+
Compute three clusters.
|
| 736 |
+
|
| 737 |
+
>>> centroid, label = kmeans2(z, 3, minit='points')
|
| 738 |
+
>>> centroid
|
| 739 |
+
array([[ 2.22274463, -0.61666946], # may vary
|
| 740 |
+
[ 0.54069047, 5.86541444],
|
| 741 |
+
[ 6.73846769, 4.01991898]])
|
| 742 |
+
|
| 743 |
+
How many points are in each cluster?
|
| 744 |
+
|
| 745 |
+
>>> counts = np.bincount(label)
|
| 746 |
+
>>> counts
|
| 747 |
+
array([29, 51, 20]) # may vary
|
| 748 |
+
|
| 749 |
+
Plot the clusters.
|
| 750 |
+
|
| 751 |
+
>>> w0 = z[label == 0]
|
| 752 |
+
>>> w1 = z[label == 1]
|
| 753 |
+
>>> w2 = z[label == 2]
|
| 754 |
+
>>> plt.plot(w0[:, 0], w0[:, 1], 'o', alpha=0.5, label='cluster 0')
|
| 755 |
+
>>> plt.plot(w1[:, 0], w1[:, 1], 'd', alpha=0.5, label='cluster 1')
|
| 756 |
+
>>> plt.plot(w2[:, 0], w2[:, 1], 's', alpha=0.5, label='cluster 2')
|
| 757 |
+
>>> plt.plot(centroid[:, 0], centroid[:, 1], 'k*', label='centroids')
|
| 758 |
+
>>> plt.axis('equal')
|
| 759 |
+
>>> plt.legend(shadow=True)
|
| 760 |
+
>>> plt.show()
|
| 761 |
+
|
| 762 |
+
"""
|
| 763 |
+
if int(iter) < 1:
|
| 764 |
+
raise ValueError("Invalid iter (%s), "
|
| 765 |
+
"must be a positive integer." % iter)
|
| 766 |
+
try:
|
| 767 |
+
miss_meth = _valid_miss_meth[missing]
|
| 768 |
+
except KeyError as e:
|
| 769 |
+
raise ValueError(f"Unknown missing method {missing!r}") from e
|
| 770 |
+
|
| 771 |
+
xp = array_namespace(data, k)
|
| 772 |
+
data = as_xparray(data, xp=xp, check_finite=check_finite)
|
| 773 |
+
code_book = copy(k, xp=xp)
|
| 774 |
+
if data.ndim == 1:
|
| 775 |
+
d = 1
|
| 776 |
+
elif data.ndim == 2:
|
| 777 |
+
d = data.shape[1]
|
| 778 |
+
else:
|
| 779 |
+
raise ValueError("Input of rank > 2 is not supported.")
|
| 780 |
+
|
| 781 |
+
if size(data) < 1 or size(code_book) < 1:
|
| 782 |
+
raise ValueError("Empty input is not supported.")
|
| 783 |
+
|
| 784 |
+
# If k is not a single value, it should be compatible with data's shape
|
| 785 |
+
if minit == 'matrix' or size(code_book) > 1:
|
| 786 |
+
if data.ndim != code_book.ndim:
|
| 787 |
+
raise ValueError("k array doesn't match data rank")
|
| 788 |
+
nc = code_book.shape[0]
|
| 789 |
+
if data.ndim > 1 and code_book.shape[1] != d:
|
| 790 |
+
raise ValueError("k array doesn't match data dimension")
|
| 791 |
+
else:
|
| 792 |
+
nc = int(code_book)
|
| 793 |
+
|
| 794 |
+
if nc < 1:
|
| 795 |
+
raise ValueError("Cannot ask kmeans2 for %d clusters"
|
| 796 |
+
" (k was %s)" % (nc, code_book))
|
| 797 |
+
elif nc != code_book:
|
| 798 |
+
warnings.warn("k was not an integer, was converted.", stacklevel=2)
|
| 799 |
+
|
| 800 |
+
try:
|
| 801 |
+
init_meth = _valid_init_meth[minit]
|
| 802 |
+
except KeyError as e:
|
| 803 |
+
raise ValueError(f"Unknown init method {minit!r}") from e
|
| 804 |
+
else:
|
| 805 |
+
rng = check_random_state(seed)
|
| 806 |
+
code_book = init_meth(data, code_book, rng, xp)
|
| 807 |
+
|
| 808 |
+
for i in range(iter):
|
| 809 |
+
# Compute the nearest neighbor for each obs using the current code book
|
| 810 |
+
label = vq(data, code_book, check_finite=check_finite)[0]
|
| 811 |
+
# Update the code book by computing centroids
|
| 812 |
+
data = np.asarray(data)
|
| 813 |
+
label = np.asarray(label)
|
| 814 |
+
new_code_book, has_members = _vq.update_cluster_means(data, label, nc)
|
| 815 |
+
if not has_members.all():
|
| 816 |
+
miss_meth()
|
| 817 |
+
# Set the empty clusters to their previous positions
|
| 818 |
+
new_code_book[~has_members] = code_book[~has_members]
|
| 819 |
+
code_book = new_code_book
|
| 820 |
+
|
| 821 |
+
return xp.asarray(code_book), xp.asarray(label)
|
rtme/lib/python3.10/site-packages/scipy/constants/__init__.py
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
r"""
|
| 2 |
+
==================================
|
| 3 |
+
Constants (:mod:`scipy.constants`)
|
| 4 |
+
==================================
|
| 5 |
+
|
| 6 |
+
.. currentmodule:: scipy.constants
|
| 7 |
+
|
| 8 |
+
Physical and mathematical constants and units.
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
Mathematical constants
|
| 12 |
+
======================
|
| 13 |
+
|
| 14 |
+
================ =================================================================
|
| 15 |
+
``pi`` Pi
|
| 16 |
+
``golden`` Golden ratio
|
| 17 |
+
``golden_ratio`` Golden ratio
|
| 18 |
+
================ =================================================================
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
Physical constants
|
| 22 |
+
==================
|
| 23 |
+
|
| 24 |
+
=========================== =================================================================
|
| 25 |
+
``c`` speed of light in vacuum
|
| 26 |
+
``speed_of_light`` speed of light in vacuum
|
| 27 |
+
``mu_0`` the magnetic constant :math:`\mu_0`
|
| 28 |
+
``epsilon_0`` the electric constant (vacuum permittivity), :math:`\epsilon_0`
|
| 29 |
+
``h`` the Planck constant :math:`h`
|
| 30 |
+
``Planck`` the Planck constant :math:`h`
|
| 31 |
+
``hbar`` :math:`\hbar = h/(2\pi)`
|
| 32 |
+
``G`` Newtonian constant of gravitation
|
| 33 |
+
``gravitational_constant`` Newtonian constant of gravitation
|
| 34 |
+
``g`` standard acceleration of gravity
|
| 35 |
+
``e`` elementary charge
|
| 36 |
+
``elementary_charge`` elementary charge
|
| 37 |
+
``R`` molar gas constant
|
| 38 |
+
``gas_constant`` molar gas constant
|
| 39 |
+
``alpha`` fine-structure constant
|
| 40 |
+
``fine_structure`` fine-structure constant
|
| 41 |
+
``N_A`` Avogadro constant
|
| 42 |
+
``Avogadro`` Avogadro constant
|
| 43 |
+
``k`` Boltzmann constant
|
| 44 |
+
``Boltzmann`` Boltzmann constant
|
| 45 |
+
``sigma`` Stefan-Boltzmann constant :math:`\sigma`
|
| 46 |
+
``Stefan_Boltzmann`` Stefan-Boltzmann constant :math:`\sigma`
|
| 47 |
+
``Wien`` Wien displacement law constant
|
| 48 |
+
``Rydberg`` Rydberg constant
|
| 49 |
+
``m_e`` electron mass
|
| 50 |
+
``electron_mass`` electron mass
|
| 51 |
+
``m_p`` proton mass
|
| 52 |
+
``proton_mass`` proton mass
|
| 53 |
+
``m_n`` neutron mass
|
| 54 |
+
``neutron_mass`` neutron mass
|
| 55 |
+
=========================== =================================================================
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
Constants database
|
| 59 |
+
------------------
|
| 60 |
+
|
| 61 |
+
In addition to the above variables, :mod:`scipy.constants` also contains the
|
| 62 |
+
2018 CODATA recommended values [CODATA2018]_ database containing more physical
|
| 63 |
+
constants.
|
| 64 |
+
|
| 65 |
+
.. autosummary::
|
| 66 |
+
:toctree: generated/
|
| 67 |
+
|
| 68 |
+
value -- Value in physical_constants indexed by key
|
| 69 |
+
unit -- Unit in physical_constants indexed by key
|
| 70 |
+
precision -- Relative precision in physical_constants indexed by key
|
| 71 |
+
find -- Return list of physical_constant keys with a given string
|
| 72 |
+
ConstantWarning -- Constant sought not in newest CODATA data set
|
| 73 |
+
|
| 74 |
+
.. data:: physical_constants
|
| 75 |
+
|
| 76 |
+
Dictionary of physical constants, of the format
|
| 77 |
+
``physical_constants[name] = (value, unit, uncertainty)``.
|
| 78 |
+
|
| 79 |
+
Available constants:
|
| 80 |
+
|
| 81 |
+
====================================================================== ====
|
| 82 |
+
%(constant_names)s
|
| 83 |
+
====================================================================== ====
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
Units
|
| 87 |
+
=====
|
| 88 |
+
|
| 89 |
+
SI prefixes
|
| 90 |
+
-----------
|
| 91 |
+
|
| 92 |
+
============ =================================================================
|
| 93 |
+
``quetta`` :math:`10^{30}`
|
| 94 |
+
``ronna`` :math:`10^{27}`
|
| 95 |
+
``yotta`` :math:`10^{24}`
|
| 96 |
+
``zetta`` :math:`10^{21}`
|
| 97 |
+
``exa`` :math:`10^{18}`
|
| 98 |
+
``peta`` :math:`10^{15}`
|
| 99 |
+
``tera`` :math:`10^{12}`
|
| 100 |
+
``giga`` :math:`10^{9}`
|
| 101 |
+
``mega`` :math:`10^{6}`
|
| 102 |
+
``kilo`` :math:`10^{3}`
|
| 103 |
+
``hecto`` :math:`10^{2}`
|
| 104 |
+
``deka`` :math:`10^{1}`
|
| 105 |
+
``deci`` :math:`10^{-1}`
|
| 106 |
+
``centi`` :math:`10^{-2}`
|
| 107 |
+
``milli`` :math:`10^{-3}`
|
| 108 |
+
``micro`` :math:`10^{-6}`
|
| 109 |
+
``nano`` :math:`10^{-9}`
|
| 110 |
+
``pico`` :math:`10^{-12}`
|
| 111 |
+
``femto`` :math:`10^{-15}`
|
| 112 |
+
``atto`` :math:`10^{-18}`
|
| 113 |
+
``zepto`` :math:`10^{-21}`
|
| 114 |
+
``yocto`` :math:`10^{-24}`
|
| 115 |
+
``ronto`` :math:`10^{-27}`
|
| 116 |
+
``quecto`` :math:`10^{-30}`
|
| 117 |
+
============ =================================================================
|
| 118 |
+
|
| 119 |
+
Binary prefixes
|
| 120 |
+
---------------
|
| 121 |
+
|
| 122 |
+
============ =================================================================
|
| 123 |
+
``kibi`` :math:`2^{10}`
|
| 124 |
+
``mebi`` :math:`2^{20}`
|
| 125 |
+
``gibi`` :math:`2^{30}`
|
| 126 |
+
``tebi`` :math:`2^{40}`
|
| 127 |
+
``pebi`` :math:`2^{50}`
|
| 128 |
+
``exbi`` :math:`2^{60}`
|
| 129 |
+
``zebi`` :math:`2^{70}`
|
| 130 |
+
``yobi`` :math:`2^{80}`
|
| 131 |
+
============ =================================================================
|
| 132 |
+
|
| 133 |
+
Mass
|
| 134 |
+
----
|
| 135 |
+
|
| 136 |
+
================= ============================================================
|
| 137 |
+
``gram`` :math:`10^{-3}` kg
|
| 138 |
+
``metric_ton`` :math:`10^{3}` kg
|
| 139 |
+
``grain`` one grain in kg
|
| 140 |
+
``lb`` one pound (avoirdupous) in kg
|
| 141 |
+
``pound`` one pound (avoirdupous) in kg
|
| 142 |
+
``blob`` one inch version of a slug in kg (added in 1.0.0)
|
| 143 |
+
``slinch`` one inch version of a slug in kg (added in 1.0.0)
|
| 144 |
+
``slug`` one slug in kg (added in 1.0.0)
|
| 145 |
+
``oz`` one ounce in kg
|
| 146 |
+
``ounce`` one ounce in kg
|
| 147 |
+
``stone`` one stone in kg
|
| 148 |
+
``grain`` one grain in kg
|
| 149 |
+
``long_ton`` one long ton in kg
|
| 150 |
+
``short_ton`` one short ton in kg
|
| 151 |
+
``troy_ounce`` one Troy ounce in kg
|
| 152 |
+
``troy_pound`` one Troy pound in kg
|
| 153 |
+
``carat`` one carat in kg
|
| 154 |
+
``m_u`` atomic mass constant (in kg)
|
| 155 |
+
``u`` atomic mass constant (in kg)
|
| 156 |
+
``atomic_mass`` atomic mass constant (in kg)
|
| 157 |
+
================= ============================================================
|
| 158 |
+
|
| 159 |
+
Angle
|
| 160 |
+
-----
|
| 161 |
+
|
| 162 |
+
================= ============================================================
|
| 163 |
+
``degree`` degree in radians
|
| 164 |
+
``arcmin`` arc minute in radians
|
| 165 |
+
``arcminute`` arc minute in radians
|
| 166 |
+
``arcsec`` arc second in radians
|
| 167 |
+
``arcsecond`` arc second in radians
|
| 168 |
+
================= ============================================================
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
Time
|
| 172 |
+
----
|
| 173 |
+
|
| 174 |
+
================= ============================================================
|
| 175 |
+
``minute`` one minute in seconds
|
| 176 |
+
``hour`` one hour in seconds
|
| 177 |
+
``day`` one day in seconds
|
| 178 |
+
``week`` one week in seconds
|
| 179 |
+
``year`` one year (365 days) in seconds
|
| 180 |
+
``Julian_year`` one Julian year (365.25 days) in seconds
|
| 181 |
+
================= ============================================================
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
Length
|
| 185 |
+
------
|
| 186 |
+
|
| 187 |
+
===================== ============================================================
|
| 188 |
+
``inch`` one inch in meters
|
| 189 |
+
``foot`` one foot in meters
|
| 190 |
+
``yard`` one yard in meters
|
| 191 |
+
``mile`` one mile in meters
|
| 192 |
+
``mil`` one mil in meters
|
| 193 |
+
``pt`` one point in meters
|
| 194 |
+
``point`` one point in meters
|
| 195 |
+
``survey_foot`` one survey foot in meters
|
| 196 |
+
``survey_mile`` one survey mile in meters
|
| 197 |
+
``nautical_mile`` one nautical mile in meters
|
| 198 |
+
``fermi`` one Fermi in meters
|
| 199 |
+
``angstrom`` one Angstrom in meters
|
| 200 |
+
``micron`` one micron in meters
|
| 201 |
+
``au`` one astronomical unit in meters
|
| 202 |
+
``astronomical_unit`` one astronomical unit in meters
|
| 203 |
+
``light_year`` one light year in meters
|
| 204 |
+
``parsec`` one parsec in meters
|
| 205 |
+
===================== ============================================================
|
| 206 |
+
|
| 207 |
+
Pressure
|
| 208 |
+
--------
|
| 209 |
+
|
| 210 |
+
================= ============================================================
|
| 211 |
+
``atm`` standard atmosphere in pascals
|
| 212 |
+
``atmosphere`` standard atmosphere in pascals
|
| 213 |
+
``bar`` one bar in pascals
|
| 214 |
+
``torr`` one torr (mmHg) in pascals
|
| 215 |
+
``mmHg`` one torr (mmHg) in pascals
|
| 216 |
+
``psi`` one psi in pascals
|
| 217 |
+
================= ============================================================
|
| 218 |
+
|
| 219 |
+
Area
|
| 220 |
+
----
|
| 221 |
+
|
| 222 |
+
================= ============================================================
|
| 223 |
+
``hectare`` one hectare in square meters
|
| 224 |
+
``acre`` one acre in square meters
|
| 225 |
+
================= ============================================================
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
Volume
|
| 229 |
+
------
|
| 230 |
+
|
| 231 |
+
=================== ========================================================
|
| 232 |
+
``liter`` one liter in cubic meters
|
| 233 |
+
``litre`` one liter in cubic meters
|
| 234 |
+
``gallon`` one gallon (US) in cubic meters
|
| 235 |
+
``gallon_US`` one gallon (US) in cubic meters
|
| 236 |
+
``gallon_imp`` one gallon (UK) in cubic meters
|
| 237 |
+
``fluid_ounce`` one fluid ounce (US) in cubic meters
|
| 238 |
+
``fluid_ounce_US`` one fluid ounce (US) in cubic meters
|
| 239 |
+
``fluid_ounce_imp`` one fluid ounce (UK) in cubic meters
|
| 240 |
+
``bbl`` one barrel in cubic meters
|
| 241 |
+
``barrel`` one barrel in cubic meters
|
| 242 |
+
=================== ========================================================
|
| 243 |
+
|
| 244 |
+
Speed
|
| 245 |
+
-----
|
| 246 |
+
|
| 247 |
+
================== ==========================================================
|
| 248 |
+
``kmh`` kilometers per hour in meters per second
|
| 249 |
+
``mph`` miles per hour in meters per second
|
| 250 |
+
``mach`` one Mach (approx., at 15 C, 1 atm) in meters per second
|
| 251 |
+
``speed_of_sound`` one Mach (approx., at 15 C, 1 atm) in meters per second
|
| 252 |
+
``knot`` one knot in meters per second
|
| 253 |
+
================== ==========================================================
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
Temperature
|
| 257 |
+
-----------
|
| 258 |
+
|
| 259 |
+
===================== =======================================================
|
| 260 |
+
``zero_Celsius`` zero of Celsius scale in Kelvin
|
| 261 |
+
``degree_Fahrenheit`` one Fahrenheit (only differences) in Kelvins
|
| 262 |
+
===================== =======================================================
|
| 263 |
+
|
| 264 |
+
.. autosummary::
|
| 265 |
+
:toctree: generated/
|
| 266 |
+
|
| 267 |
+
convert_temperature
|
| 268 |
+
|
| 269 |
+
Energy
|
| 270 |
+
------
|
| 271 |
+
|
| 272 |
+
==================== =======================================================
|
| 273 |
+
``eV`` one electron volt in Joules
|
| 274 |
+
``electron_volt`` one electron volt in Joules
|
| 275 |
+
``calorie`` one calorie (thermochemical) in Joules
|
| 276 |
+
``calorie_th`` one calorie (thermochemical) in Joules
|
| 277 |
+
``calorie_IT`` one calorie (International Steam Table calorie, 1956) in Joules
|
| 278 |
+
``erg`` one erg in Joules
|
| 279 |
+
``Btu`` one British thermal unit (International Steam Table) in Joules
|
| 280 |
+
``Btu_IT`` one British thermal unit (International Steam Table) in Joules
|
| 281 |
+
``Btu_th`` one British thermal unit (thermochemical) in Joules
|
| 282 |
+
``ton_TNT`` one ton of TNT in Joules
|
| 283 |
+
==================== =======================================================
|
| 284 |
+
|
| 285 |
+
Power
|
| 286 |
+
-----
|
| 287 |
+
|
| 288 |
+
==================== =======================================================
|
| 289 |
+
``hp`` one horsepower in watts
|
| 290 |
+
``horsepower`` one horsepower in watts
|
| 291 |
+
==================== =======================================================
|
| 292 |
+
|
| 293 |
+
Force
|
| 294 |
+
-----
|
| 295 |
+
|
| 296 |
+
==================== =======================================================
|
| 297 |
+
``dyn`` one dyne in newtons
|
| 298 |
+
``dyne`` one dyne in newtons
|
| 299 |
+
``lbf`` one pound force in newtons
|
| 300 |
+
``pound_force`` one pound force in newtons
|
| 301 |
+
``kgf`` one kilogram force in newtons
|
| 302 |
+
``kilogram_force`` one kilogram force in newtons
|
| 303 |
+
==================== =======================================================
|
| 304 |
+
|
| 305 |
+
Optics
|
| 306 |
+
------
|
| 307 |
+
|
| 308 |
+
.. autosummary::
|
| 309 |
+
:toctree: generated/
|
| 310 |
+
|
| 311 |
+
lambda2nu
|
| 312 |
+
nu2lambda
|
| 313 |
+
|
| 314 |
+
References
|
| 315 |
+
==========
|
| 316 |
+
|
| 317 |
+
.. [CODATA2018] CODATA Recommended Values of the Fundamental
|
| 318 |
+
Physical Constants 2018.
|
| 319 |
+
|
| 320 |
+
https://physics.nist.gov/cuu/Constants/
|
| 321 |
+
|
| 322 |
+
""" # noqa: E501
|
| 323 |
+
# Modules contributed by BasSw (wegwerp@gmail.com)
|
| 324 |
+
from ._codata import *
|
| 325 |
+
from ._constants import *
|
| 326 |
+
from ._codata import _obsolete_constants, physical_constants
|
| 327 |
+
|
| 328 |
+
# Deprecated namespaces, to be removed in v2.0.0
|
| 329 |
+
from . import codata, constants
|
| 330 |
+
|
| 331 |
+
_constant_names_list = [(_k.lower(), _k, _v)
|
| 332 |
+
for _k, _v in physical_constants.items()
|
| 333 |
+
if _k not in _obsolete_constants]
|
| 334 |
+
_constant_names = "\n".join(["``{}``{} {} {}".format(_x[1], " "*(66-len(_x[1])),
|
| 335 |
+
_x[2][0], _x[2][1])
|
| 336 |
+
for _x in sorted(_constant_names_list)])
|
| 337 |
+
if __doc__:
|
| 338 |
+
__doc__ = __doc__ % dict(constant_names=_constant_names)
|
| 339 |
+
|
| 340 |
+
del _constant_names
|
| 341 |
+
del _constant_names_list
|
| 342 |
+
|
| 343 |
+
__all__ = [s for s in dir() if not s.startswith('_')]
|
| 344 |
+
|
| 345 |
+
from scipy._lib._testutils import PytestTester
|
| 346 |
+
test = PytestTester(__name__)
|
| 347 |
+
del PytestTester
|