Repository: numpy/numpy

Issue:
numpy.poly gives different answers for vector and diagonal matrix inputs
The following code demonstrates this bug:

``` python
import numpy as np
N = 20
v = np.arange(1,N+1)
c1 = np.poly(v)
c2 = np.poly(np.diag(v))
print c1-c2
```

The output is not a zero vector.

See http://nbviewer.ipython.org/gist/ketch/6be81962868fa9e4fb4f for more.



Discussion:
The vector case does not cast to floating point. Adding `v =
v.astype(float)` or `v = 1.0*np.arange(1,N+1)` is a workaround.

When fed the integer vector, why does poly give back an answer with zero digits of accuracy and no warning?

The reason for the bug is obvious from the source code, given the
general policy of no warnings on integer overflows.

Wouldn't it make sense to cast the input to `np.double` unless it is `np.complex` or `np.object`? That is what is going to happen if your matrix is 2D when `eigvals` gets called, right? Or perhaps add some flag that lets you keep integers, but uses the largest available integer type.

Probably cast to floats is sensible behavior, since it's a likely
purpose to evaluate the polynomials at arbitrary points. If integer
calculations are desired, I think they would need to be done with
arbitrary precision integers that cannot overflow. In Scipy, several
functions have a exact= keyword to choose between float and arb integer
calculations.

Yes, [`scipy.misc.comb`](http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.misc.comb.html) comes to mind.

On Mon, Sep 22, 2014 at 8:17 AM, Jaime notifications@github.com wrote:

> Wouldn't it make sense to cast the input to np.double unless it is
> np.complex or np.object? That is what is going to happen if your matrix
> is 2D when eigvals gets called, right? Or perhaps add some flag that lets
> you keep integers, but uses the largest available integer type.

The Polynomial class converts to floats.

Chuck


Resolving patch:
diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py
--- a/numpy/lib/polynomial.py
+++ b/numpy/lib/polynomial.py
@@ -12,10 +12,11 @@
 import warnings
 import numpy.core.numeric as NX
 
-from numpy.core import isscalar, abs, finfo, atleast_1d, hstack, dot
+from numpy.core import (isscalar, abs, finfo, atleast_1d, hstack, dot, array,
+                        ones)
 from numpy.lib.twodim_base import diag, vander
 from numpy.lib.function_base import trim_zeros, sort_complex
-from numpy.lib.type_check import iscomplex, real, imag
+from numpy.lib.type_check import iscomplex, real, imag, mintypecode
 from numpy.linalg import eigvals, lstsq, inv
 
 class RankWarning(UserWarning):
@@ -122,19 +123,24 @@ def poly(seq_of_zeros):
     """
     seq_of_zeros = atleast_1d(seq_of_zeros)
     sh = seq_of_zeros.shape
+
     if len(sh) == 2 and sh[0] == sh[1] and sh[0] != 0:
         seq_of_zeros = eigvals(seq_of_zeros)
     elif len(sh) == 1:
-        pass
+        dt = seq_of_zeros.dtype
+        # Let object arrays slip through, e.g. for arbitrary precision
+        if dt != object:
+            seq_of_zeros = seq_of_zeros.astype(mintypecode(dt.char))
     else:
         raise ValueError("input must be 1d or non-empty square 2d array.")
 
     if len(seq_of_zeros) == 0:
         return 1.0
-
-    a = [1]
+    dt = seq_of_zeros.dtype
+    a = ones((1,), dtype=dt)
     for k in range(len(seq_of_zeros)):
-        a = NX.convolve(a, [1, -seq_of_zeros[k]], mode='full')
+        a = NX.convolve(a, array([1, -seq_of_zeros[k]], dtype=dt),
+                        mode='full')
 
     if issubclass(a.dtype.type, NX.complexfloating):
         # if complex roots are all complex conjugates, the roots are real.


Test patch:
diff --git a/numpy/lib/tests/test_polynomial.py b/numpy/lib/tests/test_polynomial.py
--- a/numpy/lib/tests/test_polynomial.py
+++ b/numpy/lib/tests/test_polynomial.py
@@ -153,6 +153,9 @@ def test_objects(self):
         assert_(p2[3] == Decimal("1.333333333333333333333333333"))
         assert_(p2[2] == Decimal('1.5'))
         assert_(np.issubdtype(p2.coeffs.dtype, np.object_))
+        p = np.poly([Decimal(1), Decimal(2)])
+        assert_equal(np.poly([Decimal(1), Decimal(2)]),
+                     [1, Decimal(-3), Decimal(2)])
 
     def test_complex(self):
         p = np.poly1d([3j, 2j, 1j])
@@ -173,5 +176,13 @@ def test_zero_dims(self):
         except ValueError:
             pass
 
+    def test_poly_int_overflow(self):
+        """
+        Regression test for gh-5096.
+        """
+        v = np.arange(1, 21)
+        assert_almost_equal(np.poly(v), np.poly(np.diag(v)))
+
+
 if __name__ == "__main__":
     run_module_suite()