PDL::Indexingp - 云端在线

这是 PDL::Indexingp 命令,可以使用我们的多个免费在线工作站之一在 OnWorks 免费托管服务提供商中运行,例如 Ubuntu Online、Fedora Online、Windows 在线模拟器或 MAC OS 在线模拟器

程序:

您的姓名


PDL::Indexing - 索引和切片 piddles 介绍。

产品详情


本手册页应作为有关索引和线程功能的第一个教程
PDL.

与所有矢量化语言一样,PDL 使用
数学向量符号。 自动循环被称为“线程”,部分
因为最终 PDL 将实现并行处理以加速循环。

PDL 的许多灵活性和功能依赖于
Perl 扩展。 索引允许以非常灵活的方式访问 piddle 的数据
大大地。 线程提供简单操作的有效矢量化。

piddle 的值作为类型值紧凑地存储在单个内存块中,
不是(如在普通的 Perl 列表中)作为单独的 Perl 标量。

在遵循许多“方法”的部分中被称为——这些是 Perl 操作符
适用于 PDL。 从 perldl(或 pdl2)shell,您可以找到有关每种方法的更多信息
通过键入“?” 后跟方法名称。

尺寸 名单
通常,piddle(PDL 变量)是一个 N 维数组,其中 N 可以是 0(对于一个
标量)、1(例如对于声音样本)或更高的图像值和更复杂的
结构。 piddle 的每个维度都有一个正整数大小。 “珀尔”
解释器将每个 piddle 视为一种特殊类型的 Perl 标量(一个受祝福的 Perl 对象,
实际上 - 但你不必知道使用它们)可以在任何地方使用
放一个正常的标量。

您可以以 Perl 列表的形式访问 piddle 的尺寸,否则可以确定大小
有几种方法的小窍门。 重要的是:

nelem - PDL 中元素的总数
ndims - 返回 PDL 中的维数
dims - 将 PDL 的维度列表作为 Perl 列表返回
dim - 返回 PDL 特定维度的大小

索引 数据流
PDL 在 piddle 和索引的子字段之间维护了“数据流”的概念
拨弄。 当您生成索引子字段或父 piddle 的单个元素时,
child 和 parent 保持连接,直到您手动断开它们。 这让你
在您的代码中以不同方式表示相同的数据——例如,您可以考虑
RGB 图像同时作为 3 x 1000 x 1000 图像中 (R,G,B) 值的集合,
并作为三个独立的 1000 x 1000 颜色平面存储在不同的变量中。 修改
任何一个变量改变了底层的内存,这些变化反映在所有
数据的表示。

有两种重要的方法可以让您控制子级之间的数据流连接
和父 PDL:

复制 - 强制 PDL 的显式副本
sever - 断开 PDL 与其父级之间的数据流连接(如果有)

穿线 尺寸 下单
大多数 PDL 操作都作用于其 piddle 参数的前几个维度。 为了
例如,“sumover”对列表中第一个维度(维度 0)的所有元素求和。
如果你输入一个 XNUMXD piddle,那么第一个维度被认为是
“活动”维度和后面的维度是“线程”维度,因为它们是
简单地循环了一遍。 有几种方法可以对维度列表进行转置或重新排序
一个 PDL。 这些技术非常快,因为它们不接触底层数据,只
改变 PDL 访问数据的方式。 主要的维度排序函数是:

mv - 将特定维度移动到维度列表中的其他位置
xchg - 交换维度列表中的两个维度,剩下的不理会
重新排序 - 允许批量混合尺寸
团块 - 将两个或更多小维度聚集成一个更大的维度
挤压 - 消除大小为 1 的任何维度

物理 尺寸
· 文档 Perl 级线程

· 线程ID

· 更新和更正切片的描述

· slice.pd 中的新函数(仿射、滞后、splitdim)

· 修改显式线程的段落

索引 穿线 - PDL


PDL 的很多灵活性和功能都依赖于
Perl 扩展。 索引允许以非常灵活的方式访问 pdl 对象的数据
大大地。 线程提供了高效的隐式循环功能(因为循环是
实现为优化的 C 代码)。

Pdl 对象(后来通常称为“pdls”)是表示多维的 Perl 对象
数组和操作。 对比简单的 Perl @x 样式列出数组数据
紧凑地存储在单个内存块中,因此占用的内存要少得多
允许使用快速 C 代码在 pdls 上实现操作(例如加法等)。

文件 能够 它们在许多情况下都能提供类似的结果。 孩子
PDL 的许多索引功能的核心是“父级”和
pdls 之间的“孩子”。 许多索引命令从现有的 pdl 创建一个新的 pdl。
新的 pdl 是“孩子”,旧的 pdl 是“父母”。 新pdl的数据是
由一个转换定义,该转换指定如何从
父母的数据。 子 pdl 与其父级之间的关系通常是双向的,
这意味着子项数据的更改会传播回父项。 (注:你
看,我们的术语已经针对新的数据流功能。 那种
索引命令使用的数据流(您将在一分钟内了解)
始终处于运行状态,不仅在您明确打开 pdl 中的数据流时
通过说“$a->doflow”。 有关数据流的更多信息,请查看 dataflow man
页。)

解释由我们的索引命令创建的 pdls 的另一种方法是将它们视为
一种智能指针,指向其父级数据的某些部分或全部。
因此,当父母的数据(或其中的一部分)发生变化时,这并不奇怪
通过这个“指针”操纵。 在这些介绍性评论之后,希望
为即将到来的事情做好准备(而不是让你太困惑)我们要潜水
直接从索引命令的描述和一些典型示例开始
如何在 PDL 程序中使用它们。 我们将进一步说明指针/数据流
在后面的一些例子中进行类比。

这种“智能指针”关系有两种不同的实现:第一种
一个,这有点慢,但适用于任何转换只是简单地做
根据需要向前和向后转换。 另一个是考虑孩子
piddle 一个“虚拟”piddle,它只存储一个指向父级和访问权限的指针
信息,以便使用 child piddle 的例程实际上直接访问数据
在父级。 如果给不能使用它的例程虚拟小提琴,PDL
在让例程使用它之前透明地物理化虚拟 piddle。

当前 (1.94_01) 所有“仿射”变换,即数据的索引
父 piddle 中的项目由线性变换(+ 常数)确定
child piddle 的索引导致虚拟 piddles。 所有其他索引例程(例如
"->index(...)") 导致物理问题。 PP编译的所有例程都可以接受仿射
piddles(除了那些将指针传递给外部库函数的例程)。

请注意,某些事物是否仿射不会影响您所做的事情的语义
以任何方式:两者

$a->index(...) .= 5;
$a->slice(...) .= 5;

更改 $a 中的数据。 然而,亲和力确实对记忆有重大影响
使用和性能。

切片 文件
可能父/子 pdls 概念最重要的应用是
通过虚拟 pdl 表示物理 pdl 的矩形切片。 谈过
足够长的概念让我们更具体。 假设我们正在使用 2D pdl
代表一个 5x5 的图像(它非常小,所以我们可以在不填充的情况下打印它
几个充满数字的屏幕;)。

pdl> $im = 序列(5,5)
pdl> p $im

[
[0 1 2 3 4]
[5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]
[20 21 22 23 24]
]

pdl> 帮助变量
包 main 中的 PDL 变量::

