[Numpy-svn] r5954 - in trunk/numpy/core: code_generators src tests

numpy-svn@scip... numpy-svn@scip...
Tue Oct 21 15:13:22 CDT 2008


Author: charris
Date: 2008-10-21 15:13:17 -0500 (Tue, 21 Oct 2008)
New Revision: 5954

Modified:
   trunk/numpy/core/code_generators/generate_umath.py
   trunk/numpy/core/src/umathmodule.c.src
   trunk/numpy/core/tests/test_umath.py
Log:
Change way maximum and minimum deal with nans.
Add ufuncs fmax and fmin.

In the following, a complex number is considered a nan if the real or imaginary
part is nan. This means that there are many different complex numbers that are
nans and this effects the nan values returned by the maximum, minimum, fmax, and
fmin ufuncs.

The maximum and minimum ufuncs are the same as before unless nans are involved.
In the case of nans, if both values being compared are nans, then the first is
returned, otherwise the nan value is returned. This has the effect of
propagating nans.

The fmax and fmin ufuncs return the same values as maximum and minimum if
neither value being compared is nan.  In the case of nans, if both values being
compared are nans, then the first is returned, otherwise the non-nan value is
returned. This has the effect that nans are ignored unless both values are nan.


Modified: trunk/numpy/core/code_generators/generate_umath.py
===================================================================
--- trunk/numpy/core/code_generators/generate_umath.py	2008-10-21 04:49:44 UTC (rev 5953)
+++ trunk/numpy/core/code_generators/generate_umath.py	2008-10-21 20:13:17 UTC (rev 5954)
@@ -316,6 +316,16 @@
           TD(noobj),
           TD(O, f='_npy_ObjectMin')
           ),
+'fmax' :
+    Ufunc(2, 1, None,
+          "",
+          TD(inexact)
+          ),
+'fmin' :
+    Ufunc(2, 1, None,
+          "",
+          TD(inexact)
+          ),
 'bitwise_and' :
     Ufunc(2, 1, One,
           docstrings.get('numpy.core.umath.bitwise_and'),

Modified: trunk/numpy/core/src/umathmodule.c.src
===================================================================
--- trunk/numpy/core/src/umathmodule.c.src	2008-10-21 04:49:44 UTC (rev 5953)
+++ trunk/numpy/core/src/umathmodule.c.src	2008-10-21 20:13:17 UTC (rev 5954)
@@ -654,7 +654,7 @@
 {
     UNARY_LOOP {
         const @s@@type@ in1 = *(@s@@type@ *)ip1;
-        *((@s@@type@ *)op) = 1.0/in1;
+        *((@s@@type@ *)op) = (@s@@type@)(1.0/in1);
     }
 }
 
@@ -924,6 +924,9 @@
  *  #C = F, , L#
  */
 
+#define ONE 1.0@c@
+#define ZERO 0.0@c@
+
 /**begin repeat1
  * Arithmetic
  * # kind = add, subtract, multiply, divide#
@@ -991,7 +994,7 @@
 
 /**begin repeat1
  * #kind = maximum, minimum#
- * #OP =  >, <#
+ * #OP =  >=, <=#
  **/
 static void
 @TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
@@ -1000,12 +1003,28 @@
     BINARY_LOOP {
         const @type@ in1 = *(@type@ *)ip1;
         const @type@ in2 = *(@type@ *)ip2;
-        *((@type@ *)op) = in1 @OP@ in2 ? in1 : in2;
+        *((@type@ *)op) = (in1 @OP@ in2 || isnan(in1)) ? in1 : in2;
     }
 }
 /**end repeat1**/
 
+/**begin repeat1
+ * #kind = fmax, fmin#
+ * #OP =  >=, <=#
+ **/
 static void
+@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func)
+{
+    /*  */
+    BINARY_LOOP {
+        const @type@ in1 = *(@type@ *)ip1;
+        const @type@ in2 = *(@type@ *)ip2;
+        *((@type@ *)op) = (in1 @OP@ in2 || isnan(in2)) ? in1 : in2;
+    }
+}
+/**end repeat1**/
+
+static void
 @TYPE@_floor_divide(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
 {
     BINARY_LOOP {
@@ -1045,7 +1064,7 @@
 {
     UNARY_LOOP {
         const @type@ in1 = *(@type@ *)ip1;
-        *((@type@ *)op) = 1.0/in1;
+        *((@type@ *)op) = ONE/in1;
     }
 }
 
@@ -1053,7 +1072,7 @@
 @TYPE@_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data))
 {
     OUTPUT_LOOP {
-        *((@type@ *)op) = 1;
+        *((@type@ *)op) = ONE;
     }
 }
 
@@ -1071,7 +1090,7 @@
 {
     UNARY_LOOP {
         const @type@ in1 = *(@type@ *)ip1;
-        const @type@ tmp = (in1 > 0) ? in1 : -in1;
+        const @type@ tmp = in1 > 0 ? in1 : -in1;
         /* add 0 to clear -0.0 */
         *((@type@ *)op) = tmp + 0;
     }
@@ -1138,6 +1157,9 @@
 
 #define @TYPE@_true_divide @TYPE@_divide
 
+#undef ONE
+#undef ZERO
+
 /**end repeat**/
 
 
@@ -1155,6 +1177,11 @@
  * #c = f, , l#
  */
 
+#define CGE(xr,xi,yr,yi) (xr > yr || (xr == yr && xi >= yi))
+#define CLE(xr,xi,yr,yi) (xr < yr || (xr == yr && xi <= yi))
+#define ONE 1.0@c@
+#define ZERO 0.0@c@
+
 /**begin repeat1
  * arithmetic
  * #kind = add, subtract#
@@ -1348,8 +1375,8 @@
 @CTYPE@_ones_like(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(data))
 {
     OUTPUT_LOOP {
-        ((@type@ *)op)[0] = 1;
-        ((@type@ *)op)[1] = 0;
+        ((@type@ *)op)[0] = ONE;
+        ((@type@ *)op)[1] = ZERO;
     }
 }
 
@@ -1380,29 +1407,30 @@
         const @type@ in1r = ((@type@ *)ip1)[0];
         const @type@ in1i = ((@type@ *)ip1)[1];
         if (in1r > 0) {
-            ((@type@ *)op)[0] = 1;
+            ((@type@ *)op)[0] = ONE;
         }
         else if (in1r < 0) {
-            ((@type@ *)op)[0] = -1;
+            ((@type@ *)op)[0] = -ONE;
         }
         else {
             if (in1i > 0) {
-                ((@type@ *)op)[0] = 1;
+                ((@type@ *)op)[0] = ONE;
             }
             else if (in1i < 0) {
-                ((@type@ *)op)[0] = -1;
+                ((@type@ *)op)[0] = -ONE;
             }
             else {
-                ((@type@ *)op)[0] = 0;
+                ((@type@ *)op)[0] = ZERO;
             }
         }
-        ((@type@ *)op)[1] = 0;
+        ((@type@ *)op)[1] = ZERO;
     }
 }
 
 /**begin repeat1
  * #kind = maximum, minimum#
- * #OP = >, <#
+ * #OP1 = CGE, CLE#
+ * #OP2 = CLE, CGE#
  */
 static void
 @CTYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
@@ -1412,7 +1440,7 @@
         const @type@ in1i = ((@type@ *)ip1)[1];
         const @type@ in2r = ((@type@ *)ip2)[0];
         const @type@ in2i = ((@type@ *)ip2)[1];
-        if (in1r @OP@ in2r || ((in1r == in2r) && (in1i @OP@ in2i))) {
+        if (@OP1@(in1r, in1i, in2r, in2i) || isnan(in1r) || isnan(in1i)) {
             ((@type@ *)op)[0] = in1r;
             ((@type@ *)op)[1] = in1i;
         }
@@ -1424,7 +1452,37 @@
 }
 /**end repeat1**/
 
+/**begin repeat1
+ * #kind = fmax, fmin#
+ * #OP1 = CGE, CLE#
+ */
+static void
+@CTYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func)
+{
+    BINARY_LOOP {
+        const @type@ in1r = ((@type@ *)ip1)[0];
+        const @type@ in1i = ((@type@ *)ip1)[1];
+        const @type@ in2r = ((@type@ *)ip2)[0];
+        const @type@ in2i = ((@type@ *)ip2)[1];
+        if (@OP1@(in1r, in1i, in2r, in2i) || isnan(in2r) || isnan(in2i)) {
+            ((@type@ *)op)[0] = in1r;
+            ((@type@ *)op)[1] = in1i;
+        }
+        else {
+            ((@type@ *)op)[0] = in2r;
+            ((@type@ *)op)[1] = in2i;
+        }
+    }
+}
+/**end repeat1**/
+
 #define @CTYPE@_true_divide @CTYPE@_divide
+
+#undef CGE
+#undef CLE
+#undef ONE
+#undef ZERO
+
 /**end repeat**/
 
 /*
@@ -1447,6 +1505,7 @@
 }
 /**end repeat**/
 
+static void
 OBJECT_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
 {
     PyObject *zero = PyInt_FromLong(0);
@@ -1464,6 +1523,16 @@
  */
 
 
+/*
+ *****************************************************************************
+ **                            SETUP UFUNCS                                 **
+ *****************************************************************************
+ */
+
+#include "__umath_generated.c"
+#include "ufuncobject.c"
+#include "__ufunc_api.c"
+
 static PyUFuncGenericFunction frexp_functions[] = {
 #ifdef HAVE_FREXPF
     FLOAT_frexp,
@@ -1485,7 +1554,6 @@
 #endif
 };
 
-
 static PyUFuncGenericFunction ldexp_functions[] = {
 #ifdef HAVE_LDEXPF
     FLOAT_ldexp,
@@ -1507,10 +1575,6 @@
 };
 
 
-#include "__umath_generated.c"
-#include "ufuncobject.c"
-#include "__ufunc_api.c"
-
 static double
 pinf_init(void)
 {

Modified: trunk/numpy/core/tests/test_umath.py
===================================================================
--- trunk/numpy/core/tests/test_umath.py	2008-10-21 04:49:44 UTC (rev 5953)
+++ trunk/numpy/core/tests/test_umath.py	2008-10-21 20:13:17 UTC (rev 5954)
@@ -47,13 +47,84 @@
 
 class TestMaximum(TestCase):
     def test_reduce_complex(self):
-        assert_equal(ncu.maximum.reduce([1,2j]),1)
-        assert_equal(ncu.maximum.reduce([1+3j,2j]),1+3j)
+        assert_equal(np.maximum.reduce([1,2j]),1)
+        assert_equal(np.maximum.reduce([1+3j,2j]),1+3j)
 
+    def test_float_nans(self):
+        nan = np.nan
+        arg1 = np.array([0,   nan, nan])
+        arg2 = np.array([nan, 0,   nan])
+        out  = np.array([nan, nan, nan])
+        assert_equal(np.maximum(arg1, arg2), out)
+
+    def test_complex_nans(self):
+        nan = np.nan
+        for cnan in [nan, nan*1j, nan + nan*1j] :
+            arg1 = np.array([0, cnan, cnan], dtype=np.complex)
+            arg2 = np.array([cnan, 0, cnan], dtype=np.complex)
+            out  = np.array([nan, nan, nan], dtype=np.complex)
+            assert_equal(np.maximum(arg1, arg2), out)
+
 class TestMinimum(TestCase):
     def test_reduce_complex(self):
-        assert_equal(ncu.minimum.reduce([1,2j]),2j)
+        assert_equal(np.minimum.reduce([1,2j]),2j)
+        assert_equal(np.minimum.reduce([1+3j,2j]),2j)
 
+    def test_float_nans(self):
+        nan = np.nan
+        arg1 = np.array([0,   nan, nan])
+        arg2 = np.array([nan, 0,   nan])
+        out  = np.array([nan, nan, nan])
+        assert_equal(np.minimum(arg1, arg2), out)
+
+    def test_complex_nans(self):
+        nan = np.nan
+        for cnan in [nan, nan*1j, nan + nan*1j] :
+            arg1 = np.array([0, cnan, cnan], dtype=np.complex)
+            arg2 = np.array([cnan, 0, cnan], dtype=np.complex)
+            out  = np.array([nan, nan, nan], dtype=np.complex)
+            assert_equal(np.minimum(arg1, arg2), out)
+
+class TestFmax(TestCase):
+    def test_reduce_complex(self):
+        assert_equal(np.fmax.reduce([1,2j]),1)
+        assert_equal(np.fmax.reduce([1+3j,2j]),1+3j)
+
+    def test_float_nans(self):
+        nan = np.nan
+        arg1 = np.array([0,   nan, nan])
+        arg2 = np.array([nan, 0,   nan])
+        out  = np.array([0,   0,   nan])
+        assert_equal(np.fmax(arg1, arg2), out)
+
+    def test_complex_nans(self):
+        nan = np.nan
+        for cnan in [nan, nan*1j, nan + nan*1j] :
+            arg1 = np.array([0, cnan, cnan], dtype=np.complex)
+            arg2 = np.array([cnan, 0, cnan], dtype=np.complex)
+            out  = np.array([0,    0, nan], dtype=np.complex)
+            assert_equal(np.fmax(arg1, arg2), out)
+
+class TestFmin(TestCase):
+    def test_reduce_complex(self):
+        assert_equal(np.fmin.reduce([1,2j]),2j)
+        assert_equal(np.fmin.reduce([1+3j,2j]),2j)
+
+    def test_float_nans(self):
+        nan = np.nan
+        arg1 = np.array([0,   nan, nan])
+        arg2 = np.array([nan, 0,   nan])
+        out  = np.array([0,   0,   nan])
+        assert_equal(np.fmin(arg1, arg2), out)
+
+    def test_complex_nans(self):
+        nan = np.nan
+        for cnan in [nan, nan*1j, nan + nan*1j] :
+            arg1 = np.array([0, cnan, cnan], dtype=np.complex)
+            arg2 = np.array([cnan, 0, cnan], dtype=np.complex)
+            out  = np.array([0,    0, nan], dtype=np.complex)
+            assert_equal(np.fmin(arg1, arg2), out)
+
 class TestFloatingPoint(TestCase):
     def test_floating_point(self):
         assert_equal(ncu.FLOATING_POINT_SUPPORT, 1)



More information about the Numpy-svn mailing list