Numpy: Как на самом деле работает np.abs под капотом? - PullRequest
0 голосов
/ 26 февраля 2019

Я пытаюсь реализовать свою собственную абсолютную функцию для плотных векторов в Go.Я блуждаю, есть ли лучший способ получить абсолютное значение массива, чем возведение в квадрат, а затем квадратное укоренение?

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

1 Ответ

0 голосов
/ 26 февраля 2019

Исходный код NumPy может быть сложным для навигации, потому что он имеет так много функций для очень многих типов данных.Вы можете найти исходный код уровня C для функции абсолютного значения в файле scalarmath.c.src.Этот файл на самом деле является шаблоном с определениями функций, которые впоследствии реплицируются системой сборки для нескольких типов данных.Обратите внимание, что каждая функция - это «ядро», которое запускается для каждого элемента массива (цикл в массиве выполняется где-то еще).Функции всегда называются <name of the type>_ctype_absolute, где <name of the type> - это тип данных, к которому он применяется, и обычно является шаблонным.Давайте рассмотрим их.

/**begin repeat
 * #name = ubyte, ushort, uint, ulong, ulonglong#
 */

#define @name@_ctype_absolute @name@_ctype_positive

/**end repeat**/

Это для неподписанных типов.В этом случае абсолютное значение совпадает с np.positive, которое просто копирует значение без каких-либо действий (это то, что вы получаете, если у вас есть массив a и вы делаете +a).

/**begin repeat
 * #name = byte, short, int, long, longlong#
 * #type = npy_byte, npy_short, npy_int, npy_long, npy_longlong#
 */
static void
@name@_ctype_absolute(@type@ a, @type@ *out)
{
    *out = (a < 0 ? -a : a);
}
/**end repeat**/

Это для целых чисел со знаком.Довольно просто.

/**begin repeat
 * #name = float, double, longdouble#
 * #type = npy_float, npy_double, npy_longdouble#
 * #c = f,,l#
 */
static void
@name@_ctype_absolute(@type@ a, @type@ *out)
{
    *out = npy_fabs@c@(a);
}
/**end repeat**/

Это для значений с плавающей запятой.Здесь используются функции npy_fabsf, npy_fabs и npy_fabsl.Они объявлены в npy_math.h, но определены с помощью шаблонного кода C в npy_math_internal.h.src, по сути, вызывая аналоги C / C99 (если C99 не доступен, , в этом случае fabsf и fabsl эмулируются с fabs).Вы можете подумать, что предыдущий код должен работать также для типов с плавающей запятой, но на самом деле они более сложные, поскольку в них есть такие вещи, как NaN, бесконечность или знаковые нули, поэтому лучше использовать стандартные функции C, которые имеют дело со всемнадежно.

static void
half_ctype_absolute(npy_half a, npy_half *out)
{
    *out = a&0x7fffu;
}

На самом деле это не шаблон, это функция абсолютного значения для значений с плавающей точкой с половинной точностью .Оказывается, вы можете изменить знак, просто выполнив эту побитовую операцию (установите первый бит равным 0), поскольку половинная точность проще (если более ограничена), чем другие типы с плавающей запятой (обычно это то же самое для тех, но в особых случаях).

/**begin repeat
 * #name = cfloat, cdouble, clongdouble#
 * #type = npy_cfloat, npy_cdouble, npy_clongdouble#
 * #rtype = npy_float, npy_double, npy_longdouble#
 * #c = f,,l#
 */
static void
@name@_ctype_absolute(@type@ a, @rtype@ *out)
{
    *out = npy_cabs@c@(a);
}
/**end repeat**/

Последний предназначен для сложных типов.Они используют функции npy_cabsf, npycabs и npy_cabsl, снова объявленные в npy_math.h, но в этом случае реализованные в шаблонах в npy_math_complex.c.src с использованием функций C99 (если это не доступно, в этом случае эмулируется с np.hypot).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...