numexpr 2.10.2__tar.gz → 2.12.0__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 (61) hide show
  1. numexpr-2.12.0/ADDFUNCS.rst +222 -0
  2. {numexpr-2.10.2 → numexpr-2.12.0}/ANNOUNCE.rst +12 -11
  3. {numexpr-2.10.2 → numexpr-2.12.0}/AUTHORS.txt +1 -1
  4. numexpr-2.12.0/LICENSES/cpuinfo.txt +31 -0
  5. {numexpr-2.10.2 → numexpr-2.12.0}/PKG-INFO +26 -6
  6. {numexpr-2.10.2 → numexpr-2.12.0}/README.rst +18 -0
  7. {numexpr-2.10.2 → numexpr-2.12.0}/RELEASE_NOTES.rst +32 -1
  8. numexpr-2.12.0/VERSION +1 -0
  9. {numexpr-2.10.2 → numexpr-2.12.0}/bench/boolean_timing.py +2 -0
  10. numexpr-2.12.0/bench/free_threading.py +171 -0
  11. {numexpr-2.10.2 → numexpr-2.12.0}/bench/issue-36.py +6 -3
  12. {numexpr-2.10.2 → numexpr-2.12.0}/bench/issue-47.py +1 -0
  13. {numexpr-2.10.2 → numexpr-2.12.0}/bench/large_array_vs_numpy.py +4 -2
  14. {numexpr-2.10.2 → numexpr-2.12.0}/bench/multidim.py +4 -3
  15. {numexpr-2.10.2 → numexpr-2.12.0}/bench/poly.py +3 -1
  16. {numexpr-2.10.2 → numexpr-2.12.0}/bench/timing.py +4 -1
  17. {numexpr-2.10.2 → numexpr-2.12.0}/bench/unaligned-simple.py +3 -0
  18. {numexpr-2.10.2 → numexpr-2.12.0}/bench/varying-expr.py +3 -0
  19. {numexpr-2.10.2 → numexpr-2.12.0}/bench/vml_timing.py +3 -0
  20. {numexpr-2.10.2 → numexpr-2.12.0}/bench/vml_timing2.py +4 -1
  21. {numexpr-2.10.2 → numexpr-2.12.0}/bench/vml_timing3.py +3 -1
  22. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/__init__.py +10 -10
  23. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/cpuinfo.py +5 -3
  24. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/expressions.py +9 -2
  25. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/functions.hpp +28 -0
  26. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/interp_body.cpp +22 -4
  27. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/interpreter.cpp +66 -11
  28. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/interpreter.hpp +13 -1
  29. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/module.cpp +12 -3
  30. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/module.hpp +2 -2
  31. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/msvc_function_stubs.hpp +27 -3
  32. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/necompiler.py +82 -75
  33. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/numexpr_config.hpp +12 -1
  34. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/numexpr_object.cpp +0 -1
  35. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/opcodes.hpp +39 -26
  36. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/tests/__init__.py +1 -1
  37. numexpr-2.12.0/numexpr/tests/conftest.py +17 -0
  38. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/tests/test_numexpr.py +172 -128
  39. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/utils.py +13 -11
  40. numexpr-2.12.0/numexpr/version.py +5 -0
  41. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/win32/stdint.h +6 -6
  42. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr.egg-info/PKG-INFO +26 -6
  43. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr.egg-info/SOURCES.txt +4 -0
  44. {numexpr-2.10.2 → numexpr-2.12.0}/pyproject.toml +20 -6
  45. {numexpr-2.10.2 → numexpr-2.12.0}/setup.py +6 -5
  46. numexpr-2.10.2/VERSION +0 -1
  47. numexpr-2.10.2/numexpr/version.py +0 -5
  48. {numexpr-2.10.2 → numexpr-2.12.0}/LICENSE.txt +0 -0
  49. {numexpr-2.10.2 → numexpr-2.12.0}/MANIFEST.in +0 -0
  50. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/complex_functions.hpp +0 -0
  51. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/missing_posix_functions.hpp +0 -0
  52. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/numexpr_object.hpp +0 -0
  53. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/str-two-way.hpp +0 -0
  54. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/win32/pthread.c +0 -0
  55. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr/win32/pthread.h +0 -0
  56. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr.egg-info/dependency_links.txt +0 -0
  57. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr.egg-info/requires.txt +0 -0
  58. {numexpr-2.10.2 → numexpr-2.12.0}/numexpr.egg-info/top_level.txt +0 -0
  59. {numexpr-2.10.2 → numexpr-2.12.0}/requirements.txt +0 -0
  60. {numexpr-2.10.2 → numexpr-2.12.0}/setup.cfg +0 -0
  61. {numexpr-2.10.2 → numexpr-2.12.0}/site.cfg.example +0 -0
@@ -0,0 +1,222 @@
1
+ Functions and Function signatures
2
+ =================================
3
+
4
+ Adding functions
5
+ ----------------
6
+
7
+ In order to add new functions to ``numexpr``, currently it is necessary to edit several files. Consider adding a function
8
+ ``out_type myfunc(arg_type)``.
9
+
10
+ * ``numexpr/expressions.py``
11
+ Add ``'myfunc': func(numpy.myfunc, out_dtype),`` to the dict of functions, ``functions = {...``. If the return type of the function is ``bool``, add
12
+ the function to the list ``if opcode in ("isnan", "isfinite"):`` in the ``__init__`` function of the ``FuncNode`` class.
13
+ In the future it might be nice to refactor this function since it sets the output type based on the type of the inputs in general.
14
+
15
+ * ``numexpr/necompiler.py``
16
+ Add ``"myfunc"`` to the list of functions:
17
+
18
+ .. code-block:: python3
19
+
20
+ "floor",
21
+ "isnan",
22
+ "isfinite",
23
+ "myfunc"
24
+ ]
25
+
26
+ * ``numexpr/functions.hpp``
27
+ Find the correct function signature ``FUNC_OA`` where ``O`` is the return type, and ``A`` the argument type(s). For example, if the function
28
+ is ``double myfunc(double)``, one should edit within the ``FUNC_DD`` clause. If you cannot find your function signature you will have to add it,
29
+ following the template of the other functions.
30
+ Most likely, you will want to add support for several function signatures (e.g. double -> bool and float -> bool) and so you will have to add the
31
+ function in two clauses. If your function has a float input, you will see that there are 5 arguments in the
32
+ ``FUNC_OA`` macro, and you will have to add ``myfunc2`` here is order to compile on MSVC machines (i.e. Windows, see following).
33
+ Example:
34
+
35
+ .. code-block:: cpp
36
+ :emphasize-lines: 6, 20
37
+
38
+ #ifndef FUNC_DD
39
+ #define ELIDE_FUNC_DD
40
+ #define FUNC_DD(...)
41
+ #endif
42
+ ...
43
+ FUNC_DD(FUNC_MYFUNC_DD, "myfunc_dd", myfunc, vdMyfunc)
44
+ FUNC_DD(FUNC_DD_LAST, NULL, NULL, NULL)
45
+ #ifdef ELIDE_FUNC_DD
46
+ #undef ELIDE_FUNC_DD
47
+ #undef FUNC_DD
48
+ #endif
49
+
50
+ ...
51
+
52
+ #ifndef FUNC_FF
53
+ #define ELIDE_FUNC_FF
54
+ #define FUNC_FF(...)
55
+ #endif
56
+ ...
57
+ FUNC_FF(FUNC_MYFUNC_FF, "myfunc_ff", myfuncf, myfuncf2, vfMyfunc)
58
+ FUNC_FF(FUNC_FF_LAST, NULL, NULL, NULL, NULL)
59
+ #ifdef ELIDE_FUNC_FF
60
+ #undef ELIDE_FUNC_FF
61
+ #undef FUNC_FF
62
+ #endif
63
+
64
+ * ``numexpr/msvc_function_stubs.hpp``
65
+ In order to support float arguments, due to oddities of MSVC, you have to provide explicit support for your function in this file.
66
+ Add ``#define myfuncf(x) ((float)floor((double)(x)))`` (if your function is float -> float) to the ``#if`` clause at the top of the file
67
+ which is for old versions of MSVC which did not have support for single precision fucntions. Then in the body, add an inline function
68
+
69
+ .. code-block:: cpp
70
+
71
+ inline float myfuncf2(float x) {
72
+ return myfuncf(x);
73
+ }
74
+
75
+ This is the function that appears as the ``f_win32`` parameter in ``functions.hpp``.
76
+
77
+ * ``numexpr/tests/test_numexpr.py``
78
+ Don't forget to add a test for your function!
79
+
80
+ Adding function signatures
81
+ --------------------------
82
+ It may so happen that you cannot find your desired function signature in ``functions.hpp``. This means you will have to add it yourself!
83
+ This involves editing a few more files. In addition, there may be certain bespoke changes, specific to the function signature
84
+ that you may have to make (see Notes, below)
85
+
86
+ * ``numexpr/functions.hpp``
87
+ Firstly, add clause(s) for your function signature. For example, if the function signature is ``bool(double)`` and ``bool(float)``, add
88
+ ``FUNC_BD`` and ``FUNC_BF`` clauses (in the latter case you will need the macro to take 5 arguments for MSVC-compatibility.)
89
+
90
+ .. code-block:: cpp
91
+
92
+ #ifndef FUNC_BD
93
+ #define ELIDE_FUNC_BD
94
+ #define FUNC_BD(...)
95
+ #endif
96
+ ...
97
+ FUNC_BD(FUNC_BD_LAST, NULL, NULL, NULL)
98
+ #ifdef ELIDE_FUNC_BD
99
+ #undef ELIDE_FUNC_BD
100
+ #undef FUNC_BD
101
+ #endif
102
+
103
+ #ifndef FUNC_BF
104
+ #define ELIDE_FUNC_BF
105
+ #define FUNC_BF(...)
106
+ #endif
107
+ ...
108
+ FUNC_BF(FUNC_BF_LAST, NULL, NULL, NULL, NULL)
109
+ #ifdef ELIDE_FUNC_BF
110
+ #undef ELIDE_FUNC_BF
111
+ #undef FUNC_BF
112
+ #endif
113
+
114
+ The ultimate source of the functions in the macro ``FUNC_BF(...)`` are the headers included in ``numexpr/interpreter.cpp`` (in particular
115
+ ``numexpr/numexpr_config.hpp``, which can be used to overwrite ``<math.h>`` functions), so the functions should be available from there.
116
+
117
+ * ``numexpr/interp_body.cpp``
118
+ Add case support for OPCODES associated to your new function signatures via e.g. ``case OP_FUNC_BFN`` and ``case OP_FUNC_BDN``, following
119
+ the framework suggested by the other functions:
120
+
121
+ .. code-block:: cpp
122
+
123
+ case OP_FUNC_BFN:
124
+ #ifdef USE_VML
125
+ VEC_ARG1_VML(functions_bf_vml[arg2](BLOCK_SIZE,
126
+ (float*)x1, (bool*)dest));
127
+ #else
128
+ VEC_ARG1(b_dest = functions_bf[arg2](f1));
129
+ #endif
130
+
131
+ Note that it is important that the out variable matches the output type of the function (i.e. ``b_dest`` for bool, ``f_dest`` for float etc.)
132
+
133
+ * ``numexpr/interpreter.hpp``
134
+ Add clauses to read the ``functions.hpp`` macros correctly
135
+
136
+ .. code-block:: cpp
137
+
138
+ enum FuncBFCodes {
139
+ #define FUNC_BF(fop, ...) fop,
140
+ #include "functions.hpp"
141
+ #undef FUNC_BF
142
+ };
143
+
144
+ * ``numexpr/interpreter.cpp``
145
+ Add clauses to generate the FUNC_CODES from the ``functions.hpp`` header, making sure to include clauses for ``_WIN32`` and
146
+ ``VML`` as necessary accoridng to the framework suggested by the other functions.
147
+
148
+ .. code-block:: cpp
149
+
150
+ typedef bool (*FuncBFPtr)(float);
151
+ #ifdef _WIN32
152
+ FuncBFPtr functions_bf[] = {
153
+ #define FUNC_BF(fop, s, f, f_win32, ...) f_win32,
154
+ #include "functions.hpp"
155
+ #undef FUNC_BF
156
+ };
157
+ #else
158
+ FuncBFPtr functions_bf[] = {
159
+ #define FUNC_BF(fop, s, f, ...) f,
160
+ #include "functions.hpp"
161
+ #undef FUNC_BF
162
+ };
163
+ #endif
164
+
165
+ #ifdef USE_VML
166
+ typedef void (*FuncBFPtr_vml)(MKL_INT, const float*, bool*);
167
+ FuncBFPtr_vml functions_bf_vml[] = {
168
+ #define FUNC_BF(fop, s, f, f_win32, f_vml) f_vml,
169
+ #include "functions.hpp"
170
+ #undef FUNC_BF
171
+ };
172
+ #endif
173
+
174
+ Add case handling to the ``check_program`` function
175
+
176
+ .. code-block:: cpp
177
+
178
+ else if (op == OP_FUNC_BDN) {
179
+ if (arg < 0 || arg >= FUNC_BD_LAST) {
180
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
181
+ return -1;
182
+ }
183
+ }
184
+ else if (op == OP_FUNC_BFN) {
185
+ if (arg < 0 || arg >= FUNC_BF_LAST) {
186
+ PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc);
187
+ return -1;
188
+ }
189
+ }
190
+
191
+ * ``numexpr/module.cpp``
192
+ Add code here to define the ``FUNC_OA`` macros you require
193
+
194
+ .. code-block:: cpp
195
+
196
+ #define FUNC_BF(name, sname, ...) add_func(name, sname);
197
+ #define FUNC_BD(name, sname, ...) add_func(name, sname);
198
+ ...
199
+ #include "functions.hpp"
200
+ ...
201
+ #undef FUNC_BD
202
+ #undef FUNC_BF
203
+
204
+ * ``numexpr/opcodes.hpp``
205
+ Finally, add the ``OP_FUNC_BDN`` etc. codes here. It is necessary for the OPCODES in the file to be in (ascending order) with
206
+ ``NOOP`` as 0 and ``OP_LAST`` as the largest number. Secondly, all reduction OPCODES must appear last. Hence, after adding your
207
+ function signatures (just before the reduction OPCODES) it is necessary to increment all succeeding OPCODES.
208
+
209
+ .. code-block:: cpp
210
+
211
+ OPCODE(106, OP_FUNC_BDN, "func_bdn", Tb, Td, Tn, T0)
212
+ OPCODE(107, OP_FUNC_BFN, "func_bfn", Tb, Tf, Tn, T0)
213
+
214
+ Notes
215
+ -----
216
+ In many cases this process will not be very smooth since one relies on the internal C/C++ standard functions (which can be fussy, to varying degrees on different platforms). Some common gotchas are then:
217
+
218
+ * OPCODES are currently only supported up to 255 - if it becomes necessary to increment further, one will have to change the ``latin_1`` encoding used in ``quadrupleToString`` in ``necompiler.py``. In addition, since the OPCDE table is assumed to be of type ``unsigned char`` the ``get_return_sig`` function in ``numexpr/interpreter.cpp`` may have to be changed (possibly other changes too).
219
+
220
+ * Depending on the new function signature (above all if the out type is different to the input types), one may have to edit the ``__init__`` function in the ``FuncNode`` class in ``expressions.py``.
221
+
222
+ * Depending on MSVC support, namespace clashes, casting problems, it may be necessary to make various changes to ``numexpr/numexpr_config.hpp`` and ``numexpr/msvc_function_stubs.hpp``. For example, in PR #523, non-clashing wrappers were introduced for ``isnan`` and ``isfinite`` since the float versions ``isnanf, isfinitef`` were inconsistently defined (and output ints) - depending on how strict the platform interpreter is, the implicit cast from int to bool was acceptable or not for example. In addition, the base functions were in different namespaces or had different names across platforms.
@@ -1,28 +1,29 @@
1
1
  =========================
2
- Announcing NumExpr 2.10.2
2
+ Announcing NumExpr 2.12.0
3
3
  =========================
4
4
 
5
5
  Hi everyone,
6
6
 
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.
7
+ NumExpr 2.12.0 comes with new isnan/isfinite/isinf functions.
8
+ Most importantly, we have added instructions for adding new functions
9
+ to the virtual machine. See ADDFUNCS.rst for more details. Thanks to
10
+ Luke Shaw for these contributions.
11
11
 
12
12
  Project documentation is available at:
13
13
 
14
14
  http://numexpr.readthedocs.io/
15
15
 
16
- Changes from 2.10.1 to 2.10.2
16
+ Changes from 2.11.0 to 2.12.0
17
17
  -----------------------------
18
18
 
19
- * Better support for CPUs that do not have a power of 2 number of
20
- cores. See #479 and #490. Thanks to @avalentino.
19
+ * Added isnan/isfinite/isinf functions. Thanks to Luke Shaw.
21
20
 
22
- * Allow numexpr to run with the multithreading package in Python.
23
- See PR #496. Thanks to @emmaai
21
+ * New instructions for adding new functions to the virtual machine.
22
+ They are available at ADDFUNCS.rst. Thanks to Luke Shaw.
24
23
 
25
- * Wheels for Python 3.13 are now provided.
24
+ * We are distributing binary wheels for Python 3.14 and 3.14t now.
25
+
26
+ * We are distributing musllinux wheels too! Thanks to Clément Robert.
26
27
 
27
28
  What's Numexpr?
28
29
  ---------------
@@ -23,7 +23,7 @@ Google Inc. contributed bug fixes.
23
23
 
24
24
  David Cox improved readability of the Readme.
25
25
 
26
- Robert A. McLeod contributed bug fixes and ported the documentation to
26
+ Robert A. McLeod contributed bug fixes and ported the documentation to
27
27
  numexpr.readthedocs.io. He has served as the maintainer of the package
28
28
  since 2016 to 2023.
29
29
 
@@ -0,0 +1,31 @@
1
+ Copyright statement for `cpuinfo` module.
2
+
3
+ Copyright 2002 Pearu Peterson all rights reserved,
4
+ Pearu Peterson <pearu@cens.ioc.ee>
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
10
+ notice, this list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above
13
+ copyright notice, this list of conditions and the following
14
+ disclaimer in the documentation and/or other materials provided
15
+ with the distribution.
16
+
17
+ * Neither the name of Pearu Peterson nor the names of its
18
+ contributors may be used to endorse or promote products derived
19
+ from this software without specific prior written permission.
20
+
21
+ HIS SOFTWARE IS PROVIDED BY PEARU PETERSON ''AS IS'' AND ANY EXPRESS
22
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ DISCLAIMED. IN NO EVENT SHALL PEARU PETERSON BE LIABLE FOR ANY DIRECT,
25
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
+ POSSIBILITY OF SUCH DAMAGE.
@@ -1,9 +1,10 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: numexpr
3
- Version: 2.10.2
3
+ Version: 2.12.0
4
4
  Summary: Fast numerical expression evaluator for NumPy
5
5
  Author-email: "David M. Cooke, Francesc Alted, and others" <blosc@blosc.org>
6
6
  Maintainer-email: Blosc Development Team <blosc@blosc.org>
7
+ License-Expression: MIT
7
8
  Project-URL: homepage, https://github.com/pydata/numexpr
8
9
  Project-URL: documentation, https://numexpr.readthedocs.io
9
10
  Project-URL: repository, https://github.com/pydata/numexpr
@@ -11,21 +12,22 @@ Classifier: Development Status :: 6 - Mature
11
12
  Classifier: Intended Audience :: Developers
12
13
  Classifier: Intended Audience :: Information Technology
13
14
  Classifier: Intended Audience :: Science/Research
14
- Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Programming Language :: Python
16
16
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
17
  Classifier: Operating System :: Microsoft :: Windows
18
18
  Classifier: Operating System :: Unix
19
19
  Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
23
22
  Classifier: Programming Language :: Python :: 3.12
24
- Requires-Python: >=3.9
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Requires-Python: >=3.10
25
26
  Description-Content-Type: text/x-rst
26
27
  License-File: LICENSE.txt
27
- License-File: AUTHORS.txt
28
+ License-File: LICENSES/cpuinfo.txt
28
29
  Requires-Dist: numpy>=1.23.0
30
+ Dynamic: license-file
29
31
 
30
32
  ======================================================
31
33
  NumExpr: Fast numerical expression evaluator for NumPy
@@ -188,6 +190,24 @@ Usage
188
190
  array([ True, False, False], dtype=bool)
189
191
 
190
192
 
193
+ Free-threading support
194
+ ----------------------
195
+ Starting on CPython 3.13 onwards there is a new distribution that disables the
196
+ Global Interpreter Lock (GIL) altogether, thus increasing the performance yields
197
+ under multi-threaded conditions on a single interpreter, as opposed to having to use
198
+ multiprocessing.
199
+
200
+ Whilst numexpr has been demonstrated to work under free-threaded
201
+ CPython, considerations need to be taken when using numexpr native parallel
202
+ implementation vs using Python threads directly in order to prevent oversubscription,
203
+ we recommend either using the main CPython interpreter thread to spawn multiple C threads
204
+ using the parallel numexpr API, or spawning multiple CPython threads that do not use
205
+ the parallel API.
206
+
207
+ For more information about free-threaded CPython, we recommend visiting the following
208
+ `community Wiki <https://py-free-threading.github.io/>`
209
+
210
+
191
211
  Documentation
192
212
  -------------
193
213
 
@@ -159,6 +159,24 @@ Usage
159
159
  array([ True, False, False], dtype=bool)
160
160
 
161
161
 
162
+ Free-threading support
163
+ ----------------------
164
+ Starting on CPython 3.13 onwards there is a new distribution that disables the
165
+ Global Interpreter Lock (GIL) altogether, thus increasing the performance yields
166
+ under multi-threaded conditions on a single interpreter, as opposed to having to use
167
+ multiprocessing.
168
+
169
+ Whilst numexpr has been demonstrated to work under free-threaded
170
+ CPython, considerations need to be taken when using numexpr native parallel
171
+ implementation vs using Python threads directly in order to prevent oversubscription,
172
+ we recommend either using the main CPython interpreter thread to spawn multiple C threads
173
+ using the parallel numexpr API, or spawning multiple CPython threads that do not use
174
+ the parallel API.
175
+
176
+ For more information about free-threaded CPython, we recommend visiting the following
177
+ `community Wiki <https://py-free-threading.github.io/>`
178
+
179
+
162
180
  Documentation
163
181
  -------------
164
182
 
@@ -1,7 +1,38 @@
1
1
  =====================================
2
- Release notes for NumExpr 2.10 series
2
+ Release notes for NumExpr 2.12 series
3
3
  =====================================
4
4
 
5
+ Changes from 2.11.0 to 2.12.0
6
+ -----------------------------
7
+
8
+ * Added isnan/isfinite/isinf functions. Thanks to Luke Shaw.
9
+
10
+ * New instructions for adding new functions to the virtual machine.
11
+ They are available at ADDFUNCS.rst. Thanks to Luke Shaw.
12
+
13
+ * We are distributing binary wheels for Python 3.14 and 3.14t now.
14
+
15
+ * We are distributing musllinux wheels too! Thanks to Clément Robert.
16
+
17
+
18
+ Changes from 2.10.2 to 2.11.0
19
+ -----------------------------
20
+
21
+ * Initial support for free-threaded Python 3.13t has been added.
22
+ This is still experimental, so please report any issues you find.
23
+ For more info, see discussions PRs #504, #505 and #508.
24
+ Thanks to @andfoy, @rgommers and @FrancescAlted for the work.
25
+
26
+ * Fix imaginary evaluation in the form of `1.1e1j`. This was
27
+ previously not supported and would raise an error. Thanks to @27rabbitlt
28
+ for the fix.
29
+
30
+ * The test suite has been modernized to use `pytest` instead of `unittest`.
31
+ This should make it easier to run the tests and contribute to the project.
32
+
33
+ * Python 3.10 is now the minimum supported version.
34
+
35
+
5
36
  Changes from 2.10.1 to 2.10.2
6
37
  -----------------------------
7
38
 
numexpr-2.12.0/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.12.0
@@ -9,8 +9,10 @@
9
9
  ####################################################################
10
10
 
11
11
  from __future__ import print_function
12
+
12
13
  import sys
13
14
  import timeit
15
+
14
16
  import numpy
15
17
 
16
18
  array_size = 5_000_000
@@ -0,0 +1,171 @@
1
+ #################################################################################
2
+ # To compare the performance of numexpr when free-threading CPython is used.
3
+ #
4
+ # This example makes use of Python threads, as opposed to C native ones
5
+ # in order to highlight the improvement introduced by free-threading CPython,
6
+ # which now disables the GIL altogether.
7
+ #################################################################################
8
+ """
9
+ Results with GIL-enabled CPython:
10
+
11
+ Benchmarking Expression 1:
12
+ NumPy time (threaded over 32 chunks with 16 threads): 1.173090 seconds
13
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 0.951071 seconds
14
+ numexpr speedup: 1.23x
15
+ ----------------------------------------
16
+ Benchmarking Expression 2:
17
+ NumPy time (threaded over 32 chunks with 16 threads): 10.410874 seconds
18
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 8.248753 seconds
19
+ numexpr speedup: 1.26x
20
+ ----------------------------------------
21
+ Benchmarking Expression 3:
22
+ NumPy time (threaded over 32 chunks with 16 threads): 9.605909 seconds
23
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 11.087108 seconds
24
+ numexpr speedup: 0.87x
25
+ ----------------------------------------
26
+ Benchmarking Expression 4:
27
+ NumPy time (threaded over 32 chunks with 16 threads): 3.836962 seconds
28
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 18.054531 seconds
29
+ numexpr speedup: 0.21x
30
+ ----------------------------------------
31
+
32
+ Results with free-threading CPython:
33
+
34
+ Benchmarking Expression 1:
35
+ NumPy time (threaded over 32 chunks with 16 threads): 3.415349 seconds
36
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 2.618876 seconds
37
+ numexpr speedup: 1.30x
38
+ ----------------------------------------
39
+ Benchmarking Expression 2:
40
+ NumPy time (threaded over 32 chunks with 16 threads): 19.005238 seconds
41
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 12.611407 seconds
42
+ numexpr speedup: 1.51x
43
+ ----------------------------------------
44
+ Benchmarking Expression 3:
45
+ NumPy time (threaded over 32 chunks with 16 threads): 20.555149 seconds
46
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 17.690749 seconds
47
+ numexpr speedup: 1.16x
48
+ ----------------------------------------
49
+ Benchmarking Expression 4:
50
+ NumPy time (threaded over 32 chunks with 16 threads): 38.338372 seconds
51
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 35.074684 seconds
52
+ numexpr speedup: 1.09x
53
+ ----------------------------------------
54
+ """
55
+
56
+ import os
57
+
58
+ os.environ["NUMEXPR_NUM_THREADS"] = "2"
59
+ import threading
60
+ import timeit
61
+
62
+ import numpy as np
63
+
64
+ import numexpr as ne
65
+
66
+ array_size = 10**8
67
+ num_runs = 10
68
+ num_chunks = 32 # Number of chunks
69
+ num_threads = 16 # Number of threads constrained by how many chunks memory can hold
70
+
71
+ a = np.random.rand(array_size).reshape(10**4, -1)
72
+ b = np.random.rand(array_size).reshape(10**4, -1)
73
+ c = np.random.rand(array_size).reshape(10**4, -1)
74
+
75
+ chunk_size = array_size // num_chunks
76
+
77
+ expressions_numpy = [
78
+ lambda a, b, c: a + b * c,
79
+ lambda a, b, c: a**2 + b**2 - 2 * a * b * np.cos(c),
80
+ lambda a, b, c: np.sin(a) + np.log(b) * np.sqrt(c),
81
+ lambda a, b, c: np.exp(a) + np.tan(b) - np.sinh(c),
82
+ ]
83
+
84
+ expressions_numexpr = [
85
+ "a + b * c",
86
+ "a**2 + b**2 - 2 * a * b * cos(c)",
87
+ "sin(a) + log(b) * sqrt(c)",
88
+ "exp(a) + tan(b) - sinh(c)",
89
+ ]
90
+
91
+
92
+ def benchmark_numpy_chunk(func, a, b, c, results, indices):
93
+ for index in indices:
94
+ start = index * chunk_size
95
+ end = (index + 1) * chunk_size
96
+ time_taken = timeit.timeit(
97
+ lambda: func(a[start:end], b[start:end], c[start:end]), number=num_runs
98
+ )
99
+ results.append(time_taken)
100
+
101
+
102
+ def benchmark_numexpr_re_evaluate(expr, a, b, c, results, indices):
103
+ for index in indices:
104
+ start = index * chunk_size
105
+ end = (index + 1) * chunk_size
106
+ # if index == 0:
107
+ # Evaluate the first chunk with evaluate
108
+ time_taken = timeit.timeit(
109
+ lambda: ne.evaluate(
110
+ expr,
111
+ local_dict={
112
+ "a": a[start:end],
113
+ "b": b[start:end],
114
+ "c": c[start:end],
115
+ },
116
+ ),
117
+ number=num_runs,
118
+ )
119
+ results.append(time_taken)
120
+
121
+
122
+ def run_benchmark_threaded():
123
+ chunk_indices = list(range(num_chunks))
124
+
125
+ for i in range(len(expressions_numpy)):
126
+ print(f"Benchmarking Expression {i+1}:")
127
+
128
+ results_numpy = []
129
+ results_numexpr = []
130
+
131
+ threads_numpy = []
132
+ for j in range(num_threads):
133
+ indices = chunk_indices[j::num_threads] # Distribute chunks across threads
134
+ thread = threading.Thread(
135
+ target=benchmark_numpy_chunk,
136
+ args=(expressions_numpy[i], a, b, c, results_numpy, indices),
137
+ )
138
+ threads_numpy.append(thread)
139
+ thread.start()
140
+
141
+ for thread in threads_numpy:
142
+ thread.join()
143
+
144
+ numpy_time = sum(results_numpy)
145
+ print(
146
+ f"NumPy time (threaded over {num_chunks} chunks with {num_threads} threads): {numpy_time:.6f} seconds"
147
+ )
148
+
149
+ threads_numexpr = []
150
+ for j in range(num_threads):
151
+ indices = chunk_indices[j::num_threads] # Distribute chunks across threads
152
+ thread = threading.Thread(
153
+ target=benchmark_numexpr_re_evaluate,
154
+ args=(expressions_numexpr[i], a, b, c, results_numexpr, indices),
155
+ )
156
+ threads_numexpr.append(thread)
157
+ thread.start()
158
+
159
+ for thread in threads_numexpr:
160
+ thread.join()
161
+
162
+ numexpr_time = sum(results_numexpr)
163
+ print(
164
+ f"numexpr time (threaded with re_evaluate over {num_chunks} chunks with {num_threads} threads): {numexpr_time:.6f} seconds"
165
+ )
166
+ print(f"numexpr speedup: {numpy_time / numexpr_time:.2f}x")
167
+ print("-" * 40)
168
+
169
+
170
+ if __name__ == "__main__":
171
+ run_benchmark_threaded()
@@ -2,10 +2,14 @@
2
2
  # performs better than the serial code. See issue #36 for details.
3
3
 
4
4
  from __future__ import print_function
5
+
6
+ from time import time
7
+
5
8
  import numpy as np
6
- import numexpr as ne
7
9
  from numpy.testing import assert_array_equal
8
- from time import time
10
+
11
+ import numexpr as ne
12
+
9
13
 
10
14
  def bench(N):
11
15
  print("*** array length:", N)
@@ -31,4 +35,3 @@ if __name__ == "__main__":
31
35
  ne.set_num_threads(2)
32
36
  for N in range(10, 20):
33
37
  bench(2**N)
34
-
@@ -1,4 +1,5 @@
1
1
  import numpy
2
+
2
3
  import numexpr
3
4
 
4
5
  numexpr.set_num_threads(8)
@@ -31,10 +31,12 @@ numexpr speedup: 2.38x
31
31
  import os
32
32
 
33
33
  os.environ["NUMEXPR_NUM_THREADS"] = "16"
34
+ import threading
35
+ import timeit
36
+
34
37
  import numpy as np
38
+
35
39
  import numexpr as ne
36
- import timeit
37
- import threading
38
40
 
39
41
  array_size = 10**8
40
42
  num_runs = 10