Liusuthu commited on
Commit
2fa66a5
โ€ข
1 Parent(s): 1464d1f

Upload folder using huggingface_hub

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 +26 -0
  2. scipy/__config__.py +161 -0
  3. scipy/__init__.py +169 -0
  4. scipy/__pycache__/__config__.cpython-39.pyc +0 -0
  5. scipy/__pycache__/__init__.cpython-39.pyc +0 -0
  6. scipy/__pycache__/_distributor_init.cpython-39.pyc +0 -0
  7. scipy/__pycache__/conftest.cpython-39.pyc +0 -0
  8. scipy/__pycache__/version.cpython-39.pyc +0 -0
  9. scipy/_distributor_init.py +32 -0
  10. scipy/_lib/__init__.py +14 -0
  11. scipy/_lib/__pycache__/__init__.cpython-39.pyc +0 -0
  12. scipy/_lib/__pycache__/_array_api.cpython-39.pyc +0 -0
  13. scipy/_lib/__pycache__/_bunch.cpython-39.pyc +0 -0
  14. scipy/_lib/__pycache__/_ccallback.cpython-39.pyc +0 -0
  15. scipy/_lib/__pycache__/_disjoint_set.cpython-39.pyc +0 -0
  16. scipy/_lib/__pycache__/_docscrape.cpython-39.pyc +0 -0
  17. scipy/_lib/__pycache__/_finite_differences.cpython-39.pyc +0 -0
  18. scipy/_lib/__pycache__/_gcutils.cpython-39.pyc +0 -0
  19. scipy/_lib/__pycache__/_pep440.cpython-39.pyc +0 -0
  20. scipy/_lib/__pycache__/_testutils.cpython-39.pyc +0 -0
  21. scipy/_lib/__pycache__/_threadsafety.cpython-39.pyc +0 -0
  22. scipy/_lib/__pycache__/_tmpdirs.cpython-39.pyc +0 -0
  23. scipy/_lib/__pycache__/_util.cpython-39.pyc +0 -0
  24. scipy/_lib/__pycache__/decorator.cpython-39.pyc +0 -0
  25. scipy/_lib/__pycache__/deprecation.cpython-39.pyc +0 -0
  26. scipy/_lib/__pycache__/doccer.cpython-39.pyc +0 -0
  27. scipy/_lib/__pycache__/uarray.cpython-39.pyc +0 -0
  28. scipy/_lib/_array_api.py +353 -0
  29. scipy/_lib/_bunch.py +225 -0
  30. scipy/_lib/_ccallback.py +251 -0
  31. scipy/_lib/_ccallback_c.cp39-win_amd64.dll.a +0 -0
  32. scipy/_lib/_ccallback_c.cp39-win_amd64.pyd +0 -0
  33. scipy/_lib/_disjoint_set.py +254 -0
  34. scipy/_lib/_docscrape.py +679 -0
  35. scipy/_lib/_finite_differences.py +145 -0
  36. scipy/_lib/_fpumode.cp39-win_amd64.dll.a +0 -0
  37. scipy/_lib/_fpumode.cp39-win_amd64.pyd +0 -0
  38. scipy/_lib/_gcutils.py +105 -0
  39. scipy/_lib/_pep440.py +487 -0
  40. scipy/_lib/_test_ccallback.cp39-win_amd64.dll.a +0 -0
  41. scipy/_lib/_test_ccallback.cp39-win_amd64.pyd +0 -0
  42. scipy/_lib/_test_deprecation_call.cp39-win_amd64.dll.a +0 -0
  43. scipy/_lib/_test_deprecation_call.cp39-win_amd64.pyd +0 -0
  44. scipy/_lib/_test_deprecation_def.cp39-win_amd64.dll.a +0 -0
  45. scipy/_lib/_test_deprecation_def.cp39-win_amd64.pyd +0 -0
  46. scipy/_lib/_testutils.py +257 -0
  47. scipy/_lib/_threadsafety.py +58 -0
  48. scipy/_lib/_tmpdirs.py +86 -0
  49. scipy/_lib/_uarray/LICENSE +29 -0
  50. scipy/_lib/_uarray/__init__.py +116 -0
.gitattributes CHANGED
@@ -33,3 +33,29 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ scipy/fft/_pocketfft/pypocketfft.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
37
+ scipy/interpolate/_rbfinterp_pythran.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
38
+ scipy/io/_fast_matrix_market/_fmm_core.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
39
+ scipy/linalg/_flapack.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
40
+ scipy/misc/face.dat filter=lfs diff=lfs merge=lfs -text
41
+ scipy/optimize/_group_columns.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
42
+ scipy/optimize/_highs/_highs_wrapper.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
43
+ scipy/signal/_max_len_seq_inner.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
44
+ scipy/signal/_spectral.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
45
+ scipy/sparse/_sparsetools.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
46
+ scipy/spatial/_ckdtree.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
47
+ scipy/spatial/_distance_pybind.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
48
+ scipy/spatial/_qhull.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
49
+ scipy/special/_ufuncs.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
50
+ scipy/special/_ufuncs_cxx.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
51
+ scipy/special/cython_special.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
52
+ scipy/stats/_boost/beta_ufunc.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
53
+ scipy/stats/_boost/binom_ufunc.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
54
+ scipy/stats/_boost/hypergeom_ufunc.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
55
+ scipy/stats/_boost/invgauss_ufunc.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
56
+ scipy/stats/_boost/nbinom_ufunc.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
57
+ scipy/stats/_boost/ncf_ufunc.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
58
+ scipy/stats/_boost/nct_ufunc.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
59
+ scipy/stats/_boost/ncx2_ufunc.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
60
+ scipy/stats/_stats_pythran.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
61
+ scipy/stats/_unuran/unuran_wrapper.cp39-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
scipy/__config__.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is generated by SciPy's build process
2
+ # It contains system_info results at the time of building this package.
3
+ from enum import Enum
4
+
5
+ __all__ = ["show"]
6
+ _built_with_meson = True
7
+
8
+
9
+ class DisplayModes(Enum):
10
+ stdout = "stdout"
11
+ dicts = "dicts"
12
+
13
+
14
+ def _cleanup(d):
15
+ """
16
+ Removes empty values in a `dict` recursively
17
+ This ensures we remove values that Meson could not provide to CONFIG
18
+ """
19
+ if isinstance(d, dict):
20
+ return { k: _cleanup(v) for k, v in d.items() if v != '' and _cleanup(v) != '' }
21
+ else:
22
+ return d
23
+
24
+
25
+ CONFIG = _cleanup(
26
+ {
27
+ "Compilers": {
28
+ "c": {
29
+ "name": "gcc",
30
+ "linker": "ld.bfd",
31
+ "version": "10.3.0",
32
+ "commands": "cc",
33
+ "args": "",
34
+ "linker args": "",
35
+ },
36
+ "cython": {
37
+ "name": "cython",
38
+ "linker": "cython",
39
+ "version": "3.0.8",
40
+ "commands": "cython",
41
+ "args": "",
42
+ "linker args": "",
43
+ },
44
+ "c++": {
45
+ "name": "gcc",
46
+ "linker": "ld.bfd",
47
+ "version": "10.3.0",
48
+ "commands": "c++",
49
+ "args": "",
50
+ "linker args": "",
51
+ },
52
+ "fortran": {
53
+ "name": "gcc",
54
+ "linker": "ld.bfd",
55
+ "version": "10.3.0",
56
+ "commands": "gfortran",
57
+ "args": "",
58
+ "linker args": "",
59
+ },
60
+ "pythran": {
61
+ "version": "0.15.0",
62
+ "include directory": r"C:\Users\runneradmin\AppData\Local\Temp\pip-build-env-rikezz0v\overlay\Lib\site-packages/pythran"
63
+ },
64
+ },
65
+ "Machine Information": {
66
+ "host": {
67
+ "cpu": "x86_64",
68
+ "family": "x86_64",
69
+ "endian": "little",
70
+ "system": "windows",
71
+ },
72
+ "build": {
73
+ "cpu": "x86_64",
74
+ "family": "x86_64",
75
+ "endian": "little",
76
+ "system": "windows",
77
+ },
78
+ "cross-compiled": bool("False".lower().replace('false', '')),
79
+ },
80
+ "Build Dependencies": {
81
+ "blas": {
82
+ "name": "openblas",
83
+ "found": bool("True".lower().replace('false', '')),
84
+ "version": "0.3.21.dev",
85
+ "detection method": "pkgconfig",
86
+ "include directory": r"/c/opt/64/include",
87
+ "lib directory": r"/c/opt/64/lib",
88
+ "openblas configuration": "USE_64BITINT= DYNAMIC_ARCH=1 DYNAMIC_OLDER= NO_CBLAS= NO_LAPACK= NO_LAPACKE= NO_AFFINITY=1 USE_OPENMP= SKYLAKEX MAX_THREADS=2",
89
+ "pc file directory": r"c:/opt/64/lib/pkgconfig",
90
+ },
91
+ "lapack": {
92
+ "name": "openblas",
93
+ "found": bool("True".lower().replace('false', '')),
94
+ "version": "0.3.21.dev",
95
+ "detection method": "pkgconfig",
96
+ "include directory": r"/c/opt/64/include",
97
+ "lib directory": r"/c/opt/64/lib",
98
+ "openblas configuration": "USE_64BITINT= DYNAMIC_ARCH=1 DYNAMIC_OLDER= NO_CBLAS= NO_LAPACK= NO_LAPACKE= NO_AFFINITY=1 USE_OPENMP= SKYLAKEX MAX_THREADS=2",
99
+ "pc file directory": r"c:/opt/64/lib/pkgconfig",
100
+ },
101
+ "pybind11": {
102
+ "name": "pybind11",
103
+ "version": "2.11.1",
104
+ "detection method": "config-tool",
105
+ "include directory": r"unknown",
106
+ },
107
+ },
108
+ "Python Information": {
109
+ "path": r"C:\Users\runneradmin\AppData\Local\Temp\cibw-run-ee1litsm\cp39-win_amd64\build\venv\Scripts\python.exe",
110
+ "version": "3.9",
111
+ },
112
+ }
113
+ )
114
+
115
+
116
+ def _check_pyyaml():
117
+ import yaml
118
+
119
+ return yaml
120
+
121
+
122
+ def show(mode=DisplayModes.stdout.value):
123
+ """
124
+ Show libraries and system information on which SciPy was built
125
+ and is being used
126
+
127
+ Parameters
128
+ ----------
129
+ mode : {`'stdout'`, `'dicts'`}, optional.
130
+ Indicates how to display the config information.
131
+ `'stdout'` prints to console, `'dicts'` returns a dictionary
132
+ of the configuration.
133
+
134
+ Returns
135
+ -------
136
+ out : {`dict`, `None`}
137
+ If mode is `'dicts'`, a dict is returned, else None
138
+
139
+ Notes
140
+ -----
141
+ 1. The `'stdout'` mode will give more readable
142
+ output if ``pyyaml`` is installed
143
+
144
+ """
145
+ if mode == DisplayModes.stdout.value:
146
+ try: # Non-standard library, check import
147
+ yaml = _check_pyyaml()
148
+
149
+ print(yaml.dump(CONFIG))
150
+ except ModuleNotFoundError:
151
+ import warnings
152
+ import json
153
+
154
+ warnings.warn("Install `pyyaml` for better output", stacklevel=1)
155
+ print(json.dumps(CONFIG, indent=2))
156
+ elif mode == DisplayModes.dicts.value:
157
+ return CONFIG
158
+ else:
159
+ raise AttributeError(
160
+ f"Invalid `mode`, use one of: {', '.join([e.value for e in DisplayModes])}"
161
+ )
scipy/__init__.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SciPy: A scientific computing package for Python
3
+ ================================================
4
+
5
+ Documentation is available in the docstrings and
6
+ online at https://docs.scipy.org.
7
+
8
+ Subpackages
9
+ -----------
10
+ Using any of these subpackages requires an explicit import. For example,
11
+ ``import scipy.cluster``.
12
+
13
+ ::
14
+
15
+ cluster --- Vector Quantization / Kmeans
16
+ constants --- Physical and mathematical constants and units
17
+ datasets --- Dataset methods
18
+ fft --- Discrete Fourier transforms
19
+ fftpack --- Legacy discrete Fourier transforms
20
+ integrate --- Integration routines
21
+ interpolate --- Interpolation Tools
22
+ io --- Data input and output
23
+ linalg --- Linear algebra routines
24
+ misc --- Utilities that don't have another home.
25
+ ndimage --- N-D image package
26
+ odr --- Orthogonal Distance Regression
27
+ optimize --- Optimization Tools
28
+ signal --- Signal Processing Tools
29
+ sparse --- Sparse Matrices
30
+ spatial --- Spatial data structures and algorithms
31
+ special --- Special functions
32
+ stats --- Statistical Functions
33
+
34
+ Public API in the main SciPy namespace
35
+ --------------------------------------
36
+ ::
37
+
38
+ __version__ --- SciPy version string
39
+ LowLevelCallable --- Low-level callback function
40
+ show_config --- Show scipy build configuration
41
+ test --- Run scipy unittests
42
+
43
+ """
44
+
45
+
46
+ # start delvewheel patch
47
+ def _delvewheel_patch_1_5_2():
48
+ import ctypes
49
+ import os
50
+ import platform
51
+ import sys
52
+ libs_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'scipy.libs'))
53
+ is_conda_cpython = platform.python_implementation() == 'CPython' and (hasattr(ctypes.pythonapi, 'Anaconda_GetVersion') or 'packaged by conda-forge' in sys.version)
54
+ if sys.version_info[:2] >= (3, 8) and not is_conda_cpython or sys.version_info[:2] >= (3, 10):
55
+ if os.path.isdir(libs_dir):
56
+ os.add_dll_directory(libs_dir)
57
+ else:
58
+ load_order_filepath = os.path.join(libs_dir, '.load-order-scipy-1.12.0')
59
+ if os.path.isfile(load_order_filepath):
60
+ with open(os.path.join(libs_dir, '.load-order-scipy-1.12.0')) as file:
61
+ load_order = file.read().split()
62
+ for lib in load_order:
63
+ lib_path = os.path.join(os.path.join(libs_dir, lib))
64
+ kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
65
+ if os.path.isfile(lib_path) and not kernel32.LoadLibraryExW(ctypes.c_wchar_p(lib_path), None, 0x00000008):
66
+ raise OSError('Error loading {}; {}'.format(lib, ctypes.FormatError(ctypes.get_last_error())))
67
+
68
+
69
+ _delvewheel_patch_1_5_2()
70
+ del _delvewheel_patch_1_5_2
71
+ # end delvewheel patch
72
+
73
+ import importlib as _importlib
74
+
75
+ from numpy import __version__ as __numpy_version__
76
+
77
+
78
+ try:
79
+ from scipy.__config__ import show as show_config
80
+ except ImportError as e:
81
+ msg = """Error importing SciPy: you cannot import SciPy while
82
+ being in scipy source directory; please exit the SciPy source
83
+ tree first and relaunch your Python interpreter."""
84
+ raise ImportError(msg) from e
85
+
86
+
87
+ from scipy.version import version as __version__
88
+
89
+
90
+ # Allow distributors to run custom init code
91
+ from . import _distributor_init
92
+ del _distributor_init
93
+
94
+
95
+ from scipy._lib import _pep440
96
+ # In maintenance branch, change to np_maxversion N+3 if numpy is at N
97
+ np_minversion = '1.22.4'
98
+ np_maxversion = '1.29.0'
99
+ if (_pep440.parse(__numpy_version__) < _pep440.Version(np_minversion) or
100
+ _pep440.parse(__numpy_version__) >= _pep440.Version(np_maxversion)):
101
+ import warnings
102
+ warnings.warn(f"A NumPy version >={np_minversion} and <{np_maxversion}"
103
+ f" is required for this version of SciPy (detected "
104
+ f"version {__numpy_version__})",
105
+ UserWarning, stacklevel=2)
106
+ del _pep440
107
+
108
+
109
+ # This is the first import of an extension module within SciPy. If there's
110
+ # a general issue with the install, such that extension modules are missing
111
+ # or cannot be imported, this is where we'll get a failure - so give an
112
+ # informative error message.
113
+ try:
114
+ from scipy._lib._ccallback import LowLevelCallable
115
+ except ImportError as e:
116
+ msg = "The `scipy` install you are using seems to be broken, " + \
117
+ "(extension modules cannot be imported), " + \
118
+ "please try reinstalling."
119
+ raise ImportError(msg) from e
120
+
121
+
122
+ from scipy._lib._testutils import PytestTester
123
+ test = PytestTester(__name__)
124
+ del PytestTester
125
+
126
+
127
+ submodules = [
128
+ 'cluster',
129
+ 'constants',
130
+ 'datasets',
131
+ 'fft',
132
+ 'fftpack',
133
+ 'integrate',
134
+ 'interpolate',
135
+ 'io',
136
+ 'linalg',
137
+ 'misc',
138
+ 'ndimage',
139
+ 'odr',
140
+ 'optimize',
141
+ 'signal',
142
+ 'sparse',
143
+ 'spatial',
144
+ 'special',
145
+ 'stats'
146
+ ]
147
+
148
+ __all__ = submodules + [
149
+ 'LowLevelCallable',
150
+ 'test',
151
+ 'show_config',
152
+ '__version__',
153
+ ]
154
+
155
+
156
+ def __dir__():
157
+ return __all__
158
+
159
+
160
+ def __getattr__(name):
161
+ if name in submodules:
162
+ return _importlib.import_module(f'scipy.{name}')
163
+ else:
164
+ try:
165
+ return globals()[name]
166
+ except KeyError:
167
+ raise AttributeError(
168
+ f"Module 'scipy' has no attribute '{name}'"
169
+ )
scipy/__pycache__/__config__.cpython-39.pyc ADDED
Binary file (3.63 kB). View file
 
scipy/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (4.75 kB). View file
 
scipy/__pycache__/_distributor_init.cpython-39.pyc ADDED
Binary file (930 Bytes). View file
 
scipy/__pycache__/conftest.cpython-39.pyc ADDED
Binary file (5.76 kB). View file
 
scipy/__pycache__/version.cpython-39.pyc ADDED
Binary file (299 Bytes). View file
 
scipy/_distributor_init.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ '''
3
+ Helper to preload windows dlls to prevent dll not found errors.
4
+ Once a DLL is preloaded, its namespace is made available to any
5
+ subsequent DLL. This file originated in the numpy-wheels repo,
6
+ and is created as part of the scripts that build the wheel.
7
+ '''
8
+ import os
9
+ import glob
10
+ if os.name == 'nt':
11
+ # convention for storing / loading the DLL from
12
+ # numpy/.libs/, if present
13
+ try:
14
+ from ctypes import WinDLL
15
+ basedir = os.path.dirname(__file__)
16
+ except:
17
+ pass
18
+ else:
19
+ libs_dir = os.path.abspath(os.path.join(basedir, '.libs'))
20
+ DLL_filenames = []
21
+ if os.path.isdir(libs_dir):
22
+ for filename in glob.glob(os.path.join(libs_dir,
23
+ '*openblas*dll')):
24
+ # NOTE: would it change behavior to load ALL
25
+ # DLLs at this path vs. the name restriction?
26
+ WinDLL(os.path.abspath(filename))
27
+ DLL_filenames.append(filename)
28
+ if len(DLL_filenames) > 1:
29
+ import warnings
30
+ warnings.warn("loaded more than 1 DLL from .libs:"
31
+ "\n%s" % "\n".join(DLL_filenames),
32
+ stacklevel=1)
scipy/_lib/__init__.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Module containing private utility functions
3
+ ===========================================
4
+
5
+ The ``scipy._lib`` namespace is empty (for now). Tests for all
6
+ utilities in submodules of ``_lib`` can be run with::
7
+
8
+ from scipy import _lib
9
+ _lib.test()
10
+
11
+ """
12
+ from scipy._lib._testutils import PytestTester
13
+ test = PytestTester(__name__)
14
+ del PytestTester
scipy/_lib/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (520 Bytes). View file
 
