Я хочу запустить рекурсивную функцию в Numba, используя режим nopython. До сих пор я получаю только ошибки. Это очень простой код, пользователь дает кортеж с менее чем пятью элементами, а затем функция создает еще один кортеж с новым значением, добавляемым к кортежу (в данном случае число 3). Это повторяется до тех пор, пока окончательный кортеж не будет иметь длину 5. По какой-то причине это не работает, не знаю почему.
@njit
def tup(a):
if len(a) == 5:
return a
else:
b = a + (3,)
b = tup(b)
return b
Например, если a = (0,1)
, я ожидаю, что конечным результатом будет кортеж (0,1,3,3,3)
.
Обновлено: я использую Numba 0.41.0, и ошибка, которую я получаю, связана с умиранием ядра: «Кажется, ядро умерло. Он перезапустится автоматически».
Согласно этот список предложений в текущих выпусках:
Recursion support in numba is currently limited to self-recursion with explicit type annotation for the function. This limitation comes from the inability to determine the return type of a recursive call.
Итак, вместо этого попробуйте:
from numba import jit
@jit()
def tup(a:tuple) -> tuple:
if len(a) == 5:
return a
return tup(a + (3,))
print(tup((0, 1)))
Чтобы увидеть, работает ли это лучше для вас.
Это действительно работает, когда вы запускаете его? Я только что попытался запустить код, по крайней мере, на моей машине, которая просто создает StackOverflow. Какая у тебя нумба версия?
@MSeifert, я только что зашел на веб-сайт numba и на их страницу jupyter «попробуйте онлайн». У меня там работало с аннотациями, но не без. Все я знаю.
Это интересно спасибо за объяснение. Но это также не работает в режиме nopython.
Есть несколько причин, почему этого делать не следует:
O(n)
, даже в numba. Таким образом, общая производительность функции будет O(n**2)
. Это можно улучшить, используя структуру данных, которая поддерживает добавление O(1)
, или структуру данных, которая поддерживает предварительное выделение размера. Или просто не используя «циклический» или «рекурсивный» подход.njit
и передадите кортеж, содержащий 6 элементов? (подсказка: он достигнет предела рекурсии, потому что он никогда не выполняет конечное условие рекурсии).Numba на момент написания 0.43.1 поддерживает только простые рекурсии, когда тип аргументов не меняется между рекурсиями. В вашем случае тип изменяется, вы передаете tuple(int64 x 2)
, но рекурсивный вызов пытается передать tuple(int64 x 3)
, который является другим типом. Как ни странно, на моем компьютере он сталкивается с StackOverflow
, что похоже на ошибку в numba.
Мое предложение состояло бы в том, чтобы использовать это (без numba, без рекурсии):
def tup(a):
if len(a) < 5:
a += (3, ) * (5 - len(a))
return a
Что также возвращает ожидаемый результат:
>>> tup((1,))
(1, 3, 3, 3, 3)
>>> tup((1, 2))
(1, 2, 3, 3, 3)
большое спасибо за ответ. На самом деле этот код не совсем то, что я хочу запустить, это просто проблемная часть того, что я пытаюсь сделать. исходная проблема состоит в том, чтобы запустить произвольный вложенный цикл по элементам многомерного массива. Поскольку количество циклов может варьироваться, я делал рекурсивные вызовы, каждый из которых входил в следующий цикл и соответственно строил (мульти)индекс.
Все было бы проще, если бы я просто передал кортеж с его окончательным размером, который я могу узнать до запуска цикла, проблема в том, что кортежи неизменяемы, поэтому я не могу присвоить им значения индексов. Вместо кортежей я мог бы использовать массивы, и можно присваивать новые значения, но я не могу использовать массив индексов, скажем, [1,2,0,0,1] для доступа к значению M[1,2, 0,0,1] многомерного M. Также numba не позволяет мне преобразовать массив в кортеж... Я тоже не могу найти решение со списками... кажется, у каждого пути есть небольшая деталь, мешающая мне делать то, что я хочу делать.
Во всяком случае, я уже видел, что чистый python на самом деле быстрее, чем numba для рекурсии, как вы сказали. Это немного разочаровывает, я ожидаю повышения производительности, поскольку numba лучше работает с циклами.
@Integral Не каждый цикл можно оптимизировать с помощью numba, это действительно зависит от операций, которые вы выполняете внутри цикла.
@Integral Проблема с преобразованием массива в кортеж в numba заключается в том, что кортежи должны иметь фиксированный размер времени компиляции. Массивы не имеют такого размера (как и списки), поэтому вы не можете создать кортеж из массива. Я также обнаружил, что работа с многомерными массивами и пользовательскими индексами в numba довольно сложна.
Я просто пытаюсь создать все возможные кортежи с помощью модуля itertools, а затем могу запустить все в одном цикле. По какой-то причине это было намного хуже, чем рекурсивный подход, даже с numba в режиме nopython. Вы знаете, почему это происходит?
@Integral Это может лучше подходить для новый вопрос, потому что слишком сложно обсуждать это в комментариях, и редактирование этого вопроса (вероятно) сделает ответы недействительными.
Пожалуйста, не забудьте показать, какие ошибки вы получаете. Включение версии numba также может быть полезным, поскольку numba все еще находится в активной разработке, а набор поддерживаемых функций меняется быстрыми темпами.