Как указать кортеж в подписи Numba Vectorize?

Я определяю функцию и хочу использовать Numba Vectorize для ее ускорения с помощью cuda. У меня проблемы с подписью функции. Функция вернет значение float64. Я хочу передать два значения float64, которые будут векторизованы, и, кроме того, 9-кортеж значений float64, которые будут скалярами.

Вот мой заголовок функции:

from numba import vectorize

@vectorize(['float64(float64, float64, UniTuple(float64, 9))'], target='cuda')
def fn_vec(E, L, fparams):
    # calculations... 
    return result

но это дает ошибку:

TypeError: data type "(float64 x 9)" not understood

Я пробовал много вариантов, в том числе (float64,..., float64) вместо UniTuple(), но ничего не могу заставить работать. Как мне это сделать?


person kevinea    schedule 19.04.2019    source источник


Ответы (1)


Как указать кортеж в подписи Numba Vectorize?

В функции numba.vectorize вы не можете использовать кортеж. Это потому, что vectorize векторизует код для массивов этих типов.

Таким образом, использование сигнатуры float, float, tuple создает функцию, которая ожидает два массива, содержащих числа с плавающей запятой, и один массив, содержащий кортежи. Проблема в том, что для массива, содержащего кортежи, нет dtype - он может работать, если вы используете структурированный массив вместо массива, содержащего кортежи, но я этого не пробовал.

Как указать кортеж в подписи Numba jit?

Правильный способ указать UniTuple в нумба-подписи — использовать numba.types.containers.UniTuple. В твоем случае:

nb.types.containers.UniTuple(nb.types.float64, 9)

Таким образом, правильная подпись будет выглядеть примерно так:

import numba as nb

@nb.njit(
    nb.types.float64(
        nb.types.float64, 
        nb.types.float64, 
        nb.types.containers.UniTuple(nb.types.float64, 9)))
def func(f1, f2, ftuple):
    # ...
    return f1

Я часто избегаю явного ввода своих функций numba, но когда я это делаю, я нахожу очень полезным использовать numba.typeof, например:

>>> nb.typeof((1.0, ) * 9)
tuple(float64 x 9)

>>> type(nb.typeof((1.0, ) * 9))
numba.types.containers.UniTuple

>>> help(type(nb.typeof((1.0, ) * 9)))  # I shortened the result:
Help on class UniTuple in module numba.types.containers:

class UniTuple(BaseAnonymousTuple, _HomogeneousTuple, numba.types.abstract.Sequence)
 |  UniTuple(*args, **kwargs)
 |  
 |  Type class for homogeneous tuples.
 |  
 |  Methods defined here:
 |  
 |  __init__(self, dtype, count)
 |      Initialize self.  See help(type(self)) for accurate signature.

Итак, вся информация есть: это numba.types.containes.UniTuple, и вы создаете его экземпляр с двумя аргументами: dtype (здесь float64) и числом (в данном случае 9).

Если вы хотите векторизовать только массивы с плавающей запятой

Если вы не хотите векторизовать функцию для аргумента кортежа, вы можете просто создать векторизованную функцию внутри другой функции и вызвать ее там:

import numba as nb
import numpy as np

def func(E, L, fparams):
    @nb.vectorize(['float64(float64, float64)'])
    def fn_vec(e, l):
        return e + l + fparams[1]  # just to illustrate that the tuple is available
    return fn_vec(E, L)

Это делает кортеж доступным внутри функции vectorized. Однако он должен создавать внутреннюю функцию и компилировать ее каждый раз, когда вы вызываете внешнюю функцию, поэтому на самом деле это может быть медленнее. Я также не уверен, что это будет работать с target="cuda", возможно, вам придется проверить это самостоятельно.

person MSeifert    schedule 20.04.2019
comment
Спасибо за ответ. К сожалению, это не сработало для меня. Я использую @vectorize, а не @njit; может в этом причина? Я пытался@nb.vectorize( nb.types.float64( nb.types.float64, nb.types.float64, nb.types.containers.UniTuple(nb.types.float64, 9))) def fn_vec2(E, L, fparams): # calcs... return E, но получаю TypeError: 'Signature' object is not iterable - person kevinea; 21.04.2019
comment
Vectorize ожидает повторение подписей, вы забыли список вокруг подписи? - person MSeifert; 21.04.2019
comment
Вы правы, я забыл список. Итак, теперь мой декоратор @nb.vectorize([nb.types.float64( nb.types.float64, nb.types.float64, nb.types.containers.UniTuple(nb.types.float64, 9))]), и я получаю сообщение об ошибке NotImplementedError: (float64 x 9) не может быть представлено как dtype Numpy` - person kevinea; 18.05.2019
comment
@kevinea Не могли бы вы привести небольшой пример того, как вы бы вызвали функцию. Я правильно понимаю, что вы называете это примерно так: fn_vec(np.ones(10), np.ones(10), tuple(np.ones(9)))? - person MSeifert; 18.05.2019
comment
Да, это точно. И еще раз спасибо за ответ! - person kevinea; 19.05.2019
comment
спасибо за работу над этим. Ваши примеры действительно работают сейчас, но, как вы подозревали, они довольно медленные, когда функция векторизации находится во внутренней функции. Я подтвердил, что это работает, и что target="cuda" тоже работает. - person kevinea; 23.05.2019