scipy/_lib/__pycache__/_array_api.cpython-39.pyc ADDED
Binary file (9.52 kB). View file
 
scipy/_lib/__pycache__/_bunch.cpython-39.pyc ADDED
Binary file (6.88 kB). View file
 
scipy/_lib/__pycache__/_ccallback.cpython-39.pyc ADDED
Binary file (7.03 kB). View file
 
scipy/_lib/__pycache__/_disjoint_set.cpython-39.pyc ADDED
Binary file (6.36 kB). View file
 
scipy/_lib/__pycache__/_docscrape.cpython-39.pyc ADDED
Binary file (18.9 kB). View file
 
scipy/_lib/__pycache__/_finite_differences.cpython-39.pyc ADDED
Binary file (4.04 kB). View file
 
scipy/_lib/__pycache__/_gcutils.cpython-39.pyc ADDED
Binary file (3.02 kB). View file
 
scipy/_lib/__pycache__/_pep440.cpython-39.pyc ADDED
Binary file (13.1 kB). View file
 
scipy/_lib/__pycache__/_testutils.cpython-39.pyc ADDED
Binary file (7.94 kB). View file
 
scipy/_lib/__pycache__/_threadsafety.cpython-39.pyc ADDED
Binary file (2.27 kB). View file
 
scipy/_lib/__pycache__/_tmpdirs.cpython-39.pyc ADDED
Binary file (2.72 kB). View file
 
scipy/_lib/__pycache__/_util.cpython-39.pyc ADDED
Binary file (26.4 kB). View file
 
scipy/_lib/__pycache__/decorator.cpython-39.pyc ADDED
Binary file (11.4 kB). View file
 
scipy/_lib/__pycache__/deprecation.cpython-39.pyc ADDED
Binary file (7.37 kB). View file
 
scipy/_lib/__pycache__/doccer.cpython-39.pyc ADDED
Binary file (7.78 kB). View file
 
scipy/_lib/__pycache__/uarray.cpython-39.pyc ADDED
Binary file (772 Bytes). View file
 
scipy/_lib/_array_api.py ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utility functions to use Python Array API compatible libraries.
2
+
3
+ For the context about the Array API see:
4
+ https://data-apis.org/array-api/latest/purpose_and_scope.html
5
+
6
+ The SciPy use case of the Array API is described on the following page:
7
+ https://data-apis.org/array-api/latest/use_cases.html#use-case-scipy
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import warnings
13
+
14
+ import numpy as np
15
+ from numpy.testing import assert_
16
+ import scipy._lib.array_api_compat.array_api_compat as array_api_compat
17
+ from scipy._lib.array_api_compat.array_api_compat import size
18
+ import scipy._lib.array_api_compat.array_api_compat.numpy as array_api_compat_numpy
19
+
20
+ __all__ = ['array_namespace', 'as_xparray', 'size']
21
+
22
+
23
+ # To enable array API and strict array-like input validation
24
+ SCIPY_ARRAY_API: str | bool = os.environ.get("SCIPY_ARRAY_API", False)
25
+ # To control the default device - for use in the test suite only
26
+ SCIPY_DEVICE = os.environ.get("SCIPY_DEVICE", "cpu")
27
+
28
+ _GLOBAL_CONFIG = {
29
+ "SCIPY_ARRAY_API": SCIPY_ARRAY_API,
30
+ "SCIPY_DEVICE": SCIPY_DEVICE,
31
+ }
32
+
33
+
34
+ def compliance_scipy(arrays):
35
+ """Raise exceptions on known-bad subclasses.
36
+
37
+ The following subclasses are not supported and raise and error:
38
+ - `numpy.ma.MaskedArray`
39
+ - `numpy.matrix`
40
+ - NumPy arrays which do not have a boolean or numerical dtype
41
+ - Any array-like which is neither array API compatible nor coercible by NumPy
42
+ - Any array-like which is coerced by NumPy to an unsupported dtype
43
+ """
44
+ for i in range(len(arrays)):
45
+ array = arrays[i]
46
+ if isinstance(array, np.ma.MaskedArray):
47
+ raise TypeError("Inputs of type `numpy.ma.MaskedArray` are not supported.")
48
+ elif isinstance(array, np.matrix):
49
+ raise TypeError("Inputs of type `numpy.matrix` are not supported.")
50
+ if isinstance(array, (np.ndarray, np.generic)):
51
+ dtype = array.dtype
52
+ if not (np.issubdtype(dtype, np.number) or np.issubdtype(dtype, np.bool_)):
53
+ raise TypeError(f"An argument has dtype `{dtype!r}`; "
54
+ f"only boolean and numerical dtypes are supported.")
55
+ elif not array_api_compat.is_array_api_obj(array):
56
+ try:
57
+ array = np.asanyarray(array)
58
+ except TypeError:
59
+ raise TypeError("An argument is neither array API compatible nor "
60
+ "coercible by NumPy.")
61
+ dtype = array.dtype
62
+ if not (np.issubdtype(dtype, np.number) or np.issubdtype(dtype, np.bool_)):
63
+ message = (
64
+ f"An argument was coerced to an unsupported dtype `{dtype!r}`; "
65
+ f"only boolean and numerical dtypes are supported."
66
+ )
67
+ raise TypeError(message)
68
+ arrays[i] = array
69
+ return arrays
70
+
71
+
72
+ def _check_finite(array, xp):
73
+ """Check for NaNs or Infs."""
74
+ msg = "array must not contain infs or NaNs"
75
+ try:
76
+ if not xp.all(xp.isfinite(array)):
77
+ raise ValueError(msg)
78
+ except TypeError:
79
+ raise ValueError(msg)
80
+
81
+
82
+ def array_namespace(*arrays):
83
+ """Get the array API compatible namespace for the arrays xs.
84
+
85
+ Parameters
86
+ ----------
87
+ *arrays : sequence of array_like
88
+ Arrays used to infer the common namespace.
89
+
90
+ Returns
91
+ -------
92
+ namespace : module
93
+ Common namespace.
94
+
95
+ Notes
96
+ -----
97
+ Thin wrapper around `array_api_compat.array_namespace`.
98
+
99
+ 1. Check for the global switch: SCIPY_ARRAY_API. This can also be accessed
100
+ dynamically through ``_GLOBAL_CONFIG['SCIPY_ARRAY_API']``.
101
+ 2. `compliance_scipy` raise exceptions on known-bad subclasses. See
102
+ it's definition for more details.
103
+
104
+ When the global switch is False, it defaults to the `numpy` namespace.
105
+ In that case, there is no compliance check. This is a convenience to
106
+ ease the adoption. Otherwise, arrays must comply with the new rules.
107
+ """
108
+ if not _GLOBAL_CONFIG["SCIPY_ARRAY_API"]:
109
+ # here we could wrap the namespace if needed
110
+ return array_api_compat_numpy
111
+
112
+ arrays = [array for array in arrays if array is not None]
113
+
114
+ arrays = compliance_scipy(arrays)
115
+
116
+ return array_api_compat.array_namespace(*arrays)
117
+
118
+
119
+ def as_xparray(
120
+ array, dtype=None, order=None, copy=None, *, xp=None, check_finite=False
121
+ ):
122
+ """SciPy-specific replacement for `np.asarray` with `order` and `check_finite`.
123
+
124
+ Memory layout parameter `order` is not exposed in the Array API standard.
125
+ `order` is only enforced if the input array implementation
126
+ is NumPy based, otherwise `order` is just silently ignored.
127
+
128
+ `check_finite` is also not a keyword in the array API standard; included
129
+ here for convenience rather than that having to be a separate function
130
+ call inside SciPy functions.
131
+ """
132
+ if xp is None:
133
+ xp = array_namespace(array)
134
+ if xp.__name__ in {"numpy", "scipy._lib.array_api_compat.array_api_compat.numpy"}:
135
+ # Use NumPy API to support order
136
+ if copy is True:
137
+ array = np.array(array, order=order, dtype=dtype)
138
+ else:
139
+ array = np.asarray(array, order=order, dtype=dtype)
140
+
141
+ # At this point array is a NumPy ndarray. We convert it to an array
142
+ # container that is consistent with the input's namespace.
143
+ array = xp.asarray(array)
144
+ else:
145
+ try:
146
+ array = xp.asarray(array, dtype=dtype, copy=copy)
147
+ except TypeError:
148
+ coerced_xp = array_namespace(xp.asarray(3))
149
+ array = coerced_xp.asarray(array, dtype=dtype, copy=copy)
150
+
151
+ if check_finite:
152
+ _check_finite(array, xp)
153
+
154
+ return array
155
+
156
+
157
+ def atleast_nd(x, *, ndim, xp=None):
158
+ """Recursively expand the dimension to have at least `ndim`."""
159
+ if xp is None:
160
+ xp = array_namespace(x)
161
+ x = xp.asarray(x)
162
+ if x.ndim < ndim:
163
+ x = xp.expand_dims(x, axis=0)
164
+ x = atleast_nd(x, ndim=ndim, xp=xp)
165
+ return x
166
+
167
+
168
+ def copy(x, *, xp=None):
169
+ """
170
+ Copies an array.
171
+
172
+ Parameters
173
+ ----------
174
+ x : array
175
+
176
+ xp : array_namespace
177
+
178
+ Returns
179
+ -------
180
+ copy : array
181
+ Copied array
182
+
183
+ Notes
184
+ -----
185
+ This copy function does not offer all the semantics of `np.copy`, i.e. the
186
+ `subok` and `order` keywords are not used.
187
+ """
188
+ # Note: xp.asarray fails if xp is numpy.
189
+ if xp is None:
190
+ xp = array_namespace(x)
191
+
192
+ return as_xparray(x, copy=True, xp=xp)
193
+
194
+
195
+ def is_numpy(xp):
196
+ return xp.__name__ == 'scipy._lib.array_api_compat.array_api_compat.numpy'
197
+
198
+
199
+ def is_cupy(xp):
200
+ return xp.__name__ == 'scipy._lib.array_api_compat.array_api_compat.cupy'
201
+
202
+
203
+ def is_torch(xp):
204
+ return xp.__name__ == 'scipy._lib.array_api_compat.array_api_compat.torch'
205
+
206
+
207
+ def _strict_check(actual, desired, xp,
208
+ check_namespace=True, check_dtype=True, check_shape=True):
209
+ if check_namespace:
210
+ _assert_matching_namespace(actual, desired)
211
+
212
+ desired = xp.asarray(desired)
213
+
214
+ if check_dtype:
215
+ assert_(actual.dtype == desired.dtype,
216
+ "dtypes do not match.\n"
217
+ f"Actual: {actual.dtype}\n"
218
+ f"Desired: {desired.dtype}")
219
+
220
+ if check_shape:
221
+ assert_(actual.shape == desired.shape,
222
+ "Shapes do not match.\n"
223
+ f"Actual: {actual.shape}\n"
224
+ f"Desired: {desired.shape}")
225
+ _check_scalar(actual, desired, xp)
226
+
227
+ desired = xp.broadcast_to(desired, actual.shape)
228
+ return desired
229
+
230
+
231
+ def _assert_matching_namespace(actual, desired):
232
+ actual = actual if isinstance(actual, tuple) else (actual,)
233
+ desired_space = array_namespace(desired)
234
+ for arr in actual:
235
+ arr_space = array_namespace(arr)
236
+ assert_(arr_space == desired_space,
237
+ "Namespaces do not match.\n"
238
+ f"Actual: {arr_space.__name__}\n"
239
+ f"Desired: {desired_space.__name__}")
240
+
241
+
242
+ def _check_scalar(actual, desired, xp):
243
+ # Shape check alone is sufficient unless desired.shape == (). Also,
244
+ # only NumPy distinguishes between scalars and arrays.
245
+ if desired.shape != () or not is_numpy(xp):
246
+ return
247
+ # We want to follow the conventions of the `xp` library. Libraries like
248
+ # NumPy, for which `np.asarray(0)[()]` returns a scalar, tend to return
249
+ # a scalar even when a 0D array might be more appropriate:
250
+ # import numpy as np
251
+ # np.mean([1, 2, 3]) # scalar, not 0d array
252
+ # np.asarray(0)*2 # scalar, not 0d array
253
+ # np.sin(np.asarray(0)) # scalar, not 0d array
254
+ # Libraries like CuPy, for which `cp.asarray(0)[()]` returns a 0D array,
255
+ # tend to return a 0D array in scenarios like those above.
256
+ # Therefore, regardless of whether the developer provides a scalar or 0D
257
+ # array for `desired`, we would typically want the type of `actual` to be
258
+ # the type of `desired[()]`. If the developer wants to override this
259
+ # behavior, they can set `check_shape=False`.
260
+ desired = desired[()]
261
+ assert_((xp.isscalar(actual) and xp.isscalar(desired)
262
+ or (not xp.isscalar(actual) and not xp.isscalar(desired))),
263
+ "Types do not match:\n"
264
+ f"Actual: {type(actual)}\n"
265
+ f"Desired: {type(desired)}")
266
+
267
+
268
+ def xp_assert_equal(actual, desired, check_namespace=True, check_dtype=True,
269
+ check_shape=True, err_msg='', xp=None):
270
+ if xp is None:
271
+ xp = array_namespace(actual)
272
+ desired = _strict_check(actual, desired, xp, check_namespace=check_namespace,
273
+ check_dtype=check_dtype, check_shape=check_shape)
274
+ if is_cupy(xp):
275
+ return xp.testing.assert_array_equal(actual, desired, err_msg=err_msg)
276
+ elif is_torch(xp):
277
+ # PyTorch recommends using `rtol=0, atol=0` like this
278
+ # to test for exact equality
279
+ err_msg = None if err_msg == '' else err_msg
280
+ return xp.testing.assert_close(actual, desired, rtol=0, atol=0, equal_nan=True,
281
+ check_dtype=False, msg=err_msg)
282
+ return np.testing.assert_array_equal(actual, desired, err_msg=err_msg)
283
+
284
+
285
+ def xp_assert_close(actual, desired, rtol=1e-07, atol=0, check_namespace=True,
286
+ check_dtype=True, check_shape=True, err_msg='', xp=None):
287
+ if xp is None:
288
+ xp = array_namespace(actual)
289
+ desired = _strict_check(actual, desired, xp, check_namespace=check_namespace,
290
+ check_dtype=check_dtype, check_shape=check_shape)
291
+ if is_cupy(xp):
292
+ return xp.testing.assert_allclose(actual, desired, rtol=rtol,
293
+ atol=atol, err_msg=err_msg)
294
+ elif is_torch(xp):
295
+ err_msg = None if err_msg == '' else err_msg
296
+ return xp.testing.assert_close(actual, desired, rtol=rtol, atol=atol,
297
+ equal_nan=True, check_dtype=False, msg=err_msg)
298
+ return np.testing.assert_allclose(actual, desired, rtol=rtol,
299
+ atol=atol, err_msg=err_msg)
300
+
301
+
302
+ def xp_assert_less(actual, desired, check_namespace=True, check_dtype=True,
303
+ check_shape=True, err_msg='', verbose=True, xp=None):
304
+ if xp is None:
305
+ xp = array_namespace(actual)
306
+ desired = _strict_check(actual, desired, xp, check_namespace=check_namespace,
307
+ check_dtype=check_dtype, check_shape=check_shape)
308
+ if is_cupy(xp):
309
+ return xp.testing.assert_array_less(actual, desired,
310
+ err_msg=err_msg, verbose=verbose)
311
+ elif is_torch(xp):
312
+ if actual.device.type != 'cpu':
313
+ actual = actual.cpu()
314
+ if desired.device.type != 'cpu':
315
+ desired = desired.cpu()
316
+ return np.testing.assert_array_less(actual, desired,
317
+ err_msg=err_msg, verbose=verbose)
318
+
319
+
320
+ def cov(x, *, xp=None):
321
+ if xp is None:
322
+ xp = array_namespace(x)
323
+
324
+ X = copy(x, xp=xp)
325
+ dtype = xp.result_type(X, xp.float64)
326
+
327
+ X = atleast_nd(X, ndim=2, xp=xp)
328
+ X = xp.asarray(X, dtype=dtype)
329
+
330
+ avg = xp.mean(X, axis=1)
331
+ fact = X.shape[1] - 1
332
+
333
+ if fact <= 0:
334
+ warnings.warn("Degrees of freedom <= 0 for slice",
335
+ RuntimeWarning, stacklevel=2)
336
+ fact = 0.0
337
+
338
+ X -= avg[:, None]
339
+ X_T = X.T
340
+ if xp.isdtype(X_T.dtype, 'complex floating'):
341
+ X_T = xp.conj(X_T)
342
+ c = X @ X_T
343
+ c /= fact
344
+ axes = tuple(axis for axis, length in enumerate(c.shape) if length == 1)
345
+ return xp.squeeze(c, axis=axes)
346
+
347
+
348
+ def xp_unsupported_param_msg(param):
349
+ return f'Providing {param!r} is only supported for numpy arrays.'
350
+
351
+
352
+ def is_complex(x, xp):
353
+ return xp.isdtype(x.dtype, 'complex floating')
scipy/_lib/_bunch.py ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys as _sys
2
+ from keyword import iskeyword as _iskeyword
3
+
4
+
5
+ def _validate_names(typename, field_names, extra_field_names):
6
+ """
7
+ Ensure that all the given names are valid Python identifiers that
8
+ do not start with '_'. Also check that there are no duplicates
9
+ among field_names + extra_field_names.
10
+ """
11
+ for name in [typename] + field_names + extra_field_names:
12
+ if not isinstance(name, str):
13
+ raise TypeError('typename and all field names must be strings')
14
+ if not name.isidentifier():
15
+ raise ValueError('typename and all field names must be valid '
16
+ f'identifiers: {name!r}')
17
+ if _iskeyword(name):
18
+ raise ValueError('typename and all field names cannot be a '
19
+ f'keyword: {name!r}')
20
+
21
+ seen = set()
22
+ for name in field_names + extra_field_names:
23
+ if name.startswith('_'):
24
+ raise ValueError('Field names cannot start with an underscore: '
25
+ f'{name!r}')
26
+ if name in seen:
27
+ raise ValueError(f'Duplicate field name: {name!r}')
28
+ seen.add(name)
29
+
30
+
31
+ # Note: This code is adapted from CPython:Lib/collections/__init__.py
32
+ def _make_tuple_bunch(typename, field_names, extra_field_names=None,
33
+ module=None):
34
+ """
35
+ Create a namedtuple-like class with additional attributes.
36
+
37
+ This function creates a subclass of tuple that acts like a namedtuple
38
+ and that has additional attributes.
39
+
40
+ The additional attributes are listed in `extra_field_names`. The
41
+ values assigned to these attributes are not part of the tuple.
42
+
43
+ The reason this function exists is to allow functions in SciPy
44
+ that currently return a tuple or a namedtuple to returned objects
45
+ that have additional attributes, while maintaining backwards
46
+ compatibility.
47
+
48
+ This should only be used to enhance *existing* functions in SciPy.
49
+ New functions are free to create objects as return values without
50
+ having to maintain backwards compatibility with an old tuple or
51
+ namedtuple return value.
52
+
53
+ Parameters
54
+ ----------
55
+ typename : str
56
+ The name of the type.
57
+ field_names : list of str
58
+ List of names of the values to be stored in the tuple. These names
59
+ will also be attributes of instances, so the values in the tuple
60
+ can be accessed by indexing or as attributes. At least one name
61
+ is required. See the Notes for additional restrictions.
62
+ extra_field_names : list of str, optional
63
+ List of names of values that will be stored as attributes of the
64
+ object. See the notes for additional restrictions.
65
+
66
+ Returns
67
+ -------
68
+ cls : type
69
+ The new class.
70
+
71
+ Notes
72
+ -----
73
+ There are restrictions on the names that may be used in `field_names`
74
+ and `extra_field_names`:
75
+
76
+ * The names must be unique--no duplicates allowed.
77
+ * The names must be valid Python identifiers, and must not begin with
78
+ an underscore.
79
+ * The names must not be Python keywords (e.g. 'def', 'and', etc., are
80
+ not allowed).
81
+
82
+ Examples
83
+ --------
84
+ >>> from scipy._lib._bunch import _make_tuple_bunch
85
+
86
+ Create a class that acts like a namedtuple with length 2 (with field
87
+ names `x` and `y`) that will also have the attributes `w` and `beta`:
88
+
89
+ >>> Result = _make_tuple_bunch('Result', ['x', 'y'], ['w', 'beta'])
90
+
91
+ `Result` is the new class. We call it with keyword arguments to create
92
+ a new instance with given values.
93
+
94
+ >>> result1 = Result(x=1, y=2, w=99, beta=0.5)
95
+ >>> result1
96
+ Result(x=1, y=2, w=99, beta=0.5)
97
+
98
+ `result1` acts like a tuple of length 2:
99
+
100
+ >>> len(result1)
101
+ 2
102
+ >>> result1[:]
103
+ (1, 2)
104
+
105
+ The values assigned when the instance was created are available as
106
+ attributes:
107
+
108
+ >>> result1.y
109
+ 2
110
+ >>> result1.beta
111
+ 0.5
112
+ """
113
+ if len(field_names) == 0:
114
+ raise ValueError('field_names must contain at least one name')
115
+
116
+ if extra_field_names is None:
117
+ extra_field_names = []
118
+ _validate_names(typename, field_names, extra_field_names)
119
+
120
+ typename = _sys.intern(str(typename))
121
+ field_names = tuple(map(_sys.intern, field_names))
122
+ extra_field_names = tuple(map(_sys.intern, extra_field_names))
123
+
124
+ all_names = field_names + extra_field_names
125
+ arg_list = ', '.join(field_names)
126
+ full_list = ', '.join(all_names)
127
+ repr_fmt = ''.join(('(',
128
+ ', '.join(f'{name}=%({name})r' for name in all_names),
129
+ ')'))
130
+ tuple_new = tuple.__new__
131
+ _dict, _tuple, _zip = dict, tuple, zip
132
+
133
+ # Create all the named tuple methods to be added to the class namespace
134
+
135
+ s = f"""\
136
+ def __new__(_cls, {arg_list}, **extra_fields):
137
+ return _tuple_new(_cls, ({arg_list},))
138
+
139
+ def __init__(self, {arg_list}, **extra_fields):
140
+ for key in self._extra_fields:
141
+ if key not in extra_fields:
142
+ raise TypeError("missing keyword argument '%s'" % (key,))
143
+ for key, val in extra_fields.items():
144
+ if key not in self._extra_fields:
145
+ raise TypeError("unexpected keyword argument '%s'" % (key,))
146
+ self.__dict__[key] = val
147
+
148
+ def __setattr__(self, key, val):
149
+ if key in {repr(field_names)}:
150
+ raise AttributeError("can't set attribute %r of class %r"
151
+ % (key, self.__class__.__name__))
152
+ else:
153
+ self.__dict__[key] = val
154
+ """
155
+ del arg_list
156
+ namespace = {'_tuple_new': tuple_new,
157
+ '__builtins__': dict(TypeError=TypeError,
158
+ AttributeError=AttributeError),
159
+ '__name__': f'namedtuple_{typename}'}
160
+ exec(s, namespace)
161
+ __new__ = namespace['__new__']
162
+ __new__.__doc__ = f'Create new instance of {typename}({full_list})'
163
+ __init__ = namespace['__init__']
164
+ __init__.__doc__ = f'Instantiate instance of {typename}({full_list})'
165
+ __setattr__ = namespace['__setattr__']
166
+
167
+ def __repr__(self):
168
+ 'Return a nicely formatted representation string'
169
+ return self.__class__.__name__ + repr_fmt % self._asdict()
170
+
171
+ def _asdict(self):
172
+ 'Return a new dict which maps field names to their values.'
173
+ out = _dict(_zip(self._fields, self))
174
+ out.update(self.__dict__)
175
+ return out
176
+
177
+ def __getnewargs_ex__(self):
178
+ 'Return self as a plain tuple. Used by copy and pickle.'
179
+ return _tuple(self), self.__dict__
180
+
181
+ # Modify function metadata to help with introspection and debugging
182
+ for method in (__new__, __repr__, _asdict, __getnewargs_ex__):
183
+ method.__qualname__ = f'{typename}.{method.__name__}'
184
+
185
+ # Build-up the class namespace dictionary
186
+ # and use type() to build the result class
187
+ class_namespace = {
188
+ '__doc__': f'{typename}({full_list})',
189
+ '_fields': field_names,
190
+ '__new__': __new__,
191
+ '__init__': __init__,
192
+ '__repr__': __repr__,
193
+ '__setattr__': __setattr__,
194
+ '_asdict': _asdict,
195
+ '_extra_fields': extra_field_names,
196
+ '__getnewargs_ex__': __getnewargs_ex__,
197
+ }
198
+ for index, name in enumerate(field_names):
199
+
200
+ def _get(self, index=index):
201
+ return self[index]
202
+ class_namespace[name] = property(_get)
203
+ for name in extra_field_names:
204
+
205
+ def _get(self, name=name):
206
+ return self.__dict__[name]
207
+ class_namespace[name] = property(_get)
208
+
209
+ result = type(typename, (tuple,), class_namespace)
210
+
211
+ # For pickling to work, the __module__ variable needs to be set to the
212
+ # frame where the named tuple is created. Bypass this step in environments
213
+ # where sys._getframe is not defined (Jython for example) or sys._getframe
214
+ # is not defined for arguments greater than 0 (IronPython), or where the
215
+ # user has specified a particular module.
216
+ if module is None:
217
+ try:
218
+ module = _sys._getframe(1).f_globals.get('__name__', '__main__')
219
+ except (AttributeError, ValueError):
220
+ pass
221
+ if module is not None:
222
+ result.__module__ = module
223
+ __new__.__module__ = module
224
+
225
+ return result
scipy/_lib/_ccallback.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from . import _ccallback_c
2
+
3
+ import ctypes
4
+
5
+ PyCFuncPtr = ctypes.CFUNCTYPE(ctypes.c_void_p).__bases__[0]
6
+
7
+ ffi = None
8
+
9
+ class CData:
10
+ pass
11
+
12
+ def _import_cffi():
13
+ global ffi, CData
14
+
15
+ if ffi is not None:
16
+ return
17
+
18
+ try:
19
+ import cffi
20
+ ffi = cffi.FFI()
21
+ CData = ffi.CData
22
+ except ImportError:
23
+ ffi = False
24
+
25
+
26
+ class LowLevelCallable(tuple):
27
+ """
28
+ Low-level callback function.
29
+
30
+ Some functions in SciPy take as arguments callback functions, which
31
+ can either be python callables or low-level compiled functions. Using
32
+ compiled callback functions can improve performance somewhat by
33
+ avoiding wrapping data in Python objects.
34
+
35
+ Such low-level functions in SciPy are wrapped in `LowLevelCallable`
36
+ objects, which can be constructed from function pointers obtained from
37
+ ctypes, cffi, Cython, or contained in Python `PyCapsule` objects.
38
+
39
+ .. seealso::
40
+
41
+ Functions accepting low-level callables:
42
+
43
+ `scipy.integrate.quad`, `scipy.ndimage.generic_filter`,
44
+ `scipy.ndimage.generic_filter1d`, `scipy.ndimage.geometric_transform`
45
+
46
+ Usage examples:
47
+
48
+ :ref:`ndimage-ccallbacks`, :ref:`quad-callbacks`
49
+
50
+ Parameters
51
+ ----------
52
+ function : {PyCapsule, ctypes function pointer, cffi function pointer}
53
+ Low-level callback function.
54
+ user_data : {PyCapsule, ctypes void pointer, cffi void pointer}
55
+ User data to pass on to the callback function.
56
+ signature : str, optional
57
+ Signature of the function. If omitted, determined from *function*,
58
+ if possible.
59
+
60
+ Attributes
61
+ ----------
62
+ function
63
+ Callback function given.
64
+ user_data
65
+ User data given.
66
+ signature
67
+ Signature of the function.
68
+
69
+ Methods
70
+ -------
71
+ from_cython
72
+ Class method for constructing callables from Cython C-exported
73
+ functions.
74
+
75
+ Notes
76
+ -----
77
+ The argument ``function`` can be one of:
78
+
79
+ - PyCapsule, whose name contains the C function signature
80
+ - ctypes function pointer
81
+ - cffi function pointer
82
+
83
+ The signature of the low-level callback must match one of those expected
84
+ by the routine it is passed to.
85
+
86
+ If constructing low-level functions from a PyCapsule, the name of the
87
+ capsule must be the corresponding signature, in the format::
88
+
89
+ return_type (arg1_type, arg2_type, ...)
90
+
91
+ For example::
92
+
93
+ "void (double)"
94
+ "double (double, int *, void *)"
95
+
96
+ The context of a PyCapsule passed in as ``function`` is used as ``user_data``,
97
+ if an explicit value for ``user_data`` was not given.
98
+
99
+ """
100
+
101
+ # Make the class immutable
102
+ __slots__ = ()
103
+
104
+ def __new__(cls, function, user_data=None, signature=None):
105
+ # We need to hold a reference to the function & user data,
106
+ # to prevent them going out of scope
107
+ item = cls._parse_callback(function, user_data, signature)
108
+ return tuple.__new__(cls, (item, function, user_data))
109
+
110
+ def __repr__(self):
111
+ return f"LowLevelCallable({self.function!r}, {self.user_data!r})"
112
+
113
+ @property
114
+ def function(self):
115
+ return tuple.__getitem__(self, 1)
116
+
117
+ @property
118
+ def user_data(self):
119
+ return tuple.__getitem__(self, 2)
120
+
121
+ @property
122
+ def signature(self):
123
+ return _ccallback_c.get_capsule_signature(tuple.__getitem__(self, 0))
124
+
125
+ def __getitem__(self, idx):
126
+ raise ValueError()
127
+
128
+ @classmethod
129
+ def from_cython(cls, module, name, user_data=None, signature=None):
130
+ """
131
+ Create a low-level callback function from an exported Cython function.
132
+
133
+ Parameters
134
+ ----------
135
+ module : module
136
+ Cython module where the exported function resides
137
+ name : str
138
+ Name of the exported function
139
+ user_data : {PyCapsule, ctypes void pointer, cffi void pointer}, optional
140
+ User data to pass on to the callback function.
141
+ signature : str, optional
142
+ Signature of the function. If omitted, determined from *function*.
143
+
144
+ """
145
+ try:
146
+ function = module.__pyx_capi__[name]
147
+ except AttributeError as e:
148
+ message = "Given module is not a Cython module with __pyx_capi__ attribute"
149
+ raise ValueError(message) from e
150
+ except KeyError as e:
151
+ message = f"No function {name!r} found in __pyx_capi__ of the module"
152
+ raise ValueError(message) from e
153
+ return cls(function, user_data, signature)
154
+
155
+ @classmethod
156
+ def _parse_callback(cls, obj, user_data=None, signature=None):
157
+ _import_cffi()
158
+
159
+ if isinstance(obj, LowLevelCallable):
160
+ func = tuple.__getitem__(obj, 0)
161
+ elif isinstance(obj, PyCFuncPtr):
162
+ func, signature = _get_ctypes_func(obj, signature)
163
+ elif isinstance(obj, CData):
164
+ func, signature = _get_cffi_func(obj, signature)
165
+ elif _ccallback_c.check_capsule(obj):
166
+ func = obj
167
+ else:
168
+ raise ValueError("Given input is not a callable or a "
169
+ "low-level callable (pycapsule/ctypes/cffi)")
170
+
171
+ if isinstance(user_data, ctypes.c_void_p):
172
+ context = _get_ctypes_data(user_data)
173
+ elif isinstance(user_data, CData):
174
+ context = _get_cffi_data(user_data)
175
+ elif user_data is None:
176
+ context = 0
177
+ elif _ccallback_c.check_capsule(user_data):
178
+ context = user_data
179
+ else:
180
+ raise ValueError("Given user data is not a valid "
181
+ "low-level void* pointer (pycapsule/ctypes/cffi)")
182
+
183
+ return _ccallback_c.get_raw_capsule(func, signature, context)
184
+
185
+
186
+ #
187
+ # ctypes helpers
188
+ #
189
+
190
+ def _get_ctypes_func(func, signature=None):
191
+ # Get function pointer
192
+ func_ptr = ctypes.cast(func, ctypes.c_void_p).value
193
+
194
+ # Construct function signature
195
+ if signature is None:
196
+ signature = _typename_from_ctypes(func.restype) + " ("
197
+ for j, arg in enumerate(func.argtypes):
198
+ if j == 0:
199
+ signature += _typename_from_ctypes(arg)
200
+ else:
201
+ signature += ", " + _typename_from_ctypes(arg)
202
+ signature += ")"
203
+
204
+ return func_ptr, signature
205
+
206
+
207
+ def _typename_from_ctypes(item):
208
+ if item is None:
209
+ return "void"
210
+ elif item is ctypes.c_void_p:
211
+ return "void *"
212
+
213
+ name = item.__name__
214
+
215
+ pointer_level = 0
216
+ while name.startswith("LP_"):
217
+ pointer_level += 1
218
+ name = name[3:]
219
+
220
+ if name.startswith('c_'):
221
+ name = name[2:]
222
+
223
+ if pointer_level > 0:
224
+ name += " " + "*"*pointer_level
225
+
226
+ return name
227
+
228
+
229
+ def _get_ctypes_data(data):
230
+ # Get voidp pointer
231
+ return ctypes.cast(data, ctypes.c_void_p).value
232
+
233
+
234
+ #
235
+ # CFFI helpers
236
+ #
237
+
238
+ def _get_cffi_func(func, signature=None):
239
+ # Get function pointer
240
+ func_ptr = ffi.cast('uintptr_t', func)
241
+
242
+ # Get signature
243
+ if signature is None:
244
+ signature = ffi.getctype(ffi.typeof(func)).replace('(*)', ' ')
245
+
246
+ return func_ptr, signature
247
+
248
+
249
+ def _get_cffi_data(data):
250
+ # Get pointer
251
+ return ffi.cast('uintptr_t', data)
scipy/_lib/_ccallback_c.cp39-win_amd64.dll.a ADDED
Binary file (1.6 kB). View file
 
scipy/_lib/_ccallback_c.cp39-win_amd64.pyd ADDED
Binary file (86.5 kB). View file
 
scipy/_lib/_disjoint_set.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Disjoint set data structure
3
+ """
4
+
5
+
6
+ class DisjointSet:
7
+ """ Disjoint set data structure for incremental connectivity queries.
8
+
9
+ .. versionadded:: 1.6.0
10
+
11
+ Attributes
12
+ ----------
13
+ n_subsets : int
14
+ The number of subsets.
15
+
16
+ Methods
17
+ -------
18
+ add
19
+ merge
20
+ connected
21
+ subset
22
+ subset_size
23
+ subsets
24
+ __getitem__
25
+
26
+ Notes
27
+ -----
28
+ This class implements the disjoint set [1]_, also known as the *union-find*
29
+ or *merge-find* data structure. The *find* operation (implemented in
30
+ `__getitem__`) implements the *path halving* variant. The *merge* method
31
+ implements the *merge by size* variant.
32
+
33
+ References
34
+ ----------
35
+ .. [1] https://en.wikipedia.org/wiki/Disjoint-set_data_structure
36
+
37
+ Examples
38
+ --------
39
+ >>> from scipy.cluster.hierarchy import DisjointSet
40
+
41
+ Initialize a disjoint set:
42
+
43
+ >>> disjoint_set = DisjointSet([1, 2, 3, 'a', 'b'])
44
+
45
+ Merge some subsets:
46
+
47
+ >>> disjoint_set.merge(1, 2)
48
+ True
49
+ >>> disjoint_set.merge(3, 'a')
50
+ True
51
+ >>> disjoint_set.merge('a', 'b')
52
+ True
53
+ >>> disjoint_set.merge('b', 'b')
54
+ False
55
+
56
+ Find root elements:
57
+
58
+ >>> disjoint_set[2]
59
+ 1
60
+ >>> disjoint_set['b']
61
+ 3
62
+
63
+ Test connectivity:
64
+
65
+ >>> disjoint_set.connected(1, 2)
66
+ True
67
+ >>> disjoint_set.connected(1, 'b')
68
+ False
69
+
70
+ List elements in disjoint set:
71
+
72
+ >>> list(disjoint_set)
73
+ [1, 2, 3, 'a', 'b']
74
+
75
+ Get the subset containing 'a':
76
+
77
+ >>> disjoint_set.subset('a')
78
+ {'a', 3, 'b'}
79
+
80
+ Get the size of the subset containing 'a' (without actually instantiating
81
+ the subset):
82
+
83
+ >>> disjoint_set.subset_size('a')
84
+ 3
85
+
86
+ Get all subsets in the disjoint set:
87
+
88
+ >>> disjoint_set.subsets()
89
+ [{1, 2}, {'a', 3, 'b'}]
90
+ """
91
+ def __init__(self, elements=None):
92
+ self.n_subsets = 0
93
+ self._sizes = {}
94
+ self._parents = {}
95
+ # _nbrs is a circular linked list which links connected elements.
96
+ self._nbrs = {}
97
+ # _indices tracks the element insertion order in `__iter__`.
98
+ self._indices = {}
99
+ if elements is not None:
100
+ for x in elements:
101
+ self.add(x)
102
+
103
+ def __iter__(self):
104
+ """Returns an iterator of the elements in the disjoint set.
105
+
106
+ Elements are ordered by insertion order.
107
+ """
108
+ return iter(self._indices)
109
+
110
+ def __len__(self):
111
+ return len(self._indices)
112
+
113
+ def __contains__(self, x):
114
+ return x in self._indices
115
+
116
+ def __getitem__(self, x):
117
+ """Find the root element of `x`.
118
+
119
+ Parameters
120
+ ----------
121
+ x : hashable object
122
+ Input element.
123
+
124
+ Returns
125
+ -------
126
+ root : hashable object
127
+ Root element of `x`.
128
+ """
129
+ if x not in self._indices:
130
+ raise KeyError(x)
131
+
132
+ # find by "path halving"
133
+ parents = self._parents
134
+ while self._indices[x] != self._indices[parents[x]]:
135
+ parents[x] = parents[parents[x]]
136
+ x = parents[x]
137
+ return x
138
+
139
+ def add(self, x):
140
+ """Add element `x` to disjoint set
141
+ """
142
+ if x in self._indices:
143
+ return
144
+
145
+ self._sizes[x] = 1
146
+ self._parents[x] = x
147
+ self._nbrs[x] = x
148
+ self._indices[x] = len(self._indices)
149
+ self.n_subsets += 1
150
+
151
+ def merge(self, x, y):
152
+ """Merge the subsets of `x` and `y`.
153
+
154
+ The smaller subset (the child) is merged into the larger subset (the
155
+ parent). If the subsets are of equal size, the root element which was
156
+ first inserted into the disjoint set is selected as the parent.
157
+
158
+ Parameters
159
+ ----------
160
+ x, y : hashable object
161
+ Elements to merge.
162
+
163
+ Returns
164
+ -------
165
+ merged : bool
166
+ True if `x` and `y` were in disjoint sets, False otherwise.
167
+ """
168
+ xr = self[x]
169
+ yr = self[y]
170
+ if self._indices[xr] == self._indices[yr]:
171
+ return False
172
+
173
+ sizes = self._sizes
174
+ if (sizes[xr], self._indices[yr]) < (sizes[yr], self._indices[xr]):
175
+ xr, yr = yr, xr
176
+ self._parents[yr] = xr
177
+ self._sizes[xr] += self._sizes[yr]
178
+ self._nbrs[xr], self._nbrs[yr] = self._nbrs[yr], self._nbrs[xr]
179
+ self.n_subsets -= 1
180
+ return True
181
+
182
+ def connected(self, x, y):
183
+ """Test whether `x` and `y` are in the same subset.
184
+
185
+ Parameters
186
+ ----------
187
+ x, y : hashable object
188
+ Elements to test.
189
+
190
+ Returns
191
+ -------
192
+ result : bool
193
+ True if `x` and `y` are in the same set, False otherwise.
194
+ """
195
+ return self._indices[self[x]] == self._indices[self[y]]
196
+
197
+ def subset(self, x):
198
+ """Get the subset containing `x`.
199
+
200
+ Parameters
201
+ ----------
202
+ x : hashable object
203
+ Input element.
204
+
205
+ Returns
206
+ -------
207
+ result : set
208
+ Subset containing `x`.
209
+ """
210
+ if x not in self._indices:
211
+ raise KeyError(x)
212
+
213
+ result = [x]
214
+ nxt = self._nbrs[x]
215
+ while self._indices[nxt] != self._indices[x]:
216
+ result.append(nxt)
217
+ nxt = self._nbrs[nxt]
218
+ return set(result)
219
+
220
+ def subset_size(self, x):
221
+ """Get the size of the subset containing `x`.
222
+
223
+ Note that this method is faster than ``len(self.subset(x))`` because
224
+ the size is directly read off an internal field, without the need to
225
+ instantiate the full subset.
226
+
227
+ Parameters
228
+ ----------
229
+ x : hashable object
230
+ Input element.
231
+
232
+ Returns
233
+ -------
234
+ result : int
235
+ Size of the subset containing `x`.
236
+ """
237
+ return self._sizes[self[x]]
238
+
239
+ def subsets(self):
240
+ """Get all the subsets in the disjoint set.
241
+
242
+ Returns
243
+ -------
244
+ result : list
245
+ Subsets in the disjoint set.
246
+ """
247
+ result = []
248
+ visited = set()
249
+ for x in self:
250
+ if x not in visited:
251
+ xset = self.subset(x)
252
+ visited.update(xset)
253
+ result.append(xset)
254
+ return result
scipy/_lib/_docscrape.py ADDED
@@ -0,0 +1,679 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Extract reference documentation from the NumPy source tree.
2
+
3
+ """
4
+ # copied from numpydoc/docscrape.py
5
+ import inspect
6
+ import textwrap
7
+ import re
8
+ import pydoc
9
+ from warnings import warn
10
+ from collections import namedtuple
11
+ from collections.abc import Callable, Mapping
12
+ import copy
13
+ import sys
14
+
15
+
16
+ def strip_blank_lines(l):
17
+ "Remove leading and trailing blank lines from a list of lines"
18
+ while l and not l[0].strip():
19
+ del l[0]
20
+ while l and not l[-1].strip():
21
+ del l[-1]
22
+ return l
23
+
24
+
25
+ class Reader:
26
+ """A line-based string reader.
27
+
28
+ """
29
+ def __init__(self, data):
30
+ """
31
+ Parameters
32
+ ----------
33
+ data : str
34
+ String with lines separated by '\\n'.
35
+
36
+ """
37
+ if isinstance(data, list):
38
+ self._str = data
39
+ else:
40
+ self._str = data.split('\n') # store string as list of lines
41
+
42
+ self.reset()
43
+
44
+ def __getitem__(self, n):
45
+ return self._str[n]
46
+
47
+ def reset(self):
48
+ self._l = 0 # current line nr
49
+
50
+ def read(self):
51
+ if not self.eof():
52
+ out = self[self._l]
53
+ self._l += 1
54
+ return out
55
+ else:
56
+ return ''
57
+
58
+ def seek_next_non_empty_line(self):
59
+ for l in self[self._l:]:
60
+ if l.strip():
61
+ break
62
+ else:
63
+ self._l += 1
64
+
65
+ def eof(self):
66
+ return self._l >= len(self._str)
67
+
68
+ def read_to_condition(self, condition_func):
69
+ start = self._l
70
+ for line in self[start:]:
71
+ if condition_func(line):
72
+ return self[start:self._l]
73
+ self._l += 1
74
+ if self.eof():
75
+ return self[start:self._l+1]
76
+ return []
77
+
78
+ def read_to_next_empty_line(self):
79
+ self.seek_next_non_empty_line()
80
+
81
+ def is_empty(line):
82
+ return not line.strip()
83
+
84
+ return self.read_to_condition(is_empty)
85
+
86
+ def read_to_next_unindented_line(self):
87
+ def is_unindented(line):
88
+ return (line.strip() and (len(line.lstrip()) == len(line)))
89
+ return self.read_to_condition(is_unindented)
90
+
91
+ def peek(self, n=0):
92
+ if self._l + n < len(self._str):
93
+ return self[self._l + n]
94
+ else:
95
+ return ''
96
+
97
+ def is_empty(self):
98
+ return not ''.join(self._str).strip()
99
+
100
+
101
+ class ParseError(Exception):
102
+ def __str__(self):
103
+ message = self.args[0]
104
+ if hasattr(self, 'docstring'):
105
+ message = f"{message} in {self.docstring!r}"
106
+ return message
107
+
108
+
109
+ Parameter = namedtuple('Parameter', ['name', 'type', 'desc'])
110
+
111
+
112
+ class NumpyDocString(Mapping):
113
+ """Parses a numpydoc string to an abstract representation
114
+
115
+ Instances define a mapping from section title to structured data.
116
+
117
+ """
118
+
119
+ sections = {
120
+ 'Signature': '',
121
+ 'Summary': [''],
122
+ 'Extended Summary': [],
123
+ 'Parameters': [],
124
+ 'Returns': [],
125
+ 'Yields': [],
126
+ 'Receives': [],
127
+ 'Raises': [],
128
+ 'Warns': [],
129
+ 'Other Parameters': [],
130
+ 'Attributes': [],
131
+ 'Methods': [],
132
+ 'See Also': [],
133
+ 'Notes': [],
134
+ 'Warnings': [],
135
+ 'References': '',
136
+ 'Examples': '',
137
+ 'index': {}
138
+ }
139
+
140
+ def __init__(self, docstring, config={}):
141
+ orig_docstring = docstring
142
+ docstring = textwrap.dedent(docstring).split('\n')
143
+
144
+ self._doc = Reader(docstring)
145
+ self._parsed_data = copy.deepcopy(self.sections)
146
+
147
+ try:
148
+ self._parse()
149
+ except ParseError as e:
150
+ e.docstring = orig_docstring
151
+ raise
152
+
153
+ def __getitem__(self, key):
154
+ return self._parsed_data[key]
155
+
156
+ def __setitem__(self, key, val):
157
+ if key not in self._parsed_data:
158
+ self._error_location("Unknown section %s" % key, error=False)
159
+ else:
160
+ self._parsed_data[key] = val
161
+
162
+ def __iter__(self):
163
+ return iter(self._parsed_data)
164
+
165
+ def __len__(self):
166
+ return len(self._parsed_data)
167
+
168
+ def _is_at_section(self):
169
+ self._doc.seek_next_non_empty_line()
170
+
171
+ if self._doc.eof():
172
+ return False
173
+
174
+ l1 = self._doc.peek().strip() # e.g. Parameters
175
+
176
+ if l1.startswith('.. index::'):
177
+ return True
178
+
179
+ l2 = self._doc.peek(1).strip() # ---------- or ==========
180
+ return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1))
181
+
182
+ def _strip(self, doc):
183
+ i = 0
184
+ j = 0
185
+ for i, line in enumerate(doc):
186
+ if line.strip():
187
+ break
188
+
189
+ for j, line in enumerate(doc[::-1]):
190
+ if line.strip():
191
+ break
192
+
193
+ return doc[i:len(doc)-j]
194
+
195
+ def _read_to_next_section(self):
196
+ section = self._doc.read_to_next_empty_line()
197
+
198
+ while not self._is_at_section() and not self._doc.eof():
199
+ if not self._doc.peek(-1).strip(): # previous line was empty
200
+ section += ['']
201
+
202
+ section += self._doc.read_to_next_empty_line()
203
+
204
+ return section
205
+
206
+ def _read_sections(self):
207
+ while not self._doc.eof():
208
+ data = self._read_to_next_section()
209
+ name = data[0].strip()
210
+
211
+ if name.startswith('..'): # index section
212
+ yield name, data[1:]
213
+ elif len(data) < 2:
214
+ yield StopIteration
215
+ else:
216
+ yield name, self._strip(data[2:])
217
+
218
+ def _parse_param_list(self, content, single_element_is_type=False):
219
+ r = Reader(content)
220
+ params = []
221
+ while not r.eof():
222
+ header = r.read().strip()
223
+ if ' : ' in header:
224
+ arg_name, arg_type = header.split(' : ')[:2]
225
+ else:
226
+ if single_element_is_type:
227
+ arg_name, arg_type = '', header
228
+ else:
229
+ arg_name, arg_type = header, ''
230
+
231
+ desc = r.read_to_next_unindented_line()
232
+ desc = dedent_lines(desc)
233
+ desc = strip_blank_lines(desc)
234
+
235
+ params.append(Parameter(arg_name, arg_type, desc))
236
+
237
+ return params
238
+
239
+ # See also supports the following formats.
240
+ #
241
+ # <FUNCNAME>
242
+ # <FUNCNAME> SPACE* COLON SPACE+ <DESC> SPACE*
243
+ # <FUNCNAME> ( COMMA SPACE+ <FUNCNAME>)+ (COMMA | PERIOD)? SPACE*
244
+ # <FUNCNAME> ( COMMA SPACE+ <FUNCNAME>)* SPACE* COLON SPACE+ <DESC> SPACE*
245
+
246
+ # <FUNCNAME> is one of
247
+ # <PLAIN_FUNCNAME>
248
+ # COLON <ROLE> COLON BACKTICK <PLAIN_FUNCNAME> BACKTICK
249
+ # where
250
+ # <PLAIN_FUNCNAME> is a legal function name, and
251
+ # <ROLE> is any nonempty sequence of word characters.
252
+ # Examples: func_f1 :meth:`func_h1` :obj:`~baz.obj_r` :class:`class_j`
253
+ # <DESC> is a string describing the function.
254
+
255
+ _role = r":(?P<role>\w+):"
256
+ _funcbacktick = r"`(?P<name>(?:~\w+\.)?[a-zA-Z0-9_\.-]+)`"
257
+ _funcplain = r"(?P<name2>[a-zA-Z0-9_\.-]+)"
258
+ _funcname = r"(" + _role + _funcbacktick + r"|" + _funcplain + r")"
259
+ _funcnamenext = _funcname.replace('role', 'rolenext')
260
+ _funcnamenext = _funcnamenext.replace('name', 'namenext')
261
+ _description = r"(?P<description>\s*:(\s+(?P<desc>\S+.*))?)?\s*$"
262
+ _func_rgx = re.compile(r"^\s*" + _funcname + r"\s*")
263
+ _line_rgx = re.compile(
264
+ r"^\s*" +
265
+ r"(?P<allfuncs>" + # group for all function names
266
+ _funcname +
267
+ r"(?P<morefuncs>([,]\s+" + _funcnamenext + r")*)" +
268
+ r")" + # end of "allfuncs"
269
+ # Some function lists have a trailing comma (or period) '\s*'
270
+ r"(?P<trailing>[,\.])?" +
271
+ _description)
272
+
273
+ # Empty <DESC> elements are replaced with '..'
274
+ empty_description = '..'
275
+
276
+ def _parse_see_also(self, content):
277
+ """
278
+ func_name : Descriptive text
279
+ continued text
280
+ another_func_name : Descriptive text
281
+ func_name1, func_name2, :meth:`func_name`, func_name3
282
+
283
+ """
284
+
285
+ items = []
286
+
287
+ def parse_item_name(text):
288
+ """Match ':role:`name`' or 'name'."""
289
+ m = self._func_rgx.match(text)
290
+ if not m:
291
+ raise ParseError("%s is not a item name" % text)
292
+ role = m.group('role')
293
+ name = m.group('name') if role else m.group('name2')
294
+ return name, role, m.end()
295
+
296
+ rest = []
297
+ for line in content:
298
+ if not line.strip():
299
+ continue
300
+
301
+ line_match = self._line_rgx.match(line)
302
+ description = None
303
+ if line_match:
304
+ description = line_match.group('desc')
305
+ if line_match.group('trailing') and description:
306
+ self._error_location(
307
+ 'Unexpected comma or period after function list at '
308
+ 'index %d of line "%s"' % (line_match.end('trailing'),
309
+ line),
310
+ error=False)
311
+ if not description and line.startswith(' '):
312
+ rest.append(line.strip())
313
+ elif line_match:
314
+ funcs = []
315
+ text = line_match.group('allfuncs')
316
+ while True:
317
+ if not text.strip():
318
+ break
319
+ name, role, match_end = parse_item_name(text)
320
+ funcs.append((name, role))
321
+ text = text[match_end:].strip()
322
+ if text and text[0] == ',':
323
+ text = text[1:].strip()
324
+ rest = list(filter(None, [description]))
325
+ items.append((funcs, rest))
326
+ else:
327
+ raise ParseError("%s is not a item name" % line)
328
+ return items
329
+
330
+ def _parse_index(self, section, content):
331
+ """
332
+ .. index:: default
333
+ :refguide: something, else, and more
334
+
335
+ """
336
+ def strip_each_in(lst):
337
+ return [s.strip() for s in lst]
338
+
339
+ out = {}
340
+ section = section.split('::')
341
+ if len(section) > 1:
342
+ out['default'] = strip_each_in(section[1].split(','))[0]
343
+ for line in content:
344
+ line = line.split(':')
345
+ if len(line) > 2:
346
+ out[line[1]] = strip_each_in(line[2].split(','))
347
+ return out
348
+
349
+ def _parse_summary(self):
350
+ """Grab signature (if given) and summary"""
351
+ if self._is_at_section():
352
+ return
353
+
354
+ # If several signatures present, take the last one
355
+ while True:
356
+ summary = self._doc.read_to_next_empty_line()
357
+ summary_str = " ".join([s.strip() for s in summary]).strip()
358
+ compiled = re.compile(r'^([\w., ]+=)?\s*[\w\.]+\(.*\)$')
359
+ if compiled.match(summary_str):
360
+ self['Signature'] = summary_str
361
+ if not self._is_at_section():
362
+ continue
363
+ break
364
+
365
+ if summary is not None:
366
+ self['Summary'] = summary
367
+
368
+ if not self._is_at_section():
369
+ self['Extended Summary'] = self._read_to_next_section()
370
+
371
+ def _parse(self):
372
+ self._doc.reset()
373
+ self._parse_summary()
374
+
375
+ sections = list(self._read_sections())
376
+ section_names = {section for section, content in sections}
377
+
378
+ has_returns = 'Returns' in section_names
379
+ has_yields = 'Yields' in section_names
380
+ # We could do more tests, but we are not. Arbitrarily.
381
+ if has_returns and has_yields:
382
+ msg = 'Docstring contains both a Returns and Yields section.'
383
+ raise ValueError(msg)
384
+ if not has_yields and 'Receives' in section_names:
385
+ msg = 'Docstring contains a Receives section but not Yields.'
386
+ raise ValueError(msg)
387
+
388
+ for (section, content) in sections:
389
+ if not section.startswith('..'):
390
+ section = (s.capitalize() for s in section.split(' '))
391
+ section = ' '.join(section)
392
+ if self.get(section):
393
+ self._error_location("The section %s appears twice"
394
+ % section)
395
+
396
+ if section in ('Parameters', 'Other Parameters', 'Attributes',
397
+ 'Methods'):
398
+ self[section] = self._parse_param_list(content)
399
+ elif section in ('Returns', 'Yields', 'Raises', 'Warns',
400
+ 'Receives'):
401
+ self[section] = self._parse_param_list(
402
+ content, single_element_is_type=True)
403
+ elif section.startswith('.. index::'):
404
+ self['index'] = self._parse_index(section, content)
405
+ elif section == 'See Also':
406
+ self['See Also'] = self._parse_see_also(content)
407
+ else:
408
+ self[section] = content
409
+
410
+ def _error_location(self, msg, error=True):
411
+ if hasattr(self, '_obj'):
412
+ # we know where the docs came from:
413
+ try:
414
+ filename = inspect.getsourcefile(self._obj)
415
+ except TypeError:
416
+ filename = None
417
+ msg = msg + (f" in the docstring of {self._obj} in {filename}.")
418
+ if error:
419
+ raise ValueError(msg)
420
+ else:
421
+ warn(msg, stacklevel=3)
422
+
423
+ # string conversion routines
424
+
425
+ def _str_header(self, name, symbol='-'):
426
+ return [name, len(name)*symbol]
427
+
428
+ def _str_indent(self, doc, indent=4):
429
+ out = []
430
+ for line in doc:
431
+ out += [' '*indent + line]
432
+ return out
433
+
434
+ def _str_signature(self):
435
+ if self['Signature']:
436
+ return [self['Signature'].replace('*', r'\*')] + ['']
437
+ else:
438
+ return ['']
439
+
440
+ def _str_summary(self):
441
+ if self['Summary']:
442
+ return self['Summary'] + ['']
443
+ else:
444
+ return []
445
+
446
+ def _str_extended_summary(self):
447
+ if self['Extended Summary']:
448
+ return self['Extended Summary'] + ['']
449
+ else:
450
+ return []
451
+
452
+ def _str_param_list(self, name):
453
+ out = []
454
+ if self[name]:
455
+ out += self._str_header(name)
456
+ for param in self[name]:
457
+ parts = []
458
+ if param.name:
459
+ parts.append(param.name)
460
+ if param.type:
461
+ parts.append(param.type)
462
+ out += [' : '.join(parts)]
463
+ if param.desc and ''.join(param.desc).strip():
464
+ out += self._str_indent(param.desc)
465
+ out += ['']
466
+ return out
467
+
468
+ def _str_section(self, name):
469
+ out = []
470
+ if self[name]:
471
+ out += self._str_header(name)
472
+ out += self[name]
473
+ out += ['']
474
+ return out
475
+
476
+ def _str_see_also(self, func_role):
477
+ if not self['See Also']:
478
+ return []
479
+ out = []
480
+ out += self._str_header("See Also")
481
+ out += ['']
482
+ last_had_desc = True
483
+ for funcs, desc in self['See Also']:
484
+ assert isinstance(funcs, list)
485
+ links = []
486
+ for func, role in funcs:
487
+ if role:
488
+ link = f':{role}:`{func}`'
489
+ elif func_role:
490
+ link = f':{func_role}:`{func}`'
491
+ else:
492
+ link = "`%s`_" % func
493
+ links.append(link)
494
+ link = ', '.join(links)
495
+ out += [link]
496
+ if desc:
497
+ out += self._str_indent([' '.join(desc)])
498
+ last_had_desc = True
499
+ else:
500
+ last_had_desc = False
501
+ out += self._str_indent([self.empty_description])
502
+
503
+ if last_had_desc:
504
+ out += ['']
505
+ out += ['']
506
+ return out
507
+
508
+ def _str_index(self):
509
+ idx = self['index']
510
+ out = []
511
+ output_index = False
512
+ default_index = idx.get('default', '')
513
+ if default_index:
514
+ output_index = True
515
+ out += ['.. index:: %s' % default_index]
516
+ for section, references in idx.items():
517
+ if section == 'default':
518
+ continue
519
+ output_index = True
520
+ out += [' :{}: {}'.format(section, ', '.join(references))]
521
+ if output_index:
522
+ return out
523
+ else:
524
+ return ''
525
+
526
+ def __str__(self, func_role=''):
527
+ out = []
528
+ out += self._str_signature()
529
+ out += self._str_summary()
530
+ out += self._str_extended_summary()
531
+ for param_list in ('Parameters', 'Returns', 'Yields', 'Receives',
532
+ 'Other Parameters', 'Raises', 'Warns'):
533
+ out += self._str_param_list(param_list)
534
+ out += self._str_section('Warnings')
535
+ out += self._str_see_also(func_role)
536
+ for s in ('Notes', 'References', 'Examples'):
537
+ out += self._str_section(s)
538
+ for param_list in ('Attributes', 'Methods'):
539
+ out += self._str_param_list(param_list)
540
+ out += self._str_index()
541
+ return '\n'.join(out)
542
+
543
+
544
+ def indent(str, indent=4):
545
+ indent_str = ' '*indent
546
+ if str is None:
547
+ return indent_str
548
+ lines = str.split('\n')
549
+ return '\n'.join(indent_str + l for l in lines)
550
+
551
+
552
+ def dedent_lines(lines):
553
+ """Deindent a list of lines maximally"""
554
+ return textwrap.dedent("\n".join(lines)).split("\n")
555
+
556
+
557
+ def header(text, style='-'):
558
+ return text + '\n' + style*len(text) + '\n'
559
+
560
+
561
+ class FunctionDoc(NumpyDocString):
562
+ def __init__(self, func, role='func', doc=None, config={}):
563
+ self._f = func
564
+ self._role = role # e.g. "func" or "meth"
565
+
566
+ if doc is None:
567
+ if func is None:
568
+ raise ValueError("No function or docstring given")
569
+ doc = inspect.getdoc(func) or ''
570
+ NumpyDocString.__init__(self, doc, config)
571
+
572
+ def get_func(self):
573
+ func_name = getattr(self._f, '__name__', self.__class__.__name__)
574
+ if inspect.isclass(self._f):
575
+ func = getattr(self._f, '__call__', self._f.__init__)
576
+ else:
577
+ func = self._f
578
+ return func, func_name
579
+
580
+ def __str__(self):
581
+ out = ''
582
+
583
+ func, func_name = self.get_func()
584
+
585
+ roles = {'func': 'function',
586
+ 'meth': 'method'}
587
+
588
+ if self._role:
589
+ if self._role not in roles:
590
+ print("Warning: invalid role %s" % self._role)
591
+ out += '.. {}:: {}\n \n\n'.format(roles.get(self._role, ''),
592
+ func_name)
593
+
594
+ out += super().__str__(func_role=self._role)
595
+ return out
596
+
597
+
598
+ class ClassDoc(NumpyDocString):
599
+
600
+ extra_public_methods = ['__call__']
601
+
602
+ def __init__(self, cls, doc=None, modulename='', func_doc=FunctionDoc,
603
+ config={}):
604
+ if not inspect.isclass(cls) and cls is not None:
605
+ raise ValueError("Expected a class or None, but got %r" % cls)
606
+ self._cls = cls
607
+
608
+ if 'sphinx' in sys.modules:
609
+ from sphinx.ext.autodoc import ALL
610
+ else:
611
+ ALL = object()
612
+
613
+ self.show_inherited_members = config.get(
614
+ 'show_inherited_class_members', True)
615
+
616
+ if modulename and not modulename.endswith('.'):
617
+ modulename += '.'
618
+ self._mod = modulename
619
+
620
+ if doc is None:
621
+ if cls is None:
622
+ raise ValueError("No class or documentation string given")
623
+ doc = pydoc.getdoc(cls)
624
+
625
+ NumpyDocString.__init__(self, doc)
626
+
627
+ _members = config.get('members', [])
628
+ if _members is ALL:
629
+ _members = None
630
+ _exclude = config.get('exclude-members', [])
631
+
632
+ if config.get('show_class_members', True) and _exclude is not ALL:
633
+ def splitlines_x(s):
634
+ if not s:
635
+ return []
636
+ else:
637
+ return s.splitlines()
638
+ for field, items in [('Methods', self.methods),
639
+ ('Attributes', self.properties)]:
640
+ if not self[field]:
641
+ doc_list = []
642
+ for name in sorted(items):
643
+ if (name in _exclude or
644
+ (_members and name not in _members)):
645
+ continue
646
+ try:
647
+ doc_item = pydoc.getdoc(getattr(self._cls, name))
648
+ doc_list.append(
649
+ Parameter(name, '', splitlines_x(doc_item)))
650
+ except AttributeError:
651
+ pass # method doesn't exist
652
+ self[field] = doc_list
653
+
654
+ @property
655
+ def methods(self):
656
+ if self._cls is None:
657
+ return []
658
+ return [name for name, func in inspect.getmembers(self._cls)
659
+ if ((not name.startswith('_')
660
+ or name in self.extra_public_methods)
661
+ and isinstance(func, Callable)
662
+ and self._is_show_member(name))]
663
+
664
+ @property
665
+ def properties(self):
666
+ if self._cls is None:
667
+ return []
668
+ return [name for name, func in inspect.getmembers(self._cls)
669
+ if (not name.startswith('_') and
670
+ (func is None or isinstance(func, property) or
671
+ inspect.isdatadescriptor(func))
672
+ and self._is_show_member(name))]
673
+
674
+ def _is_show_member(self, name):
675
+ if self.show_inherited_members:
676
+ return True # show all class members
677
+ if name not in self._cls.__dict__:
678
+ return False # class member is inherited, we do not show it
679
+ return True
scipy/_lib/_finite_differences.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from numpy import arange, newaxis, hstack, prod, array
2
+
3
+
4
+ def _central_diff_weights(Np, ndiv=1):
5
+ """
6
+ Return weights for an Np-point central derivative.
7
+
8
+ Assumes equally-spaced function points.
9
+
10
+ If weights are in the vector w, then
11
+ derivative is w[0] * f(x-ho*dx) + ... + w[-1] * f(x+h0*dx)
12
+
13
+ Parameters
14
+ ----------
15
+ Np : int
16
+ Number of points for the central derivative.
17
+ ndiv : int, optional
18
+ Number of divisions. Default is 1.
19
+
20
+ Returns
21
+ -------
22
+ w : ndarray
23
+ Weights for an Np-point central derivative. Its size is `Np`.
24
+
25
+ Notes
26
+ -----
27
+ Can be inaccurate for a large number of points.
28
+
29
+ Examples
30
+ --------
31
+ We can calculate a derivative value of a function.
32
+
33
+ >>> def f(x):
34
+ ... return 2 * x**2 + 3
35
+ >>> x = 3.0 # derivative point
36
+ >>> h = 0.1 # differential step
37
+ >>> Np = 3 # point number for central derivative
38
+ >>> weights = _central_diff_weights(Np) # weights for first derivative
39
+ >>> vals = [f(x + (i - Np/2) * h) for i in range(Np)]
40
+ >>> sum(w * v for (w, v) in zip(weights, vals))/h
41
+ 11.79999999999998
42
+
43
+ This value is close to the analytical solution:
44
+ f'(x) = 4x, so f'(3) = 12
45
+
46
+ References
47
+ ----------
48
+ .. [1] https://en.wikipedia.org/wiki/Finite_difference
49
+
50
+ """
51
+ if Np < ndiv + 1:
52
+ raise ValueError(
53
+ "Number of points must be at least the derivative order + 1."
54
+ )
55
+ if Np % 2 == 0:
56
+ raise ValueError("The number of points must be odd.")
57
+ from scipy import linalg
58
+
59
+ ho = Np >> 1
60
+ x = arange(-ho, ho + 1.0)
61
+ x = x[:, newaxis]
62
+ X = x**0.0
63
+ for k in range(1, Np):
64
+ X = hstack([X, x**k])
65
+ w = prod(arange(1, ndiv + 1), axis=0) * linalg.inv(X)[ndiv]
66
+ return w
67
+
68
+
69
+ def _derivative(func, x0, dx=1.0, n=1, args=(), order=3):
70
+ """
71
+ Find the nth derivative of a function at a point.
72
+
73
+ Given a function, use a central difference formula with spacing `dx` to
74
+ compute the nth derivative at `x0`.
75
+
76
+ Parameters
77
+ ----------
78
+ func : function
79
+ Input function.
80
+ x0 : float
81
+ The point at which the nth derivative is found.
82
+ dx : float, optional
83
+ Spacing.
84
+ n : int, optional
85
+ Order of the derivative. Default is 1.
86
+ args : tuple, optional
87
+ Arguments
88
+ order : int, optional
89
+ Number of points to use, must be odd.
90
+
91
+ Notes
92
+ -----
93
+ Decreasing the step size too small can result in round-off error.
94
+
95
+ Examples
96
+ --------
97
+ >>> def f(x):
98
+ ... return x**3 + x**2
99
+ >>> _derivative(f, 1.0, dx=1e-6)
100
+ 4.9999999999217337
101
+
102
+ """
103
+ if order < n + 1:
104
+ raise ValueError(
105
+ "'order' (the number of points used to compute the derivative), "
106
+ "must be at least the derivative order 'n' + 1."
107
+ )
108
+ if order % 2 == 0:
109
+ raise ValueError(
110
+ "'order' (the number of points used to compute the derivative) "
111
+ "must be odd."
112
+ )
113
+ # pre-computed for n=1 and 2 and low-order for speed.
114
+ if n == 1:
115
+ if order == 3:
116
+ weights = array([-1, 0, 1]) / 2.0
117
+ elif order == 5:
118
+ weights = array([1, -8, 0, 8, -1]) / 12.0
119
+ elif order == 7:
120
+ weights = array([-1, 9, -45, 0, 45, -9, 1]) / 60.0
121
+ elif order == 9:
122
+ weights = array([3, -32, 168, -672, 0, 672, -168, 32, -3]) / 840.0
123
+ else:
124
+ weights = _central_diff_weights(order, 1)
125
+ elif n == 2:
126
+ if order == 3:
127
+ weights = array([1, -2.0, 1])
128
+ elif order == 5:
129
+ weights = array([-1, 16, -30, 16, -1]) / 12.0
130
+ elif order == 7:
131
+ weights = array([2, -27, 270, -490, 270, -27, 2]) / 180.0
132
+ elif order == 9:
133
+ weights = (
134
+ array([-9, 128, -1008, 8064, -14350, 8064, -1008, 128, -9])
135
+ / 5040.0
136
+ )
137
+ else:
138
+ weights = _central_diff_weights(order, 2)
139
+ else:
140
+ weights = _central_diff_weights(order, n)
141
+ val = 0.0
142
+ ho = order >> 1
143
+ for k in range(order):
144
+ val += weights[k] * func(x0 + (k - ho) * dx, *args)
145
+ return val / prod((dx,) * n, axis=0)
scipy/_lib/_fpumode.cp39-win_amd64.dll.a ADDED
Binary file (1.55 kB). View file
 
