numexpr 2.10.1__tar.gz → 2.10.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {numexpr-2.10.1 → numexpr-2.10.2}/ANNOUNCE.rst +12 -13
  2. numexpr-2.10.2/PKG-INFO +214 -0
  3. {numexpr-2.10.1 → numexpr-2.10.2}/RELEASE_NOTES.rst +17 -5
  4. numexpr-2.10.2/VERSION +1 -0
  5. numexpr-2.10.2/bench/large_array_vs_numpy.py +152 -0
  6. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/necompiler.py +3 -5
  7. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/tests/test_numexpr.py +73 -1
  8. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/utils.py +81 -0
  9. numexpr-2.10.2/numexpr/version.py +5 -0
  10. numexpr-2.10.2/numexpr.egg-info/PKG-INFO +214 -0
  11. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr.egg-info/SOURCES.txt +1 -0
  12. {numexpr-2.10.1 → numexpr-2.10.2}/pyproject.toml +1 -0
  13. {numexpr-2.10.1 → numexpr-2.10.2}/setup.py +6 -0
  14. numexpr-2.10.1/PKG-INFO +0 -27
  15. numexpr-2.10.1/VERSION +0 -1
  16. numexpr-2.10.1/numexpr/version.py +0 -5
  17. numexpr-2.10.1/numexpr.egg-info/PKG-INFO +0 -27
  18. {numexpr-2.10.1 → numexpr-2.10.2}/AUTHORS.txt +0 -0
  19. {numexpr-2.10.1 → numexpr-2.10.2}/LICENSE.txt +0 -0
  20. {numexpr-2.10.1 → numexpr-2.10.2}/MANIFEST.in +0 -0
  21. {numexpr-2.10.1 → numexpr-2.10.2}/README.rst +0 -0
  22. {numexpr-2.10.1 → numexpr-2.10.2}/bench/boolean_timing.py +0 -0
  23. {numexpr-2.10.1 → numexpr-2.10.2}/bench/issue-36.py +0 -0
  24. {numexpr-2.10.1 → numexpr-2.10.2}/bench/issue-47.py +0 -0
  25. {numexpr-2.10.1 → numexpr-2.10.2}/bench/multidim.py +0 -0
  26. {numexpr-2.10.1 → numexpr-2.10.2}/bench/poly.py +0 -0
  27. {numexpr-2.10.1 → numexpr-2.10.2}/bench/timing.py +0 -0
  28. {numexpr-2.10.1 → numexpr-2.10.2}/bench/unaligned-simple.py +0 -0
  29. {numexpr-2.10.1 → numexpr-2.10.2}/bench/varying-expr.py +0 -0
  30. {numexpr-2.10.1 → numexpr-2.10.2}/bench/vml_timing.py +0 -0
  31. {numexpr-2.10.1 → numexpr-2.10.2}/bench/vml_timing2.py +0 -0
  32. {numexpr-2.10.1 → numexpr-2.10.2}/bench/vml_timing3.py +0 -0
  33. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/__init__.py +0 -0
  34. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/complex_functions.hpp +0 -0
  35. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/cpuinfo.py +0 -0
  36. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/expressions.py +0 -0
  37. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/functions.hpp +0 -0
  38. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/interp_body.cpp +0 -0
  39. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/interpreter.cpp +0 -0
  40. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/interpreter.hpp +0 -0
  41. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/missing_posix_functions.hpp +0 -0
  42. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/module.cpp +0 -0
  43. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/module.hpp +0 -0
  44. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/msvc_function_stubs.hpp +0 -0
  45. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/numexpr_config.hpp +0 -0
  46. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/numexpr_object.cpp +0 -0
  47. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/numexpr_object.hpp +0 -0
  48. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/opcodes.hpp +0 -0
  49. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/str-two-way.hpp +0 -0
  50. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/tests/__init__.py +0 -0
  51. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/win32/pthread.c +0 -0
  52. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/win32/pthread.h +0 -0
  53. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr/win32/stdint.h +0 -0
  54. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr.egg-info/dependency_links.txt +0 -0
  55. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr.egg-info/requires.txt +0 -0
  56. {numexpr-2.10.1 → numexpr-2.10.2}/numexpr.egg-info/top_level.txt +0 -0
  57. {numexpr-2.10.1 → numexpr-2.10.2}/requirements.txt +0 -0
  58. {numexpr-2.10.1 → numexpr-2.10.2}/setup.cfg +0 -0
  59. {numexpr-2.10.1 → numexpr-2.10.2}/site.cfg.example +0 -0
@@ -1,29 +1,28 @@
1
1
  =========================
2
- Announcing NumExpr 2.10.1
2
+ Announcing NumExpr 2.10.2
3
3
  =========================
4
4
 
5
5
  Hi everyone,
6
6
 
7
- NumExpr 2.10.1 continues to establize the support for NumPy 2.0.0.
8
- Also, the default number of 'safe' threads has been upgraded to 16
9
- (instead of previous 8). Finally, preliminary support for Python 3.13;
10
- thanks to Karolina Surma.
7
+ NumExpr 2.10.2 provides wheels for Python 2.13 for first time.
8
+ Also, there is better support for CPUs that do not have a power
9
+ of 2 number of cores. Finally, numexpr is allowed to run with
10
+ the multithreading package in Python.
11
11
 
12
12
  Project documentation is available at:
13
13
 
14
14
  http://numexpr.readthedocs.io/
15
15
 
16
- Changes from 2.9.0 to 2.10.0
17
- ----------------------------
16
+ Changes from 2.10.1 to 2.10.2
17
+ -----------------------------
18
18
 
19
- * Support for NumPy 2.0.0. This is still experimental, so please
20
- report any issues you find. Thanks to Clément Robert and Thomas
21
- Caswell for the work.
19
+ * Better support for CPUs that do not have a power of 2 number of
20
+ cores. See #479 and #490. Thanks to @avalentino.
22
21
 