名称 类型 维度 Flow State Mem
-------------------------------------------------- --------------
$im 双 D [5,5] P 0.20Kb

[ 在这里快速讨论一下提供的“帮助变量”命令可能是合适的
有关 PDL 附带的交互式“perldl”或“pdl2”外壳中的 pdls 的信息。 ]

现在假设我们要创建一个一维 pdl,它只引用图像的一行,比如
第 2 行; 或代表图像所有偶数行的 pdl(想象一下我们必须处理
由于我们帧的某些特殊行为,交错图像的偶数和奇数帧
抓取器)。 作为切片的另一个常见应用,我们可能想要创建一个 pdl
表示图像的顶部和底部颠倒的矩形区域。 所有这些
使用强大的切片功能可以轻松实现效果(以及更多):

pdl> $line = $im->slice(':,(2)')
pdl> $even = $im->slice(':,1:-1:2')
pdl> $area = $im->slice('3:4,3:1')
pdl> help vars # 或者只是 PDL->vars
包 main 中的 PDL 变量::

名称 类型 维度 Flow State Mem
-------------------------------------------------- --------------
$even 双 D [5,2] -C 0.00Kb
$im 双 D [5,5] P 0.20Kb
$line 双 D [5] -C 0.00Kb
$area 双D [2,3] -C 0.00Kb

所有三个“子”pdls 都是 $im 或另一个(基本上等效)的子项
指向 $im 数据的解释指针。 仅对那些虚拟 pdls 访问的操作
切片的参数指定的数据部分。 所以我们可以打印
第2行:

pdl> p $line
[10 11 12 13 14]

还要注意上面和下面 $area 的“流动状态”的区别:

pdl> p $area
pdl> 帮助 $area
这个变量是 Double D [2,3] VC 0.00Kb

以下说明 $im 和 $line 的行为确实与您期望的一样
类似指针的对象(或在数据流图片中:$line 数据的变化是
传播回 $im):

pdl> $im++
pdl> p $line
[11 12 13 14 15]
pdl> $line += 2
pdl> p $im

[
[1 2 3 4 5]
[6 7 8 9 10]
[13 14 15 16 17]
[16 17 18 19 20]
[21 22 23 24 25]
]

注意子虚拟 pdl 上的赋值操作如何改变父物理 pdl
反之亦然(但是,基本的“=”赋值没有,使用“.=”来获得这种效果。
原因见下文)。 虚拟子 pdls 类似于“实时链接”到
“原始”父 pdl。 如前所述,可以认为它们的工作方式类似于
C 指针。 但与 C 指针相比,它们携带更多的信息。 首先,他们
指定它们表示的数据的结构(新 pdl 的维度)和
其次,指定如何从其父数据创建此结构(工作方式
隐藏在 PDL 的内部结构中,无论如何对您来说都不重要(除非您
想在未来破解核心或想成为一般的 PDL 大师(对于一个
这个奇怪生物的定义见 PDL::Internals))。

前面的示例演示了切片函数的典型用法。 由于
切片功能非常重要,这里是对字符串语法的解释
切片的参数:

$vpdl = $a->slice('ind0,ind1...')

其中“ind0”指定如何处理 pdl $a 的第 0 号索引等。
逗号分隔列表可以具有以下形式之一:

':' 使用整个维度

'n' 仅使用索引“n”。 该索引在生成的虚拟 pdl 中的维度为 1。
涉及前两种索引格式的示例:

pdl> $column = $im->slice('2,:')
pdl> $row = $im->slice(':,0')
pdl> p $column

[
[3]
[8]
[15]
[18]
[23]
]

pdl> p $row

[
[1 2 3 4 5]
]

pdl> 帮助 $column
这个变量是 Double D [1,5] VC 0.00Kb

pdl> 帮助 $row
这个变量是 Double D [5,1] VC 0.00Kb

'(n)' 仅使用索引“n”。 该维度从生成的 pdl 中删除(依赖于
事实上,大小为 1 的维度总是可以被删除)。 这之间的区别
case 和前一个在左手和右手的作业中变得重要
边必须有适当的尺寸。

pdl> $line = $im->slice(':,(0)')
pdl> 帮助 $line
这个变量是 Double D [5] -C 0.00Kb

pdl> p $line
[1 2 3 4 5]

发现与上一个示例的不同之处?

'n1:n2' or 'n1:n2:n3'
取从“n1”到“n2”的索引范围或(第二种形式)取范围
索引从“n1”到“n2”,步骤为“n3”。 使用这种格式的一个例子是
由偶数行组成的子图像的先前定义。

pdl> $even = $im->slice(':,1:-1:2')

此示例还演示了负索引的工作方式与正常情况一样
Perl 样式数组通过从维度的末尾向后计数。 如果“n2”是
小于“n1”(在示例中 -1 相当于索引 4)中的元素
虚拟 pdl 相对于其父级有效地恢复。

'*[n]'
添加虚拟维度。 此维度的大小默认为 1 或等于
"n" 如果给出了可选的数字参数。

乍一看,这确实有点奇怪。 什么是假人
方面? 虚拟维度会在之前没有的维度插入一个维度。 如何
完成了吗? 那么,在大小为 1 的新维度的情况下,它可以很容易地
通过您可以用
“(1,m)”或“(m,1)”矩阵。 这同样适用于更高维度的物体。
更有趣的是大小大于 XNUMX 的虚拟维度的情况(例如
“切片('* 5,:')”)。 这与调用虚拟函数创建的方式相同
一个新的虚拟维度。 所以请继续阅读并检查下面的解释。

'([n1:n2[:n3]]=i)'
[尚未实施??????] 有了这样的论点,你 一般性
对角线。 该 对角线 将是尺寸编号。 新输出 pdl 的“i”和(如果
指定括号中的可选部分)将沿索引范围扩展
指定各自的父 pdl 的维度。 一般来说,像这样的论点
只有在对 slice 的同一个调用中有其他类似的参数时才有意义。
括号中的部分对于此类参数是可选的。 这一切的论据
指定相同目标维度“i”的类型必须与相同数量的
其父维度中的索引。 解释它的最好方法可能是给出一个
例如,这里我们制作了一个 pdl,它指的是沿空间对角线的元素
它的父 pdl(一个多维数据集):

$cube = 零 (5,5,5);
$sdiag = $cube->slice('(=0),(=0),(=0)');

上面的命令创建了一个虚拟 pdl 表示沿线的对角线
父母的尺寸编号0、1 和 2 并使其维数为 0(唯一维数)
它。 如果父维度的维度大小您
想要构建具有不同尺寸的对角线,或者您想要反转
对角线上的元素序列,例如

$rect = 零(12,3,5,6,2);
$vpdl = $rect->slice('2:7,(0:1=1),(4),(5:4=1),(=1)');

所以 $vpdl 的元素将与它的父元素相关联,我们可以
表达为:

vpdl(i,j) = rect(i+2,j,4,5-j,j) 0<=i<5, 0<=j<2

[ 在新的索引函数中工作:“$b = $a->index($c);” ???? ]

那里 旨在 不同 of 任务 in PDL
前面的例子已经表明,可以使用虚拟 pdls 对或
访问父 pdl 的部分数据。 它们也可以用作赋值中的左值
(正如上面的一些例子中“++”的使用已经证明的那样)。 对于显式
分配给由虚拟 pdl 表示的数据,您必须使用重载的“.=”
运算符(在这种情况下我们称之为 传播 分配)。 为什么你不能使用
普通赋值运算符“=”?

好吧,您绝对仍然可以使用 '=' 运算符,但它不会做您想要的。 这
是因为 '=' 运算符不能以与其他运算符相同的方式重载
赋值运算符。 如果我们尝试使用 '=' 尝试将数据分配给 a 的一部分
通过虚拟 pdl 的物理 pdl 我们不会达到预期的效果(而是
代表虚拟 pdl 的变量(对受祝福的事物的引用)将在
赋值只包含对另一个有福的东西的引用,它会表现得
未来分配作为原始右值的“物理”副本[这实际上还没有
在 PDL 开发人员邮件列表中明确和讨论的主题]。 从这个意义上说
会断开 pdl 与父级的连接 [ 在某种意义上不是这种行为吗
与数据流中发生的情况相反,其中“.=”中断了与父级的连接? ]。

例如

pdl> $line = $im->slice(':,(2)')
pdl> $line = (5);
pdl> $line++;
pdl> p $im

[
[1 2 3 4 5]
[6 7 8 9 10]
[13 14 15 16 17]
[16 17 18 19 20]
[21 22 23 24 25]
]

pdl> p $line
[1 1 1 1 1]

但是使用“.=”

pdl> $line = $im->slice(':,(2)')
pdl> $line .= (5)
pdl> $line++
pdl> p $im

[
[1 2 3 4 5]
[6 7 8 9 10]
[1 1 1 1 1]
[16 17 18 19 20]
[21 22 23 24 25]
]

pdl> 打印 $line
[1 1 1 1 1]

另外,你可以替换

pdl> $line .= 0;

对于上面的分配(零被转换为标量 piddle,没有维度,所以
它可以分配给任何 piddle)。

最近 perl 版本的一个很好的特性是左值子程序(即 5.6.x 和
更高,包括 PDL 当前支持的所有 perls)。 这允许一个人使用
赋值两边的切片语法:

pdl> $im->slice(':,(2)') .= (5)->xvals->浮动

与左值子赋值功能相关的是粗心的一个小陷阱:最近的 perls
引入了一个“功能”,当
在 perl 调试器“perl -d”下运行。 在调试器下,上面的用法给出了一个
错误如:“无法从左值子例程返回临时...”所以你必须使用语法
喜欢这个:

pdl> ($pdl = $im->slice(':,(2)')) .= (5)->xvals->浮动

它在有和没有调试器的情况下都可以工作,但可以说是笨拙且难以阅读。

请注意,当 lvalue 和 rvalue pdls 时,这样的赋值可能会出现问题
参考父 pdl 中数据的重叠部分:

# 还原 $a 第一行的元素
($tmp = $a->slice(':,(1)')) .= $a->slice('-1:0,(1)');

目前,作业右侧的父数据在
(内部)分配循环进行。 因此,这项任务的结果将取决于
关于元素分配的顺序,几乎可以肯定 而不去 做你想做的
通缉。 所以语义目前是 未定义 目前,随时可能更改。 到
获得所需的行为,使用

($tmp = $a->slice(':,(1)')) .= $a->slice('-1:0,(1)')->copy;

制作切片的物理副本或

($tmp = $a->slice(':,(1)')) .= $a->slice('-1:0,(1)')->sever;

它返回相同的切片,但切断了切片与其父级的连接。

其他 功能 操纵 尺寸
已经广泛讨论了切片功能,应该注意的是,这不是
只有 PDL 索引功能。 还有其他有用的索引功能
(特别是在我们稍后将讨论的线程上下文中)。 这是一个列表
以及一些如何使用它们的示例。

“假的”
在所选位置插入您指定大小(默认为 1)的虚拟尺寸。
您迫不及待地想知道这是如何实现的? 好吧,所有索引为“(X,x,Y)”的元素
("0<=x
pdl(其中“X”和“Y”指的是位置前后的索引组
插入虚拟尺寸的位置。)

此示例计算图像质心的 x 坐标(稍后我们将
了解到由于隐式的魔力,我们实际上并不需要虚拟维度
穿线; 但是使用虚拟尺寸,代码也可以在无线程的世界中工作;
虽然一旦你使用过 PDL 线程,你就不想没有它们
再次)。

# 质心
($xd,$yd) = $im->dims;
$xc = sum($im*xvals(zeroes($xd))->dummy(1,$yd))/sum($im);

让我们更详细地解释它是如何工作的。 一、产品:

$xvs = xvals(零($xd));
打印 $xvs->dummy(1,$yd); # 重复行 $yd 次
$prod = $im*xvs->dummy(1,$yd); # 形成像素级乘积
# x 值的重复行

剩下的就是将逐像素乘积的结果相加,然后
用原始图像中所有像素值的总和进行归一化,从而计算
图像“质心”的 x 坐标(将像素值解释为
局部质量),称为图像的质心。

接下来是一个(从内存消耗的角度)非常便宜的转换
灰度到 RGB,即每个像素现在拥有三倍的值而不是标量。
幸运的是,三元组中的三个值对于灰度图像都是相同的,所以
我们的技巧运作良好,因为它将三元组的所有三个成员映射到
相同的源元素:

# 廉价的灰度到 RGB 转换
$rgb = $grey->dummy(0,3)

不幸的是,这个技巧不能用于将您的旧黑白照片转换为彩色照片
以你喜欢的方式。 :(

请注意,具有虚拟尺寸的 piddles 的内存使用对
内部代表。 如果 piddle 可以表示为虚拟仿射
(``vaffine'') piddle,只存储控制结构。 但是如果 $b 在

$a = (10000);
$b = $a->dummy(1,10000);

被一些例程物理化,你会发现你的程序的内存使用
突然增加了 100Mb。

“对角线”
用一个维度替换两个维度(必须具有相同的大小)
沿这两个维度沿“对角线”引用所有元素。 在这里,我们
有两个例子,任何做过线性的人都应该很熟悉
代数。 首先,制作一个单位矩阵:

# 单位矩阵
$e = zeroes(float, 3, 3); # 使一切归零
($tmp = $e->diagonal(0,1)) .= 1; # 设置沿对角线的元素为1
打印 $e;

或另一条对角线:

($tmp = $e->slice(':-1:0')->diagonal(0,1)) .= 2;
打印 $e;

(你有没有注意到我们之前是如何使用 slice 函数来恢复行的顺序的?
设置新孩子的对角线,从而设置交叉对角线
parent ?) 或从对角矩阵空间到其上的域的映射
定义了矩阵,矩阵的迹:

# 矩阵的迹
$trace = sum($mat->diagonal(0,1)); # 对所有对角线元素求和

“xchg”和“mv”
xchg 交换或“转置”两个指定的维度。 一个直截了当的
例:

# 转置一个矩阵(没有明确地重新排列数据和
#制作副本)
$prod = $ax $a->xchg(0,1);

如果 $a 是正交矩阵, $prod 现在应该非常接近统一矩阵。
通常“xchg”将在线程上下文中使用,但稍后会详细介绍。

mv 以类似的方式工作。 它移动一个维度(由它在
parent) 到新子 pdl 中的新位置:

$b = $a->mv(4,0); # 使 $a 的第 5 维成为第一个
# 新孩子 $b

"xchg" 和 "mv" 的区别在于 "xchg" 只改变两个位置
维度彼此,而“mv”将第一个维度插入到
其次,相应地移动其他维度。

“丛”
将多个维度合二为一。 它的唯一参数指定了多少维
应折叠源 pdl(从第一个开始)。 一个(诚然
不切实际的)示例是一个 3D pdl,它保存来自一堆图像文件的数据
刚刚读入。 然而,来自每个图像的数据实际上代表了一个一维时间
系列并且仅以这种方式排列,因为它是用框架数字化的
抓取器。 所以要再次将它作为你说的时间序列数组

pdl> $seqs = $stack->(2)
pdl> 帮助变量
包 main 中的 PDL 变量::

名称 类型 维度 Flow State Mem
-------------------------------------------------- --------------
$seqs 双 D [8000,50] -C 0.00Kb
$stack 双 D [100,80,50] P 3.05Mb

虽然看起来不切实际,但我们的共聚焦显微镜软件会写入数据(有时)
这边走。 但是更多的时候你在使用隐式的时候会使用clump来达到一定的效果
或显式线程。

呼叫 索引 功能 能够 be 链接
正如您在上面的一些示例中可能已经注意到的那样调用索引函数
可以很好地链接,因为所有这些函数都返回一个新创建的子对象。
但是,在链中进行广泛的索引操作时,一定要跟踪什么
你在做什么,例如

$a->xchg(0,1)->mv(0,4)

将 $a 的维度 1 移动到位置 4,因为在执行第二个命令时
原始维度 1 已移动到调用“mv”的新子节点的位置 0
功能。 我想你明白了(尽管我的解释令人费解)。

传播的 任务 ('.=') 假的 尺寸
与索引相关的一个 sublety 是分配给包含虚拟维度的 pdls
大小大于 1。这些赋值(使用“.=”)是被禁止的,因为有几个元素
的左值 pdl 指向父元素的相同元素。 因此,值
这些父元素可能不明确,并且取决于其中的顺序
实现对元素进行分配。 因此,像这样的分配:

$a = pdl[1,2,3];
$b = $a->dummy(1,4);
$b .= yvals(零(3,4));

可能会产生意想不到的结果,并且结果是明确的 未定义 通过 PDL 因为
当 PDL 获得并行计算特性时,当前的结果很可能会发生变化。

从数据流的角度来看,引入了大于一的假人
维度被认为是不可逆的变换(类似于
热力学),它阻止了分配给父级的反向传播(您
已明确要求使用“.=”赋值)。 需要注意的类似问题
发生在线程上下文中,有时会隐式创建虚拟维度
在线程循环期间(见下文)。

原因 HPMC胶囊 这些因素包括原料奶的可用性以及达到必要粉末质量水平所需的工艺。 父母/孩子 (或 “指针”) 概念
[这将不得不等待一点]

XXXXX 是内存高效的
线程上下文中的 XXXXX
XXXXX 非常灵活和强大的访问 pdl 数据部分的方式
(以比秒等更通用的方式允许)
XXXXX 高效执行
XXXXX 与 section/at 等的差异。

创新中心 使 再次
[ XXXXX 等一切都解决了再填写 ]

** 需要时(xsub 例程接口 C lib 函数)
** 如何实现(-> 物理)
** 如何测试(是物理的(解释它目前是如何工作的))
** -> 复制和 -> 切断

穿线


在上一段关于索引的内容中,我们偶尔会提到这个词,但是
现在是时候明确地谈论 pdls 的“线程”了。 术语线程有
在不同的计算领域有许多不同的含义。 在 PDL 的框架内
可能可以松散地定义为隐式循环工具。 这是隐含的,因为
您没有指定任何类似于封闭 for 循环的内容,而是循环是自动的
(或“神奇地”)由 PDL 根据所​​涉及的 pdls 的尺寸生成。 这
应该让您首先了解为什么您遇到的索引/维度操作函数
在前面的段落中在上下文中特别重要和有用
穿线。 线程的另一个成分(除了所涉及的 pdls)是
线程感知函数(通常,这些是 PDL::PP 编译函数)和
pdls 被“线程化”了。 关于术语的内容太多了,现在让我们尝试
阐明这一切的含义。

隐含的 穿线 - a 第一 例子
线程有两种略有不同的变体。 我们从我们所说的开始
“隐式线程”。 让我们选择一个涉及函数循环的实际示例
pdl 的许多元素。 假设我们有一个想要转换为灰色的 RGB 图像
规模。 RGB 图像由 3 维 pdl "im(3,x,y)" 表示,其中第一维
包含每个像素的三个颜色分量,“x”和“y”是宽度和高度
图像,分别。 接下来我们需要指定如何在给定的条件下转换颜色三重
像素转换为灰度值(作为一个现实的例子,它应该代表相对
我们对颜色不敏感的眼细胞将检测到该颜色以达到的强度
我们称之为从颜色到灰度的自然转换)。 一个近似值
效果很好的是计算每个 RGB 三元组 (r,g,b) 的灰度强度作为
加权和

灰度值 = 77/256*r + 150/256*g + 29/256*b =
内部([77,150,29]/256, [r,g,b])

其中最后一种形式表明我们可以将其写为 3-vector 的内积
包含红色、绿色和蓝色分量的权重,其中 3 向量包含
颜色成分。 传统上,我们可能会编写如下函数来
处理整个图像:

我的@dims=$im->dims;
# 这里通常检查第一个dim 的大小是否正确(3),等等
$grey=zeroes(@dims[1,2]); # 为生成的灰度图像制作 pdl
$w = pdl [77,150,29] / 256; # 权重向量
对于 ($j=0;$j
对于 ($i=0;$i
# 计算像素值
$tmp = inner($w,$im->slice(':,(i),(j)'));
设置($grey,$i,$j,$tmp); # 并将其设置在灰度图像中
}
}

现在我们使用线程编写相同的代码(注意“inner”是一个线程感知函数
在 PDL::Primitive 包中定义)

$grey = 内部($im,pdl([77,150,29]/256));

我们最终得到了一个单行代码,它会自动创建带有正确的 pdl $grey
维度的数量和大小并自动执行循环(这些循环是
在 PDL 内部实现为快速 C 代码)。 好吧,我们还欠你一个
解释如何实现这种“魔法”。

创新中心 这些因素包括原料奶的可用性以及达到必要粉末质量水平所需的工艺。 例子 工作 ?
首先要注意的是,每个线程感知的函数(这些都没有
从 PDL::PP 的简明描述编译的异常函数,后来称为 PP-
函数)需要定义的(最小)维数(我们称之为核心维数)
从它的每个 pdl 参数。 内部函数需要两个一维(输入)
它从中计算零维(输出)参数的参数。 我们这样写
象征性地为“inner((n),(n),[o]())”并称之为“inner” 签名, 其中 n 代表
该维度的大小。 n 在第一个和第二个参数中相等意味着
在任何调用中,这些尺寸都必须具有相同的大小。 作为一个不同的例子,
使用两个一维向量生成二维矩阵的外积,符号写为
“外((n),(m),[o](n,m))”。 两个例子中的“[o]”表示这个(这里是第三个)
参数是输出参数。 在后一个例子中,第一个和第二个的维度
论点不必同意,但您会看到它们如何确定两个维度的大小
输出 pdl。

这是线程最终进入游戏的关键。 如果你调用 PP 函数
pds 有 更多 比所需的核心尺寸 pdl 的第一个尺寸
参数用作核心尺寸,附加的额外尺寸是螺纹
超过。 让我们先用上面的例子来证明这一点

$grey = 内部($im,$w); # w 是上面的权重向量

在这种情况下,$w 是 1D,因此仅提供核心尺寸,$im 是 3D,更多
特别是“(3,x,y)”。 第一个维度(大小为 3)是所需的核心维度
匹配(根据内部要求)$w 的第一个(也是唯一一个)维度。 第二
尺寸是第一个螺纹尺寸(尺寸为“x”),第三个是这里的第二个
螺纹尺寸(尺寸“y”)。 输出 pdl 是自动创建的(根据要求
在调用之前将 $grey 设置为“null”)。 输出维度由下式获得
附加 循环 尺寸 (此处为“(x,y)”)到核心输出维度(此处为 0D)到
生成自动创建的 pdl 的最终尺寸(此处为“0D+2D=2D”以生成 2D 输出
大小为“(x,y)”)。

所以上面的命令调用了计算两个内积的核心功能
一维向量 "x*y" 次,$w 和 $im 和所有形式为 "(':,(i),(j)')" 的一维切片
将输出 pdl "$grey(i,j)" 的各个元素设置为每个的结果
计算。 我们可以象征性地写成

$灰色(0,0) = f($w,$im(:,(0),(0)))
$灰色(1,0) = f($w,$im(:,(1),(0)))
.
.
.
$灰色(x-2,y-1) = f($w,$im(:,(x-2),(y-1)))
$灰色(x-1,y-1) = f($w,$im(:,(x-1),(y-1)))

但这是由 PDL 自动完成的,无需编写任何显式 Perl 循环。 我们看
该命令确实创建了一个具有正确尺寸的输出 pdl 并设置了
元素确实是输入图像的每个像素的计算结果。

当涉及更多 pdls 和额外维度时,事情会变得更加复杂。
我们将首先给出螺纹尺寸如何依赖于螺纹尺寸的一般规则
输入 pdls 使您能够找出自动创建的输出 pdl 的维度
(对于任何给定的输入 pdls 集和所讨论的 PP 函数的核心维度)。 这
一般规则乍一看很可能会让人有点困惑,因此我们将开始
用一组进一步的例子来说明用法(希望也能
证明确实有许多实际情况可以使用线程
非常方便)。

A 呼叫 HPMC胶囊 编码 纪律
在我们指出线程的其他技术细节之前,请注意这个要求
使用线程时的编程规则:

为了保持人类的可读性, 评论你的任何不平凡的表达
涉及线程的代码。 最重要的是,对于任何子程序,包括信息在
关于您期望尺寸表示的内容(或尺寸范围)的开始。

作为警告,请查看这个未记录的函数并尝试猜测可能会发生什么:

子查找{
我的 ($im,$palette) = @_;
我的 $res;
索引($palette->xchg(0,1),
$im->long->dummy(0,($palette->dim)[0]),
($res=null));
返回 $res;
}

您是否同意可能难以确定预期的尺寸、目的
例程等? (如果您想了解这段代码的作用,请参见下文)

创新中心 数字 这些因素包括原料奶的可用性以及达到必要粉末质量水平所需的工艺。 循环 尺寸
有几个规则可以让你计算出循环的数量和大小
尺寸(以及输入 pdls 的大小是否符合线程规则)。
任何 pdl 参数的维度都分为以下两组: 核心
尺寸(由 PP 函数定义,见 附录 B 获取 PDL 原语列表)
和额外的维度,包括该 pdl 的所有剩余维度。 例如
使用 pdl 调用带有签名“func((n,m),[o](n))”的函数“func”
"a(2,4,7,1,3)" 为 "f($a,($o = null))" 导致 a 维度的语义分裂
进入:核心维度“(2,4)”和额外维度“(7,1,3)”。

R0 核心尺寸用相应 pdl 的前 N ​​个尺寸标识
参数(并且是必需的)。 任何进一步的维度都是额外的维度,用于
确定回路尺寸。

R1(隐式)循环维度的数量等于额外的最大数量
pdl 参数集的维度。

R2 每个循环维度的大小来自各自的大小
pdl 参数的维度。 循环维度的大小由下式给出
在具有此额外维度的任何 pdls 中发现的最大尺寸。

R3 对于所有具有给定额外维度的 pdls,大小必须等于
循环维度(由上一条规则确定)或 1; 否则你提出一个
运行时异常。 如果 pdl 中额外维度的大小为 XNUMX,则为

执行线程循环。

R4 如果一个 pdl 没有循环维度,在线程循环中这个 pdl 被视为 if
具有大小等于该循环维度大小的虚拟维度。

R5 如果使用输出自动创建(通过在之前将相关的 pdl 设置为“PDL->null”
调用)创建的 pdl 的维数等于
核心输出维度数 + 循环维度数。 核心尺寸
输出维度来自输入 pdls 的相关维度(如指定的
在函数定义中),其他维度的大小等于
从中派生的循环维度的大小。 自动创建的 pdl 将是
物理(除非数据流正在运行)。

在这种情况下,请注意,您可能会遇到分配给包含的 pdls 的问题
大于一个的虚拟尺寸(见上文)。 虽然您的输出 pdl(s) 不包含
首先是任何虚拟尺寸,它们最终可能会以隐式创建的虚拟尺寸结束
尺寸根据 R4.

例如,假设我们有一个(此处未指定)带有签名的 PP 函数:

func((m,n),(m,n,o),(m),[o](m,o))

你用 3 个 pdls "a(5,3,10,11)", "b(5,3,2,10,1,12)" 和 "c(5,1,11,12)" 调用它作为

函数($a,$b,$c,($d=null))

那么循环维度的数量是 3(来自 $b 和 $c 的“R0+R1”),大小为
“(10,11,12)”(由R2); 两个输出核心维度是“(5,2)”(来自
func) 产生大小为“(5)”的 5,2,10,11,12 维输出 pdl $c(参见 R5)和
(自动创建的)$d 以可以表示的方式从“($a,$b,$c)”派生而来
在 pdl 伪代码中为

$d(:,:,i,j,k) .= func($a(:,:,i,j),$b(:,:,:,i,0,k),$c(:, 0,j,k))
0<=i<10, 0<=j<=11, 0<=k<12

如果我们考虑到这些规则再次分析颜色到灰度的转换,我们会注意到
隐式线程的另一个巨大优势。 我们可以用 pdl 调用转换
代表一个像素(im(3)),一行rgb像素("im(3,x)"),适当的彩色图像
(“im(3,x,y)”)或一整叠 RGB 图像(“im(3,x,y,z)”)。 只要 $im 是
形成“(3,...)”自动创建的输出 pdl 将包含正确数量的
维度并包含我们期望的强度数据,因为循环已经
由于 含蓄 穿线. 你可以很容易地说服自己
用颜色像素 $grey 调用是 0D,用一条线它变成 1D 灰色(x),带有图像
我们得到“grey(x,y)”,最后我们得到一个转换后的图像堆栈“grey(x,y,z)”。

让我们通过更多的经历来填充这些一般规则
例子。 读者可以尝试找出具有明确 for- 的等效公式
循环并比较那些使用隐式线程的例程的灵活性
明确的表述。 此外,尤其是在使用多个螺纹尺寸时,它是一个
通过做一些基准测试来检查相对速度的有用练习(我们仍然
得做)。

行中的第一个是稍微重新设计的质心示例,现在使用线程进行编码
心神。

# 线程mult计算质心坐标,也适用于堆栈
$xc = sumover(($im*xvals(($im->dims)[0]))->(2)) /
sumover($im->(2));

让我们一步一步地分析发生了什么。 先说产品:

$prod = $im*xvals(zeroes(($im->dims)[0]))

这实际上适用于 $im 是一维、二维、三维和更高维。 如果 $im 是
一维它只是一个普通的产品(从某种意义上说 $im 的每个元素都是
如果 $im 有更多维度,则乘以“xvals(...)”的相应元素
通过向“xvals(...)”添加适当的虚拟尺寸来完成进一步的线程化
根据 R4。 更重要的是,这两个求和运算展示了如何
使用尺寸操作命令。 快速浏览 sumover 的签名
会提醒您它只会“吞噬”给定输入 pdl 的第一个维度。
但是如果我们想真正计算前两个元素的所有元素的总和呢?
方面? 好吧,没有什么能阻止我们将虚拟 pdl 传递到 sumover 中,在此
case 是通过将“父 pdl”的前两个维度聚集成一个来形成的。 来自
从父 pdl 的角度来看,现在在前两个维度上计算总和,
正如我们想要的那样,尽管 sumover 刚刚完成了其签名指定的工作。 拿到
它 ?

编写这样的代码的另一个技巧:我们故意使用
"sumover($pdl->(2))" 而不是 "sum($pdl)" 这样我们可以只传递一个图像
"(x,y)" 或一堆图像 "(x,y,t)" 进入这个例程,然后只得到一个
x-coordiante 或 x 坐标向量(大小为 t)作为回报。

另一组常见操作可以称为“投影操作”。 这些
操作将 ND pdl 作为输入并返回 (N-1)-D “投影” pdl。 这些操作
通常使用 sumover、prodover、最小值和最大值等函数执行。 使用
再次以图像为例,我们可能想要计算每行的最大像素值
图像或图像堆栈。 我们知道怎么做

# 行的最大值(作为行号和时间的函数)
最大值($stack,($ret=null));

但是如果你想在隐式线程总是适用的情况下计算每列的最大值怎么办
第一维的核心功能和所有其他维度的线程? 我们怎样才能
实现将核心功能应用到第二维
线程是在其他线程上完成的。 你能猜到吗? 是的,我们制作了一个具有
使用“mv”命令将“parent pdl”的第二个维度作为其第一个维度。

# 列的最大值(作为列号和时间的函数)
最大值($stack->mv(1,0),($ret=null));

并且计算第三维上所有子切片的总和现在几乎太容易了

# 时间像素总和(假设时间是第三个暗淡)
sumover($stack->mv(2,0),($ret=null));

最后,如果您想将操作应用于所有元素(例如 max over all elements 或
对所有元素求和)无论有问题的 pdl 的尺寸如何,“团块”都出现了
派上用场。 作为一个例子,看看“sum”的定义(在“Ufunc.pm”中定义):

子和{
PDL::Ufunc::sumover($name->clump(-1),($tmp=null));
返回 $tmp->at(); # 返回一个 Perl 编号,而不是一个 0D pdl
}

我们已经提到所有基本操作都支持线程和赋值是没有的
例外。 所以这里有几个线程分配

pdl> $im = zeroes(byte, 10,20)
pdl> $line = exp(-右值(10)**2/9)
#线程分配
pdl> $im .= $line # 将 $im 的每一行设置为 $line
pdl> $im2 .= 5 # 将 $im2 的每个元素设置为 5

到现在为止,您可能已经了解了它的工作原理和作用,不是吗?

为了完成本段中的示例,这里是一个用于创建 RGB 图像的函数
所谓的调色板图像。 调色板图像由两部分组成:
索引到颜色查找表和颜色查找表本身。 [描述它是如何
工作 ] 我们将使用一个我们在之前还没有遇到的 PP 函数
例子。 它是恰当命名的索引函数,签名“((n),(),[o]())”(见 附录
B) 的核心功能是“index(pdl (0,2,4,5),2,($ret=null))”将返回
第一个输入 pdl 的索引为 2 的元素。 在这种情况下,$ret 将包含值 4。
所以这里是例子:

# 线程索引查找以生成 RGB、RGBA 或 YMCK 图像
# 来自调色板图像(由查找表 $palette 和
# 颜色索引图像 $im)
#你可以说只是 假的(0) 因为穿线规则使它适合
pdl> 索引($palette->xchg(0,1),
$im->long->dummy(0,($palette->dim)[0]),
($res=null));

让我们通过它并解释所涉及的步骤。 假设我们正在处理一个 RGB
查找表 $palette 的大小为“(3,x)”。 首先我们交换调色板的尺寸
这样循环是在 $palette 的第一个维度上完成的(大小为 3,代表 r,
g 和 b 组分)。 现在查看 $im,我们添加一个大小等于
组件数量的长度(在我们在这里讨论的情况下,我们可以只
使用数字 3,因为我们有 3 个颜色分量)。 我们可以使用虚拟维度,因为
对于红色、绿色和蓝色颜色分量,我们使用与原始图像相同的索引,
例如,假设 $im 的某个像素的值为 4,那么查找应该产生
三重

[调色板(0,4),调色板(1,4),调色板(2,4)]

用于输出图像的新红色、绿色和蓝色分量。 希望现在你已经
某种想法上面的代码应该做什么(实际上通常是
详细描述一段线程代码的工作原理相当复杂; 前进吧
并尝试一下以获得更好的感觉)。

如果您仔细阅读了线程规则,那么您可能已经注意到我们没有
必须明确说明我们为 $im 创建的虚拟维度的大小; 什么时候我们
创建大小为 1(默认值)的线程规则使其自动适合
所需的大小(根据规则 R3,在我们的示例中,假设调色板为 3
大小“(3,x)”)。 由于这种情况在实践中经常发生,这就是为什么
引入了规则 R3(使尺寸 1 的尺寸适合螺纹的部分
循环暗淡尺寸)。 所以我们只能说

pdl> 索引($palette->xchg(0,1),$im->long->假的(0),($res=null));

同样,您可以说服自己,如果调用此例程将创建正确的输出
一个像素($im 是 0D),一条线($im 是 1D),一个图像($im 是 2D),...,一个 RGB 查找
表(调色板是“(3,x)”)和RGBA查找表(调色板是“(4,x)”,参见例如OpenGL)。
这种灵活性是通过线程的规则来实现的,这些规则是为了做正确的事
大多数情况下的事情。

再总结一下,大致思路如下。 如果你想达到
在某些维度上循环并具有 核心 功能 应用于另一个
指定的一组尺寸,您可以使用尺寸操作命令创建(或
一些) 虚拟 pdl(s) 以便从 pdl(s) 你得到了什么
你想要(总是有相关函数的签名和 R1-R5 记住!)。
很简单,不是吗?

输出 自动创建 PP-功能 调用 公约
在这一点上,我们必须转移到一些与一般情况有关的技术细节上
PP 函数的调用约定和输出参数的自动创建。
基本上,有两种调用 pdl 例程的方法,即

$结果 = 函数($a,$b);



函数($a,$b,$结果);

如果您只使用隐式线程,那么输出变量可以自动
由 PDL 创建。 您可以通过将输出参数设置为
从调用返回的函数“PDL->null”返回的特殊类型的 pdl
一个本质上“空”的 pdl(对于那些对细节感兴趣的人,C pdl 中有一个标志
结构)。 创建的 pdl 的尺寸由以下规则确定
隐式线程:第一个维度是核心输出维度
附加螺纹尺寸(依次由螺纹尺寸决定)
如上所述输入 pdls)。 所以你可以说

func($a,$b,($result=PDL->null));

or

$结果 = 函数 ($a,$b)

哪个是 究竟 当量。

请注意,您可以 而不去 使用显式线程时使用输出自动创建(对于
原因在下一节中解释 明确的 穿线, 的第二个变体
穿线)。

在“紧密”循环中,您可能希望避免隐式创建临时 pdl
循环的每一步都伴随着“功能”风格,而是说

# 仅在第一次调用时创建适当大小的输出 pdl
$结果=空;
对于 (0...$n) {
func($a,$b,$result); # 除了第一次调用之外的所有 $result
func2($b); # 已定义并具有合适的大小
# 在 $b 的暗淡不变的情况下取输出
twiddle($result,$a); # 做一些从 $result 到 $a 的迭代
}

本节再次强调:注意输出限制
使用时创建 明确的 穿线.

明确的 穿线
到目前为止只讨论了线程的第一种风格,现在是时候开始了
介绍第二种变体。 而不是一直在尺寸周围洗牌
依靠隐式线程的规则来让它一切顺利,你有时可能想要
以更明确的方式指定如何执行线程循环。 这可能不是太
令人惊讶的是,这个游戏的变体被称为 明确的 穿线. 现在,在我们之前
造成错误的印象:两者都不是 含蓄 or 明确的; 两种口味都可以
混合。 但稍后会详细介绍。

具有显式线程的两个最常用的函数是 thread 和 unthread。 我们开始
举例说明前者的典型用法:

[ # ** 这是最糟糕的例子]
# 但可以用来表示 $mat += $line 与
# $mat->(0) += $行
# 显式线程将向量添加到矩阵的每一列
pdl> $mat = zeroes(4,3)
pdl> $line = pdl (3.1416,2,-2)
pdl> ($tmp = $mat->(0)) += $线

在这个例子中,“$mat->(0)" 告诉 PDL 你想要这个的第二个维度
pdl 首先被线程化,导致一个线程循环,可以表示为

对于 (j=0; j<3; j++) {
对于 (i=0; i<4; i++) {
垫(i,j)+= src(j);
}
}

“线程”将数字列表作为参数,明确指定要使用的维度
先穿线。 随着显式线程的引入,pdl 的尺寸是
从概念上分为三个不同的组,后两个我们已经
遇到:螺纹尺寸、芯尺寸和额外尺寸。

从概念上讲,最好考虑在 pdl 中指定的那些维度
对“线程”的调用被从正常维度集合中取出并放在一个
单独的堆栈。 所以假设我们有一个 pdl "a(4,7,2,8)" 说

$b = $a->thread(2,1)

创建一个新的维度为“b(4,8)”的虚拟 pdl(我们称之为剩余的暗淡)
也有 2 个尺寸为“(2,7)”的螺纹尺寸。 为了本文档的目的,我们写
象征性地为“b(4,8){2,7}”。 与前面的例子的一个重要区别是
仅使用隐式线程是核心尺寸匹配的事实
这些因素包括原料奶的可用性以及达到必要粉末质量水平所需的工艺。 其余 尺寸 这不一定是 pdl 的第一个维度。 我们
现在将指定螺纹尺寸的存在如何改变螺纹的规则 R1-R5
循环(适用于没有任何 pdl 参数有任何线程的特殊情况
方面)。

T0 核心尺寸与前 n 个匹配 其余 尺寸 pdl的
参数(注意与 R1 的区别)。 进一步 其余 尺寸 旨在 额外
尺寸 并用于确定 含蓄 循环 尺寸.

T1a 数量 含蓄 循环 尺寸 等于额外的最大数量
pdl 参数集的维度。

T1b 数量 明确的 循环 尺寸 等于最大线程数
pdl 参数集的维度。

T1c 总人数 循环 尺寸 等于 明确的 循环 尺寸
含蓄 循环 尺寸. 在线程循环中, 明确的 循环 尺寸 旨在
先穿线,然后是 含蓄 循环 尺寸.

T2 每个的大小 循环 尺寸 是从各自的大小派生的
pdl 参数的维度。 它由在任何 pdls 中找到的最大大小给出
具有此螺纹尺寸(对于 明确的 循环 尺寸) 或额外维度(对于
含蓄 循环 尺寸).

T3 此规则适用于任何 明确的 循环 尺寸 以及任何 含蓄 循环
尺寸. 对于所有具有给定的 pdls 线程/额外 尺寸 尺寸必须是
等于各自的大小 显式/隐式 循环 尺寸 或 1; 否则
你引发了一个运行时异常。 如果一个大小 线程/额外 尺寸 pdl 是一个
它被隐式地视为大小等于 显式/隐式
循环 尺寸.

T4 如果 pdl 没有 线程/额外 尺寸 对应于
显式/隐式 循环 尺寸, 在线程循环中,此 pdl 被视为具有
大小等于该循环维度大小的虚拟维度。

T4a 确实有的所有 pdls 尺寸 必须有相同数量的线程
尺寸。

如果任何 pdl 参数有任何值,则无法使用 T5 输出自动创建
尺寸. 否则 R5 适用。

相同的限制适用于隐式虚拟维度(由
T4 的应用),如隐式线程部分中已经提到的:如果有
输出 pdls 具有(显式或隐式创建)大于一个的虚拟维度 a
将引发运行时异常。

让我们在一般情况下演示这些规则。 假设我们有一个(这里
未指定)带有签名的 PP 函数:

func((m,n),(m),(),[o](m))

你用 3 个 pdls "a(5,3,10,11)", "b(3,5,10,1,12)", "c(10)" 和一个输出 pdl
“d(3,11,5,10,12)”(可以在这里 而不去 自动创建)作为

func($a->thread(1,3),$b->thread(0,3),$c,$d->thread(0,1))

从 func 的签名和上面的调用中,pdls 分为以下几组
核心、额外和线程维度(以“pdl(core dims){thread dims}[extra
变暗]”):

一(5,10){3,11}[] b(5){3,1}[10,12] c(){}[10] d(5){3,11}[10,12]

有了这个来帮助我们(一般来说,像这样写下论点是有帮助的
当您开始玩线程并想要跟踪正在发生的事情时)我们
进一步推导出显式循环维数为 2(通过 $a 和 $b 中的 T1b)
大小为“(3,11)”(按 T2); 大小的 2 个隐式循环维度(来自 $b 和 $d 的 T1a)
"(10,12)" (by T2) 和 的元素是从输入 pdls 计算的
用 pdl 伪代码表示为

对于 (l=0;l<12;l++)
对于 (k=0;k<10;k++)
for (j=0;j<11;j++) 将其视为虚拟暗淡的效果(索引 j)
对于 (i=0;i<3;i++) |
d(i,j,:,k,l) = func(a(:,i,:,j),b(i,:,k,0,l),c(k))

呃,这个例子在记账方面真的很不容易。 它主要用作
例如,当您遇到复杂的外观时如何弄清楚发生了什么
表达。 但是现在是时候通过提供更多内容来展示线程的有用性了
我们所谓的“实际”例子。

[以下示例将来需要一些额外的解释。 为了
请尝试接受代码片段中的注释。 ]

例如1:

*** 由 eigvecs 和 eigvals 表示的矩阵的逆
** 给定一个对称矩阵 M = A^T x diag(lambda_i) x A
** => 逆 M^-1 = A^T x diag(1/lambda_i) x A
** 第一个 $tmp = diag(1/lambda_i)*A
** 然后 A^T * $tmp 由螺纹内积
# 索引处理,以便矩阵在 pdl 下正确打印
$inv .= $evecs*0; # 只需复制以获得适当大小的输出
$tmp .= $evecs; # 初始化,无反向传播
($tmp2 = $tmp->(0)) /= $evals; #线程划分
# 现在是变相的矩阵乘法
PDL::Primitive::inner($evecs->xchg(0,1)->thread(-1,1),
$tmp->thread(0,-1),
$inv->thread(0,1));
# 使用隐式线程替代矩阵 mult,
# 第一个 xchg 仅用于转置
PDL::Primitive::inner($evecs->xchg(0,1)->假的(1)
$tmp->xchg(0,1)->假的(2)
($inv=null));

例如2:

# 线程乘法的外积
# 强调我们需要通过显式调用 my_biop1 来实现
# 使用显式线程时
$res=zeroes(($a->dims)[0],($b->dims)[0]);
my_biop1($a->thread(0,-1),$b->thread(-1,0),$res->(0,1),"*");
# 通过使用自动创建的 pdl 进行隐式线程处理,类似的事情
$res = $a->假的(1) * $b->假的(0);

例如3:

# 不同使用thread和unthread来shuffle多个
# 一次性维度,无需大量调用 ->xchg 和 ->mv

# 使用 thread/unthread 来调整周围的尺寸
# 尝试一下并将子 pdl 与其父项进行比较
$trans = $a->thread(4,1,0,3,2)->unthread;

例如4:

# 计算几个边界框
# $bb 将持有 BB 为 [xmin,xmax],[ymin,ymax],[zmin,zmax]
# 我们再次使用 thread 和 unthread 来调整周围的尺寸
pdl> $bb = zeroes(double, 2,3 );
pdl> 最小值($vertices->(0)->团块->解开(1), $bb->slice('(0),:'));
pdl> 最大值($vertices->(0)->团块->解开(1), $bb->slice('(1),:'));

例如5:

# 计算一个自我配给(即自我归一化)的图像序列
# 使用显式线程和隐式线程划分
$stack = read_image_stack();
# 计算前 $n+1 张图像的平均值(每像素平均值)
$aver = zeroes([stack->dims]->[0,1]); # 使输出 pdl
sumover($stack->slice(":,:,0:$n")->thread(0,1),$aver);
$aver /= ($n+1);
$stack /= $aver; # 通过线程划分来规范化堆栈
# 隐式与显式
# 或者使用隐式线程和自动创建计算 $aver
sumover($stack->slice(":,:,0:$n")->mv(2,0),($aver=null));
$aver /= ($n+1);
#

隐含的 而不是 明确的 穿线
在本段中,我们将说明何时显式线程优于
隐式线程,反之亦然。 但话又说回来,这可能不是最好的方式
既然你已经知道,就把案子放在一边:这两种口味确实混合了。 所以,更多的是关于如何
获得两全其美,无论如何,在最好的 Perl 传统中:TIMTOWTDI!

[抱歉,这个还是要在以后的版本中填写; 要么参考上面的例子
或选择一些新的]

最后,这可能是证明我们一直在进行的所有技术细节合理的好地方
关于几页:为什么要线程?

嗯,使用线程的代码应该(相当)比使用线程的代码快
显式 for 循环(或类似的 Perl 结构)来实现相同的功能。
特别是在超级计算机上(具有矢量计算设施/并行处理)PDL
线程将以利用附加设施的方式实现
这些机器。 此外,它在概念上是一个简单的构造(尽管技术
有时可能会涉及细节)并且可以 非常 降低语法复杂度
PDL 代码(但请记住对文档的警告)。 一旦你感到舒服
穿线 思维方式(和编码)应该不会太难
理解别人写的代码(假设他让你知道什么是
预期的输入维度是等)。 作为提高性能的一般提示
代码:如果您必须在代码中引入循环,请尝试重新表述问题,以便
您可以使用线程来执行循环(就像任何有例外的事情一样)
这个经验法则; 但本文档的作者倾向于认为这些很少见
情况;)。

PDL::PP


An 方式 定义 功能 旨在 察觉 of 索引 穿线 (和 这些因素包括原料奶的可用性以及达到必要粉末质量水平所需的工艺。 宇宙
一切)
PDL:PP 是 PDL 发行版的一部分。 它用于生成可感知的函数
索引和线程规则来自非常简洁的描述。 它对你有用,如果
你想编写自己的函数,或者如果你想从一个函数接口
外部库,以便它们支持索引和线程(可能还有数据流,
请参见 PDL::Dataflow)。 有关更多详细信息,请查看 PDL::PP。

附录 A


仿射 转换 - a 特别 of 简单 强大 转换
[ 这也是将来版本中要添加的内容。 我们是否已经有了将军
PDL 中的 make_affine 例程? 我们可能会参考另一个合适的人
页面从这里]

附录 B


签名 of PDL::PP 编译 功能
选择 PDL 原语的签名以显示 PP 编译了多少维
函数吞噬了(因此您可以弄清楚将被线程化的内容)。 大多数
这些函数是“primitive.pd”中定义的基本函数

#primitive.pd 中的函数
#
求和 ((n),[o]())
普罗多弗 ((n),[o]())
轴值 ((n)) 就地
内部 ((n),(n),[o]())
外 ((n),(m),[o](n,m))
内部重量((n),(n),(n),[o]())
内部 2 ((m),(m,n),(n),[o]())
内2t ((j,n),(n,m),(m,k),[o]())
索引 (1D,0D,[o])
最小值 (1D,[o])
最大值 (1D,[o])
wstat ((n),(n),(),[o],())
分配 ((),())

# 基本操作
二元运算 ((),(),[o]())
一元运算 ((),[o]())

著者 & 版权


版权所有 (C) 1997 Christian Soeller (c.soeller@auckland.ac.nz) & Tuomas J. Lukka
(lukka@fas.harvard.edu)。 版权所有。 虽然注定要作为手册页发布
使用标准的 PDL 发行版,它不是公共领域。 许可被授予
自由分发本文档的逐字副本,前提是没有外部修改
格式,并且此通知保持不变。 你被允许并且
鼓励在您自己的源代码中使用它的代码及其派生类来获得乐趣或
您认为合适的利润。

使用 onworks.net 服务在线使用 PDL::Indexingp



最新的 Linux 和 Windows 在线程序