Numpy: Проблема с отслеживанием реализации NEP-18 (__array_function__)

Созданный на 25 сент. 2018  ·  54Комментарии  ·  Источник: numpy/numpy

  • [x] Основные функции для поддержки переопределений:

    • [x] Первоначальная реализация на чистом Python (# 12005)

    • [x] Проверить функции диспетчера в array_function_dispatch (https://github.com/numpy/numpy/pull/12099)



      • Отключить проверку, если NumPy не тестируется (если есть ощутимое влияние на время импорта) (необязательно)



    • [x] Добавьте атрибут .__skip_array_function__ function, чтобы можно было пропустить отправку __array_function__ . (https://github.com/numpy/numpy/pull/13389)

  • [x] Переопределите части numpy/core/overrides.py в C для повышения скорости (https://github.com/numpy/numpy/issues/12028):

    • [x] get_overloaded_types_and_args

    • [x] array_function_implementation_or_override

    • [x] ndarray.__array_function__ ?

    • [x] array_function_dispatch ?

  • [x] Поддержка переопределений для всех общедоступных функций NumPy.

    • [x] numpy.core



      • [x] простая часть (https://github.com/numpy/numpy/pull/12115)


      • [x] np.core.defchararray (# 12154)


      • [x] np.einsum и np.block (https://github.com/numpy/numpy/pull/12163)



    • [x] numpy.lib



      • [x] часть 1 (https://github.com/numpy/numpy/pull/12116)


      • [x] часть 2 (# 12119)



    • [x] numpy.fft / numpy.linalg (https://github.com/numpy/numpy/pull/12117)

    • [x] функции в настоящее время написаны полностью на C : empty_like, concatenate, inner, where, lexsort, can_cast, min_scalar_type, result_type, dot, vdot, is_busday, busday_offset, busday_count, datetime_as_string (https://github.com/numpy/numpy/ тянуть / 12175)

    • [x] linspace

    • [] [ arange? ] (https://github.com/numpy/numpy/issues/12379)

  • [x] Улучшения удобства использования

    • [x] [улучшенное сообщение об ошибке] (https://github.com/numpy/numpy/issues/12213) для нереализованных функций (https://github.com/numpy/numpy/pull/12251)

    • [x] ndarray.__repr__ не должны полагаться на __array_function__ (https://github.com/numpy/numpy/pull/12212)

    • [x] stacklevel следует увеличить на 1 для обернутых функций, чтобы трассировки указывали в нужное место (gh-13329)

  • [x] Исправить все известные ошибки / сбои последующих тестов
  • [ ] Документация

    • [x] Примечания к выпуску (# 12028)

    • [x] Документы с описанием

    • [] Исправлены строки документации, чтобы прояснить перегруженные аргументы?

__array_function__

Самый полезный комментарий

Есть ли предложение относительно внесения такого рода изменений или вы говорите, что поддержка отправки np.array будет действительно сложной задачей, и поэтому мы никогда не сможем получить 100% поддержку?

В NEP 22 варианты обсуждаются здесь. Я не думаю, что мы можем безопасно изменить семантику np.asarray() чтобы возвращать что-либо, кроме объекта numpy.ndarray - для этого нам понадобится новый протокол.

Проблема в том, что np.asarray() в настоящее время является идиоматическим способом преобразования в объект массива numpy, который использует can и ожидает точного соответствия numpy.ndarray , например, вплоть до макета памяти.

Безусловно, существует множество вариантов использования, в которых это не так, но переключение этого поведения приведет к поломке большого количества нисходящего кода, так что это не начало. Проекты, находящиеся ниже по течению, должны будут согласиться хотя бы на этот аспект набора утиных массивов.

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

Да. NEP 18 не предназначен для того, чтобы быть полным решением для альтернативных вариантов NumPy, но это шаг в этом направлении.

Все 54 Комментарий

Было бы неплохо объединить предварительный «Украсьте все общедоступные функции NumPy с помощью @array_function_dispatch» для некоторых важных функций и попросить нижестоящих потребителей протокола опробовать его.

Как только мы объединим https://github.com/numpy/numpy/pull/12099, у меня будет готов еще один PR, который добавит декораторы отправки для большей части numpy.core . Доделать будет довольно легко - на сборку ушло меньше часа.

коп @ eric-wieser @mrocklin @mhvk @hameerabbasi

См. Https://github.com/shoyer/numpy/tree/array-function-easy-impl для моей ветки, реализующей все «простые» переопределения функций с оболочками Python. Остальные части - это np.block , np.einsum и несколько многомассивных функций, полностью написанных на C (например, np.concatenate ). Я разделю это на кучу PR, когда мы закончим с # 12099.

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

@shoyer - по тестам, согласен, писать тесты для каждого не особо полезно; вместо этого в numpy может иметь смысл относительно быстро начать использовать переопределения в MaskedArray .

Для меня

См. Https://github.com/numpy/numpy/pull/12115 , https://github.com/numpy/numpy/pull/12116 , # 12119 и https://github.com/numpy/numpy/pull/ 12117 для __array_function__ для функций, определенных в Python.

@shoyer -

  • Для некоторых функций, таких как reshape , исходная функциональность уже предоставила способ переопределить ее, определив метод reshape . Мы фактически осуждаем это для любого класса, который определяет __array_function__ .
  • Для других функций, таких как np.median , осторожное использование np.asanyarray и ufuncs гарантирует, что подклассы уже могут их использовать. Но к этим функциям больше нельзя получить доступ напрямую.

Я думаю, что в целом эти две вещи, вероятно, являются преимуществами, поскольку мы упрощаем интерфейс и можем оптимизировать реализации для чистого ndarray - хотя последнее предполагает, что ndarray.__array_function__ должен взять на себя преобразование списков и т. Д. в ndarray , чтобы реализации могли пропустить эту часть). Тем не менее, я подумал, что отмечу это, потому что это заставляет меня бояться реализовать это за Quantity немного больше, чем я думал - как с точки зрения объема работы, так и с точки зрения возможного снижения производительности.

хотя последний предполагает, что ndarray .__ array_function__ должен взять на себя преобразование списков и т.д. в ndarray, чтобы реализации могли пропустить эту часть).

Я не уверен, что слежу за этим.

Мы действительно эффективно отказываемся от старого способа переопределения таких функций, как reshape и mean , хотя старый способ по-прежнему поддерживает неполные реализации API NumPy.

Я не уверен, что слежу за этим.

Я думаю, проблема в том, что если мы реализуем __array_function__ даже для одной функции, предыдущие механизмы полностью сломаются и не будет возможности переключения при отказе. Вот почему я предлагаю вернуться к моему предложению NotImplementedButCoercible .

@hameerabbasi - да, вот в чем проблема. Хотя здесь нам нужно быть осторожными, насколько легко мы можем полагаться на решения из клейкой ленты, от которых мы действительно предпочли бы избавиться ... (вот почему я написал выше, что мои "проблемы" на самом деле могут быть преимуществами ...) . Может быть, есть случай попробовать, как в 1.16, а затем решить на собственном опыте, хотим ли мы предоставить запасной вариант «игнорировать мой __array_function__ для этого случая».

Re: стиль диспетчера: Мои предпочтения по стилю основаны на соображениях памяти / времени импорта и многословности. Проще говоря, объедините диспетчеров, где подпись, скорее всего, останется прежней. Таким образом, мы создаем наименьшее количество объектов, и попадания в кеш будут также выше.

Тем не менее, я не слишком против лямбда-стиля.

Стиль написания диспетчерских функций теперь появился в нескольких PR. Было бы хорошо сделать последовательный выбор через NumPy.

У нас есть несколько вариантов:


Вариант 1. Напишите отдельный диспетчер для каждой функции, например,

def _sin_dispatcher(a):
    return (a,)


@array_function_dispatch(_sin_dispatcher)
def sin(a):
     ...


def _cos_dispatcher(a):
    return (a,)


@array_function_dispatch(_cos_dispatcher)
def cos(a):
    ...

Преимущества:

  • Очень читаемый
  • Легко найти определения функций диспетчера
  • Удалять сообщение об ошибке, если вы вводите неверные аргументы, например, sin(x=1) -> TypeError: _sin_dispatcher() got an unexpected keyword argument 'x' .

Недостатки:

  • Много повторений, даже если многие функции в модуле имеют одинаковую сигнатуру.

Вариант 2 : повторно использовать функции диспетчера в модуле, например,

def _unary_dispatcher(a):
    return (a,)


@array_function_dispatch(_unary_dispatcher)
def sin(a):
     ...


@array_function_dispatch(_unary_dispatcher)
def cos(a):
    ...

Преимущества:

  • Меньше повторений
  • Удобочитаемый

Недостатки:

  • Может быть немного сложнее найти определения функций диспетчера
  • Немного менее четкие сообщения об ошибках для неверных аргументов, например, sin(x=1) -> TypeError: _unary_dispatcher() got an unexpected keyword argument 'x'

Вариант 3. Используйте функции lambda если определение диспетчера помещается в одну строку, например,

# inline style (shorter)
@array_function_dispatch(lambda a: (a,))
def sin(a):
     ...


@array_function_dispatch(lambda a, n=None, axis=None, norm=None: (a,))
def fft(a, n=None, axis=-1, norm=None):
     ...
# multiline style (more readable?)
@array_function_dispatch(
    lambda a: (a,)
)
def sin(a):
     ...


@array_function_dispatch(
    lambda a, n=None, axis=None, norm=None: (a,)
)
def fft(a, n=None, axis=-1, norm=None):
     ...

Преимущества:

  • Не нужно искать определения диспетчера, они тут же.
  • Меньшее количество символов и строк кодов.
  • Очень хорошо смотрится для коротких случаев (например, с одним аргументом), особенно когда лямбда короче имени функции.

Недостатки:

  • Более повторяющийся код, чем вариант 2.
  • Выглядит довольно загроможденным, если аргументов больше, чем несколько
  • Также имеет менее четкие сообщения об ошибках ( TypeError: <lambda>() got an unexpected keyword argument 'x' )

@shoyer : отредактировано, чтобы добавить двухстрочный интервал PEP8, чтобы сделать аспект "строк кода" более реалистичным.

Обратите внимание, что проблемы с сообщениями об ошибках могут быть устранены путем реконструкции объекта кода , хотя это требует некоторых затрат времени импорта. Возможно, стоит изучить тунца @nschloe , чтобы сравнить некоторые варианты.

Да, модуль декоратора также можно использовать для генерации определения функции (он использует несколько иной подход для генерации кода, немного больше похожий на namedtuple, поскольку он использует exec() ).

Пока ошибка не решена, я думаю, нам нужно придерживаться параметров с диспетчером с понятным именем. Я бы немного связал диспетчеров вместе (2) по соображениям памяти, хотя тогда бы очень хорошо помнил сообщение об ошибке, поэтому предлагал бы вызвать диспетчера что-то вроде _dispatch_on_x .

Хотя, если мы сможем исправить ошибку, все изменится. Например, это может быть так же просто, как перехват исключений, замена <lambda> на имя функции в тексте исключения, а затем повторное повышение. (Или эта цепочка в наши дни?)

Я согласен с тем, что сообщение об ошибке должно быть четким, в идеале вообще не должно меняться.

Хорошо, на данный момент я думаю, что лучше воздержаться от использования lambda , если мы не получим какую-то работу по генерации кода.

https://github.com/numpy/numpy/pull/12175 добавляет черновик того, как могут выглядеть переопределения для функций с несколькими массивами (написанные на C), если мы воспользуемся подходом оболочки Python.

@mattip Где мы находимся в реализации matmul как ufunc? Как только мы закончим все эти __array_function__ переопределения, я думаю, это последнее, что нам нужно для того, чтобы сделать публичный API NumPy полностью перегружаемым. Было бы хорошо, если бы все это было готово для NumPy 1.16!

PR № 11175, который реализует NEP 20, продвигается медленно. Это блокировщик для PR # 11133, который имеет код цикла matmul. Этот код еще нужно обновить, а затем проверить с помощью тестов, что новый код не медленнее старого.

У меня есть четыре PR для проверки, которые должны выполнить полный набор переопределений. Мы будем благодарны за окончательные обзоры / подписи / слияния, так что мы можем начать всерьез тестировать __array_function__ ! https://github.com/numpy/numpy/pull/12154 , https://github.com/numpy/numpy/pull/12163 , https://github.com/numpy/numpy/pull/12119 , https: //github.com/numpy/numpy/pull/12175

Добавление переопределений в np.core привело к сбою нескольких тестов pandas (https://github.com/pandas-dev/pandas/issues/23172). Мы еще не совсем уверены в том, что происходит, но мы обязательно должны разобраться в этом перед выпуском.

См. Https://github.com/numpy/numpy/issues/12225, чтобы узнать, почему это вызывает сбои тестов в dask / pandas.

Некоторые тесты времени импорта (на моем MacBook Pro с твердотельным накопителем):

  • NumPy 1.15.2: 152,451 мс
  • Мастер NumPy: 156,5745 мс
  • Использование decorator.decorate (# 12226): 183,694 мс

Мой тестовый скрипт

import numpy as np
import subprocess

times = []
for _ in range(100):
    result = subprocess.run("python -X importtime -c 'import numpy'",
                            shell=True, capture_output=True)
    last_line = result.stderr.rstrip().split(b'\n')[-1]
    time = float(last_line.decode('ascii')[-15:-7].strip().rstrip())
    times.append(time)

print(np.median(times) / 1e3)

Есть идеи по использованию памяти (до / после)? Это тоже полезно, особенно для приложений Интернета вещей.

Вы знаете, как надежно измерить использование памяти для модуля?
В субботу, 20 октября 2018 г., в 6:56 Хамир Аббаси [email protected]
написал:

Есть идеи по использованию памяти (до / после)? Это полезно как
ну особенно для приложений IoT.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/12028#issuecomment-431584123 или отключить звук
нить
https://github.com/notifications/unsubscribe-auth/ABKS1k_IkrJ2YmYReaDrnkNvcH2X0-ZCks5umyuogaJpZM4W3kSC
.

Я думаю, что написание сценария, содержащего import numpy as np , добавление оператора сна и отслеживание памяти процесса должно быть достаточно хорошим. https://superuser.com/questions/581108/how-can-i-track-and-log-cpu-and-memory-usage-on-a-mac

Любые другие разработчики ядра хотят быстро взглянуть (на самом деле, он включает только две функции!) На https://github.com/numpy/numpy/pull/12163? Это последний PR, добавляющий array_function_dispatch к внутренним функциям numpy.

Для справки, вот разница в производительности, которую я вижу при отключении __array_function__ :

       before           after         ratio
     [45718fd7]       [4e5aa2cd]
     <master>         <disable-array-function>
+        72.5±2ms         132±20ms     1.82  bench_io.LoadtxtCSVdtypes.time_loadtxt_dtypes_csv('complex128', 10000)
-        44.9±2μs       40.8±0.6μs     0.91  bench_ma.Concatenate.time_it('ndarray', 2)
-      15.3±0.3μs       13.3±0.7μs     0.87  bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'object'>)
-        38.4±1μs         32.7±2μs     0.85  bench_linalg.Linalg.time_op('norm', 'longfloat')
-        68.7±3μs         56.5±3μs     0.82  bench_linalg.Linalg.time_op('norm', 'complex256')
-        80.6±4μs         65.9±1μs     0.82  bench_function_base.Median.time_even
-        82.4±2μs         66.8±3μs     0.81  bench_shape_base.Block.time_no_lists(100)
-        73.5±3μs         59.3±3μs     0.81  bench_function_base.Median.time_even_inplace
-      15.2±0.3μs       12.2±0.6μs     0.80  bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'str'>)
-      2.20±0.1ms      1.76±0.04ms     0.80  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint64', (4, 4))
-        388±20μs         310±10μs     0.80  bench_lib.Pad.time_pad((10, 10, 10), 3, 'linear_ramp')
-        659±20μs         524±20μs     0.80  bench_linalg.Linalg.time_op('det', 'float32')
-      22.9±0.7μs       18.2±0.8μs     0.79  bench_function_base.Where.time_1
-        980±50μs         775±20μs     0.79  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint32', (4, 4))
-        36.6±1μs         29.0±1μs     0.79  bench_ma.Concatenate.time_it('unmasked', 2)
-      16.4±0.7μs       12.9±0.6μs     0.79  bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'str'>)
-      16.4±0.5μs       12.9±0.4μs     0.79  bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'object'>)
-         141±5μs          110±4μs     0.78  bench_lib.Pad.time_pad((10, 100), (0, 5), 'linear_ramp')
-      18.0±0.6μs       14.1±0.6μs     0.78  bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'object'>)
-      11.9±0.6μs       9.28±0.5μs     0.78  bench_core.CountNonzero.time_count_nonzero_axis(1, 100, <type 'int'>)
-        54.6±3μs         42.4±2μs     0.78  bench_function_base.Median.time_odd_small
-        317±10μs          246±7μs     0.78  bench_lib.Pad.time_pad((10, 10, 10), 1, 'linear_ramp')
-      13.8±0.5μs       10.7±0.7μs     0.77  bench_reduce.MinMax.time_min(<type 'numpy.float64'>)
-        73.3±6μs         56.6±4μs     0.77  bench_lib.Pad.time_pad((1000,), (0, 5), 'mean')
-      14.7±0.7μs       11.4±0.3μs     0.77  bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'str'>)
-        21.5±2μs       16.5±0.6μs     0.77  bench_reduce.MinMax.time_min(<type 'numpy.int64'>)
-         117±4μs         89.2±3μs     0.76  bench_lib.Pad.time_pad((1000,), 3, 'linear_ramp')
-        43.7±1μs         33.4±1μs     0.76  bench_linalg.Linalg.time_op('norm', 'complex128')
-      12.6±0.6μs       9.55±0.2μs     0.76  bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'int'>)
-        636±20μs         482±20μs     0.76  bench_ma.MA.time_masked_array_l100
-        86.6±4μs         65.6±4μs     0.76  bench_lib.Pad.time_pad((1000,), (0, 5), 'linear_ramp')
-         120±4μs         90.4±2μs     0.75  bench_lib.Pad.time_pad((1000,), 1, 'linear_ramp')
-         160±5μs          119±8μs     0.74  bench_ma.Concatenate.time_it('ndarray+masked', 100)
-      14.4±0.6μs       10.7±0.3μs     0.74  bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'str'>)
-      15.7±0.4μs       11.7±0.6μs     0.74  bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'str'>)
-        21.8±2μs       16.1±0.7μs     0.74  bench_reduce.MinMax.time_max(<type 'numpy.int64'>)
-      11.9±0.6μs       8.79±0.3μs     0.74  bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'bool'>)
-        53.8±3μs         39.4±2μs     0.73  bench_function_base.Median.time_even_small
-        106±20μs         76.7±4μs     0.73  bench_function_base.Select.time_select
-        168±10μs          122±4μs     0.72  bench_shape_base.Block2D.time_block2d((512, 512), 'uint32', (2, 2))
-      12.5±0.5μs       8.96±0.4μs     0.72  bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'int'>)
-        162±10μs          115±5μs     0.71  bench_function_base.Percentile.time_percentile
-        12.9±1μs       9.12±0.4μs     0.71  bench_random.Random.time_rng('normal')
-      9.71±0.4μs       6.88±0.3μs     0.71  bench_core.CorrConv.time_convolve(1000, 10, 'full')
-      15.1±0.8μs       10.7±0.4μs     0.71  bench_reduce.MinMax.time_max(<type 'numpy.float64'>)
-         153±9μs          108±7μs     0.71  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint8', (2, 2))
-         109±5μs         76.9±5μs     0.71  bench_ma.Concatenate.time_it('ndarray+masked', 2)
-        34.3±1μs       24.2±0.6μs     0.71  bench_linalg.Linalg.time_op('norm', 'complex64')
-      9.80±0.2μs       6.84±0.5μs     0.70  bench_core.CorrConv.time_convolve(1000, 10, 'same')
-        27.4±6μs         19.1±2μs     0.70  bench_core.CountNonzero.time_count_nonzero_axis(1, 10000, <type 'bool'>)
-      9.35±0.4μs       6.50±0.3μs     0.70  bench_core.CorrConv.time_convolve(50, 100, 'full')
-        65.2±4μs         45.2±1μs     0.69  bench_shape_base.Block.time_block_simple_row_wise(100)
-        12.9±1μs       8.89±0.3μs     0.69  bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'bool'>)
-        19.6±3μs       13.5±0.4μs     0.69  bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'object'>)
-        75.6±2μs         52.1±3μs     0.69  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'reflect')
-        12.4±1μs       8.51±0.4μs     0.69  bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'bool'>)
-        172±30μs          117±4μs     0.68  bench_ma.Concatenate.time_it('unmasked+masked', 100)
-      23.1±0.5μs       15.8±0.9μs     0.68  bench_linalg.Linalg.time_op('norm', 'int16')
-      8.18±0.9μs       5.57±0.1μs     0.68  bench_core.CorrConv.time_correlate(1000, 10, 'full')
-         153±5μs          103±3μs     0.68  bench_function_base.Percentile.time_quartile
-       758±100μs         512±20μs     0.68  bench_linalg.Linalg.time_op('det', 'int16')
-        55.4±6μs         37.4±1μs     0.68  bench_ma.Concatenate.time_it('masked', 2)
-        234±30μs          157±5μs     0.67  bench_shape_base.Block.time_nested(100)
-         103±4μs         69.3±3μs     0.67  bench_linalg.Eindot.time_dot_d_dot_b_c
-      19.2±0.4μs       12.9±0.6μs     0.67  bench_core.Core.time_tril_l10x10
-         122±7μs         81.7±4μs     0.67  bench_lib.Pad.time_pad((10, 10, 10), 3, 'edge')
-        22.9±1μs       15.3±0.5μs     0.67  bench_linalg.Linalg.time_op('norm', 'int32')
-        16.6±2μs       11.0±0.3μs     0.66  bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'object'>)
-      9.98±0.3μs       6.58±0.1μs     0.66  bench_core.CorrConv.time_convolve(1000, 10, 'valid')
-         118±6μs         77.9±4μs     0.66  bench_shape_base.Block2D.time_block2d((512, 512), 'uint16', (2, 2))
-        212±50μs          140±8μs     0.66  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'mean')
-      21.9±0.7μs       14.4±0.5μs     0.66  bench_linalg.Linalg.time_op('norm', 'int64')
-         131±5μs         85.9±5μs     0.65  bench_lib.Pad.time_pad((10, 10, 10), 3, 'constant')
-        56.8±2μs         37.0±3μs     0.65  bench_lib.Pad.time_pad((1000,), (0, 5), 'constant')
-        58.9±3μs         38.1±1μs     0.65  bench_lib.Pad.time_pad((10, 100), (0, 5), 'reflect')
-        72.1±2μs         46.5±3μs     0.64  bench_lib.Pad.time_pad((10, 100), (0, 5), 'constant')
-      8.66±0.3μs       5.58±0.2μs     0.64  bench_core.CorrConv.time_correlate(50, 100, 'full')
-        300±30μs         193±10μs     0.64  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint8', (4, 4))
-        15.9±5μs       10.2±0.3μs     0.64  bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'int'>)
-      13.7±0.5μs       8.80±0.1μs     0.64  bench_random.Random.time_rng('uniform')
-      8.60±0.5μs       5.50±0.2μs     0.64  bench_core.CorrConv.time_correlate(1000, 10, 'same')
-        44.7±2μs       28.5±0.7μs     0.64  bench_lib.Pad.time_pad((1000,), 1, 'reflect')
-        72.7±3μs         46.2±2μs     0.64  bench_lib.Pad.time_pad((10, 10, 10), 3, 'wrap')
-        567±50μs         360±40μs     0.63  bench_shape_base.Block2D.time_block2d((512, 512), 'uint64', (2, 2))
-        58.0±3μs         36.7±2μs     0.63  bench_lib.Pad.time_pad((10, 100), 3, 'reflect')
-        219±30μs          138±7μs     0.63  bench_lib.Pad.time_pad((10, 100), 1, 'mean')
-        261±60μs         164±10μs     0.63  bench_lib.Pad.time_pad((10, 100), 1, 'linear_ramp')
-       825±100μs         519±30μs     0.63  bench_shape_base.Block2D.time_block2d((512, 512), 'uint64', (4, 4))
-         121±5μs         75.7±2μs     0.63  bench_lib.Pad.time_pad((10, 10, 10), 1, 'constant')
-      8.16±0.2μs       5.08±0.4μs     0.62  bench_core.CorrConv.time_convolve(50, 100, 'same')
-        66.6±3μs         41.3±2μs     0.62  bench_lib.Pad.time_pad((1000,), 3, 'constant')
-        53.1±3μs       32.9±0.8μs     0.62  bench_lib.Pad.time_pad((10, 100), 3, 'wrap')
-        285±60μs         177±10μs     0.62  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'linear_ramp')
-      8.30±0.9μs       5.14±0.1μs     0.62  bench_core.CorrConv.time_correlate(1000, 10, 'valid')
-         115±3μs         71.2±3μs     0.62  bench_shape_base.Block2D.time_block2d((256, 256), 'uint64', (2, 2))
-      19.1±0.5μs       11.8±0.6μs     0.62  bench_linalg.Linalg.time_op('norm', 'float64')
-        95.3±5μs         58.6±2μs     0.62  bench_lib.Pad.time_pad((10, 100), 1, 'constant')
-        44.6±1μs       27.2±0.9μs     0.61  bench_lib.Pad.time_pad((1000,), (0, 5), 'edge')
-        447±20μs         270±10μs     0.61  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint16', (4, 4))
-        53.9±2μs         32.6±2μs     0.60  bench_lib.Pad.time_pad((10, 100), 1, 'wrap')
-        11.6±1μs       6.97±0.4μs     0.60  bench_reduce.MinMax.time_max(<type 'numpy.float32'>)
-        95.9±5μs         57.7±2μs     0.60  bench_lib.Pad.time_pad((10, 100), 3, 'constant')
-        47.2±2μs         28.2±2μs     0.60  bench_lib.Pad.time_pad((1000,), (0, 5), 'reflect')
-      5.51±0.2μs      3.27±0.07μs     0.59  bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'object'>)
-        74.3±3μs         44.0±2μs     0.59  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'wrap')
-        76.2±3μs       45.0±0.8μs     0.59  bench_lib.Pad.time_pad((10, 10, 10), 1, 'reflect')
-        57.1±1μs         33.5±2μs     0.59  bench_lib.Pad.time_pad((10, 100), (0, 5), 'wrap')
-        52.0±2μs         30.4±1μs     0.58  bench_lib.Pad.time_pad((1000,), 1, 'edge')
-        42.6±2μs       24.9±0.9μs     0.58  bench_lib.Pad.time_pad((1000,), 3, 'wrap')
-        15.0±3μs       8.73±0.3μs     0.58  bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'bool'>)
-        16.0±3μs       9.29±0.3μs     0.58  bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'int'>)
-        53.1±1μs         30.9±2μs     0.58  bench_lib.Pad.time_pad((1000,), 3, 'edge')
-        88.0±8μs         51.1±3μs     0.58  bench_lib.Pad.time_pad((10, 10, 10), 3, 'reflect')
-        44.6±2μs         25.9±1μs     0.58  bench_lib.Pad.time_pad((1000,), (0, 5), 'wrap')
-        90.3±5μs         51.9±1μs     0.57  bench_shape_base.Block2D.time_block2d((512, 512), 'uint8', (2, 2))
-      15.6±0.5μs       8.93±0.3μs     0.57  bench_linalg.Linalg.time_op('norm', 'float32')
-         102±6μs       58.3±0.9μs     0.57  bench_lib.Pad.time_pad((10, 10, 10), 1, 'edge')
-        80.1±4μs         45.6±3μs     0.57  bench_lib.Pad.time_pad((10, 100), 3, 'edge')
-        44.2±2μs         24.9±1μs     0.56  bench_lib.Pad.time_pad((1000,), 1, 'wrap')
-        71.6±8μs         39.5±1μs     0.55  bench_lib.Pad.time_pad((10, 10, 10), 1, 'wrap')
-       81.7±10μs         44.8±2μs     0.55  bench_lib.Pad.time_pad((10, 100), 1, 'edge')
-        420±90μs         230±10μs     0.55  bench_shape_base.Block.time_3d(10, 'block')
-        114±20μs         62.3±2μs     0.55  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'constant')
-      5.76±0.1μs      3.13±0.08μs     0.54  bench_core.CorrConv.time_convolve(50, 10, 'same')
-      5.30±0.1μs      2.84±0.08μs     0.54  bench_core.CorrConv.time_correlate(50, 100, 'valid')
-        92.5±4μs         49.3±1μs     0.53  bench_shape_base.Block2D.time_block2d((256, 256), 'uint32', (2, 2))
-        13.5±3μs       7.07±0.2μs     0.52  bench_reduce.MinMax.time_min(<type 'numpy.float32'>)
-        7.66±1μs       3.88±0.2μs     0.51  bench_core.CorrConv.time_convolve(50, 100, 'valid')
-        29.0±3μs       14.5±0.8μs     0.50  bench_shape_base.Block.time_no_lists(10)
-      6.62±0.3μs       3.30±0.2μs     0.50  bench_core.CorrConv.time_convolve(1000, 1000, 'valid')
-        74.2±7μs       36.2±0.9μs     0.49  bench_shape_base.Block2D.time_block2d((256, 256), 'uint16', (2, 2))
-      5.55±0.3μs       2.70±0.2μs     0.49  bench_core.CorrConv.time_convolve(50, 10, 'valid')
-       73.9±20μs         35.8±2μs     0.48  bench_lib.Pad.time_pad((10, 100), 1, 'reflect')
-        224±20μs          107±7μs     0.48  bench_shape_base.Block2D.time_block2d((256, 256), 'uint64', (4, 4))
-      3.87±0.1μs      1.83±0.06μs     0.47  bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'str'>)
-        109±30μs         51.5±3μs     0.47  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'edge')
-        240±20μs          112±4μs     0.47  bench_shape_base.Block2D.time_block2d((512, 512), 'uint16', (4, 4))
-        337±40μs          158±7μs     0.47  bench_shape_base.Block2D.time_block2d((512, 512), 'uint32', (4, 4))
-         188±8μs         88.0±2μs     0.47  bench_shape_base.Block2D.time_block2d((512, 512), 'uint8', (4, 4))
-      4.39±0.2μs      2.04±0.09μs     0.47  bench_core.CountNonzero.time_count_nonzero(3, 10000, <type 'bool'>)
-        73.2±4μs       33.9±0.5μs     0.46  bench_shape_base.Block2D.time_block2d((128, 128), 'uint64', (2, 2))
-        5.48±1μs       2.44±0.1μs     0.45  bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'object'>)
-      4.46±0.1μs      1.97±0.08μs     0.44  bench_core.CorrConv.time_correlate(50, 10, 'full')
-        30.4±9μs       13.3±0.3μs     0.44  bench_shape_base.Block.time_no_lists(1)
-      7.05±0.2μs      3.05±0.06μs     0.43  bench_reduce.SmallReduction.time_small
-        7.35±1μs       3.12±0.2μs     0.42  bench_core.CorrConv.time_convolve(50, 10, 'full')
-      4.36±0.1μs      1.84±0.07μs     0.42  bench_core.CorrConv.time_correlate(50, 10, 'same')
-      3.51±0.2μs      1.46±0.05μs     0.42  bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'object'>)
-     4.03±0.05μs       1.66±0.1μs     0.41  bench_core.CorrConv.time_correlate(1000, 1000, 'valid')
-        199±10μs         80.1±3μs     0.40  bench_shape_base.Block2D.time_block2d((256, 256), 'uint32', (4, 4))
-      3.98±0.2μs      1.60±0.08μs     0.40  bench_core.CountNonzero.time_count_nonzero(2, 10000, <type 'bool'>)
-        61.8±2μs         24.8±1μs     0.40  bench_shape_base.Block2D.time_block2d((256, 256), 'uint8', (2, 2))
-      4.13±0.1μs      1.62±0.05μs     0.39  bench_core.CorrConv.time_correlate(50, 10, 'valid')
-        61.6±2μs         23.9±1μs     0.39  bench_shape_base.Block2D.time_block2d((128, 128), 'uint32', (2, 2))
-        184±10μs         70.5±3μs     0.38  bench_shape_base.Block2D.time_block2d((256, 256), 'uint16', (4, 4))
-        56.1±4μs       21.0±0.9μs     0.38  bench_shape_base.Block2D.time_block2d((64, 64), 'uint64', (2, 2))
-        40.0±2μs       15.0±0.6μs     0.37  bench_shape_base.Block.time_block_simple_column_wise(10)
-         121±2μs         45.1±2μs     0.37  bench_shape_base.Block.time_nested(1)
-         179±4μs         66.1±4μs     0.37  bench_shape_base.Block2D.time_block2d((128, 128), 'uint64', (4, 4))
-        59.8±2μs         22.0±1μs     0.37  bench_shape_base.Block2D.time_block2d((128, 128), 'uint16', (2, 2))
-     3.19±0.05μs      1.17±0.02μs     0.37  bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'str'>)
-        54.0±3μs         19.7±1μs     0.37  bench_shape_base.Block2D.time_block2d((32, 32), 'uint64', (2, 2))
-        56.9±1μs       20.7±0.7μs     0.36  bench_shape_base.Block2D.time_block2d((64, 64), 'uint32', (2, 2))
-      3.14±0.1μs      1.14±0.04μs     0.36  bench_core.CountNonzero.time_count_nonzero(1, 10000, <type 'bool'>)
-        92.7±2μs         33.7±2μs     0.36  bench_shape_base.Block.time_block_complicated(1)
-         104±4μs         37.8±1μs     0.36  bench_shape_base.Block.time_block_complicated(10)
-         128±5μs         45.5±2μs     0.36  bench_shape_base.Block.time_nested(10)
-       196±100μs         69.4±3μs     0.35  bench_ma.Concatenate.time_it('unmasked+masked', 2)
-         153±5μs         53.9±2μs     0.35  bench_shape_base.Block2D.time_block2d((128, 128), 'uint16', (4, 4))
-        39.4±2μs       13.8±0.5μs     0.35  bench_shape_base.Block.time_block_simple_column_wise(1)
-        53.5±2μs         18.7±1μs     0.35  bench_shape_base.Block2D.time_block2d((32, 32), 'uint8', (2, 2))
-        55.2±2μs       19.3±0.6μs     0.35  bench_shape_base.Block2D.time_block2d((32, 32), 'uint16', (2, 2))
-        16.9±1μs       5.89±0.5μs     0.35  bench_core.Core.time_dstack_l
-        60.6±3μs       21.1±0.6μs     0.35  bench_shape_base.Block2D.time_block2d((128, 128), 'uint8', (2, 2))
-      25.5±0.2μs       8.88±0.3μs     0.35  bench_shape_base.Block.time_block_simple_row_wise(10)
-        54.6±3μs       19.0±0.6μs     0.35  bench_shape_base.Block2D.time_block2d((16, 16), 'uint64', (2, 2))
-        52.6±2μs       18.2±0.7μs     0.35  bench_shape_base.Block2D.time_block2d((16, 16), 'uint16', (2, 2))
-        6.57±2μs      2.25±0.08μs     0.34  bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'str'>)
-        24.3±1μs       8.30±0.6μs     0.34  bench_shape_base.Block.time_block_simple_row_wise(1)
-         148±3μs         50.0±3μs     0.34  bench_shape_base.Block2D.time_block2d((16, 16), 'uint32', (4, 4))
-         171±8μs         57.9±4μs     0.34  bench_shape_base.Block2D.time_block2d((256, 256), 'uint8', (4, 4))
-         159±5μs         53.8±1μs     0.34  bench_shape_base.Block2D.time_block2d((64, 64), 'uint64', (4, 4))
-        171±20μs         57.7±2μs     0.34  bench_shape_base.Block2D.time_block2d((128, 128), 'uint32', (4, 4))
-      3.15±0.3μs      1.06±0.03μs     0.34  bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'int'>)
-        55.7±5μs       18.7±0.2μs     0.34  bench_shape_base.Block2D.time_block2d((16, 16), 'uint8', (2, 2))
-         158±7μs         52.6±3μs     0.33  bench_shape_base.Block2D.time_block2d((128, 128), 'uint8', (4, 4))
-         153±4μs         50.7±1μs     0.33  bench_shape_base.Block2D.time_block2d((32, 32), 'uint64', (4, 4))
-         152±7μs         50.3±1μs     0.33  bench_shape_base.Block2D.time_block2d((16, 16), 'uint8', (4, 4))
-        53.6±3μs       17.7±0.4μs     0.33  bench_shape_base.Block2D.time_block2d((16, 16), 'uint32', (2, 2))
-         156±4μs         51.4±3μs     0.33  bench_shape_base.Block2D.time_block2d((64, 64), 'uint8', (4, 4))
-         148±3μs         48.2±2μs     0.33  bench_shape_base.Block2D.time_block2d((16, 16), 'uint16', (4, 4))
-        160±10μs         52.0±1μs     0.33  bench_shape_base.Block2D.time_block2d((64, 64), 'uint32', (4, 4))
-         159±8μs         51.4±3μs     0.32  bench_shape_base.Block2D.time_block2d((64, 64), 'uint16', (4, 4))
-        59.8±3μs         19.3±1μs     0.32  bench_shape_base.Block2D.time_block2d((32, 32), 'uint32', (2, 2))
-         153±4μs         49.4±2μs     0.32  bench_shape_base.Block2D.time_block2d((32, 32), 'uint32', (4, 4))
-      15.6±0.6μs       5.03±0.3μs     0.32  bench_core.Core.time_vstack_l
-         154±7μs         49.7±2μs     0.32  bench_shape_base.Block2D.time_block2d((32, 32), 'uint8', (4, 4))
-        59.6±6μs       19.1±0.8μs     0.32  bench_shape_base.Block2D.time_block2d((64, 64), 'uint8', (2, 2))
-      3.03±0.4μs         969±30ns     0.32  bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'int'>)
-        120±10μs         38.4±2μs     0.32  bench_shape_base.Block.time_3d(1, 'block')
-         156±5μs         49.3±1μs     0.32  bench_shape_base.Block2D.time_block2d((16, 16), 'uint64', (4, 4))
-        164±10μs         49.3±2μs     0.30  bench_shape_base.Block2D.time_block2d((32, 32), 'uint16', (4, 4))
-       65.7±10μs       19.6±0.7μs     0.30  bench_shape_base.Block2D.time_block2d((64, 64), 'uint16', (2, 2))
-     2.82±0.08μs         732±30ns     0.26  bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'int'>)
-     2.77±0.07μs         664±30ns     0.24  bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'bool'>)
-      2.61±0.1μs         624±20ns     0.24  bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'bool'>)
-        16.8±3μs       3.97±0.2μs     0.24  bench_core.Core.time_hstack_l
-      2.78±0.1μs         637±20ns     0.23  bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'bool'>)
-      2.36±0.2μs          207±5ns     0.09  bench_overrides.ArrayFunction.time_mock_broadcast_to_numpy
-      2.68±0.1μs          221±7ns     0.08  bench_overrides.ArrayFunction.time_mock_concatenate_numpy
-      2.58±0.1μs         201±10ns     0.08  bench_overrides.ArrayFunction.time_mock_broadcast_to_duck
-      3.02±0.2μs          222±6ns     0.07  bench_overrides.ArrayFunction.time_mock_concatenate_duck
-      4.29±0.3μs          216±6ns     0.05  bench_overrides.ArrayFunction.time_mock_concatenate_mixed
-        142±20μs          213±8ns     0.00  bench_overrides.ArrayFunction.time_mock_concatenate_many

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.

