Spec-Zone.ru › NumPy 1.16

API итератора массива

Новая версия 1.6.

Итератор массива

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

На этой странице документирован API итератора. Итератор называется NpyIter, а функции называются NpyIter_*.

Существует руководство по итерации массивов, которое может быть полезно для тех, кто использует этот C API. Во многих случаях, проверка идей, создавая итератор в Python, является хорошей идеей перед написанием кода итерации на C.

Пример простой итерации

Лучший способ ознакомиться с итератором – посмотреть его использование в собственном коде NumPy. Например, вот немного изменённая версия кода для PyArray_CountNonzero, которая подсчитывает количество ненулевых элементов в массиве.

npy_intp PyArray_CountNonzero(PyArrayObject* self)
{
    /* Nonzero boolean function */
    PyArray_NonzeroFunc* nonzero = PyArray_DESCR(self)->f->nonzero;

    NpyIter* iter;
    NpyIter_IterNextFunc *iternext;
    char** dataptr;
    npy_intp nonzero_count;
    npy_intp* strideptr,* innersizeptr;

    /* Handle zero-sized arrays specially */
    if (PyArray_SIZE(self) == 0) {
        return 0;
    }

    /*
     * Create and use an iterator to count the nonzeros.
     *   flag NPY_ITER_READONLY
     *     - The array is never written to.
     *   flag NPY_ITER_EXTERNAL_LOOP
     *     - Inner loop is done outside the iterator for efficiency.
     *   flag NPY_ITER_NPY_ITER_REFS_OK
     *     - Reference types are acceptable.
     *   order NPY_KEEPORDER
     *     - Visit elements in memory order, regardless of strides.
     *       This is good for performance when the specific order
     *       elements are visited is unimportant.
     *   casting NPY_NO_CASTING
     *     - No casting is required for this operation.
     */
    iter = NpyIter_New(self, NPY_ITER_READONLY|
                             NPY_ITER_EXTERNAL_LOOP|
                             NPY_ITER_REFS_OK,
                        NPY_KEEPORDER, NPY_NO_CASTING,
                        NULL);
    if (iter == NULL) {
        return -1;
    }

    /*
     * The iternext function gets stored in a local variable
     * so it can be called repeatedly in an efficient manner.
     */
    iternext = NpyIter_GetIterNext(iter, NULL);
    if (iternext == NULL) {
        NpyIter_Deallocate(iter);
        return -1;
    }
    /* The location of the data pointer which the iterator may update */
    dataptr = NpyIter_GetDataPtrArray(iter);
    /* The location of the stride which the iterator may update */
    strideptr = NpyIter_GetInnerStrideArray(iter);
    /* The location of the inner loop size which the iterator may update */
    innersizeptr = NpyIter_GetInnerLoopSizePtr(iter);

    nonzero_count = 0;
    do {
        /* Get the inner loop data/stride/count values */
        char* data = *dataptr;
        npy_intp stride = *strideptr;
        npy_intp count = *innersizeptr;

        /* This is a typical inner loop for NPY_ITER_EXTERNAL_LOOP */
        while (count--) {
            if (nonzero(data, self)) {
                ++nonzero_count;
            }
            data += stride;
        }

        /* Increment the iterator to the next inner loop */
    } while(iternext(iter));

    NpyIter_Deallocate(iter);

    return nonzero_count;
}

Пример простой многократной итерации

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

PyObject *CopyArray(PyObject *arr, NPY_ORDER order)
{
    NpyIter *iter;
    NpyIter_IterNextFunc *iternext;
    PyObject *op[2], *ret;
    npy_uint32 flags;
    npy_uint32 op_flags[2];
    npy_intp itemsize, *innersizeptr, innerstride;
    char **dataptrarray;

    /*
     * No inner iteration - inner loop is handled by CopyArray code
     */
    flags = NPY_ITER_EXTERNAL_LOOP;
    /*
     * Tell the constructor to automatically allocate the output.
     * The data type of the output will match that of the input.
     */
    op[0] = arr;
    op[1] = NULL;
    op_flags[0] = NPY_ITER_READONLY;
    op_flags[1] = NPY_ITER_WRITEONLY | NPY_ITER_ALLOCATE;

    /* Construct the iterator */
    iter = NpyIter_MultiNew(2, op, flags, order, NPY_NO_CASTING,
                            op_flags, NULL);
    if (iter == NULL) {
        return NULL;
    }

    /*
     * Make a copy of the iternext function pointer and
     * a few other variables the inner loop needs.
     */
    iternext = NpyIter_GetIterNext(iter, NULL);
    innerstride = NpyIter_GetInnerStrideArray(iter)[0];
    itemsize = NpyIter_GetDescrArray(iter)[0]->elsize;
    /*
     * The inner loop size and data pointers may change during the
     * loop, so just cache the addresses.
     */
    innersizeptr = NpyIter_GetInnerLoopSizePtr(iter);
    dataptrarray = NpyIter_GetDataPtrArray(iter);

    /*
     * Note that because the iterator allocated the output,
     * it matches the iteration order and is packed tightly,
     * so we don't need to check it like the input.
     */
    if (innerstride == itemsize) {
        do {
            memcpy(dataptrarray[1], dataptrarray[0],
                                    itemsize * (*innersizeptr));
        } while (iternext(iter));
    } else {
        /* For efficiency, should specialize this based on item size... */
        npy_intp i;
        do {
            npy_intp size = *innersizeptr;
            char *src = dataptrarray[0], *dst = dataptrarray[1];
            for(i = 0; i < size; i++, src += innerstride, dst += itemsize) {
                memcpy(dst, src, itemsize);
            }
        } while (iternext(iter));
    }

    /* Get the result from the iterator object array */
    ret = NpyIter_GetOperandArray(iter)[1];
    Py_INCREF(ret);

    if (NpyIter_Deallocate(iter) != NPY_SUCCEED) {
        Py_DECREF(ret);
        return NULL;
    }

    return ret;
}

Типы данных итератора

Структура итератора – это внутренняя деталь, и код пользователя видит только неполную структуру.

NpyIter

Это тип указателя для итератора. Доступ к его содержимому возможен только через API итератора.

NpyIter_Type

Это тип, который предоставляет итератор Python. В настоящее время нет API, предоставляющего доступ к значениям итератора, созданного в Python. Если итератор создаётся в Python, он должен использоваться в Python и наоборот. Такой API, вероятно, будет создан в будущей версии.

NpyIter_IterNextFunc

Это указатель на функцию для цикла итерации, возвращаемый NpyIter_GetIterNext.

NpyIter_GetMultiIndexFunc

Это указатель на функцию для получения текущего многомерного индекса итератора, возвращаемый NpyIter_GetGetMultiIndex.

Создание и уничтожение

NpyIter* NpyIter_New(PyArrayObject* op, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, PyArray_Descr* dtype)

Создаёт итератор для заданного объекта массива NumPy op.

Флаги, которые могут быть переданы в flags – это любая комбинация глобальных и операторных флагов, задокументированных в NpyIter_MultiNew, за исключением NPY_ITER_ALLOCATE.

Любое из значений перечисления NPY_ORDER может быть передано в order. Для эффективной итерации наилучшим вариантом является NPY_KEEPORDER, а другие порядки навязывают определённый порядок итерации.

Любое из значений перечисления NPY_CASTING может быть передано в casting. Значения включают NPY_NO_CASTING, NPY_EQUIV_CASTING, NPY_SAFE_CASTING, NPY_SAME_KIND_CASTING и NPY_UNSAFE_CASTING. Чтобы разрешить преобразования, необходимо также включить копирование или буферизацию.

Если dtype не NULL, то требуется этот тип данных. Если разрешено копирование, при необходимости преобразования данных будет создана временная копия. Если включен NPY_ITER_UPDATEIFCOPY, при уничтожении итератора данные также будут скопированы обратно с другим преобразованием.

Возвращает NULL, если произошла ошибка, в противном случае возвращает выделенный итератор.

Для создания итератора, аналогичного старому итератору, это должно сработать.

iter = NpyIter_New(op, NPY_ITER_READWRITE,
                    NPY_CORDER, NPY_NO_CASTING, NULL);

Если вы хотите изменить массив с выровненным double кодом, но порядок не имеет значения, вы бы использовали это.

dtype = PyArray_DescrFromType(NPY_DOUBLE);
iter = NpyIter_New(op, NPY_ITER_READWRITE|
                    NPY_ITER_BUFFERED|
                    NPY_ITER_NBO|
                    NPY_ITER_ALIGNED,
                    NPY_KEEPORDER,
                    NPY_SAME_KIND_CASTING,
                    dtype);
Py_DECREF(dtype);
NpyIter* NpyIter_MultiNew(npy_intp nop, PyArrayObject** op, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, npy_uint32* op_flags, PyArray_Descr** op_dtypes)

Создает итератор для трансляции массивов nop объектов, предоставленных в op, используя стандартные правила трансляции NumPy.

Любое из значений перечисления NPY_ORDER может быть передано в order. Для эффективной итерации наилучшим вариантом является NPY_KEEPORDER, а другие порядки навязывают определённую схему итерации. При использовании NPY_KEEPORDER, если необходимо гарантировать, что итерация не инвертируется вдоль оси, следует передать флаг NPY_ITER_DONT_NEGATE_STRIDES.

Любое из значений перечисления NPY_CASTING может быть передано в casting. Значения включают NPY_NO_CASTING, NPY_EQUIV_CASTING, NPY_SAFE_CASTING, NPY_SAME_KIND_CASTING и NPY_UNSAFE_CASTING. Для выполнения преобразований также необходимо включить копирование или буферизацию.

Если op_dtypes не NULL, он определяет тип данных или NULL для каждого op[i].

Возвращает NULL, если произошла ошибка, в противном случае возвращает выделенный итератор.

Флаги, которые могут быть переданы в flags, применяются ко всему итератору:

NPY_ITER_C_INDEX

Принуждает итератор отслеживать сглаженный плоский индекс, соответствующий порядку C. Этот параметр не может быть использован с NPY_ITER_F_INDEX.

NPY_ITER_F_INDEX

Принуждает итератор отслеживать сглаженный плоский индекс, соответствующий порядку Fortran. Этот параметр не может быть использован с NPY_ITER_C_INDEX.

NPY_ITER_MULTI_INDEX

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

NPY_ITER_EXTERNAL_LOOP

Принуждает итератор пропустить итерацию внутреннего цикла, потребовав от пользователя управления им.

Этот флаг несовместим с NPY_ITER_C_INDEX, NPY_ITER_F_INDEX и NPY_ITER_MULTI_INDEX.

NPY_ITER_DONT_NEGATE_STRIDES

Это влияет только на итератор, когда для параметра order задано NPY_KEEPORDER. По умолчанию с NPY_KEEPORDER итератор инвертирует оси с отрицательными шагами, чтобы память проходилась в прямом направлении. Этот параметр отключает этот этап. Используйте этот флаг, если вы хотите использовать внутренний порядок памяти осей, но не хотите инвертировать ось. Такое поведение, например, имеет numpy.ravel(a, order='K').

NPY_ITER_COMMON_DTYPE

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

Если общий тип данных известен заранее, не используйте этот флаг. Вместо этого задайте требуемый тип данных для всех операндов.

NPY_ITER_REFS_OK

Указывает, что массивы со ссылками (массивы объектов или структурированные массивы, содержащие тип объекта) могут быть приняты и использованы в итераторе. Если этот флаг включен, вызывающая сторона должна убедиться, что NpyIter_IterationNeedsAPI(iter) истинно, в противном случае она может не освободить GIL во время итерации.

NPY_ITER_ZEROSIZE_OK

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

NPY_ITER_REDUCE_OK

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

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

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

NPY_ITER_RANGED

Включает поддержку итерации по поддиапазонам всего iterindex диапазона [0, NpyIter_IterSize(iter)). Используйте функцию NpyIter_ResetToIterIndexRange для задания диапазона итерации.

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

NPY_ITER_BUFFERED

Принуждает итератор хранить данные буферизации и использовать буферизацию для удовлетворения требований к типу данных, выравниванию и порядку байтов. Чтобы буферизовать операнд, не указывайте флаги NPY_ITER_COPY или NPY_ITER_UPDATEIFCOPY, потому что они переопределят буферизацию. Буферизация особенно полезна для кода Python, использующего итератор, позволяя работать с большими объемами данных сразу, чтобы амортизировать накладные расходы интерпретатора Python.

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

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

В стандартной буферизации размер каждого внутреннего цикла равен размеру буфера или, возможно, больше, если указан NPY_ITER_GROWINNER. Если включён NPY_ITER_REDUCE_OK и выполняется сокращение, внутренние циклы могут стать меньше в зависимости от структуры сокращения.

NPY_ITER_GROWINNER

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

NPY_ITER_DELAY_BUFALLOC

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

Другое применение этого флага - для настройки операций сокращения. После создания итератора и автоматического выделения выходных данных сокращения итератором (не забудьте указать доступ READWRITE), его значение можно инициализировать значением сокращения. Используйте NpyIter_GetOperandArray для получения объекта. Затем вызовите NpyIter_Reset для выделения и заполнения буферов их начальными значениями.

NPY_ITER_COPY_IF_OVERLAP

Если какой-либо записываемый операнд перекрывает любой считываемый операнд, устраните всё перекрытие путём создания временных копий (при необходимости включите UPDATEIFCOPY для записываемых операндов). Пара операндов перекрывается, если есть адрес памяти, содержащий данные, общие для обоих массивов.

Поскольку точная проверка перекрытия имеет экспоненциальное время выполнения относительно количества измерений, решение принимается на основе эвристики, которая имеет ложноположительные результаты (необязательные копии в необычных случаях), но не имеет ложноотрицательных.

Если существует какое-либо перекрытие чтения/записи, этот флаг гарантирует, что результат операции будет таким же, как если бы все операнды были скопированы. В тех случаях, когда нужно создавать копии, результат вычисления может быть неопределённым без этого флага!

Флаги, которые могут быть переданы в op_flags[i], где 0 <= i < nop:

NPY_ITER_READWRITE
NPY_ITER_READONLY
NPY_ITER_WRITEONLY

Указывает, как пользователь итератора будет читать или записывать в op[i]. Для каждого операнда должен быть указан ровно один из этих флагов. Использование NPY_ITER_READWRITE или NPY_ITER_WRITEONLY для операнда, предоставленного пользователем, может вызвать семантику WRITEBACKIFCOPY`. Данные будут перезаписаны в исходный массив при вызове NpyIter_Deallocate.

NPY_ITER_COPY

Разрешает создание копии op[i], если она не соответствует требованиям типа данных или выравнивания, указанным флагами и параметрами конструктора.

NPY_ITER_UPDATEIFCOPY

Вызывает NPY_ITER_COPY, и когда операнд массива помечен для записи и скопирован, вызывает копирование данных из копии обратно в op[i] при вызове NpyIter_Deallocate.

Если операнд помечен как только для записи и требуется копия, будет создан временный массив без инициализации, а затем скопирован обратно в op[i] при вызове NpyIter_Deallocate, вместо выполнения ненужной операции копирования.

NPY_ITER_NBO
NPY_ITER_ALIGNED
NPY_ITER_CONTIG

Заставляет итератор предоставлять данные для op[i] в родном порядке байтов, выровненные в соответствии с требованиями dtype, непрерывные или любая комбинация.

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

Ограничение непрерывности относится только к внутреннему циклу; последующие внутренние циклы могут иметь произвольные изменения указателей.

Если запрашиваемый тип данных имеет неродной порядок байтов, флаг NBO переопределяет его, и запрашиваемый тип данных преобразуется в родной порядок байтов.

NPY_ITER_ALLOCATE

Это для выходных массивов и требует, чтобы был установлен флаг NPY_ITER_WRITEONLY или NPY_ITER_READWRITE. Если op[i] равно NULL, создаёт новый массив с конечными размерами широковещательной передачи и структурой расположения, соответствующей порядку итерации итератора.

Когда op[i] равно NULL, запрашиваемый тип данных op_dtypes[i] также может быть NULL, в этом случае он автоматически генерируется из типов данных массивов, помеченных как читаемые. Правила генерации типа данных такие же, как и для UFuncs. Важно отметить обработку порядка байтов в выбранном dtype. Если имеется ровно один вход, используется dtype входа как есть. В противном случае, если несколько входных dtypes комбинируются вместе, вывод будет в родном порядке байтов.

После выделения с этим флагом вызывающая сторона может получить новый массив, вызвав NpyIter_GetOperandArray и получив i-й объект в возвращённом массиве C. Вызывающая сторона должна вызвать Py_INCREF на нём, чтобы получить ссылку на массив.

NPY_ITER_NO_SUBTYPE

Для использования с NPY_ITER_ALLOCATE этот флаг отключает выделение подтипа массива для вывода, принуждая его к прямому ndarray.

TODO: Может быть, лучше было бы ввести функцию NpyIter_GetWrappedOutput и удалить этот флаг?

NPY_ITER_NO_BROADCAST

Обеспечивает, что вход или выход точно соответствуют размерам итерации.

NPY_ITER_ARRAYMASK

Добавлен в версии 1.7.

Указывает, что этот операнд — маска, используемая для выбора элементов при записи в операнды, которым применён флаг NPY_ITER_WRITEMASKED. Только один операнд может иметь применённым флаг NPY_ITER_ARRAYMASK.

Тип данных операнда с этим флагом должен быть либо NPY_BOOL, NPY_MASK, либо структурированным типом данных, все поля которого являются допустимыми типами данных маски. В последнем случае он должен соответствовать структурированному операнду, имеющему WRITEMASKED, так как он указывает маску для каждого поля этого массива.

Этот флаг влияет только на запись из буфера обратно в массив. Это означает, что если операнд также является NPY_ITER_READWRITE или NPY_ITER_WRITEONLY, код, выполняющий итерацию, может записывать в этот операнд, чтобы управлять тем, какие элементы будут нетронутыми, а какие — изменёнными. Это полезно, когда маска должна быть комбинацией входных масок, например. Значения масок можно создавать с помощью функции NpyMask_Create.

NPY_ITER_WRITEMASKED

Добавлен в версии 1.7.

Указывает, что только элементы, которые операнд с флагом ARRAYMASK указывает, предназначены для изменения итерацией. В общем случае итератор не навязывает это; код, выполняющий итерацию, должен следовать этому обещанию. Код может использовать встроенную функцию NpyMask_IsExposed для проверки того, позволяет ли маска на конкретном элементе запись.

Когда используется этот флаг, и этот операнд буферизован, это изменяет способ копирования данных из буфера в массив. Используется процедура копирования с маской, которая копирует только элементы в буфере, для которых NpyMask_IsExposed возвращает true для соответствующего элемента в операнде ARRAYMASK.

NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE

При проверке перекрытия в памяти предполагается, что операнды с включённым флагом NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE обращаются только в порядке итерации.

Это позволяет итератору рассуждать о зависимости данных, возможно, избегая ненужных копий.

Этот флаг действует только при включённом флага NPY_ITER_COPY_IF_OVERLAP на итераторе.

NpyIter* NpyIter_AdvancedNew(npy_intp nop, PyArrayObject** op, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, npy_uint32* op_flags, PyArray_Descr** op_dtypes, int oa_ndim, int** op_axes, npy_intp* itershape, npy_intp buffersize)

Расширяет NpyIter_MultiNew несколькими расширенными параметрами, предоставляющими больший контроль над широковещательной передачей и буферизацией.

Если значения -1/NULL передаются oa_ndim, op_axes, itershape, и buffersize, это эквивалентно NpyIter_MultiNew.

Параметр oa_ndim, когда не равен нулю или -1, задаёт количество измерений, которые будут итерироваться с настроенной широковещательной передачей. Если он предоставлен, op_axes должен быть предоставлен, а itershape также может быть предоставлен. Параметр op_axes позволяет детально управлять тем, как оси массивов операндов совмещаются и итерируются. В op_axes, необходимо предоставить массив указателей nop на массивы размером oa_ndim типа npy_intp. Если элемент в op_axes равен NULL, будут применяться обычные правила широковещательной передачи. В op_axes[j][i] хранится либо действительная ось массива op[j], либо -1, что означает newaxis. В каждом массиве op_axes[j] оси не могут повторяться. Следующий пример демонстрирует, как применяются обычные правила широковещательной передачи к 3-мерному массиву, 2-мерному массиву, 1-мерному массиву и скаляру.

Примечание: До NumPy 1.8 oa_ndim == 0` was used for signalling that that ``op_axes и itershape не используются. Это устарело и должно быть заменено на -1. Более совместимая обратная совместимость может быть достигнута с помощью NpyIter_MultiNew в этом случае.

int oa_ndim = 3;               /* # iteration axes */
int op0_axes[] = {0, 1, 2};    /* 3-D operand */
int op1_axes[] = {-1, 0, 1};   /* 2-D operand */
int op2_axes[] = {-1, -1, 0};  /* 1-D operand */
int op3_axes[] = {-1, -1, -1}  /* 0-D (scalar) operand */
int* op_axes[] = {op0_axes, op1_axes, op2_axes, op3_axes};

Параметр itershape позволяет принудительно задать определённую форму итерации итератора. Это массив длиной oa_ndim. Когда элемент равен отрицательному значению, его значение определяется по операндам. Этот параметр позволяет автоматически выделенным выходным данным получить дополнительные размеры, которые не соответствуют ни одному размеру входных данных.

Если buffersize равно нулю, используется размер буфера по умолчанию, в противном случае он задаёт размер буфера.

Возвращает NULL, если произошла ошибка, в противном случае возвращает выделенный итератор.

NpyIter* NpyIter_Copy(NpyIter* iter)

Создаёт копию данного итератора. Эта функция в основном предназначена для возможности многопоточной итерации данных.

TODO: Переместить это в раздел о многопоточной итерации.

Рекомендуемый подход к многопоточной итерации заключается в создании итератора с флагами NPY_ITER_EXTERNAL_LOOP, NPY_ITER_RANGED, NPY_ITER_BUFFERED, NPY_ITER_DELAY_BUFALLOC, и, возможно, NPY_ITER_GROWINNER. Создайте копию этого итератора для каждого потока (за исключением первого итератора). Затем возьмите диапазон индексов итерации [0, NpyIter_GetIterSize(iter)) и разделите его на задачи, например, используя цикл TBB parallel_for. Когда поток получает задачу для выполнения, он использует свою копию итератора, вызывая NpyIter_ResetToIterIndexRange и перебирая весь диапазон.

При использовании итератора в многопоточном коде или в коде, не удерживающем блокировку Python GIL, необходимо быть внимательными, чтобы вызывать только функции, безопасные в этом контексте. NpyIter_Copy нельзя безопасно вызывать без Python GIL, потому что он увеличивает ссылки Python. Функции Reset* и некоторые другие функции могут быть безопасно вызваны, передав параметр errmsg как не-NULL, чтобы функции передавали ошибки через него вместо установки исключения Python.

NpyIter_Deallocate необходимо вызвать для каждой копии.

int NpyIter_RemoveAxis(NpyIter* iter, int axis)``

Удаляет ось из итерации. Это требует, чтобы NPY_ITER_MULTI_INDEX был установлен для создания итератора, и не работает, если буферизация включена или отслеживается индекс. Эта функция также сбрасывает итератор в исходное состояние.

Это полезно для настройки цикла накопления, например. Итератор сначала можно создать со всеми измерениями, включая ось накопления, чтобы выходной результат создавался правильно. Затем ось накопления можно удалить, и вычисление выполнить вложенным образом.

ВНИМАНИЕ: Эта функция может изменить внутреннюю структуру памяти итератора. Все кэшированные функции или указатели итератора должны быть получены заново! Диапазон итератора также будет сброшен.

Возвращает NPY_SUCCEED или NPY_FAIL.

int NpyIter_RemoveMultiIndex(NpyIter* iter)

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

ВНИМАНИЕ: Эта функция может изменить внутреннюю структуру памяти итератора. Все кэшированные функции или указатели итератора должны быть получены заново!

После вызова этой функции NpyIter_HasMultiIndex(iter) вернёт false.

Возвращает NPY_SUCCEED или NPY_FAIL.

int NpyIter_EnableExternalLoop(NpyIter* iter)

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

ВНИМАНИЕ: Эта функция изменяет внутреннюю логику итератора. Все кэшированные функции или указатели итератора должны быть получены заново!

Возвращает NPY_SUCCEED или NPY_FAIL.

int NpyIter_Deallocate(NpyIter* iter)

Освобождает объект итератора и обрабатывает все необходимые записи обратно.

Возвращает NPY_SUCCEED или NPY_FAIL.

int NpyIter_Reset(NpyIter* iter, char** errmsg)

Сбрасывает итератор в исходное состояние, в начало диапазона итерации.

Возвращает NPY_SUCCEED или NPY_FAIL. Если errmsg не NULL, при возвращении NPY_FAIL не устанавливается исключение Python. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не NULL, функция может быть безопасно вызвана без удерживания Python GIL.

int NpyIter_ResetToIterIndexRange(NpyIter* iter, npy_intp istart, npy_intp iend, char** errmsg)

Сбрасывает итератор и ограничивает его диапазоном iterindex [istart, iend). См. NpyIter_Copy для объяснения использования этого для многопоточной итерации. Это требует, чтобы флаг NPY_ITER_RANGED был передан при создании итератора.

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

/* Set to a trivial empty range */
NpyIter_ResetToIterIndexRange(iter, 0, 0);
/* Set the base pointers */
NpyIter_ResetBasePointers(iter, baseptrs);
/* Set to the desired range */
NpyIter_ResetToIterIndexRange(iter, istart, iend);

Возвращает NPY_SUCCEED или NPY_FAIL. Если errmsg не NULL, при возвращении NPY_FAIL не устанавливается исключение Python. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не NULL, функция может быть безопасно вызвана без удерживания Python GIL.

int NpyIter_ResetBasePointers(NpyIter *iter, char** baseptrs, char** errmsg)

Сбрасывает итератор в исходное состояние, но используя значения в baseptrs для данных вместо указателей из массивов, по которым происходит итерация. Эта функция предназначена для использования вместе с параметром op_axes в коде вложенной итерации с двумя или более итераторами.

Возвращает NPY_SUCCEED или NPY_FAIL. Если errmsg не NULL, при возвращении NPY_FAIL не устанавливается исключение Python. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не NULL, функция может быть безопасно вызвана без удерживания Python GIL.

TODO: Переместить следующее в специальный раздел о вложенных итераторах.

Создание итераторов для вложенной итерации требует внимательности. Все операнды итератора должны точно совпадать, иначе вызовы NpyIter_ResetBasePointers будут недействительными. Это означает, что автоматические копии и выделение выходных данных не должны использоваться бездумно. Можно по-прежнему использовать функции автоматического преобразования данных и преобразования итератора, создав один из итераторов со всеми параметрами преобразования, затем взяв выделенные операнды с помощью функции NpyIter_GetOperandArray и передав их в конструкторы для остальных итераторов.

ВНИМАНИЕ: При создании итераторов для вложенной итерации код не должен использовать измерение более одного раза в различных итераторах. Если это сделано, вложенная итерация приведет к указателям за пределами границ при итерации.

ВНИМАНИЕ: При создании итераторов для вложенной итерации буферизация может быть применена только к самому внутреннему итератору. Если буферизованный итератор используется в качестве источника для baseptrs, он будет указывать в небольшой буфер вместо массива, и внутренняя итерация будет недействительной.

Шаблон использования вложенных итераторов следующий:

NpyIter *iter1, *iter1;
NpyIter_IterNextFunc *iternext1, *iternext2;
char **dataptrs1;

/*
 * With the exact same operands, no copies allowed, and
 * no axis in op_axes used both in iter1 and iter2.
 * Buffering may be enabled for iter2, but not for iter1.
 */
iter1 = ...; iter2 = ...;

iternext1 = NpyIter_GetIterNext(iter1);
iternext2 = NpyIter_GetIterNext(iter2);
dataptrs1 = NpyIter_GetDataPtrArray(iter1);

do {
    NpyIter_ResetBasePointers(iter2, dataptrs1);
    do {
        /* Use the iter2 values */
    } while (iternext2(iter2));
} while (iternext1(iter1));
int NpyIter_GotoMultiIndex(NpyIter* iter, npy_intp* multi_index)

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

Возвращает NPY_SUCCEED или NPY_FAIL.

int NpyIter_GotoIndex(NpyIter* iter, npy_intp index)

Настраивает итератор для указания на указанный index. Если итератор был создан с флагом NPY_ITER_C_INDEX, index — это индекс в порядке C, и если итератор был создан с флагом NPY_ITER_F_INDEX, index — это индекс в порядке Fortran. Возвращает ошибку, если индекс не отслеживается, индекс выходит за пределы диапазона или итерация внутреннего цикла отключена.

Возвращает NPY_SUCCEED или NPY_FAIL.

npy_intp NpyIter_GetIterSize(NpyIter* iter)

Возвращает количество итерируемых элементов. Это произведение всех измерений в форме. Когда отслеживается многоиндекс (и NpyIter_RemoveAxis может быть вызван), размер может быть -1 для обозначения того, что итератор слишком большой. Такой итератор недействителен, но может стать действительным после вызова NpyIter_RemoveAxis. Проверять этот случай необязательно.

npy_intp NpyIter_GetIterIndex(NpyIter* iter)

Получает iterindex итератора, который является индексом, соответствующим порядку итерации итератора.

void NpyIter_GetIterIndexRange(NpyIter* iter, npy_intp* istart, npy_intp* iend)

Получает iterindex поддиапазон, по которому происходит итерация. Если NPY_ITER_RANGED не был указан, это всегда возвращает диапазон [0, NpyIter_IterSize(iter)).

int NpyIter_GotoIterIndex(NpyIter* iter, npy_intp iterindex)

Настраивает итератор для указания на указанный iterindex. IterIndex — это индекс, соответствующий порядку итерации итератора. Возвращает ошибку, если iterindex выходит за пределы диапазона, буферизация включена или итерация внутреннего цикла отключена.

Возвращает NPY_SUCCEED или NPY_FAIL.

npy_bool NpyIter_HasDelayedBufAlloc(NpyIter* iter)

Возвращает 1, если флаг NPY_ITER_DELAY_BUFALLOC был передан при создании итератора и ни один из функций Reset не был вызван, иначе 0.

npy_bool NpyIter_HasExternalLoop(NpyIter* iter)

Возвращает 1, если вызывающей стороне нужно обработать внутренний цикл по одному измерению, или 0, если итератор обрабатывает все циклы. Это управляется флагом конструктора NPY_ITER_EXTERNAL_LOOP или NpyIter_EnableExternalLoop.

npy_bool NpyIter_HasMultiIndex(NpyIter* iter)

Возвращает 1, если итератор был создан с флагом NPY_ITER_MULTI_INDEX, иначе 0.

npy_bool NpyIter_HasIndex(NpyIter* iter)

Возвращает 1, если итератор был создан с флагом NPY_ITER_C_INDEX или NPY_ITER_F_INDEX, иначе 0.

npy_bool NpyIter_RequiresBuffering(NpyIter* iter)

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

npy_bool NpyIter_IsBuffered(NpyIter* iter)

Возвращает 1, если итератор был создан с флагом NPY_ITER_BUFFERED, иначе 0.

npy_bool NpyIter_IsGrowInner(NpyIter* iter)

Возвращает 1, если итератор был создан с флагом NPY_ITER_GROWINNER, иначе 0.

npy_intp NpyIter_GetBufferSize(NpyIter* iter)

Если итератор буферизован, возвращает размер используемого буфера, иначе возвращает 0.

int NpyIter_GetNDim(NpyIter* iter)

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

int NpyIter_GetNOp(NpyIter* iter)

Возвращает количество операндов в итераторе.

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

int NpyIter_GetFirstMaskNAOp(NpyIter* iter)

Добавлено в версии 1.7.

Возвращает индекс первого операнда маски NA в массиве. Это значение равно количеству операндов, переданных в конструктор.

npy_intp* NpyIter_GetAxisStrideArray(NpyIter* iter, int axis)

Получает массив шагов для указанной оси. Требуется, чтобы итератор отслеживал многоиндекс и чтобы буферизация не была включена.

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

Возвращает NULL при ошибке.

int NpyIter_GetShape(NpyIter* iter, npy_intp* outshape)

Возвращает широковещательную форму итератора в outshape. Это может быть вызвано только для итератора, который отслеживает многоиндекс.

Возвращает NPY_SUCCEED или NPY_FAIL.

PyArray_Descr** NpyIter_GetDescrArray(NpyIter* iter)

Возвращает указатель на типы данных Descr объектов, по которым выполняется итерация. Результат указывает на данные в iter, поэтому вызывающая сторона не получает ссылок на Descr.

Этот указатель можно кэшировать перед циклом итерации, вызов iternext не изменит его.

PyObject** NpyIter_GetOperandArray(NpyIter* iter)

Возвращает указатель на операнды PyObjects, по которым выполняется итерация. Результат указывает на данные в iter, поэтому вызывающая сторона не получает ссылок на PyObjects.

npy_int8* NpyIter_GetMaskNAIndexArray(NpyIter* iter)

Добавлено в версии 1.7.

Возвращает указатель на индексы nop, которые сопоставляют операнды построения с флагом NPY_ITER_USE_MASKNA их соответствующим операндам маски NA и наоборот. Для операндов, которые не были помечены флагом NPY_ITER_USE_MASKNA, этот массив содержит отрицательные значения.

PyObject* NpyIter_GetIterView(NpyIter* iter, npy_intp i)

Возвращает ссылку на новый вид ndarray, который представляет собой представление i-го объекта в массиве NpyIter_GetOperandArray, размеры и шаги которого соответствуют внутреннему оптимизированному шаблону итерации. Итерация по этому представлению в порядке C эквивалентна порядку итерации итератора.

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

void NpyIter_GetReadFlags(NpyIter* iter, char* outreadflags)

Заполняет флаги nop. Устанавливает outreadflags[i] в 1, если из op[i] можно читать, и в 0, если нет.

void NpyIter_GetWriteFlags(NpyIter* iter, char* outwriteflags)

Заполняет флаги nop. Устанавливает outwriteflags[i] в 1, если в op[i] можно писать, и в 0, если нет.

int NpyIter_CreateCompatibleStrides(NpyIter* iter, npy_intp itemsize, npy_intp* outstrides)

Создаёт набор шагов, которые совпадают с шагами выходного массива, созданного с использованием флага NPY_ITER_ALLOCATE, где для op_axes было передано значение NULL. Это для данных, упакованных непрерывно, но необязательно в порядке C или Fortran. Это следует использовать вместе с NpyIter_GetShape и NpyIter_GetNDim с флагом NPY_ITER_MULTI_INDEX, переданным в конструктор.

Случай использования этой функции заключается в том, чтобы согласовать форму и структуру итератора и добавить одно или несколько измерений. Например, чтобы сгенерировать вектор на каждый входной элемент для численного градиента, передайте ndim*itemsize для itemsize, а затем добавьте ещё одно измерение в конец с размером ndim и шагом itemsize. Для матрицы Гессе вы делаете то же самое, но добавляете два измерения, или используете симметрию и упаковываете её в 1 измерение с определённым кодированием.

Эта функция может быть вызвана только если итератор отслеживает многоиндекс и если NPY_ITER_DONT_NEGATE_STRIDES использовался для предотвращения итерации оси в обратном порядке.

Если массив создан этим методом, просто добавление «itemsize» для каждой итерации пройдёт по новому массиву, соответствующему итератору.

Возвращает NPY_SUCCEED или NPY_FAIL.

npy_bool NpyIter_IsFirstVisit(NpyIter* iter, int iop)

Добавлено в версии 1.7.

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

Эта функция предназначена для использования только в режиме EXTERNAL_LOOP и даст некоторые неверные ответы, если этот режим не включён.

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

ПРЕДУПРЕЖДЕНИЕ: по соображениям производительности, «iop» не проверяется на границы, не подтверждено, что «iop» на самом деле является операндом редукции, и не подтверждено, что включён режим EXTERNAL_LOOP. Эти проверки являются ответственностью вызывающей стороны и должны выполняться вне любых внутренних циклов.

Функции итерации

NpyIter_IterNextFunc* NpyIter_GetIterNext(NpyIter* iter, char** errmsg)

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

Возвращает NULL, если произошла ошибка. Если errmsg не NULL, при возвращении NPY_FAIL не устанавливается исключение Python. Вместо этого в *errmsg записывается сообщение об ошибке. Когда errmsg не NULL, функцию можно безопасно вызывать без удерживания GIL Python.

Типичная структура цикла итерации:

NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL);
char** dataptr = NpyIter_GetDataPtrArray(iter);

do {
    /* use the addresses dataptr[0], ... dataptr[nop-1] */
} while(iternext(iter));

Когда указан NPY_ITER_EXTERNAL_LOOP, типичная структура внутреннего цикла следующая:

NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL);
char** dataptr = NpyIter_GetDataPtrArray(iter);
npy_intp* stride = NpyIter_GetInnerStrideArray(iter);
npy_intp* size_ptr = NpyIter_GetInnerLoopSizePtr(iter), size;
npy_intp iop, nop = NpyIter_GetNOp(iter);

do {
    size = *size_ptr;
    while (size--) {
        /* use the addresses dataptr[0], ... dataptr[nop-1] */
        for (iop = 0; iop < nop; ++iop) {
            dataptr[iop] += stride[iop];
        }
    }
} while (iternext());

Обратите внимание, что мы используем массив dataptr внутри итератора, а не копируем значения во временную локальную переменную. Это возможно, потому что при вызове iternext(), эти указатели будут перезаписаны новыми значениями, а не инкрементально обновлены.

Если используется буфер с фиксированным размером во время компиляции (оба флага NPY_ITER_BUFFERED и NPY_ITER_EXTERNAL_LOOP), внутренний размер также может использоваться в качестве сигнала. Размер гарантированно станет нулём, когда iternext() возвращает false, что позволяет использовать следующую структуру цикла. Обратите внимание, что если вы используете эту структуру, вы не должны передавать NPY_ITER_GROWINNER как флаг, потому что это приведёт к большему размеру в некоторых случаях.

/* The constructor should have buffersize passed as this value */
#define FIXED_BUFFER_SIZE 1024

NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL);
char **dataptr = NpyIter_GetDataPtrArray(iter);
npy_intp *stride = NpyIter_GetInnerStrideArray(iter);
npy_intp *size_ptr = NpyIter_GetInnerLoopSizePtr(iter), size;
npy_intp i, iop, nop = NpyIter_GetNOp(iter);

/* One loop with a fixed inner size */
size = *size_ptr;
while (size == FIXED_BUFFER_SIZE) {
    /*
     * This loop could be manually unrolled by a factor
     * which divides into FIXED_BUFFER_SIZE
     */
    for (i = 0; i < FIXED_BUFFER_SIZE; ++i) {
        /* use the addresses dataptr[0], ... dataptr[nop-1] */
        for (iop = 0; iop < nop; ++iop) {
            dataptr[iop] += stride[iop];
        }
    }
    iternext();
    size = *size_ptr;
}

/* Finish-up loop with variable inner size */
if (size > 0) do {
    size = *size_ptr;
    while (size--) {
        /* use the addresses dataptr[0], ... dataptr[nop-1] */
        for (iop = 0; iop < nop; ++iop) {
            dataptr[iop] += stride[iop];
        }
    }
} while (iternext());
NpyIter_GetMultiIndexFunc *NpyIter_GetGetMultiIndex(NpyIter* iter, char** errmsg)

Возвращает указатель на функцию для получения текущего многомерного индекса итератора. Возвращает NULL, если итератор не отслеживает многомерный индекс. Рекомендуется кэшировать этот указатель на функцию в локальной переменной перед циклом итерации.

Возвращает NULL, если произошла ошибка. Если errmsg не равен NULL, исключение Python не устанавливается, когда NPY_FAIL возвращает NULL. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без удерживания GIL Python.

char** NpyIter_GetDataPtrArray(NpyIter* iter)

Возвращает указатель на массив указателей на данные. Если был задан NPY_ITER_EXTERNAL_LOOP, каждый указатель на данные указывает на текущий элемент данных итератора. Если внутренняя итерация не была задана, он указывает на первый элемент данных внутреннего цикла.

Этот указатель можно кэшировать перед циклом итерации; вызов iternext не изменит его. Эту функцию можно безопасно вызывать без удерживания GIL Python.

char** NpyIter_GetInitialDataPtrArray(NpyIter* iter)

Получает массив указателей на данные напрямую в массивы (никогда не в буферы), соответствующие индексу итерации 0.

Эти указатели отличаются от указателей, принимаемых NpyIter_ResetBasePointers, так как направление вдоль некоторых осей может быть изменено.

Эту функцию можно безопасно вызывать без удерживания GIL Python.

npy_intp* NpyIter_GetIndexPtr(NpyIter* iter)

Возвращает указатель на отслеживаемый индекс или NULL, если индекс не отслеживается. Он может быть использован только если были заданы флаги NPY_ITER_C_INDEX или NPY_ITER_F_INDEX во время построения.

Когда используется флаг NPY_ITER_EXTERNAL_LOOP, коду необходимо знать параметры для выполнения внутреннего цикла. Эти функции предоставляют эту информацию.

npy_intp* NpyIter_GetInnerStrideArray(NpyIter* iter)

Возвращает указатель на массив nop шагов, по одному для каждого итерируемого объекта, который используется внутренним циклом.

Этот указатель можно кэшировать перед циклом итерации; вызов iternext не изменит его. Эту функцию можно безопасно вызывать без удерживания GIL Python.

ВНИМАНИЕ: Хотя указатель можно кэшировать, его значения могут измениться, если итератор буферизован.

npy_intp* NpyIter_GetInnerLoopSizePtr(NpyIter* iter)

Возвращает указатель на количество итераций, которые должен выполнить внутренний цикл.

Этот адрес можно кэшировать перед циклом итерации; вызов iternext не изменит его. Значение само может измениться во время итерации, особенно если включена буферизация. Эту функцию можно безопасно вызывать без удерживания GIL Python.

void NpyIter_GetInnerFixedStrideArray(NpyIter* iter, npy_intp* out_strides)

Получает массив шагов, которые фиксированы или не изменятся в течение всей итерации. Для шагов, которые могут измениться, значение NPY_MAX_INTP помещается в шаг.

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

Эту функцию можно безопасно вызывать без удерживания GIL Python.

Преобразование из предыдущих итераторов NumPy

Старая API итераторов включает функции, такие как PyArrayIter_Check, PyArray_Iter* и PyArray_ITER_*. Массив многомерного итератора включает PyArray_MultiIter*, PyArray_Broadcast и PyArray_RemoveSmallest. Новый дизайн итератора заменяет всю эту функциональность единственным объектом и соответствующей API. Одна из целей новой API заключается в том, что все использования существующего итератора могут быть заменены новым итератором без значительных усилий. В версии 1.6 основным исключением из этого является итератор окрестностей, у которого нет соответствующих функций в этом итераторе.

Вот таблица преобразования, показывающая, какие функции использовать с новым итератором:

Функции итератора
PyArray_IterNew NpyIter_New
PyArray_IterAllButAxis NpyIter_New + axes параметр или флаг итератора NPY_ITER_EXTERNAL_LOOP
PyArray_BroadcastToShape НЕ ПОДДЕРЖИВАЕТСЯ (Используйте поддержку нескольких операндов вместо этого.)
PyArrayIter_Check Необходимо добавить в Python-интерфейс
PyArray_ITER_RESET NpyIter_Reset
PyArray_ITER_NEXT Указатель на функцию из NpyIter_GetIterNext
PyArray_ITER_DATA NpyIter_GetDataPtrArray
PyArray_ITER_GOTO NpyIter_GotoMultiIndex
PyArray_ITER_GOTO1D NpyIter_GotoIndex или NpyIter_GotoIterIndex
PyArray_ITER_NOTDONE Значение возвращаемое функцией iternext
Функции многомерного итератора
PyArray_MultiIterNew NpyIter_MultiNew
PyArray_MultiIter_RESET NpyIter_Reset
PyArray_MultiIter_NEXT Указатель на функцию из NpyIter_GetIterNext
PyArray_MultiIter_DATA NpyIter_GetDataPtrArray
PyArray_MultiIter_NEXTi НЕ ПОДДЕРЖИВАЕТСЯ (всегда итерация шагом в один элемент)
PyArray_MultiIter_GOTO NpyIter_GotoMultiIndex
PyArray_MultiIter_GOTO1D NpyIter_GotoIndex или NpyIter_GotoIterIndex
PyArray_MultiIter_NOTDONE Значение, возвращаемое функцией iternext
PyArray_Broadcast Обрабатывается NpyIter_MultiNew
PyArray_RemoveSmallest Флаг итератора NPY_ITER_EXTERNAL_LOOP
Другие функции
PyArray_ConvertToCommonType Флаг итератора NPY_ITER_COMMON_DTYPE

© 2005–2019 NumPy Developers
Licensed under the 3-clause BSD License.
https://docs.scipy.org/doc/numpy-1.16.1/reference/c-api.iterator.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API