Spec-Zone.ru › NumPy 1.15

Интерфейс итератора массивов

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

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

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

На этой странице документирован интерфейс итератора. Итератор называется 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

Это влияет только на итератор, когда для параметра порядка указано 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, в этом случае он автоматически генерируется из типов данных массивов, помеченных как читаемые. Правила генерации dtype такие же, как для UFunc. Особо следует отметить обработку порядка байтов в выбранном dtype. Если имеется ровно один вход, используется dtype входного массива как есть. В противном случае, если несколько входных dtype объединяются вместе, выход будет в родном порядке байтов.

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

NPY_ITER_NO_SUBTYPE

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

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

NPY_ITER_NO_BROADCAST

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

NPY_ITER_ARRAYMASK

New in version 1.7.

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

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

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

NPY_ITER_WRITEMASKED

New in version 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, исключение Python не устанавливается, когда NPY_FAIL возвращается. Вместо этого *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, исключение Python не устанавливается, когда NPY_FAIL возвращается. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не NULL, функция может быть безопасно вызвана без удерживания блокировки Python GIL.

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

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

Возвращает NPY_SUCCEED или NPY_FAIL. Если errmsg не NULL, исключение Python не устанавливается, когда NPY_FAIL возвращается. Вместо этого *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)

Настраивает итератор на указание на индексы ndim , указанные 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)

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

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

PyObject** NpyIter_GetOperandArray(NpyIter* iter)

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

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. Для матрицы Гессе вы делаете то же самое, но добавляете два измерения или используете преимущества симметрии и упаковываете их в одно измерение с определённым кодированием.

Эта функция может быть вызвана только если итератор отслеживает многомерный индекс и если 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, исключение Python не устанавливается, когда NPY_FAIL возвращается. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без удержания Python GIL.

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

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 возвращается. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без удержания Python GIL.

char** NpyIter_GetDataPtrArray(NpyIter* iter)

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

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

char** NpyIter_GetInitialDataPtrArray(NpyIter* iter)

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

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

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

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 не изменит его. Эту функцию можно безопасно вызывать без удержания Python GIL.

ПРЕДУПРЕЖДЕНИЕ: Хотя указатель можно кэшировать, его значения могут измениться, если итератор буферизован.

npy_intp* NpyIter_GetInnerLoopSizePtr(NpyIter* iter)

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

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

void NpyIter_GetInnerFixedStrideArray(NpyIter* iter, npy_intp* out_strides)

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

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

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

Преобразование из предыдущих итераторов 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.15.4/reference/c-api.iterator.html

Spec-Zone.ru

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