Я пытаюсь использовать Python/Numpy векторизованные функции, чтобы уменьшить циклы.
Мой вызов функции выглядит так
out_vectors = v_calculation(
in_vectors,
p
)
Определение векторизованной функции
v_calculation = np.vectorize(
my_calculation,
signature='(j,i),(i)->()'
)
in_vectors - это массив формы
(3,6,200,3)
Но первые 2 измерения (3,6) могут быть любыми. Это размеры циклы.
р является массивом формы
(3,)
Мой расчет такой
def my_calculation(in_vector, p):
"""
total magnetic field from Biot-Savart law
"""
out_vector = np.zeros((3,))
l_vector = in_vector[1:, :] - in_vector[:-1, :]
r_vector = (in_vector[:-1, :] + l_vector / 2) - p
out_vector = np.sum(np.cross(l_vector, r_vector) / \
np.linalg.norm(r_vector) ** 3,
axis=0
)
return out_vector
В этой функции in_vector является массивом формы (200, 3), а p - той же формой (3,). Форма out_vector - (3,). Это правильно.
out_vectors, результат векторизованной функции должен быть (6,3). Это должны быть результаты my_calculation, суммированные по первому измерению input_vectors (3 в данном случае) для каждого второго измерения input_vectors (6 в данном случае). Второе измерение результата - 3 (x, y, z компоненты для вектора), такое же, как измерение p и четвертое измерение input_vectors. Я надеюсь, что это все ясно.
Мой код терпит неудачу в вызове векторизованной функции
Трассировки стека
~/path/to/my/code.py in calculate_vectors(mgr)
588 out_vectors = v_calculation(
589 in_vectors,
--> 590 p
591 )
~/miniconda/lib/python3.7/site-packages/numpy/lib/function_base.py in __call__(self, *args, **kwargs)
1970 vargs.extend([kwargs[_n] for _n in names])
1971
-> 1972 return self._vectorize_call(func=func, args=vargs)
1973
1974 def _get_ufunc_and_otypes(self, func, args):
~/miniconda/lib/python3.7/site-packages/numpy/lib/function_base.py in _vectorize_call(self, func, args)
2036 """Vectorized call to 'func' over positional 'args'."""
2037 if self.signature is not None:
-> 2038 res = self._vectorize_call_with_signature(func, args)
2039 elif not args:
2040 res = func()
~/miniconda/lib/python3.7/site-packages/numpy/lib/function_base.py in _vectorize_call_with_signature(self, func, args)
2100
2101 for output, result in zip(outputs, results):
-> 2102 output[index] = result
2103
2104 if outputs is None:
ValueError: setting an array element with a sequence.
Это работает для меня. Примечание. Я изменил подпись возврата, чтобы она соответствовала общему итоговому измерению обоих входов.
In [54]: A = np.arange(12).reshape(4,3); b = np.arange(3)
In [55]: my_calculation(A,b)
Out[55]: array([0., 0., 0.])
In [56]: f = np.vectorize(my_calculation, signature='(j,i),(i)->(i)')
In [57]: f(A,b)
Out[57]: array([0., 0., 0.])
In [58]: f([A,A,A],b)
Out[58]:
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
np.vectorize
медленнее, чем более прямая итерация. А с подписью еще медленнее. Так что используйте это для удобства, а не для скорости. Также я не видел и не отвечал на многие вопросы, связанные с опциейsignature
. Это относительно новая функция.output[index]
- это слот с одним элементомoutput
массива, ноresult
- это некий массив. Что говорит о том, что подпись не соответствует вашей функции. (Но я тоже не исследовал).