23
- * Avoid erroring when OMP_NUM_THREADS is empty string. Thanks to
24
- Patrick Hoefler.
22
+ * Allow numexpr to run with the multithreading package in Python.
23
+ See PR #496. Thanks to @emmaai
25
24
 
26
- * Do not warn if OMP_NUM_THREAD set.
25
+ * Wheels for Python 3.13 are now provided.
27
26
 
28
27
  What's Numexpr?
29
28
  ---------------
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.1
2
+ Name: numexpr
3
+ Version: 2.10.2
4
+ Summary: Fast numerical expression evaluator for NumPy
5
+ Author-email: "David M. Cooke, Francesc Alted, and others" <blosc@blosc.org>
6
+ Maintainer-email: Blosc Development Team <blosc@blosc.org>
7
+ Project-URL: homepage, https://github.com/pydata/numexpr
8
+ Project-URL: documentation, https://numexpr.readthedocs.io
9
+ Project-URL: repository, https://github.com/pydata/numexpr
10
+ Classifier: Development Status :: 6 - Mature
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Information Technology
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: Unix
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/x-rst
26
+ License-File: LICENSE.txt
27
+ License-File: AUTHORS.txt
28
+ Requires-Dist: numpy>=1.23.0
29
+
30
+ ======================================================
31
+ NumExpr: Fast numerical expression evaluator for NumPy
32
+ ======================================================
33
+
34
+ :Author: David M. Cooke, Francesc Alted, and others.
35
+ :Maintainer: Francesc Alted
36
+ :Contact: faltet@gmail.com
37
+ :URL: https://github.com/pydata/numexpr
38
+ :Documentation: http://numexpr.readthedocs.io/en/latest/
39
+ :GitHub Actions: |actions|
40
+ :PyPi: |version|
41
+ :DOI: |doi|
42
+ :readthedocs: |docs|
43
+
44
+ .. |actions| image:: https://github.com/pydata/numexpr/workflows/Build/badge.svg
45
+ :target: https://github.com/pydata/numexpr/actions
46
+ .. |travis| image:: https://travis-ci.org/pydata/numexpr.png?branch=master
47
+ :target: https://travis-ci.org/pydata/numexpr
48
+ .. |docs| image:: https://readthedocs.org/projects/numexpr/badge/?version=latest
49
+ :target: http://numexpr.readthedocs.io/en/latest
50
+ .. |doi| image:: https://zenodo.org/badge/doi/10.5281/zenodo.2483274.svg
51
+ :target: https://doi.org/10.5281/zenodo.2483274
52
+ .. |version| image:: https://img.shields.io/pypi/v/numexpr
53
+ :target: https://pypi.python.org/pypi/numexpr
54
+
55
+
56
+ What is NumExpr?
57
+ ----------------
58
+
59
+ NumExpr is a fast numerical expression evaluator for NumPy. With it,
60
+ expressions that operate on arrays (like :code:`'3*a+4*b'`) are accelerated
61
+ and use less memory than doing the same calculation in Python.
62
+
63
+ In addition, its multi-threaded capabilities can make use of all your
64
+ cores -- which generally results in substantial performance scaling compared
65
+ to NumPy.
66
+
67
+ Last but not least, numexpr can make use of Intel's VML (Vector Math
68
+ Library, normally integrated in its Math Kernel Library, or MKL).
69
+ This allows further acceleration of transcendent expressions.
70
+
71
+
72
+ How NumExpr achieves high performance
73
+ -------------------------------------
74
+
75
+ The main reason why NumExpr achieves better performance than NumPy is
76
+ that it avoids allocating memory for intermediate results. This
77
+ results in better cache utilization and reduces memory access in
78
+ general. Due to this, NumExpr works best with large arrays.
79
+
80
+ NumExpr parses expressions into its own op-codes that are then used by
81
+ an integrated computing virtual machine. The array operands are split
82
+ into small chunks that easily fit in the cache of the CPU and passed
83
+ to the virtual machine. The virtual machine then applies the
84
+ operations on each chunk. It's worth noting that all temporaries and
85
+ constants in the expression are also chunked. Chunks are distributed among
86
+ the available cores of the CPU, resulting in highly parallelized code
87
+ execution.
88
+
89
+ The result is that NumExpr can get the most of your machine computing
90
+ capabilities for array-wise computations. Common speed-ups with regard
91
+ to NumPy are usually between 0.95x (for very simple expressions like
92
+ :code:`'a + 1'`) and 4x (for relatively complex ones like :code:`'a*b-4.1*a > 2.5*b'`),
93
+ although much higher speed-ups can be achieved for some functions and complex
94
+ math operations (up to 15x in some cases).
95
+
96
+ NumExpr performs best on matrices that are too large to fit in L1 CPU cache.
97
+ In order to get a better idea on the different speed-ups that can be achieved
98
+ on your platform, run the provided benchmarks.
99
+
100
+ Installation
101
+ ------------
102
+
103
+ From wheels
104
+ ^^^^^^^^^^^
105
+
106
+ NumExpr is available for install via `pip` for a wide range of platforms and
107
+ Python versions (which may be browsed at: https://pypi.org/project/numexpr/#files).
108
+ Installation can be performed as::
109
+
110
+ pip install numexpr
111
+
112
+ If you are using the Anaconda or Miniconda distribution of Python you may prefer
113
+ to use the `conda` package manager in this case::
114
+
115
+ conda install numexpr
116
+
117
+ From Source
118
+ ^^^^^^^^^^^
119
+
120
+ On most \*nix systems your compilers will already be present. However if you
121
+ are using a virtual environment with a substantially newer version of Python than
122
+ your system Python you may be prompted to install a new version of `gcc` or `clang`.
123
+
124
+ For Windows, you will need to install the Microsoft Visual C++ Build Tools
125
+ (which are free) first. The version depends on which version of Python you have
126
+ installed:
127
+
128
+ https://wiki.python.org/moin/WindowsCompilers
129
+
130
+ For Python 3.6+ simply installing the latest version of MSVC build tools should
131
+ be sufficient. Note that wheels found via pip do not include MKL support. Wheels
132
+ available via `conda` will have MKL, if the MKL backend is used for NumPy.
133
+
134
+ See `requirements.txt` for the required version of NumPy.
135
+
136
+ NumExpr is built in the standard Python way::
137
+
138
+ python setup.py build install
139
+
140
+ You can test `numexpr` with::
141
+
142
+ python -c "import numexpr; numexpr.test()"
143
+
144
+ Do not test NumExpr in the source directory or you will generate import errors.
145
+
146
+ Enable Intel® MKL support
147
+ ^^^^^^^^^^^^^^^^^^^^^^^^^
148
+
149
+ NumExpr includes support for Intel's MKL library. This may provide better
150
+ performance on Intel architectures, mainly when evaluating transcendental
151
+ functions (trigonometrical, exponential, ...).
152
+
153
+ If you have Intel's MKL, copy the `site.cfg.example` that comes with the
154
+ distribution to `site.cfg` and edit the latter file to provide correct paths to
155
+ the MKL libraries in your system. After doing this, you can proceed with the
156
+ usual building instructions listed above.
157
+
158
+ Pay attention to the messages during the building process in order to know
159
+ whether MKL has been detected or not. Finally, you can check the speed-ups on
160
+ your machine by running the `bench/vml_timing.py` script (you can play with
161
+ different parameters to the `set_vml_accuracy_mode()` and `set_vml_num_threads()`
162
+ functions in the script so as to see how it would affect performance).
163
+
164
+ Usage
165
+ -----
166
+
167
+ ::
168
+
169
+ >>> import numpy as np
170
+ >>> import numexpr as ne
171
+
172
+ >>> a = np.arange(1e6) # Choose large arrays for better speedups
173
+ >>> b = np.arange(1e6)
174
+
175
+ >>> ne.evaluate("a + 1") # a simple expression
176
+ array([ 1.00000000e+00, 2.00000000e+00, 3.00000000e+00, ...,
177
+ 9.99998000e+05, 9.99999000e+05, 1.00000000e+06])
178
+
179
+ >>> ne.evaluate("a * b - 4.1 * a > 2.5 * b") # a more complex one
180
+ array([False, False, False, ..., True, True, True], dtype=bool)
181
+
182
+ >>> ne.evaluate("sin(a) + arcsinh(a/b)") # you can also use functions
183
+ array([ NaN, 1.72284457, 1.79067101, ..., 1.09567006,
184
+ 0.17523598, -0.09597844])
185
+
186
+ >>> s = np.array([b'abba', b'abbb', b'abbcdef'])
187
+ >>> ne.evaluate("b'abba' == s") # string arrays are supported too
188
+ array([ True, False, False], dtype=bool)
189
+
190
+
191
+ Documentation
192
+ -------------
193
+
194
+ Please see the official documentation at `numexpr.readthedocs.io <https://numexpr.readthedocs.io>`_.
195
+ Included is a user guide, benchmark results, and the reference API.
196
+
197
+
198
+ Authors
199
+ -------
200
+
201
+ Please see `AUTHORS.txt <https://github.com/pydata/numexpr/blob/master/AUTHORS.txt>`_.
202
+
203
+
204
+ License
205
+ -------
206
+
207
+ NumExpr is distributed under the `MIT <http://www.opensource.org/licenses/mit-license.php>`_ license.
208
+
209
+
210
+ .. Local Variables:
211
+ .. mode: text
212
+ .. coding: utf-8
213
+ .. fill-column: 70
214
+ .. End:
@@ -2,21 +2,33 @@
2
2
  Release notes for NumExpr 2.10 series
3
3
  =====================================
4
4
 
5
+ Changes from 2.10.1 to 2.10.2
6
+ -----------------------------
7
+
8
+ * Better support for CPUs that do not have a power of 2 number of
9
+ cores. See #479 and #490. Thanks to @avalentino.
10
+
11
+ * Allow numexpr to run with the multithreading package in Python.
12
+ See PR #496. Thanks to @emmaai
13
+
14
+ * Wheels for Python 3.13 are now provided.
15
+
16
+
5
17
  Changes from 2.10.0 to 2.10.1
6
18
  -----------------------------
7
19
 
8
- - The default number of 'safe' threads has been upgraded to 16 (instead of
20
+ * The default number of 'safe' threads has been upgraded to 16 (instead of
9
21
  previous 8). That means that if your CPU has > 16 cores, the default is
10
22
  to use 16. You can always override this with the "NUMEXPR_MAX_THREADS"
11
23
  environment variable.
12
24
 
13
- - NumPy 1.23 is now the minimum supported.
25
+ * NumPy 1.23 is now the minimum supported.
14
26
 
15
- - Preliminary support for Python 3.13. Thanks to Karolina Surma.
27
+ * Preliminary support for Python 3.13. Thanks to Karolina Surma.
16
28
 
17
- - Fix tests on nthreads detection (closes: #479). Thanks to @avalentino.
29
+ * Fix tests on nthreads detection (closes: #479). Thanks to @avalentino.
18
30
 
19
- - The build process has been modernized and now uses the `pyproject.toml`
31
+ * The build process has been modernized and now uses the `pyproject.toml`
20
32
  file for more of the configuration options.
21
33
 
22
34
 
numexpr-2.10.2/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.10.2
@@ -0,0 +1,152 @@
1
+ #################################################################################
2
+ # To mimic the scenario that computation is i/o bound and constrained by memory
3
+ #
4
+ # It's a much simplified version that the chunk is computed in a loop,
5
+ # and expression is evaluated in a sequence, which is not true in reality.
6
+ # Neverthless, numexpr outperforms numpy.
7
+ #################################################################################
8
+ """
9
+ Benchmarking Expression 1:
10
+ NumPy time (threaded over 32 chunks with 2 threads): 4.612313 seconds
11
+ numexpr time (threaded with re_evaluate over 32 chunks with 2 threads): 0.951172 seconds
12
+ numexpr speedup: 4.85x
13
+ ----------------------------------------
14
+ Benchmarking Expression 2:
15
+ NumPy time (threaded over 32 chunks with 2 threads): 23.862752 seconds
16
+ numexpr time (threaded with re_evaluate over 32 chunks with 2 threads): 2.182058 seconds
17
+ numexpr speedup: 10.94x
18
+ ----------------------------------------
19
+ Benchmarking Expression 3:
20
+ NumPy time (threaded over 32 chunks with 2 threads): 20.594895 seconds
21
+ numexpr time (threaded with re_evaluate over 32 chunks with 2 threads): 2.927881 seconds
22
+ numexpr speedup: 7.03x
23
+ ----------------------------------------
24
+ Benchmarking Expression 4:
25
+ NumPy time (threaded over 32 chunks with 2 threads): 12.834101 seconds
26
+ numexpr time (threaded with re_evaluate over 32 chunks with 2 threads): 5.392480 seconds
27
+ numexpr speedup: 2.38x
28
+ ----------------------------------------
29
+ """
30
+
31
+ import os
32
+
33
+ os.environ["NUMEXPR_NUM_THREADS"] = "16"
34
+ import numpy as np
35
+ import numexpr as ne
36
+ import timeit
37
+ import threading
38
+
39
+ array_size = 10**8
40
+ num_runs = 10
41
+ num_chunks = 32 # Number of chunks
42
+ num_threads = 2 # Number of threads constrained by how many chunks memory can hold
43
+
44
+ a = np.random.rand(array_size).reshape(10**4, -1)
45
+ b = np.random.rand(array_size).reshape(10**4, -1)
46
+ c = np.random.rand(array_size).reshape(10**4, -1)
47
+
48
+ chunk_size = array_size // num_chunks
49
+
50
+ expressions_numpy = [
51
+ lambda a, b, c: a + b * c,
52
+ lambda a, b, c: a**2 + b**2 - 2 * a * b * np.cos(c),
53
+ lambda a, b, c: np.sin(a) + np.log(b) * np.sqrt(c),
54
+ lambda a, b, c: np.exp(a) + np.tan(b) - np.sinh(c),
55
+ ]
56
+
57
+ expressions_numexpr = [
58
+ "a + b * c",
59
+ "a**2 + b**2 - 2 * a * b * cos(c)",
60
+ "sin(a) + log(b) * sqrt(c)",
61
+ "exp(a) + tan(b) - sinh(c)",
62
+ ]
63
+
64
+
65
+ def benchmark_numpy_chunk(func, a, b, c, results, indices):
66
+ for index in indices:
67
+ start = index * chunk_size
68
+ end = (index + 1) * chunk_size
69
+ time_taken = timeit.timeit(
70
+ lambda: func(a[start:end], b[start:end], c[start:end]), number=num_runs
71
+ )
72
+ results.append(time_taken)
73
+
74
+
75
+ def benchmark_numexpr_re_evaluate(expr, a, b, c, results, indices):
76
+ for index in indices:
77
+ start = index * chunk_size
78
+ end = (index + 1) * chunk_size
79
+ if index == 0:
80
+ # Evaluate the first chunk with evaluate
81
+ time_taken = timeit.timeit(
82
+ lambda: ne.evaluate(
83
+ expr,
84
+ local_dict={
85
+ "a": a[start:end],
86
+ "b": b[start:end],
87
+ "c": c[start:end],
88
+ },
89
+ ),
90
+ number=num_runs,
91
+ )
92
+ else:
93
+ # Re-evaluate subsequent chunks with re_evaluate
94
+ time_taken = timeit.timeit(
95
+ lambda: ne.re_evaluate(
96
+ local_dict={"a": a[start:end], "b": b[start:end], "c": c[start:end]}
97
+ ),
98
+ number=num_runs,
99
+ )
100
+ results.append(time_taken)
101
+
102
+
103
+ def run_benchmark_threaded():
104
+ chunk_indices = list(range(num_chunks))
105
+
106
+ for i in range(len(expressions_numpy)):
107
+ print(f"Benchmarking Expression {i+1}:")
108
+
109
+ results_numpy = []
110
+ results_numexpr = []
111
+
112
+ threads_numpy = []
113
+ for j in range(num_threads):
114
+ indices = chunk_indices[j::num_threads] # Distribute chunks across threads
115
+ thread = threading.Thread(
116
+ target=benchmark_numpy_chunk,
117
+ args=(expressions_numpy[i], a, b, c, results_numpy, indices),
118
+ )
119
+ threads_numpy.append(thread)
120
+ thread.start()
121
+
122
+ for thread in threads_numpy:
123
+ thread.join()
124
+
125
+ numpy_time = sum(results_numpy)
126
+ print(
127
+ f"NumPy time (threaded over {num_chunks} chunks with {num_threads} threads): {numpy_time:.6f} seconds"
128
+ )
129
+
130
+ threads_numexpr = []
131
+ for j in range(num_threads):
132
+ indices = chunk_indices[j::num_threads] # Distribute chunks across threads
133
+ thread = threading.Thread(
134
+ target=benchmark_numexpr_re_evaluate,
135
+ args=(expressions_numexpr[i], a, b, c, results_numexpr, indices),
136
+ )
137
+ threads_numexpr.append(thread)
138
+ thread.start()
139
+
140
+ for thread in threads_numexpr:
141
+ thread.join()
142
+
143
+ numexpr_time = sum(results_numexpr)
144
+ print(
145
+ f"numexpr time (threaded with re_evaluate over {num_chunks} chunks with {num_threads} threads): {numexpr_time:.6f} seconds"
146
+ )
147
+ print(f"numexpr speedup: {numpy_time / numexpr_time:.2f}x")
148
+ print("-" * 40)
149
+
150
+
151
+ if __name__ == "__main__":
152
+ run_benchmark_threaded()
@@ -19,7 +19,7 @@ import numpy
19
19
 
20
20
  is_cpu_amd_intel = False # DEPRECATION WARNING: WILL BE REMOVED IN FUTURE RELEASE
21
21
  from numexpr import interpreter, expressions, use_vml
22
- from numexpr.utils import CacheDict
22
+ from numexpr.utils import CacheDict, ContextDict
23
23
 
24
24
  # Declare a double type that does not exist in Python space
25
25
  double = numpy.double
@@ -776,11 +776,9 @@ def getArguments(names, local_dict=None, global_dict=None, _frame_depth: int=2):
776
776
  # Dictionaries for caching variable names and compiled expressions
777
777
  _names_cache = CacheDict(256)
778
778
  _numexpr_cache = CacheDict(256)
779
- _numexpr_last = {}
779
+ _numexpr_last = ContextDict()
780
780
  evaluate_lock = threading.Lock()
781
781
 
782
- # MAYBE: decorate this function to add attributes instead of having the
783
- # _numexpr_last dictionary?
784
782
  def validate(ex: str,
785
783
  local_dict: Optional[Dict] = None,
786
784
  global_dict: Optional[Dict] = None,
@@ -887,7 +885,7 @@ def validate(ex: str,
887
885
  compiled_ex = _numexpr_cache[numexpr_key] = NumExpr(ex, signature, sanitize=sanitize, **context)
888
886
  kwargs = {'out': out, 'order': order, 'casting': casting,
889
887
  'ex_uses_vml': ex_uses_vml}
890
- _numexpr_last = dict(ex=compiled_ex, argnames=names, kwargs=kwargs)
888
+ _numexpr_last.set(ex=compiled_ex, argnames=names, kwargs=kwargs)
891
889
  except Exception as e:
892
890
  return e
893
891
  return None
@@ -1129,7 +1129,7 @@ class test_threading_config(TestCase):
1129
1129
  "if 'NUMEXPR_MAX_THREADS' in os.environ: os.environ.pop('NUMEXPR_MAX_THREADS')",
1130
1130
  "if 'OMP_NUM_THREADS' in os.environ: os.environ.pop('OMP_NUM_THREADS')",
1131
1131
  "import numexpr",
1132
- "assert(numexpr.nthreads <= 8)",
1132
+ f"assert(numexpr.nthreads <= {MAX_THREADS})",
1133
1133
  "exit(0)"])
1134
1134
  subprocess.check_call([sys.executable, '-c', script])
1135
1135
 
@@ -1201,6 +1201,7 @@ class test_threading(TestCase):
1201
1201
  test.join()
1202
1202
 
1203
1203
  def test_multithread(self):
1204
+
1204
1205
  import threading
1205
1206
 
1206
1207
  # Running evaluate() from multiple threads shouldn't crash
@@ -1218,6 +1219,77 @@ class test_threading(TestCase):
1218
1219
  for t in threads:
1219
1220
  t.join()
1220
1221
 
1222
+ def test_thread_safety(self):
1223
+ """
1224
+ Expected output
1225
+
1226
+ When not safe (before the pr this test is commited)
1227
+ AssertionError: Thread-0 failed: result does not match expected
1228
+
1229
+ When safe (after the pr this test is commited)
1230
+ Should pass without failure
1231
+ """
1232
+ import threading
1233
+ import time
1234
+
1235
+ barrier = threading.Barrier(4)
1236
+
1237
+ # Function that each thread will run with different expressions
1238
+ def thread_function(a_value, b_value, expression, expected_result, results, index):
1239
+ validate(expression, local_dict={"a": a_value, "b": b_value})
1240
+ # Wait for all threads to reach this point
1241
+ # such that they all set _numexpr_last
1242
+ barrier.wait()
1243
+
1244
+ # Simulate some work or a context switch delay
1245
+ time.sleep(0.1)
1246
+
1247
+ result = re_evaluate(local_dict={"a": a_value, "b": b_value})
1248
+ results[index] = np.array_equal(result, expected_result)
1249
+
1250
+ def test_thread_safety_with_numexpr():
1251
+ num_threads = 4
1252
+ array_size = 1000000
1253
+
1254
+ expressions = [
1255
+ "a + b",
1256
+ "a - b",
1257
+ "a * b",
1258
+ "a / b"
1259
+ ]
1260
+
1261
+ a_value = [np.full(array_size, i + 1) for i in range(num_threads)]
1262
+ b_value = [np.full(array_size, (i + 1) * 2) for i in range(num_threads)]
1263
+
1264
+ expected_results = [
1265
+ a_value[i] + b_value[i] if expr == "a + b" else
1266
+ a_value[i] - b_value[i] if expr == "a - b" else
1267
+ a_value[i] * b_value[i] if expr == "a * b" else
1268
+ a_value[i] / b_value[i] if expr == "a / b" else None
1269
+ for i, expr in enumerate(expressions)
1270
+ ]
1271
+
1272
+ results = [None] * num_threads
1273
+ threads = []
1274
+
1275
+ # Create and start threads with different expressions
1276
+ for i in range(num_threads):
1277
+ thread = threading.Thread(
1278
+ target=thread_function,
1279
+ args=(a_value[i], b_value[i], expressions[i], expected_results[i], results, i)
1280
+ )
1281
+ threads.append(thread)
1282
+ thread.start()
1283
+
1284
+ for thread in threads:
1285
+ thread.join()
1286
+
1287
+ for i in range(num_threads):
1288
+ if not results[i]:
1289
+ self.fail(f"Thread-{i} failed: result does not match expected")
1290
+
1291
+ test_thread_safety_with_numexpr()
1292
+
1221
1293
 
1222
1294
  # The worker function for the subprocess (needs to be here because Windows
1223
1295
  # has problems pickling nested functions with the multiprocess module :-/)
@@ -13,6 +13,7 @@ log = logging.getLogger(__name__)
13
13
 
14
14
  import os
15
15
  import subprocess
16
+ import contextvars
16
17
 
17
18
  from numexpr.interpreter import _set_num_threads, _get_num_threads, MAX_THREADS
18
19
  from numexpr import use_vml
@@ -226,3 +227,83 @@ class CacheDict(dict):
226
227
  super(CacheDict, self).__delitem__(k)
227
228
  super(CacheDict, self).__setitem__(key, value)
228
229
 
230
+
231
+ class ContextDict:
232
+ """
233
+ A context aware version dictionary
234
+ """
235
+ def __init__(self):
236
+ self._context_data = contextvars.ContextVar('context_data', default={})
237
+
238
+ def set(self, key=None, value=None, **kwargs):
239
+ data = self._context_data.get().copy()
240
+
241
+ if key is not None:
242
+ data[key] = value
243
+
244
+ for k, v in kwargs.items():
245
+ data[k] = v
246
+
247
+ self._context_data.set(data)
248
+
249
+ def get(self, key, default=None):
250
+ data = self._context_data.get()
251
+ return data.get(key, default)
252
+
253
+ def delete(self, key):
254
+ data = self._context_data.get().copy()
255
+ if key in data:
256
+ del data[key]
257
+ self._context_data.set(data)
258
+
259
+ def clear(self):
260
+ self._context_data.set({})
261
+
262
+ def all(self):
263
+ return self._context_data.get()
264
+
265
+ def update(self, *args, **kwargs):
266
+ data = self._context_data.get().copy()
267
+
268
+ if args:
269
+ if len(args) > 1:
270
+ raise TypeError(f"update() takes at most 1 positional argument ({len(args)} given)")
271
+ other = args[0]
272
+ if isinstance(other, dict):
273
+ data.update(other)
274
+ else:
275
+ for k, v in other:
276
+ data[k] = v
277
+
278
+ data.update(kwargs)
279
+ self._context_data.set(data)
280
+
281
+ def keys(self):
282
+ return self._context_data.get().keys()
283
+
284
+ def values(self):
285
+ return self._context_data.get().values()
286
+
287
+ def items(self):
288
+ return self._context_data.get().items()
289
+
290
+ def __getitem__(self, key):
291
+ return self.get(key)
292
+
293
+ def __setitem__(self, key, value):
294
+ self.set(key, value)
295
+
296
+ def __delitem__(self, key):
297
+ self.delete(key)
298
+
299
+ def __contains__(self, key):
300
+ return key in self._context_data.get()
301
+
302
+ def __len__(self):
303
+ return len(self._context_data.get())
304
+
305
+ def __iter__(self):
306
+ return iter(self._context_data.get())
307
+
308
+ def __repr__(self):
309
+ return repr(self._context_data.get())
@@ -0,0 +1,5 @@
1
+ # THIS FILE IS GENERATED BY `setup.py`
2
+ __version__ = '2.10.2'
3
+ version = '2.10.2'
4
+ numpy_build_version = '2.1.3'
5
+ platform_machine = 'AMD64'
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.1
2
+ Name: numexpr
3
+ Version: 2.10.2
4
+ Summary: Fast numerical expression evaluator for NumPy
5
+ Author-email: "David M. Cooke, Francesc Alted, and others" <blosc@blosc.org>
6
+ Maintainer-email: Blosc Development Team <blosc@blosc.org>
7
+ Project-URL: homepage, https://github.com/pydata/numexpr
8
+ Project-URL: documentation, https://numexpr.readthedocs.io
9
+ Project-URL: repository, https://github.com/pydata/numexpr
10
+ Classifier: Development Status :: 6 - Mature
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Information Technology
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: Unix
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/x-rst
26
+ License-File: LICENSE.txt
27
+ License-File: AUTHORS.txt
28
+ Requires-Dist: numpy>=1.23.0
29
+
30
+ ======================================================
31
+ NumExpr: Fast numerical expression evaluator for NumPy
32
+ ======================================================
33
+
34
+ :Author: David M. Cooke, Francesc Alted, and others.
35
+ :Maintainer: Francesc Alted
36
+ :Contact: faltet@gmail.com
37
+ :URL: https://github.com/pydata/numexpr
38
+ :Documentation: http://numexpr.readthedocs.io/en/latest/
39
+ :GitHub Actions: |actions|
40
+ :PyPi: |version|
41
+ :DOI: |doi|
42
+ :readthedocs: |docs|
43
+
44
+ .. |actions| image:: https://github.com/pydata/numexpr/workflows/Build/badge.svg
45
+ :target: https://github.com/pydata/numexpr/actions
46
+ .. |travis| image:: https://travis-ci.org/pydata/numexpr.png?branch=master
47
+ :target: https://travis-ci.org/pydata/numexpr
48
+ .. |docs| image:: https://readthedocs.org/projects/numexpr/badge/?version=latest
49
+ :target: http://numexpr.readthedocs.io/en/latest
50
+ .. |doi| image:: https://zenodo.org/badge/doi/10.5281/zenodo.2483274.svg
51
+ :target: https://doi.org/10.5281/zenodo.2483274
52
+ .. |version| image:: https://img.shields.io/pypi/v/numexpr
53
+ :target: https://pypi.python.org/pypi/numexpr
54
+
55
+
56
+ What is NumExpr?
57
+ ----------------
58
+
59
+ NumExpr is a fast numerical expression evaluator for NumPy. With it,
60
+ expressions that operate on arrays (like :code:`'3*a+4*b'`) are accelerated
61
+ and use less memory than doing the same calculation in Python.
62
+
63
+ In addition, its multi-threaded capabilities can make use of all your
64
+ cores -- which generally results in substantial performance scaling compared
65
+ to NumPy.
66
+
67
+ Last but not least, numexpr can make use of Intel's VML (Vector Math
68
+ Library, normally integrated in its Math Kernel Library, or MKL).
69
+ This allows further acceleration of transcendent expressions.
70
+
71
+
72
+ How NumExpr achieves high performance
73
+ -------------------------------------
74
+
75
+ The main reason why NumExpr achieves better performance than NumPy is
76
+ that it avoids allocating memory for intermediate results. This
77
+ results in better cache utilization and reduces memory access in
78
+ general. Due to this, NumExpr works best with large arrays.
79
+
80
+ NumExpr parses expressions into its own op-codes that are then used by
81
+ an integrated computing virtual machine. The array operands are split
82
+ into small chunks that easily fit in the cache of the CPU and passed
83
+ to the virtual machine. The virtual machine then applies the
84
+ operations on each chunk. It's worth noting that all temporaries and
85
+ constants in the expression are also chunked. Chunks are distributed among
86
+ the available cores of the CPU, resulting in highly parallelized code
87
+ execution.
88
+
89
+ The result is that NumExpr can get the most of your machine computing
90
+ capabilities for array-wise computations. Common speed-ups with regard
91
+ to NumPy are usually between 0.95x (for very simple expressions like
92
+ :code:`'a + 1'`) and 4x (for relatively complex ones like :code:`'a*b-4.1*a > 2.5*b'`),
93
+ although much higher speed-ups can be achieved for some functions and complex
94
+ math operations (up to 15x in some cases).
95
+
96
+ NumExpr performs best on matrices that are too large to fit in L1 CPU cache.
97
+ In order to get a better idea on the different speed-ups that can be achieved
98
+ on your platform, run the provided benchmarks.
99
+
100
+ Installation
101
+ ------------
102
+
103
+ From wheels
104
+ ^^^^^^^^^^^
105
+
106
+ NumExpr is available for install via `pip` for a wide range of platforms and
107
+ Python versions (which may be browsed at: https://pypi.org/project/numexpr/#files).
108
+ Installation can be performed as::
109
+
110
+ pip install numexpr
111
+
112
+ If you are using the Anaconda or Miniconda distribution of Python you may prefer
113
+ to use the `conda` package manager in this case::
114
+
115
+ conda install numexpr
116
+
117
+ From Source
118
+ ^^^^^^^^^^^
119
+
120
+ On most \*nix systems your compilers will already be present. However if you
121
+ are using a virtual environment with a substantially newer version of Python than
122
+ your system Python you may be prompted to install a new version of `gcc` or `clang`.
123
+
124
+ For Windows, you will need to install the Microsoft Visual C++ Build Tools
125
+ (which are free) first. The version depends on which version of Python you have
126
+ installed:
127
+
128
+ https://wiki.python.org/moin/WindowsCompilers
129
+
130
+ For Python 3.6+ simply installing the latest version of MSVC build tools should
131
+ be sufficient. Note that wheels found via pip do not include MKL support. Wheels
132
+ available via `conda` will have MKL, if the MKL backend is used for NumPy.
133
+
134
+ See `requirements.txt` for the required version of NumPy.
135
+
136
+ NumExpr is built in the standard Python way::
137
+
138
+ python setup.py build install
139
+
140
+ You can test `numexpr` with::
141
+
142
+ python -c "import numexpr; numexpr.test()"
143
+
144
+ Do not test NumExpr in the source directory or you will generate import errors.
145
+
146
+ Enable Intel® MKL support
147
+ ^^^^^^^^^^^^^^^^^^^^^^^^^
148
+
149
+ NumExpr includes support for Intel's MKL library. This may provide better
150
+ performance on Intel architectures, mainly when evaluating transcendental
151
+ functions (trigonometrical, exponential, ...).
152
+
153
+ If you have Intel's MKL, copy the `site.cfg.example` that comes with the
154
+ distribution to `site.cfg` and edit the latter file to provide correct paths to
155
+ the MKL libraries in your system. After doing this, you can proceed with the
156
+ usual building instructions listed above.
157
+
158
+ Pay attention to the messages during the building process in order to know
159
+ whether MKL has been detected or not. Finally, you can check the speed-ups on
160
+ your machine by running the `bench/vml_timing.py` script (you can play with
161
+ different parameters to the `set_vml_accuracy_mode()` and `set_vml_num_threads()`
162
+ functions in the script so as to see how it would affect performance).
163
+
164
+ Usage
165
+ -----
166
+
167
+ ::
168
+
169
+ >>> import numpy as np
170
+ >>> import numexpr as ne
171
+
172
+ >>> a = np.arange(1e6) # Choose large arrays for better speedups
173
+ >>> b = np.arange(1e6)
174
+
175
+ >>> ne.evaluate("a + 1") # a simple expression
176
+ array([ 1.00000000e+00, 2.00000000e+00, 3.00000000e+00, ...,
177
+ 9.99998000e+05, 9.99999000e+05, 1.00000000e+06])
178
+
179
+ >>> ne.evaluate("a * b - 4.1 * a > 2.5 * b") # a more complex one
180
+ array([False, False, False, ..., True, True, True], dtype=bool)
181
+
182
+ >>> ne.evaluate("sin(a) + arcsinh(a/b)") # you can also use functions
183
+ array([ NaN, 1.72284457, 1.79067101, ..., 1.09567006,
184
+ 0.17523598, -0.09597844])
185
+
186
+ >>> s = np.array([b'abba', b'abbb', b'abbcdef'])
187
+ >>> ne.evaluate("b'abba' == s") # string arrays are supported too
188
+ array([ True, False, False], dtype=bool)
189
+
190
+
191
+ Documentation
192
+ -------------
193
+
194
+ Please see the official documentation at `numexpr.readthedocs.io <https://numexpr.readthedocs.io>`_.
195
+ Included is a user guide, benchmark results, and the reference API.
196
+
197
+
198
+ Authors
199
+ -------
200
+
201
+ Please see `AUTHORS.txt <https://github.com/pydata/numexpr/blob/master/AUTHORS.txt>`_.
202
+
203
+
204
+ License
205
+ -------
206
+
207
+ NumExpr is distributed under the `MIT <http://www.opensource.org/licenses/mit-license.php>`_ license.
208
+
209
+
210
+ .. Local Variables:
211
+ .. mode: text
212
+ .. coding: utf-8
213
+ .. fill-column: 70
214
+ .. End:
@@ -12,6 +12,7 @@ site.cfg.example
12
12
  bench/boolean_timing.py
13
13
  bench/issue-36.py
14
14
  bench/issue-47.py
15
+ bench/large_array_vs_numpy.py
15
16
  bench/multidim.py
16
17
  bench/poly.py
17
18
  bench/timing.py
@@ -12,6 +12,7 @@ include = ["numexpr"]
12
12
  [project]
13
13
  name = "numexpr"
14
14
  description = "Fast numerical expression evaluator for NumPy"
15
+ readme = "README.rst"
15
16
  authors = [{name = "David M. Cooke, Francesc Alted, and others", email = "blosc@blosc.org"}]
16
17
  maintainers = [{ name = "Blosc Development Team", email = "blosc@blosc.org"}]
17
18
  classifiers = [
@@ -31,6 +31,10 @@ with open('numexpr/version.py', 'w') as fh:
31
31
  pass
32
32
  fh.write("platform_machine = '%s'\n" % platform.machine())
33
33
 
34
+ # Read the contents of your README file
35
+ with open('README.rst', encoding='utf-8') as f:
36
+ long_description = f.read()
37
+
34
38
  lib_dirs = []
35
39
  inc_dirs = [np.get_include()]
36
40
  libs = [] # Pre-built libraries ONLY, like python36.so
@@ -101,6 +105,8 @@ def setup_package():
101
105
 
102
106
  metadata = dict(
103
107
  version=version,
108
+ long_description=long_description,
109
+ long_description_content_type='text/x-rst',
104
110
  install_requires=requirements,
105
111
  libraries=clibs,
106
112
  ext_modules=[
numexpr-2.10.1/PKG-INFO DELETED
@@ -1,27 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: numexpr
3
- Version: 2.10.1
4
- Summary: Fast numerical expression evaluator for NumPy
5
- Author-email: "David M. Cooke, Francesc Alted, and others" <blosc@blosc.org>
6
- Maintainer-email: Blosc Development Team <blosc@blosc.org>
7
- Project-URL: homepage, https://github.com/pydata/numexpr
8
- Project-URL: documentation, https://numexpr.readthedocs.io
9
- Project-URL: repository, https://github.com/pydata/numexpr
10
- Classifier: Development Status :: 6 - Mature
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Intended Audience :: Information Technology
13
- Classifier: Intended Audience :: Science/Research
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Programming Language :: Python
16
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
- Classifier: Operating System :: Microsoft :: Windows
18
- Classifier: Operating System :: Unix
19
- Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.9
21
- Classifier: Programming Language :: Python :: 3.10
22
- Classifier: Programming Language :: Python :: 3.11
23
- Classifier: Programming Language :: Python :: 3.12
24
- Requires-Python: >=3.9
25
- License-File: LICENSE.txt
26
- License-File: AUTHORS.txt
27
- Requires-Dist: numpy>=1.23.0
numexpr-2.10.1/VERSION DELETED
@@ -1 +0,0 @@
1
- 2.10.1
@@ -1,5 +0,0 @@
1
- # THIS FILE IS GENERATED BY `setup.py`
2
- __version__ = '2.10.1'
3
- version = '2.10.1'
4
- numpy_build_version = '2.0.0'
5
- platform_machine = 'AMD64'
@@ -1,27 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: numexpr
3
- Version: 2.10.1
4
- Summary: Fast numerical expression evaluator for NumPy
5
- Author-email: "David M. Cooke, Francesc Alted, and others" <blosc@blosc.org>
6
- Maintainer-email: Blosc Development Team <blosc@blosc.org>
7
- Project-URL: homepage, https://github.com/pydata/numexpr
8
- Project-URL: documentation, https://numexpr.readthedocs.io
9
- Project-URL: repository, https://github.com/pydata/numexpr
10
- Classifier: Development Status :: 6 - Mature
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Intended Audience :: Information Technology
13
- Classifier: Intended Audience :: Science/Research
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Programming Language :: Python
16
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
- Classifier: Operating System :: Microsoft :: Windows
18
- Classifier: Operating System :: Unix
19
- Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.9
21
- Classifier: Programming Language :: Python :: 3.10
22
- Classifier: Programming Language :: Python :: 3.11
23
- Classifier: Programming Language :: Python :: 3.12
24
- Requires-Python: >=3.9
25
- License-File: LICENSE.txt
26
- License-File: AUTHORS.txt
27
- Requires-Dist: numpy>=1.23.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes