question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

BUG: numpy.einsum fails to match dimensions on arrays with singleton dimensions

See original GitHub issue

When operating on arrays with singleton dimensions, numpy.einsum does not always enforce shape matching over summed dimensions. Instead it can return a surprising result. Minimal example:

import numpy as np
a = np.arange(4).reshape(4,1)
b = 2 * np.arange(3).reshape(1,3)
c = np.einsum('ik,jk->ij', a, b)

Here I would expect a ValueError: we are summing over the k index, but k has length 1 for a and length 3 for b. However, einsum instead returns a value for c that is equal to a * b.sum().

>>> print(c)
[[ 0]
 [ 6]
 [12]
 [18]]
>>> c.shape
(4, 1)

I suppose this could be related to broadcasting behavior, but the documentation is fairly clear that broadcasting should only occur when the subscripts argument contains ellipses.

For arrays with no singleton dimensions, einsum throws a ValueError as expected. This example:

import numpy as np
a = np.arange(8).reshape(4,2)
b = 2 * np.arange(6).reshape(2,3)
c = np.einsum('ik,jk->ij', a, b)

produces

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/dave/.conda/envs/dk_env/lib/python3.6/site-packages/numpy/core/einsumfunc.py", line 1069, in einsum
    return c_einsum(*operands, **kwargs)
ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (4,2)->(4,newaxis,2) (2,3)->(2,3) 

I am running numpy 1.14.5 under python 3.6.5 on Mac OSX.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
charriscommented, Jul 12, 2018

I think this is a documentation problem. Explicit singleton dimensions broadcast, it is how broadcasting is indicated to the numpy iterator machinery. When the singleton is not explicit, broadcasting is achieved by adding singleton dimensions. I think einsum is giving you the option of adding the missing singletons or not, but I don’t think it applies to preexisting singletons, they always broadcast.

0reactions
sebergcommented, Apr 21, 2021

The iterator was unable to check this for along time (i.e. distinguish "broadcast and non-broadcast axis explicitly). It now could. I think the diff at the end would do it. But if you want to go through deprecation (which seems better here), you will have to do it manually first probably.

Also note that optimize=True will take a different path I think and needs to get an identical fix if anyone looks into it. Mainly posting, since the correct NpyIter usage for the “final” version is pretty arcane.

diff --git a/numpy/core/src/multiarray/einsum.c.src b/numpy/core/src/multiarray/einsum.c.src
index 85806fab3..d6c2489c9 100644
--- a/numpy/core/src/multiarray/einsum.c.src
+++ b/numpy/core/src/multiarray/einsum.c.src
@@ -470,7 +470,7 @@ prepare_op_axes(int ndim, int iop, char *labels, int *axes,
              * extend it with a "newaxis"
              */
             if (ibroadcast < 0) {
-                axes[i] = -1;
+                axes[i] = NPY_ITER_REDUCTION_AXIS(-1);
             }
             /* Otherwise map to the broadcast axis */
             else {
@@ -998,12 +998,14 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop,
     for (iop = 0; iop < nop; ++iop) {
         op_flags[iop] = NPY_ITER_READONLY|
                         NPY_ITER_NBO|
-                        NPY_ITER_ALIGNED;
+                        NPY_ITER_ALIGNED|
+                        NPY_ITER_NO_BROADCAST;
     }
     op_flags[nop] = NPY_ITER_READWRITE|
                     NPY_ITER_NBO|
                     NPY_ITER_ALIGNED|
-                    NPY_ITER_ALLOCATE;
+                    NPY_ITER_ALLOCATE|
+                    NPY_ITER_NO_BROADCAST;
     iter_flags = NPY_ITER_EXTERNAL_LOOP|
             NPY_ITER_BUFFERED|
             NPY_ITER_DELAY_BUFALLOC|
Read more comments on GitHub >

github_iconTop Results From Across the Web

Numpy einsum gives error: dimensions in operand 0 for ...
However, I run into the following error: ValueError: dimensions in operand 0 for collapsing index 'q' don't match (4 != 2).
Read more >
numpy.einsum — NumPy v1.24 Manual
Evaluates the Einstein summation convention on the operands. Using the Einstein summation convention, many common multi-dimensional, linear algebraic array ...
Read more >
Release Notes — NumPy v1.15 Manual
#10559: BUG: Fix einsum optimize logic for singleton dimensions ... Additionally, the dtype guessing now matches that of np.array - so when ...
Read more >
[Solved]-numpy einsum collapse all but first dimension-numpy
[Solved]-numpy einsum collapse all but first dimension-numpy ... In [20]: arr.sum(axis=(-1)) Out[20]: array([[ 6, 22, 38], [54, 70, 86]]) In [21]: ...
Read more >
Numpy Einsum Gives Error: Dimensions In Operand 0 for ... - ADocLib
The exception is if a subscript is repeated for the same input operand in which case the dimensions labeled with this subscript for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found