ckadirt commited on
Commit
5df640c
·
verified ·
1 Parent(s): 0f7ba26

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. rtme/lib/python3.10/site-packages/scipy/_lib/__pycache__/_pep440.cpython-310.pyc +0 -0
  3. rtme/lib/python3.10/site-packages/scipy/_lib/__pycache__/_testutils.cpython-310.pyc +0 -0
  4. rtme/lib/python3.10/site-packages/scipy/_lib/_ccallback_c.cpython-310-x86_64-linux-gnu.so +3 -0
  5. rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/LICENSE +29 -0
  6. rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/__init__.py +116 -0
  7. rtme/lib/python3.10/site-packages/scipy/_lib/_uarray/_backend.py +704 -0
  8. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/__init__.py +22 -0
  9. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/_internal.py +43 -0
  10. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/__init__.py +1 -0
  11. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_aliases.py +530 -0
  12. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_helpers.py +234 -0
  13. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_linalg.py +155 -0
  14. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/common/_typing.py +20 -0
  15. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/__init__.py +16 -0
  16. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/_aliases.py +69 -0
  17. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/_typing.py +46 -0
  18. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/cupy/linalg.py +41 -0
  19. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/__init__.py +22 -0
  20. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/_aliases.py +69 -0
  21. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/_typing.py +46 -0
  22. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/numpy/linalg.py +34 -0
  23. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/__init__.py +22 -0
  24. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/_aliases.py +704 -0
  25. rtme/lib/python3.10/site-packages/scipy/_lib/array_api_compat/array_api_compat/torch/linalg.py +55 -0
  26. rtme/lib/python3.10/site-packages/scipy/_lib/tests/__init__.py +0 -0
  27. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__gcutils.py +101 -0
  28. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__pep440.py +67 -0
  29. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__testutils.py +32 -0
  30. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__threadsafety.py +51 -0
  31. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test__util.py +408 -0
  32. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_array_api.py +109 -0
  33. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_bunch.py +162 -0
  34. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_ccallback.py +204 -0
  35. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_deprecation.py +10 -0
  36. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_import_cycles.py +14 -0
  37. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_public_api.py +474 -0
  38. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_scipy_version.py +18 -0
  39. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_tmpdirs.py +42 -0
  40. rtme/lib/python3.10/site-packages/scipy/_lib/tests/test_warnings.py +135 -0
  41. rtme/lib/python3.10/site-packages/scipy/_lib/uarray.py +31 -0
  42. rtme/lib/python3.10/site-packages/scipy/cluster/__init__.py +31 -0
  43. rtme/lib/python3.10/site-packages/scipy/cluster/hierarchy.py +0 -0
  44. rtme/lib/python3.10/site-packages/scipy/cluster/tests/__init__.py +0 -0
  45. rtme/lib/python3.10/site-packages/scipy/cluster/tests/hierarchy_test_data.py +145 -0
  46. rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_disjoint_set.py +202 -0
  47. rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_hierarchy.py +1349 -0
  48. rtme/lib/python3.10/site-packages/scipy/cluster/tests/test_vq.py +416 -0
  49. rtme/lib/python3.10/site-packages/scipy/cluster/vq.py +821 -0
  50. 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