см. также https://docs.google.com/spreadsheets/d/15-AFI_cmZqfkU6mo2p1znsQF2E52PEXpF68QqYqEar4/edit#gid = 0 для электронной таблицы.

Неудивительно, что наибольшая разница в производительности наблюдается для функций, которые вызывают другие функции numpy внутри много раз, например, для np.block() .

@shoyer - меня немного нужна реализация на C, но тем временем я сделал PR с некоторыми небольшими изменениями, которые сэкономили время для общего случая только одного type, а для случая, когда единственный тип - ndarray . См. № 12321.

@shoyer - Я поднял два вопроса в списке рассылки, которые, вероятно, тоже стоит изложить здесь:

  1. Должны ли все уникальные типы аргументов, подобных массиву, быть включены в types ? (а не только те аргументы, которые обеспечивают переопределение.) Было бы полезно знать об реализациях. (см. №12327).
  2. Должна ли реализация ndarray.__array_function__ принимать подклассы, даже если они переопределяют __array_function__ ? Это было бы разумно, учитывая принцип замены Лискова и учитывая, что у подкласса уже был шанс выйти под залог. Это будет означать вызов реализации, а не публичной функции внутри ndarray.__array_function__ . (И нечто подобное в __array_ufunc__ ...) См. # 12328 для ознакомления только с __array_function__ .

