Skip to content

Commit 8ba43af

Browse files
authored
Merge branch 'master' into static-typing
2 parents 36ca8d1 + cd730c8 commit 8ba43af

File tree

10 files changed

+185
-94
lines changed

10 files changed

+185
-94
lines changed

ANNOUNCE.rst

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
=========================
2-
Announcing NumExpr 2.13.0
2+
Announcing NumExpr 2.13.
33
=========================
44

55
Hi everyone,
66

7-
NumExpr 2.13.0 introduced a bunch of new features including new
8-
bitwise operators (&, |, ^, ~), floor division (//). It also adds
9-
many new functions (like hypot, log2, maximum, minimum, nextafter...).
7+
NumExpr 2.13.1 introduces a couple of patches for maximum/minimum and
8+
multiplication/addition for booleans to match NumPy behaviour.
109
Thanks to Luke Shaw for these contributions.
1110

1211
Project documentation is available at:

RELEASE_NOTES.rst

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Release notes for NumExpr 2.13 series
33
=====================================
44

5-
Changes from 2.13.0 to 2.13.1
5+
Changes from 2.13.1 to 2.13.2
66
-----------------------------
77

88
* **Under development.**
@@ -12,18 +12,23 @@ Changes from 2.13.0 to 2.13.1
1212
Thanks to Joren Hammudoglu (@jorenham) for the work.
1313

1414

15+
Changes from 2.13.0 to 2.13.1
16+
-----------------------------
17+
18+
* Patch to maximum/minimum functions in order to match NumPy NaN handling
19+
* Patch to convert '+'->'|' and '*'->'&' for booleans
20+
21+
1522
Changes from 2.12.1 to 2.13.0
1623
-----------------------------
1724

1825
* New functionality has been added:
19-
* Bitwise operators (and, or, not, xor): `&, |, ~, ^`
20-
* New binary arithmetic operator for floor division: `//`
21-
* New functions: `signbit`, `hypot`, `copysign`, `nextafter`, `maximum`,
22-
`minimum`, `log2`, `trunc`, `round` and `sign`.
23-
* Also enables integer outputs for integer inputs for
24-
`abs`, `fmod`, `copy`, `ones_like`, `sign` and `round`.
25-
26-
Thanks to Luke Shaw for the contributions.
26+
* Bitwise operators (and, or, not, xor): `&, |, ~, ^`
27+
* New binary arithmetic operator for floor division: `//`
28+
* New functions: `signbit`, `hypot`, `copysign`, `nextafter`, `maximum`, `minimum`, `log2`, `trunc`, `round` and `sign`.
29+
* Also enables integer outputs for integer inputs for `abs`, `copy`, `ones_like`, `sign` and `round`.
30+
31+
Thanks to Luke Shaw for the contributions.
2732

2833
* New wheels for Python 3.14 and 3.14t are provided.
2934

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.13.1.dev0
1+
2.13.2.dev0

numexpr/bespoke_functions.hpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,51 @@ inline long fabsl(long x) {return x<0 ? -x: x;}
3535
// inline long fmodl(long x, long y) {return (long)fmodf((long)x, (long)y);}
3636

3737
#ifdef USE_VML
38+
// To match Numpy behaviour for NaNs
39+
static void vsFmax_(MKL_INT n, const float* x1, const float* x2, float* dest)
40+
{
41+
vsFmax(n, x1, x2, dest);
42+
MKL_INT j;
43+
for (j=0; j<n; j++) {
44+
if (isnanf_(x1[j]) | isnanf_(x2[j])){
45+
dest[j] = NAN;
46+
}
47+
};
48+
};
49+
50+
static void vsFmin_(MKL_INT n, const float* x1, const float* x2, float* dest)
51+
{
52+
vsFmin(n, x1, x2, dest);
53+
MKL_INT j;
54+
for (j=0; j<n; j++) {
55+
if (isnanf_(x1[j]) | isnanf_(x2[j])){
56+
dest[j] = NAN;
57+
}
58+
};
59+
};
60+
// To match Numpy behaviour for NaNs
61+
static void vdFmax_(MKL_INT n, const double* x1, const double* x2, double* dest)
62+
{
63+
vdFmax(n, x1, x2, dest);
64+
MKL_INT j;
65+
for (j=0; j<n; j++) {
66+
if (isnand(x1[j]) | isnand(x2[j])){
67+
dest[j] = NAN;
68+
}
69+
};
70+
};
71+
72+
static void vdFmin_(MKL_INT n, const double* x1, const double* x2, double* dest)
73+
{
74+
vdFmin(n, x1, x2, dest);
75+
MKL_INT j;
76+
for (j=0; j<n; j++) {
77+
if (isnand(x1[j]) | isnand(x2[j])){
78+
dest[j] = NAN;
79+
}
80+
};
81+
};
82+
3883
static void viRint(MKL_INT n, const int* x, int* dest)
3984
{
4085
memcpy(dest, x, n * sizeof(int)); // just copy x1 which is already int

numexpr/complex_functions.hpp

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -390,42 +390,45 @@ nc_sinh(std::complex<double> *x, std::complex<double> *r)
390390
static void
391391
nc_tan(std::complex<double> *x, std::complex<double> *r)
392392
{
393-
double sr,cr,shi,chi;
394-
double rs,is,rc,ic;
395-
double d;
396-
double xr=x->real(), xi=x->imag();
397-
sr = sin(xr);
398-
cr = cos(xr);
399-
shi = sinh(xi);
400-
chi = cosh(xi);
401-
rs = sr*chi;
402-
is = cr*shi;
403-
rc = cr*chi;
404-
ic = -sr*shi;
405-
d = rc*rc + ic*ic;
406-
r->real((rs*rc+is*ic)/d);
407-
r->imag((is*rc-rs*ic)/d);
393+
double xr = x->real();
394+
double xi = x->imag();
395+
double imag_part;
396+
397+
double denom = cos(2*xr) + cosh(2*xi);
398+
// handle overflows
399+
if (xi > 20) {
400+
imag_part = 1.0 / (1.0 + exp(-4*xi));
401+
} else if (xi < -20) {
402+
imag_part = -1.0 / (1.0 + exp(4*xi));
403+
} else {
404+
imag_part = sinh(2*xi) / denom;
405+
}
406+
double real_part = sin(2*xr) / denom;
407+
408+
r->real(real_part);
409+
r->imag(imag_part);
408410
return;
409411
}
410412

411413
static void
412414
nc_tanh(std::complex<double> *x, std::complex<double> *r)
413415
{
414-
double si,ci,shr,chr;
415-
double rs,is,rc,ic;
416-
double d;
417-
double xr=x->real(), xi=x->imag();
418-
si = sin(xi);
419-
ci = cos(xi);
420-
shr = sinh(xr);
421-
chr = cosh(xr);
422-
rs = ci*shr;
423-
is = si*chr;
424-
rc = ci*chr;
425-
ic = si*shr;
426-
d = rc*rc + ic*ic;
427-
r->real((rs*rc+is*ic)/d);
428-
r->imag((is*rc-rs*ic)/d);
416+
double xr = x->real();
417+
double xi = x->imag();
418+
double real_part;
419+
double denom = cosh(2*xr) + cos(2*xi);
420+
// handle overflows
421+
if (xr > 20) {
422+
real_part = 1.0 / (1.0 + exp(-4*xr));
423+
} else if (xr < -20) {
424+
real_part = -1.0 / (1.0 + exp(4*xr));
425+
} else {
426+
real_part = sinh(2*xr) / denom;
427+
}
428+
double imag_part = sin(2*xi) / denom;
429+
430+
r->real(real_part);
431+
r->imag(imag_part);
429432
return;
430433
}
431434

numexpr/expressions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,9 @@ def __init__(
573573
) -> None:
574574
if (kind is None) and (args is not None):
575575
kind = commonKind(args)
576+
if kind=='bool': # handle bool*bool and bool+bool cases
577+
opcode = 'and' if opcode=='mul' else opcode
578+
opcode = 'or' if opcode=='add' else opcode
576579
ExpressionNode.__init__(self, value=opcode, kind=kind, children=args)
577580

578581

numexpr/functions.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ FUNC_FFF(FUNC_ARCTAN2_FFF, "arctan2_fff", atan2f, atan2f2, vsAtan2)
5656
FUNC_FFF(FUNC_HYPOT_FFF, "hypot_fff", hypotf, hypotf2, vsHypot)
5757
FUNC_FFF(FUNC_NEXTAFTER_FFF, "nextafter_fff", nextafterf, nextafterf2, vsNextAfter)
5858
FUNC_FFF(FUNC_COPYSIGN_FFF, "copysign_fff", copysignf, copysignf2, vsCopySign)
59-
FUNC_FFF(FUNC_MAXIMUM_FFF, "maximum_fff", fmaxf, fmaxf2, vsFmax)
60-
FUNC_FFF(FUNC_MINIMUM_FFF, "minimum_fff", fminf, fminf2, vsFmin)
59+
FUNC_FFF(FUNC_MAXIMUM_FFF, "maximum_fff", fmaxf_, fmaxf2, vsFmax_)
60+
FUNC_FFF(FUNC_MINIMUM_FFF, "minimum_fff", fminf_, fminf2, vsFmin_)
6161
FUNC_FFF(FUNC_FFF_LAST, NULL, NULL, NULL, NULL)
6262
#ifdef ELIDE_FUNC_FFF
6363
#undef ELIDE_FUNC_FFF
@@ -140,8 +140,8 @@ FUNC_DDD(FUNC_ARCTAN2_DDD, "arctan2_ddd", atan2, vdAtan2)
140140
FUNC_DDD(FUNC_HYPOT_DDD, "hypot_ddd", hypot, vdHypot)
141141
FUNC_DDD(FUNC_NEXTAFTER_DDD, "nextafter_ddd", nextafter, vdNextAfter)
142142
FUNC_DDD(FUNC_COPYSIGN_DDD, "copysign_ddd", copysign, vdCopySign)
143-
FUNC_DDD(FUNC_MAXIMUM_DDD, "maximum_ddd", fmaxd, vdFmax)
144-
FUNC_DDD(FUNC_MINIMUM_DDD, "minimum_ddd", fmind, vdFmin)
143+
FUNC_DDD(FUNC_MAXIMUM_DDD, "maximum_ddd", fmaxd, vdFmax_)
144+
FUNC_DDD(FUNC_MINIMUM_DDD, "minimum_ddd", fmind, vdFmin_)
145145
FUNC_DDD(FUNC_DDD_LAST, NULL, NULL, NULL)
146146
#ifdef ELIDE_FUNC_DDD
147147
#undef ELIDE_FUNC_DDD

numexpr/msvc_function_stubs.hpp

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,47 +16,6 @@
1616
definitions in <math.h> are actually #define'd and are not usable
1717
as function pointers :-/ */
1818

19-
#if _MSC_VER < 1400 // 1310 == MSVC 7.1
20-
/* Apparently, single precision functions are not included in MSVC 7.1 */
21-
22-
#define sqrtf(x) ((float)sqrt((double)(x)))
23-
#define sinf(x) ((float)sin((double)(x)))
24-
#define cosf(x) ((float)cos((double)(x)))
25-
#define tanf(x) ((float)tan((double)(x)))
26-
#define asinf(x) ((float)asin((double)(x)))
27-
#define acosf(x) ((float)acos((double)(x)))
28-
#define atanf(x) ((float)atan((double)(x)))
29-
#define sinhf(x) ((float)sinh((double)(x)))
30-
#define coshf(x) ((float)cosh((double)(x)))
31-
#define tanhf(x) ((float)tanh((double)(x)))
32-
#define asinhf(x) ((float)asinh((double)(x)))
33-
#define acoshf(x) ((float)acosh((double)(x)))
34-
#define atanhf(x) ((float)atanh((double)(x)))
35-
#define logf(x) ((float)log((double)(x)))
36-
#define log1pf(x) ((float)log1p((double)(x)))
37-
#define log10f(x) ((float)log10((double)(x)))
38-
#define log2f(x) ((float)log2((double)(x)))
39-
#define expf(x) ((float)exp((double)(x)))
40-
#define expm1f(x) ((float)expm1((double)(x)))
41-
#define fabsf(x) ((float)fabs((double)(x)))
42-
#define fmodf(x, y) ((float)fmod((double)(x), (double)(y)))
43-
#define atan2f(x, y) ((float)atan2((double)(x), (double)(y)))
44-
#define hypotf(x, y) ((float)hypot((double)(x), (double)(y)))
45-
#define copysignf(x, y) ((float)copysign((double)(x), (double)(y)))
46-
#define nextafterf(x, y) ((float)nextafter((double)(x), (double)(y)))
47-
#define fmaxf(x, y) ((float)fmaxd((double)(x), (double)(y)))
48-
#define fminf(x, y) ((float)fmind((double)(x), (double)(y)))
49-
#define ceilf(x) ((float)ceil((double)(x)))
50-
#define hypotf(x) ((float)hypot((double)(x)))
51-
#define rintf(x) ((float)rint((double)(x)))
52-
#define truncf(x) ((float)trunc((double)(x)))
53-
54-
55-
/* The next are directly called from interp_body.cpp */
56-
#define powf(x, y) ((float)pow((double)(x), (double)(y)))
57-
#define floorf(x) ((float)floor((double)(x)))
58-
#endif // _MSC_VER < 1400
59-
6019
/* Due to casting problems (normally return ints not bools, easiest to define
6120
non-overloaded wrappers for these functions) */
6221
// MSVC version: use global ::isfinite / ::isnan
@@ -67,6 +26,57 @@ inline bool isnand(double x) { return !!::_isnan(x); }
6726
inline bool isinfd(double x) { return !!::isinf(x); }
6827
inline bool isinff_(float x) { return !!::isinf(x); }
6928

29+
// To handle overloading of fmax/fmin in cmath and match NumPy behaviour for NaNs
30+
inline double fmaxd(double x, double y) { return (isnand(x) | isnand(y))? NAN : fmax(x, y); }
31+
inline double fmind(double x, double y) { return (isnand(x) | isnand(y))? NAN : fmin(x, y); }
32+
33+
34+
#if _MSC_VER < 1400 // 1310 == MSVC 7.1
35+
/* Apparently, single precision functions are not included in MSVC 7.1 */
36+
37+
#define sqrtf(x) ((float)sqrt((double)(x)))
38+
#define sinf(x) ((float)sin((double)(x)))
39+
#define cosf(x) ((float)cos((double)(x)))
40+
#define tanf(x) ((float)tan((double)(x)))
41+
#define asinf(x) ((float)asin((double)(x)))
42+
#define acosf(x) ((float)acos((double)(x)))
43+
#define atanf(x) ((float)atan((double)(x)))
44+
#define sinhf(x) ((float)sinh((double)(x)))
45+
#define coshf(x) ((float)cosh((double)(x)))
46+
#define tanhf(x) ((float)tanh((double)(x)))
47+
#define asinhf(x) ((float)asinh((double)(x)))
48+
#define acoshf(x) ((float)acosh((double)(x)))
49+
#define atanhf(x) ((float)atanh((double)(x)))
50+
#define logf(x) ((float)log((double)(x)))
51+
#define log1pf(x) ((float)log1p((double)(x)))
52+
#define log10f(x) ((float)log10((double)(x)))
53+
#define log2f(x) ((float)log2((double)(x)))
54+
#define expf(x) ((float)exp((double)(x)))
55+
#define expm1f(x) ((float)expm1((double)(x)))
56+
#define fabsf(x) ((float)fabs((double)(x)))
57+
#define fmodf(x, y) ((float)fmod((double)(x), (double)(y)))
58+
#define atan2f(x, y) ((float)atan2((double)(x), (double)(y)))
59+
#define hypotf(x, y) ((float)hypot((double)(x), (double)(y)))
60+
#define copysignf(x, y) ((float)copysign((double)(x), (double)(y)))
61+
#define nextafterf(x, y) ((float)nextafter((double)(x), (double)(y)))
62+
#define ceilf(x) ((float)ceil((double)(x)))
63+
#define hypotf(x) ((float)hypot((double)(x)))
64+
#define rintf(x) ((float)rint((double)(x)))
65+
#define truncf(x) ((float)trunc((double)(x)))
66+
67+
68+
/* The next are directly called from interp_body.cpp */
69+
#define powf(x, y) ((float)pow((double)(x), (double)(y)))
70+
#define floorf(x) ((float)floor((double)(x)))
71+
72+
#define fmaxf_(x, y) ((float)fmaxd((double)(x), (double)(y))) // define fmaxf_ since fmaxf doesn't exist for early MSVC
73+
#define fminf_(x, y) ((float)fmind((double)(x), (double)(y)))
74+
#else
75+
inline float fmaxf_(float x, float y) { return (isnanf_(x) | isnanf_(y))? NAN : fmaxf(x, y); }
76+
inline float fminf_(float x, float y) { return (isnanf_(x) | isnanf_(y))? NAN : fminf(x, y); }
77+
#endif // _MSC_VER < 1400
78+
79+
7080
/* Now the actual stubs */
7181

7282
inline float sqrtf2(float x) {
@@ -170,11 +180,11 @@ inline float copysignf2(float x, float y) {
170180
}
171181

172182
inline float fmaxf2(float x, float y) {
173-
return fmaxf(x, y);
183+
return fmaxf_(x, y);
174184
}
175185

176186
inline float fminf2(float x, float y) {
177-
return fminf(x, y);
187+
return fminf_(x, y);
178188
}
179189

180190

numexpr/numexpr_config.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@
4343
#include <cmath>
4444
//no single precision version of signbit in C++ standard
4545
inline bool signbitf(float x) { return signbit((double)x); }
46-
// To handle overloading of fmax/fmin in cmath
47-
inline double fmaxd(double x, double y) { return fmax(x, y); }
48-
inline double fmind(double x, double y) { return fmin(x, y); }
46+
4947
#ifdef _WIN32
5048
#ifndef __MINGW32__
5149
#include "missing_posix_functions.hpp"
@@ -62,6 +60,12 @@ inline bool isfinited(double x) { return !!std::isfinite(x); }
6260
inline bool isnand(double x) { return !!std::isnan(x); }
6361
inline bool isinff_(float x) { return !!std::isinf(x); }
6462
inline bool isinfd(double x) { return !!std::isinf(x); }
63+
64+
// To handle overloading of fmax/fmin in cmath and match NumPy behaviour for NaNs
65+
inline double fmaxd(double x, double y) { return (isnand(x) | isnand(y))? NAN : fmax(x, y); }
66+
inline double fmind(double x, double y) { return (isnand(x) | isnand(y))? NAN : fmin(x, y); }
67+
inline float fmaxf_(float x, float y) { return (isnanf_(x) | isnanf_(y))? NAN : fmaxf(x, y); }
68+
inline float fminf_(float x, float y) { return (isnanf_(x) | isnanf_(y))? NAN : fminf(x, y); }
6569
#endif
6670

6771
#endif // NUMEXPR_CONFIG_HPP

numexpr/tests/test_numexpr.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,13 +481,35 @@ def test_bitwise_operators(self):
481481
assert_array_equal(evaluate("x | y"), x | y) # or
482482
assert_array_equal(evaluate("~x"), ~x) # invert
483483

484+
def test_complex_tan(self):
485+
# old version of NumExpr had overflow problems
486+
x = np.arange(1, 400., step=16., dtype=np.complex128)
487+
y = 1j*np.arange(1, 400., step=16., dtype=np.complex128)
488+
assert_array_almost_equal(evaluate("tan(x + y)"), tan(x + y))
489+
assert_array_almost_equal(evaluate("tanh(x + y)"), tanh(x + y))
490+
484491
def test_maximum_minimum(self):
485492
for dtype in [float, double, int, np.int64]:
486493
x = arange(10, dtype=dtype)
487494
y = 2 * arange(10, dtype=dtype)[::-1]
495+
if dtype in (float, double):
496+
y[5] = np.nan
497+
x[2] = np.nan
488498
assert_array_equal(evaluate("maximum(x,y)"), maximum(x,y))
489499
assert_array_equal(evaluate("minimum(x,y)"), minimum(x,y))
490500

501+
def test_addmult_booleans(self):
502+
x = np.asarray([0, 1, 0, 0, 1], dtype=bool)
503+
y = x[::-1]
504+
res_ne = evaluate("x * y")
505+
res_np = x * y
506+
assert_array_equal(res_ne, res_np)
507+
assert res_ne.dtype == res_np.dtype
508+
res_ne = evaluate("x + y")
509+
res_np = x + y
510+
assert_array_equal(res_ne, res_np)
511+
assert res_ne.dtype == res_np.dtype
512+
491513
def test_sign_round(self):
492514
for dtype in [float, double, np.int32, np.int64, complex]:
493515
x = arange(10, dtype=dtype)

0 commit comments

Comments
 (0)