scipy/_lib/_fpumode.cp39-win_amd64.pyd ADDED
Binary file (15.9 kB). View file
 
scipy/_lib/_gcutils.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Module for testing automatic garbage collection of objects
3
+
4
+ .. autosummary::
5
+ :toctree: generated/
6
+
7
+ set_gc_state - enable or disable garbage collection
8
+ gc_state - context manager for given state of garbage collector
9
+ assert_deallocated - context manager to check for circular references on object
10
+
11
+ """
12
+ import weakref
13
+ import gc
14
+
15
+ from contextlib import contextmanager
16
+ from platform import python_implementation
17
+
18
+ __all__ = ['set_gc_state', 'gc_state', 'assert_deallocated']
19
+
20
+
21
+ IS_PYPY = python_implementation() == 'PyPy'
22
+
23
+
24
+ class ReferenceError(AssertionError):
25
+ pass
26
+
27
+
28
+ def set_gc_state(state):
29
+ """ Set status of garbage collector """
30
+ if gc.isenabled() == state:
31
+ return
32
+ if state:
33
+ gc.enable()
34
+ else:
35
+ gc.disable()
36
+
37
+
38
+ @contextmanager
39
+ def gc_state(state):
40
+ """ Context manager to set state of garbage collector to `state`
41
+
42
+ Parameters
43
+ ----------
44
+ state : bool
45
+ True for gc enabled, False for disabled
46
+
47
+ Examples
48
+ --------
49
+ >>> with gc_state(False):
50
+ ... assert not gc.isenabled()
51
+ >>> with gc_state(True):
52
+ ... assert gc.isenabled()
53
+ """
54
+ orig_state = gc.isenabled()
55
+ set_gc_state(state)
56
+ yield
57
+ set_gc_state(orig_state)
58
+
59
+
60
+ @contextmanager
61
+ def assert_deallocated(func, *args, **kwargs):
62
+ """Context manager to check that object is deallocated
63
+
64
+ This is useful for checking that an object can be freed directly by
65
+ reference counting, without requiring gc to break reference cycles.
66
+ GC is disabled inside the context manager.
67
+
68
+ This check is not available on PyPy.
69
+
70
+ Parameters
71
+ ----------
72
+ func : callable
73
+ Callable to create object to check
74
+ \\*args : sequence
75
+ positional arguments to `func` in order to create object to check
76
+ \\*\\*kwargs : dict
77
+ keyword arguments to `func` in order to create object to check
78
+
79
+ Examples
80
+ --------
81
+ >>> class C: pass
82
+ >>> with assert_deallocated(C) as c:
83
+ ... # do something
84
+ ... del c
85
+
86
+ >>> class C:
87
+ ... def __init__(self):
88
+ ... self._circular = self # Make circular reference
89
+ >>> with assert_deallocated(C) as c: #doctest: +IGNORE_EXCEPTION_DETAIL
90
+ ... # do something
91
+ ... del c
92
+ Traceback (most recent call last):
93
+ ...
94
+ ReferenceError: Remaining reference(s) to object
95
+ """
96
+ if IS_PYPY:
97
+ raise RuntimeError("assert_deallocated is unavailable on PyPy")
98
+
99
+ with gc_state(False):
100
+ obj = func(*args, **kwargs)
101
+ ref = weakref.ref(obj)
102
+ yield obj
103
+ del obj
104
+ if ref() is not None:
105
+ raise ReferenceError("Remaining reference(s) to object")
scipy/_lib/_pep440.py ADDED
@@ -0,0 +1,487 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utility to compare pep440 compatible version strings.
2
+
3
+ The LooseVersion and StrictVersion classes that distutils provides don't
4
+ work; they don't recognize anything like alpha/beta/rc/dev versions.
5
+ """
6
+
7
+ # Copyright (c) Donald Stufft and individual contributors.
8
+ # All rights reserved.
9
+
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+
13
+ # 1. Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+
16
+ # 2. Redistributions in binary form must reproduce the above copyright
17
+ # notice, this list of conditions and the following disclaimer in the
18
+ # documentation and/or other materials provided with the distribution.
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
23
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ # POSSIBILITY OF SUCH DAMAGE.
31
+
32
+ import collections
33
+ import itertools
34
+ import re
35
+
36
+
37
+ __all__ = [
38
+ "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN",
39
+ ]
40
+
41
+
42
+ # BEGIN packaging/_structures.py
43
+
44
+
45
+ class Infinity:
46
+ def __repr__(self):
47
+ return "Infinity"
48
+
49
+ def __hash__(self):
50
+ return hash(repr(self))
51
+
52
+ def __lt__(self, other):
53
+ return False
54
+
55
+ def __le__(self, other):
56
+ return False
57
+
58
+ def __eq__(self, other):
59
+ return isinstance(other, self.__class__)
60
+
61
+ def __ne__(self, other):
62
+ return not isinstance(other, self.__class__)
63
+
64
+ def __gt__(self, other):
65
+ return True
66
+
67
+ def __ge__(self, other):
68
+ return True
69
+
70
+ def __neg__(self):
71
+ return NegativeInfinity
72
+
73
+
74
+ Infinity = Infinity()
75
+
76
+
77
+ class NegativeInfinity:
78
+ def __repr__(self):
79
+ return "-Infinity"
80
+
81
+ def __hash__(self):
82
+ return hash(repr(self))
83
+
84
+ def __lt__(self, other):
85
+ return True
86
+
87
+ def __le__(self, other):
88
+ return True
89
+
90
+ def __eq__(self, other):
91
+ return isinstance(other, self.__class__)
92
+
93
+ def __ne__(self, other):
94
+ return not isinstance(other, self.__class__)
95
+
96
+ def __gt__(self, other):
97
+ return False
98
+
99
+ def __ge__(self, other):
100
+ return False
101
+
102
+ def __neg__(self):
103
+ return Infinity
104
+
105
+
106
+ # BEGIN packaging/version.py
107
+
108
+
109
+ NegativeInfinity = NegativeInfinity()
110
+
111
+ _Version = collections.namedtuple(
112
+ "_Version",
113
+ ["epoch", "release", "dev", "pre", "post", "local"],
114
+ )
115
+
116
+
117
+ def parse(version):
118
+ """
119
+ Parse the given version string and return either a :class:`Version` object
120
+ or a :class:`LegacyVersion` object depending on if the given version is
121
+ a valid PEP 440 version or a legacy version.
122
+ """
123
+ try:
124
+ return Version(version)
125
+ except InvalidVersion:
126
+ return LegacyVersion(version)
127
+
128
+
129
+ class InvalidVersion(ValueError):
130
+ """
131
+ An invalid version was found, users should refer to PEP 440.
132
+ """
133
+
134
+
135
+ class _BaseVersion:
136
+
137
+ def __hash__(self):
138
+ return hash(self._key)
139
+
140
+ def __lt__(self, other):
141
+ return self._compare(other, lambda s, o: s < o)
142
+
143
+ def __le__(self, other):
144
+ return self._compare(other, lambda s, o: s <= o)
145
+
146
+ def __eq__(self, other):
147
+ return self._compare(other, lambda s, o: s == o)
148
+
149
+ def __ge__(self, other):
150
+ return self._compare(other, lambda s, o: s >= o)
151
+
152
+ def __gt__(self, other):
153
+ return self._compare(other, lambda s, o: s > o)
154
+
155
+ def __ne__(self, other):
156
+ return self._compare(other, lambda s, o: s != o)
157
+
158
+ def _compare(self, other, method):
159
+ if not isinstance(other, _BaseVersion):
160
+ return NotImplemented
161
+
162
+ return method(self._key, other._key)
163
+
164
+
165
+ class LegacyVersion(_BaseVersion):
166
+
167
+ def __init__(self, version):
168
+ self._version = str(version)
169
+ self._key = _legacy_cmpkey(self._version)
170
+
171
+ def __str__(self):
172
+ return self._version
173
+
174
+ def __repr__(self):
175
+ return f"<LegacyVersion({repr(str(self))})>"
176
+
177
+ @property
178
+ def public(self):
179
+ return self._version
180
+
181
+ @property
182
+ def base_version(self):
183
+ return self._version
184
+
185
+ @property
186
+ def local(self):
187
+ return None
188
+
189
+ @property
190
+ def is_prerelease(self):
191
+ return False
192
+
193
+ @property
194
+ def is_postrelease(self):
195
+ return False
196
+
197
+
198
+ _legacy_version_component_re = re.compile(
199
+ r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE,
200
+ )
201
+
202
+ _legacy_version_replacement_map = {
203
+ "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@",
204
+ }
205
+
206
+
207
+ def _parse_version_parts(s):
208
+ for part in _legacy_version_component_re.split(s):
209
+ part = _legacy_version_replacement_map.get(part, part)
210
+
211
+ if not part or part == ".":
212
+ continue
213
+
214
+ if part[:1] in "0123456789":
215
+ # pad for numeric comparison
216
+ yield part.zfill(8)
217
+ else:
218
+ yield "*" + part
219
+
220
+ # ensure that alpha/beta/candidate are before final
221
+ yield "*final"
222
+
223
+
224
+ def _legacy_cmpkey(version):
225
+ # We hardcode an epoch of -1 here. A PEP 440 version can only have an epoch
226
+ # greater than or equal to 0. This will effectively put the LegacyVersion,
227
+ # which uses the defacto standard originally implemented by setuptools,
228
+ # as before all PEP 440 versions.
229
+ epoch = -1
230
+
231
+ # This scheme is taken from pkg_resources.parse_version setuptools prior to
232
+ # its adoption of the packaging library.
233
+ parts = []
234
+ for part in _parse_version_parts(version.lower()):
235
+ if part.startswith("*"):
236
+ # remove "-" before a prerelease tag
237
+ if part < "*final":
238
+ while parts and parts[-1] == "*final-":
239
+ parts.pop()
240
+
241
+ # remove trailing zeros from each series of numeric parts
242
+ while parts and parts[-1] == "00000000":
243
+ parts.pop()
244
+
245
+ parts.append(part)
246
+ parts = tuple(parts)
247
+
248
+ return epoch, parts
249
+
250
+
251
+ # Deliberately not anchored to the start and end of the string, to make it
252
+ # easier for 3rd party code to reuse
253
+ VERSION_PATTERN = r"""
254
+ v?
255
+ (?:
256
+ (?:(?P<epoch>[0-9]+)!)? # epoch
257
+ (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
258
+ (?P<pre> # pre-release
259
+ [-_\.]?
260
+ (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
261
+ [-_\.]?
262
+ (?P<pre_n>[0-9]+)?
263
+ )?
264
+ (?P<post> # post release
265
+ (?:-(?P<post_n1>[0-9]+))
266
+ |
267
+ (?:
268
+ [-_\.]?
269
+ (?P<post_l>post|rev|r)
270
+ [-_\.]?
271
+ (?P<post_n2>[0-9]+)?
272
+ )
273
+ )?
274
+ (?P<dev> # dev release
275
+ [-_\.]?
276
+ (?P<dev_l>dev)
277
+ [-_\.]?
278
+ (?P<dev_n>[0-9]+)?
279
+ )?
280
+ )
281
+ (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
282
+ """
283
+
284
+
285
+ class Version(_BaseVersion):
286
+
287
+ _regex = re.compile(
288
+ r"^\s*" + VERSION_PATTERN + r"\s*$",
289
+ re.VERBOSE | re.IGNORECASE,
290
+ )
291
+
292
+ def __init__(self, version):
293
+ # Validate the version and parse it into pieces
294
+ match = self._regex.search(version)
295
+ if not match:
296
+ raise InvalidVersion(f"Invalid version: '{version}'")
297
+
298
+ # Store the parsed out pieces of the version
299
+ self._version = _Version(
300
+ epoch=int(match.group("epoch")) if match.group("epoch") else 0,
301
+ release=tuple(int(i) for i in match.group("release").split(".")),
302
+ pre=_parse_letter_version(
303
+ match.group("pre_l"),
304
+ match.group("pre_n"),
305
+ ),
306
+ post=_parse_letter_version(
307
+ match.group("post_l"),
308
+ match.group("post_n1") or match.group("post_n2"),
309
+ ),
310
+ dev=_parse_letter_version(
311
+ match.group("dev_l"),
312
+ match.group("dev_n"),
313
+ ),
314
+ local=_parse_local_version(match.group("local")),
315
+ )
316
+
317
+ # Generate a key which will be used for sorting
318
+ self._key = _cmpkey(
319
+ self._version.epoch,
320
+ self._version.release,
321
+ self._version.pre,
322
+ self._version.post,
323
+ self._version.dev,
324
+ self._version.local,
325
+ )
326
+
327
+ def __repr__(self):
328
+ return f"<Version({repr(str(self))})>"
329
+
330
+ def __str__(self):
331
+ parts = []
332
+
333
+ # Epoch
334
+ if self._version.epoch != 0:
335
+ parts.append(f"{self._version.epoch}!")
336
+
337
+ # Release segment
338
+ parts.append(".".join(str(x) for x in self._version.release))
339
+
340
+ # Pre-release
341
+ if self._version.pre is not None:
342
+ parts.append("".join(str(x) for x in self._version.pre))
343
+
344
+ # Post-release
345
+ if self._version.post is not None:
346
+ parts.append(f".post{self._version.post[1]}")
347
+
348
+ # Development release
349
+ if self._version.dev is not None:
350
+ parts.append(f".dev{self._version.dev[1]}")
351
+
352
+ # Local version segment
353
+ if self._version.local is not None:
354
+ parts.append(
355
+ "+{}".format(".".join(str(x) for x in self._version.local))
356
+ )
357
+
358
+ return "".join(parts)
359
+
360
+ @property
361
+ def public(self):
362
+ return str(self).split("+", 1)[0]
363
+
364
+ @property
365
+ def base_version(self):
366
+ parts = []
367
+
368
+ # Epoch
369
+ if self._version.epoch != 0:
370
+ parts.append(f"{self._version.epoch}!")
371
+
372
+ # Release segment
373
+ parts.append(".".join(str(x) for x in self._version.release))
374
+
375
+ return "".join(parts)
376
+
377
+ @property
378
+ def local(self):
379
+ version_string = str(self)
380
+ if "+" in version_string:
381
+ return version_string.split("+", 1)[1]
382
+
383
+ @property
384
+ def is_prerelease(self):
385
+ return bool(self._version.dev or self._version.pre)
386
+
387
+ @property
388
+ def is_postrelease(self):
389
+ return bool(self._version.post)
390
+
391
+
392
+ def _parse_letter_version(letter, number):
393
+ if letter:
394
+ # We assume there is an implicit 0 in a pre-release if there is
395
+ # no numeral associated with it.
396
+ if number is None:
397
+ number = 0
398
+
399
+ # We normalize any letters to their lower-case form
400
+ letter = letter.lower()
401
+
402
+ # We consider some words to be alternate spellings of other words and
403
+ # in those cases we want to normalize the spellings to our preferred
404
+ # spelling.
405
+ if letter == "alpha":
406
+ letter = "a"
407
+ elif letter == "beta":
408
+ letter = "b"
409
+ elif letter in ["c", "pre", "preview"]:
410
+ letter = "rc"
411
+ elif letter in ["rev", "r"]:
412
+ letter = "post"
413
+
414
+ return letter, int(number)
415
+ if not letter and number:
416
+ # We assume that if we are given a number but not given a letter,
417
+ # then this is using the implicit post release syntax (e.g., 1.0-1)
418
+ letter = "post"
419
+
420
+ return letter, int(number)
421
+
422
+
423
+ _local_version_seperators = re.compile(r"[\._-]")
424
+
425
+
426
+ def _parse_local_version(local):
427
+ """
428
+ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
429
+ """
430
+ if local is not None:
431
+ return tuple(
432
+ part.lower() if not part.isdigit() else int(part)
433
+ for part in _local_version_seperators.split(local)
434
+ )
435
+
436
+
437
+ def _cmpkey(epoch, release, pre, post, dev, local):
438
+ # When we compare a release version, we want to compare it with all of the
439
+ # trailing zeros removed. So we'll use a reverse the list, drop all the now
440
+ # leading zeros until we come to something non-zero, then take the rest,
441
+ # re-reverse it back into the correct order, and make it a tuple and use
442
+ # that for our sorting key.
443
+ release = tuple(
444
+ reversed(list(
445
+ itertools.dropwhile(
446
+ lambda x: x == 0,
447
+ reversed(release),
448
+ )
449
+ ))
450
+ )
451
+
452
+ # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
453
+ # We'll do this by abusing the pre-segment, but we _only_ want to do this
454
+ # if there is no pre- or a post-segment. If we have one of those, then
455
+ # the normal sorting rules will handle this case correctly.
456
+ if pre is None and post is None and dev is not None:
457
+ pre = -Infinity
458
+ # Versions without a pre-release (except as noted above) should sort after
459
+ # those with one.
460
+ elif pre is None:
461
+ pre = Infinity
462
+
463
+ # Versions without a post-segment should sort before those with one.
464
+ if post is None:
465
+ post = -Infinity
466
+
467
+ # Versions without a development segment should sort after those with one.
468
+ if dev is None:
469
+ dev = Infinity
470
+
471
+ if local is None:
472
+ # Versions without a local segment should sort before those with one.
473
+ local = -Infinity
474
+ else:
475
+ # Versions with a local segment need that segment parsed to implement
476
+ # the sorting rules in PEP440.
477
+ # - Alphanumeric segments sort before numeric segments
478
+ # - Alphanumeric segments sort lexicographically
479
+ # - Numeric segments sort numerically
480
+ # - Shorter versions sort before longer versions when the prefixes
481
+ # match exactly
482
+ local = tuple(
483
+ (i, "") if isinstance(i, int) else (-Infinity, i)
484
+ for i in local
485
+ )
486
+
487
+ return epoch, release, pre, post, dev, local
scipy/_lib/_test_ccallback.cp39-win_amd64.dll.a ADDED
Binary file (1.64 kB). View file
 
scipy/_lib/_test_ccallback.cp39-win_amd64.pyd ADDED
Binary file (52.2 kB). View file
 
scipy/_lib/_test_deprecation_call.cp39-win_amd64.dll.a ADDED
Binary file (1.72 kB). View file
 
scipy/_lib/_test_deprecation_call.cp39-win_amd64.pyd ADDED
Binary file (34.8 kB). View file
 
scipy/_lib/_test_deprecation_def.cp39-win_amd64.dll.a ADDED
Binary file (1.71 kB). View file
 
scipy/_lib/_test_deprecation_def.cp39-win_amd64.pyd ADDED
Binary file (27.1 kB). View file
 
scipy/_lib/_testutils.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Generic test utilities.
3
+
4
+ """
5
+
6
+ import os
7
+ import re
8
+ import sys
9
+ import numpy as np
10
+ import inspect
11
+ import sysconfig
12
+
13
+
14
+ __all__ = ['PytestTester', 'check_free_memory', '_TestPythranFunc', 'IS_MUSL']
15
+
16
+
17
+ IS_MUSL = False
18
+ try:
19
+ # Note that packaging is not a dependency, hence we need this try-except:
20
+ from packaging.tags import sys_tags
21
+ _tags = list(sys_tags())
22
+ if 'musllinux' in _tags[0].platform:
23
+ IS_MUSL = True
24
+ except ImportError:
25
+ # fallback to sysconfig (might be flaky)
26
+ v = sysconfig.get_config_var('HOST_GNU_TYPE') or ''
27
+ if 'musl' in v:
28
+ IS_MUSL = True
29
+
30
+
31
+ class FPUModeChangeWarning(RuntimeWarning):
32
+ """Warning about FPU mode change"""
33
+ pass
34
+
35
+
36
+ class PytestTester:
37
+ """
38
+ Run tests for this namespace
39
+
40
+ ``scipy.test()`` runs tests for all of SciPy, with the default settings.
41
+ When used from a submodule (e.g., ``scipy.cluster.test()``, only the tests
42
+ for that namespace are run.
43
+
44
+ Parameters
45
+ ----------
46
+ label : {'fast', 'full'}, optional
47
+ Whether to run only the fast tests, or also those marked as slow.
48
+ Default is 'fast'.
49
+ verbose : int, optional
50
+ Test output verbosity. Default is 1.
51
+ extra_argv : list, optional
52
+ Arguments to pass through to Pytest.
53
+ doctests : bool, optional
54
+ Whether to run doctests or not. Default is False.
55
+ coverage : bool, optional
56
+ Whether to run tests with code coverage measurements enabled.
57
+ Default is False.
58
+ tests : list of str, optional
59
+ List of module names to run tests for. By default, uses the module
60
+ from which the ``test`` function is called.
61
+ parallel : int, optional
62
+ Run tests in parallel with pytest-xdist, if number given is larger than
63
+ 1. Default is 1.
64
+
65
+ """
66
+ def __init__(self, module_name):
67
+ self.module_name = module_name
68
+
69
+ def __call__(self, label="fast", verbose=1, extra_argv=None, doctests=False,
70
+ coverage=False, tests=None, parallel=None):
71
+ import pytest
72
+
73
+ module = sys.modules[self.module_name]
74
+ module_path = os.path.abspath(module.__path__[0])
75
+
76
+ pytest_args = ['--showlocals', '--tb=short']
77
+
78
+ if doctests:
79
+ raise ValueError("Doctests not supported")
80
+
81
+ if extra_argv:
82
+ pytest_args += list(extra_argv)
83
+
84
+ if verbose and int(verbose) > 1:
85
+ pytest_args += ["-" + "v"*(int(verbose)-1)]
86
+
87
+ if coverage:
88
+ pytest_args += ["--cov=" + module_path]
89
+
90
+ if label == "fast":
91
+ pytest_args += ["-m", "not slow"]
92
+ elif label != "full":
93
+ pytest_args += ["-m", label]
94
+
95
+ if tests is None:
96
+ tests = [self.module_name]
97
+
98
+ if parallel is not None and parallel > 1:
99
+ if _pytest_has_xdist():
100
+ pytest_args += ['-n', str(parallel)]
101
+ else:
102
+ import warnings
103
+ warnings.warn('Could not run tests in parallel because '
104
+ 'pytest-xdist plugin is not available.',
105
+ stacklevel=2)
106
+
107
+ pytest_args += ['--pyargs'] + list(tests)
108
+
109
+ try:
110
+ code = pytest.main(pytest_args)
111
+ except SystemExit as exc:
112
+ code = exc.code
113
+
114
+ return (code == 0)
115
+
116
+
117
+ class _TestPythranFunc:
118
+ '''
119
+ These are situations that can be tested in our pythran tests:
120
+ - A function with multiple array arguments and then
121
+ other positional and keyword arguments.
122
+ - A function with array-like keywords (e.g. `def somefunc(x0, x1=None)`.
123
+ Note: list/tuple input is not yet tested!
124
+
125
+ `self.arguments`: A dictionary which key is the index of the argument,
126
+ value is tuple(array value, all supported dtypes)
127
+ `self.partialfunc`: A function used to freeze some non-array argument
128
+ that of no interests in the original function
129
+ '''
130
+ ALL_INTEGER = [np.int8, np.int16, np.int32, np.int64, np.intc, np.intp]
131
+ ALL_FLOAT = [np.float32, np.float64]
132
+ ALL_COMPLEX = [np.complex64, np.complex128]
133
+
134
+ def setup_method(self):
135
+ self.arguments = {}
136
+ self.partialfunc = None
137
+ self.expected = None
138
+
139
+ def get_optional_args(self, func):
140
+ # get optional arguments with its default value,
141
+ # used for testing keywords
142
+ signature = inspect.signature(func)
143
+ optional_args = {}
144
+ for k, v in signature.parameters.items():
145
+ if v.default is not inspect.Parameter.empty:
146
+ optional_args[k] = v.default
147
+ return optional_args
148
+
149
+ def get_max_dtype_list_length(self):
150
+ # get the max supported dtypes list length in all arguments
151
+ max_len = 0
152
+ for arg_idx in self.arguments:
153
+ cur_len = len(self.arguments[arg_idx][1])
154
+ if cur_len > max_len:
155
+ max_len = cur_len
156
+ return max_len
157
+
158
+ def get_dtype(self, dtype_list, dtype_idx):
159
+ # get the dtype from dtype_list via index
160
+ # if the index is out of range, then return the last dtype
161
+ if dtype_idx > len(dtype_list)-1:
162
+ return dtype_list[-1]
163
+ else:
164
+ return dtype_list[dtype_idx]
165
+
166
+ def test_all_dtypes(self):
167
+ for type_idx in range(self.get_max_dtype_list_length()):
168
+ args_array = []
169
+ for arg_idx in self.arguments:
170
+ new_dtype = self.get_dtype(self.arguments[arg_idx][1],
171
+ type_idx)
172
+ args_array.append(self.arguments[arg_idx][0].astype(new_dtype))
173
+ self.pythranfunc(*args_array)
174
+
175
+ def test_views(self):
176
+ args_array = []
177
+ for arg_idx in self.arguments:
178
+ args_array.append(self.arguments[arg_idx][0][::-1][::-1])
179
+ self.pythranfunc(*args_array)
180
+
181
+ def test_strided(self):
182
+ args_array = []
183
+ for arg_idx in self.arguments:
184
+ args_array.append(np.repeat(self.arguments[arg_idx][0],
185
+ 2, axis=0)[::2])
186
+ self.pythranfunc(*args_array)
187
+
188
+
189
+ def _pytest_has_xdist():
190
+ """
191
+ Check if the pytest-xdist plugin is installed, providing parallel tests
192
+ """
193
+ # Check xdist exists without importing, otherwise pytests emits warnings
194
+ from importlib.util import find_spec
195
+ return find_spec('xdist') is not None
196
+
197
+
198
+ def check_free_memory(free_mb):
199
+ """
200
+ Check *free_mb* of memory is available, otherwise do pytest.skip
201
+ """
202
+ import pytest
203
+
204
+ try:
205
+ mem_free = _parse_size(os.environ['SCIPY_AVAILABLE_MEM'])
206
+ msg = '{} MB memory required, but environment SCIPY_AVAILABLE_MEM={}'.format(
207
+ free_mb, os.environ['SCIPY_AVAILABLE_MEM'])
208
+ except KeyError:
209
+ mem_free = _get_mem_available()
210
+ if mem_free is None:
211
+ pytest.skip("Could not determine available memory; set SCIPY_AVAILABLE_MEM "
212
+ "variable to free memory in MB to run the test.")
213
+ msg = f'{free_mb} MB memory required, but {mem_free/1e6} MB available'
214
+
215
+ if mem_free < free_mb * 1e6:
216
+ pytest.skip(msg)
217
+
218
+
219
+ def _parse_size(size_str):
220
+ suffixes = {'': 1e6,
221
+ 'b': 1.0,
222
+ 'k': 1e3, 'M': 1e6, 'G': 1e9, 'T': 1e12,
223
+ 'kb': 1e3, 'Mb': 1e6, 'Gb': 1e9, 'Tb': 1e12,
224
+ 'kib': 1024.0, 'Mib': 1024.0**2, 'Gib': 1024.0**3, 'Tib': 1024.0**4}
225
+ m = re.match(r'^\s*(\d+)\s*({})\s*$'.format('|'.join(suffixes.keys())),
226
+ size_str,
227
+ re.I)
228
+ if not m or m.group(2) not in suffixes:
229
+ raise ValueError("Invalid size string")
230
+
231
+ return float(m.group(1)) * suffixes[m.group(2)]
232
+
233
+
234
+ def _get_mem_available():
235
+ """
236
+ Get information about memory available, not counting swap.
237
+ """
238
+ try:
239
+ import psutil
240
+ return psutil.virtual_memory().available
241
+ except (ImportError, AttributeError):
242
+ pass
243
+
244
+ if sys.platform.startswith('linux'):
245
+ info = {}
246
+ with open('/proc/meminfo') as f:
247
+ for line in f:
248
+ p = line.split()
249
+ info[p[0].strip(':').lower()] = float(p[1]) * 1e3
250
+
251
+ if 'memavailable' in info:
252
+ # Linux >= 3.14
253
+ return info['memavailable']
254
+ else:
255
+ return info['memfree'] + info['cached']
256
+
257
+ return None
scipy/_lib/_threadsafety.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ import scipy._lib.decorator
4
+
5
+
6
+ __all__ = ['ReentrancyError', 'ReentrancyLock', 'non_reentrant']
7
+
8
+
9
+ class ReentrancyError(RuntimeError):
10
+ pass
11
+
12
+
13
+ class ReentrancyLock:
14
+ """
15
+ Threading lock that raises an exception for reentrant calls.
16
+
17
+ Calls from different threads are serialized, and nested calls from the
18
+ same thread result to an error.
19
+
20
+ The object can be used as a context manager or to decorate functions
21
+ via the decorate() method.
22
+
23
+ """
24
+
25
+ def __init__(self, err_msg):
26
+ self._rlock = threading.RLock()
27
+ self._entered = False
28
+ self._err_msg = err_msg
29
+
30
+ def __enter__(self):
31
+ self._rlock.acquire()
32
+ if self._entered:
33
+ self._rlock.release()
34
+ raise ReentrancyError(self._err_msg)
35
+ self._entered = True
36
+
37
+ def __exit__(self, type, value, traceback):
38
+ self._entered = False
39
+ self._rlock.release()
40
+
41
+ def decorate(self, func):
42
+ def caller(func, *a, **kw):
43
+ with self:
44
+ return func(*a, **kw)
45
+ return scipy._lib.decorator.decorate(func, caller)
46
+
47
+
48
+ def non_reentrant(err_msg=None):
49
+ """
50
+ Decorate a function with a threading lock and prevent reentrant calls.
51
+ """
52
+ def decorator(func):
53
+ msg = err_msg
54
+ if msg is None:
55
+ msg = "%s is not re-entrant" % func.__name__
56
+ lock = ReentrancyLock(msg)
57
+ return lock.decorate(func)
58
+ return decorator
scipy/_lib/_tmpdirs.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ''' Contexts for *with* statement providing temporary directories
2
+ '''
3
+ import os
4
+ from contextlib import contextmanager
5
+ from shutil import rmtree
6
+ from tempfile import mkdtemp
7
+
8
+
9
+ @contextmanager
10
+ def tempdir():
11
+ """Create and return a temporary directory. This has the same
12
+ behavior as mkdtemp but can be used as a context manager.
13
+
14
+ Upon exiting the context, the directory and everything contained
15
+ in it are removed.
16
+
17
+ Examples
18
+ --------
19
+ >>> import os
20
+ >>> with tempdir() as tmpdir:
21
+ ... fname = os.path.join(tmpdir, 'example_file.txt')
22
+ ... with open(fname, 'wt') as fobj:
23
+ ... _ = fobj.write('a string\\n')
24
+ >>> os.path.exists(tmpdir)
25
+ False
26
+ """
27
+ d = mkdtemp()
28
+ yield d
29
+ rmtree(d)
30
+
31
+
32
+ @contextmanager
33
+ def in_tempdir():
34
+ ''' Create, return, and change directory to a temporary directory
35
+
36
+ Examples
37
+ --------
38
+ >>> import os
39
+ >>> my_cwd = os.getcwd()
40
+ >>> with in_tempdir() as tmpdir:
41
+ ... _ = open('test.txt', 'wt').write('some text')
42
+ ... assert os.path.isfile('test.txt')
43
+ ... assert os.path.isfile(os.path.join(tmpdir, 'test.txt'))
44
+ >>> os.path.exists(tmpdir)
45
+ False
46
+ >>> os.getcwd() == my_cwd
47
+ True
48
+ '''
49
+ pwd = os.getcwd()
50
+ d = mkdtemp()
51
+ os.chdir(d)
52
+ yield d
53
+ os.chdir(pwd)
54
+ rmtree(d)
55
+
56
+
57
+ @contextmanager
58
+ def in_dir(dir=None):
59
+ """ Change directory to given directory for duration of ``with`` block
60
+
61
+ Useful when you want to use `in_tempdir` for the final test, but
62
+ you are still debugging. For example, you may want to do this in the end:
63
+
64
+ >>> with in_tempdir() as tmpdir:
65
+ ... # do something complicated which might break
66
+ ... pass
67
+
68
+ But, indeed, the complicated thing does break, and meanwhile, the
69
+ ``in_tempdir`` context manager wiped out the directory with the
70
+ temporary files that you wanted for debugging. So, while debugging, you
71
+ replace with something like:
72
+
73
+ >>> with in_dir() as tmpdir: # Use working directory by default
74
+ ... # do something complicated which might break
75
+ ... pass
76
+
77
+ You can then look at the temporary file outputs to debug what is happening,
78
+ fix, and finally replace ``in_dir`` with ``in_tempdir`` again.
79
+ """
80
+ cwd = os.getcwd()
81
+ if dir is None:
82
+ yield cwd
83
+ return
84
+ os.chdir(dir)
85
+ yield dir
86
+ os.chdir(cwd)
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.
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'