@shoyer - см. # 12327 для быстрой реализации (1) - если мы действительно пойдем по этому пути, я думаю, нам также следует скорректировать нэп.

И # 12328 для пробной версии (2), в основном, чтобы посмотреть, как это выглядит.

Я здесь +1 к обеим модификациям.

Название диспетчерских функций в сообщениях об ошибках снова появилось в https://github.com/numpy/numpy/pull/12789 , где кто-то был удивлен, увидев TypeError: _pad_dispatcher missing 1 required positional argument

В дополнение к альтернативам, описанным выше https://github.com/numpy/numpy/issues/12028#issuecomment -429377396 (сейчас мы используем 2), я добавлю четвертый вариант:

Вариант 4 : Напишите отдельный диспетчер для каждой функции с тем же именем, что и функция:

def sin(a):
    return (a,)


@array_function_dispatch(sin)
def sin(a):
     ...


def cos(a):
    return (a,)


@array_function_dispatch(cos)
def cos(a):
    ...

Преимущества:

  • Python теперь всегда предоставляет правильные имена функций в сообщениях об ошибках.

Недостатки:

  • Больше повторений кода
  • Больше косвенности - больше неясно, какое имя функции pad получило неправильные аргументы (но у нас есть тесты, чтобы убедиться, что они синхронизированы).

Я думаю, что для того, чтобы текущий код работал, фактическая функция должна появиться _после_ диспетчера.

Верно, но мы можем дать ему то же имя, что и диспетчер. Имя диспетчера будет перезаписано.

Было бы здорово иметь возможность определять настраиваемую отправку для таких функций, как np.arange или np.empty.

Я предполагаю, что одним из вариантов было бы, чтобы NumPy отправлял скаляры, а также массивы. Это несовместимо с нэпом? Что-нибудь сломается с этим изменением?

Для обсуждения np.arange см. Https://github.com/numpy/numpy/issues/12379.

Я не понимаю, как np.empty() может выполнять диспетчеризацию - здесь не на что отправлять, только форма и dtype. Но, безусловно, np.empty_like() может выполнять отправку с перезаписанной формой - это именно то, что поддерживает https://github.com/numpy/numpy/pull/13046 .

Вариант 4 : Напишите отдельный диспетчер для каждой функции с тем же именем, что и функция:

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

Я не понимаю, как np.empty () может выполнять диспетчеризацию - здесь не на что отправлять, только фигура и dtype

Возможно, вы захотите отправить любой из них. Например, вот объект нестандартной формы, который мы можем отправить по-другому.

Screen Shot 2019-04-03 at 1 06 46 PM

Этот пример не очень полезен, но идея состоит в том, что у меня есть ленивый объект, который ведет себя как shape, но не возвращает целые числа, он возвращает выражения. Например, было бы неплохо сделать что-то вроде этого:

class ExprShape:
    def __getitem__(self, i):
        return ('getitem', self, i)
    def __len__(self):
        return ('len', self)

numpy.empty(ExprShape())

Что бы я хотел переопределить, чтобы вернуть что-то вроде ExprArray('empty', ExprShape()) .

Да, в принципе, по форме тоже можно отправить. Это добавило бы протоколу дополнительной сложности / накладных расходов. Есть ли у вас варианты использования, когда использование массива в качестве шаблона (например, empty_like с shape ) будет недостаточно?

В других случаях я могу думать это size аргумент np.random.RandomState методы, но обратите внимание , что в настоящее время мы не поддерживаем тех , кто вообще - см http://www.numpy.org/ neps / nep-0018-array-function-protocol.html # callable -objects-generated-at-runtime

Есть ли у вас варианты использования, когда использование массива в качестве шаблона (например, empty_like с shape) недостаточно?

Если мы берем существующий API, который зависит от NumPy, и хотели бы, чтобы он прозрачно работал на другом сервере, без изменения существующего исходного кода.

Например, предположим, что мы пытались вызвать scipy.optimize.differential_evolution с NP-подобными массивами, которые создают граф вызовов вместо немедленного выполнения.

Вы можете увидеть здесь , было бы полезно , если бы мы могли изменить np.full , чтобы создать символическую массив вместо массива в Numpy по умолчанию, если вход перешел в это тоже символично.

Если мы берем существующий API, который зависит от NumPy, и хотели бы, чтобы он прозрачно работал на другом сервере, без изменения существующего исходного кода.

В общем, это невозможно. Явную конструкцию массива, такую ​​как np.array() , определенно нужно будет переписать, чтобы она была совместима с утиной типизацией.

В этом случае переключение energies = np.full(num_members, np.inf) на energies = np.full_like(population, np.inf, shape=num_members) кажется простым и понятным изменением.

В общем, это невозможно. Явное построение массива, такое как np.array (), определенно нужно будет переписать, чтобы оно было совместимо с утиной типизацией.

Есть ли предложение относительно внесения такого рода изменений или вы говорите, что поддержка отправки np.array будет действительно сложной задачей, и поэтому мы никогда не сможем получить 100% поддержку?

В этом случае переключение энергий = np.full (num_members, np.inf) на energy = np.full_like (Population, np.inf, shape = num_members) кажется простым и читаемым изменением.

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

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

  • Предоставление нового модуля, который действует как numpy, но ведет себя так, как вы хотите. Требует, чтобы пользователи изменили свой импорт
  • Осмотрите источник, чтобы понять поведение. ала-нумба или касательная.

Оба эти варианта могут потребоваться в определенных случаях (например, разрешить пользователям вызывать np.full и в настоящее время возвращать символический результат), но если я правильно понимаю, цель NEP-18 - попытаться ограничить, когда они необходимы и позволить людям использовать оригинальный NumPy в большем количестве случаев.

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

Есть ли предложение относительно внесения такого рода изменений или вы говорите, что поддержка отправки np.array будет действительно сложной задачей, и поэтому мы никогда не сможем получить 100% поддержку?

В NEP 22 варианты обсуждаются здесь. Я не думаю, что мы можем безопасно изменить семантику np.asarray() чтобы возвращать что-либо, кроме объекта numpy.ndarray - для этого нам понадобится новый протокол.

Проблема в том, что np.asarray() в настоящее время является идиоматическим способом преобразования в объект массива numpy, который использует can и ожидает точного соответствия numpy.ndarray , например, вплоть до макета памяти.

Безусловно, существует множество вариантов использования, в которых это не так, но переключение этого поведения приведет к поломке большого количества нисходящего кода, так что это не начало. Проекты, находящиеся ниже по течению, должны будут согласиться хотя бы на этот аспект набора утиных массивов.

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

Да. NEP 18 не предназначен для того, чтобы быть полным решением для альтернативных вариантов NumPy, но это шаг в этом направлении.

Я подготовил пересмотр NEP-18 для добавления атрибута __numpy_implementation__ :
https://github.com/numpy/numpy/pull/13305

Мне приходит в голову, что мы забываем деформировать функции в numpy.testing : https://github.com/numpy/numpy/issues/13588

Я сделаю это в ближайшее время ...

Есть одна версия, которую я хотел бы внести в NEP, в частности, чтобы прояснить, какие гарантии NEP-18 предлагает авторам подклассов: https://github.com/numpy/numpy/pull/13633

Я отметил, что задачи по удобству использования выполнены, так как gh-13329 был исправлен. Мы решили, что # 13588 может подождать до выпуска 1.17. Это оставляет улучшения документации и arange gh-12379 по-прежнему открытыми для включения в 1.17.

Там же # 13728 - ошибка в диспетчере histogram[2d]d

Это оставляет улучшения документации и arange gh-12379 для включения в 1.17.

Проблема с документацией отсутствовала, поэтому я открыл gh-13844. Я думаю, что документы намного важнее, чем открытый вопрос arange .

@shoyer мы можем закрыть это?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги