Repository: numpy/numpy

Issue:
Einsum indexing very fragile, because it tests for int (and int64 is not int)
<!-- Please describe the issue in detail here, and fill in the fields below -->

The index arrays for einsum do not accept the output of any numpy array, because these datatypes cannot be ints (see #2951; #12322). The test should be modified to accept numpy ints, because converting the datatype to 'int' explicitly is counter-intuitive and unnecessary.

Though easy to circumvent, this will incur a lot of unnecessary debugging time, as it appears to break abstraction boundaries, has strange interactions with `tolist`, and indices that work in test environments fail when constructed programatically.

### Reproducing code example:

<!-- A short code example that reproduces the problem/missing feature. It should be
self-contained, i.e., possible to run as-is via 'python myproblem.py' -->

```python
import numpy as np
X = np.arange(9).reshape(3,3)

# each of these results in an identical list [0] as far as equality test is concerned 
idx1 = [0]                # list[ int ] 
idx2 = np.unique(idx1)    # np.array [int64]
idx3 = idx2.tolist()      # list [int]
idx3 = list(idx2)         # list [int64]

np.einsum(X, [0], idx1 ) # succeeds
np.einsum(X, [0], idx2 ) # fails
np.einsum(X, [0], idx3 ) # succeeds
np.einsum(X, [0], idx4 ) # fails

```

### Error message:

ValueError: each subscript must be either an integer or an ellipsis

<!-- If you are reporting a segfault please include a GDB traceback, which you
can generate by following
https://github.com/numpy/numpy/blob/master/doc/source/dev/development_environment.rst#debugging -->


Full error message if `optimize_arg` is `False`:
<!-- Full error message, if any (starting from line Traceback: ...) -->
```
<__array_function__ internals> in einsum(*args, **kwargs)

~/.local/lib/python3.6/site-packages/numpy/core/einsumfunc.py in einsum(*operands, **kwargs)
   1354     # If no optimization, run pure einsum
   1355     if optimize_arg is False:
-> 1356         return c_einsum(*operands, **kwargs)
   1357 
   1358     valid_einsum_kwargs = ['out', 'dtype', 'order', 'casting']

ValueError: each subscript must be either an integer or an ellipsis
```
Full error message when `optimize_arg` is `True`:
```
<__array_function__ internals> in einsum(*args, **kwargs)

~/.local/lib/python3.6/site-packages/numpy/core/einsumfunc.py in einsum(*operands, **kwargs)
   1377     # Build the contraction list and operand
   1378     operands, contraction_list = einsum_path(*operands, optimize=optimize_arg,
-> 1379                                              einsum_call=True)
   1380 
   1381     handle_out = False
```
### Numpy/Python version information:

<!-- Output from 'import sys, numpy; print(numpy.__version__, sys.version)' -->
1.17.3 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0]




Discussion:
We ought to be calling `operator.index` or the equivalent C API here, rather than the `PyInt_Check` I assume we must be doing today.
Relevant lines are here:

https://github.com/numpy/numpy/blob/b489287af94b32d6a6d550c6e05f1cf92adf3248/numpy/core/src/multiarray/multiarraymodule.c#L2462-L2501

Logic ought to be the C equivalent of:
```python
try:
    s = operator.index(item)
except TypeError as e:
    raise TypeError("each subscript must be either an integer "
                    "or an ellipsis") from e
# do stuff with s
```
I will take this one.
@guilhermeleobas you working on this ? If not I would like to take this up. Thanks
Hi @tinaoberoi, it was on my plans but you can go ahead and work on it :)
Thanks @guilhermeleobas , I new here, any previous findings and help is appreciated. :-)
I'm also a first time contributor. I might try fixing this. I don't really mind if someone else contribute first. In any case, the experience is good for me. 

Resolving patch:
diff --git a/numpy/core/einsumfunc.py b/numpy/core/einsumfunc.py
--- a/numpy/core/einsumfunc.py
+++ b/numpy/core/einsumfunc.py
@@ -3,6 +3,7 @@
 
 """
 import itertools
+import operator
 
 from numpy.core.multiarray import c_einsum
 from numpy.core.numeric import asanyarray, tensordot
@@ -576,11 +577,13 @@ def _parse_einsum_input(operands):
             for s in sub:
                 if s is Ellipsis:
                     subscripts += "..."
-                elif isinstance(s, int):
-                    subscripts += einsum_symbols[s]
                 else:
-                    raise TypeError("For this input type lists must contain "
-                                    "either int or Ellipsis")
+                    try:
+                        s = operator.index(s)
+                    except TypeError as e:
+                        raise TypeError("For this input type lists must contain "
+                                        "either int or Ellipsis") from e
+                    subscripts += einsum_symbols[s]
             if num != last:
                 subscripts += ","
 
@@ -589,11 +592,13 @@ def _parse_einsum_input(operands):
             for s in output_list:
                 if s is Ellipsis:
                     subscripts += "..."
-                elif isinstance(s, int):
-                    subscripts += einsum_symbols[s]
                 else:
-                    raise TypeError("For this input type lists must contain "
-                                    "either int or Ellipsis")
+                    try:
+                        s = operator.index(s)
+                    except TypeError as e:
+                        raise TypeError("For this input type lists must contain "
+                                        "either int or Ellipsis") from e
+                    subscripts += einsum_symbols[s]
     # Check for proper "->"
     if ("-" in subscripts) or (">" in subscripts):
         invalid = (subscripts.count("-") > 1) or (subscripts.count(">") > 1)


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
@@ -274,6 +274,13 @@ def check_einsum_sums(self, dtype, do_opt=False):
             assert_equal(np.einsum(a, [0, 0], optimize=do_opt),
                          np.trace(a).astype(dtype))
 
+            # gh-15961: should accept numpy int64 type in subscript list
+            np_array = np.asarray([0, 0])
+            assert_equal(np.einsum(a, np_array, optimize=do_opt),
+                         np.trace(a).astype(dtype))
+            assert_equal(np.einsum(a, list(np_array), optimize=do_opt),
+                         np.trace(a).astype(dtype))
+
         # multiply(a, b)
         assert_equal(np.einsum("..., ...", 3, 4), 12)  # scalar case
         for n in range(1, 17):