Skip to content

BUG: numpy.einsum with ellipses has different behavior when optimize=True or False. #30349

Open

Description

@alexandrelaplante

Describe the issue:

The following einsum works as expected when optimize=True but throws a ValueError when optimize=False. I believe the correct behavior is the one from optimize=True.

Reproduce the code example:

import numpy as np
x = np.array([[[1,2,3], [1,2,3], [1,2,3]], [[1,2,3], [1,2,3], [1,2,3]], [[1,2,3], [1,2,3], [1,2,3]]])
np.einsum('i...->i', x, optimize=True)
# array([18, 18, 18])
np.einsum('i...->i', x)
# ValueError: output has more dimensions than subscripts given in einstein sum, but no '...' ellipsis provided to broadcast the extra dimensions

Error message:

Traceback (most recent call last):
  File "<python-input-0>", line 5, in <module>
    np.einsum('i...->i', x)
    ~~~~~~~~~^^^^^^^^^^^^^^
  File "[...]/venv/lib/python3.13/site-packages/numpy/_core/einsumfunc.py", line 1423, in einsum
    return c_einsum(*operands, **kwargs)
ValueError: output has more dimensions than subscripts given in einstein sum, but no '...' ellipsis provided to broadcast the extra dimensions.

Python and NumPy Versions:

2.3.3
3.13.7 (main, Aug 15 2025, 12:34:02) [GCC 15.2.1 20250813]

Runtime Environment:

[{'numpy_version': '2.3.3',
'python': '3.13.7 (main, Aug 15 2025, 12:34:02) [GCC 15.2.1 20250813]',
'uname': uname_result(system='Linux', node='desktop', release='6.17.1-arch1-1', version='#1 SMP PREEMPT_DYNAMIC Mon, 06 Oct 2025 18:48:29 +0000', machine='x86_64')},
{'simd_extensions': {'baseline': ['SSE', 'SSE2', 'SSE3'],
'found': ['SSSE3',
'SSE41',
'POPCNT',
'SSE42',
'AVX',
'F16C',
'FMA3',
'AVX2'],
'not_found': ['AVX512F',
'AVX512CD',
'AVX512_KNL',
'AVX512_KNM',
'AVX512_SKX',
'AVX512_CLX',
'AVX512_CNL',
'AVX512_ICL',
'AVX512_SPR']}}]

Context for the issue:

This was one of the first things I tried after learning about einsum. It seemed like a simpler alternative to summing over every axis except the first in a tensor whose number of dimensions is not known in advance.

np.einsum('i...->i', x)

Activity

  1. mattip commented on Dec 1, 2025

    @mattip
    Member

    @jcmgray could I bother you to take a look?

  2. jcmgray commented on Dec 1, 2025

    @jcmgray
    Contributor

    Seems to be a genuine bug (or just unsupported case) in the equation parsing of c_einsum, triggering this here -

    /* If no ellipsis was found there should be no broadcast dimensions. */
    if (!ellipsis && ndim_broadcast > 0) {
    PyErr_SetString(PyExc_ValueError,
    "output has more dimensions than subscripts "
    "given in einstein sum, but no '...' ellipsis "
    "provided to broadcast the extra dimensions.");
    return -1;
    }
    - unnecessarily, since the intent of the equation is clear. I'm afraid I'm not familiar enough with C to actually debug and fix though, sorry.

  3. added this to the 2.4.0 release milestone on Dec 1, 2025
  4. mattip commented on Dec 1, 2025

    @mattip
    Member

    OK, thanks for peeking

  5. seberg commented on Dec 2, 2025

    @seberg
    Member

    This needs new logic I would imagine. My understanding is that ... is implemented as indicated broadcasting dimensions and while this overloads it as an arbitrary number of reduce dimensions with the name ....

    It gets a bit more unclear to me that this works:

    In [17]: np.einsum("i...,i...->i", x, x[0], optimize=True)
    Out[17]: array([42, 42, 42])
    

    where I guess the ... of the second operand are actually broadcast to fit the first (not sure, it might be weirder). It isn't clear to me that the intention is clear there.

    I do agree that this intention seems like a sensible generalization, but it is a bit unclear that broadcasting is actually sensible for reduced dimensions with multiple operands.

  6. jcmgray commented on Dec 2, 2025

    @jcmgray
    Contributor
    In [17]: np.einsum("i...,i...->i", x, x[0], optimize=True)
    Out[17]: array([42, 42, 42])
    

    Yes this I agree is not well defined and should probably error, in my opinion.

    I always assumed the behavior of ellipses was "replace ... everywhere with a matching sequence of indices, inferred from the inputs". That includes broadcasting, but also cases where the ellipses don't appear in the output or some inputs, but not the above case.

  7. seberg commented on Dec 3, 2025

    @seberg
    Member

    Yeah, I guess it was written as indicating outer dimensions. Which may be slightly odd, because it effectively means we constantly write ellipsis everywhere since most functionality should allow an outer iteration.
    I am slightly surprised about the use-case of i...->i for truly arbitrary number of dimensions, but it isn't like we support the gufunc notion of a ? dimensions here to write ijk?->i (supporting not arbitrary dimensions, but a specific variable number).

  8. TomPOGET commented on Jan 4, 2026

    @TomPOGET

    Apologies for potentially taking over, I hope this isn't poor practice (I am new to contributing to open source projects).
    I have opened a pull request that solves this specific issue as described above; relating to the rest of the discussion, in particular the fact that np.einsum("i...,i...->i", x, x[0], optimize=True) should error, I do agree that it makes sense and that the logic of the handling of the ellipses should probably be changed.
    However, I also noticed that the milestone for this specific bugfix was set to 2.4.1; considering that making that syntax not work anymore would, I suppose, be a breaking change, I felt that it might be better to do that in a separate pull request.
    The one I made as far as I'm aware is not breaking.

  9. removed this from the 2.4.1 Release milestone on Jan 5, 2026
  10. seberg commented on Jan 5, 2026

    @seberg
    Member

    I have removed the milestone, I don't think we should consider this a bug, rather a reasonable enhancement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions