Repository: numpy/numpy

Issue:
einsum error with unicode input (regression in Numpy 1.14)
This was working before Numpy 1.14 and now fails (on Python 2):

```python
In [1]: from __future__ import unicode_literals
   ...: import numpy as np
   ...: np.einsum('...i,...i', [1,2,3], [2,3,4])
   ...: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-1-dea65e271386> in <module>()
      1 from __future__ import unicode_literals
      2 import numpy as np
----> 3 np.einsum('...i,...i', [1,2,3], [2,3,4])

/home/conseil/miniconda3/envs/py2/lib/python2.7/site-packages/numpy/core/einsumfunc.pyc in einsum(*operands, **kwargs)
   1085     # Build the contraction list and operand
   1086     operands, contraction_list = einsum_path(*operands, optimize=optimize_arg,
-> 1087                                              einsum_call=True)
   1088
   1089     handle_out = False

/home/conseil/miniconda3/envs/py2/lib/python2.7/site-packages/numpy/core/einsumfunc.pyc in einsum_path(*operands, **kwargs)
    686
    687     # Python side parsing
--> 688     input_subscripts, output_subscript, operands = _parse_einsum_input(operands)
    689     subscripts = input_subscripts + '->' + output_subscript
    690

/home/conseil/miniconda3/envs/py2/lib/python2.7/site-packages/numpy/core/einsumfunc.pyc in _parse_einsum_input(operands)
    529         if char not in input_subscripts:
    530             raise ValueError("Output character %s did not appear in the input"
--> 531                              % char)
    532
    533     # Make sure number operands is equivalent to the number of terms

ValueError: Output character e did not appear in the input
```
einsum error with unicode input (regression in Numpy 1.14)
This was working before Numpy 1.14 and now fails (on Python 2):

```python
In [1]: from __future__ import unicode_literals
   ...: import numpy as np
   ...: np.einsum('...i,...i', [1,2,3], [2,3,4])
   ...: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-1-dea65e271386> in <module>()
      1 from __future__ import unicode_literals
      2 import numpy as np
----> 3 np.einsum('...i,...i', [1,2,3], [2,3,4])

/home/conseil/miniconda3/envs/py2/lib/python2.7/site-packages/numpy/core/einsumfunc.pyc in einsum(*operands, **kwargs)
   1085     # Build the contraction list and operand
   1086     operands, contraction_list = einsum_path(*operands, optimize=optimize_arg,
-> 1087                                              einsum_call=True)
   1088
   1089     handle_out = False

/home/conseil/miniconda3/envs/py2/lib/python2.7/site-packages/numpy/core/einsumfunc.pyc in einsum_path(*operands, **kwargs)
    686
    687     # Python side parsing
--> 688     input_subscripts, output_subscript, operands = _parse_einsum_input(operands)
    689     subscripts = input_subscripts + '->' + output_subscript
    690

/home/conseil/miniconda3/envs/py2/lib/python2.7/site-packages/numpy/core/einsumfunc.pyc in _parse_einsum_input(operands)
    529         if char not in input_subscripts:
    530             raise ValueError("Output character %s did not appear in the input"
--> 531                              % char)
    532
    533     # Make sure number operands is equivalent to the number of terms

ValueError: Output character e did not appear in the input
```


Discussion:

Resolving patch:
diff --git a/numpy/core/einsumfunc.py b/numpy/core/einsumfunc.py
--- a/numpy/core/einsumfunc.py
+++ b/numpy/core/einsumfunc.py
@@ -4,6 +4,7 @@
 """
 from __future__ import division, absolute_import, print_function
 
+from numpy.compat import basestring
 from numpy.core.multiarray import c_einsum
 from numpy.core.numeric import asarray, asanyarray, result_type, tensordot, dot
 
@@ -399,7 +400,7 @@ def _parse_einsum_input(operands):
     if len(operands) == 0:
         raise ValueError("No input operands")
 
-    if isinstance(operands[0], str):
+    if isinstance(operands[0], basestring):
         subscripts = operands[0].replace(" ", "")
         operands = [asanyarray(v) for v in operands[1:]]
 
@@ -665,7 +666,7 @@ def einsum_path(*operands, **kwargs):
     memory_limit = None
 
     # No optimization or a named path algorithm
-    if (path_type is False) or isinstance(path_type, str):
+    if (path_type is False) or isinstance(path_type, basestring):
         pass
 
     # Given an explicit path
@@ -673,7 +674,7 @@ def einsum_path(*operands, **kwargs):
         pass
 
     # Path tuple with memory limit
-    elif ((len(path_type) == 2) and isinstance(path_type[0], str) and
+    elif ((len(path_type) == 2) and isinstance(path_type[0], basestring) and
             isinstance(path_type[1], (int, float))):
         memory_limit = int(path_type[1])
         path_type = path_type[0]


Test patch:
diff --git a/numpy/core/tests/test_einsum.py b/numpy/core/tests/test_einsum.py
--- a/numpy/core/tests/test_einsum.py
+++ b/numpy/core/tests/test_einsum.py
@@ -538,6 +538,13 @@ def test_einsum_misc(self):
         assert_equal(np.einsum('ij...,j...->i...', a, b), [[[2], [2]]])
         assert_equal(np.einsum('ij...,j...->i...', a, b, optimize=True), [[[2], [2]]])
 
+        # Regression test for issue #10369 (test unicode inputs with Python 2)
+        assert_equal(np.einsum(u'ij...,j...->i...', a, b), [[[2], [2]]])
+        assert_equal(np.einsum('...i,...i', [1, 2, 3], [2, 3, 4]), 20)
+        assert_equal(np.einsum(u'...i,...i', [1, 2, 3], [2, 3, 4]), 20)
+        assert_equal(np.einsum('...i,...i', [1, 2, 3], [2, 3, 4],
+                               optimize=u'greedy'), 20)
+
         # The iterator had an issue with buffering this reduction
         a = np.ones((5, 12, 4, 2, 3), np.int64)
         b = np.ones((5, 12, 11), np.int64)