3

В python 3.5 был введен оператор @ для умножения матрицы, следующий за PEP465. Это реализовано, например. в numpy как matmul operator.Сделать матричный оператор умножения @ работать для скаляров в numpy

Однако, как это было предложено PEP, то NumPy оператор выдает исключение при вызове со скалярным операндом:

>>> import numpy as np 
>>> np.array([[1,2],[3,4]]) @ np.array([[1,2],[3,4]]) # works 
array([[ 7, 10], 
     [15, 22]]) 
>>> 1 @ 2            # doesn't work 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
TypeError: unsupported operand type(s) for @: 'int' and 'int' 

Это настоящий выключение для меня, так как я уверен, реализации алгоритмов цифровой обработки сигналов, которые должен работать как для скаляров, так и для матриц. Уравнения для обоих случаев математически точно эквивалентны, что неудивительно, так как «умножение матрицы 1-D x 1-D» эквивалентно скалярному умножению. Однако текущее состояние заставляет меня писать дублирующий код, чтобы правильно обрабатывать оба случая.

Итак, учитывая, что текущее состояние неудовлетворительное, есть ли разумный способ заставить оператора @ работать с скалярами? Я думал о добавлении настраиваемого метода __matmul__(self, other) к скалярным типам данных, но это похоже на много хлопот, учитывая количество задействованных внутренних типов данных. Могу ли я изменить реализацию метода __matmul__ для типов данных numpy массива, чтобы не генерировать исключение для операндов массива 1x1?

И, по причине, что является обоснованием этого дизайнерского решения? С головы до ног я не могу придумать никаких веских причин не применять этот оператор для скаляров.

+0

как насчет '[1] @ [2]'? Скалары уже «*», поэтому зачем их дублировать. – furas

+4

Похоже, что реальная проблема заключается в том, что ваш код иногда возвращает скаляры и иногда возвращает матрицы. Почему бы не рефакторировать, чтобы ваш код возвращал 1 x 1 матрицы вместо скаляров? Или напишите быструю функцию, которая принимает матрицу или скаляр и возвращает эту матрицу или матрицу 1x1 со скаляром в ней. –

+0

Почему вы не можете использовать процедуру try-except? – Jalo

ответ

1

Как предлагалось ajcr, вы можете обойти эту проблему, вызывая некоторую минимальную размерность на объекты, которые умножаются. Существует два разумных варианта: atleast_1d и atleast_2d, которые имеют разные результаты в отношении возвращаемого типа @: скаляр и 2D-массив размером 1 на 1.

x = 3 
y = 5 
z = np.atleast_1d(x) @ np.atleast_1d(y) # returns 15 
z = np.atleast_2d(x) @ np.atleast_2d(y) # returns array([[15]]) 

Однако:

  • Использование atleast_2d приведет к ошибке, если х и у являются 1D-массивы, которые бы в противном случае быть умножен обычно
  • Использование atleast_1d приведет к продукту, который является либо скаляр или матрицу, и вы не знаете, что именно.
  • Оба эти более подробные, чем np.dot(x, y), которые будут обрабатывать все эти случаи.

Кроме того, версия atleast_1d имеет тот же недостаток, который также можно было бы использовать с помощью скаляра @ scalar = scalar: вы не знаете, что можно сделать с выходом. Будет ли z.T или z.shape выбросить ошибку? Они работают для матриц 1 на 1, но не для скаляров. В настройке Python нельзя просто игнорировать различия между скалярами и массивами 1 на 1, не отказываясь от всех методов и свойств, которые имеют последние.