numpy C代码解释

狂热包括当你忘记目标时加倍努力。--- 乔治·桑塔亚纳

权威是一个能告诉你比你真正想知道的更多的事情的人。--- 未知

本章试图解释一些新代码背后的逻辑。这些解释背后的目的是使某人能够比仅仅盯着代码更容易地理解实现背后的想法。也许通过这种方式,更多的人可以改进、借鉴和/或优化算法。

记忆模型

ndarray的一个基本方面是,数组被视为从某个位置开始的内存“块”。这种记忆的解释取决于步伐信息。对于中的每个维度 N -维度数组,一个整数(跨距)指示必须跳过多少字节才能到达该维度中的下一个元素。除非您有一个单段数组,否则在遍历数组时必须参考此跨步信息。编写接受跨步的代码并不困难,只需使用(char * )指针,因为步幅以字节为单位。还要记住,步幅不必是元素大小的单位倍数。另外,请记住,如果数组的维数为0(有时称为秩0数组),那么跨距和维数变量为空。

除了跨步和维度中包含的结构信息之外, PyArrayObject ,这些标志包含有关如何访问数据的重要信息。尤其是, NPY_ARRAY_ALIGNED 根据数据类型数组,当内存在适当的边界上时设置标志。即使您有一个连续的内存块,也不能假定可以安全地取消对特定于数据类型的元素指针的引用。只有如果 NPY_ARRAY_ALIGNED 是否设置了标志?这是一个安全操作(在某些平台上它可以工作,但在其他平台上,如Solaris,它将导致总线错误)。这个 NPY_ARRAY_WRITEABLE 如果您计划写入数组的内存区域,也应该确保这一点。还可以获得指向不可写内存区的指针。有时,写到内存区时 NPY_ARRAY_WRITEABLE 没有设置标志只会很粗鲁。有时它会导致程序崩溃( e.g. 是只读内存映射文件的数据区域)。

数据类型封装

数据类型是ndarray的一个重要抽象。操作将查找数据类型以提供在阵列上操作所需的关键功能。此功能在“f”成员指向的函数指针列表中提供 PyArray_Descr 结构。通过这种方式,只需提供 PyArray_Descr 结构,在“f”成员中具有适当的函数指针。对于内置类型,有一些可以绕过此机制的优化,但是数据类型抽象的要点是允许添加新的数据类型。

void数据类型是内置的数据类型之一,它允许任意的结构化类型,其中包含1个或多个字段作为数组元素。字段只是另一个数据类型对象,以及当前结构化类型的偏移量。为了支持任意嵌套的字段,对void类型实现了数个数据类型访问的递归实现。一个常见的习惯用法是循环遍历字典的元素,并根据存储在给定偏移量的数据类型对象执行特定的操作。这些偏移量可以是任意数字。因此,必须认识到遇到不一致数据的可能性,并在必要时加以考虑。

N-D迭代器

在许多numpy代码中,一个非常常见的操作是需要迭代一个普通的、跨步的、n维数组的所有元素。通用N维循环的这种操作是在迭代器对象的概念中抽象出来的。要编写一个n维循环,只需从ndarray创建一个迭代器对象,使用迭代器对象结构的dataptr成员并调用宏 PyArray_ITER_NEXT (它)在迭代器对象上移动到下一个元素。“next”元素总是按C-连续顺序排列。宏的工作方式是首先对C-continuous、1-D和2-D案例进行特殊的封装,这些案例的工作非常简单。

对于一般情况,迭代通过跟踪迭代器对象中的坐标计数器列表来工作。每次迭代时,最后一个坐标计数器都会增加(从0开始)。如果此计数器小于该维度中数组的大小(一个预先计算和存储的值),则计数器将增大,并且dataptr成员将随着该维度和宏结束的步幅而增大。如果达到维度的结尾,则最后一个维度的计数器将重置为零,并且通过减去小于该维度中元素数1倍的步幅值,将dataptr移回该维度的开头(这也是预先计算的,并存储在迭代器对象的back strides成员中)。在这种情况下,宏不会结束,但局部维度计数器会递减,以便“下一个到最后一个维度”替换上一个维度所扮演的角色,并在下一个到最后一个维度上再次执行先前描述的测试。通过这种方式,可以适当地调整dataptr以适应任意删除。

