Repository: numpy/numpy

Issue:
Problems encountered when trying to upgrade sagemath to numpy-1.10.1
I have been looking on and off at moving sagemath beyond numpy 1.8.x and numpy 1.10.1 behaves better for us than the 1.9 series [ see http://trac.sagemath.org/ticket/17642]. However we have a strange conversion problem within numpy's `linspace` function

```
sage: import numpy
sage: u1min, u1max = -1.5, 1.5
sage: u1num = 10
sage: u1min.__array_interface__
{'typestr': '=f8'}
sage: u1max.__array_interface__
{'typestr': '=f8'}
sage: u1_array = numpy.linspace(u1min, u1max, u1num)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-666f5f61234c> in <module>()
----> 1 u1_array = numpy.linspace(u1min, u1max, u1num)

/home/frb15/gentoo/usr/lib/python2.7/site-packages/numpy/core/function_base.py in linspace(start, stop, num, endpoint, retstep, dtype)
     91     stop = stop * 1.
     92 
---> 93     dt = result_type(start, stop, float(num))
     94     if dtype is None:
     95         dtype = dt

TypeError: data type not understood
```

It appears that `start` and `stop` are not converted to float by the multiplication by `1.`. The initial suggestion in issue #3504 to use `float` to cast the value to the right types would work here. Of course in that context `dt` becomes trivially `float`.



Discussion:
Note that is was done that way so as to work with complex also. I assume the problem here is the use of `__array_interface__`. What types are `u1min` and `u1max`? 

Technical they would be real types from `sage` 

```
sage: type(u1min)
<type 'sage.rings.real_mpfr.RealLiteral'>
```

We historically gave them an array interface components so they would be easily coerced to numpy types. The mechanism broke down before (issue #291).

I should also note that in the case of `sage` the multiplication by `1.` has, in all probability, never worked. The problem only became apparent when the call to `result_type` was introduced.

Does sage support the python `numbers` module?.

What happens if `asarray` is applied to the types?

For the first question, if by support you mean does sage types inherit from `numbers`, then I don't think so but that would need to be checked by someone who has a deeper understanding of the internals. 

For the second question 

```
sage: numpy.asarray(u1min)
array(-1.5)
```

Introducing `asarray` seems to solve the problem, but is it appropriate?

```
    # Convert float/complex array scalars to float, gh-3504
    from numpy import asarray
    start = asarray(start) * 1.
    stop = asarray(stop) * 1.
```

After various checks I decide to submit it as a PR. Travis is happy, that's always that.

that code now works after gh-6659 was merged.

any chance we can get this fixed in 1.12 as well? this would help to reduce the number of errors in Sage test suite in Debian (ref for the request: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=842348#25) - thanks for considering!
Want to make a PR? #6659 consists of 5 crappy unsquashed commits, so cut and paste may be the way to go.
not really :) but if that's what it takes to get it in 1.12 i'll prepare one in the coming days
Note that I want to get 1.12.0rc2 out the door in a couple of days.

Resolving patch:
diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py
--- a/numpy/core/function_base.py
+++ b/numpy/core/function_base.py
@@ -5,7 +5,7 @@
 
 from . import numeric as _nx
 from .numeric import (result_type, NaN, shares_memory, MAY_SHARE_BOUNDS,
-                      TooHardError)
+                      TooHardError,asanyarray)
 
 __all__ = ['logspace', 'linspace', 'geomspace']
 
@@ -104,8 +104,9 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
     div = (num - 1) if endpoint else num
 
     # Convert float/complex array scalars to float, gh-3504
-    start = start * 1.0
-    stop = stop * 1.0
+    # and make sure one can use variables that have an __array_interface__, gh-6634
+    start = asanyarray(start) * 1.0
+    stop  = asanyarray(stop)  * 1.0
 
     dt = result_type(start, stop, float(num))
     if dtype is None:


Test patch:
diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py
--- a/numpy/core/tests/test_function_base.py
+++ b/numpy/core/tests/test_function_base.py
@@ -262,6 +262,42 @@ def test_subclass(self):
         assert type(ls) is PhysicalQuantity2
         assert_equal(ls, linspace(0.0, 1.0, 1))
 
+    def test_array_interface(self):
+        # Regression test for https://github.com/numpy/numpy/pull/6659
+        # Ensure that start/stop can be objects that implement
+        # __array_interface__ and are convertible to numeric scalars
+
+        class Arrayish(object):
+            """
+            A generic object that supports the __array_interface__ and hence
+            can in principle be converted to a numeric scalar, but is not
+            otherwise recognized as numeric, but also happens to support
+            multiplication by floats.
+
+            Data should be an object that implements the buffer interface,
+            and contains at least 4 bytes.
+            """
+
+            def __init__(self, data):
+                self._data = data
+
+            @property
+            def __array_interface__(self):
+                # Ideally should be `'shape': ()` but the current interface
+                # does not allow that
+                return {'shape': (1,), 'typestr': '<i4', 'data': self._data,
+                        'version': 3}
+
+            def __mul__(self, other):
+                # For the purposes of this test any multiplication is an
+                # identity operation :)
+                return self
+
+        one = Arrayish(array(1, dtype='<i4'))
+        five = Arrayish(array(5, dtype='<i4'))
+
+        assert_equal(linspace(one, five), linspace(1, 5))
+
     def test_denormal_numbers(self):
         # Regression test for gh-5437. Will probably fail when compiled
         # with ICC, which flushes denormals to zero