Jean Brouwers <[EMAIL PROTECTED]> added the comment:

Mark,
Thank you very much for your comments.  Here is my initial response to the
first 3.

(1) Attached is an attempt to address the 1st issue (just the
mathmodule). The macros PyFPE_START_PROTECT/_END_PROTECT have been moved
outside the main loop and the errno is set following the IEEE 754 rules as
you suggested.

One related issue is testing these, how can a NaN and +/-Infinity float
object be created in Python?

(2) On your 2nd comment, supporting non-IEEE floating point, perhaps the
Kahan method should be used in that case.  If so, the next question is how
to detect that?  There are two symbols in pyconfig.h HAVE_IEEEFP_H and
HAVE_LIBIEEE.  Are those the proper ones to determine IEEE floating point
support?

(3) On the 3rd comment, Raymond and I did discus using a single function to
be called by the math and cmath modules.  The question is where should that
function reside?  The math and cmath modules are not the right place since
both are loadable modules.  It will have to be somewhere inside the Python
main/core.

Also, depending on the implementation of that function, it may require
iterating the complex sequence twice.  And that will force the C complex
numbers to be created twice by the PyComplex_AsCComplex() call.  Would that
be a concern?

/Jean Brouwers

On Sun, May 11, 2008 at 1:49 PM, Mark Dickinson <[EMAIL PROTECTED]>
wrote:

>
> Mark Dickinson <[EMAIL PROTECTED]> added the comment:
>
> One more question:
>
> What are the use cases for an exact summation algorithm?  That is, in what
> situations does one care about exactness rather than simply accuracy?  I
> know that loss of accuracy is a problem in things like numeric integration
> routines, but something like Kahan summation (faster and simpler, but not
> exact) usually takes care of that.
>
> __________________________________
> Tracker <[EMAIL PROTECTED]>
> <http://bugs.python.org/issue2819>
> __________________________________
>

Added file: http://bugs.python.org/file10306/unnamed
Added file: http://bugs.python.org/file10307/mathmodule.c.2.6a3.take2.diff

__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue2819>
__________________________________
Mark,<div><br></div><div>Thank you very much for your comments. &nbsp;Here is 
my initial response to the first 3.</div><div><br></div><div><br></div><div>(1) 
Attached is an attempt to address the 1st issue (just the mathmodule).&nbsp;The 
macros&nbsp;<span class="Apple-style-span" style="border-collapse: collapse; 
">PyFPE_START_PROTECT/_END_PROTECT<span class="Apple-style-span" 
style="border-collapse: separate; ">&nbsp;have been moved outside the main loop 
and the errno is set following the IEEE 754 rules as you 
suggested.</span></span></div>
<div><br></div><div>One related issue is testing these, how can a NaN and 
+/-Infinity float object be created in 
Python?</div><div><br></div><div><br></div><div><div>(2) On your 2nd comment, 
supporting non-IEEE floating point, perhaps the Kahan method should be used in 
that case. &nbsp;If so, the next question is how to detect that? &nbsp;There 
are two symbols in pyconfig.h HAVE_IEEEFP_H and HAVE_LIBIEEE. &nbsp;Are those 
the proper ones to determine IEEE floating point support?</div>
<div><br></div><div><br></div><div>(3) On the 3rd comment, Raymond and I did 
discus using a single function to be called by the math and cmath modules. 
&nbsp;The question is where should that function reside? &nbsp;The math and 
cmath modules are not the right place since both are loadable modules. &nbsp;It 
will have to be somewhere inside the Python main/core.</div>
<div><br></div><div>Also, depending on the implementation of that function, it 
may require iterating the complex sequence twice. &nbsp;And that will force the 
C complex numbers to be created twice by the&nbsp;PyComplex_AsCComplex() call. 
&nbsp;Would that be a concern?</div>
<div><br></div><div><br></div><div>/Jean 
Brouwers</div><div><br></div><div>&nbsp;</div><div><br></div><div 
class="gmail_quote">On Sun, May 11, 2008 at 1:49 PM, Mark Dickinson &lt;<a 
href="mailto:[EMAIL PROTECTED]">[EMAIL PROTECTED]</a>&gt; wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc 
solid;padding-left:1ex;"><div class="Ih2E3d"><br>
Mark Dickinson &lt;<a href="mailto:[EMAIL PROTECTED]">[EMAIL PROTECTED]</a>&gt; 
added the comment:<br>
<br>
</div>One more question:<br>
<br>
What are the use cases for an exact summation algorithm? &nbsp;That is, in 
what<br>
situations does one care about exactness rather than simply accuracy? 
&nbsp;I<br>
know that loss of accuracy is a problem in things like numeric integration<br>
routines, but something like Kahan summation (faster and simpler, but not<br>
exact) usually takes care of that.<br>
<div><div></div><div class="Wj3C7c"><br>
__________________________________<br>
Tracker &lt;<a href="mailto:[EMAIL PROTECTED]">[EMAIL PROTECTED]</a>&gt;<br>
&lt;<a href="http://bugs.python.org/issue2819"; 
target="_blank">http://bugs.python.org/issue2819</a>&gt;<br>
__________________________________<br>
</div></div></blockquote></div><br></div>
--- Python-2.6a3/Modules/mathmoduleORIG.c       2008-04-20 18:55:50.000000000 
-0700
+++ Python-2.6a3/Modules/mathmodule.c   2008-05-12 08:51:15.000000000 -0700
@@ -307,6 +307,186 @@
 FUNC1(tanh, tanh, 0,
       "tanh(x)\n\nReturn the hyperbolic tangent of x.")
 
+/* Summation function as function msum() in this Python recipe
+   <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/393090>
+
+   def msum(iterable):
+       "Full precision summation using multiple floats for intermediate values"
+
+       # Rounded x+y stored in hi with the round-off stored in lo.  Together
+       # hi+lo are exactly equal to x+y.  The inner loop applies hi/lo 
summation
+       # to each partial so that the list of partial sums remains exact.
+
+       # Depends on IEEE-754 arithmetic guarantees.  See proof of correctness 
at:
+       # 
www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps
+
+       ps = []  # sorted, non-overlapping partial sums
+       for x in iterable:
+           i = 0
+           for y in ps:
+               if abs(x) < abs(y):
+                   x, y = y, x
+               hi = x + y
+               lo = y - (hi - x)
+               if lo:
+                   ps[i] = lo
+                   i += 1
+               x = hi
+           ps[i:] = [x]
+       return sum(ps, 0.0)
+
+   Note, a duplicate of this code is in file ./cmathmodule.c.
+   Make any changes in both places.
+*/
+
+static double
+_math_sum_add(double x, double y)
+{
+       double s;
+
+       errno = 0;
+       s = x + y;
+       /* following the IEEE 754 rules for summation:
+
+       1. if the summands include a NaN, return a NaN
+
+       2. else if the summands include infinities of
+          both signs, raise ValueError,
+
+       3. else if the summands include infinities of
+          only one sign, return infinity with that sign,
+
+       4. else (all summands are finite) if the result
+          is infinite, raise OverflowError.  The result
+          can never be a NaN if all summands are finite.
+       */
+       if (Py_IS_NAN(s)) {
+               if (!Py_IS_NAN(x) && !Py_IS_NAN(y))
+                       errno = EDOM;
+               else
+                       errno = 0;  /* rule 1 */
+       }
+       else if (Py_IS_INFINITY(s)) {
+               if (Py_IS_FINITE(x) && Py_IS_FINITE(y))
+                       errno = ERANGE;  /* rule 4 */
+               else if (Py_IS_INFINITY(x) && Py_IS_INFINITY(y) && x != y)
+                       errno = EDOM;  /* rule 2 */
+               else
+                       errno = 0;  /* rule 3 */
+       }
+       return s;
+}
+
+static PyObject*
+math_sum(PyObject *self, PyObject *args)
+{
+#      define MATH_SUM_PS  128  /* stack partials */
+
+       Py_ssize_t i, j, n = 0, m = MATH_SUM_PS;
+       double x, y, s, hi, lo, ps[MATH_SUM_PS], *p = ps;
+       PyObject *seq, *iter, *item, *sum = NULL;
+
+       if (!PyArg_UnpackTuple(args, "sum", 1, 2, &seq, &sum))
+               return NULL;
+
+       iter = PyObject_GetIter(seq);
+       if (iter == NULL)
+               return NULL;
+
+       if (sum != NULL) { 
+               s = PyFloat_AsDouble(sum);
+               sum = NULL;
+               if (s == -1.0 && PyErr_Occurred())
+                       goto math_sum_exit;
+               p[n++] = s;
+       } else
+               p[0] = 0.0;
+
+       PyFPE_START_PROTECT("sum", goto math_sum_exit)
+
+       /* for x in iterable */
+       for(;;) {
+               /* some invariants */
+               assert(sum == NULL);
+               assert(n >= 0);
+               assert(m >= n);
+               assert((m == MATH_SUM_PS && p == ps) ||
+                      (m >  MATH_SUM_PS && p != NULL));
+
+               item = PyIter_Next(iter);
+               if (item == NULL) {
+                       if (PyErr_Occurred())
+                               goto math_sum_error;
+                       else
+                               break;
+               }
+               x = PyFloat_AsDouble(item);
+               Py_DECREF(item);
+               if (x == -1.0 && PyErr_Occurred())
+                       goto math_sum_error;
+               /* i = 0; for y in partials */
+               for (i = j = 0; j < n; j++) {
+                       y = p[j];
+                       hi = _math_sum_add(x, y);
+                       if (errno && is_error(hi))
+                               goto math_sum_error;
+                       /* lo = min_x_y - (hi - max_x_y) */
+                       lo = fabs(x) < fabs(y)
+                          ? x - (hi - y)
+                          : y - (hi - x);
+                       if (lo != 0.0)
+                               p[i++] = lo;
+                       x = hi;
+               }
+               /* ps[i:] = [x] */
+               n = i;
+               if (n >= m) {  /* extend partials */
+                       void *v = NULL;
+                       m += m / 2;  /* plus 50% */
+                       if (n < m && m < (PY_SSIZE_T_MAX / sizeof(double))) {
+                               if (p == ps) {
+                                       v = PyMem_Malloc(sizeof(double) * m);
+                                       if (v != NULL)
+                                               memcpy(v, ps, sizeof(double) * 
n);
+                               } else
+                                       v = PyMem_Realloc(p, sizeof(double) * 
m);
+                       }
+                       if (v == NULL) {  /* no memory or size overflow */
+                               PyErr_SetString(PyExc_MemoryError, "math sum 
error");
+                               goto math_sum_error;
+                       }
+                       p = (double*) v;
+               }
+               p[n++] = x;
+       }
+
+       /* sum(ps, 0.0) */
+       s = p[0];  /* always valid */
+       for (i = 1; i < n; i++) {
+               s = _math_sum_add(p[i], s);
+               if (errno && is_error(s))
+                       goto math_sum_error;
+       }
+       sum = PyFloat_FromDouble(s);
+
+math_sum_error:
+       PyFPE_END_PROTECT(s)
+
+math_sum_exit:
+       Py_DECREF(iter);
+       if (p != ps)
+               PyMem_Free(p);
+       return sum;
+
+#      undef MATH_SUM_PS
+}
+
+PyDoc_STRVAR(math_sum_doc,
+"sum(sequence[, start=0])\n"
+"\n"
+"Return the full precision sum of a sequence of numbers plus the value\n"
+"of parameter 'start'.  When the sequence is empty, return start.");
+
 static PyObject *
 math_trunc(PyObject *self, PyObject *number)
 {
@@ -718,6 +898,7 @@
        {"sin",         math_sin,       METH_O,         math_sin_doc},
        {"sinh",        math_sinh,      METH_O,         math_sinh_doc},
        {"sqrt",        math_sqrt,      METH_O,         math_sqrt_doc},
+       {"sum",         math_sum,       METH_VARARGS,   math_sum_doc},
        {"tan",         math_tan,       METH_O,         math_tan_doc},
        {"tanh",        math_tanh,      METH_O,         math_tanh_doc},
        {"trunc",       math_trunc,     METH_O,         math_trunc_doc},
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to