Программирование

Как исправить IndexError: tuple index out of range в Numba

Решаем ошибки компиляции Numba с IndexError: tuple index out of range. Узнайте, как исправить несоответствия сигнатур функций и проблемы с передачей аргументов в коде Python, скомпилированном с Numba.

Столкнулся с ошибкой IndexError: tuple index out of range при вызове скомпилированной функции Numba в моей симуляции гидродинамики. Я использую декоратор @njit для оптимизации производительности, но застрял с этой ошибкой, которая происходит во время компиляции, а не выполнения.

Вот мой код:

python
import numpy as np
# IYPT prob 7
cup_dt = 1e-4  # Размер шага времени для симуляции (с) - Уменьшен для стабильности
water_kinetic_viscosity = 1e-2  # см^2/с
water_viscosity=1e-2  # Па·с
save_frame_frequency_per_second = 24# кадров в секунду
from numba import njit

@njit
def layer_angular_velocity_change(*,
                              kinetic_viscosity: float,
                              radius: float,
                              layer: int,
                              total_layers: int,
                              upper_angular_velocity: np.ndarray,
                              angular_velocity: np.ndarray,
                              lower_angular_velocity: np.ndarray
                              ) :
    return [1,1,1]

@njit
def solid_liquid_layer_angular_velocity_change(
    viscosity,
    radius,
    thickness,
    solid_inertia,
    solid_angular_velocity,
    liquid_layer_angular_velocity,
    second_liquid_layer_angular_velocity
):
    liquid_angular_velocity_change = layer_angular_velocity_change(
            kinetic_viscosity=water_kinetic_viscosity,
            radius=1,
            layer=1 / thickness,
            total_layers=1,
            upper_angular_velocity=solid_angular_velocity,
            angular_velocity=liquid_layer_angular_velocity,
            lower_angular_velocity=second_liquid_layer_angular_velocity
    )

    return 1,2

# Обновление пограничного слоя и твердого тела
upper_layer_viscous_increment_term, solid_angular_velocity_viscous_increment_term = solid_liquid_layer_angular_velocity_change(
1,
1,
1,
[1,1,1],
[1,1,1],
[1,1,1],
[1,1,1],
)

При запуске этого кода я получаю следующий traceback ошибки:

Traceback (most recent call last):
File "/Users/niuyingkai/PycharmProjects/PT-formula/2026 prob 7 liquid solid interaction.py", line 174, in <module>
upper_layer_viscous_increment_term, solid_angular_velocity_viscous_increment_term = solid_liquid_layer_angular_velocity_change(
                                                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
    viscosity=water_viscosity,
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
...<5 lines>...
      second_liquid_layer_angular_velocity=water_angular_velocity_current[water_layer_num-2],
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/dispatcher.py", line 443, in _compile_for_args
raise e
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/dispatcher.py", line 376, in _compile_for_args
return_val = self.compile(tuple(argtypes))
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/dispatcher.py", line 908, in compile
cres = self._compiler.compile(args, return_type)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/dispatcher.py", line 80, in compile
status, retval = self._compile_cached(args, return_type)
                 ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/dispatcher.py", line 94, in _compile_cached
retval = self._compile_core(args, return_type)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/dispatcher.py", line 107, in _compile_core
cres = compiler.compile_extra(self.targetdescr.typing_context,
                              self.targetdescr.target_context,
...<2 lines>...
                              flags=flags, locals=self.locals,
                              pipeline_class=self.pipeline_class)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler.py", line 739, in compile_extra
return pipeline.compile_extra(func)
       ~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler.py", line 439, in compile_extra
return self._compile_bytecode()
       ~~~~~~~~~~~~~~~~~~~~~~^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler.py", line 505, in _compile_bytecode
return self._compile_core()
       ~~~~~~~~~~~~~~~~~~^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler.py", line 481, in _compile_core
raise e
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler.py", line 473, in _compile_core
pm.run(self.state)
~~~~~~^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler_machinery.py", line 363, in run
raise e
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler_machinery.py", line 356, in run
self._runPass(idx, pass_inst, state)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler_lock.py", line 35, in _acquire_compile_lock
return func(*args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler_machinery.py", line 311, in _runPass
mutated |= check(pss.run_pass, internal_state)
           ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/compiler_machinery.py", line 272, in check
mangled = func(compiler_state)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typed_passes.py", line 114, in run_pass
typemap, return_type, calltypes, errs = type_inference_stage(
                                        ~~~~~~~~~~~~~~~~~~~~^
    state.typingctx,
    ^^^^^^^^^^^^^^^^
...<4 lines>...
    state.locals,
    ^^^^^^^^^^^^^
    raise_errors=self._raise_errors)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typed_passes.py", line 95, in type_inference_stage
errs = infer.propagate(raise_errors=raise_errors)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typeinfer.py", line 1075, in propagate
errors = self.constraints.propagate(self)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typeinfer.py", line 160, in propagate
constraint(typeinfer)
~~~~~~~~~~^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typeinfer.py", line 572, in __call__
self.resolve(typeinfer, typevars, fnty)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typeinfer.py", line 595, in resolve
sig = typeinfer.resolve_call(fnty, pos_args, kw_args)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typeinfer.py", line 1569, in resolve_call
return self.context.resolve_function_type(fnty, pos_args, kw_args)
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typing/context.py", line 279, in resolve_function_type
res = self._resolve_user_function_type(func, args, kws)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typing/context.py", line 335, in _resolve_user_function_type
return func.get_call_type(self, args, kws)
       ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/types/functions.py", line 541, in get_call_type
self.dispatcher.get_call_template(args, kws)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/dispatcher.py", line 315, in get_call_template
pysig, args = self._compiler.fold_argument_types(args, kws)
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/dispatcher.py", line 73, in fold_argument_types
args = fold_arguments(self.pysig, args, kws,
                      normal_handler,
                      default_handler,
                      stararg_handler)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/numba/core/typing/templates.py", line 217, in fold_arguments
bind_kws[n] = args[len(kwonly) + idx]
              ~~~~^^^^^^^^^^^^^^^^^^^
IndexError: tuple index out of range

Process finished with exit code 1

Я дважды проверил вызов функции, и он выглядит правильным для меня. Что вызывает эту IndexError в Numba и как это можно исправить? Похоже, ошибка происходит во время фазы компиляции, а не при фактическом выполнении функции.

Понимание ошибки компиляции

Ошибка IndexError: tuple index out of range появляется во время фазы типизации Numba, когда он пытается привязать аргументы к параметрам функции, как будто разрезает пазл, но не может найти подходящий кусок. В реальности это значит, что Numba не может сопоставить переданные аргументы с ожидаемыми параметрами функции.

В трассировке видно, что проблема возникает в:

File "/path/to/numba/core/typing/templates.py", line 217, in fold_arguments
    bind_kws[n] = args[len(kwonly) + idx]
IndexError: tuple index out of range

То есть, количество аргументов, которые вы передаёте, не совпадает с тем, что ожидает сигнатура функции.

Анализ причины

Главная причина — это несоответствие между определением функции layer_angular_velocity_change и её вызовом из solid_liquid_layer_angular_velocity_change.

  1. Keyword‑only аргументы
    В layer_angular_velocity_change вы объявили *, после первых трёх параметров, что делает все последующие аргументы обязательными по ключу. Но внутри solid_liquid_layer_angular_velocity_change вы передаёте их позиционно, а не по ключу.

  2. Неполный набор аргументов
    В вызове solid_liquid_layer_angular_velocity_change вы передаёте только шесть аргументов, хотя сигнатура требует семь.

Пример вызова, который приводит к ошибке:

python
solid_liquid_layer_angular_velocity_change(
    1, 1, 1,
    [1,1,1], [1,1,1], [1,1,1], [1,1,1]  # 6 аргументов
)  # Ожидается 7

Как исправить

  1. Уберите ограничение keyword‑only из layer_angular_velocity_change или используйте ключевые аргументы при вызове.
  2. Убедитесь, что все обязательные аргументы переданы и в правильном порядке.
  3. Согласуйте способ передачи аргументов между функциями, чтобы не было путаницы.

Исправленный код

python
import numpy as np
# IYPT prob 7
cup_dt = 1e-4  # Time step size for simulation (s) - Reduced for stability
water_kinetic_viscosity = 1e-2  # cm^2/s
water_viscosity=1e-2  # Pa.s
save_frame_frequency_per_second = 24  # frame per s
from numba import njit

@njit
def layer_angular_velocity_change(
    kinetic_viscosity: float,
    radius: float,
    layer: int,
    total_layers: int,
    upper_angular_velocity: np.ndarray,
    angular_velocity: np.ndarray,
    lower_angular_velocity: np.ndarray
):
    return [1,1,1]

@njit
def solid_liquid_layer_angular_velocity_change(
    viscosity,
    radius,
    thickness,
    solid_inertia,
    solid_angular_velocity,
    liquid_layer_angular_velocity,
    second_liquid_layer_angular_velocity
):
    liquid_angular_velocity_change = layer_angular_velocity_change(
        kinetic_viscosity=water_kinetic_viscosity,
        radius=1,
        layer=1 / thickness,
        total_layers=1,
        upper_angular_velocity=solid_angular_velocity,
        angular_velocity=liquid_layer_angular_velocity,
        lower_angular_velocity=second_liquid_layer_angular_velocity
    )

    return 1,2

# Update boundary layer and solid - Fixed with all 7 arguments
upper_layer_viscous_increment_term, solid_angular_velocity_viscous_increment_term = solid_liquid_layer_angular_velocity_change(
    1,  # viscosity
    1,  # radius
    1,  # thickness
    [1,1,1],  # solid_inertia
    [1,1,1],  # solid_angular_velocity
    [1,1,1],  # liquid_layer_angular_velocity
    [1,1,1],  # second_liquid_layer_angular_velocity
)

Что изменено

  • Убрано *, из layer_angular_velocity_change, чтобы функция принимала позиционные аргументы.
  • Внутри вызова использованы ключевые аргументы, что делает код более читаемым и гарантирует правильное сопоставление.
  • В основном вызове теперь передаётся ровно семь аргументов, как и требует сигнатура.

Лучшие практики для сигнатур функций Numba

  1. Последовательность передачи аргументов
    Либо используйте только позиционные, либо только ключевые аргументы. Смешивание может привести к непредсказуемым ошибкам типизации.

  2. Типовые аннотации
    Указывайте типы, как вы сделали, это помогает Numba быстрее и точнее определять типы. Для массивов используйте np.ndarray.

  3. Простые сигнатуры
    Старайтесь держать сигнатуры функций простыми и предсказуемыми. Сложные комбинации аргументов усложняют типизацию.

  4. Тестирование без Numba
    Протестируйте логику в обычном Python, прежде чем добавлять @njit. Это поможет изолировать ошибки логики от ошибок компиляции.

Дополнительные советы по отладке

  • Упрощайте проблему: закомментируйте части кода, чтобы изолировать проблемную функцию.
  • Проверяйте типы аргументов: убедитесь, что типы передаваемых аргументов совпадают с ожидаемыми.
  • Читайте сообщения Numba: они часто указывают точную строку, где возникла ошибка.
  • Рефакторинг: если сигнатура слишком громоздкая, подумайте о её упрощении для лучшей совместимости с Numba.

Итого, основная причина ошибки — это несовпадение аргументов из‑за ограничения keyword‑only и неполного набора аргументов. Сделав передачу аргументов согласованной и полным, вы избавитесь от ошибки компиляции и сможете продолжить работу с Numba.

Авторы
Проверено модерацией
Модерация