的坐标成员 PyArrayIterObject 结构维护当前的N-D计数器,除非基础数组是C-连续的,在这种情况下,坐标计数是通过传递的。的索引成员 PyArrayIterObject 跟踪迭代器的当前平面索引。它由更新 PyArray_ITER_NEXT 宏。

广播

在numpy的前身numpy中,广播是在深埋于ufuncobject.c.的几行代码中实现的。在numpy中,广播的概念被抽象出来,以便在多个地方执行。广播由功能处理 PyArray_Broadcast . 此函数需要 PyArrayMultiIterObject (或二进制等价物)要传入。这个 PyArrayMultiIterObject 跟踪每个维度中维度和大小的广播数量以及广播结果的总大小。它还跟踪正在广播的数组的数量,并为每个正在广播的数组提供一个指向迭代器的指针。

这个 PyArray_Broadcast 函数获取已定义的迭代器,并使用它们确定每个维度中的广播形状(要在广播发生的同时创建迭代器,请使用 PyArray_MultiIterNew 函数)。然后,对迭代器进行调整,使每个迭代器都认为它正在广播大小的数组上迭代。这是通过调整迭代器的维数和每个维数中的形状来完成的。这是因为迭代器步幅也被调整了。广播只调整(或增加)长度-1尺寸。对于这些维度,steps变量简单地设置为0,这样当广播操作在扩展维度上运行时,该数组上的迭代器的数据指针就不会移动。

广播总是使用扩展维度的0值步幅以数字形式实现。这是完全相同的方式在 NumPy 。最大的区别是,现在的步幅数组在 PyArrayIterObject ,广播结果中涉及的迭代器在 PyArrayMultiIterObjectPyArray_Broadcast call实现广泛的强制转换规则。

数组标量

数组标量提供了一个python类型的层次结构,允许存储在数组中的数据类型与从数组中提取元素时返回的python类型之间的一一对应关系。此规则的一个例外是使用对象数组。对象数组是任意Python对象的异构集合。当从对象数组中选择一个项时,您将返回原始的python对象(而不是对象数组标量,它确实存在,但很少用于实际目的)。

数组标量还提供与数组相同的方法和属性,目的是相同的代码可以用于支持任意维度(包括0维)。数组标量是只读的(不可变),除了可以写入的void标量之外,它还可以使结构化数组字段设置更自然地工作(a [0] ['f1'] = value

索引

所有python索引操作 arr[index] 首先准备索引并查找索引类型。支持的索引类型包括:

  • 整数

  • 内瓦西斯

  • 省略

  • 整数数组/数组喜欢(花式)

  • 布尔(单个布尔数组);如果有多个布尔数组作为索引,或者形状不完全匹配,则布尔数组将转换为整数数组。

  • 0-D布尔(也是整数);0-D布尔数组是一种特殊情况,必须在高级索引代码中处理。它们表示必须将0-D布尔数组解释为整数数组。

以及表示整数数组被解释为整数索引的标量数组特殊情况,这一点很重要,因为整数数组索引强制进行复制,但如果返回标量(整数索引),则忽略该索引。准备好的索引保证有效,但高级索引的超出限制值和广播错误除外。这包括为不完整的索引添加省略号,例如,当二维数组使用单个整数进行索引时。

下一步取决于找到的索引类型。如果所有维度都用整数索引,则返回或设置一个标量。单个布尔索引数组将调用专用布尔函数。包含省略号或切片但没有高级索引的索引将始终通过计算新的步幅和内存偏移量在旧数组中创建视图。然后可以返回此视图,也可以使用 PyArray_CopyObject . 注意 PyArray_CopyObject 当数组为对象数据类型时,也可以在其他分支中的临时数组上调用以支持复杂的分配。

高级索引

到目前为止,最复杂的情况是高级索引,它可能与典型的基于视图的索引结合使用,也可能不结合使用。这里,整数索引被解释为基于视图的索引。在试图理解这一点之前,你可能想让自己熟悉它的微妙之处。高级索引代码有三个不同的分支和一个特殊情况:

  • 有一个索引数组,它以及赋值数组都可以进行琐碎的迭代。例如,它们可能是连续的。另外,索引数组必须是 intp 类型和赋值中的值数组应为正确的类型。这纯粹是一条快车道。

  • 只有整数数组索引,因此不存在子数组。

  • 基于视图和高级索引是混合的。在这种情况下,基于视图的索引定义了由高级索引组合的子数组集合。例如, arr[[1, 2, 3], :] 通过垂直堆叠子阵列创建 arr[1, :]arr[2,:]arr[3, :] .

  • 有一个子数组,但它只有一个元素。这种情况可以像没有子阵列一样处理,但在设置期间需要一些注意。

决定什么情况下适用,检查广播,并确定所需的换位类型都是在 PyArray_MapIterNew . 设置后,有两种情况。如果没有子数组或它只有一个元素,则不需要子数组迭代,并准备一个迭代器来迭代所有索引数组。 以及 结果或值数组。如果存在子数组,则准备三个迭代器。一个用于索引数组,一个用于结果或值数组(减去其子数组),一个用于原始数组和结果/分配数组的子数组。前两个迭代器给出(或允许计算)进入子数组开始的指针,然后允许重新启动子数组迭代。

当高级索引相邻时,可能需要换位。所有必要的换位都由 PyArray_MapIterSwapAxes 必须由呼叫者处理,除非 PyArray_MapIterNew 要求分配结果。

在准备之后,获取和设置相对直接,尽管需要考虑不同的迭代模式。除非在项目获取期间只有一个索引数组,否则将预先检查索引的有效性。否则,它将在内部循环本身中处理以进行优化。

通用函数

通用函数是可调用的对象 N 投入与产出 M 通过将基本的一维循环包装成完全易于使用的函数输出,这些函数无缝地实现广播、类型检查和缓冲强制以及输出参数处理。新的通用函数通常是在C语言中创建的,尽管有一种从python函数创建ufunc的机制 (frompyfunc )用户必须提供一个1-D循环,该循环实现获取输入标量值的基本函数,并将生成的标量放入相应的输出槽中,如实现中所述。

安装程序

每个ufunc计算都涉及与设置计算相关的一些开销。这种开销的实际意义在于,即使实际的ufunc计算速度非常快,您也可以编写数组和类型特定的代码,这些代码在小数组中比ufunc工作得更快。特别是,使用ufuncs在0-D数组上执行许多计算将比其他基于python的解决方案慢(静默导入的scalarMath模块精确存在,以使数组scalars具有基于ufunc的计算的外观和感觉,同时显著降低开销)。

当一个ufunc被调用时,必须做很多事情。从这些设置操作收集的信息存储在循环对象中。这个循环对象是一个C结构(它可以成为一个python对象,但不会被初始化,因为它只在内部使用)。此循环对象的布局需要与pyarray_broadcast一起使用,以便可以像在代码的其他部分处理广播一样处理广播。

首先,在线程特定的全局字典中查找缓冲区大小、错误掩码和相关错误对象的当前值。错误掩码的状态控制在发现错误条件时发生的情况。应该注意的是,只有在执行每个一维循环之后才执行硬件错误标志的检查。这意味着,如果输入和输出数组是连续的,并且类型正确,从而执行单个一维循环,则在计算完数组的所有元素之前,不能检查标志。在特定于线程的字典中查找这些值需要一些时间,除了非常小的数组之外,很容易忽略这些时间。

检查完特定于线程的全局变量后,对输入进行评估,以确定UFUNC应如何进行,并在必要时构造输入和输出数组。任何不是数组的输入都将转换为数组(必要时使用上下文)。记录了哪些输入是标量(因此转换为0-D数组)。

接下来,根据输入数组类型,从UFUNC可用的一维循环中选择适当的一维循环。这个一维循环是通过尝试将输入数据类型的签名与可用签名匹配来选择的。与内置类型对应的签名存储在ufunc结构的types成员中。与用户定义类型对应的签名存储在函数信息的链接列表中,head元素存储为 CObject 在由数据类型编号键控的userloops字典中(参数列表中的第一个用户定义类型用作键)。搜索签名,直到找到可以安全地将输入数组全部转换为的签名(忽略不允许确定结果类型的任何标量参数)。这个搜索过程的含义是,在存储签名时,“较小的类型”应该放在“较大的类型”下面。如果找不到一维循环,则报告错误。否则,参数_列表将用存储的签名更新---以防需要强制转换,并修复一维循环假定的输出类型。

如果ufunc有2个输入和1个输出,而第二个输入是对象数组,则会执行特殊情况检查,以便在第二个输入不是ndarray时返回notimplemented,并且 __array_priority__ 属性,并且具有 __r{{op}}__ 特殊方法。通过这种方式,可以向Python发出信号,让另一个对象有机会完成操作,而不是使用一般的对象数组计算。这允许(例如)稀疏矩阵覆盖乘法运算符一维循环。

对于小于指定缓冲区大小的输入数组,将对所有非连续、不对齐或按字节顺序排列的数组进行复制,以确保对小数组使用单个循环。然后,为所有输入数组创建数组迭代器,并将迭代器的结果集合广播到单个形状。

然后处理输出参数(如果有),并构造任何缺少的返回数组。如果提供的任何输出数组没有正确的类型(或未正确对齐),并且小于缓冲区大小,则使用 NPY_ARRAY_WRITEBACKIFCOPY 标志集。在函数结束时, PyArray_ResolveWritebackIfCopy 调用,以便将其内容复制回输出数组。然后处理输出参数的迭代器。

最后,决定了如何执行循环机制,以确保将输入数组的所有元素组合在一起以生成正确类型的输出数组。循环执行的选项包括一个循环(用于连续、对齐和正确的数据类型)、跨步循环(用于非连续但仍然对齐和正确的数据类型)和缓冲循环(用于不对齐或不正确的数据类型情况)。根据调用的执行方法,然后设置并计算循环。

函数调用

本节描述如何为这三种不同类型的执行分别设置和执行基本的通用函数计算循环。如果 NPY_ALLOW_THREADS 在编译期间定义,然后只要不涉及对象数组,就在调用循环之前释放python全局解释器锁(gil)。如果需要处理错误情况,则重新获取。只有在一维循环完成后,才会检查硬件错误标志。

单回路

这是最简单的情况。通过调用底层的一维循环一次来执行ufunc。只有当输入和输出的数据类型(包括字节顺序)正确且所有数组的步幅都一致(连续、0-D或1-D)时,这才可能发生。在这种情况下,一维计算循环被调用一次,以计算整个数组的计算。请注意,只有在整个计算完成后才检查硬件错误标志。

梯形环

当输入和输出数组对齐且类型正确,但跨距不均匀(非连续和二维或更大)时,则使用第二个循环结构进行计算。此方法将输入和输出参数的所有迭代器转换为对除最大维度之外的所有维度进行迭代。然后,内部循环由底层的一维计算循环处理。外部循环是转换迭代器上的标准迭代器循环。在每个一维循环完成后,将检查硬件错误标志。

缓冲环

当输入和/或输出数组与底层一维循环期望的数据类型不一致或数据类型错误(包括字节交换)时,这是处理这种情况的代码。数组也被认为是非连续的。代码的工作方式与跨步循环非常相似,除了内部的一维循环被修改,以便对输入执行预处理,对bufsize块(其中bufsize是用户可设置的参数)中的输出执行后处理。底层的一维计算循环对复制的数据(如果需要)进行调用。在这种情况下,设置代码和循环代码要复杂得多,因为它必须处理:

  • 临时缓冲区的内存分配

  • 决定是否在输入和输出数据上使用缓冲区(错误对齐和/或错误的数据类型)

  • 为任何需要缓冲区的输入或输出复制并可能强制转换数据。

  • 特殊的大小写对象数组,以便在需要复制和/或强制转换时正确处理引用计数。

  • 将内部一维循环分解为bufsize块(可能还有余数)。

同样,在每个一维循环的末尾检查硬件错误标志。

最终输出操作

UFUNC允许其他类似数组的类无缝地通过接口传递,因为特定类的输入将导致输出属于同一类。其工作机制如下。如果任何输入不是ndarrays,则定义 __array_wrap__ 方法,然后用最大的 __array_priority__ 属性确定所有输出的类型(除了传入的任何输出数组)。这个 __array_wrap__ 将调用输入数组的方法,并从ufunc返回ndarray作为其输入。有两种呼叫方式 __array_wrap__ 支持的函数。第一个将ndarray作为第一个参数,将“context”的元组作为第二个参数。上下文是(ufunc、参数、输出参数号)。这是第一次尝试呼叫。如果出现类型错误,则只使用ndarray作为第一个参数调用函数。

方法

UFUNC有三种方法需要计算,类似于通用UFUNC。这些是减少、积累和减少。这些方法中的每一个都需要一个SETUP命令,后跟一个循环。对于与无元素、一个元素、跨步循环和缓冲循环相对应的方法,有四种循环样式。这些基本循环样式与一般用途函数调用所实现的基本循环样式相同,但没有元素和一个元素的情况除外,这是在输入数组对象分别有0和1个元素时发生的特殊情况。

安装程序

所有三种方法的设置功能是 construct_reduce . 此函数创建一个还原循环对象,并用完成循环所需的参数填充它。所有的方法都只适用于接受2个输入并返回1个输出的UFunc。因此,选择底层的一维循环时假设签名为 [ otype, otype, otype ] 在哪里? otype 是请求的缩减数据类型。然后从(每个线程)全局存储中检索缓冲区大小和错误处理。对于不对齐或数据类型不正确的小数组,将进行复制,以便使用未缓冲的代码部分。然后,选择循环策略。如果数组中有1个元素或0个元素,则选择简单的循环方法。如果阵列没有错位,并且数据类型正确,则选择跨步循环。否则,必须执行缓冲循环。然后建立循环参数,构造返回数组。根据方法是“减少”、“累积”还是“减少”,输出数组的形状不同。如果已经提供了输出数组,则会检查其形状。如果输出数组不是C-连续、对齐且数据类型正确,则使用writebackifcopy标志集进行临时复制。这样,这些方法将能够与行为良好的输出数组一起工作,但当 PyArray_ResolveWritebackIfCopy 在函数完成时调用。最后,迭代器被设置为在正确的轴上循环(取决于提供给方法的轴的值),并且设置例程返回到实际的计算例程。

减少

所有的ufunc方法都使用相同的底层一维计算循环,并调整输入和输出参数,以便进行适当的减少。例如,reduce功能的关键在于调用一维循环时,输出和第二个输入指向内存中的相同位置,两者的步长都为0。第一个输入指向输入数组,其步长由选定轴的适当跨距给定。这样,执行的操作是

System Message: WARNING/2 (\开始{align*} [0] \\ [k] \textrm{<op>}o\quad k=1\ldots N)

latex exited with error [stdout] This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2019/dev/Debian) (preloaded format=latex) restricted \write18 enabled. entering extended mode (./math.tex LaTeX2e <2018-12-01> (/usr/share/texlive/texmf-dist/tex/latex/base/article.cls Document Class: article 2018/09/03 v1.4i Standard LaTeX document class (/usr/share/texlive/texmf-dist/tex/latex/base/size12.clo)) (/usr/share/texlive/texmf-dist/tex/latex/base/inputenc.sty) (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty For additional information on amsmath, use the `?' option. (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty)) (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty) (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty)) (/usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty) (/usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty (/usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty)) (/usr/share/texlive/texmf-dist/tex/latex/anyfontsize/anyfontsize.sty) (/usr/share/texlive/texmf-dist/tex/latex/tools/bm.sty) (./math.aux) ! Undefined control sequence. l.13 \fontsize{12}{14}\selectfont \� ��始{align*} [0] \\ [k] \textrm{<op>}o\... ! Package inputenc Error: Invalid UTF-8 byte "BC. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 \fontsize{12}{14}\selectfont \� �始{align*} [0] \\ [k] \textrm{<op>}o\... ! Package inputenc Error: Invalid UTF-8 byte "80. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 \fontsize{12}{14}\selectfont \开 始{align*} [0] \\ [k] \textrm{<op>}o\... ! Package inputenc Error: Unicode character 始 (U+59CB) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 \fontsize{12}{14}\selectfont \开始 {align*} [0] \\ [k] \textrm{<op>}o\... ! Missing number, treated as zero. <to be read again> k l.13 ...{14}\selectfont \开始{align*} [0] \\ [k] \textrm{<op>}o\quad k=1\l... ! Illegal unit of measure (pt inserted). <to be read again> k l.13 ...{14}\selectfont \开始{align*} [0] \\ [k] \textrm{<op>}o\quad k=1\l... [1] (./math.aux) ) (see the transcript file for additional information) Output written on math.dvi (1 page, 256 bytes). Transcript written on math.log.

在哪里? N+1 是输入中的元素数, io 是输出,并且 i[k]k^{{\textrm{{th}}}} 元素 i 沿所选轴。对于尺寸大于1的数组重复此基本操作,以便沿选定轴的每个一维子数组都进行缩减。删除选定维度的迭代器处理此循环。

对于缓冲循环,在调用循环函数之前必须注意复制和转换数据,因为底层循环需要正确数据类型(包括字节顺序)的对齐数据。缓冲循环必须在对不大于用户指定bufsize的块调用循环函数之前处理此复制和强制转换。

累加

累积函数与reduce函数非常相似,因为输出和第二个输入都指向输出。不同的是,第二个输入指向内存,比当前输出指针落后一步。因此,执行的操作是

System Message: WARNING/2 (\开始{align*} [0] &=我(&i) [0] \\ [k] &=我(&i) [k] \文本rm{<op>}o [k-1] \四边形k=1\ldots N。)

latex exited with error [stdout] This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2019/dev/Debian) (preloaded format=latex) restricted \write18 enabled. entering extended mode (./math.tex LaTeX2e <2018-12-01> (/usr/share/texlive/texmf-dist/tex/latex/base/article.cls Document Class: article 2018/09/03 v1.4i Standard LaTeX document class (/usr/share/texlive/texmf-dist/tex/latex/base/size12.clo)) (/usr/share/texlive/texmf-dist/tex/latex/base/inputenc.sty) (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty For additional information on amsmath, use the `?' option. (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty)) (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty) (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty)) (/usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty) (/usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty (/usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty)) (/usr/share/texlive/texmf-dist/tex/latex/anyfontsize/anyfontsize.sty) (/usr/share/texlive/texmf-dist/tex/latex/tools/bm.sty) (./math.aux) ! Undefined control sequence. l.13 \fontsize{12}{14}\selectfont \� ��始{align*} [0] &=我(&i) [0] \\ [... ! Package inputenc Error: Invalid UTF-8 byte "BC. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 \fontsize{12}{14}\selectfont \� �始{align*} [0] &=我(&i) [0] \\ [... ! Package inputenc Error: Invalid UTF-8 byte "80. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 \fontsize{12}{14}\selectfont \开 始{align*} [0] &=我(&i) [0] \\ [... ! Package inputenc Error: Unicode character 始 (U+59CB) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 \fontsize{12}{14}\selectfont \开始 {align*} [0] &=我(&i) [0] \\ [... ! Misplaced alignment tab character &. l.13 ...e{12}{14}\selectfont \开始{align*} [0] & =我(&i) [0] \\ [k] &=... ! Package inputenc Error: Unicode character 我 (U+6211) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...}{14}\selectfont \开始{align*} [0] &=我 (&i) [0] \\ [k] &=我�... ! Package inputenc Error: Unicode character ( (U+FF08) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...4}\selectfont \开始{align*} [0] &=我( &i) [0] \\ [k] &=我(&... ! Misplaced alignment tab character &. l.13 ...}\selectfont \开始{align*} [0] &=我(& i) [0] \\ [k] &=我(&i... ! Package inputenc Error: Unicode character ) (U+FF09) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...lectfont \开始{align*} [0] &=我(&i) [0] \\ [k] &=我(&i) ... ! Missing number, treated as zero. <to be read again> k l.13 ...��始{align*} [0] &=我(&i) [0] \\ [k] &=我(&i) [k] \文本... ! Illegal unit of measure (pt inserted). <to be read again> k l.13 ...��始{align*} [0] &=我(&i) [0] \\ [k] &=我(&i) [k] \文本... ! Misplaced alignment tab character &. l.13 ...始{align*} [0] &=我(&i) [0] \\ [k] & =我(&i) [k] \文本rm... ! Package inputenc Error: Unicode character 我 (U+6211) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...align*} [0] &=我(&i) [0] \\ [k] &=我 (&i) [k] \文本rm{<op... ! Package inputenc Error: Unicode character ( (U+FF08) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...gn*} [0] &=我(&i) [0] \\ [k] &=我( &i) [k] \文本rm{<op>}o... ! Misplaced alignment tab character &. l.13 ...n*} [0] &=我(&i) [0] \\ [k] &=我(& i) [k] \文本rm{<op>}o ... ! Package inputenc Error: Unicode character ) (U+FF09) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...[0] &=我(&i) [0] \\ [k] &=我(&i) [k] \文本rm{<op>}o [k-1... ! Undefined control sequence. l.13 ...��(&i) [0] \\ [k] &=我(&i) [k] \� ��本rm{<op>}o [k-1] \四�... ! Package inputenc Error: Invalid UTF-8 byte "96. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...�(&i) [0] \\ [k] &=我(&i) [k] \� �本rm{<op>}o [k-1] \四�... ! Package inputenc Error: Invalid UTF-8 byte "87. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...(&i) [0] \\ [k] &=我(&i) [k] \文 本rm{<op>}o [k-1] \四边... ! Package inputenc Error: Unicode character 本 (U+672C) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...&i) [0] \\ [k] &=我(&i) [k] \文本 rm{<op>}o [k-1] \四边形... ! Undefined control sequence. l.13 ...=我(&i) [k] \文本rm{<op>}o [k-1] \� ��边形k=1\ldots N。 ! Package inputenc Error: Invalid UTF-8 byte "9B. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...我(&i) [k] \文本rm{<op>}o [k-1] \� �边形k=1\ldots N。 ! Package inputenc Error: Invalid UTF-8 byte "9B. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...��(&i) [k] \文本rm{<op>}o [k-1] \四 边形k=1\ldots N。 ! Package inputenc Error: Unicode character 边 (U+8FB9) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...��&i) [k] \文本rm{<op>}o [k-1] \四边 形k=1\ldots N。 ! Package inputenc Error: Unicode character 形 (U+5F62) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...i) [k] \文本rm{<op>}o [k-1] \四边形 k=1\ldots N。 ! Package inputenc Error: Unicode character 。 (U+3002) (inputenc) not set up for use with LaTeX. See the inputenc package documentation for explanation. Type H <return> for immediate help. ... l.13 ...��rm{<op>}o [k-1] \四边形k=1\ldots N。 [1] (./math.aux) ) (see the transcript file for additional information) Output written on math.dvi (1 page, 280 bytes). Transcript written on math.log.

输出的形状与输入的形状相同,并且每个一维循环在 N 当所选轴中的形状为 N+1 . 同样,缓冲循环在调用底层的一维计算循环之前要注意复制和转换数据。

还原剂

约简函数是约简函数和累积函数的推广。它在由索引指定的输入数组的范围内实现reduce。在进行循环计算之前,将检查“额外索引”参数,以确保所选维度上的输入数组的每个输入都不太大。循环实现是使用与减少索引输入中元素重复次数非常相似的代码来处理的。特别是:传递给底层一维计算循环的第一个输入指针指向索引数组指示的正确位置处的输入数组。此外,输出指针和传递给底层一维循环的第二个输入指针指向内存中的相同位置。一维计算循环的大小固定为当前索引和下一个索引之间的差异(当当前索引是最后一个索引时,则下一个索引假定为沿选定维度的数组长度)。这样,一维循环将实现对指定索引的减少。

使用缓冲代码处理对齐错误或与输入和/或输出数据类型不匹配的循环数据类型,其中in数据复制到临时缓冲区,并在调用基础一维函数之前根据需要转换为正确的数据类型。临时缓冲区以不大于用户可设置缓冲区大小值的(元素)大小创建。因此,循环必须足够灵活,能够调用底层的一维计算循环足够多次,以不大于缓冲区大小的块完成总计算。