diff --git a/ADDFUNCS.rst b/ADDFUNCS.rst index 2a8d17f..497c6fe 100644 --- a/ADDFUNCS.rst +++ b/ADDFUNCS.rst @@ -54,7 +54,7 @@ Example: #define FUNC_FF(...) #endif ... - FUNC_FF(FUNC_MYFUNC_FF, "myfunc_ff", myfuncf, myfuncf2, vfMyfunc) + FUNC_FF(FUNC_MYFUNC_FF, "myfunc_ff", myfuncf, myfuncf2, vsMyfunc) FUNC_FF(FUNC_FF_LAST, NULL, NULL, NULL, NULL) #ifdef ELIDE_FUNC_FF #undef ELIDE_FUNC_FF @@ -171,7 +171,7 @@ Add clauses to generate the FUNC_CODES from the ``functions.hpp`` header, making }; #endif -Some functions (e.g. ``fmod``, ``isnan``) are not available in MKL, and so must be hard-coded here as well: +Some functions (e.g. ``fmod``, ``isnan``) are not available in MKL, and so must be hard-coded in ``bespoke_functions.hpp`` as well: .. code-block:: cpp @@ -186,7 +186,7 @@ Some functions (e.g. ``fmod``, ``isnan``) are not available in MKL, and so must }; #endif -The complex case is slightlñy different (see other examples in the same file). +The complex case is slightly different (see other examples in the same file). Add case handling to the ``check_program`` function diff --git a/doc/user_guide.rst b/doc/user_guide.rst index ade4e46..75dc4de 100644 --- a/doc/user_guide.rst +++ b/doc/user_guide.rst @@ -188,10 +188,10 @@ Supported operators *NumExpr* supports the set of operators listed below: - * Bitwise operators (and, or, not, xor): :code:`&, |, ~, ^` + * Bitwise and logical operators (and, or, not, xor): :code:`&, |, ~, ^` * Comparison operators: :code:`<, <=, ==, !=, >=, >` * Unary arithmetic operators: :code:`-` - * Binary arithmetic operators: :code:`+, -, *, /, **, %, <<, >>` + * Binary arithmetic operators: :code:`+, -, *, /, //, **, %, <<, >>` Supported functions @@ -203,22 +203,33 @@ The next are the current supported set: is true, number2 otherwise. * :code:`{isinf, isnan, isfinite}(float|complex): bool` -- returns element-wise True for ``inf`` or ``NaN``, ``NaN``, not ``inf`` respectively. + * :code:`signbit(float|complex): bool` -- returns element-wise True if signbit is set + False otherwise. * :code:`{sin,cos,tan}(float|complex): float|complex` -- trigonometric sine, cosine or tangent. * :code:`{arcsin,arccos,arctan}(float|complex): float|complex` -- trigonometric inverse sine, cosine or tangent. * :code:`arctan2(float1, float2): float` -- trigonometric inverse tangent of float1/float2. + * :code:`hypot(float1, float2): float` -- Euclidean distance between float1, float2 + * :code:`nextafter(float1, float2): float` -- next representable floating-point value after + float1 in direction of float2 + * :code:`copysign(float1, float2): float` -- return number with magnitude of float1 and + sign of float2 + * :code:`{maximum,minimum}(float1, float2): float` -- return max/min of float1, float2 * :code:`{sinh,cosh,tanh}(float|complex): float|complex` -- hyperbolic sine, cosine or tangent. * :code:`{arcsinh,arccosh,arctanh}(float|complex): float|complex` -- hyperbolic inverse sine, cosine or tangent. - * :code:`{log,log10,log1p}(float|complex): float|complex` -- natural, base-10 and + * :code:`{log,log10,log1p,log2}(float|complex): float|complex` -- natural, base-10 and log(1+x) logarithms. * :code:`{exp,expm1}(float|complex): float|complex` -- exponential and exponential minus one. * :code:`sqrt(float|complex): float|complex` -- square root. - * :code:`abs(float|complex): float|complex` -- absolute value. + * :code:`trunc(float): float` -- round towards zero + * :code:`round(float|complex|int): float|complex|int` -- round to nearest integer (`rint`) + * :code:`sign(float|complex|int): float|complex|int` -- return -1, 0, +1 depending on sign + * :code:`abs(float|complex|int): float|complex|int` -- absolute value. * :code:`conj(complex): complex` -- conjugate value. * :code:`{real,imag}(complex): float` -- real or imaginary part of complex. * :code:`complex(float, float): complex` -- complex from real and imaginary diff --git a/numexpr/bespoke_functions.hpp b/numexpr/bespoke_functions.hpp new file mode 100644 index 0000000..782f358 --- /dev/null +++ b/numexpr/bespoke_functions.hpp @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include "numexpr_config.hpp" // isnan definitions + +// Generic sign function +inline int signi(int x) {return (0 < x) - (x < 0);} +inline long signl(long x) {return (0 < x) - (x < 0);} +inline double sign(double x){ + // Floats: -1.0, 0.0, +1.0, NaN stays NaN + if (isnand(x)) {return NAN;} + if (x > 0) {return 1;} + if (x < 0) {return -1;} + return 0; // handles +0.0 and -0.0 + } +inline float signf(float x){ + // Floats: -1.0, 0.0, +1.0, NaN stays NaN + if (isnanf_(x)) {return NAN;} + if (x > 0) {return 1;} + if (x < 0) {return -1;} + return 0; // handles +0.0 and -0.0 + } + +// round function for ints +inline int rinti(int x) {return x;} +inline long rintl(long x) {return x;} +// abs function for ints +inline int fabsi(int x) {return x<0 ? -x: x;} +inline long fabsl(long x) {return x<0 ? -x: x;} +// fmod function for ints +inline int fmodi(int x, int y) {return (int)fmodf((float)x, (float)y);} +inline long fmodl(long x, long y) {return (long)fmodf((long)x, (long)y);} + +#ifdef USE_VML +static void viRint(MKL_INT n, const int* x, int* dest) +{ + memcpy(dest, x, n * sizeof(int)); // just copy x1 which is already int +}; + +static void vlRint(MKL_INT n, const long* x, long* dest) +{ + memcpy(dest, x, n * sizeof(long)); // just copy x1 which is already int +}; + +static void viFabs(MKL_INT n, const int* x, int* dest) +{ + MKL_INT j; + for (j=0; j +#include // NAN #include /* constants */ @@ -347,6 +348,8 @@ nc_cosh(std::complex *x, std::complex *r) #define M_LOG10_E 0.434294481903251827651128918916605082294397 +#define M_LOG2_E 1.44269504088896340735992468100189213742664 + static void nc_log10(std::complex *x, std::complex *r) @@ -357,6 +360,15 @@ nc_log10(std::complex *x, std::complex *r) return; } +static void +nc_log2(std::complex *x, std::complex *r) +{ + nc_log(x, r); + r->real(r->real() * M_LOG2_E); + r->imag(r->imag() * M_LOG2_E); + return; +} + static void nc_sin(std::complex *x, std::complex *r) { @@ -424,6 +436,13 @@ nc_abs(std::complex *x, std::complex *r) r->imag(0); } +static void +nc_rint(std::complex *x, std::complex *r) +{ + r->real(rint(x->real())); + r->imag(rint(x->imag())); +} + static bool nc_isinf(std::complex *x) { @@ -453,4 +472,24 @@ nc_isfinite(std::complex *x) br = isfinited(xr); return bi && br; } + +static void +nc_sign(std::complex *x, std::complex *r) +{ + if (nc_isnan(x)){ + r->real(NAN); + r->imag(NAN); + } + std::complex mag; + nc_abs(x, &mag); + if (mag.real() == 0){ + r->real(0); + r->imag(0); + } + else{ + r->real(x->real()/mag.real()); + r->imag(x->imag()/mag.real()); + } +} + #endif // NUMEXPR_COMPLEX_FUNCTIONS_HPP diff --git a/numexpr/expressions.py b/numexpr/expressions.py index ce724bf..c15538e 100644 --- a/numexpr/expressions.py +++ b/numexpr/expressions.py @@ -186,11 +186,10 @@ def function(*args): return ConstantNode(func(*[x.value for x in args])) kind = commonKind(args) if kind in ('int', 'long'): - # Exception for following NumPy casting rules - #FIXME: this is not always desirable. The following - # functions which return ints (for int inputs) on numpy - # but not on numexpr: copy, abs, fmod, ones_like - kind = 'double' + if func.__name__ not in ('copy', 'abs', 'fmod', 'ones_like', 'round', 'sign'): + # except for these special functions (which return ints for int inputs in NumPy) + # just do a cast to double + kind = 'double' else: # Apply regular casting rules if minkind and kind_rank.index(minkind) > kind_rank.index(kind): @@ -348,16 +347,26 @@ def multiply(x, y): 'fmod': func(numpy.fmod, 'float'), 'arctan2': func(numpy.arctan2, 'float'), + 'hypot': func(numpy.hypot, 'double'), + 'nextafter': func(numpy.nextafter, 'double'), + 'copysign': func(numpy.copysign, 'double'), + 'maximum': func(numpy.maximum, 'double'), + 'minimum': func(numpy.minimum, 'double'), + 'log': func(numpy.log, 'float'), 'log1p': func(numpy.log1p, 'float'), 'log10': func(numpy.log10, 'float'), + 'log2': func(numpy.log2, 'float'), 'exp': func(numpy.exp, 'float'), 'expm1': func(numpy.expm1, 'float'), 'abs': func(numpy.absolute, 'float'), 'ceil': func(numpy.ceil, 'float', 'double'), 'floor': func(numpy.floor, 'float', 'double'), + 'round': func(numpy.round, 'double'), + 'trunc': func(numpy.trunc, 'double'), + 'sign': func(numpy.sign, 'double'), 'where': where_func, @@ -366,9 +375,10 @@ def multiply(x, y): 'complex': func(complex, 'complex'), 'conj': func(numpy.conj, 'complex'), - 'isnan': func(numpy.isnan, 'bool'), - 'isfinite': func(numpy.isfinite, 'bool'), - 'isinf': func(numpy.isinf, 'bool'), + 'isnan': func(numpy.isnan, 'double'), + 'isfinite': func(numpy.isfinite, 'double'), + 'isinf': func(numpy.isinf, 'double'), + 'signbit': func(numpy.signbit, 'double'), 'sum': gen_reduce_axis_func('sum'), 'prod': gen_reduce_axis_func('prod'), @@ -441,6 +451,7 @@ def __bool__(self): __mul__ = __rmul__ = binop('mul') __truediv__ = truediv_op __rtruediv__ = rtruediv_op + __floordiv__ = binop("floordiv") __pow__ = pow_op __rpow__ = binop('pow', reversed=True) __mod__ = binop('mod') @@ -525,6 +536,6 @@ class FuncNode(OpNode): def __init__(self, opcode=None, args=None, kind=None): if (kind is None) and (args is not None): kind = commonKind(args) - if opcode in ("isnan", "isfinite", "isinf"): # bodge for boolean return functions + if opcode in ("isnan", "isfinite", "isinf", "signbit"): # bodge for boolean return functions kind = 'bool' OpNode.__init__(self, opcode, args, kind) diff --git a/numexpr/functions.hpp b/numexpr/functions.hpp index 17310d6..109b8f3 100644 --- a/numexpr/functions.hpp +++ b/numexpr/functions.hpp @@ -30,12 +30,17 @@ FUNC_FF(FUNC_ARCTANH_FF, "arctanh_ff", atanhf, atanhf2, vsAtanh) FUNC_FF(FUNC_LOG_FF, "log_ff", logf, logf2, vsLn) FUNC_FF(FUNC_LOG1P_FF, "log1p_ff", log1pf, log1pf2, vsLog1p) FUNC_FF(FUNC_LOG10_FF, "log10_ff", log10f, log10f2, vsLog10) +FUNC_FF(FUNC_LOG2_FF, "log2_ff", log2f, log2f2, vsLog2) FUNC_FF(FUNC_EXP_FF, "exp_ff", expf, expf2, vsExp) FUNC_FF(FUNC_EXPM1_FF, "expm1_ff", expm1f, expm1f2, vsExpm1) FUNC_FF(FUNC_ABS_FF, "absolute_ff", fabsf, fabsf2, vsAbs) FUNC_FF(FUNC_CONJ_FF, "conjugate_ff",fconjf, fconjf2, vsConj) FUNC_FF(FUNC_CEIL_FF, "ceil_ff", ceilf, ceilf2, vsCeil) FUNC_FF(FUNC_FLOOR_FF, "floor_ff", floorf, floorf2, vsFloor) +FUNC_FF(FUNC_TRUNC_FF, "trunc_ff", truncf, truncf2, vsTrunc) +FUNC_FF(FUNC_SIGN_FF, "sign_ff", signf, signf2, vsSign) +//rint rounds to nearest even integer, matching NumPy (round doesn't) +FUNC_FF(FUNC_ROUND_FF, "round_ff", rintf, rintf2, vsRint) FUNC_FF(FUNC_FF_LAST, NULL, NULL, NULL, NULL) #ifdef ELIDE_FUNC_FF #undef ELIDE_FUNC_FF @@ -48,6 +53,11 @@ FUNC_FF(FUNC_FF_LAST, NULL, NULL, NULL, NULL) #endif FUNC_FFF(FUNC_FMOD_FFF, "fmod_fff", fmodf, fmodf2, vsfmod) FUNC_FFF(FUNC_ARCTAN2_FFF, "arctan2_fff", atan2f, atan2f2, vsAtan2) +FUNC_FFF(FUNC_HYPOT_FFF, "hypot_fff", hypotf, hypotf2, vsHypot) +FUNC_FFF(FUNC_NEXTAFTER_FFF, "nextafter_fff", nextafterf, nextafterf2, vsNextAfter) +FUNC_FFF(FUNC_COPYSIGN_FFF, "copysign_fff", copysignf, copysignf2, vsCopySign) +FUNC_FFF(FUNC_MAXIMUM_FFF, "maximum_fff", fmaxf, fmaxf2, vsFmax) +FUNC_FFF(FUNC_MINIMUM_FFF, "minimum_fff", fminf, fminf2, vsFmin) FUNC_FFF(FUNC_FFF_LAST, NULL, NULL, NULL, NULL) #ifdef ELIDE_FUNC_FFF #undef ELIDE_FUNC_FFF @@ -74,12 +84,17 @@ FUNC_DD(FUNC_ARCTANH_DD, "arctanh_dd", atanh, vdAtanh) FUNC_DD(FUNC_LOG_DD, "log_dd", log, vdLn) FUNC_DD(FUNC_LOG1P_DD, "log1p_dd", log1p, vdLog1p) FUNC_DD(FUNC_LOG10_DD, "log10_dd", log10, vdLog10) +FUNC_DD(FUNC_LOG2_DD, "log2_dd", log2, vdLog2) FUNC_DD(FUNC_EXP_DD, "exp_dd", exp, vdExp) FUNC_DD(FUNC_EXPM1_DD, "expm1_dd", expm1, vdExpm1) FUNC_DD(FUNC_ABS_DD, "absolute_dd", fabs, vdAbs) FUNC_DD(FUNC_CONJ_DD, "conjugate_dd",fconj, vdConj) FUNC_DD(FUNC_CEIL_DD, "ceil_dd", ceil, vdCeil) FUNC_DD(FUNC_FLOOR_DD, "floor_dd", floor, vdFloor) +FUNC_DD(FUNC_TRUNC_DD, "trunc_dd", trunc, vdTrunc) +FUNC_DD(FUNC_SIGN_DD, "sign_dd", sign, vdSign) +//rint rounds to nearest even integer, matching NumPy (round doesn't) +FUNC_DD(FUNC_ROUND_DD, "round_dd", rint, vdRint) FUNC_DD(FUNC_DD_LAST, NULL, NULL, NULL) #ifdef ELIDE_FUNC_DD #undef ELIDE_FUNC_DD @@ -94,6 +109,7 @@ FUNC_DD(FUNC_DD_LAST, NULL, NULL, NULL) FUNC_BD(FUNC_ISNAN_BD, "isnan_bd", isnand, vdIsnan) FUNC_BD(FUNC_ISFINITE_BD, "isfinite_bd", isfinited, vdIsfinite) FUNC_BD(FUNC_ISINF_BD, "isinf_bd", isinfd, vdIsinf) +FUNC_BD(FUNC_SIGNBIT_BD, "signbit_bd", signbit, vdSignBit) FUNC_BD(FUNC_BD_LAST, NULL, NULL, NULL) #ifdef ELIDE_FUNC_BD #undef ELIDE_FUNC_BD @@ -105,9 +121,10 @@ FUNC_BD(FUNC_BD_LAST, NULL, NULL, NULL) #define ELIDE_FUNC_BF #define FUNC_BF(...) #endif // use wrappers as there is name collision with isnanf in std -FUNC_BF(FUNC_ISNAN_BF, "isnan_bf", isnanf_, isnanf2, vfIsnan) -FUNC_BF(FUNC_ISFINITE_BF, "isfinite_bf", isfinitef_, isfinitef2, vfIsfinite) -FUNC_BF(FUNC_ISINF_BF, "isinf_bf", isinff_, isinff2, vfIsinf) +FUNC_BF(FUNC_ISNAN_BF, "isnan_bf", isnanf_, isnanf2, vsIsnan) +FUNC_BF(FUNC_ISFINITE_BF, "isfinite_bf", isfinitef_, isfinitef2, vsIsfinite) +FUNC_BF(FUNC_ISINF_BF, "isinf_bf", isinff_, isinff2, vsIsinf) +FUNC_BF(FUNC_SIGNBIT_BF, "signbit_bf", signbitf, signbitf2, vsSignBit) FUNC_BF(FUNC_BF_LAST, NULL, NULL, NULL, NULL) #ifdef ELIDE_FUNC_BF #undef ELIDE_FUNC_BF @@ -120,6 +137,11 @@ FUNC_BF(FUNC_BF_LAST, NULL, NULL, NULL, NULL) #endif FUNC_DDD(FUNC_FMOD_DDD, "fmod_ddd", fmod, vdfmod) FUNC_DDD(FUNC_ARCTAN2_DDD, "arctan2_ddd", atan2, vdAtan2) +FUNC_DDD(FUNC_HYPOT_DDD, "hypot_ddd", hypot, vdHypot) +FUNC_DDD(FUNC_NEXTAFTER_DDD, "nextafter_ddd", nextafter, vdNextAfter) +FUNC_DDD(FUNC_COPYSIGN_DDD, "copysign_ddd", copysign, vdCopySign) +FUNC_DDD(FUNC_MAXIMUM_DDD, "maximum_ddd", fmaxd, vdFmax) +FUNC_DDD(FUNC_MINIMUM_DDD, "minimum_ddd", fmind, vdFmin) FUNC_DDD(FUNC_DDD_LAST, NULL, NULL, NULL) #ifdef ELIDE_FUNC_DDD #undef ELIDE_FUNC_DDD @@ -146,10 +168,14 @@ FUNC_CC(FUNC_ARCTANH_CC, "arctanh_cc", nc_atanh, vzAtanh) FUNC_CC(FUNC_LOG_CC, "log_cc", nc_log, vzLn) FUNC_CC(FUNC_LOG1P_CC, "log1p_cc", nc_log1p, vzLog1p) FUNC_CC(FUNC_LOG10_CC, "log10_cc", nc_log10, vzLog10) +FUNC_CC(FUNC_LOG2_CC, "log2_cc", nc_log2, vzLog2) FUNC_CC(FUNC_EXP_CC, "exp_cc", nc_exp, vzExp) FUNC_CC(FUNC_EXPM1_CC, "expm1_cc", nc_expm1, vzExpm1) FUNC_CC(FUNC_ABS_CC, "absolute_cc", nc_abs, vzAbs_) FUNC_CC(FUNC_CONJ_CC, "conjugate_cc",nc_conj, vzConj) +FUNC_CC(FUNC_SIGN_CC, "sign_cc", nc_sign, vzSign) +// rint rounds to nearest even integer, matches NumPy behaviour (round doesn't) +FUNC_CC(FUNC_ROUND_CC, "round_cc", nc_rint, vzRint) FUNC_CC(FUNC_CC_LAST, NULL, NULL, NULL) #ifdef ELIDE_FUNC_CC #undef ELIDE_FUNC_CC @@ -180,3 +206,30 @@ FUNC_BC(FUNC_BC_LAST, NULL, NULL, NULL) #undef ELIDE_FUNC_BC #undef FUNC_BC #endif + +// int -> int functions +#ifndef FUNC_II +#define ELIDE_FUNC_II +#define FUNC_II(...) +#endif +FUNC_II(FUNC_SIGN_II, "sign_ii", signi, viSign) +FUNC_II(FUNC_ROUND_II, "round_ii", rinti, viRint) +FUNC_II(FUNC_ABS_II, "absolute_ii", fabsi, viFabs) +FUNC_II(FUNC_II_LAST, NULL, NULL, NULL) +#ifdef ELIDE_FUNC_II +#undef ELIDE_FUNC_II +#undef FUNC_II +#endif + +#ifndef FUNC_LL +#define ELIDE_FUNC_LL +#define FUNC_LL(...) +#endif +FUNC_LL(FUNC_SIGN_LL, "sign_ll", signl, vlSign) +FUNC_LL(FUNC_ROUND_LL, "round_ll", rintl, vlRint) +FUNC_LL(FUNC_ABS_LL, "absolute_ll", fabsl, vlFabs) +FUNC_LL(FUNC_LL_LAST, NULL, NULL, NULL) +#ifdef ELIDE_FUNC_LL +#undef ELIDE_FUNC_LL +#undef FUNC_LL +#endif diff --git a/numexpr/interp_body.cpp b/numexpr/interp_body.cpp index e207e1a..743f8ab 100644 --- a/numexpr/interp_body.cpp +++ b/numexpr/interp_body.cpp @@ -265,6 +265,7 @@ case OP_DIV_III: VEC_ARG2(i_dest = i2 ? (i1 / i2) : 0); case OP_POW_III: VEC_ARG2(i_dest = (i2 < 0) ? (1 / i1) : (int)pow((double)i1, i2)); case OP_MOD_III: VEC_ARG2(i_dest = i2 == 0 ? 0 :((i1 % i2) + i2) % i2); + case OP_FLOORDIV_III: VEC_ARG2(i_dest = i2 ? (i1 / i2) - ((i1 % i2 != 0) && (i1 < 0 != i2 < 0)) : 0); case OP_LSHIFT_III: VEC_ARG2(i_dest = i1 << i2); case OP_RSHIFT_III: VEC_ARG2(i_dest = i1 >> i2); @@ -290,6 +291,7 @@ case OP_POW_LLL: VEC_ARG2(l_dest = (l2 < 0) ? (1 / l1) : (long long)llround(pow((long double)l1, (long double)l2))); #endif case OP_MOD_LLL: VEC_ARG2(l_dest = l2 == 0 ? 0 :((l1 % l2) + l2) % l2); + case OP_FLOORDIV_LLL: VEC_ARG2(l_dest = l2 ? (l1 / l2) - ((l1 % l2 != 0) && (l1 < 0 != l2 < 0)): 0); case OP_LSHIFT_LLL: VEC_ARG2(l_dest = l1 << l2); case OP_RSHIFT_LLL: VEC_ARG2(l_dest = l1 >> l2); @@ -324,6 +326,7 @@ VEC_ARG2(f_dest = powf(f1, f2)); #endif case OP_MOD_FFF: VEC_ARG2(f_dest = f1 - floorf(f1/f2) * f2); + case OP_FLOORDIV_FFF: VEC_ARG2(f_dest = floorf(f1/f2)); case OP_SQRT_FF: #ifdef USE_VML @@ -375,6 +378,7 @@ VEC_ARG2(d_dest = pow(d1, d2)); #endif case OP_MOD_DDD: VEC_ARG2(d_dest = d1 - floor(d1/d2) * d2); + case OP_FLOORDIV_DDD: VEC_ARG2(d_dest = floor(d1/d2)); case OP_SQRT_DD: #ifdef USE_VML @@ -490,6 +494,21 @@ b_dest = functions_bc[arg2](&ca)); #endif + /* Integer return types */ + case OP_FUNC_IIN: +#ifdef USE_VML + VEC_ARG1_VML(functions_ii_vml[arg2](BLOCK_SIZE, + (int*)x1, (int*)dest)); +#else + VEC_ARG1(i_dest = functions_ii[arg2](i1)); +#endif + case OP_FUNC_LLN: +#ifdef USE_VML + VEC_ARG1_VML(functions_ll_vml[arg2](BLOCK_SIZE, + (long*)x1, (long*)dest)); +#else + VEC_ARG1(l_dest = functions_ll[arg2](l1)); +#endif /* Reductions */ case OP_SUM_IIN: VEC_ARG1(i_reduce += i1); diff --git a/numexpr/interpreter.cpp b/numexpr/interpreter.cpp index b724295..409ad3d 100644 --- a/numexpr/interpreter.cpp +++ b/numexpr/interpreter.cpp @@ -18,6 +18,7 @@ #include "complex_functions.hpp" #include "interpreter.hpp" #include "numexpr_object.hpp" +#include "bespoke_functions.hpp" #ifdef _MSC_VER /* Some missing symbols and functions for Win */ @@ -129,6 +130,9 @@ op_signature(int op, unsigned int n) { typedef float (*FuncFFPtr)(float); #ifdef _WIN32 +inline float signf2(float x) { // needed to wait for bespoke_functions to be loaded + return signf(x); +} FuncFFPtr functions_ff[] = { #define FUNC_FF(fop, s, f, f_win32, ...) f_win32, #include "functions.hpp" @@ -142,17 +146,6 @@ FuncFFPtr functions_ff[] = { }; #endif -#ifdef USE_VML -/* Fake vsConj function just for casting purposes inside numexpr */ -static void vsConj(MKL_INT n, const float* x1, float* dest) -{ - MKL_INT j; - for (j=0; j= FUNC_II_LAST) { + PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc); + return -1; + } + } + else if (op == OP_FUNC_LLN) { + if (arg < 0 || arg >= FUNC_LL_LAST) { + PyErr_Format(PyExc_RuntimeError, "invalid program: funccode out of range (%i) at %i", arg, argloc); + return -1; + } } else if (op >= OP_REDUCTION) { ; diff --git a/numexpr/interpreter.hpp b/numexpr/interpreter.hpp index 55f210c..3ec09bb 100644 --- a/numexpr/interpreter.hpp +++ b/numexpr/interpreter.hpp @@ -48,6 +48,18 @@ enum FuncBCCodes { #undef FUNC_BC }; +enum FuncIICodes { +#define FUNC_II(fop, ...) fop, +#include "functions.hpp" +#undef FUNC_II +}; + +enum FuncLLCodes { +#define FUNC_LL(fop, ...) fop, +#include "functions.hpp" +#undef FUNC_LL +}; + enum FuncDDDCodes { #define FUNC_DDD(fop, ...) fop, #include "functions.hpp" diff --git a/numexpr/module.cpp b/numexpr/module.cpp index 649aa17..67629bd 100644 --- a/numexpr/module.cpp +++ b/numexpr/module.cpp @@ -512,7 +512,11 @@ PyInit_interpreter(void) { #define FUNC_DDD(name, sname, ...) add_func(name, sname); #define FUNC_CC(name, sname, ...) add_func(name, sname); #define FUNC_CCC(name, sname, ...) add_func(name, sname); +#define FUNC_II(name, sname, ...) add_func(name, sname); +#define FUNC_LL(name, sname, ...) add_func(name, sname); #include "functions.hpp" +#undef FUNC_LL +#undef FUNC_II #undef FUNC_CCC #undef FUNC_CC #undef FUNC_DDD diff --git a/numexpr/msvc_function_stubs.hpp b/numexpr/msvc_function_stubs.hpp index 27ea54b..6fac288 100644 --- a/numexpr/msvc_function_stubs.hpp +++ b/numexpr/msvc_function_stubs.hpp @@ -35,12 +35,22 @@ #define logf(x) ((float)log((double)(x))) #define log1pf(x) ((float)log1p((double)(x))) #define log10f(x) ((float)log10((double)(x))) +#define log2f(x) ((float)log2((double)(x))) #define expf(x) ((float)exp((double)(x))) #define expm1f(x) ((float)expm1((double)(x))) #define fabsf(x) ((float)fabs((double)(x))) #define fmodf(x, y) ((float)fmod((double)(x), (double)(y))) #define atan2f(x, y) ((float)atan2((double)(x), (double)(y))) +#define hypotf(x, y) ((float)hypot((double)(x), (double)(y))) +#define copysignf(x, y) ((float)copysign((double)(x), (double)(y))) +#define nextafterf(x, y) ((float)nextafter((double)(x), (double)(y))) +#define fmaxf(x, y) ((float)fmaxd((double)(x), (double)(y))) +#define fminf(x, y) ((float)fmind((double)(x), (double)(y))) #define ceilf(x) ((float)ceil((double)(x))) +#define hypotf(x) ((float)hypot((double)(x))) +#define rintf(x) ((float)rint((double)(x))) +#define truncf(x) ((float)trunc((double)(x))) + /* The next are directly called from interp_body.cpp */ #define powf(x, y) ((float)pow((double)(x), (double)(y))) @@ -123,6 +133,10 @@ inline float log10f2(float x) { return log10f(x); } +inline float log2f2(float x) { + return log2f(x); +} + inline float expf2(float x) { return expf(x); } @@ -143,6 +157,27 @@ inline float atan2f2(float x, float y) { return atan2f(x, y); } +inline float hypotf2(float x, float y) { + return hypotf(x, y); +} + +inline float nextafterf2(float x, float y) { + return nextafterf(x, y); +} + +inline float copysignf2(float x, float y) { + return copysignf(x, y); +} + +inline float fmaxf2(float x, float y) { + return fmaxf(x, y); +} + +inline float fminf2(float x, float y) { + return fminf(x, y); +} + + // Boolean output functions inline bool isnanf2(float x) { return isnanf_(x); @@ -171,4 +206,16 @@ inline float floorf2(float x) { return floorf(x); } +inline float rintf2(float x) { + return rintf(x); +} + +inline float truncf2(float x) { + return truncf(x); +} + +inline bool signbitf2(float x) { + return signbitf(x); +} + #endif // NUMEXPR_MSVC_FUNCTION_STUBS_HPP diff --git a/numexpr/necompiler.py b/numexpr/necompiler.py index aea9dfc..8b80737 100644 --- a/numexpr/necompiler.py +++ b/numexpr/necompiler.py @@ -62,6 +62,7 @@ "log", "log1p", "log10", + "log2", "exp", "expm1", "absolute", @@ -73,6 +74,15 @@ "isnan", "isfinite", "isinf", + "hypot", + "round", + "trunc", + "nextafter", + "copysign", + "signbit", + "sign", + "minimum", + "maximum", ] diff --git a/numexpr/numexpr_config.hpp b/numexpr/numexpr_config.hpp index 98f2701..4ed64ab 100644 --- a/numexpr/numexpr_config.hpp +++ b/numexpr/numexpr_config.hpp @@ -41,7 +41,11 @@ #include "mkl_service.h" #endif #include - +//no single precision version of signbit in C++ standard +inline bool signbitf(float x) { return signbit((double)x); } +// To handle overloading of fmax/fmin in cmath +inline double fmaxd(double x, double y) { return fmax(x, y); } +inline double fmind(double x, double y) { return fmin(x, y); } #ifdef _WIN32 #ifndef __MINGW32__ #include "missing_posix_functions.hpp" diff --git a/numexpr/opcodes.hpp b/numexpr/opcodes.hpp index dafd89c..5b1c46f 100644 --- a/numexpr/opcodes.hpp +++ b/numexpr/opcodes.hpp @@ -66,134 +66,144 @@ OPCODE(34, OP_MUL_III, "mul_iii", Ti, Ti, Ti, T0) OPCODE(35, OP_DIV_III, "div_iii", Ti, Ti, Ti, T0) OPCODE(36, OP_POW_III, "pow_iii", Ti, Ti, Ti, T0) OPCODE(37, OP_MOD_III, "mod_iii", Ti, Ti, Ti, T0) +OPCODE(38, OP_FLOORDIV_III, "floordiv_iii", Ti, Ti, Ti, T0) -OPCODE(38, OP_LSHIFT_III, "lshift_iii", Ti, Ti, Ti, T0) -OPCODE(39, OP_RSHIFT_III, "rshift_iii", Ti, Ti, Ti, T0) -OPCODE(40, OP_WHERE_IBII, "where_ibii", Ti, Tb, Ti, Ti) +OPCODE(39, OP_LSHIFT_III, "lshift_iii", Ti, Ti, Ti, T0) +OPCODE(40, OP_RSHIFT_III, "rshift_iii", Ti, Ti, Ti, T0) + +OPCODE(41, OP_WHERE_IBII, "where_ibii", Ti, Tb, Ti, Ti) // Bitwise ops -OPCODE(41, OP_INVERT_II, "invert_ii", Ti, Ti, T0, T0) -OPCODE(42, OP_AND_III, "and_iii", Ti, Ti, Ti, T0) -OPCODE(43, OP_OR_III, "or_iii", Ti, Ti, Ti, T0) -OPCODE(44, OP_XOR_III, "xor_iii", Ti, Ti, Ti, T0) - -OPCODE(45, OP_CAST_LI, "cast_li", Tl, Ti, T0, T0) -OPCODE(46, OP_COPY_LL, "copy_ll", Tl, Tl, T0, T0) -OPCODE(47, OP_ONES_LIKE_LL, "ones_like_ll", Tl, T0, T0, T0) -OPCODE(48, OP_NEG_LL, "neg_ll", Tl, Tl, T0, T0) -OPCODE(49, OP_ADD_LLL, "add_lll", Tl, Tl, Tl, T0) -OPCODE(50, OP_SUB_LLL, "sub_lll", Tl, Tl, Tl, T0) -OPCODE(51, OP_MUL_LLL, "mul_lll", Tl, Tl, Tl, T0) -OPCODE(52, OP_DIV_LLL, "div_lll", Tl, Tl, Tl, T0) -OPCODE(53, OP_POW_LLL, "pow_lll", Tl, Tl, Tl, T0) -OPCODE(54, OP_MOD_LLL, "mod_lll", Tl, Tl, Tl, T0) - -OPCODE(55, OP_LSHIFT_LLL, "lshift_lll", Tl, Tl, Tl, T0) -OPCODE(56, OP_RSHIFT_LLL, "rshift_lll", Tl, Tl, Tl, T0) - -OPCODE(57, OP_WHERE_LBLL, "where_lbll", Tl, Tb, Tl, Tl) +OPCODE(42, OP_INVERT_II, "invert_ii", Ti, Ti, T0, T0) +OPCODE(43, OP_AND_III, "and_iii", Ti, Ti, Ti, T0) +OPCODE(44, OP_OR_III, "or_iii", Ti, Ti, Ti, T0) +OPCODE(45, OP_XOR_III, "xor_iii", Ti, Ti, Ti, T0) + +OPCODE(46, OP_CAST_LI, "cast_li", Tl, Ti, T0, T0) +OPCODE(47, OP_COPY_LL, "copy_ll", Tl, Tl, T0, T0) +OPCODE(48, OP_ONES_LIKE_LL, "ones_like_ll", Tl, T0, T0, T0) +OPCODE(49, OP_NEG_LL, "neg_ll", Tl, Tl, T0, T0) +OPCODE(50, OP_ADD_LLL, "add_lll", Tl, Tl, Tl, T0) +OPCODE(51, OP_SUB_LLL, "sub_lll", Tl, Tl, Tl, T0) +OPCODE(52, OP_MUL_LLL, "mul_lll", Tl, Tl, Tl, T0) +OPCODE(53, OP_DIV_LLL, "div_lll", Tl, Tl, Tl, T0) +OPCODE(54, OP_POW_LLL, "pow_lll", Tl, Tl, Tl, T0) +OPCODE(55, OP_MOD_LLL, "mod_lll", Tl, Tl, Tl, T0) +OPCODE(56, OP_FLOORDIV_LLL, "floordiv_lll", Tl, Tl, Tl, T0) + +OPCODE(57, OP_LSHIFT_LLL, "lshift_lll", Tl, Tl, Tl, T0) +OPCODE(58, OP_RSHIFT_LLL, "rshift_lll", Tl, Tl, Tl, T0) + +OPCODE(59, OP_WHERE_LBLL, "where_lbll", Tl, Tb, Tl, Tl) // Bitwise ops -OPCODE(58, OP_INVERT_LL, "invert_ll", Tl, Tl, T0, T0) -OPCODE(59, OP_AND_LLL, "and_lll", Tl, Tl, Tl, T0) -OPCODE(60, OP_OR_LLL, "or_lll", Tl, Tl, Tl, T0) -OPCODE(61, OP_XOR_LLL, "xor_lll", Tl, Tl, Tl, T0) - -OPCODE(62, OP_CAST_FI, "cast_fi", Tf, Ti, T0, T0) -OPCODE(63, OP_CAST_FL, "cast_fl", Tf, Tl, T0, T0) -OPCODE(64, OP_COPY_FF, "copy_ff", Tf, Tf, T0, T0) -OPCODE(65, OP_ONES_LIKE_FF, "ones_like_ff", Tf, T0, T0, T0) -OPCODE(66, OP_NEG_FF, "neg_ff", Tf, Tf, T0, T0) -OPCODE(67, OP_ADD_FFF, "add_fff", Tf, Tf, Tf, T0) -OPCODE(68, OP_SUB_FFF, "sub_fff", Tf, Tf, Tf, T0) -OPCODE(69, OP_MUL_FFF, "mul_fff", Tf, Tf, Tf, T0) -OPCODE(70, OP_DIV_FFF, "div_fff", Tf, Tf, Tf, T0) -OPCODE(71, OP_POW_FFF, "pow_fff", Tf, Tf, Tf, T0) -OPCODE(72, OP_MOD_FFF, "mod_fff", Tf, Tf, Tf, T0) -OPCODE(73, OP_SQRT_FF, "sqrt_ff", Tf, Tf, T0, T0) -OPCODE(74, OP_WHERE_FBFF, "where_fbff", Tf, Tb, Tf, Tf) -OPCODE(75, OP_FUNC_FFN, "func_ffn", Tf, Tf, Tn, T0) -OPCODE(76, OP_FUNC_FFFN, "func_fffn", Tf, Tf, Tf, Tn) - -OPCODE(77, OP_CAST_DI, "cast_di", Td, Ti, T0, T0) -OPCODE(78, OP_CAST_DL, "cast_dl", Td, Tl, T0, T0) -OPCODE(79, OP_CAST_DF, "cast_df", Td, Tf, T0, T0) -OPCODE(80, OP_COPY_DD, "copy_dd", Td, Td, T0, T0) -OPCODE(81, OP_ONES_LIKE_DD, "ones_like_dd", Td, T0, T0, T0) -OPCODE(82, OP_NEG_DD, "neg_dd", Td, Td, T0, T0) -OPCODE(83, OP_ADD_DDD, "add_ddd", Td, Td, Td, T0) -OPCODE(84, OP_SUB_DDD, "sub_ddd", Td, Td, Td, T0) -OPCODE(85, OP_MUL_DDD, "mul_ddd", Td, Td, Td, T0) -OPCODE(86, OP_DIV_DDD, "div_ddd", Td, Td, Td, T0) -OPCODE(87, OP_POW_DDD, "pow_ddd", Td, Td, Td, T0) -OPCODE(88, OP_MOD_DDD, "mod_ddd", Td, Td, Td, T0) -OPCODE(89, OP_SQRT_DD, "sqrt_dd", Td, Td, T0, T0) -OPCODE(90, OP_WHERE_DBDD, "where_dbdd", Td, Tb, Td, Td) -OPCODE(91, OP_FUNC_DDN, "func_ddn", Td, Td, Tn, T0) -OPCODE(92, OP_FUNC_DDDN, "func_dddn", Td, Td, Td, Tn) - -OPCODE(93, OP_EQ_BCC, "eq_bcc", Tb, Tc, Tc, T0) -OPCODE(94, OP_NE_BCC, "ne_bcc", Tb, Tc, Tc, T0) - -OPCODE(95, OP_CAST_CI, "cast_ci", Tc, Ti, T0, T0) -OPCODE(96, OP_CAST_CL, "cast_cl", Tc, Tl, T0, T0) -OPCODE(97, OP_CAST_CF, "cast_cf", Tc, Tf, T0, T0) -OPCODE(98, OP_CAST_CD, "cast_cd", Tc, Td, T0, T0) -OPCODE(99, OP_ONES_LIKE_CC, "ones_like_cc", Tc, T0, T0, T0) -OPCODE(100, OP_COPY_CC, "copy_cc", Tc, Tc, T0, T0) -OPCODE(101, OP_NEG_CC, "neg_cc", Tc, Tc, T0, T0) -OPCODE(102, OP_ADD_CCC, "add_ccc", Tc, Tc, Tc, T0) -OPCODE(103, OP_SUB_CCC, "sub_ccc", Tc, Tc, Tc, T0) -OPCODE(104, OP_MUL_CCC, "mul_ccc", Tc, Tc, Tc, T0) -OPCODE(105, OP_DIV_CCC, "div_ccc", Tc, Tc, Tc, T0) -OPCODE(106, OP_WHERE_CBCC, "where_cbcc", Tc, Tb, Tc, Tc) -OPCODE(107, OP_FUNC_CCN, "func_ccn", Tc, Tc, Tn, T0) -OPCODE(108, OP_FUNC_CCCN, "func_cccn", Tc, Tc, Tc, Tn) - -OPCODE(109, OP_REAL_DC, "real_dc", Td, Tc, T0, T0) -OPCODE(110, OP_IMAG_DC, "imag_dc", Td, Tc, T0, T0) -OPCODE(111, OP_COMPLEX_CDD, "complex_cdd", Tc, Td, Td, T0) - -OPCODE(112, OP_COPY_SS, "copy_ss", Ts, Ts, T0, T0) - -OPCODE(113, OP_WHERE_BBBB, "where_bbbb", Tb, Tb, Tb, Tb) - -OPCODE(114, OP_CONTAINS_BSS, "contains_bss", Tb, Ts, Ts, T0) +OPCODE(60, OP_INVERT_LL, "invert_ll", Tl, Tl, T0, T0) +OPCODE(61, OP_AND_LLL, "and_lll", Tl, Tl, Tl, T0) +OPCODE(62, OP_OR_LLL, "or_lll", Tl, Tl, Tl, T0) +OPCODE(63, OP_XOR_LLL, "xor_lll", Tl, Tl, Tl, T0) + +OPCODE(64, OP_CAST_FI, "cast_fi", Tf, Ti, T0, T0) +OPCODE(65, OP_CAST_FL, "cast_fl", Tf, Tl, T0, T0) +OPCODE(66, OP_COPY_FF, "copy_ff", Tf, Tf, T0, T0) +OPCODE(67, OP_ONES_LIKE_FF, "ones_like_ff", Tf, T0, T0, T0) +OPCODE(68, OP_NEG_FF, "neg_ff", Tf, Tf, T0, T0) +OPCODE(69, OP_ADD_FFF, "add_fff", Tf, Tf, Tf, T0) +OPCODE(70, OP_SUB_FFF, "sub_fff", Tf, Tf, Tf, T0) +OPCODE(71, OP_MUL_FFF, "mul_fff", Tf, Tf, Tf, T0) +OPCODE(72, OP_DIV_FFF, "div_fff", Tf, Tf, Tf, T0) +OPCODE(73, OP_POW_FFF, "pow_fff", Tf, Tf, Tf, T0) +OPCODE(74, OP_MOD_FFF, "mod_fff", Tf, Tf, Tf, T0) +OPCODE(75, OP_FLOORDIV_FFF, "floordiv_fff", Tf, Tf, Tf, T0) +OPCODE(76, OP_SQRT_FF, "sqrt_ff", Tf, Tf, T0, T0) +OPCODE(77, OP_WHERE_FBFF, "where_fbff", Tf, Tb, Tf, Tf) + +OPCODE(78, OP_FUNC_FFN, "func_ffn", Tf, Tf, Tn, T0) +OPCODE(79, OP_FUNC_FFFN, "func_fffn", Tf, Tf, Tf, Tn) + +OPCODE(80, OP_CAST_DI, "cast_di", Td, Ti, T0, T0) +OPCODE(81, OP_CAST_DL, "cast_dl", Td, Tl, T0, T0) +OPCODE(82, OP_CAST_DF, "cast_df", Td, Tf, T0, T0) +OPCODE(83, OP_COPY_DD, "copy_dd", Td, Td, T0, T0) +OPCODE(84, OP_ONES_LIKE_DD, "ones_like_dd", Td, T0, T0, T0) +OPCODE(85, OP_NEG_DD, "neg_dd", Td, Td, T0, T0) +OPCODE(86, OP_ADD_DDD, "add_ddd", Td, Td, Td, T0) +OPCODE(87, OP_SUB_DDD, "sub_ddd", Td, Td, Td, T0) +OPCODE(88, OP_MUL_DDD, "mul_ddd", Td, Td, Td, T0) +OPCODE(89, OP_DIV_DDD, "div_ddd", Td, Td, Td, T0) +OPCODE(90, OP_POW_DDD, "pow_ddd", Td, Td, Td, T0) +OPCODE(91, OP_MOD_DDD, "mod_ddd", Td, Td, Td, T0) +OPCODE(92, OP_FLOORDIV_DDD, "floordiv_ddd", Td, Td, Td, T0) + +OPCODE(93, OP_SQRT_DD, "sqrt_dd", Td, Td, T0, T0) +OPCODE(94, OP_WHERE_DBDD, "where_dbdd", Td, Tb, Td, Td) +OPCODE(95, OP_FUNC_DDN, "func_ddn", Td, Td, Tn, T0) +OPCODE(96, OP_FUNC_DDDN, "func_dddn", Td, Td, Td, Tn) + +OPCODE(97, OP_EQ_BCC, "eq_bcc", Tb, Tc, Tc, T0) +OPCODE(98, OP_NE_BCC, "ne_bcc", Tb, Tc, Tc, T0) + +OPCODE(99, OP_CAST_CI, "cast_ci", Tc, Ti, T0, T0) +OPCODE(100, OP_CAST_CL, "cast_cl", Tc, Tl, T0, T0) +OPCODE(101, OP_CAST_CF, "cast_cf", Tc, Tf, T0, T0) +OPCODE(102, OP_CAST_CD, "cast_cd", Tc, Td, T0, T0) +OPCODE(103, OP_ONES_LIKE_CC, "ones_like_cc", Tc, T0, T0, T0) +OPCODE(104, OP_COPY_CC, "copy_cc", Tc, Tc, T0, T0) +OPCODE(105, OP_NEG_CC, "neg_cc", Tc, Tc, T0, T0) +OPCODE(106, OP_ADD_CCC, "add_ccc", Tc, Tc, Tc, T0) +OPCODE(107, OP_SUB_CCC, "sub_ccc", Tc, Tc, Tc, T0) +OPCODE(108, OP_MUL_CCC, "mul_ccc", Tc, Tc, Tc, T0) +OPCODE(109, OP_DIV_CCC, "div_ccc", Tc, Tc, Tc, T0) +OPCODE(110, OP_WHERE_CBCC, "where_cbcc", Tc, Tb, Tc, Tc) +OPCODE(111, OP_FUNC_CCN, "func_ccn", Tc, Tc, Tn, T0) +OPCODE(112, OP_FUNC_CCCN, "func_cccn", Tc, Tc, Tc, Tn) + +OPCODE(113, OP_REAL_DC, "real_dc", Td, Tc, T0, T0) +OPCODE(114, OP_IMAG_DC, "imag_dc", Td, Tc, T0, T0) +OPCODE(115, OP_COMPLEX_CDD, "complex_cdd", Tc, Td, Td, T0) + +OPCODE(116, OP_COPY_SS, "copy_ss", Ts, Ts, T0, T0) + +OPCODE(117, OP_WHERE_BBBB, "where_bbbb", Tb, Tb, Tb, Tb) + +OPCODE(118, OP_CONTAINS_BSS, "contains_bss", Tb, Ts, Ts, T0) //Boolean outputs -OPCODE(115, OP_FUNC_BDN, "func_bdn", Tb, Td, Tn, T0) -OPCODE(116, OP_FUNC_BFN, "func_bfn", Tb, Tf, Tn, T0) -OPCODE(117, OP_FUNC_BCN, "func_bcn", Tb, Tc, Tn, T0) +OPCODE(119, OP_FUNC_BDN, "func_bdn", Tb, Td, Tn, T0) +OPCODE(120, OP_FUNC_BFN, "func_bfn", Tb, Tf, Tn, T0) +OPCODE(121, OP_FUNC_BCN, "func_bcn", Tb, Tc, Tn, T0) +//Integer funcs +OPCODE(122, OP_FUNC_IIN, "func_iin", Ti, Ti, Tn, T0) +OPCODE(123, OP_FUNC_LLN, "func_lln", Tl, Tl, Tn, T0) // Reductions always have to be at the end - parts of the code // use > OP_REDUCTION to decide whether operation is a reduction -OPCODE(118, OP_REDUCTION, NULL, T0, T0, T0, T0) +OPCODE(124, OP_REDUCTION, NULL, T0, T0, T0, T0) /* Last argument in a reduction is the axis of the array the reduction should be applied along. */ -OPCODE(119, OP_SUM_IIN, "sum_iin", Ti, Ti, Tn, T0) -OPCODE(120, OP_SUM_LLN, "sum_lln", Tl, Tl, Tn, T0) -OPCODE(121, OP_SUM_FFN, "sum_ffn", Tf, Tf, Tn, T0) -OPCODE(122, OP_SUM_DDN, "sum_ddn", Td, Td, Tn, T0) -OPCODE(123, OP_SUM_CCN, "sum_ccn", Tc, Tc, Tn, T0) - -OPCODE(124, OP_PROD, NULL, T0, T0, T0, T0) -OPCODE(125, OP_PROD_IIN, "prod_iin", Ti, Ti, Tn, T0) -OPCODE(126, OP_PROD_LLN, "prod_lln", Tl, Tl, Tn, T0) -OPCODE(127, OP_PROD_FFN, "prod_ffn", Tf, Tf, Tn, T0) -OPCODE(128, OP_PROD_DDN, "prod_ddn", Td, Td, Tn, T0) -OPCODE(129, OP_PROD_CCN, "prod_ccn", Tc, Tc, Tn, T0) - -OPCODE(130, OP_MIN, NULL, T0, T0, T0, T0) -OPCODE(131, OP_MIN_IIN, "min_iin", Ti, Ti, Tn, T0) -OPCODE(132, OP_MIN_LLN, "min_lln", Tl, Tl, Tn, T0) -OPCODE(133, OP_MIN_FFN, "min_ffn", Tf, Tf, Tn, T0) -OPCODE(134, OP_MIN_DDN, "min_ddn", Td, Td, Tn, T0) - -OPCODE(135, OP_MAX, NULL, T0, T0, T0, T0) -OPCODE(136, OP_MAX_IIN, "max_iin", Ti, Ti, Tn, T0) -OPCODE(137, OP_MAX_LLN, "max_lln", Tl, Tl, Tn, T0) -OPCODE(138, OP_MAX_FFN, "max_ffn", Tf, Tf, Tn, T0) -OPCODE(139, OP_MAX_DDN, "max_ddn", Td, Td, Tn, T0) +OPCODE(125, OP_SUM_IIN, "sum_iin", Ti, Ti, Tn, T0) +OPCODE(126, OP_SUM_LLN, "sum_lln", Tl, Tl, Tn, T0) +OPCODE(127, OP_SUM_FFN, "sum_ffn", Tf, Tf, Tn, T0) +OPCODE(128, OP_SUM_DDN, "sum_ddn", Td, Td, Tn, T0) +OPCODE(129, OP_SUM_CCN, "sum_ccn", Tc, Tc, Tn, T0) + +OPCODE(130, OP_PROD, NULL, T0, T0, T0, T0) +OPCODE(131, OP_PROD_IIN, "prod_iin", Ti, Ti, Tn, T0) +OPCODE(132, OP_PROD_LLN, "prod_lln", Tl, Tl, Tn, T0) +OPCODE(133, OP_PROD_FFN, "prod_ffn", Tf, Tf, Tn, T0) +OPCODE(134, OP_PROD_DDN, "prod_ddn", Td, Td, Tn, T0) +OPCODE(135, OP_PROD_CCN, "prod_ccn", Tc, Tc, Tn, T0) + +OPCODE(136, OP_MIN, NULL, T0, T0, T0, T0) +OPCODE(137, OP_MIN_IIN, "min_iin", Ti, Ti, Tn, T0) +OPCODE(138, OP_MIN_LLN, "min_lln", Tl, Tl, Tn, T0) +OPCODE(139, OP_MIN_FFN, "min_ffn", Tf, Tf, Tn, T0) +OPCODE(140, OP_MIN_DDN, "min_ddn", Td, Td, Tn, T0) + +OPCODE(141, OP_MAX, NULL, T0, T0, T0, T0) +OPCODE(142, OP_MAX_IIN, "max_iin", Ti, Ti, Tn, T0) +OPCODE(143, OP_MAX_LLN, "max_lln", Tl, Tl, Tn, T0) +OPCODE(144, OP_MAX_FFN, "max_ffn", Tf, Tf, Tn, T0) +OPCODE(145, OP_MAX_DDN, "max_ddn", Td, Td, Tn, T0) /* When we get to 255, will maybe have to change code again @@ -201,4 +211,4 @@ When we get to 255, will maybe have to change code again other than unsigned char for OPCODE table) */ /* Should be the last opcode */ -OPCODE(140, OP_END, NULL, T0, T0, T0, T0) +OPCODE(146, OP_END, NULL, T0, T0, T0, T0) diff --git a/numexpr/tests/test_numexpr.py b/numexpr/tests/test_numexpr.py index 488f383..9d97eab 100644 --- a/numexpr/tests/test_numexpr.py +++ b/numexpr/tests/test_numexpr.py @@ -24,10 +24,12 @@ from numpy import all as alltrue from numpy import (allclose, arange, arccos, arccosh, arcsin, arcsinh, arctan, arctan2, arctanh, array, array_equal, cdouble, ceil, conj, - copy, cos, cosh, empty, exp, expm1, float64, floor, fmod, - int32, int64, isinf, isnan, linspace, log, log1p, log10, - ones_like, prod, ravel, rec, shape, sin, sinh, sqrt, sum, - tan, tanh, uint16, where, zeros) + copy, copysign, cos, cosh, empty, exp, expm1, float64, + floor, fmod, hypot, int32, int64, isfinite, isinf, isnan, + linspace, log, log1p, log2, log10, maximum, minimum, + nextafter, ones_like, prod, ravel, rec, round, shape, sign, + signbit, sin, sinh, sqrt, sum, tan, tanh, trunc, uint16, + where, zeros) from numpy.testing import (assert_allclose, assert_array_almost_equal, assert_array_equal, assert_equal) @@ -478,6 +480,24 @@ def test_bitwise_operators(self): assert_array_equal(evaluate("x | y"), x | y) # or assert_array_equal(evaluate("~x"), ~x) # invert + def test_maximum_minimum(self): + for dtype in [float, double, int, np.int64]: + x = arange(10, dtype=dtype) + y = 2 * arange(10, dtype=dtype)[::-1] + assert_array_equal(evaluate("maximum(x,y)"), maximum(x,y)) + assert_array_equal(evaluate("minimum(x,y)"), minimum(x,y)) + + def test_sign_round(self): + for dtype in [float, double, np.int32, np.int64, complex]: + x = arange(10, dtype=dtype) + y = 2 * arange(10, dtype=dtype)[::-1] + r = x-y + if not np.issubdtype(dtype, np.integer): + r[-1] = np.nan + assert evaluate("round(r)").dtype == round(r).dtype + assert evaluate("sign(r)").dtype == sign(r).dtype + assert_array_equal(evaluate("sign(r)"), sign(r)) + assert_array_equal(evaluate("round(r)"), round(r)) def test_rational_expr(self): a = arange(1e6) @@ -689,11 +709,15 @@ def test_negative_mod(self): n = np.array([-360, -360, -360, 360, 360, 360], dtype=np.int32) out_i = evaluate('a % n') assert_equal(out_i, np.mod(a, n)) + main_i = evaluate('a // n') + assert_equal(main_i, a // n) b = a.astype(np.int64) m = n.astype(np.int64) out_l = evaluate('b % m') assert_equal(out_l, np.mod(b, m)) + main_l = evaluate('b // m') + assert_equal(main_l, a // m) def test_negative_power_scalar(self): # Test for issue #428, where the power is negative and the base is an @@ -709,8 +733,8 @@ def test_negative_power_scalar(self): def test_ex_uses_vml(self): vml_funcs = [ "sin", "cos", "tan", "arcsin", "arccos", "arctan", "sinh", "cosh", "tanh", "arcsinh", "arccosh", "arctanh", - "log", "log1p","log10", "exp", "expm1", "abs", "conj", - "arctan2", "fmod"] + "log", "log1p","log10", "log2", "exp", "expm1", "abs", "conj", + "arctan2", "fmod", "hypot"] for func in vml_funcs: strexpr = func+'(a)' _, ex_uses_vml = numexpr.necompiler.getExprNames(strexpr, {}) @@ -723,20 +747,26 @@ def test_bool_funcs(self): a = np.arange(2 * array_size, dtype=dtype) a[array_size//2] = np.nan a[array_size//3] = np.inf + a[array_size//4] = -2 + + assert_equal(evaluate("isnan(a)"), isnan(a)) + assert_equal(evaluate("isfinite(a)"), isfinite(a)) + assert_equal(evaluate("isinf(a)"), isinf(a)) + assert_equal(evaluate("signbit(a)"), signbit(a)) - assert np.all(evaluate("isnan(a)") == np.isnan(a)) - assert np.all(evaluate("isfinite(a)") == np.isfinite(a)) - assert np.all(evaluate("isinf(a)") == np.isinf(a)) a = a.astype(np.float64) assert a.dtype == np.float64 - assert np.all(evaluate("isnan(a)") == np.isnan(a)) - assert np.all(evaluate("isfinite(a)") == np.isfinite(a)) - assert np.all(evaluate("isinf(a)") == np.isinf(a)) + assert_equal(evaluate("isnan(a)"), isnan(a)) + assert_equal(evaluate("isfinite(a)"), isfinite(a)) + assert_equal(evaluate("isinf(a)"), isinf(a)) + assert_equal(evaluate("signbit(a)"), signbit(a)) + a = a.astype(np.complex128) assert a.dtype == np.complex128 assert np.all(evaluate("isnan(a)") == np.isnan(a)) assert np.all(evaluate("isfinite(a)") == np.isfinite(a)) assert np.all(evaluate("isinf(a)") == np.isinf(a)) + # signbit not defined for complex numbers if 'sparc' not in platform.machine(): # Execution order set here so as to not use too many threads @@ -803,13 +833,13 @@ def test_changing_nthreads_01_dec(self): for func in ['copy', 'ones_like', 'sqrt', 'sin', 'cos', 'tan', 'arcsin', 'arccos', 'arctan', 'sinh', 'cosh', 'tanh', 'arcsinh', 'arccosh', 'arctanh', - 'log', 'log1p', 'log10', 'exp', 'expm1', 'abs', 'conj', - 'ceil', 'floor']: + 'log', 'log1p', 'log10', "log2", 'exp', 'expm1', 'abs', 'conj', + 'ceil', 'floor', 'round', 'trunc', 'sign']: func1tests.append("a + %s(b+c)" % func) tests.append(('1_ARG_FUNCS', func1tests)) func2tests = [] -for func in ['arctan2', 'fmod']: +for func in ['arctan2', 'fmod', 'hypot', 'nextafter', 'copysign']: func2tests.append("a + %s(b+c, d+1)" % func) func2tests.append("a + %s(b+c, 1)" % func) func2tests.append("a + %s(1, d+1)" % func) @@ -873,6 +903,8 @@ class Skip(Exception): pass or "%" in expr or "arctan2" in expr or "fmod" in expr + # or "hypot" in expr + # or "nextafter" in expr or "floor" in expr or "ceil" in expr )