Как переопределить numpy ufunc с __array_ufunc__ - PullRequest
0 голосов
/ 28 марта 2019

Я пытаюсь реализовать ufunc в numpy для работы с классом, используя метод __array_ufunc__, представленный в numpy v1.13.

Чтобы упростить, класс может выглядеть следующим образом:

class toto():
    def __init__(self, value, name):
        self.value = value
        self.name = name
    def __add__(self, other):
    """add values and concatenate names"""
        return toto(self.value + other.value, self.name + other.name)
    def __sub__(self, other):
    """sub values and concatenate names"""
        return toto(self.value - other.value, self.name + other.name)  

tata = toto(5, "first")  
titi = toto(1, "second")

Теперь, если я попытаюсь применить np.add между этими двумя, я получу ожидаемый результат, так как np.add использует add .Но если я звоню, скажем, np.exp, я получаю сообщение об ошибке, как и ожидалось:

>>> np.exp(tata)
AttributeError: 'toto' object has no attribute 'exp'

Теперь я хотел бы «переопределить» все numpy ufuncs для плавной работы с этим классом без необходимости переопределятьвсе методы (exp (self), log (self), ...) в классе.

Я планировал использовать numpy ufunc's [__array_ufunc __] 1 , но яне совсем понимаю документ, так как он не дает простого примера реализации.

Если у кого-нибудь был опыт работы с этой новой функциональностью, которая выглядит многообещающе, не могли бы вы привести простой пример?

1 Ответ

0 голосов
/ 28 марта 2019

Если я расширю ваш класс с помощью метода __array_ufunc____repr__):

class toto():
    def __init__(self, value, name):
        self.value = value
        self.name = name
    def __add__(self, other):
        """add values and concatenate names"""
        return toto(self.value + other.value, self.name + other.name)
    def __sub__(self, other):
        """sub values and concatenate names"""
        return toto(self.value - other.value, self.name + other.name)

    def __repr__(self):
        return f"toto: {self.value}, {self.name}"
    def __array_ufunc__(self, *args, **kwargs):
        print(args)
        print(kwargs)

и попробую несколько вызовов ufunc:

In [458]: np.exp(tata)                                                          
(<ufunc 'exp'>, '__call__', toto: 5, first)
{}
In [459]: np.exp.reduce(tata)                                                   
(<ufunc 'exp'>, 'reduce', toto: 5, first)
{}
In [460]: np.multiply.reduce(tata)                                              
(<ufunc 'multiply'>, 'reduce', toto: 5, first)
{}
In [461]: np.exp.reduce(tata,axes=(1,2))                                        
(<ufunc 'exp'>, 'reduce', toto: 5, first)
{'axes': (1, 2)}
In [463]: np.exp.reduce(tata,axes=(1,2),out=np.arange(3))                       
(<ufunc 'exp'>, 'reduce', toto: 5, first)
{'axes': (1, 2), 'out': (array([0, 1, 2]),)}

, показывающихинформация, которую получает ваш класс.Очевидно, вы можете делать то, что вы хотите.Может вернуть NotImplemented.Я полагаю, что в вашем случае он может применить первый аргумент к вашему self.value или выполнить какой-либо специальный расчет.

Например, если я добавлю

      val = args[0].__call__(self.value) 
      return toto(val, self.name) 

, я получу:

In [468]: np.exp(tata)                                                          
(<ufunc 'exp'>, '__call__', toto: 5, first)
{}
Out[468]: toto: 148.4131591025766, first
In [469]: np.sin(tata)                                                          
(<ufunc 'sin'>, '__call__', toto: 5, first)
{}
Out[469]: toto: -0.9589242746631385, first

Однако, если я помещаю объект в массив, я все равно получаю ошибку метода

In [492]: np.exp(np.array(tata))                                                
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-492-4dc37eb0ffe3> in <module>
----> 1 np.exp(np.array(tata))

AttributeError: 'toto' object has no attribute 'exp'

Видимо ufunc в массиве объекта dtype выполняет итерации по элементам массива, ожидая использовать«соответствующий» метод.Для np.add (+) он ищет метод __add__.Для np.exp он ищет метод exp.Это __array_ufunc__ не называется.

Так что похоже, что оно предназначено больше для подкласса ndarray или чего-то подобного.Вы, я думаю, пытаетесь реализовать класс, который может работать как элементы массива dtype объекта.

...