Julia: 认真对待向量转置

创建于 2013-11-10  ·  417评论  ·  资料来源: JuliaLang/julia

来自@alanedelman

我们真的应该仔细考虑向量的转置应该如何调度各种A_*op*_B*方法。 必须避免使用新的类型和丑陋的数学。 例如,向量'向量产生向量(#2472,#2936),向量'产生矩阵和向量'产生矩阵(#2686)都是不好的数学。

在数学上对我有效(避免引入新类型)的是一维Vector v

  • v'是空操作(即,仅返回v ),
  • v'vv'*v是标量,
  • v*v'是一个矩阵,并且
  • v'Av'*A (其中AAbstractMatrix )是向量

一般的_N_维转置可以颠倒索引的顺序。 具有一个索引的向量在转置下应该是不变的。

实际上, v'很少用于隔离,通常在矩阵向量乘积和矩阵矩阵乘积中遇到。 一个常见的例子是构造双线性形式v'A*w和二次形式v'A*v ,它们用于共轭梯度,瑞利商等。

引入一种新的Transpose{Vector}类型的唯一原因是要表示协变向量和协变向量之间的差异,但我认为这种吸引力不足。

arrays breaking design linear algebra

最有用的评论

BAM

所有417条评论

例如,向量'向量产生向量(#2472,#2936),向量'产生矩阵和向量'产生矩阵(#2686)都是不好的数学。

有限维向量空间的双对偶是同构的,不完全相同。 所以我不清楚这是不好的数学。 更重要的是,我们倾向于掩盖数学上同构的事物之间的区别,因为人的大脑善于处理那种滑溜的歧义并做正确的事情。 也就是说,我同意应该对此进行改进,但这并不是因为它在数学上是不正确的,而是因为它很烦人。

如何v' == v ,但是v'*v != v*v呢? 是否比我们认为让x' * y成为自己的运算符更有意义?

有限维向量空间的双对偶是同构的,不完全相同。

(现在说我自己)它不仅是同构的,它自然是同构的,即同构与基础的选择无关。 我想不出一个值得在这种同构和身份之间进行区分的实际应用。 IMO的烦恼因素来自进行这种区分。

是否比我们认为x' * y作为自己的运算符更有意义?

那是我从今天下午与@alanedelman的讨论中得到的印象。

我认为Jeff提出的要求是正确的……开始看起来像x'_y,x_y'赚了更多
比以往任何时候都有意义。

我与@stefan达成暴力协议。 不好的数学并不意味着错误的数学,它是
意思是讨厌的数学。 在技​​术上有很多事情是正确的,但不是很好。

如果遵循这种逻辑,我们有两种选择

x_x仍然是错误...可能带有“也许您要使用点”的建议
或x_x是点积(我不喜欢这种选择)

如果xx'是同一件事,那么如果您希望(x')*y表示dot(x,y) ,则意味着x*y也是dot(x,y) 。 没有办法了。 我们可以将x'yx'*y设为不同的操作,但是我不确定这是个好主意。 人们希望能够以明显的方式将其括起来并使其仍然有效。

我要指出的是,如果我们允许x*x表示点积,则基本上没有回头路可走。 那将在人们的代码中普遍存在,而根除它将是一场噩梦。 因此,无论自然同构与否,这都不是纯数学,我们必须处理一个事实,即计算机中的不同事物是不同的。

这是我喜欢的区分“上元组”和“下元组”的实际讨论:

http://mitpress.mit.edu/sites/default/files/titles/content/sicm/book-ZH-79.html#%_idx_3310

它小心地避免使用诸如“ vector”和“ dual”之类的词,也许是为了避免使人烦恼。 我发现偏导数的应用很有说服力:

http://mitpress.mit.edu/sites/default/files/titles/content/sicm/book-ZH-79.html#%_sec_Temp_453

区分M[1,:]M[:,1]另一个原因是,当前我们的广播行为允许这种非常方便的行为: M./sum(M,1)是列随机的,而M./sum(M,2)是行随机的。 如果我们“修复” norm函数以允许应用程序轻松地遍历行和列,则可以对标准化进行相同的操作。 当然,我们仍然可以有sum(M,1)sum(M,2)返回矩阵,而不是上下向量,但这似乎有些偏离。

我喜欢上下向量的想法。 问题在于,以一种不完全疯狂的方式将其推广到更高的尺寸。 或者,您可以使向量成为特殊情况。 但这也感觉不对。

的确,向上/向下可能是一个单独的理论。 将它们泛化的方法似乎是嵌套结构,它将事物推向不同的方向。 他们很可能没有将它们称为向量的原因。

此外, x*y = dot(x,y)会使*不具关联性,如x*(y*z)(x*y)*z 。 我真的希望我们能够避免这种情况。

是。 对我来说,这是完全不能接受的。 从技术上讲,我的意思是,浮点数*是非关联的,但是它几乎是关联的,而这显然是非关联的。

我们都同意x*x不应该是点积。

问题仍然在于我们是否可以将v'wv'*w视为点积-
我真的很喜欢这种方式。

@JeffBezanson和我聊天

提案如下:

v'是向量的错误(这是mathematica所做的)
v'wv'*w是一个点积(结果=标量)
v*w是一个外部乘积矩阵(结果=矩阵)

行向量和列向量之间没有区别。 我还是喜欢这个
很高兴看到mathematica的先例
来自mathematica: http :
由于Mathematica使用列表表示向量和矩阵的方式,因此您不必区分“行”向量和“列”向量

用户必须注意,没有行向量..句点。

因此,如果M是一个矩阵

M[1,:]*v是一个错误...(假设我们使用M[1,:]是一个标量
该警告可能会建议您尝试dot'*M[i:i,:]

M[[1],:]*vM[1:1,:]*v是长度为1的向量(无论如何,这是茱莉亚的当前行为)

关于https://groups.google.com/forum/#!topic/julia -users / L3vPeZ7kews中密切相关的问题

Mathematica压缩类似标量的数组部分:

m = Array[a, {2, 2, 2}] 


Out[49]= {{{a[1, 1, 1], a[1, 1, 2]}, {a[1, 2, 1], 
   a[1, 2, 2]}}, {{a[2, 1, 1], a[2, 1, 2]}, {a[2, 2, 1], a[2, 2, 2]}}}

In[123]:= Dimensions[m]
Dimensions[m[[All, 1, All]]]
Dimensions[m[[2, 1, All]]]
Dimensions[m[[2, 1 ;; 1, All]]]

Out[123]= {2, 2, 2}

Out[124]= {2, 2}

Out[125]= {2}

Out[126]= {1, 2}

[编辑:代码格式– @StefanKarpinski]

@alanedelman

假设我们使用M [1 ,:]是一个标量

您是说M [1 ,:]只是一个向量吗?

是的对不起我的想法是M [1 ,:]正在处理标量1 :-)

Mathematica使用句号.而不是星号*
然后将整个9码放进去,使(vector。vector)变成标量,这正是我们要避免的
带星号。

毫无疑问,这段时期存在许多问题,其中之一就是它没有
看起来像点产品中的“点”,其中另一个是与
点的“逐点运算”读数,

Unicode确实很好地提供了一个名为“点运算符”的字符
(char(8901))我们可以想象提供

这样我们就可以让(v ⋅ w)成为(v'*w)代名词

总而言之,目前的辩论主题是

  1. 标量索引因此杀死了维
    A[i,:]是向量, A[:,i,j]是向量
  2. 向量索引厚
    A[ i:i , : ]A[ [i], : ]返回具有一行的矩阵
  3. v'wv'*w是向量的点积(类似v*w'是外积)
  4. v'对于矢量是未定义的(将用户指向permutedims(v,1) ????)
  5. 如果A是矩阵,则v*A返回向量
  6. v⋅w也会返回点积(但在矩阵上的运算不会达到mathematica的.
  7. v*w对于矢量是未定义的,但是警告可能会向用户提示一些好的建议,包括

结果是

一种。 如果您坚持将所有向量作为列向量,则一切正常
b。 如果您将所有内容制作成矩阵,那么一切都肯定可以工作,并且很容易将所有内容制作成矩阵
C。 如果您的思维无法将行向量与单行矩阵区分开,那么您很有可能会受到教育
礼貌优雅地
d。 这个点符号让人感到愉悦

建议5)对我来说看起来很奇怪。 我更喜欢v'*A ,这样很明显您正在使用对偶向量。 这在复杂的矢量空间中尤其重要,在复杂的矢量空间中,对偶不仅仅是“形状”变换。

我想回应@StefanKarpinski ,不幸的是,在所有这些方面失去我们简洁的广播行为。 进行此更改之后,采用向量v并将这些值归一化矩阵A的列的简洁语法是什么? 目前,可以使用A ./ v' 。 这对于数据操作非常好。

好问题

我的方案并不排除v'*A接受v的复共轭和A的多重共轭
以及我尚未明确提及的所有其他情况,但很容易

我们可以消除5
也许这是可取的
它不符合我的列向量规则

这种广播方式既可爱又笨拙
现在一种解决方案是A ./ v[:,[1]]

这样做的好处是可以记录在哪个维度上广播
并推广到高维数组

哦, v[:,[1]]解决方案的优点是不采用复共轭
这可能是用户想要的。

我喜欢这两个示例,因为第一个是线性代数示例
非常需要复数共轭的地方,但是第二个例子是
一个多维数据示例,我们希望事物在各个维度上都能正常工作
不只是用于矩阵,而且我们很可能不想复杂的共轭

需要#552。 这是过去两周中第三次出现此消息。

区分M [1 ,:]和M [:,1]的另一个原因是,目前我们的广播行为允许这种非常方便的行为:M./sum(M,1)是列随机的,而M./sum(M, 2)是行随机的。 如果我们“固定”规范函数以允许轻松地在行和列上进行应用,则可以对标准化进行相同的操作。 当然,我们仍然可以使用sum(M,1)和sum(M,2)返回矩阵,而不是上下向量,但这似乎有点不合时宜。

在我看来,尽管有时候播放效果不错,但最终却不得不经常挤压那些多余的单元调光器。 因此,如果系统的其余部分更好,则必须在某些时候做相反的事情(我确实认为降低标量尺寸会使系统更好)。 因此,您将需要一个类似

julia> widen(A::AbstractArray,dim::Int) = reshape(A,insert!([size(A)...],dim,1)...)
# methods for generic function widen
widen(A::AbstractArray{T,N},dim::Int64) at none:1

它将允许使用M ./ widen(sum(M,2),2)A ./ widen(v,1) (请参见上面的@blakejohnson示例)

M [:,0 ,:]和v [:,0] ?????

在减少问题上,我与@blakejohnson在一起。 我个人认为, squeeze尺寸比widen更清楚。 我怀疑我会一直在看文档,以确定widen是在指示的索引处还是在指示的索引后插入维,如果您想一次扩展多个维,则编号会变得有些复杂。 (这是什么widen(v, (1, 2))为向量v吗?)这些都不是问题squeeze

无论我们是扩大还是缩小默认值,我都认为Julia在扩大范围时应该遵循numpy的原则,并允许v[:, newaxis] 。 但是我确实相信我更喜欢保留尺寸而不是放弃尺寸,要发现一个错误,与以错误的方式挤压(通常会产生错误)相比,在错误地以错误的方式加宽错误的难度更大。

在@alanedelman的列表中
我觉得

如果A是矩阵,则v * A返回向量

不好

如果A不为1x1(索引范围不匹配),则v_A应该为错误
v'_A应该是正确的方法。

解决此问题的一种方法是将向量v自动转换为nx1矩阵(需要时)
并始终将v'视为1xn矩阵(切勿将其转换为向量或nx1矩阵)
另外,我们允许将1x1矩阵自动转换为定标器编号(需要时)。

我觉得这代表了关于线性代数的一致一致的思考方式。 (好的数学)

处理所有这些问题的统一方法是允许自动(类型?)转换(需要时)
在大小为(n),(n,1),(n,1,1),(n,1,1,1)的数组之间等(但不在大小为(n,1)和(1,n)的数组之间)
(就像我们在需要时将实数自动转换为复数一样)

在这种情况下,大小为(1,1)的数组可以转换为数字(需要时)(请参阅#4797)

小刚(物理学家)

这留下v'_A但是...。我真的希望v'_A * w工作

我对Julia的线性代数的印象是,它的结构非常像矩阵代数,尽管存在标量和真实向量(我认为这很好!)

让我们考虑如何处理像x*y*z*w这样的乘积,其中每个因子可以是标量,向量或矩阵,并且上面可能有转置。 矩阵代数定义矩阵的乘积,其中矩阵的大小n x m 。 一种方法是扩展此定义,以便可以用absent代替nm ,就计算产品而言,它的作用就像一个值,但用于区分标量和向量与矩阵:

  • 标量将是absent x absent
  • (列)向量将是n x absent
  • 行向量将是absent x n

理想情况下,我们希望对事物进行排列,以使我们永远不必表示行向量,但足以实现x'*yx*y' 。 我感到这是我们许多人正在寻找的方案的味道。

但是我开始怀疑在这种方案中禁止行向量会带来很高的代价。 示例:考虑一下我们将如何对产品加括号以避免在任何中间步骤形成行向量:( a是标量, uv是向量)

a*u'*v = a*(u'*v) // a*u' is forbidden
v*u'*a = (v*u')*a // u'*a is forbidden

要在避免产生行向量的情况下评估乘积x*y'*z ,我们需要在选择乘法顺序之前知道因子的类型! 如果用户自己做,这似乎是通用编程的障碍。 而且我不确定Julia如何以理智的方式自动执行此操作。

另一个不预先确定乘法顺序的原因:我似乎记得有个想法,使用动态编程来选择*(x,y,z,w)的最佳评估顺序,以最大程度地减少所需的运算次数。 我们为避免形成行向量所做的任何事情都可能会干扰这一点。

因此,现在,引入转置向量类型似乎是我最明智的选择。 那样做,或像现在那样做,但是在保留单身尺寸时保留它们会导致错误。

换位只是置换模式的一种特殊方式。 如果您允许v.' ,其中v是向量,则permutedims(v,[2 1])应该返回完全相同的东西。 要么都返回一种特殊的行向量类型,要么它们引入了新的维度。

对行向量有特殊类型对我来说似乎不是一个好的解决方案,因为对于其他类型的模式n向量,例如permutedims([1:4],[3 2 1]) ,您会怎么做? 我敦促您在做出决定之前也要考虑多线性代数。

@toivoh提到

“一种方法是扩展此定义,以便用不存在的n或m代替它,就计算乘积而言,它的作用就像一个值,但用于区分标量和向量与矩阵:

  1. 标量将不存在x不存在
  2. (列)向量将不存在nx
  3. 行向量将不存在xn“

在多线性代数中(或对于高rand张量),以上提议对应于使用来表示
范围1的许多索引,即大小(m,n,不存在)可能对应于(m,n),(m,n,1),(m,n,1,1)等。

如果我们使用对absent的这种解释,那么1.和2.可以很好地拥有,但是3.可能不是很好。
我们不想混合大小为(1,n)和(1,1,n)的数组。

我不是张量理论的专家,但是我已经使用上述所有系统(没有任何附加软件包)来进行涉及线性代数的大量项目。

[TL; DR:跳至摘要]

这是最常见的情况,在这些情况下,我发现需要比普通的矩阵向量操作更大的数组处理通用性:

(1)功能分析:例如,一旦您使用向量值函数的Hessian,就需要高阶张量来工作。 如果您正在编写大量数学运算,那么在这些情况下必须使用特殊的语法将是一个巨大的痛苦。

(2)评估控制:例如,给定任何可以计算的产品,一个人应该应该能够分别计算该产品的任何子实体,因为一个人可能希望将其与多个不同的子实体结合起来以形成不同的产品。 因此,Toivo关于例如a*u'被禁止的担忧不仅是编译问题,而且是编程问题。 一个更常见的变体是预先计算x'Q以计算二次形式x'Q*y1x'Q*y2 ,...(其中这些必须顺序进行)。

(3)简化代码:在处理映射到多维数据集上的算术运算时,我发现在系统中可以用一两个简短的数组运算代替6-7行难懂的循环或函数映射代码。提供适当的一般性。 更具可读性,而且速度更快。

这是我在上述系统上的一般经验:

MATLAB:核心语言不仅限于常见的矩阵向量操作,因此通常最终会使用索引来编写循环。

NumPy:比MATLAB更通用的功能,但混乱且复杂。 对于几乎每个平凡的问题实例,我都必须参考文档,甚至有时发现我必须自己实现一些我认为应该直观定义的数组操作。 似乎系统中有很多独立的想法,因此任何给定的用户和开发人员都很难自动猜测其他人对某事的看法。 通常可以找到一种短而有效的方法来完成此操作,但是这种方法对于作者或读者而言并不总是显而易见的。 特别是,我感到对扩展和单例维度的需求仅反映了在应用操作符的实现中缺乏通用性(尽管有些人觉得更直观)。

Mathematica:简洁且通用-特别是,所有相关算子的设计都考虑了高阶张量行为。 除了“点”之外,例如,请参见有关“转置”,“展平/分区”和“内部/外部”的文档。 通过仅组合这些操作,您已经可以涵盖大多数数组变戏法的用例,并且在版本9中,它们甚至还向核心语言中添加了其他张量代数运算。 不利的一面是,即使Mathematica的处理方式是干净且有意义的(如果您知道该语言),它可能显然也不符合执行该操作的常用数学符号。 当然,通用性使得很难知道代码将如何执行。

scmutils:对于功能分析,它是干净,通用的,并且提供以上任何一种方法中最数学上直观的操作(包括写入和读取)。 上/下元组的想法实际上只是人们通常使用移调符号,微分惯例和其他半标准化概念在书面数学中所做的工作的更一致和更一般的扩展; 但一切正常。 (写我的博士学位论文时,我最终开发出一种一致且明确的符号,类似于传统的数学符号,但与Sussman&Wisdom的SICM语法同构。)他们还将其用于微分几何实现[1],它具有启发了一个移植到SymPy的端口[2]。 我没有将它用于数据分析,但是我希望在通用数组上下文中,您只需要一种元组(如Mathematica的List),您可以按照惯例选择一个(“上”)。 同样,通用性使程序员无法考虑性能,但是我希望这是Julia可以擅长的领域。

概要

我认为,建议的转置向量类型应被描述为scmutils中更通用的“向下”元组,而常规向量将是“向上”元组。 称呼他们像“向量”和“转置向量”之类的东西可能比称呼他们“上”和“下”(以简洁为代价)更有意义。 这将支持三类使用:

(1)进行数据分析时,如果人们只需要嵌套数组,则只需要“向量”;
(2)对于基本矩阵向量线性代数,人们可以按照数学惯例直接使用“向量”和“转置向量”(“矩阵”等同于“向量”的“转置向量”);
(3)对于高阶张量运算(其中标准化程度较低,人们通常不得不思考),实现应支持两种元组算术系统的完全通用性。

我相信这种方法反映了上述各种操作结果的新兴共识,不同之处在于那些较早发布的帖子被认为是错误的情况( v'v*A )实际上会有意义(并且通常很有用) )结果。

[1] http://dspace.mit.edu/handle/1721.1/30520
[2] http://krastanov.wordpress.com/diff-geometry-in-python/

@thomasmcoffee听起来就像您提倡在

我会认为这是一个常见的应用程序,但是对于我所提倡的内容却过于具体:对我来说,这具有几何意义,意味着它限制了数字的矩形张量(用于坐标表示)。 因为我想象(没有该领域的专业知识)通常可以使用具有标准数组的张量代数函数库满足此目的,所以我很同情Alan的观点,即仅凭此点就不足以引入两种类型的系统核心语言。

我主要考虑的是依赖于更通用的嵌套结构的其他应用程序,例如,对多维混合参数的函数进行微积分,如果核心语言不支持,以后将很难将其开发为“附加组件”这种区别。 也许我们是指同一件事。

向量向上和向下的问题是您需要将概念扩展到通用数组。 否则,向量将变得特殊且与数组分离,而不仅仅是数组的一维情况,这将导致可怕的问题。 我已经考虑了很多方法,但是还没有提出任何可以接受的方法。 如果您有关于如何将向量的上下合理地推广到数组的任何好主意,我很想听听它们。

只是想推断这个想法。 据我了解,为了使用一个数组来计算上下向量,您需要为每个维度指示它是向上还是向下。 通常,这可以通过将数组包装在类似

immutable UpDownTensor{T, N, UPMASK} <: AbstractArray{T, N}
    A::AbstractArray{T, N}
end

其中UPMASK将是一个位掩码,以指示向上的尺寸。 然后,可以通过提供默认的UPMASK作为N的函数来实现对非包装数组的操作:向量将默认为单个上移,矩阵为第一个上移和第二个下移; 那么我不确定如何合理地继续下去。

一些随机的想法:

  • 二次/双线性形式可以更好地用二维表示吗?
  • 如果tranpose对应于仅翻转每个维度的向上/向下,我想我们也会得到一个转置矩阵类型,其中第一个维度向下,第二个维度向上。
  • 对应于默认值的向上模式可以直接由基础数组表示,而不必包装它。

好吧,这当然是Transposed类型的一种概括,它当然也有优点。 不确定是否可行。

我认为Toivo的建议是我上面所提倡的合理理解。 对我而言,最明智的默认方法是继续以更高的顺序交替显示方向:例如,如果有人将幂级数的组件作为非包装数组提供,这将做对事情。

但是,经过进一步的思考,我认为将这两种思想结合起来可能非常有力:(1)上下向量之间的区别,以及(2)数组和向量之间的区别。 然后,您可能会遇到一些对象,其中某些维度向上,某些维度向下,而有些则是“中性的”。 区分数组和向量的原因是,从语义上讲,数组是用于组织的(收集相同种类的多个事物),而向量是用于协调的(表示多维空间)。 将两个区别组合在一个对象中的能力在于,它可以同时满足这两个目的。 中性维度将根据广播规则进行处理,而向上/向下维度将根据张量算术规则进行处理。

回到我的较早的示例,假设您要为不同的向量y1, y2, ...计算许多二次形式x'Q*y1, x'Q*y2, ... y1, y2, ... 。 继SICM,表示了元组(列矢量)由(...)通过上下的元组(行向量) [...] 。 如果您想一次完成所有操作,并且只限于上/下,则常规方法是使用下元组(of将yi成矩阵Y = [y1, y2, ...]元组),并计算r = x'Q*Y ,这将为您提供下元组r 。 但是,如果要将每个结果乘以(列)向量v怎么办? 您不能只执行r*v ,因为您会得到一个收缩(点积)。 您可以将r转换

相反,假设您还有中性元组(数组),表示为{...} 。 然后,您自然会将ys = {y1, y2, ...}写为一个数组(由向上的元组组成),这样r = x'Q*ys是一个数组,而r*v也是一个数组(由向上元组组成)。 一切都说得通,并且不需要任意转换。

Stefan建议将一维数组与向上/向下向量区分开来是灾难性的,但是我认为这个问题可以通过以下事实解决:大多数函数对数组上的向量或起作用,而不对两个向量起作用。 (或者,在向量数组上的矩阵_or_上,在向量数组上的_or_上,或者在数组数组上的_or_上,但不_other_。等等。)因此,使用适当的转换规则,我没有想到不会这样做的常见情况正确的事情。 也许有人可以?

在深入研究[1]时,我发现scmutils实际上是将它们所谓的“向量”与引擎盖下的上下元组区分开; 但目前已设置了转换规则,以便这些“向量”在进入上/下世界时都映射到上元组(如我之前所建议的),但需要注意的是,“我们保留更改此实现的权利,以区别来自元组的方案向量。” (也许校园里的某个人可以问GJS是否有任何特定的想法。)Sage系统[2]在很大程度上将数组的处理与向量和矩阵分开(目前对张量没有核心支持),这是我遇到的唯一问题这与在显然很有意义的情况下它们之间缺乏内置转换有关。

[1] http://groups.csail.mit.edu/mac/users/gjs/6946/refman.txt-从“结构化对象”开始
[2] http://www.sagemath.org/

我在午餐桌上和@jiahao聊天,他提到Julia小组正在尝试弄清楚如何将线性代数运算推广到高维数组。 两年前,我花了几个月的时间思考这个问题,因为KroneckerBio需要它。 我想分享我的方法。

现在,我们仅考虑两个数组之间的乘积。 其他操作也有类似的概括。 处理数组时,三种最常见的乘积类型是外部乘积,内部乘积和元素乘积。 我们通常会考虑在两个对象之间进行这样的操作,例如inner(A,B)A*B 。 但是,在高维数组上执行这些操作时,它们不是在整个数组之间进行,而是在数组的特定尺寸之间进行。 多个外部/内部/元素子操作在两个数组之间的单个操作中发生,并且每个数组的每个维都必须精确分配给一个子操作(显式或默认)。 对于内部乘积和元素乘积,必须将左侧的一个尺寸与右侧的相同尺寸的尺寸配对。 外部产品尺寸不必配对。 在大多数情况下,用户在做一对尺寸与其他所有尺寸之间的内部产品或元素产品。 外部产品是一个很好的默认设置,因为它是最常见的产品,不需要配对。

我通常认为尺寸是命名的,而不是有序的,很像绘图的x,y和z轴。 但是,如果希望用户实际上能够通过有序索引(例如A[1,2,5]而不是A[a1=1, a3=5, a2=2] )访问数组,则必须具有一致的过程来对操作结果进行排序。 我建议通过列出第一个数组的所有维,然后列出第二个数组的所有维,对结果进行排序。 会挤出所有参与内部产品的尺寸,对于参与元素产品的尺寸,仅第二个数组中的尺寸会被挤出。

我将为此补充一些符号。 快来朱利叶菲吧。 假设Aa1 by a2 by a3的数组,而Bb1的数组b2 。 假设array_product(A, B, inner=[2, 1], elementwise=[3, 2])将采用维度a2b1之间的内部乘积, a3b2之间的元素乘积,以及a1外部乘积。 结果将是一个数组,该数组是a1 × a3

应该清楚的是,在高维数组的上下文中,没有任何二进制或一元运算符会具有很大的意义。 您需要两个以上的参数来指定如何处理每个维度。 但是,可以通过使Matlab运算符简化为仅在前两个维度上进行数组运算的方式来重新获得线性代数的难易程度:

Matlab的A*Barray_product(A, B, inner=[2,1])

Matlab的A.'permute(A, B, [2,1]) ,其中permute保持不变,所有维数都高于第三个参数的计数。

您可以选择在数组的维数大于2甚至不等于2时是否引发错误,就像Mathematica对矢量转置所做的那样。 如果仅使用常规数组计算,则不必决定是否采用@wenxgwen的建议将所有(n,m)个数组解释为(n,m,1)和(n,m,1 ,1)。 仅当使用线性代数运算符或其他期望数组或特定维数的运算符时,才需要做出此决定。 我喜欢@wenxgwen的建议,因为动态类型语言几乎没有什么缺点。

对系统进行了更

感谢您的观点! 我发现这对理解一般的阵列*阵列产品实际上是什么样的野兽很有启发。

将多维数组乘法的建议与PEP 0465中针对矩阵乘法算符提议的语义进行交叉引用可能会很有趣。 特别是:

通过在形状的前面或后面添加“ 1”,将1d向量输入提升为2d,执行该操作,然后从输出中删除添加的尺寸。 1总是添加在形状的“外部”:左参数前面,而右参数后面。 结果是matrix @ vector和vector @ matrix都是合法的(假定形状兼容),并且都返回1d向量。 vector @ vector返回标量...一维向量的定义不明确之处是,在某些情况下,它使@不关联((Mat1 @ vec)@ Mat2!= Mat1 @(vec @ Mat2))。 但这似乎是实用性胜过纯度的情况

在Python中避开键入会引起一个特殊的问题。 自然,数组和矩阵应该是可互换的(相同的基础数据)。 但是由于Python不鼓励类型检查,因此在期望数组的函数开头,矩阵不会转换为正确的接口,反之亦然。 这就是为什么它们必须具有不同的运算符。 使用运行时类型检查和convert方法的Julia不会受到这种歧义的困扰。

从PEP 0465:

一维向量的这种定义的不足之处是在某些情况下它使@不关联((Mat1 @ vec)@ Mat2!= Mat1 @(vec @ Mat2))

值得注意的是,这种定义可能会在Mathematica中产生不正确的结果,因为当以符号方式求值时(如与f一样),假定Dot. )是关联的( Flat )低于f ,但不包含g ):

In[1]:= f=X.(y.Z);
g:=X.(y.Z)

In[3]:= Block[{
X=Array[a,{2,2}],
y=Array[b,2],
Z=Array[c,{2,2}]
},{f,g}]

Out[3]= {{(a[1,1] b[1]+a[1,2] b[2]) c[1,1]+(a[2,1] b[1]+a[2,2] b[2]) c[2,1],(a[1,1] b[1]+a[1,2] b[2]) c[1,2]+(a[2,1] b[1]+a[2,2] b[2]) c[2,2]},{a[1,1] (b[1] c[1,1]+b[2] c[2,1])+a[1,2] (b[1] c[1,2]+b[2] c[2,2]),a[2,1] (b[1] c[1,1]+b[2] c[2,1])+a[2,2] (b[1] c[1,2]+b[2] c[2,2])}}

In[4]:= SameQ@@Expand[%]
Out[4]= False

来自@drhagen

使用运行时类型检查和convert方法的Julia不会受到这种歧义的困扰。

这就是为什么我觉得应该为朱莉娅(Julia _should_)选择正确的解决方案的原因是让数据本身区分类数组语义(用于通用广播)和类张量语义(用于可能的收缩)。

我绝对不是这里的权威,但是我不认为一般的任意维集合类型( Array )应该支持执行点积的运算符。 不能合理地为此类型定义此运算符,因为点积可以在任意两个维之间,需要不能提供给二进制运算符的其他参数。 所有线性代数运算( invtranspose等)也是如此。

要在线性代数的数学领域中进行运算,应该再有三种类型: MatrixColumnVectorRowVector ,在其上所有正常的线性代数运算符和函数正常工作。

现在已经很好地定义了类型结构,您可以通过将MatrixArray{2}ColumnVectorArray{1} ,和RowVectorArray{2} (对此不确定), Array{2}MatrixArray{1}ColumnVector

我上面的建议(https://github.com/JuliaLang/julia/issues/4774#issuecomment-32705055)允许多维结构的每个维度区分是否具有中性(“集合” /“数组”),向上(“列”)或向下(“行”)语义。 我认为您所描述的就是一个特例。

这种通用方法的优点是,即使在具有许多维度的数据或空间的计算中,您也可以使运算符执行正确的操作,而无需明确指定应在哪个维度上进行操作。 我认为我们同意,至少在Julia中,用户通过选择类型参数来指定输入数据的含义要比通过调用每个实例来指定每个操作的含义更为直观和可读。提供尺寸索引的其他参数。 在必须以不同寻常的方式在中间更改含义的情况下,隐含或显式转换仍可以使用,具有全维通用性。

@thomasmcoffee我非常喜欢您的建议。 我在DSL(很久以前,很远)中实现了一些模糊的相似的东西,并带有一些指导原则(又名个人观点):

  1. 对偶概念对于任何明智的自洽语义都至关重要。
  2. 使用参数化运算符(或与此相关的数据外部的任何东西)临时执行张量代数在美学上是非常不愉快的。

我当时最大的抱怨(而且很大声)是关于您的三价语义(添加一个中性的集合概念)所解决的那种不便。 真好! 这个想法对我来说从来没有发生过,但是现在你把它付诸实践就变得非常有意义。 我真的很想使用这样的系统,我的意思是进行实际工作。 如果朱莉娅可以容纳这个,那就太好了!

你们似乎在描述的是规则张量。 我怀疑这是一个足够普通的用例,不足以证明它存在于标准库中,因为其他两个用例(集合和线性代数)更常见。 但是,如果可以无缝集成,我会支持。 您能否举一些例子说明该系统下的一些常见操作,例如矢量矩阵乘法,标量数组乘法,将一个数组的加法分布到一组数组中,等等?

我认为您是对的大卫。 我们实际上是在谈论两个用例。

多数人最常需要线性代数的子集
说。 即使在那儿,我也主张保持v和v'之间的区别。

我真正想要的(在这里插入贪婪的信息)是张量
一流(或接近)状态...接近自然速度(在极限情况下,
相较于线性代数性能),语法简单,不会过度使用
类型问题,数据中编码有协方差/协方差,未强加于
运营商。 一旦定义了数据语义,操作应
正常工作。 张口鸭打字。

也许张量和TDT属于一个包而不是核心,只是在
相对受欢迎的理由。 但是就像朱莉娅的宣言
独立说,朱莉娅生于贪婪。 就像戈登·壁虎(Gordon Gecko)所说,
贪婪是好的。 :)
2014年3月21日上午3:14,“ David Hagen” [email protected]写道:

你们似乎在描述的是规则张量。 我怀疑那是一个
足够普通的用例足以证明存在于标准库中,例如
其他两个用例(集合和线性代数)则更多
共同。 但是,如果可以无缝集成,我会支持。
您能否举例说明一些常见的操作
在这个系统下,例如向量矩阵乘法,标量数组
乘法,将一个数组的相加分布在一个数组上
数组等?

直接回复此电子邮件或在Gi tHub上查看它

我认为,如果有足够多的类型族,那么无缝集成绝对是可以实现的。 在上面扩展Toivo的https://github.com/JuliaLang/julia/issues/4774#issuecomment -32693110时,它在概念上可能像这样开始:

immutable AbstractTensorArray{T, N, UPMASK, DOWNMASK} <: AbstractArray{T, N}
    A::AbstractArray{T, N}
end
# where !any(UPMASK & DOWNMASK)

typealias AbstractColumnVector{T} AbstractTensorArray{T, 1, [true], [false]}
typealias AbstractRowVector{T} AbstractTensorArray{T, 1, [false], [true]}
typealias AbstractMatrix{T} AbstractTensorArray{T, 2, [false, true], [true, false]}

(当前AbstractMatrix{T}只是别名AbstractArray{T, 2} ;可能在这里使用其他名称)

从这里开始,以下实现似乎是合乎逻辑的:

  1. 在重新排列尺寸以及相应的UPMASK和DOWNMASK索引之后,广义的transpose方法会互换UPMASK和DOWNMASK。 中性尺寸不会受到影响。
  2. 默认情况下,通常在张量运算中将任何AbstractArray{T, N}子类型默认转换为相应的交替AbstractTensorArray{T, N, [..., false, true, false, true], [..., true, false, true, false]}子类型。 这保留了Julia对于向量和矩阵的特殊数组语法的现有语义。
  3. AbstractTensorArray的构造函数方法(例如array )用于产生中性尺寸,并且可以组合其他AbstractTensorArray s(或可转换为其的类型)以创建组合的AbstractTensorArray ,中性顶层尺寸。

考虑@drhagen的示例:

向量矩阵乘法,标量数组乘法

c = 1               # Int
v = [1, 2]          # Array{Int, 1}
M = [[1, 2] [3, 4]] # Array{Int, 2}

# scalar-array
c * M               # UNCHANGED: *(Int, Array{Int, 2}) => Array{Int, 2}

# matrix-vector
M * v               # *(Array{Int, 2}, Array{Int, 1}) => *(Matrix{Int}, ColumnVector{Int}) => ColumnVector{Int}

# vector-matrix
v' * M              # transpose(Array{Int, 1}) => transpose(ColumnVector{Int}) => RowVector{Int}
                    # *(RowVector{Int}, Array{Int, 2}) => *(RowVector{Int}, Matrix{Int}) => RowVector{Int}

# (1-array)-(2-array)
v .* M              # UNCHANGED: .*(Array{Int, 1}, Array{Int, 2}) => Array{Int, 2}

(使用Matrix并具有与上述AbstractMatrix定义相对应的定义)

将一个数组的相加分布在一个数组中

我的意思是,在一个向量数组上添加一个向量,在一个矩阵数组上添加一个矩阵,依此类推:

# vector-(vector-array)
ws = array([1, 2], [3, 4])
                    # TensorArray{Int, 2, [false, true], [false, false]}
v + ws              # +(Array{Int, 1}, TensorArray{Int, 2, [false, true], [false, false]}) => +(ColumnVector{Int}, TensorArray{Int, 2, [false, true], [false, false]}) => TensorArray{Int, 2, [false, true], [false, false]}
# => array([2, 4], [4, 6])

# array-(vector-array)
u = array(1, 2)     # TensorArray{Int, 1, [false], [false]}
u + ws              # +(TensorArray{Int, 1, [false], [false]}, TensorArray{Int, 2, [false, true], [false, false]}) => TensorArray{Int, 2, [false, true], [false, false]}
# => array([2, 3], [5, 6])
# alternatively:
v .+ ws             # .+(Array{Int, 1}, TensorArray{Int, 2, [false, true], [false, false]}) => TensorArray{Int, 2, [false, true], [false, false]}
# => array([2, 3], [5, 6])
# same effect, but meaning less clear:
v .+ M              # UNCHANGED: .+(Array{Int, 1}, Array{Int, 2}) => Array{Int, 2}
# => [[2, 4] [4, 6]]

# matrix-(matrix-array)
Ns = array([[1, 2] [3, 4]], [[5, 6] [7, 8]])
                    # TensorArray{Int, 2, [false, false, true], [false, true, false]}
M + Ns              # +(Array{Int, 2}, TensorArray{Int, 2, [false, false, true], [false, true, false]}) => +(Matrix{Int}, TensorArray{Int, 2, [false, false, true], [false, true, false]}) => TensorArray{Int, 2, [false, false, true], [false, true, false]}
# => array([[2, 4] [6, 8]], [[6, 8] [10, 12]])

考虑我前面的示例,将矢量v缩放为几种不同的二次形式x'M*w1, x'M*w2, ... ,以获得最终结果x'M*w1*v, x'M*w2*v, ...

x = v
x' * M * ws * v     # *(RowVector{Int}, Array{Int, 2}) => *(RowVector{Int}, Matrix{Int}) => RowVector{Int}
                    # *(RowVector{Int}, TensorArray{Int, 2, [false, true], [false, false]}) => TensorArray{Int, 1, [false], [false]}
                    # *(TensorArray{Int, 1, [false], [false]}, Array{Int, 1}) => *(TensorArray{Int, 1, [false], [false]}, ColumnVector{Int}) => TensorArray{Int, 1, [false, true], [false, false]}
# => array([27, 54], [59, 118])

在此概念性实现中,我假设AbstractArray是单独放置的,因此AbstractTensorArray在类型层次结构中形成了自己的“空间”。 如果将整个AbstractArray系列简单地替换为AbstractTensorArray ,事情可能会得到简化,但这是另一个讨论。

在为量子物理学提供某种封装的情况下,我一直在尝试定义自己的张量类型(实际上是多个)。 最初,我还具有一些以两种形式出现的索引概念(上,下,传入和传出,协变量和逆变量,但是您要称呼它),这些信息存储在类型为字段或字段中。类型参数。 过了一会儿,我觉得这真是麻烦。 只需将张量的索引与向量空间关联(我已经做过),并允许该向量空间具有不同的对偶就容易得多。 在实践中,矢量空间只是指一个简单的Julia类型,它包装了空间的维数以及它是否是对偶的。 如果张量索引与法线向量空间相关联,则为向上索引;如果张量索引与对偶索引相关联,则为向下索引。 您是否要使用没有区别的张量/数组,您只是定义了一个不区分法向矢量空间和对偶矢量空间的矢量空间类型。

在此建议中,您只能收缩与彼此成对的向量空间相关联的张量索引。 ctranspose(= Hermitian共轭)将每个索引的向量空间映射到其对偶空间(在矩阵的情况下,以及索引的排列,以及高阶张量的首选定义)等。

当然,在这种情况下,正常换位和复杂共轭的定义不是很好(即,这些不是与基础无关的概念)

简而言之,它看起来像这样:

immutable Space
    dim::Int
    dual::Bool
end
Space(dim::Int)=Space(dim,false) # assume normal vector space by default
dual(s::Space)=Space(s.dim,!s.dual)

matrix=Tensor((Space(3),dual(Space(5))))
# size is no longer sufficient to characterise the tensor and needs to be replaced by space
space(matrix) # returns (Space(3),dual(Space(5))) 
space(matrix') # returns (Space(5),dual(Space(3)))

当然,您可以发明一些语法,以便不必不断编写Space。 您可以为double(s)== s创建一个不同的空间类型,以使张量不区分上下索引,依此类推。 但是当然,在不破坏所有内容的情况下,仍然无法将其构建到Julia的标准Array类型中...

我一直想知道为什么在工程/物理中使用张量与在数学软件程序中如何处理张量之间没有更紧密的关系。 我发现了一个有关该主题的有趣的堆栈交换对话... http://math.stackexchange.com/questions/412423/differences-between-a-matrix-and-a-tensor。 在这里,也是一个很好的参考文章。
http://www.mat.univie.ac.at/~neum/physfaq/topics/tensors

我在日常的科学计算中经常使用Matlab,但在Julia上却是新手。 在这里,我注意到有很多关于高维数组乘法和转置或其他类似数组操作的讨论。 我建议看一下http://www.mathworks.com/matlabcentral/fileexchange/8773-multiple-matrix-multiplications-with-array-expansion-enabled

它基本上遵循与@drhagen在先前的文章中提到的语法类似的语法,例如对数组A和B之间的乘积的array_product(A,B,inner_A_dim = [1,2],inner_B_dim = [3,4]) 。给定的内部尺寸。

这是一个Matlab软件包,可以在某些选定的维度上应用乘法或换位运算。 软件包中有一本手册,介绍如何在Matlab中实现这些操作,但我认为数学理论也应适用于其他语言。 他们的想法是通过避免使用For循环来实现数组操作,并且主要依靠数组重塑等。 因此,它在Matlab中非常快。 我不知道Julia是否更喜欢矢量化操作或去矢量化操作(似乎是后者)。 我感觉如果内核支持,矢量化操作对于高维数组操作是一个优势。 也许我们现在应该真诚地考虑这种数组操作。

供您参考:在Matlab中用于INV操作的另一个类似实现在这里: http :

另请注意,自2005年发布Matlab阵列操作支持包以来,下载记录一直保持较高水平。 根据我的实际经验,数组运算在物理学和其他领域中非常常用。 我想说,如果Julia具有类似的功能来操作任意大小的数组,那么游戏将变得非常有趣!

在这里再次投票表决@alanedelman向高层提出的解决方案。 这是一个激励人心的例子。

现在,行切片是2d数组,列切片是1d数组; 这是奇怪的不对称和丑陋的:

julia> A = randn(4,4)
4x4 Array{Float64,2}:
  2.12422    0.317163   1.32883    0.967186
 -1.0433     1.44236   -0.822905  -0.130768
 -0.382788  -1.16978   -0.19184   -1.15773
 -1.2865     1.21368   -0.747717  -0.66303

julia> x = A[:,1]
4-element Array{Float64,1}:
  2.12422
 -1.0433
 -0.382788
 -1.2865

julia> y = A[1,:]
1x4 Array{Float64,2}:
 2.12422  0.317163  1.32883  0.967186

特别是,这意味着我不能在不做一些非常丑陋的操作的情况下将一行乘以一列并提取数字

julia> dot(y[:],x)
2.4284575954571106
julia> (y*x)[1]
2.42845759545711

除非您将'*用作特殊运算符,否则这不是一个一致的建议,因为这很可疑,因为那样的话x'*y(x')*y并不意味着同一件事。 而且,这将使乘法不具有关联性。

我了解x_y'和y'_x的困难。 最好治疗
内部和外部乘积作为单独的操作,例如使用dot()。 (也许
还使用cdot吗?)

但是,赞成在第一个切片上进行切片的论点是什么?
维返回一个对象,该对象的维数与沿其切片的维数不同
第二个维度? 为了保持一致性,似乎每次切片时,
最终对象的尺寸应减少一倍。

2014年7月16日,星期三,晚上8:17,Stefan Karpinski [email protected]
写道:

除非您将'*设为特殊运算符,否则这不是一个连贯的建议,
是非常可疑的,因为从那时起x'_y和(x')_ y并不意味着同一件事。
而且,这将使乘法不具有关联性。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/4774#issuecomment -49254346。

马德琳·乌德尔(Madeleine Udell)
计算与数学工程博士研究生
斯坦福大学
www.stanford.edu/~udell

@madeleineudell ,我同意你的意见,但这是一个不同的问题,请参阅#5949。 尽管该问题似乎已经结束,但我不记得有明确的协议或结论。

一旦我们切换到数组视图,探索这些方向将变得更加容易。 特别是说slice(A, i, :)可以使您获得想要的行为。 (它现在就这样做了,但是是以引入较慢的类型SubArray为代价的。)

从纯粹的数学观点来看,这里提出的所有问题都来自我们数组的含义与向量/张量/矩阵的含义的混淆(以及它们之间的混淆)。 从概念上讲,数组只是列表(或者,对于n-dim数组,列表是列表)。 因此,对于诸如数组乘法,转置等操作没有自然规范。尽管诸如permutedims,逐元素操作和特定于轴的操作(均值,中位数等)之类的功能有意义,并且可以在自然的方式,点产品之类的操作是不可能的。

如上所述,向量和张量是几何对象,尽管可以使用数组来表示它们,但这些表示并不包含与其所代表的数学对象相同的结构丰富性。 一维数组的转置是无操作的; 向量的转置为其对偶。 2维数组的转置可以唯一且自然地定义为其维数的排列,但对于张量通常不是这样:虽然自然情况适用于秩(1,1)张量(又称矩阵),但等级(2,0)张量转置为等级(0,2)张量。 同样,通过将张量视为数组,构成张量张量的几何信息会丢失。

在定义点积之类的操作时,这一点很重要。 点积具有特定的几何含义(一个向量在第二个向量定义的对偶空间上的投影),因此点积的一致定义需要保留向量中包含的几何信息。 使用某些假设可能使使用数组并仍然涵盖大多数用例成为可能,但是这些假设比较杂乱(如该线程的各种建议所见),实际上对于需要更丰富张量结构的任何人来说都变得更加困难。

因此,请认为这是对thomasmcoffee包含更丰富的AbstractTensor类型的建议的有力投票。 我个人的喜好是,甚至没有为数组定义诸如换位和点积之类的操作,但是由于我怀疑大多数人不会同意这种观点,因此我至少希望能够在需要时创建真正的张量。

这种观点的实际含义似乎是,应该用张量的子集来标识数组,而转置一维数组应该给出DualVector或错误。 我的观点是,这类似于对给出复数的实数进行运算。

我的观点是,通用的AbstractArray系列(一个多维的数据容器)具有足够的通用性,可以成为任何技术编程语言中不可或缺的一部分。 即使我很在意它,遵循严格的数学规则的张量对于专用程序包也是一个很好的对象。 实际上,我正在按照@jdbateshttps://github.com/Jutho/TensorToolbox.jl中指定的方式进行操作。 到目前为止,该文件尚未记录,并且未经测试。 我写这篇文章是为了满足量子许多人体物理学中我个人需要的东西,但是我希望它以足够通用和可扩展的方式构造,从而对关心张量的更大范围的数学家和物理学家有用。

给出一些细节(从JuliaQuantum论坛复制):我决定为张量定义一个新的类型层次结构,它独立于Julia的AbstractArray类型(尽管基本Tensor只是Array的包装)。 这种类型的层次结构应该以一种更为正式的方式工作。 张量索引与向量空间(以下称为索引空间)相关联,并且如果与张量索引关联的向量空间的类型与其对偶向量不同,则这对应于区分协变索引和反变量索引的张量。

因此,程序包的第一部分是用于定义向量空间的抽象部分,在这里我将Julia对象的类型层次结构与向量空间的数学层次结构进行匹配。 一般向量空间V有四个变种,对应于V上一般线性群的表示理论,即V本身(基本表示),conj(V),dual(V)和dual(conj(V))。 对于实向量空间,conj(V)= V,并且只有V和对偶(V),分别对应于协变和协变向量。 然后是内部乘积空间,在层次结构的顶层是欧几里得空间,这是具有标准欧几里得内积(即正交基)的内部乘积空间。 在物理学中,考虑被分解为不同扇区的向量空间也很有用,例如,它们通过例如对称动作的不可约表示来进行分级。

张量是生活在一些基本矢量空间的张量积(的子空间)中的对象。 但是,除了存在于其索引空间的张量积空间中的对象标准Tensor之外,还可以构建一些张量,它们生活在例如由irrep分级的空间,张量的对称或反对称子空间的张量积的不变扇区中。相同空间的张量积......可能具有费米子向量空间作为索引空间,这意味着张量索引的排列将根据奇偶校验扇区等引起某些符号变化。

然后,应该在张量上定义某些操作,其中最重要的是收缩张量,但例如正交分解(奇异值分解)等。最后应该有将一个张量映射到另一个张量的线性映射。 它们值得一种特殊的类型,因为它通常不想完全将它们编码为矩阵,而是以一种可以有效地计算矩阵向量乘积的方式,用于迭代方法(Lanczos等)。 到目前为止,我的两个现有软件包(TensorOperations.jl和LinearMaps.jl)为标准数组实现了此功能,正在构建的张量工具箱将为新的AbstractTensor层次结构重载/重新定义它们。

我希望该软件包具有足够的通用性,以便对更广泛的物理学/数学界有用。 例如,如果有人创建了一个用于处理流形的程序包,那么他可以将TangentSpace向量空间定义为抽象InnerProductSpace的子空间,然后他可以立即创建生活在一些切线和切线空间的张量积中的张量。 实际上,我正在考虑将用于定义向量空间的部分拆分为一个单独的包,然后可以扩展为用于定义数学结构/对象的包。

最后,与标准julia的互操作来自于在标准Array tensor上调用Tensor的对象中,该对象的索引与类型CartesianSpace空格相关联

@JeffBezanson ,关于将数组视为张量的子集,我很矛盾。 这样就不会丢失任何信息,但是同时有多种可能的数组解释,并且张量解释并不总是(甚至通常)有意义。 考虑图像:可以将图像视为(通常为2d)流形上的向量值场。 将字段限制为矩形网格可以使您自然地希望使用3d数组进行表示。 但是,实际上,这只是从网格点的空间到{R,G,B}向量空间的映射,因此前两个维度(网格的x和y标签)的几何含义与三维的几何意义(实际上是矢量)。

我不反对@Jutho的建议,将张量机制拆分为一个单独的包。 他可能是对的,需要完整张量机制的用户数量比只需要简单数组操作的用户数量要少得多。 我们在这里真正要问的问题是“线性代数应该在哪个领域内?”

线性代数的机制是张量代数的机制的足够实质的子集,至少在我看来,实现前者而不实现后者是没有意义的。 如果我们有协向量和逆向量的概念,则像v'M这样的运算可以更简洁一致地表示,但这已经使我们在一般张量运算中有了大部分使用。

我同意您的观点,这在概念上类似于对实数的运算,该运算返回复数。

考虑图像:可以将图像视为(通常为2d)流形上的向量值场。 将字段限制为矩形网格可以使您自然地希望使用3d数组进行表示。 但是,实际上,这只是从网格点的空间到{R,G,B}向量空间的映射,因此前两个维度(网格的x和y标签)的几何含义与三维的几何意义(实际上是矢量)。

尽管这不能解决您的全部问题,也无法消除您的全部想法,但https://github.com/timholy/Images.jl/pull/135正在努力实现这种想法的图像。 我希望这也可以轻松处理我要用于项目的颜色结构张量

2014年8月23日,20:36,jdbates [email protected]写道:

@JeffBezanson ,关于将数组视为张量的子集,我很矛盾。 这样就不会丢失任何信息,但是同时对图像有多种可能的解释,并且张量解释并不总是(甚至通常)有意义。 考虑图像:可以将图像视为(通常为2d)流形上的向量值场。 将字段限制为矩形网格可以使您自然地希望使用3d数组进行表示。 但是,实际上,这只是从网格点的空间到{R,G,B}向量空间的映射,因此前两个维度(网格的x和y标签)的几何含义与三维的几何意义(实际上是矢量)。

我同意张量不会取代数组。 上面的示例确实是一个不同的数学结构(即,矢量束或更一般地说是张量束),通过选择用于歧管坐标的网格和矢量空间部分的基础,其表示形式也可以作为多维数组给出。 因此,的确,您可以拥有不同的数学对象/结构,这些对象/结构可以以独立于坐标/独立于坐标的方式很好地定义,但可以(在选择坐标系或基础之后)表示为多维数组。 因此,多维数组当然不限于表示张量。 另一种方法也失败了,因为并非所有张量都具有使用多维数组的方便表示。 只有在使用称为乘积基础的特定基础时,情况才如此,该乘积是通过对张量乘积空间中所涉及的向量空间的各个基础向量的所有可能组合的直接乘积而获得的。 在某些情况下,例如,在张量积空间的对称不变子空间中使用张量时,这样的表示将不再可能,您需要为整个空间定义一个不同的基础,对于该完整的张量,它仅表示为一长串一维数字。

我不反对@Jutho的建议,将张量机制拆分为一个单独的包。 他可能是对的,需要完整张量机制的用户数量比只需要简单数组操作的用户数量要少得多。 我们在这里真正要问的问题是“线性代数应该在哪个领域内?”

线性代数的机制是张量代数的机制的足够实质的子集,至少在我看来,实现前者而不实现后者是没有意义的。 如果我们有协向量和逆向量的概念,则像v'M这样的运算可以更简洁一致地表示,但这已经使我们在一般张量运算中有了大部分使用。

我同意您的观点,这在概念上类似于对实数的运算,该运算返回复数。

-
直接回复此电子邮件或在GitHub上查看。

数组有多种可能的解释,张量解释并不总是(甚至通常)有意义。 考虑图像:可以将图像视为(通常为2d)流形上的向量值场。 将字段限制为矩形网格可以使您自然地希望使用3d数组进行表示。 但是,实际上,这只是从网格点的空间到{R,G,B}向量空间的映射,因此前两个维度(网格的x和y标签)的几何含义与三维的几何意义(实际上是矢量)。

我正试图通过允许类似数组和张量的方法在https://github.com/JuliaLang/julia/issues/4774#issuecomment -38333295的名义AbstractTensorArray建议中捕获这种区别尺寸。 在此方案下,我希望将您的示例表示为

AbstractTensorArray{Uint8, 3, [false, true, false], [true, false, false]}

因此x,y和RGB尺寸分别为“向下”,“向上”和“中性”。 然后,几何运算(例如,仿射变换)可以像张量一样处理网格坐标尺寸,同时以像数组一样的方式映射到RGB值上。 (如果您以后要以几何方式处理RGB值,则必须为此目的显式更改蒙版,但我想(a)将两种不同形式的几何运算应用于不同的子空间的情况并不常见。 (b)在这种情况下,进行显式转换可能会提高代码的清晰度。)

我没有考虑过@Jutho提到的共轭表示

我们在这里真正要问的问题是“线性代数应该在哪个领域内?”

一旦确定了类似阵列和张量的运算如何协同工作的设计,就可以通过特殊情况(例如我上面使用的别名)来定义线性代数的实体,这样纯线性代数用户就可以忽略整个广义张量层次结构,直到需要它为止(但如果需要,则不必重写任何内容)。 因此,我认为将整个事情放到Base中没有任何问题(也许膨胀除外)。

因此x,y和RGB尺寸分别为“向下”,“向上”和“中性”。 然后,几何运算(例如,仿射变换)可以像张量一样处理网格坐标尺寸,同时以像数组一样的方式映射到RGB值上。 (如果您以后要以几何方式处理RGB值,则必须为此目的显式更改蒙版,但我想(a)将两种不同形式的几何运算应用于不同的子空间的情况并不常见。 (b)在这种情况下,显式转换可能会提高代码的清晰度。)

我认为您在这里混了些东西。 在上面的讨论中,实际上解释了x和y坐标没有进行向量空间的解释,因为它们可以对应于任意弯曲流形上的坐标,而不必是平面空间。 尽管实际上这也不是最佳选择,但还是给了RGB维度以矢量的解释,正如我似乎记得的那样(我在图像处理中没有很好的背景),色彩空间也相当弯曲。 此外,即使对于域(x和y)形成向量空间的情况,为什么x和y也会上下波动,或者这仅仅是作为您的符号示例?

无论如何,我也从TensorToolbox.jl开始,将协变和逆变索引表示为某种参数或掩码,但这很快就变成了一场噩梦,这就是为什么我切换到每个张量都是某个向量空间元素的表示形式的原因,然后执行操作,必须检查空格是否匹配,就像在对数组进行操作时需要检查大小是否匹配。

x和y坐标不进行向量空间解释

抱歉,我看过“矩形网格” ---我想@jdbates正是他所说的。 但是我们不只是在谈论用通用内积代替点积吗? (原谅我,如果我误解了,我几乎所有的时间都花在欧几里得空间上:-)

每个张量都是某个向量空间的元素

似乎是个不错的主意---我很想看看一些示例,以了解它如何为用户服务(我读了很远的代码)。

对于这个问题,我有一个新的建议。


(1)APL样式切片。

size(A[i_1, ..., i_n]) == tuple(size(i_1)..., ..., size(i_n)...)

特别是,这意味着“单个切片”(即标量为标量或零维的切片)始终被丢弃,并且M[1,:]M[:,1]都是向量,而不是一个向量而另一个是行矩阵或任何其他此类区别。


(2)为向量和矩阵引入TransposeConjTranspose包装类型。 换句话说,是这样的:

immutable Transpose{T,n,A<:AbstractArray} <: AbstractArray{T,n}
    array::A
end
Transpose{T,n}(a::AbstractArray{T,n}) = Transpose{T,n,typeof(a)}(a)

以及所有适当的方法来使它们对向量和矩阵都有效。 我们可能希望将其限制为仅适用于向量和矩阵,因为目前尚不清楚一般转置对任意维度的含义(尽管仅反转维度是很诱人的)。 当您写入a'您将获得ConjTranspose(a) ,同样, v.'会产生Transpose(a)


(3)为(共轭)转置向量和矩阵定义各种专门方法,例如:

*(v::Transpose{T,1}, w::AbstractVector) = dot(v.array,w)
*(v::AbstractVector, w::Transpose{T,1}) = [ v[i]*w[j] for i=1:length(v), j=1:length(w) ]

等等,包括替换所有可怕的At_mul_B函数并使用惰性(共轭)转置构造进行特殊解析,然后在TransposeConjTranspose类型上进行分派。


(4)将广播操作限制为参数是标量或维数相同的数组的情况。 因此,以下当前无法正常工作的示例将失败:

julia> M = rand(3,4);

julia> M./M[1,:]
3x4 Array{Float64,2}:
 1.0       1.0       1.0      1.0
 0.516884  0.675712  2.11216  9.0797
 1.00641   0.726229  2.48336  4.38751

julia> M./M[:,1]
3x4 Array{Float64,2}:
 1.0  0.891557  0.561464  0.103968
 1.0  1.16552   2.29433   1.82633
 1.0  0.643353  1.38544   0.453257

相反,您将必须执行以下操作:

julia> M./M[[1],:]
3x4 Array{Float64,2}:
 1.0       1.0       1.0      1.0
 0.516884  0.675712  2.11216  9.0797
 1.00641   0.726229  2.48336  4.38751

julia> M./M[:,[1]]
3x4 Array{Float64,2}:
 1.0  0.891557  0.561464  0.103968
 1.0  1.16552   2.29433   1.82633
 1.0  0.643353  1.38544   0.453257

我相信这项建议解决了我们目前面临的所有主要问题:

  1. 对称的切片行为–尾随尺寸不再特殊。
  2. v'' === v
  3. v' == v
  4. v'wvw的点积–特别是,它是一个标量,而不是一个单元素向量。
  5. v*w'vw的外积。
  6. M*v是一个向量。
  7. M*v'是错误。
  8. v'*M是转置向量。
  9. v*M是一个错误。
  10. At_mul_B运算符和特殊解析消失了。

:+1:全部。 我在#6837中的2和3上做了一些工作,但从未完成。 @simonbyrne也对此进行了调查。

+1。 听起来它会在整个地方提供非常一致的行为。

该建议的唯一真正破坏性的部分实际上是M[1,:]是隐式垂直向量而不是显式水平行矩阵。 否则,它实际上是一组相当平滑,无中断的更改(有人希望)。 (对我而言)主要的顿悟是APL切片行为可以与延迟转置结合使用。 如果我们同意,我们可以提出一个计划并分拆工作。 我真的希望懒惰的转置和暂存的函数可以减少和简化代码。

是的,请! 张量转置可能应允许任何用户定义的排列,默认情况下将暗淡反转。

张量转置可能应允许任何用户定义的排列,默认情况下将暗淡反转。

看起来这会使类型复杂化很多,也许我们可以有一个PermuteDims类型,该类型允许任意惰性维度置换。

@Stefan :算出向量和2维似乎是个不错的主意
代数只是一些挑战:

  1. 关于多维数组的情况:对于具有维的数组A
    (i_1,i_2,...,i_n),如果想要将移调应用于[i_2,i_3]
    维度-甚至是[i_2,i_4]维度上的哈希。 你能做到吗
    转置的新定义?
  2. 关于单例尺寸:单例切片可能是
    故意离开。 朱莉娅(Julia)是否应该在
    计算? 例如,如果在向量中将向量定义为数组V
    (2,1)的维,并想将转置与矩阵A相乘
    尺寸(2,3,4)。 能否在维数上产生v'* A的结果
    (1、3、4)?

2014年10月16日星期四下午2:31,Stefan Karpinski [email protected]
写道:

唯一的破坏实际上是M [1 ,:]是一个(垂直)向量
而不是行矩阵。 否则,实际上很顺利,
非破坏性的一组变化(一个希望)。 (对我而言)主要的顿悟是
可以将APL切片行为与延迟转置结合在一起。 如果我们得到
买入,我们可以提出一个计划并分拆工作。 我真的希望
懒惰的转置和暂存函数可以减少一些代码,
简化。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/4774#issuecomment -59425385。

关于Re 2&3:经过一番刺探之后,我得出的结论是,向量转置不应该是AbstractVector的子类型,否则一切都会变得混乱(请参阅有关#6837的讨论)。 我认为最明智的方法是使用Transpose{T,A} <: AbstractMatrix{T}和一个单独的Covector类型(+ Conjugate变体)。

我遇到的另一个重要问题是,通常您希望分派特定的矩阵类型,其转置或共轭转置。 不幸的是,我无法提出一种通过现有类型机制来表达这一点的方法(请参阅此邮件列表讨论)。 没有这个,我担心我们会花@eval可能是3x3)组合使用大量的

@simonbyrne ,我

我已经指出(在不那么公开的论坛中,因此这里可能要简短提及),一种潜在的替代方法是通过扩展SubArrays可以使用的索引类型来处理所有_internally_。 特别是,即使父数组是Vector ,也可能具有“转置范围”类型,该类型将为SubArray赋予转置的形状。 (如果您不熟悉/可能如何实现子数组,请参见https://github.com/JuliaLang/julia/blob/d4cab1dd127a6e13deae5652872365653a5f4010/base/subarray.jl#L5-L9。)

我不确定这种替代策略是否会使生活更轻松或更艰难。 它减少了面向外部类型的数量,这可能意味着一种方法需要更少的方法。 (正如有人谁是失踪由于方法_still_填充Color转型Images ,这似乎是一件好事。)。另一方面,在没有它可以方便的三角调度使选择方法的编写更加尴尬,这可能会加剧@simonbyrne提出的问题。

任何见解都将受到欢迎。

除了这些细节之外,我喜欢@StefanKarpinski提案的形状。 我不愿屈服于APL风格的索引编制,但总的来说,我认为这是比我们现有的Matlab派生规则更好的选择。

两个想法:

  • 如果像A[[2], :]这样的索引变得惯用了,那么仅创建一个向量来包装单个索引2似乎有点浪费。 我们应该考虑允许A[(2,), :]用于同一事物还是类似事物? 我想单个元素范围是可以的,但最好有一个语法与[2]一样方便。
  • 如果我们需要匹配的维数进行广播,则应该有一种简单的方法将单维添加到数组中,例如numpy的newaxis索引。

我在考虑建议使用分号(例如A[2;:]建立索引是一种不同的索引模式,其中结果的维数始终与A -即不丢弃单例并使用排名超过一的任何事物都是错误的。 为了简化起见,决定将其排除在核心建议之外,但是这样做确实是一件好事。

我可以看到@simonbyrne表示的TransposeCovector类型不是AbstractArray的子类型也感到有些不愉快。 可能的解决方案是一个重大的突破性变化,可能不予考虑(但无论如何我想提一下),是为整个AbstractArray系列赋予一个额外的类型参数trans ,其值可能:N:T:C 。 对于仅假设向量为一维数字列表的所有方法,它们将不需要区分该最终参数的不同值,因此相应的方法定义可以保持不变。

对于N> 2的N维数组,有多种选择。 transpose给出错误,并且不可能实际创建类型AbstractArray{3,Float64,trans}的对象,其中trans!=:N ,或者:T只表示行主要常规数组的transpose具有反转所有尺寸的效果。 我认为后者也是使用Penrose图形表示法的人所接受的约定(请参见http://en.wikipedia.org/wiki/Penrose_graphical_notation,尽管此处未解释转置,但也请参见Cvitanović引用的书)。

我真的没有看到transpose支持任意索引置换的作用,它有permutedims ,也许还有一些使用经过改进的SubArrays的惰性方法。 此外,此问题的主要动机是简化A_mul_B动物园,并且无论如何(也不应该)通过正态乘法来支持高阶张量收缩。

我敢肯定,我尚未想到与此方法有关的一些新问题。

我想我在这里找到了解决派遣问题的合理解决方案。

@Jutho的建议似乎很有趣,我认为值得探讨。 不幸的是,评估这些事情的唯一真实方法是尝试实现它们。

@toivoh

  • A[2:2,:]还将保留维度,并且不需要分配或任何新语法。
  • newaxis这样的东西似乎非常可行。 确实,采用#8501中的架构,似乎可以通过建立索引直接创建广播:无论用户向该插槽中填充什么值,索引类型始终解析为1。

2:2是重复,如果索引有一个长表达式而不是2 。 但是,当然,您始终可以定义自己的函数来根据索引创建范围。

很好的建议:+1 :。

提醒我为什么我们要v' == v

我们并不是真正需要它,但是它很好,因为(有限维)向量空间的对偶是同构的。

或更强烈地说,由于Julia的数组无法区分协变和逆变索引,因此仅将其视为笛卡尔空间(欧几里得度量=恒等矩阵=克罗内克德尔塔)中的向量才有意义同构。

我不确定我们是否想要v'== v,但我认为这与
其余的部分。 我们是否希望列矩阵和向量比较相等,如果它们
有平等的元素?

实际上这是一个不同的问题,因为它们的维数不同。

特别是,该建议有效地消除了矢量和列矩阵之间的标识-因为如果水平或垂直切片矩阵,则将以任何一种方式获得矢量。 以前,您可能会忽略尾随的单例尺寸–或假装实际数量超出实际数量。 这样做将不再是一个好主意,因为向量可以来自数组的任何切片。

通过添加尾随单例尺寸,将convert从1-d转换为2-d有意义吗?

有了这个建议,我认为这可能不再是一个好主意。 但是也许是因为向量仍然像列一样行,而向量也像行一样。

我在#8416中指出的一件事是,现在误将sparsevec伪造为单列CSC矩阵。 一旦实现了适当的1-d稀疏向量类型,稀疏应该能够很好地适应这种情况(这将成为通用Nd COO类型最简单的有用情况,只需编写)。

只需将所有内容都考虑在内。那么以下内容将不起作用?

A [1 ,:] * A * A [:,1]#来自矩阵的行*矩阵*矩阵的列???

你写了

v'w是v和w的点积–特别是,它是一个标量,而不是一元向量。

v'* w也是标量吗?

我喜欢点(x,y)取任意两个形状为(1,...,1,m,1,...,1)和
无论如何返回点积。 但是我不希望x * y在这种意义上给出点(x,y)
除非x是一个向量,而y是一个向量。

不知道这是不是一个好主意,但是如果
A [:,1,1]是向量,A [1,:,1]或A [:, 1,:]是辅助向量。
最好沿着向量的尺寸-在其上的槽
允许收缩张量,其中标准线性代数为
1(行向量)和2列向量。

我认为,我们先前在此问题中解决的两个主要挑战是:

(A)在对多维数据进行操作时,如何区分张量语义(用于收缩)和数组语义(用于广播);
(B)如何将明显且方便的线性代数嵌入到可以推广到更高维度的一致框架中。

对我来说,尚不清楚该提案如何处理上述两个问题。 据我所知,要实现(A)仍然需要临时的用户操作(与当今的功能一样); 并使用懒包装就需要像由@timholy建议子数组扩展,此时它变成一个懒惰版的屏蔽方法的讨论前段时间地址(B)。 我可以想象使用某种类似的惰性机制(例如List包装类型)为(A)提供额外的支持,但在所有这些情况下,我似乎都认为惰性是一种可选策略。

我不知道有多少人分享@Jutho的观点,即“无论如何都不应该通过正态乘法来支持高阶张量收缩”,但是我不能完全不同意:我只做我认为普通的工程数学,我一直都需要它们。 尽管当前的语言(如Mathematica和NumPy)在这方面有其设计局限性(如我上面所讨论的),但它们至少受到支持! 例如,一旦您想以简单的数值方法使用矢量场的梯度,就需要高阶张量收缩。

当您说“ ...在这方面有其设计局限性(如上所述)时,至少得到了支持”,您是在谈论缺少的功能,还是关于向量和转置的一些根本无法解决的问题更高的级别,还是通过添加功能?

关于改善您的(A)和(B)点,该提案有什么_冲突_?

我真的看不到matlabs标准乘法运算符*或其他任何内置的matlab函数如何支持张量收缩。 Numpy具有内置功能(我忘记了名称),但据我所记得它也相当有限。

我也一直需要最普通形式的张量收缩,这就是为什么我知道指定最普通张量收缩,更不用说有效地实现它并不是完全简单的原因了。 这就是为什么我认为需要为此提供特殊功能的原因,而不是试图将一些半工作或特定功能塞入Julia基础的标准运算符中,而标准运算符并不涵盖用例的一半。 但是我很高兴改变自己的看法,例如,是否有一种“标准”收缩比其他任何收缩都重要/有用? 但这可能是非常依赖领域的,因此不适合作为在Julia Base中采用的一般规则。

Op 19-okt.-2014 om 22:52 heeft thomasmcoffee [email protected] het volgende geschreven:

我认为,我们先前在此问题中解决的两个主要挑战是:

(A)在对多维数据进行操作时,如何区分张量语义(用于收缩)和数组语义(用于广播);
(B)如何将明显且方便的线性代数嵌入到可以推广到更高维度的一致框架中。

对我来说,尚不清楚该提案如何处理上述两个问题。 据我所知,要实现(A)仍然需要临时的用户操作(与当今的功能一样); 并使用懒包装就需要像由@timholy建议子数组扩展,此时它变成一个懒惰版的屏蔽方法的讨论前段时间地址(B)。 我可以想象使用某种类似的惰性机制(如列表包装器类型)为(A)提供额外的支持,但是在所有这些情况下,我似乎都认为惰性是一种可选策略。

我不知道有多少人分享@Jutho的观点,即“无论如何都不应该通过正态乘法来支持高阶张量收缩”,但是我不能完全不同意:我只做我认为普通的工程数学,我一直都需要它们。 尽管当前的语言(如Mathematica和NumPy)在这方面有其设计局限性(如我上面所讨论的),但它们至少受到支持! 例如,一旦您想以简单的数值方法使用矢量场的梯度,就需要高阶张量收缩。

-
直接回复此电子邮件或在GitHub上查看。

这是A的最后一个索引和B的第一个索引的收缩
有点像数学家的点

function contract(A,B)
   s=size(A)
   t=size(B)
   reshape(reshape(A, prod(s[1:end-1]), s[end]) *  reshape(B,t[1],prod(t[2:end])) , [s[1:end-1]... t[2:end]...]...)
end

我一直能够进行重塑,置换,甚至复杂的总体收缩
像上面那样或多或少需要时结合

不知道张量的真正大问题是什么,为什么我们不能仅仅实现其中的几个
职能?

对,就是这样。 在本期中,我们希望继续前进的是

  1. 索引应减少什么尺寸? “ APL风格”似乎毫无争议。
  2. vector'给什么?

对于张量收缩,具有适当的类型和分段功能,我认为我们实际上可以得到相当高性能的实现。

我的感觉是张量会照顾自己,我们必须确保
线性代数用户不会感到沮丧。

我最大的担心是

(从2d阵列取得一行)*(2d阵列)*(从2d阵列取得一列)

除非我们采取,这是一个常见的操作仍然无法正常工作
(连续)使用covector或可能更好
使用常规插槽索引对其进行标记。

@JeffBezanson ,当我说支持这些操作时,是指内置数据类型和函数在设计时就考虑到了它们,例如,像Mathematica的Dot函数。 因此,对于用户而言,存在一种内置的,记录的和/或显而易见的方式来完成某些事情。 对于任何设计,都可以通过添加功能来实现对任何事物的支持,就像当前的实现一样。 所以这不是技术冲突的问题,而是设计的问题。

@Jutho ,我不太使用MATLAB,所以我无法发表评论。 我同意NumPy的设计不如Mathematica的设计(如我上面所讨论的)那么连贯,但它也支持更广泛的行为。 我同意基本线性代数应该使不需要通用张量的机器对不需要它的用户不可见,但是由于Julia的出色语言特性,似乎没有必要为它们引入不同的实现,因为NumPy和Mathematica都有被迫在某种程度上做。 在我看来,这个问题至少部分是关于为两者找到合适的统一系统,以揭示在常见的线性代数情况下应使用哪些专业化知识:例如,对vector'

A [1 ,:] * A * A [:,1]#来自矩阵的行*矩阵*矩阵的列???

正确–您必须写A[1,:]' * A * A[:,1]

v'* w也是标量吗?

是的, v'w是同一件事。 该建议的优点之一是,它完全消除了廉价的语法黑客。

不知道这是不是一个好主意...

我认为不是。 该提议的目标之一是使切片和索引规则对称,这将使其中一个索引特别,在我看来,这将使整个目标无法实现。 如果切片将是不对称的,那么我们最好还是保留当前行为。

@thomasmcoffee您只需要更具体一点即可。 当然,每个人都希望事情是连贯的,文件化的,显而易见的等。问题是,桌上的提议是否为那些目标服务? 也许当前的提议根本不会影响那些目标,这是可以的---只要它能带来其他方面的改进,我们仍然会有净改进。

所以让我说清楚

如果A不是正方形

| | 当前| 提议| MATLAB |
| --- | --- | --- | --- |
| A * A [1 ,:] | 没有是的没有
| A * A [1 ,:]'| 是的没有是的
| A [:,1] A | 没有
A [:,1]' A | 是的是的是的

如果A是正方形

| | 当前| 提议| MATLAB |
| --- | --- | --- | --- |
| A * A [:,1] | 是的是的是的
| A * A [:,1]'| 没有没有没有
| A [1 ,:] A | 没有
A [1 ,:]' A | 没有是的没有

我发誓我只是发布了一个答案,但是不知何故它消失了。 这都是正确的。 在当前安排中,您需要考虑是要获取行切片还是要获取列切片,以及在决定是否进行转置时是否在左侧或右侧进行乘法运算(列在左侧进行转置,所以移到右边)。 在提案中,您仅考虑要在哪一侧进行乘法运算-始终在左侧移置,从不移置右侧。

如果可以的话

dot(x,y)dot(x.',y)dot(x,y.')dot(x.',y.')都给出相同的标量?

即Σᵢconj(xᵢ)*yᵢ

这样一来,人们就可以做点(x,A * y)而不必考虑太多

由于APL索引, @ alanedelman的这些示例在您不应该使用的地方感觉有些向后A[i; :] ?)

但是,在上述情况下,您可能希望给出一个向量。

@alanedelman ,我看不出任何理由不应该使用dot这些方法并给出相同的结果。

我一直能够进行重塑,置换,甚至复杂的总体收缩
像上面那样或多或少需要时结合

如果选择:BLAS方法,那肯定是在TensorOperations.jl中的tensorcontract函数中实现它的方式,这对于大型张量而言无疑是最快的。 我还使用Cartesian.jl功能(并希望有一天使用分段功能)编写了本机julia实现,从而消除了对permutedims(额外的内存分配)的需求,并且对于较小的张量而言更快。

我只是在回应一个错误的说法,即matlab为此提供了内置功能,而Julia没有。 重塑和置换置换都可以在Julia中使用。 Numpy确实具有tensordot函数,该函数可以做到这一点,但不允许您指定输出张量的索引顺序,因此,如果您考虑了特定的输出顺序,则以后仍然需要一个置换排列。

但这与当前主题相距太远,当前主题的确是使向量代数和矩阵代数具有一致的行为。

+1是Stefan的提议。 它似乎给出了非常清晰但足够灵活的语义。 作为线性代数用户,即使是习惯于MATLAB样式语法的用户,我也会发现使用起来足够简单的规则。

我对'的一般含义感到有些困惑。 如果v是向量,则v'transpose 。 如果a是2d数组,则a'现在也将是transpose 。 定义这两者都是为了能够通过将b的第一维与a的第一维收缩而轻松地形成b' * a a

a的维度大于2时,似乎对a'的定义没有达成共识。 除了颠倒尺寸,我没有听到有人提出其他建议,而这恰好与b' * a紧缩了b的第一尺寸和a的第一尺寸。

我确实认为,如果我们能简单地陈述'所做的事情而又不涉及正在操作的事物的大小,那将是很好的。

这似乎是合理的在库可用于更一般的情况下,另一个收缩功能,如contract(a, b, 2, 3)合同的第二维a与第三b

顺便说一句, dot(a,b) == a'*b时, ab是矢量,但dot(a,b)目前没有定义矩阵。 可以给我们dot(a,b) = trace(a'*b)吗?

@madeleineudell :我对“一般”的含义有些困惑。

我对此表示关注。 您基本上可以将我提案中的属性2-5、7、8和10作为定义特征。 也就是说,您希望持有:

  • v'是一维的,但不是向量
  • v'' === v
  • v' == v
  • v'w是标量
  • v*w'是一个矩阵
  • v'*Mv'是同一种事物
  • M'是二维的但不是矩阵,也许是矩阵视图

特别是,对于高维数组的含义或作用没有任何限制。 关于什么是向量的通用理论,可以包含较高的维数,这是很好的方法,但是我不认为这可以在不使事物过于复杂的情况下完成,例如,通过上/下维或用索引标记每个维。

至少很明显, '的一般规则不是要颠倒尺寸,因为这对向量和余向量不是这样。

我能想到的唯一简单的规则捕获向量,协矢量和矩阵的上述行为是对前两个维进行置换(一个向量不存在第二维,一个向量不存在第一维,第二个不存在)。

2014年10月20日,09:05,toivoh [email protected]写道:

至少很明显,'的一般规则不是要颠倒尺寸,因为这不是向量和余向量的作用。

我能想到的唯一简单的规则捕获向量,协矢量和矩阵的上述行为是对前两个维进行置换(一个向量不存在第二维,一个向量不存在第一维,第二个不存在)。

我认为如果要考虑一般张量,这是不正确的。 如果仅将矩阵和向量视为其中一些带有数字的块,并且将v视为列,将v'视为行,则可能是正确的。

但是,如果将v视为向量空间的某个元素,并且将w = v'视为将v映射到对偶空间V_的元素w的某种方式,则w仍然是向量,即一维对象。 通常,需要一种度量来定义从V到V_的映射,即w_i = g_ {i,j} v ^ j。 现在,如果V是笛卡尔空间,即R ^ n具有标准的欧几里得度量,则V *自然是V的同构。这意味着没有上限或下限索引(协变或逆变)的概念,因此w_i = v_i = v ^ i = w ^ i。 我认为这是大多数编程中使用的情况,即需要Julia Julia支持的情况。 (对于复数向量空间,V *与共轭向量空间Vbar自然同构,因此自然映射为w_i = conj(v ^ i)。)

将向量表示为带有数字的列的向量,将双重向量表示为带有数字的行,因此将矩阵(它们是V \ otimes V_的元素)表示为带有数字的块的惯例非常方便,但是一旦您还想考虑更高的维数,就会停止数组,即高阶张量积空间的元素。 在这种情况下,“行向量”(即二维对象,其中第一维的大小为1)(用matlab / julia术语表示)是张量积空间V1乘以V2的某个元素,其中V1恰好是是R(或其他一维空间)。 我同意这可能不是您想要的默认行为,但我宁愿不要只是不像Matlab那样尝试为通用数组定义转置并引用置换矩阵。 将常规张量的前两个维反转为默认约定没有意义。 从某个高阶张量积空间V1 \ otimes V2 \ otimes…\ otimes Vn移置元素没有唯一的定义。 如上所述,颠倒所有尺寸的惯例仅源于Penrose方便的图形表示。 另外,这是将矩阵空间(V \ otimes V_)映射到自身(V * \ otimes V * = V \ otimes V )的映射。

我可以看到两种方法:
1)在笛卡尔张量的设置内(即,高低索引之间没有区别),使用某种选定的约定使便利运算符'(甚至*)可以与任意高阶数组一起使用。 对于典型的用例,这可能会带来一些令人惊讶的结果。

2)将'和*限制为向量和矩阵。 高阶数组上的错误。 我认为这是最受欢迎的方法(例如Matlab等)。

这个职位与我去年担任的职位相比有点偏颇,

探索双方的矛盾情绪。

希望没事。
最终让我感到震惊的是,当前的方法是有逻辑的,但是从未明确阐明。
它看起来很像骇客,让我很烦。 现在我以某种方式了解
它,我更喜欢它。

在当前的方法中,所有数组都是无穷大的,隐含的数字为1。
撇号运算符'可能意味着互换前两个维度,
但实际上,它意味着今天

ndim(A)<= 2? exchange_first_two_dims:no_op

抱歉,如果其他人都看到了,我只是错过了。 我的头脑陷入困境
向量是一维而非无限维的想法
因此,转置应该颠倒尺寸,因此是no_op。

我可以接受单引号,否则单引号总是可以互换
前两个维度-我认为我不在乎。 我认为撇号存在
用于线性代数而不是多线性代数。

我在戏弄是否是撇号(“'*”)(如果可能!或其他可能的东西)
应表示将最后一个尺寸缩小到第一个尺寸(如Mathematica的点)
并且具有apl索引并且没有余矢量。 我大脑的一部分仍然认为值得探索,
但是当我醒来的时候,当前的方法似乎越来越好。
(让我们看看今天晚些时候的感觉)

我不后悔去年开始这个话题,但我又想知道是什么情况
今天真的很烦人...我们可以举一些例子吗? ...以及是否
了解这些案例比改变我们的工作更好。

我通读了整个主题,并看到了许多原理-很好,
但没有足够的用例来说明问题。

我可以说我经常被烦恼

[1 2 3]#ndims == 2
[1,2,3]#ndims == 1
[1; 2; 3]#ndims == 1

主要是因为我似乎不记得了。

只是一个思想实验。 如果我们重新定义*作为收缩运算符(类似于@alanedelman对于'*想法),将会失去什么功能? 它是否完全消除了线性代数运算中对'的需要(除了充当矩阵的转置功能之外)? 我只能看到,它将使我们不用外部产品w*v' ,而可以很容易地用outer(w,v)代替它。

编辑:假设是APL切片。 例如, A[1,:]*A*A[:,1]将具有预期的结果,而无需使用'转换第一个操作数。

我也想到了这一点。 我认为v * w是点积,就像类固醇超载一样
可能容易出错。
这是mathematica的圆点似乎还不错的地方。

综上所述,最后签订合同似乎是合理的,但是
可以是撇号星号,也可以是*或应该是点
有问题。

有点无关但不完全无关...
我想指出的是,每个人最初读作“点星”的点星是(被告知)是
之所以要成为明星,是因为POINTWISE运算符是
的意思是

我可以说我经常被烦恼

[1 2 3]#ndims == 2
[1,2,3]#ndims == 1
[1; 2; 3]#ndims == 1

主要是因为我似乎不记得了。

我始终坚信“为什么我们不只使用, ?”

如果我们重新定义*以充当类似于@alanedelman想到的'*的收缩运算符,将会失去什么功能?

我们将失去关联性:例如(M*v)*v将给出当前的dot(v,M*v) (一个标量),而M*(v*v)将给出M.*dot(v,v) (一个矩阵)。

我们为什么不只让dot成为收缩运算符(无论如何它都不是关联的)? 我们还可以定义更高阶的收缩,例如ddot(A::Matrix,B::Matrix) == A⋅⋅B == trace(A'*B)

因此,我是否正确理解引入向量转置的唯一目的是使我们摆脱*非关联性? @alanedelman的示例的M*v*v' vs M*v'*v ),但是括号也可以解决( M*(v*v) vs (M*v)*v ),而无需为向量实现转置的所有麻烦(正如@Jutho已经指出的那样,对高阶张量实现转置在数学上还是没有意义的)。 对我来说,问题是哪种符号更易读/简洁/优雅/纯净。

实际上,目前将M*(v*v)(M*v)*v都解析为

*(M, v, v)

为了允许矩阵乘积根据参数的大小优化乘法顺序,因此解析器也必须更改。

但至少我个人认为,关联性是一件大事。 您必须在数学和大多数编程语言之间进行转换。 对于朱莉娅,我仍然希望您不必做太多翻译。

(正如@Jutho已经指出的那样,对高阶张量实现转置在数学上是没有意义的)。

我不完全是我所说的。

如果我们重新定义*以充当类似于@alanedelman想到的'*的收缩运算符,将会失去什么功能?

我们将失去关联性:例如(M_v)_v将给出当前点(v,M_v)(一个标量),而M_(v_v)将给出M._dot(v,v)(一个矩阵)。

我们为什么不只是将点号设为收缩算子(无论如何,它都不是关联的)? 我们还可以定义更高阶的收缩,例如ddot(A :: Matrix,B :: Matrix)==A⋅⋅B== trace(A'* B)。

通过这种推理,我们不再需要向量和矩阵的乘法运算符,我们只需将所有内容都用点表示即可:
A∙B
A∙v
v∙A∙w
v∙w

因此,这只是@pwl提案,但*替换为点。

但至少我个人认为,关联性是一件大事。 您必须在数学和大多数编程语言之间进行转换。 对于朱莉娅,我仍然希望您不必做太多翻译。

我想部分问题是即使在数学中也有不同的约定。 正在考虑行向量和列向量的数学运算,还是我们想要线性运算符和对偶空间的更抽象的行为(我想运算符不是与向量相乘,而是应用于向量,所以为什么不写A(v)而不是A * v或A∙v)?

我不太需要方便的语法,我个人更喜欢每次都在v'* w上写点(v,w),因为前者更清楚地表示了独立的数学运算的基础(实际上,首先需要定义一个甚至可以定义从向量到对向量的自然映射之前的标量积。)=

很抱歉这么无知,但是为什么M[1, :]不能像v'那样变调呢?

我将添加到困扰我的事物列表中

1。

[1 2 3] # ndims == 2
[1,2,3] # ndims == 1
[1;2;3] # ndims == 1

2。
v=rand(3)
v' * v当前有ndims == 1@StefanKarpinski的建议解决了这个问题)
(v' * v)/( v' * v )ndims ==2 (这真的让我很烦,也将得到修复)

还是我真的不想要矢量
和apl索引是我不断来回走的东西
---还在想,但我很想看看那些东西的清单
在当前的茱莉亚中寻找其他人

我绝对喜欢cdot的想法除了*运算符外,将最后一个索引收缩到第一个索引
并允许点(a,b,..)允许非常普遍的矛盾。

尽管有Numpy的命名约定(也许是我以前的文章的出现),但我对使用点进行一般张量收缩的感觉有些复杂。 这是维基百科的第一句话:

在数学中,点积或标量积(或有时在欧几里得空间中为内积)是代数运算,它采用两个等长的数字序列(通常是坐标矢量)并返回一个数字。

在我看来,点也应该返回标量。

@ brk00 ,您的问题的问题是不清楚如何将其扩展到更高维/更高等级的数组(对此我真的不喜欢世界维)数组。

很抱歉这么无知,但是为什么M [1,:]完全不能像v'那样变调呢?

可以,但是您如何概括呢?

@Jutho ,很抱歉,我应该用不同的措词。 我的意思是,您的行“从某个高阶张量积空间中转置元素没有唯一的定义”,因此没有数学动机来引入一个特定的定义或完全对其进行定义。

@alanedelman

我将添加到困扰我的事物列表中

[1 2 3]#ndims == 2
[1,2,3]#ndims == 1
[1; 2; 3]#ndims == 1

串联行为与此问题无关。 我们不要再使水更浑浊了。

除了*运算符外,我绝对喜欢`cdot将最后一个索引收缩为第一个索引的想法
并允许点(a,b,..)允许非常普遍的矛盾。

这也是切线的。 让我们继续关注数组和切片。


在当前的方法中,所有数组都是无穷大的,隐含的数字为1。

这不是真的。 如果是这样,则ones(n)ones(n,1)ones(n,1,1)等之间将没有区别。但是这些都是具有不同类型的不同对象,它们甚至不等于每个对象其他。 我们为实现事物付出了一些努力,以使它们的行为“类似”,但这与实际上是无限维的数组相去甚远。


当前令人烦恼的事情清单在很大程度上反映了我上面提议的好特性(或其子集)。 即:

  1. 不对称的切片行为–尾随尺寸是特殊的。
  2. v'' !== v –实际上v'' != v ; 这是因为他们的排名不同。
  3. v' != v –一样的交易。
  4. v'w是一个单元素矢量,不是标量。
  5. 我们需要对A*_mul_B*进行特殊解析,这是有史以来最糟糕的事情。

我同意@StefanKarpinski的大部分观点,除了v' == v位:我认为这些不应该相等。 是的,它们可能是同构的,但是它们仍然是不同的对象,因为它们的行为有很大不同:矩阵MM'也是同构的,但我认为我们永远都不会希望它们相等(当然,除非它们是Hermitian)。

我对Covector类型的总体看法是,它们相对较轻:除了基本操作(乘法,加法等)之外,我们将避免定义过多的操作(我什至会犹豫是否定义索引) )。 如果您想使用它做某事,则应将其转换回向量并在那里进行处理。

+1 @simonbyrne的观点,包括他对@StefanKarpinski观点的普遍认可。

也同意@simonbyrne。

是的, vv'具有不同的类型,但是我们已经认为形状和数据相同的不同数组类型是相等的,例如speye(5) == eye(5) 。 为什么covector与稀疏不同?

我希望向量矢量将像行矩阵一样广播。 对于被认为相等的数组AB ,到目前为止,

all(A .== B)

如果一个是向量而一个是子向量则不是这种情况。

对我而言,协矢量更像是行矩阵,而不是矢量或列矩阵。

我想关键问题是size(v' )是什么? 如果答案是(length(v),)那么我认为v == v'应该是正确的。 如果size(v') == (1,length(v))则可能为假,但可以说v' == reshape(v,1,length(v))应该为真。 所以问题是v'应该是一种特殊的向量还是一种特殊的矩阵?

我越来越相信这个问题确实与移调的真正含义有关。

covector不是真正的数组,因此我认为我们不能真正说出它们具有什么“形状”。 一个covector实际上是由它的作用来定义的,即*(::Covector, ::Vector)给出了一个标量:由于任何AbstractVector都不这样做,所以实际上不是同一件事。

另一个问题将出现在复杂字段中:我们将有v' == v还是v.' == v吗?

@simonbyrne

一个covector实际上是由它的作用来定义的,即*(::Covector, ::Vector)给出了一个标量:由于任何AbstractVector都不这样做,所以实际上不是同一件事。

这是一个很好的观点。 但是,如果不能将v'用作对象,那可能会令人沮丧。 也许正确的方法是将v'视为有趣的行矩阵,而不是有趣的向量。

也许正确的方法是将v'视为有趣的行矩阵,而不是有趣的向量。

有点,但我也不认为它应该是AbstractMatrix的子类型:我认为AbstractCovector应该是顶级类型。 我很乐意定义length(::Covector) ,但是我不认为我们应该定义size方法。

我不确定如何处理广播:除非我们能提出合理的标准,否则我会为未定义广播方法而犯错误。

讨论似乎正在趋向于使用转位和向量,就像它们在工程中一样,即将一切都视为矩阵。 将向量视为一列,将向量视为一行。 这对笛卡尔空间中的矢量和线性映射很有用(典型的用例),但如果尝试将其推广到多线性代数或更一般的矢量空间等,就会开始失败。笛卡尔空间中的许多事物是等效的,而对于一般空间则是等效的。 我不一定反对这是朱莉娅的默认行为,但我确实不同意上面的某些陈述,好像它们在“数学上是正确的”一样。 因此,让我与以下内容相矛盾。

2014年10月20日,17:39,Simon Byrne [email protected]写道:

我同意@StefanKarpinski的大部分观点,除了v'== v位:我认为这些不应该相等。 是的,它们可能是同构的,但它们仍然是不同的对象,因为它们的行为有很大不同:矩阵M和M'也是同构的,但我认为我们永远都希望它们相等(除非它们当然是埃尔米特式的)。

这种说法在任何层面上都是没有意义的。

1),我认为您在这里滥用同构的含义。 同构是两个空格之间的关系(在此设置中)。 通常,对于每个(实)向量空间V,都有一个线性函数的对偶空间V *(对于复数空间,还存在共轭空间和共轭空间的对偶)。 这些通常是不同的空间,甚至从一个到另一个都不存在唯一或自然的映射,即,通常没有意义将V的元素v与V *的元素phi关联。 唯一的常规操作是应用线性函数,即phi(v)是一个标量。

一旦具有双线性形式V x V->标量,通常是内部乘积/度量,就可以定义从V到V *的自然映射。 然后可以定义phi_i = g_ {i,j} v ^ j。

2),实际上没有与“转置向量”相关的自然操作(请参见第3点)。 这种混淆来自使用列矩阵识别向量。
夸张地说,它实际上太强大了,我实际上想看看使用点和某些外部乘积/张量积运算无法获得的向量转置的用例吗?

但是,如果要使用转置将向量映射到对偶向量的方法,即将V中的v映射到V_中的某些phi = v',则假定您正在使用标准的欧几里得内积(g_ { i,j} = delta_ {i,j})。 到那时,您正在消除协变量和逆变量索引之间的区别,本质上是在使用笛卡尔张量,并且V和V_变为自然同构。 如上所述,w_i = v ^ i = v_i = w ^ i,是的,v == w,我什至会说没有什么可以区分这两者。

3)“移调”操作最初仅是针对V-> W(http://en.wikipedia.org/wiki/Dual_space#Transpose_of_a_linear_map)的线性映射定义的,实际上即使在那也可能并不意味着您的想法。 线性映射A:V-> W的转置是W _-> V_的映射A ^ T,即它作用于W_中的向量,对偶向量,并产生V_的元素,即V的协向量。这意味着,如果以矩阵A的通常转置A ^ T来考虑它,则该矩阵A ^ T将与代表W *中向量的列相乘,并且由此产生的列代表V的协向量因此,在这一点上,用行向量识别对偶向量已经失败了。

但是,在实际向量空间的典型使用情况下,通过标准欧几里得内积用V和W标识V *和W *,可以用线性图的伴随关系来标识线性图的转置。大多数人认为,V-> W的贴图也是如此。 在复杂的情况下,这失败了,因为普通矩阵转置(无复共轭)仅在作为映射W _-> V_时才有意义,而作为映射W-> V则不是基础独立的定义,因此在操作上没有意义。

至于转置对高维数组意味着什么,在我们正在收敛的matlab /工程方法中:这应该是一个错误,并且不能一概而论。 这并不意味着没有定义它的方法。 问题是,高阶数组代表什么。 它是张量积空间V1 \ otimes V2 \ otimes…\ otimes VN的元素,是否是作用于V1 x V2 x…x VN的多线性图,是否是来自某些张量积空间V1 \ otimes V2 \ otimes的线性图…\ otimes VN到另一个张量积空间W1 \ otimes W2 \ otimes…\ otimes WM? 对于二维情况,有一个分辨率。 识别线性映射A:V-> W,向量为W \ otimes V_,线性映射意义上的转置对应于逆转张量积空间中的空间A ^ i_j-> A_j ^ i,V_ \ otimes中为A ^ T W = V * \ otimes W _,实际上是W

总之,我认为问题在于Julia的Vector是否需要从数学意义上捕获任意通用矢量的属性,或者它是否仅代表一列数字,一个列表,……vector,张量等单词具有良好的含义。数学中定义的与运算和基准无关的含义,这可能与您要在Julia向量上定义的运算类型冲突。 相反,从数学的角度来看,某些对象实际上是向量(双重向量等),可能不应该用Julia向量来识别它们,因为标准的Julia(Abstract)Vector类型无法区分不同的向量空间。

在这方面,存在一些不对称性,因为术语“矩阵”的重载要小得多,即使从数学的角度来看,矩阵在某种程度上也只是线性映射的方便表示。 请注意,在很多情况下,您不想将线性映射表示为Matrix而是将其表示为函数(此问题以前在eigs等的参数中出现)。 在这方面,我可以理解为什么matlab甚至不费心就拥有一个真正的一维结构的向量。 在这种方法中,无论如何,一切都被解释为“矩阵”,即带有数字的块。

题? 令c为向量。 什么是fft(c)?

答案:fft(c')'可能以意想不到的方式获取复杂的共轭
与fft(c)相比

用户可能只是受益于它的不确定性
(或者,如果记录得很好的话,这可能对用户有利)

我敢打赌,这类事情还有很多

我怀疑现在在Base要做的正确的事情是仅定义:

  • covector乘另一个向量
  • 向量乘以一个矩阵
  • covector”以获取向量

+1到目前对矢量的最小支持。

是的,+ 1。

所以如果我提到我很确定
norm(covector,q)应该是norm(vector,p),其中1 / p + 1 / q = 1
由于Holder不平等,这种情况不会长期存在。 :-)

感谢天堂p = q = 2

实施起来并不难:

norm(c::Covector, q::Integer) = norm(c.vector, q/(1-q))

您可能希望进行其他检查以避免q == 0q == 1

我认为q == 1可以

韦利格·希尔森

安德烈亚斯·诺阿克(Andreas Noack)

2014-10-22 15:19 GMT-04:00 Stefan Karpinski [email protected]

实施起来并不难:

范数(c :: Covector,q :: Integer)=范数(c.vector,q /(1-q))

您可能希望进行一些其他检查以避免q == 0和q == 1。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/4774#issuecomment -60139762。

我正在研究covector提案(我最支持的提案)的写法,但现在发表一点,使@StefanKarpinski的“有趣的行矩阵”概念精确一点可能会很有用。

辅助向量不是向量,但是对为什么的描述并不总是很清楚。 辅助向量(或物理学家称之为bra向量)是一种线性函数,它吃掉一个向量并吐出一个点积的数字。

更确切地说:

  • 假设V = V(F)W = W(F)是元素F某个字段上的向量,
  • vw是分别是VW元素的向量,
  • <.,.>是内部产品,例如<.,.> : V × W → Fv, w ↦ <v, w>

对应于v的余矢量是执行映射w ↦ <v, w>的线性函数v' : W → F w ↦ <v, w>

通常的描述以说v'是对偶空间V*的元素结尾,关于对偶空间_is_的说明很少。

对于计算机科学界人士,有一个简单的描述: v'是内部产品关于其第一个参数的循环, V*是一个单参数的函数集合不同的第一个向量的咖喱。

这篇Wikipedia文章可能有助于调和与两个描述相关的不同符号,但是它们表达的是完全相同的概念。

另外,我最初认为应该禁止索引到向量中。 但是,索引v'[1]等同于将v'应用于规范基础向量e₁ = (1, 0, ...) ,因此可以将v'[1]定义为<v, e₁> 。 像1:n这样的更一般的索引语义自然地是将v'应用于适当的投影矩阵,而每一列都是规范的基础向量。

考虑在#987上下文中向量的索引语义给出了向量不是AbstractVector s的另一个原因:通常不可能廉价地索引v'因为这都取决于数量的昂贵程度像<v, e₁>一样进行计算。 通常,您可以针对矩阵<v, w> = v'*A*w定义一个内部乘积,而索引成本则由matvec产品A*w (或A'*v )支配。太昂贵而无法获得AbstractVector资格。

既然我们在谈论DFT和规范...今天这让我无所适从

如果f是向量的函数并产生标量,则我希望diff(f)是接受Vector并产生Covector的函数这样f(x) ~= f(x0) + diff(f)(x0) * (x-x0) 。 梯度的一种非常常见的用法是在给定输入增量变化的情况下,获得函数输出增量变化的线性近似值。 如果输入是矢量,则渐变是沿着输入的所有维度收缩的感觉是很自然的。

但是,如果要执行梯度下降,则需要向其自身添加x梯度(的缩放版本)。 从这个意义上讲,梯度是指向最陡峭方向的向量,其范数与沿最陡峭方向的变化率成比例。

我的直觉说“向量输入函数在某一点上的梯度是一个余矢量”更为重要。

我怀疑现在在Base中要做的正确的事情是仅定义:

covector乘另一个向量
向量乘以一个矩阵
covector”以获取向量

尽管您印象深刻,但您可能还是从我以前的可怕帖子中得到了+1。

但是,对此有一些评论:

我正在研究covector提案(我最支持的提案)的写法,但现在发表一点,使@StefanKarpinski的“有趣的行矩阵”概念精确一点可能会很有用。

辅助向量不是向量,但是对为什么的描述并不总是很清楚。 辅助向量(或物理学家称之为bra向量)是一种线性函数,它吃掉一个向量并吐出一个点积的数字。

我认为可以在没有内部积的情况下定义对偶向量/协向量(我也更喜欢线性函数)。 如果要定义从V到V *的映射,就只需要一个内部乘积即可。

更确切地说:

令V = V(F)和W = W(F)为元素F的某个域上的向量,
v和w分别是V和W的元素的向量,
<。,。>是内积,使得<。,。>:V×W→F和v,w↦
在两个不同的向量空间V和W之间定义一个内积是很奇怪的,而且我认为这是不可能的。您如何定义正定性的性质?
与v对应的余矢量是线性函数v':W→F执行映射w↦

通常的描述以说v'是对偶空间V *的元素结尾,而关于对偶空间是什么则没有多说。

我认为很明显,对于有限维情况,对偶空间顾名思义就是矢量空间。 但是,对我之前的言论的总结是,Julia的(Abstract)Vector类型更像是一个List。 它可以用来表示某些向量,以及不具有向量数学结构的其他一维数据结构,但是捕获所有属于数学向量的对象还不够笼统(因为它无法区分不同的向量空格)。

考虑在#987上下文中向量的索引语义给出了向量不是AbstractVectors的另一个原因:通常不可能廉价地索引v',因为这完全取决于像= v'_A_w,而索引的成本则由matvec乘积A_w(或A'_v)决定,这肯定太昂贵了,无法成为AbstractVector。

这是一个循环参数。 如上所述,线性泛函(协向量)更为通用,甚至对于没有内积的向量空间也存在。 其次,当度量是正定矩阵A时,从向量v到辅助向量的自然映射确实是v'_A。 但是,这里的v'是什么? 它是否已经从v映射到相对于标准欧几里得范数(以度量为同一性)定义的其他协矢量? 不它不是。 如果向量具有相反的(较高)索引,并且协向量具有相同的(较低)索引,则度量A具有两个较低的索引,并且内积为= v ^ i A_ {i,j} v ^ j。 因此,该映射可以写成phi_j = v ^ i A_ {i,j}(使用phi与向量v相关联的向量)。 (请注意,这不同于典型的线性算子,其形式为w ^ i = O ^ i_j v ^ j)。 因此,该表达式v'_A中的v仍然是具有较高索引的那个,它仍然是相同的向量。

因此,实际上,我在上面试图指出的要点之一是,人们在不真正尝试使用向量的时候经常写v'。 他们只是尝试编写在向量上定义的表达式,例如内部乘积或类似于v'_A_w的熟悉矩阵表示形式中的v ^ i A_ {i,j} w ^ j之类的东西。 甚至标准欧几里得内积v'w也应读作v ^ i delta_ {i,j} w ^ j,并且不需要引入对数。 如上所述,我认为在大多数应用中,使用真正的适当的向量矢量(不是标量积的某种隐藏形式)的使用相当有限。 人们只是写v'就可以使用方便的矩阵语法,但是他们真正在做的是计算相对于矢量而不是协矢量定义的内积等。

顺便说一句,如果我们像Stefan最初的建议那样使v'== v,那么之前有一些关于乘法的关联性的评论。 但是,即使没有这个,我也不会说*是关联的,即使v'是一个不能用普通矢量识别的辅矢量:
A_(v'_w)是一个矩阵
(A_v')_ w产生错误

标量值函数的梯度确实是covector的适当应用之一,即,不带参数,梯度就是covector。 在诸如共轭梯度之类的应用程序中隐式假定的是,存在一个度量(实际上是反度量)可以将这些向量矢量映射回向量。 否则,做x ^ i(位置向量)+ alpha g_i(梯度)之类的事情是没有意义的。 正确的写法是x ^ i + alpha delta ^ {i,j} g_j,其中delta ^ {i,j}是反度量。 如果人们试图用非平凡的度量标准定义流形上的优化技术,那么就需要考虑这一(反)度量标准。 有一本不错的书:
http://sites.uclouvain.be/absil/amsbook/
和相应的matlab包:
http://www.manopt.org

2014年10月22日,22:52,goretkin [email protected]写道:

既然我们在谈论DFT和规范...今天这让我无所适从

如果fis是向量的函数并产生标量,则我希望diff(f)是接受向量并产生Covector的函数,以便f(x)〜= f(x0)+ diff(f)( x0)*(x-x0)。 梯度的一种非常常见的用法是在给定输入增量变化的情况下,获得函数输出增量变化的线性近似值。 如果输入是向量,则将梯度作为向量是自然的。

但是,如果要执行梯度下降,则需要将x处的梯度(自身的缩放版本)添加到其自身。 从这个意义上讲,梯度是指向最陡峭方向的向量,其范数与沿最陡峭方向的变化率成比例。

我的直觉说梯度是一个向量,更重要。

-
直接回复此电子邮件或在GitHub上查看。

A_(v'_w)是一个矩阵
(A_v')_ w产生错误

真是的

b2e4d59001f67400bbcc46e15be2bbc001f07bfe05c7c60a2f473b8dae6dd78a

对于其中包含幽默图像的帖子,此线程已过期。

@Jutho我承认我没有使用最普遍的对偶空间形式,但是鉴于我选择使用Banach空间的框架来定义对偶空间,因此我看不到我的描述完全是圆形的。 无论如何,对于有限维向量空间,Banach空间形式主义已经是多余的。

通函确实不是正确的术语。 我试图在该段中说的是,我可以颠倒关于条目有效评估的这一思路,因为例如在弯曲流形上的(共轭)梯度的情况下,我猜想梯度(covector) g_i可以有效地计算,但是将与x ^ i相加的向量A ^ {i,j} g_ {j}(使用A ^ {i,j}的反度量)可能无效。 我现在才注意到我以前的信息在Github上的轮廓多么糟糕。 我写电子邮件的时候很好。 我现在尝试修复它。 我不想重复我自己,也不想给(错误)印象,好像我不同意当前的提议。 我唯一要提出的观点。

  1. 对偶空间当然是向量空间,因此其元素是向量。 但是,目标不应该是具有矢量数学含义的所有对象都是Julia的AbstractVector子类型,也不是所有属于AbstractVector子类型的对象都具有适当的数学特征向量。 从这个意义上说,我更喜欢Matrix这个名字,而不是Vector这个名字,因为它的含义要少得多。 因此,我想我可以同意以下事实:无论在建议中引入了哪种Covector类型,它都不应是AbstractVector甚至是AbstractArray的子类型。 ,即使在V *与V自然同构的情况下(笛卡尔空间,约占应用的一半)。
  2. 内部乘积允许您构造从V到V_的映射,但这不是存在V_的先决条件。 当然,这听起来像是一个无关紧要的问题,因为在编程中,您始终具有有限维的向量空间,并且始终有一个内积(即使它可能与应用程序无关),但是我试图尝试的实际点是使是这个。 余向量在编程中有适当的应用,例如@goretkin的漂亮渐变示例,但是在大多数人编写v' ,他们并不是真正在尝试构造余向量,而是在尝试编写双线性使用v'_A_w = v ^ i A_ {i,j} w ^ j甚至v'w = v ^ i delta_ {i,j} w ^ j的两个向量之间的映射(即V x V到标量)方便的矩阵表示。 我更喜欢显式地编写dot(v,A*w) ,但这是个人选择。 由于我们没有关于矩阵的上或下索引的信息,因此可以构造用例,其中在标量表达式(如v'*A*wv'w可以是向量或协向量)在数学意义上。 这样做的唯一后果是,我不确定是否真的对调用v' Covector的类型感到满意,就像名称Vector它在数学上也太多了在大多数应用程序中可能没有道理的含义,然后引发更多(几乎不相关)的讨论,就像这样。

@jutho +1用于渐变为向量
我首先从史蒂夫·史密斯(Steve Smith)的博士学位论文中学到了这一点,并使用了这个想法
清楚地解释了共轭梯度用于特征值计算的模糊概念
人们过去常在化学和物理领域谈论。

我越来越对内部的一致性感到震惊
自成体系的是2阶张量的世界,其上,下指数为1。
这就是我们通常所说的矩阵计算,或者只是普通的老式线性代数。
哎呀,虽然我可以找到很多示例,例如norm(v,p)或fft(v),其中函数
如果它们是向量或向量,则各不相同,我真的没有一个很好的例子(但!)
在向量和一列矩阵上自然不同的函数
(有人帮助我,肯定有一个,甚至很多!!)

我也已经关注了@jutho几年
尚未完全找到空格,我记得与@StefanKarpinski聊天
几年前在我的白板上谈论这个问题。 仍然是
1)朱莉娅的新移民必须有轻松的经历,并且
2)性能
必须胜过这些花哨的东西。

通过与@jiahao交谈的结果,我真的感到有两个非常特别的世界:
线性代数(具有一个对数和一个余数的数组)和简单的张量
(每个人都是合作者,没有矛盾)。 后者在APL Mathematica中效果很好。
前者是线性代数,在MATLAB之前被最好地捕获。
他们嫁接在尺寸大于2的数组上。
在与@JeffBezanson谈了一个字之后,似乎还没那么疯狂。

顺便说一句,我认为这花了大约一年的时间,但我们终于认真对待了:-)

关于
A_(v'_w)是一个矩阵
(A_v')_ w产生错误

只有矩阵乘以“ *”是关联的。 重载标量时间矩阵永远不会
联想的。 甚至没有数学,甚至没有MATLAB。

A_(v'_w)是一个矩阵
(A_v')_ w产生错误

接得好。 但是,在任何情况下非错误都会产生不同的答案吗? 一个相关的问题:标量*向量可以做什么?

重载的标量时间矩阵从不关联。 甚至没有数学,甚至没有MATLAB。

仅涉及矩阵和标量的运算是关联的(请参见第3点)。 由于我们可以将向量视为1列数组,因此将它们包括在内也不会引起问题。 这里的问题是Covector是可以减小尺寸(将矢量映射到标量)的运算符,因此我不确定它是否适合。

标量矩阵加法是一个更大的问题,因为它破坏了分布性(尽管如果我没记错的话,我认为辩论决心保留它):

(A+s)*v != A*v + s*v

这是一个非常好的摘要:

通过与@jiahao交谈的结果,我真的感到有两个非常特别的世界:
线性代数(具有一个对数和一个余数的数组)和简单的张量
(每个人都是合作者,没有矛盾)。

显然,此讨论不是关于支持更一般的情况的,对于不需要完全一般性,不能包含在当前AbstractArray层次结构中并且更适合于一个包的人来说,这太令人困惑了(我实际上正在工作上)。 但是讨论的确实是,我们要支持这两个特殊世界中的哪个,因为它们并不相互兼容。

在前者中,所有向量v是列,所有v'是协向量,所有矩阵都是线性算子V-> W(例如,它们生活在W⊗V_中),这不包括例如bilinear映射V x V->标量(例如v ^ i A_ {i,j} w ^ j),因为这需要具有两个下标的矩阵(例如,生活在V_⊗W_中)。 当然,您仍然可以在实践中使用它并将其写为v'_A*w ,但是它与所选对象的命名法有点冲突。 另外,它没有指定高阶数组位于哪个空间。

后一种情况解决了该问题,因为它具有(V == V *),但导致令人惊讶的结果v' == v 。 此外,该解决方案实际上仅限于实向量空间,因为即使在具有标准欧几里德内积的复杂空间(即“复杂笛卡尔空间”)中,对偶空间也不自然对V同构,而是conj(V)(V bar),共轭向量空间(请参见http://en.wikipedia.org/wiki/Complex_conjugate_vector_space中的希尔伯特空间)

关于非关联性,在这方面,当前行为( v'*v不会产生标量而是数组)实际上更加一致,因为从那时起, A*(v'*v)(A*v')*v产生错误。

事物的“线性代数”方面可以另外解析为如何表示向量和余向量的不同选择:

  • 我们最近讨论了Covector,又名“有趣的行向量”建议。
  • 由Matlab等支持的“ no true vectors”语义充实了(N个向量为Nx1矩阵),(N个行向量为1xN矩阵)。
  • 当前的Julia方法-不合并稠密的N向量和Nx1矩阵,不合并稀疏的N向量和Nx1矩阵,将N行向量表示为1xN矩阵。

我想知道是否真的有必要在“无真向量”世界中合并(标量,1-向量和1x1-矩阵)。 @alanedelman和我昨天在讨论这个问题,在数值线性代数中,向量*标量和标量*向量的可交换性随处可见,但是向量*标量积是不在乎它是否在做(N,)*( ,)或(N,1)*(1,1)。

1)归根结底,最重要的事情是A)易于使用和B)性能。
2)两个世界应该能够完全和谐地共存。 在进行工程工作时,我认为没有什么比张量表示法更直观,更简单了。 如果可以的话,一个建议可能是启用环境标志,或者可以在程序开始时导入程序包。 这将允许易于使用和简单的编程逻辑。 这是不可能的吗?还是我们必须选择一种总体模式?

似乎有两个选择
1)启用工具箱,补丁或环境标志的导入以同时工作
2)建立一种能够统一特殊世界的语言,但仍然易于使用和快速

我不确定这有多大前景,但我偶然发现了一个潜在的有趣研究领域。

请允许我注意:

几何代数研究组
http://www.mrao.cam.ac.uk/~clifford/pages/introduction.htm

几何代数讲座
http://www.mrao.cam.ac.uk/~clifford/ptIIIcourse/course99/

几何代数的物理应用
http://www.mrao.cam.ac.uk/~clifford/ptIIIcourse/

21世纪物理学和工程学的统一数学语言
http://www.mrao.cam.ac.uk/%7Eclifford/publications/ps/dll_millen.pdf

几何代数
http://arxiv.org/pdf/1205.5935v1.pdf

几何代数计算的基础
http://link.springer.com/book/10.1007%2F978-3-642-31794-1

几何代数计算
http://link.springer.com/book/10.1007%2F978-1-84996-108-0

在研究了这一点之后,它看起来真的很棒。

只要将代数和几何分开,它们的进展就很缓慢并且其使用受到限制。 但是,当这两种科学结合在一起时,它们便发挥了各自的共同作用,并共同走向完美。

  • 约瑟夫·路易斯·拉格朗日

想象一下需要眼镜,而不知道您需要眼镜。 然后,当您戴上眼镜时,世界就会发生意外的变化。 GA就像是大脑内部的眼镜。

  • 巴勃罗·科拉宾托(Pablo Colapinto)

这个家伙似乎有正确的主意... https://github.com/wolftype/versor

典型的矩阵运算库具有用于向量和矩阵乘法的模板内联函数。 几何代数结合了许多其他数学(矩阵,张量,向量和谎言代数)。 Versor与此类似,但是在类固醇上,各种大小的向量和稀疏矩阵都被称为多重向量,它们代表的几何元素不仅限于xyz方向和变换矩阵。 圆,线,球,平面,点都是代数元素,旋转,扭曲,扩张和弯曲这些变量的算子也是如此。 这些元素和运算符都是多向量,它们以许多许多不同的方式相乘。

该视频详细介绍了已编程的GA数学语言Versor。
https://www.youtube.com/watch?v=W4p-e-g37tg

这些是幻灯片。 https://github.com/boostcon/cppnow_presentations_2014/blob/master/files/generic_spaces.pdf

您可以轻松地进行一些惊人的张量计算,并在编译时针对速度进行了优化。

@ esd100 ,我认为将这个线程上的讨论重点放在标题中的特定问题上会很有帮助。

@johnmyleswhite我搜索了“张量”一词,被提及了171次(现在是172次)。 虽然我同意您的说法,但总体而言,该主题有157条评论(现为158条),其中一些直接或间接地处理了原始帖子的标题(“认真考虑转置”)。 我认为我的帖子通过提供更多的视角来探讨张量数学的新应用(通过几何代数)可以完成的工作,从而间接地处理了原始帖子的标题。 我的观点是,将Versor具有的更高维度的能力构建到Julia中,这将是Julia的另一个有益功能。 youTube视频的标题是“通用空间的通用编程:使用C ++ 11的编译时几何代数”。 我不明白为什么那不是朱莉娅而不是C ++。 再说一次,我不是数学家还是计算机科学家。

@ esd100几何代数很有趣,但并不是本期涵盖的最普通的东西。 我们首先要关注的几何代数是实Clifford代数Cl(R ^ n,I); 但是,我们也会对其他非几何代数的Clifford代数感兴趣,例如像复矢量Cl(C ^ n,I)上的Clifford代数,甚至是任意场或非交换环Cl(F ^ n,I)上的代数。 此外,我们甚至不想将自己局限于Clifford代数,而是要考虑线性代数如何推广到不必定义二次形式(内积)的更通用的张量代数设置。

在张量代数设置中,OP中的“ v'是no-op”提议是很有意义的,因为它一致地泛化为逆反代数

@jiahao非常感谢您的丰富反馈。 精通这个主题的人很高兴。 我会对更多地研究这些主题感兴趣,以便更好地理解。 我很好奇为什么存在用于BLAS的超级优化代码,但是对于像Clifford Algebra这样的更复杂的代数却没有超级优化的代码。

我不确定讨论是否仍在进行中,但是我想分享我的看法,因为这些问题是使用Julia时我最重要(最烦人)的部分。 我同意以上某些意见,而我不同意。

基本上,我认为我们需要意识到这一点:所有Julia用户都希望使用Julia中已实现的快速多维数组(用于数据存储)。 许多/大多数用户也会对线性代数感兴趣,并且上述数组是打包包含在向量和矩阵中的数据的便捷方法。 一些用户会想要做通用张量(多线性)代数。

问题是如何将这些“裸”数组扩展到线性代数的数学概念? 您如何使语法看起来像普通的数学? 人们会对v''!= v感到满意吗? 等等

第一件事是,我们不想让数组变得更糟,只是可以做线性代数。 我们不想将其他数据添加到Vector或将不必要的模板参数添加到Array。 在不进行线性代数运算时,我们希望与C / C ++一样快(希望时与C / fortran一样快)。

我们都应该意识到的第二件事是,完全的多维张量收缩需要大量的额外信息。 对于最多2维的张量,请写任何乘法,例如A_B'_C * D ...而对于普通张量,更自然的是考虑定义收缩的图(张量网络图或因子图-同样的事情由许多不同的名字)。 对于高维张量,包装裸数组并用矢量空间等装饰它们以跟踪所有内容是有意义的。 这可以在Jutho(可能还有其他)正在研究的软件包中完成。 没有必要考虑3维维数张量上的'的含义-当存在置换排列或使用专用张量包时,没有人会对使用该函数感兴趣。

核心用户将需要将矩阵相乘,向量相加等等。 他们将要转置矩阵。 他们还将需要内部产品和外部产品。 或多或少,这些与双重空间结合定义。 我们知道这些是用于实数和复数的,并已预先定义它们。 是的,真实的有限向量空间的对偶空间感觉就像是同一件事,这是事实,我相信这是使整个问题蒙上阴影的原因。 当前最大的问题是我们没有适当的对偶向量。 没有它,我们无法像在纸上和纸上一样在Julia中“编写”方程式-这是一个巨大的问题! 更重要的是,我们没有v''= v。这非常不直观。

人们只希望或需要创建对偶向量来执行内部或外部产品。 一个简单的装饰性包装程序可以告诉编译器下次遇到*时应该做什么,这是一个明智的解决方案,并且运行时开销应为零。 我认为这些对偶向量/协向量应该是可索引的,可加/可减的,乘以标量,乘以向量(返回标量)并乘以矩阵(返回协向量)-就是这样! 我什至不会明确执行复杂向量的复杂共轭-可以将其内置到点乘积,外部乘积,索引等中,以提高速度/内存。

我并不担心我们会给类型系统增加复杂性。 我认为这是封装用户在高中和大学学习数学时所必需的:具有关联*(定义明确),具有v''= v,具有“ bras”和“ kets”(I在这里使用线性代数的Dirac表示法),等等。

顺便说一句,如果这个东西已经在Julia 0.4中实现了,那么我就忘了! 我仍在努力理解0.3.4 ...

@andyferris ,我通常都同意所有这些。 我认为这里的建议可以稍作修改并很好地工作:代替M*v'产生错误,而不是v*M产生错误,而是产生一个向量。 对我而言,从概念上讲,这等于M不知道其尺寸是向上还是向下–您可以为内部产品写v'*M*wv*M*w' ,任何一个都可以使用。

一个一直在反弹的想法是拥有行主要数组和col-major数组,并且将M.'从一个更改为另一个,并且类似地以v'作为v的行主要变体

+1到@andyferris 。 我还认为我们只希望简单的包装类型TransposeConjTranspose允许我们使用简洁的矩阵代数语法编写矢量,矩阵及其转置的乘积。 关于@StefanKarpinski提案的第2点,我不会将这些包装器限制为AbstractArray对象的容器,并且不会使类型成为AbstractArray类型层次结构本身的一部分。 Transpose(x)应该只是在表达式中写入x'而产生的类型,并允许通过派发Transpose来推迟其求值/进行懒惰求值(具体取决于表达式的其余部分) *中的情况下,99.9%)。 但是,人们应该也可以为这种语法赋予新的含义,这些新类型可能不是AbstractArray层次结构的一部分,例如矩阵分解对象或用于定义例如线性运算符的其他类型。

对于矩阵和向量的特定情况,我并没有真正看到M*v'的用例,但是我并不本质上反对它。 重要的是,它满足了基本期望(即,Stefan提案末尾的第2、4、5、6和8条规则)。

讨论的最后要点可能是切片的相互作用。 矩阵的行切片应自动为Transpose吗? 我的表决是针对APL切片规则的,并非如此。

@Jutho的动机是使*关联性- A*(v'w)(A*v')*w都可以工作并产生相同的结果,并且对底层元素类型进行模非关联(例如,浮动-点)。

为了使(A*v')*w获得与A*(v'*w)相同的结果, A*v'必须是N=3数组。 那是你的想法吗? 我完全错过了。

好的,非常有趣,我有几点评论。

首先,我们需要解决核心用户。 基本上,首先使用Array{T,n}作为n维存储是其主要功能。 是有意义的谁没有关于线性代数的想法,但谁不想在朱莉娅操纵数据的程序员。 因此,在任何情况下数组切片都不能返回协矢量! 这是一个严格的线性代数概念,仅适用于某些数据类型T ,这些数据类型可以定义向量空间及其对偶(例如,数值数据或“字段”)。

我可以选择APL切片。 似乎足够直观。 它是类型稳定的,没有任何令人惊讶的事情。 尽管我认为没有必要进行更改以使线性代数自洽。 我感到不安的是M[1,:]的大小是1xn,而M[:,1]的大小是n(不是nx1)...似乎太不对称了...但是我想这很好地提醒了人们布置在内存中。 无论如何..只有在对抽象数据存储和操作有意义的情况下,才应进行此更改。 对我来说,这种变化与线性代数的讨论正交。

因此,即使我们实施APL切片规则,我们仍然可以非常直接地挽救有意义的线性代数。 如果你想在i的列向量M调用colvec(M,i) ,如果你想在i的第i行共同矢量M呼叫rowvec(M,i) 。 如果i是一个元组或向量,则它可以返回一个元组或向量或(协)向量(在某些情况下,这对于推理并行化有用吗?)。 如果您希望使用矩阵,则只需使用常规的索引符号即可。 如果我们使用APL切片规则,那么使用*符号来区分行切片和列切片的动作将非常有用。 (需要考虑rowvec复杂共轭行为)。

这样,如果您正在执行线性代数,一切都会变得有意义,并且用户将不必担心Julia实施的特定切片规则。 运算符'将仅作用于矢量和余矢量(以更改修饰)和矩阵(以直接方式或惰性方式)。 可能更容易记住如何从特征分解中提取基向量,而我似乎总是忘记了这一点。

对于* ,我认为Julia中的矩阵/矢量代数应该可以工作,因此它看起来和感觉像数学上的乘法运算符,而不像两个矢量,两个协矢量,两个矩阵等之间M*v' ,因为严格地在数学中,您需要使用“ \ otimes”符号(圆圈内的时间符号)而不是标准乘法符号来编写方程。 使用乘法符号的含义不明确! 我们不能有w*M*v' == v'*M*w因为矩阵乘法不是可交换的。 同样,对于M*v'*v ,谈论*关联性没有任何意义。 您可以将其解释为需要两个不同乘法符号的innerproduct(outerproduct(M,v'),v) ,也可以解释为标量乘法M * innerproduct(v',v) ,在这两者中都可以使用* 。 使用此表达式,我们可能需要括号,或者也许编译器可以搜索有意义的操作顺序(此处请注意,最快的求值顺序也是我认为唯一有效的求值顺序)。

使用向量,协向量和矩阵,我们可以得到与标准数学一致的代数系统。 每当您要在两个向量,两个协向量或两个矩阵之间求外积时,您就必然会转移到多线性代数或通用张量代数。 在这里,您可能会有矩阵和向量使用新符号像kron(M,N)一样相乘。 或者,您可能要求用户使用完整的多线性代数包。 或其他...似乎超出了原始问题的范围(14个月前,顺便说一下...)

综上所述,我看到这里发生了四个几乎完全不同的事情:

  1. 使用APL切片规则改进对Array s任意数据的切片。
  2. 通过添加一些简单的概念,使Julia中的线性代数更加直观并与数学完全一致:余矢量和从矩阵中提取余矢量和余矢量的方法,以及余矢量,矩阵之间的运算。许多用户甚至可能没有注意到余矢量,因为使用1xn和nx1矩阵将继续按预期工作,向量的行为相同。
  3. 为了提高速度,请使用类似于covector的包装类型来实现transposeconj等的惰性求值,但也适用于矩阵。
  4. 开发通用张量包,并可能将其添加到标准库中。

可能只有第一个会是一个重大变化? 其他的则改进或增加了当前功能。 它们都可以或多或少地独立实现,并且可能都是值得做的事情。 (PS,如果您确实想要APL切片,我建议您尽快进行,在Julia变得“太大”并且它破坏太多的包之前,等等)。

是的,很抱歉,我建议允许M*v'并没有任何意义,因为不同的产品关联方式会产生完全不同的形状。 因此,我认为,如果我们要对此进行更改,那么我的原始建议就是要走的路。 到目前为止,这似乎是与APL切片行为的最佳组合。 当然,是否需要APL切片行为是一个更高层次的考虑。

我只想说几件事,请带些盐吃。 他们只是意见,我知道我的负担并不大。 但是,当我阅读@andyferris评论时,有一件事令我

@ esd100是的,我确实想到了这一点。 但是有了朱莉娅,我认为我们想变得贪婪……想要迎合许多目的,并且在很多事情上都表现出色。 处理非数值数据可能有真实的科学原因。 Julia的使用者可能是现任或前任科学家,他们希望进行更多常规编程。 我先前的观点是,我们不应通过给它一个额外的字段来谈论转置/共轭来减慢Array速度或占用更多的内存。

如果执行APL切片,则行或列切片应返回相同的对象。 问题是:没有APL切片, M[1,:]返回的最佳方式是什么? 好吧,如果M[1,:,:,:,:]返回Array{T,n} ,我觉得如果M[1,:]返回covector{T}会使所有人感到困惑。 如果我们让M = zeros(3,3,3)并调用M[1,:] (当前返回一个1x9矩阵)怎么办? 我们也应该使它成为向量吗? 如果MArray{String,3}怎么办?

在我看来,这似乎非常令人惊讶和困惑。 如果将来会发生这种情况,这也与APL切片更改不一致。 因此,我建议为线性代数目的添加新函数rowvec(M,i)colvec(M,i) 。 这不是理想的,它增加了新的功能...但是至少很清楚它们应该为线性代数目的返回什么。 这是我唯一想到的,它对通用数组具有很好的属性,对于线性代数(以及向量矢量类型)具有很好的属性,而没有尝试适应多线性代数。 如果有人对从矩阵中提取向量有更好的记号,那也将是一件好事!

@ esd100 :我非常确定程序员是Julia设计的受众。 科学计算是Julias的强项,但有许多Julia用户将其用作通用编程语言。

我同意@andyferris的观点,即必须将张量要求与容器要求分开。

在没有阅读整个线程的情况下,我个人的问题是,如果我有一个3D数组A (例如,断层扫描数据)并且

imagesc( data[1,:,:] )

这是行不通的。 恕我直言data[1,:,:]data[:,1,:]data[:,:,1]应该是2D数组(或子数组)。 当前,我使用自定义的squeeze函数来克服此问题。

同样,这只是我的观点,而且是非常不重要的观点,因为我还没有采取行动。 我觉得在开发应用程序时,应该有一套指导它的设计原则。 建设者必须就其目的和身份发送清晰,统一的消息。 如果要开发快速,直观,先进的技术计算平台是Julia的目标,那就坚持下去。 不要通过试图使其适合普通程序员的目标来发送混合信号。

关键是,即使对于纯粹的科学或数学应用程序,也存在几种相互矛盾的解释,例如,可以使用向量和矩阵表示多个数学对象,因此不应做出过于具体的选择。

最好有专用的软件包来满足遵循一组特定约定的特定学科的需求。

@jutho我的直觉是您说的对,但我想知道您得出的结论“包装”是“更好”的基础是什么,与什么替代品相比呢?

这很简单。 对于大多数Julia用户而言,重要的功能都属于Base。 更为特殊的功能属于一个包。

当然,在这里画一条线不是很容易。 但是,最好的方法是创建一个程序包,以便许多人可以对此进行测试。 最初在一个软件包中开发了Base中拥有的各种核心功能。

@ esd100 ,我不太了解您的问题。 最后,科学语言应该提供的数据结构和方法可以用科学中的典型对象来表示和计算。 但是某些数据结构可能对表示不同的数学结构很有用,有时对于相同类型的对象而言,不同的表示可能很方便。 因此,将固定的数学结构与数据类型相关联可能对某些学科而言过于严格,而对其他学科而言则过于复杂。 因此,这不应在Julia的基础上进行,而应采用仅尝试满足某一学科需求的特定软件包。

对于当前的讨论,使用向量和矩阵的每个人都将期望有可能对向量进行转置,并使v ^ T * w =标量。 但是这些向量和矩阵可能用于表示许多不同的事物,这可能取决于领域/学科。 我在上面的文章中给出了v ^ T可能不是实际的向量的示例。

2015年1月11日,18:10,esd100 [email protected]写道:

@gutho https://github.com/jutho我的直觉是你是对的,但我想知道你的结论是什么基础的基础,即软件包“更好”,以及与哪些替代方案相比呢?

-
直接回复此电子邮件,或者在GitHub https://github.com/JuliaLang/julia/issues/4774#issuecomment -69501771上查看。

这很简单。 对于大多数Julia用户而言,重要的功能都属于Base。 更为特殊的功能属于一个包。

我认为这不是那么简单。 例如,在Base中有Bessel函数,对于大多数Julia用户而言,它们不太可能很重要。 它们可以出现在Base中的原因是,对于Bessel函数的含义,有一个合理的通用标准,没有人可能将这些名称用于其他用途,或期望它们做一些不同的事情。

通过谨慎的(有些人可能会说冗长的)命名约定,Mathematica能够以核心语言放置4000多个函数,而且我发现几乎不必加载程序包非常方便。 相反,当我使用Python / Sage时,一个文件/笔记本在顶部显式导入200个函数并不稀奇(避免使用更难以追踪的“ from ___ import *”语法)。 每次我需要使用非内置函数时,我都必须执行一些额外的步骤:

(1)尝试记住我是否已经在该文件中使用过它,和/或进行文本搜索以确认(如果名称是其他内容的子字符串,则限制为整个单词)
(2)要么:(a)立即添加导入内容,迷失了思路,又找到了它; 或(b)尝试记住所做的事情后添加导入,将另一件事保存在内存中
(3)对于不太常用的功能,可能必须查找它所在的程序包

这会令人烦恼和衰弱。 因此,我认为,就像Bessel函数一样,任何被认为是标准的东西(因此不会引起名称冲突或混乱)都应该放在Base中,而不管是否有人使用它。 当然是线性代数。


回到主题,我们一直在讨论几层功能-从数组作为语义上的裸容器(在Base.AbstractArray ),到具有向上/向下语义的笛卡尔张量对象(如我所描述的) AbstractTensorArray proposal ,到索引映射到向量空间的更一般的张量对象(如@JuthoTensorToolbox )-增加通用性并降低优化潜力。

我认为对于用户而言,在这些层之间轻松过渡非常重要,这尤其是因为用户可能不知道起步时实际需要的普遍程度。 作为一个简单的示例, @ Jutho指出,在@jdbates的图像处理示例中,用户很可能希望将索引的不同子集与不同的几何形状相关联,从而以一种方式处理图像坐标,以另一种方式处理色彩空间; 但是,如果他们刚开始只使用其中的一种几何图形,应该方便地转换为更通用的表示形式,以允许每种几何图形都使用自己的几何图形。 如果在一般性的每个级别上启动的附加函数(或函数调用模式)都使用明智的默认值,例如,将AbstractTensorArray s中的上/下索引和中性索引向上转换为单个默认的笛卡尔矢量空间,则分别是“序列”空间-然后它变得几乎无缝了。 功能较低层的用户无需知道或关心较高层,直到他们需要它们为止。

对于相关用户而言,对于层次结构应该是清楚且可预测的任何部分,都可以合理地放入Base中。 真正的问题应该是-无论是用于数组运算,线性代数还是张量代数-这类功能的用户期望或希望以其他方式使用的可能性有多大?

很多人不喜欢在Python领域中完成任何工作需要多少个进口的荒谬粒度,我认为没有人会建议那么长的时间。

但是有一个重要的论点,在某些用例中,大量的依赖项和库膨胀是不受欢迎的,Julia语言实际上不应该要求在计算机上安装Fortran运行时库,但是从现在开始,无论是否使用Julia标准库,您要运行的代码正在执行任何线性代数(或Bessel函数或FFT等)。 #5155和所有其他问题都很好地解决了这些问题-“默认情况下安装”和“任何功能的最低要求”最终应分成不同的模块组。

谢谢托尼,我同意了。 此外,使用我们的软件包系统,将像“工具箱”这样的软件包打包在一起确实没有问题。 使用@reexport宏,效果很好。

但是还有另一点。 在一个包中,将想法向前推进要容易得多。 如果有人想改变某件事,那就简单地做到这一点。 在Base中,限制更为严格。

看起来,虽然拼凑了多个软件包确实可以实现更大的独立性,但它也创建了一个分散的平台,使导航变得更加困难。 似乎使用更统一,更通用的结构将简化并促进跨学科的交互和对新领域的探索。

目前最常用的向量*矩阵方式是什么?
例如说我有X与形状(K, N)b与形状(K,) ,我要乘b上向左获取长度(N,)的向量。
我叫BLAS.gemv('T',1.0,X,b)
reshape(b'*X,size(x,2))
两者都有点丑陋。

我想你可以做X'b

2015-03-13 17:15 GMT-04:00 joschu [email protected]

目前最常用的向量*矩阵方式是什么?
例如,我有X的形状为(K,N),b的形状为(K,),我想要
用左边的b乘以得到长度为(N,)的向量。
我会叫BLAS.gemv('T',1.0,X,b)
或重塑(b'* X,size(x,2))
两者都有点丑陋。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/4774#issuecomment -79405868。

是的,我以为会触发X的副本。
但是@time似乎表明没有副本,基于内存分配

julia> X = rand(1000,1000); y = rand(1000);

julia> <strong i="8">@time</strong> y'X;
elapsed time: 0.00177384 seconds (15 kB allocated)

julia> <strong i="9">@time</strong> X'y;
elapsed time: 0.000528808 seconds (7 kB allocated)

我们进行'的奇特解析,因此X'y最终为Ac_mul_B(X,y)并使得
您建议的相同的BLAS电话。

2015-03-13 17:28 GMT-04:00 joschu [email protected]

是的,尽管我曾经认为会触发X的副本。
(但是@time https://github.com/time似乎表明没有
复制,基于内存分配

茱莉亚> X = rand(1000,1000); y = rand(1000);

朱莉娅> @time y'X;
耗用时间:0.00177384秒(已分配15 kB)

朱莉娅> @time X'y;
耗用时间:0.000528808秒(已分配7 kB)

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/4774#issuecomment -79421713。

信不信由你,整个线程的写信越来越接近实现。

最后一点:有人考虑过吗? 可能实际上是与'完全不同的动物? 后者具有对偶的正确结构,但是。 如果基础字段不是实数则不行。 分配是疯狂的。 换位的“反转所有索引”概念并将所有对偶性概念保留为',从而产生“有趣的行向量” /厄米共轭?

我认为在任何情况下conj(transpose(x))都应等于ctranspose(x)

如上所述,我希望不会为transposectranspose将创建的包装器类型赋予一个包含特定数学概念的名称,例如dual 。 诸如Julia之类的科学语言应提供一组有用的数据结构并应执行常见的操作,但是我认为将严格的数学结构与之关联是错误的,因为这不适用于某些应用程序且过于复杂其他。 用户可以使用这些数据结构和运算来在其应用程序中实现数学运算。 肯定有z.' * A * z返回标量的应用程序,其中z是复数向量和A一些矩阵,即使这与内部乘积和/或对偶无关向量。

@jutho您能为z.' * A * z提供一个用例吗?

如果z1z2是两个复数向量Z1Z2 (例如,Kähler流形切线空间的全纯部分中的切向量) )和a是张量A的矩阵表示,具有两个协变索引(例如Kähler流形的复数(2,0)形式),然后是A(Z1,Z2) = z.' * a * z

请注意,我在这里强调,朱莉娅对象z1z2a仅形成某些数学对象的_representation _(相对于选定的基础/协调),因此在不知道这些数据结构代表什么的情况下,数据结构上的运算不能唯一地与数学运算相关联。

@jutho谢谢。 您关于制图表达的观点已被很好地接受,并且在本次讨论中已被多次代表。 我正在尝试找到向量和矩阵的最小接口,并查看该最小接口是否与数组的最小接口在根本上不兼容,以及我们可以分担用户定义的抽象数据类型的事情。

在这一点上,我完全赞成@StefanKarpinski的建议,上面的@andyferris赞同。 特别是

  1. APL样式索引
  2. v'给出某种Covector或Transpose类型

其他一切都是细节。 添加row(M,i)col(M,i)函数可能很好。 否则,要提取一行作为余矢量,我猜您将需要M[i,:].'吗? IIUC, M[i,:]'在这种情况下会不需要conj吗?

是的,我应该补充一点,就是我上次编写的APL风格索引并不是很好,但是现在我完全支持进行此更改。 反过来,这使得对偶矢量的内容更具吸引力,并具有row / col函数。

从那时起,我一直在尝试实现,最令人困惑的是从矩阵提取的协矢量是否是共轭的……

我认为您想采用矩阵的文字值。 使用狄拉克表示法,展开(任意)矩阵M = sum_i | i>例如[0,0,1,...,0],我们要提取U我们得到row(U,i)' = col(U’,i) ,这对于从特征分解中提取左右特征向量非常有意义。

安迪

2015年3月15日,晚上9:36,Jeff Bezanson [email protected]写道:

在这一点上,我完全赞成@StefanKarpinskihttps://github.com/StefanKarpinski建议,也大多由@andyferris呼应https://github.com/andyferris以上。 特别是

APL样式索引
v'给出某种Covector或Transpose类型
其他一切都是细节。 添加row(M,i)和col(M,i)函数可能会很好。 否则,要提取一行作为余矢量,我猜您将需要M [i ,:]。 ? IIUC,M [i ,:]'在这种情况下会不需要做一个conj吗?

-
直接回复此电子邮件或在GitHub https://github.com/JuliaLang/julia/issues/4774#issuecomment -81228816上查看。

是否有任何志愿者开始从事这项工作? 例如@mbauman@jakebolewski ,我很惊讶地发现它们已经不在这个线程中了:)

查找需要更改的所有内容可能很乏味,但是对索引行为的基本更改应该不会太糟糕。 @jiahao@andreasnoack可能会更多地讨论应如何合并Covector,例如,如果有的话,它们的超类型应该是什么。

我们需要9条评论,而无需再发表8条评论。

我可以帮忙。

我们非常接近

作为相关注释,如果将有TransposeCTranspose包装器类型,则它们也应该是普通的Conjugate包装器类型,例如conj(A)是也懒。 对于用BLAS乘以矩阵这并不是真正有用的,因为没有对此的特殊支持(并且BLAS中的C表示厄米共轭),但是如果有完整的Julia BLAS实现,也很好地支持conj(A)*B没有显式共轭。

首先,我觉得我现在比以前更重视矢量转置。

也许@andreasnoack@simonbyrne可以告诉我们是否应该重新审查#6837。

我同意@simonbyrne的意见,我们不应拥有Transpose{Array} <: AbstractArray

其他一般想法:

  • 我意识到,外部产品计算当前包含“不自动添加尾随单身尺寸”规则的例外。 如果u大小(n,) ,则u * u'是涉及(n,) x (1, n) 。 无法使用矩阵乘法的普通规则_unless_来计算此乘积,我们会自动将第一个参数重塑为(n, 1)
  • MATLAB索引语义的“自动添加尾随单例维度”规则与“转置反转所有维度”规则根本不兼容。 根据前一条规则,形状(n,)的数组在语义上与形状(n,1)和形状(n,1,1)的形状整形的数组相同,但请注意,如果转置会反转所有维,则所得数组的形状(n,)(1, n)(1,1,n) ,如果只允许添加尾随的单例,则它们不能相等。 从逻辑上讲,数组的转置可以具有任意数量的_leading_singtons,因此具有模棱两可的形状,这在逻辑上是不一致的。

我还进行了文献调查,并发现了一些有趣的APL历史。 我们所谓的APL索引规则不在艾弗森(Everson)1962年的书中,而是在APL \ 360(1968年; APL的第一个实现)中存在。 但是,APL \ 360将标量和1-向量混合在一起,这种不一致直到文献中才被认识到(Haegi,1976)。 关于多维数组形式语义的讨论首先出现在Brown的博士学位论文(1972年;他后来设计了APL2)中,并激起了将其语义形式化的工作。

对导致APL2的发展的出色调查是:

  • 卡尔·弗里茨·鲁尔(Karl Fritz Ruehr)。 “对APL扩展的调查。” 在APL国际会议论文集中,APL '82,第277-314页,美国纽约,纽约,1982年。ACM。

在文献中值得注意的是对索引规则的关注是:

  • T.更多。 “数组理论的公理和定理。” IBM杂志,1973年3月17日:135-175。

    • 一个使用Quine公理化集理论形式化多维数组语义的巨著,展示了标量如何与具有自包含集概念的rank-0数组相结合,以构造APL文献称为“浮动数组”的东西。 ([1] == 1,与“接地阵列”相反,其中[1]!=1。后来Sheila M. Singleton在她的1980年硕士论文中的工作表明,莫尔的阵列理论也可以用于描述接地阵列。)

    • 更多内容还提到将返回标量的内部乘积作为指导数组语义的重要规则。

    • 更多内容还提到处理普通多维数组的“上下”的复杂性:

      “让V是一个场上的n维向量空间。忽略对数和协方差的考虑,V上的价q的张量是列表VV ...长度为q的V的笛卡尔积到向量中的多线性映射如果V具有一个底数,则可以用_component tensor_表示V上的化合价q的张量,它是q轴上的数组,每个轴的长度为n。”

  • G.刘易斯。 “用于APL的新数组索引系统”,第七届APL国际会议论文集-APL '75,234-239,1975年。

    • 本文是第一个在索引操作中倡导索引元组成为APL中的一流对象的人,并指出通过在索引元组的每个等级上使用系统笛卡尔积,可以实现索引结果的一致构造。

  • 汉斯·R·海吉。 “将APL扩展到树状数据结构。” ACM SIGAPL APL Quote Quad,7(2):8-18,1976年。

    • 本文抱怨经典APL \ 360中的不一致,该问题混淆了标量和1数组,并主张APL索引规则要求不保留这种合并。

    • 本文还包含与Lewis,1975非常相似的构造。 这项工作似乎是独立的。

  • JA Gerth和DL Orth。 “在APL中建立索引和合并。” 在国际杀伤人员地雷国际会议记录中,APL '88,第156-161页,纽约,纽约,美国,1988年。ACM。

    • 注意,可以通过将索引视为将索引集映射到值集的广播操作来证明APL索引规则是合理的。 这种功能上的解释自然暗示了等级保留规则以及Lewis和Haegi的笛卡尔构造。

另外,如果没有“可以添加尾随的单维尺寸”规则,我们就不会服从身份

image

因为左侧是标量(根据trace的定义),而右侧的形状是(1, n) x (n, n) x (n,) = (1,) 。 相反,我们可以将此身份作为选择向量转置语义的指导原则。 要点是定义第一个标识的跟踪操作的循环属性。 迹线内的数量必须是标量或矩阵(未定义向量的迹线)。 Avv'已经明确地是一个矩阵: (Av)v'A(vv')产生相同的结果。 但是从第二个量来看, v'Av必须是一个矩阵或一个标量。 (如果为标量,则第二个标识也成立。) v'Av只有在“可以添加尾随单身维”规则处于活动状态时才可以是1-vector,在这种情况下,可以透明地将其重塑为1x1矩阵。

因此,如果我们希望定义轨迹,那么它必然会对外部产品vv'和二次形式v'Av的允许形状施加限制。

@jihao :我不太确定您要反对我们的主张。 您能否更清楚地陈述“可以添加尾随单例尺寸规则”? 什么时候适用? 您认为我们应该支持吗?

我认为您可以采用某些论据来加强我们无法将转置视为反转所有维度的立场(列向量将被转置为列向量,或者可能是具有任意数量的前导单例维度的数组)。 为了与矩阵代数保持一致,我认为必须将其替换为交换两个第一维。 然后,我相信您陈述的一些矛盾消失了。 希望,如果我们在转置时不添加任何单例尺寸,其余部分将消失(covector缺少的第一维不计算在内)。

上面我的一般评论不是提倡这些规则,而是帮助描述数组索引规则的设计空间以及它们与线性代数恒等式的相互作用。

MATLAB使用了“可以添加尾随的单维尺寸”规则,并引入了(根据@alanedelman)以支持多维数组。 MATLAB数组中使用的索引到偏移的计算由sub2ind定义,无论您向其抛出多少个尾随的1,它都会返回相同的线性索引。 此外,MATLAB的矩阵索引文档还指出了用于索引操作的内容:

为B指定的下标数目(不包括等于1的结尾下标)不超过ndims(B)。

更正式地说,我认为可以这样表示:

对于将数组作为输入的操作, (n,) -arrays, (n,1) -arrays, (n,1,1...) arrays在这些操作的有效参数上都在同等的类中。 (如果n=1 ,则等效类还包括标量。)

例子:

  • A*bA\b ,其中A是矩阵,而b可以是向量或矩阵( n x 1矩阵的相同语义),
  • hcat(A, b) ,其中A是矩阵,而b可以是向量或矩阵

在大多数情况下,除了外部产品示例外,Julia没有此“可以添加尾随单件尺寸规则”规则。 可能还有其他人,但我现在想不起来。

只要Transpose{A<:AbstractArray}不是AbstractArray的子类型,我想我就不会遇到问题(但是我可能忽略了某些东西,因为我没有像你们这样想:

typealias AbstractVectorTranspose{A<:AbstractVector} Transpose{A}
typealias AbstractMatrixTranspose{A<:AbstractMatrix} Transpose{A}
typealias AbstractTMatrix Union(AbstractMatrix, AbstractMatrixTranspose} 

(以及类似的CTranspose ),我们可以拥有

AbstractVectorTranspose * AbstractVector = Number
AbstractVector * AbstractVectorTranspose = AbstractMatrix
AbstractVectorTranspose * AbstractTMatrix = AbstractVectorTranspose
AbstractTMatrix * AbstractVector = AbstractVector
AbstractTMatrix * AbstractTMatrix = AbstractTMatrix

唯一的开放的问题是AbstractVector * AbstractTMatrix时,应支持的AbstractTMatrix具有1为第一尺寸,或者是否AbstractVector * AbstractVectorTranspose是足够的。

同样,新类型的系统可能有助于更小心地表达其中一些typealiasunion

同样,如果例如v.'*A计算为(A.'*v).' ,则如果A本身例如是A=B' ,则弹出Conjugate包装器的需求。

我同意@simonbyrne的意见,我们不应拥有Transpose{Array} <: AbstractArray

你能在那儿详细说明吗? 我认为https://github.com/JuliaLang/julia/issues/4774#issuecomment -59428215中的意见是CoVector不应该是AbstractVector的子类型,但是对于我来说,没有Transpose{Matrix} <: AbstractArray似乎有点奇怪

对于它的价值,我认为CoVector应该表现得像Vector除了Vector转换为Matrix作为列矩阵,而CoVector转换为Matrix作为行矩阵。

我想这将意味着索引到向量中应该与行矩阵中的工作相同吗?

这是一个有趣的想法。 如果仅在转置/辅助矢量类型中删除_lead_单例维度,事情会变得更容易或更复杂吗?

(我一直很感兴趣地关注这个问题,但是我的线性代数很生锈,以至于我没有资格做出贡献)。

@mbauman

如果仅在转置/协矢量类型中仅放掉前导单调维,事情会变得更容易或更复杂吗?

如果允许任意数量的前导单例维,则数组索引将不再是有序的,也没有“第一”维之类的东西,这很奇怪,我们不妨将有序作为公理。

@tkelman

我认为#4774(注释)中的意见是CoVector不应该是AbstractVector的子类型,但是对于我来说,如果没有Transpose {Matrix} <:AbstractArray,这似乎有点奇怪。

对我来说很清楚,这个问题完全是关于从线性代数语义中分离数组语义(参见#10064),并寻找混合的地方。

  • 数组语义由函数定义,例如大小,长度,getindex,setindex,hcat,vcat,reshape,rot90等。
  • 线性代数语义由+,-,*,/ 、、',trace等函数定义。

如果我们定义cat功能是一个基本的接口部分AbstractArray ,然后Transpose{<:AbstractArray}显然不是一个AbstractArray ,因为它们的级联行为是不同的。 如果仅将形状和索引视为基本界面的一部分,那么情况就不太清楚了。

如果我们需要串联作为AbstractArray的基本接口的一部分,那么也很容易证明为什么像SymTridiagonal这样的类型不是AbstractArray s,因为串联操作超过SymTridiagonal类似s [SymTridiagonal(randn(5), randn(4)) randn(5)]目前不确定的。

@toivoh

我想这将意味着索引到向量中应该与行矩阵中的工作相同吗?

有一些迹象表明, Transpose{Vector}索引编制行为应与普通的Vector s相同。 考虑到对于数字类型, v[1]产生与v' * e₁ = v ⋅ e₁相同的结果, v[1:2]产生与v' * [e₁ e₂]相同的结果,其中e₁是规范基础Vector{Int} [1, 0, 0, ...]e₂[0, 1, 0, 0, ...] 。 如果我们要求拥有与索引,内部乘积和换位相关的这些标识,则可以声称

(v')[1] == (e₁' * v'') == (v' * e₁)' == (v ⋅ e₁)' == conj(v ⋅ e₁)* = conj(v[1])

(其中第一步是一个新的公理,而第四步则是利用了转置标量是无操作的事实),因此索引到Transpose{Vector}基本上会忽略转置,而索引到CTranspose{Vector}将使索引元素共轭。

我认为这个问题完全是关于将数组语义(cf#10064)与线性代数语义分开,并寻找混合的地方。

  • 数组语义由函数定义,例如大小,长度,getindex,setindex,hcat,vcat,reshape,rot90等。
  • 线性代数语义由+,-,*,/ 、、',trace等函数定义。

对此观点+1,并且没有Transpose <: AbstractArray 。 同样,在对协矢量进行索引时,它应该使用单个索引,因为否则,covector * vector的结果(在单个索引上收缩)不会导致标量(具有零索引的对象)。

@jihao :我不确定我为什么需要或想要

(v')[1] == (e₁' * v'')

作为新的公理。 尽管即使covector将作为行矩阵进行索引,但由于线性索引,我认为我们也会获得与上述相同的结果。

+1表示与线性代数有关的向量。

但是没有理由不应该定义与SymTridiagonal ,对吗?

@toivoh用于阵列线性索引是通过首先重塑的阵列到载体,然后索引新的向量定义。 因此,要使线性索引为转置数组提供相同的结果,必须首先定义转置向量的索引规则,以便我们可以从中得出线性索引规则。 (此外,您正在对其他人执行ping操作。)

我以为线性索引是关于存储顺序的,对于向量的线性索引,确实没有其他明智的顺序,对吧? (对于拼写错误,我们深表歉意。)

内存中没有唯一的明智遍历顺序。 即使对于Fortran数组,您也可以选择以列主列,行主列,甚至列主列逆序存储元素(这正是原始IBM Fortran I编译器所做的事情)。 此外,还有其他数据结构(请参见#10064),例如try,它们可以用作数组,并具有更多遍历顺序选项。

关于向量和矩阵也可以这样说。 由于线性索引以相同的顺序访问列矩阵及其转置元素(与列向量相同),所以为什么covector应该不同? 如果应该不同的话,我认为应该根本就不会为covector定义索引。

@toivoh是,相同的定义适用于普通数组。 在任何一种情况下,这都不会改变线性索引是从普通(元组)索引派生的。

可能不允许索引Transpose对象。 我并不是说它们必须是可索引的,但是如果它们是可索引的,则它们不一定必须具有相同的索引行为。 保守起见,我们现在暂时不定义索引,看看是否有用例出现。

线性代数函数的许多纯Julia实现都希望索引到Transpose中,对吗? 如果您不必区分所涉及的两个矩阵的任何可能情况(正常,转置,ctranspose,conj?),而只将它们视为普通矩阵,则编写纯Julia矩阵乘法(对于非BLAS数字类型)将变得容易。 可以尝试不使用高速缓存的方法以获得某种本地内存访问模式。

对吧

@Jutho :我同意。 为了适应其中,向量矢量应像行矩阵一样进行索引,对吗?

@toivoh ,如果您的意思是他们应该在前面加上一个额外的索引1,那么我不同意,也看不出我的陈述是如何暗示的。 我只是在谈论矩阵矩阵产品。 Matrix * vector或covector * matrix是需要不同函数定义的不同方法,不仅因为它们具有不同的内存访问模式,而且还因为它们具有不同的返回类型(Matrix_vector = vector或covector_matrix = covector),因此朱莉娅非常实际的理由是不要混合这些东西。

通常,我不是很喜欢在索引N维数组时添加额外索引1的能力,或者不喜欢VecOrMat类型别名的能力。 这允许草率的编程,但这也是为什么它便于出错或检测其他错误的原因。 我只看到两种有用的方法来对N维数组进行inex运算,即在将其作为张量积中的向量对待时,使用精确的N个索引,以防将其用作多线性对象,或者将其用作线性索引空间(例如,用于添加两个此类数组或将其与标量相乘)。 虽然这足以满足我的使用需求,但我可以接受那对其他人来说是有限的。

@Jutho :好的,我同意这可能并不重要,因为返回类型必须有所不同。

这是尝试描述我们要做什么并给出一些公理的尝试:

我认为我们已经达成了一个非常明确的共识,即起点是矩阵代数(仅基于矩阵)。 对于大多数操作,我们知道它们在纯矩阵设置中的行为。

我相信,我们正在尝试做的是以一致的方式扩展和完善纯矩阵设置,使其也具有真实的标量和向量,并且因为为了保持一致性,所以似乎需要向量。

这是我对标量和向量的看法(从纯矩阵的角度看):标量是一个矩阵,按其类型限制为1 x1。一个向量是一个矩阵,按其类型限制为是nx1。(我将在下面争辩说,向量是一个按其类型被约束为1 x n的矩阵。)从这个角度来看,我们可以给出两个公理:(下面不做非常正式的描述)

  • 扩展:考虑从矩阵到矩阵的函数。 如果在给定维度上,给定某些类型的输入的情况下始终产生大小为1的输出矩阵,则该事实将以输出的类型进行编码(使其成为标量/向量/向量)。
  • 改进:如果在纯矩阵设置中使用矩阵自变量的函数要求输入的大小为一维或一维以上,则在此事实未使用类型编码的情况下,它可能拒绝接受输入。

如果我们与上述观点一致,则向量的转置必须是上述协向量的类型:anx 1矩阵的转置产生1 xn矩阵。 如果我们对结果的第一个维度上的大小始终为1的事实进行编码,则将获得如上所述的协矢量。

如果您从矩阵代数的角度出发,那么您所说的是正确的。 这是MATlab可能非常完美地实现的模型。 一切都是矩阵。 这是一个封闭的系统,对矩阵的所有运算都会产生新的矩阵。

我确实给人一种印象,这个问题的重点(认真考虑向量转置,因为它们不仅是矩阵)要摆脱那个矩阵代数模型,因为如果出于某种原因要将数字与1x1矩阵分开,它将开始显示不一致。效率。 然后,替代方法是遵循线性代数模型,在该模型中,场(标量),向量空间(及其对应的对偶)和线性算子/变换(矩阵)的空间之间存在明显的区别。

我认为Julia中的线性代数运算非常牢固地扎根于matlab传统,但有一些值得注意的例外,例如具有标量和向量,而不是试图对用户进行二次猜测。 距离太远的任何事物都可能造成很大的破坏。

我相信我上面的公理应该一直解决您要从矩阵中分离标量和向量时出现的不一致(出于效率和正确性的考虑)。 但是我绝对愿意听到其他可能的系统。

我在这里同意@Jutho ; 这个问题的全部重点是摆脱MATLAB的“一切都是矩阵”的语义。 为了在线性代数运算下关闭Universe,必须使用MATLAB的“可以添加尾随单调维度”规则,但是该规则定义了等价类,这些类包含类型T和其他类型Array{T,N}的成员所有N ,这是MATLAB的类型系统无法确定的主要原因。 (请参阅Joisha和Banerjee的定理1

但是我仍然认为我们对标量,向量,协向量和矩阵的乘法应该是有关联的(对于像(v'*v)*v这样的东西,其中两个非标量相乘以产生标量的东西除外)达成了很好的共识,并且例如v'*M*v应该产生一个标量, M*v一个向量,而v'*M一个向量。 我不确定在仍然满足这些条件的情况下,有可能偏离一切都是矩阵的语义。
我们试图避免的是什么,我们想获得哪些属性呢?

如果在某些情况下只合并TArray{T,0} ,情况会更糟吗? (例如,索引: M[:,2]产生Array{T,1} ,但M[2,2]不会产生Array{T,0}

我们仍然可以使用Transpose{Vector}维护有效的语义。

我重读了所有关于结合性的评论,并得出结论,大部分讨论实际上并不是关于结合性本身,而是内部产品和外部产品的语义。 如果您发现与任何一个都不相关的表达式,请指出。

Matlab类型语义的问题是M*v'v*M有时即使不起作用也可以工作。 如果Mm x 1那么M*v'是有效的,并返回类似外部产品的数量(因为v'1 x n )。 同样,如果M1 x m并且我们有“可以添加尾随单例”规则,则可以将v*M评估为n x 11 x m的乘积

在APL文献中还提出了合并TArray{T,0} -在APL中,多维数组是递归嵌套的,这引发了Array{T,0}T是否存在的问题T “固定数组”(递归向下为Array{T,0} )。 我相信1973年的《莫尔》实际上证明了这两种选择在公理上都是一致的。 我不确定APL是否在大多数从业者退休或转移到其他人之前解决了使用哪个问题。

@jiahao :我不知道你的观察有多重要

v[i] = e_i' * v

将线性代数和索引语义联系在一起。 但是那时候你还必须考虑

M[i,j] = e_i' * M * e_j

表示具有从右开始的基向量的内积对应于沿第二维的索引。 因此,我坚持认为向量矢量v'i项应被索引为

v' * e_i = v'[1, i]

当然,我们想在哪里写1以外的东西作为第一个索引,但是呢?
无论如何,因为我们允许

e_i' * v = v[i] = v[i, 1]

那么在上述情况下,也应允许1作为占位符。

v' * e_i是一个标量,因此e_1' * (v' * e_i)是一个向量,而不是一个标量。 因此,尺寸与想到v'[1, i] = e_1' * v' * e_i并不匹配

编辑:我认为这可能是反对在索引中允许尾随单身人士的论点?

是的,矩阵索引是下一个逻辑步骤,而e_i' * M * e_j实际上是关联公理变得有用的表达式之一,因为

(e_i' * M) * e_j = m_i' * e_j

e_i' * (M * e_j) = e_i' * m_j

应该相等。 矩阵索引可以从向量索引规则和协向量索引规则中得出。

我认为,要始终如一地解决此问题,可能需要禁止v[i, 1]类的表达式,因为允许这种索引行为的规则
a)应该导致虚假情况使A*v'v*A起作用(前者可以,但后者不起作用,因为我们不一致地应用尾随单例规则),并且
b)如果我们考虑v[i] = v[i, 1, 1, 1]的相等性,则相应的covector索引规则将看起来像(v')[1, 1, 1, i]并且为了一致性,必须具有对应的covector的“允许任意数量的前导单身人士”规则。 我发现缺少唯一定义的第一维非常令人不安。

我认为这种索引推理并没有真正的作用。 索引是N维数组的一般属性,可以将它们写为N=1N=2简单矩阵表达式,这很简单,并且不包含任何基本内容信息。 但这并不能推广到更高等级的数组,因此,在索引时,不应使用它来激发covector是否需要前导1。 如先前的文章所述,这很快导致不一致。

如上所述,我从来都不喜欢依赖尾随一个索引,也没有想到有必要甚至非常有用的单一情况。 但是我可以接受我的观点过于局限。

我还没有完全理解所有这些内容,但是我的主要问题很简单: size(covector)等于(n,)(1,n)吗?

如果它们不是AbstractArray系列的一部分,则甚至不严格要求定义size

虽然出于实际原因,我猜想它会被定义(就像数字一样),我的投票投向(n,) 。 从存储/容器的角度来看,我想说向量和协向量之间没有区别。 因此,也不需要使用向量矢量作为容器,因此它们不属于AbstractArray层次结构。 这只是一个简单的包装类型,用于表示它们在线性代数运算方面的行为与矢量不同。

“只要将代数和几何分开,它们的进展就很缓慢,其使用受到限制;但是,当这两种科学结合在一起时,它们就发挥了彼此的共同作用,并共同走向完美。” -约瑟夫·路易斯·拉格朗日

我希望可以做出更多贡献,但是我想说的是,我赞成拥有这样一个系统,该系统可以使物理学家和工程师使用的复杂数学工具直观易用且准确。 也许来自麻省理工学院的资源可能有用...

http://ocw.mit.edu/resources/res-8-001-applied-geometric-algebra-spring-2009/lecture-notes-contents/

其实,考虑一下,说起来更公平

e_i' * x = x[i, :] # x is a vector or matrix
x * e_j  = x[:, j] # x is a covector or matrix

然后对于矩阵索引,我们将有

e_i' * M * e_j = e_i' * (M * e_j) = e_i' * M[:, j] = M[:, j][i, :] = M[i, j]
e_i' * M * e_j = (e_i' * M) * e_j = M[i, :] * e_j  = M[i, :][:, j] = M[i, j]

当前,这在Julia中并不完全成立,例如v[i, :]当前生成1x1数组,而不是标量。 (但也许不应该)
e_i' * M * e_j矩阵乘法的关联性对应于沿不同维度M[:, j][i, :] = M[i, :][:, j]切片的可交换性,这似乎是一个理想的功能。
通过以上的收益,我们应该

v'[:,i] = conj(v[i])

@Jutho :我认为这种“将索引作为重复切片”的范式确实可以推广到更高维的数组/张量:您可以为每个维度以一个顺序应用一个切片。 这对应于一系列收缩,其一阶张量对应于e_i ,依此类推,也可以任何顺序排列(只要您跟踪匹配的尺寸)。

我认为这种“将索引作为重复切片”的范式确实可以推广到更高维的数组/张量:您可以对每个维度应用一个切片,以任何顺序进行索引。 这对应于一系列收缩,其一阶张量对应于e_i等,并且顺序也任意(只要您跟踪哪些尺寸匹配)。

是的,我对张量收缩等非常熟悉,实际上获取矩阵元素/张量元素确实相当于在标准计算基础上获取期望值,即与某些基础向量收缩(只要您至少具有正交基础) ),但对于索引是对应于e_i还是e_i'的收缩,没有唯一的关联。 这相当于确定相应的索引是出现在协变还是相反的位置,并且没有唯一的决定。 高低索引的所有可能组合都有有用的应用程序。 但是与e_ie_i'签订合同甚至都不是从数学角度提出这个问题的正确方法,因为如上所述,实际上没有这样的数学映射:向量的转置。 转置是为线性映射(矩阵)定义的操作,即使您还将对偶向量也写为列向量,则转置仅相当于矩阵转置。 向量的转置只是矩阵代数中引入的一种便利技巧(实际上向量是n x 1矩阵),并且仅在您处于欧几里德空间中时才存在从( )向量空间到对偶空间。

特别是,如果要使用上面描述的属性,则应让M[i,:]返回不同的对象( 1xn矩阵或余矢量),然后再返回M[:,i] (应是nx1矩阵或向量)。 不能完全推广到更高维度的事实恰恰是此问题的主要讨论点之一,而且似乎大多数人都赞成APL索引,在索引中删除了用数字进行索引的维度(即M[:,i]M[i,:]产生1级数组,因此是一个向量。 索引是数组的属性,正是索引行为与线性代数运算的这种混合首先导致了所有混淆/不一致。 只要您停留在N=2等级对象的封闭生态系统中,即所有事物都是矩阵,向量和数字,并且您从不考虑高维数组,就可以保持一致。

正如您所说,我也意识到M[i,:]必须通过上面的推理产生一个向量。 因此,似乎在某些程度上具有协矢量与APL索引在根本上是不一致的。 如果我们支持APL索引(我确实喜欢),那么问题就变成了在该世界与辅助矢量所居住的世界之间划清界限的地方。 (我希望这两者是可以调和的,也许你们中的其他人已经意识到我们将不得不放弃这一点?)

如果您考虑一下,这种冲突可能并不令人惊讶:

  • 在APL索引中,结果中仅保留有意义的,可索引的维度。 其余的被挤出。
  • 如果我们用v'来做,那么我们将有v' = conj(v) 。 相反,可以将向量矢量视为跟踪缺少的第一维。

我想一个好的开始是尽可能多地限制向量,基本上只定义它们的乘法,加法/减法和左除法。

这个想法似乎不是让它们成为<: AbstractArray 。 让它们成为新的LinearOperator类型的子类型是否有意义? (我知道以前一直在讨论,但不太记得结论。)

我认为,缺少多重继承或接口的语言构造(这两种情况都在讨论中)要求某些概念仅通过“隐式”接口来实现,即需要定义的一组方法。 迭代器就是一个这样的概念,线性运算符可能是另一个需要由一组方法而不是类型层次结构指定的概念。 具有抽象类型LinearOperator而没有Matrix作为其子类型会很奇怪。

@toivoh这是一个重要的考虑因素,这就是我建议使用新功能(例如row(M,i)col(M,i)从矩阵中提取i向量或协向量的原因。 这些函数仅适用于二维数组M以及对矩阵代数感兴趣的人。 乍一看,它似乎似乎不像MATLAB风格的索引那么明显,但是,总的来说,从概念上将向量的概念与对偶/转置/ covector分开有助于使事情变得清晰。 在量子力学的情况下,整个领域跳到Dirac的Bra-ket符号,仅仅是因为矢量和协矢量之间的区别对于复数矢量至关重要,而Dirac的符号使这一点变得显而易见。 我希望朱莉娅(Julia)也能做到这一点,并成为高维存储阵列和高维线性代数的好工具! (因为我们很贪心,对吗?)

我必须说,一个具有MATLAB经验并且对大多数实际矩阵有所了解的人(但不是狂热的线性代数狂热者)可能首先不会意识到为什么我们中的一些人很重要,但是我相信是。

我已经说过了,但是我会重复一遍:从我的角度来看,我们有优先级列表,因果关系向下流动:

(1)数组从根本上说是所有Julia用户都将使用的存储容器,为此我们需要最好的语义。 无论如何,对我而言,APL风格的规则似乎是一个很好的解决方案。 另一方面,带有尾随一维索引的假设的MATLAB风格的最小二维数组似乎不自然,令人困惑,并且正如Jutho所说的那样,它们甚至可能导致草率的编程,您无法正确地跟踪数组的维数。 我到目前为止可以说,对于向量v ,代码v[i,:]应该抛出错误,因为v是一维的。 +.*类的元素运算对任何容器类都是有用的,而不仅仅是矩阵或多线性代数。 人们为下面的东西所做的唯一让步存储容器的是.*等上的多余点。

(2)大多数(但不是全部)Julia用户将使用矩阵代数,因此我们为某些矢量和矩阵运算添加了一些语法糖,大多数使用*符号(但我们可能会有矩阵除法等)。 在该系统中,我们将一维和二维数组分别作为列向量和矩阵。 对于功能齐全的矩阵系统,我们还需要行向量(covector)和转置运算' 。 行向量在所有可能的意义上都是一维数组,我断言应该将其索引为此类索引(当然不像covec[1,i] !!)。使用APL规则,我们被迫制作一些向量和辅助向量之间的区别,并且类型系统对此非常理想(请记住,我们很幸运,我们已经可以将矩阵和向量重用为一维和二维数组的类型化别名,而不是包装类型。从原理上讲,我们可以也将它们包裹起来,但我不明白要点)。 使用类型系统,编译器可以确定CoVector * Vector是标量,而Vector * CoVector是矩阵,依此类推。 正如Toivoh所说,从MATLAB的角度来看,CoVector正是在跟踪“缺失”的第一维。_临时用户将不需要构造这些对象; 他们只需输入向量和矩阵,并使用*'运算。 关心的人会注意到并欣赏这种区别。 用户的_biggest_更改是将M[i,:]更改为使用新功能row(M,i)或使用Transpose(M[i,:])M[i,:]'转换为包装类的必要性-可以认为这是一种优势,因为像狄拉克(Dirac)表示法一样,您永远不会忘记哪些对象是向量,哪些对象是向量,并且在适当的情况下使用类型断言分配给对象会引发错误。 不过,我认为值得讨论这种表示法。 将来,我们甚至可以扩展包装系统,以进行有效的延迟评估。 据我所知,这对每个人都是双赢。

(3)我们中有些人对多线性代数感兴趣,不得不学习对偶空间和换位等之间的区别。Jutho谈到了这一点。 Julia的数组已经是用于存储高维张量的绝佳存储设备,并且像我们这样的人将使用其他包(可能在Base中找到或可能找不到)。 我们不需要,也不想在大于2的维数数组上定义'* 。 我看不到'的面向存储的用例,不能通过明确地对索引重新排序来更干净地完成。 尾随一维索引的整个想法只会使这个概念不那么吸引人...因此,请仅对一维和二维数组保留' -像MATLAB一样:)(看,我确实有很多东西要说一下MATLAB ...)

对于功能齐全的矩阵系统,我们还需要行向量(covector)和“转置运算”。

这句话确实切入了问题的核心。 索引,换位和*产品都是混合在一起的。 此外,如果不进一步引入诸如row(A, i)类的函数以将i行的A作为返回值,就不可能使行向量的全部语义与协向量的语义协调一致。 covector,而不是一维数组。 当前A[1, :]既要表示“采用A的第一行”,又要表示“沿A的第二维取第一个切片”,但是这些概念在covector建议中根本不兼容。

行向量在每种可能的意义上都是一维数组,我断言应该将其索引。

我相信您对此声明有点太客气了。 前面的讨论已经非常清楚地确定,如果您想要完整的线性代数语义(具有正确的乘积和转置),则行向量不能具有与一维数组相同的类型。 首先,索引到一个向量中应该返回复杂的共轭值(v')[1] = conj(v[1]) ,以确保与内积dot一致性。 第二,协向量不是向量,它们是等待与向量产生内积的线性泛函。 第三,向量和协向量在hcatvcat下具有不同的数组串联行为。 由于所有这些原因,行向量不能“在所有可能的意义上都是一维数组”。

我赞成摆脱尾随的单例:我认为我从未见过使用它的Julia代码。 我原本打算说0维数组需要它,但是我看到X[]很好用。

我认为APL索引以及行向量的row(M,i)函数最有意义,并且似乎是一个不错的折衷方案。 我不确定行向量索引,但是我不喜欢(v')[1,i]

类型,是我们尚未涉及的另一个重大决定。 我从较早前的尝试中发现,让v'成为AbstractVector真的很困难,因为这使调度变得一团糟。

两种可能的选择:

  1. 我们对矩阵和向量使用不同的类型:

    • Transpose <: AbstractMatrix

    • CoVector <: Any

    • Factorization对象的某种转置。

  2. 我们对所有转置使用相同的类型,但让X' _not_为AbstractMatrix

    • Transpose <: Any

对于共轭,我们可以

一种。 定义ConjugateTranspose (以及ConjugateCoVector如果我们使用上述选项1)

b。 请使用Conjugate包装器类型,并进行适当的嵌套:我们需要使用Transpose{Conjugate{T}}还是Conjugate{Transpose{T}}约定。

我喜欢让Transpose{Matrix}不是AbstractMatrix的子类型。 我认为我们在基础上最接近的类似物是特殊的矩阵类型,例如Symmetric ,它在代数上的表现类似于矩阵,但在索引语义上却不一样。 (#987确立了,在没有多重内在或神圣特征的情况下,类型层次结构必须尊重容器语义而不是代数语义。)

组成基本上是“语义标记”的类型的问题也出现在#8240中。 我认为Transpose{Conjugate{T}}是更可取的,因为共轭是一个涉及元素基础领域的概念。

以下示例有时遵循与MATLAB相同的尾随单例规则,而在其他时候则不遵循:

  • 索引操作中允许尾随单例。 (如MATLAB)
julia> (1:5)[5,1,1,1,1,1,1]
5
  • 索引操作中不允许尾随切片。 (不同于允许它们的MATLAB。)
julia> (1:5)[5,:]
ERROR: BoundsError()
 in getindex at abstractarray.jl:451
  • 对于等级> = 3的数组,当索引少于数组的等级并且最后一个索引是标量时(例如MATLAB),隐式尾随的单例会添加到索引赋值操作中:
julia> A=zeros(2,2,2); A[1,2]=5; A #Same as A[1,2,1]=5
2x2x2 Array{Float64,3}:
[:, :, 1] =
 0.0  5.0
 0.0  0.0

[:, :, 2] =
 0.0  0.0
 0.0  0.0
  • 对于等级> = 3的数组,当索引比数组的等级少并且最后一个索引是_not scalar_时(例如MATLAB),隐式尾随_slices_被添加到索引赋值操作中:
julia> A[:,:]=3; A
2x2x2 Array{Float64,3}:
[:, :, 1] =
 3.0  3.0
 3.0  3.0

[:, :, 2] =
 3.0  3.0
 3.0  3.0
  • 对于等级> = 3的数组,当索引数少于数组的等级并且最后一个索引是标量时(例如MATLAB),隐式尾随的单例将添加到索引操作中:
julia> A=reshape(1:8,2,2,2); A[:,1]
2-element Array{Int64,1}:
 1
 2

julia> A[:,1,1]
2-element Array{Int64,1}:
 1
 2
  • 对于等级r> = 3个数组,当k个<r个索引比数组的等级高,并且最后一个索引是一个切片时,索引操作隐式线性化剩余的等级rk数组。 (如MATLAB):
julia> A=reshape(1:8,2,2,2); A[1,:]
1x4 Array{Int64,2}:
 1  3  5  7

julia> A=reshape(1:8,2,2,2); A[1,:,:]
1x2x2 Array{Int64,3}:
[:, :, 1] =
 1  3

[:, :, 2] =
 5  7
  • 尾随的单例在分配操作中不会被丢弃。 (与MATLAB不同)
julia> A=zeros(1); A[1] = randn(1,1)
ERROR: `convert` has no method matching convert(::Type{Float64}, ::Array{Float64,2})

You might have used a 2d row vector where a 1d column vector was required.
Note the difference between 1d column vector [1,2,3] and 2d row vector [1 2 3].
You can convert to a column vector with the vec() function.
 in setindex! at array.jl:307

julia> A=zeros(1,1); A[1,1] = randn(1)
ERROR: `convert` has no method matching convert(::Type{Float64}, ::Array{Float64,1})
 in setindex! at array.jl:308
  • 当允许有效操作时,Julia不会自动附加尾随单例尺寸。 (与Matlab不同,后者确实如此)
julia> 1/[1.0,] #In MATLAB, interpreted as the inverse of a 1x1 matrix
ERROR: `/` has no method matching /(::Int64, ::Array{Float64,1})
  • 外部产品有效,并且是先前规则的例外; 它们的语义在第一个参数中隐式使用尾随单例。 (如Matlab)
julia> [1:5]*[1:5]' # Shapes are (5,) and (1,5) - promoting to (5,1) x (1,5) works
5x5 Array{Int64,2}:
 1   2   3   4   5
 2   4   6   8  10
 3   6   9  12  15
 4   8  12  16  20
 5  10  15  20  25

一旦我们得出了我们希望它们如何表现的结论,这些事情似乎应该值得清理。 当索引太少而最后一个是切片时,当前的行为似乎特别麻烦。

哇。 Jiahao提出的示例非常令人不安……由于索引的隐式性和歧义性,我不喜欢索引操作和索引赋值操作中尾随单例。 如果您事先不知道这些行为,那么您可能在实际尝试做另一件事时最终会做一件事。 我赞成严格,明确地使用该语言,并避免使用歧义性的快捷方式。

要实际实现这一点,我们将需要某种三角形的调度,否则我不确定您将如何表达诸如“具有Complex64Complex128项的矩阵,其转置” ,或其共轭转置”。 特别是如果我们使用上面的选项2 + b。

@ esd100 ,其中哪些是模棱两可或危险的? 这些都是很方便的-当您为您设想了“虚拟单身人士”而您想要它们时–或不便-当您想要它们而不是它们时。 这些都不具有具有不同行为的两个合理含义。

行向量在每种可能的意义上都是一维数组,我断言应该将其索引。

我相信您对此声明有点太客气了。

真正! 抱歉@jiahao ,我会[] 。 您是正确的,串联是一个重要的考虑因素,我认为这样做的自然方式(构造一个矩阵)是相当明显的(在这种情况下,将其视为1 xn大小)。 显然,向量不是“一维Julia数组”,而是“重点”。

佳豪的例子也打扰了我。 我赞成删除任何可能的尾随单例行为。 线性化后面的维度可能有用,但也可能会引起某种懒惰,您会忘记数组有多少维度...(我认为这就是“模糊”和“危险”的含义……我真的想要例如,当我将16维数组像15维数组一样对待时,Julia会抛出一个错误。

其中哪些是模棱两可或危险的?

第四个对我来说既模棱两可又危险。

julia> A=zeros(2,2,2); A[:,:]=3; A
2x2x2 Array{Float64,3}:
[:, :, 1] =
 3.0  3.0
 3.0  3.0

[:, :, 2] =
 3.0  3.0
 3.0  3.0

显然(对我而言)这应该是语法错误。 A为等级3,根本没有引用第3维。 第三个维度很可能是由于错误而遗漏的,现在代码中引入了难以发现的错误。 我很高兴能得到他的观点的错误(因为IDL和fortran都存在)。

我认为最好是数组只允许使用正确数量的索引或一维线性索引的特殊情况进行索引(因为它非常方便)。 这也将包括不允许尾随单身人士。

或一维线性索引的特殊情况(因为它非常方便)

出售我们的原则多么容易! :)

我从来没有那么喜欢线性索引。 这让我有些不高兴。 通常,我们只是想以最快的方式遍历数组。 对于除了简单的密集阵列以外的所有阵列,线性索引的确会很慢。 也就是说,我们可能无法完全消除线性索引(出于性能原因,并且破坏了太多代码)。

是的,足够真实。 但是至少一维索引在视觉上是不同的。 而用5D索引6D数组则要少得多。 无论如何,我宁愿拥有更严格的索引编制规则并放弃一维线性索引编制,而不愿转向其他方向。 特别是因为可以很容易地获得对共享内存的数组的重塑引用。

我们可以使用{}括号(或其他一些)进行线性索引编制,并保留[]进行多维索引编制吗? 只是个主意...

2015年3月23日,下午2:36,Bob Portmann [email protected]写道:

是的,足够真实。 但是至少一维索引在视觉上是不同的。 而用5D索引6D数组则要少得多。 无论如何,我宁愿拥有更严格的索引编制规则并放弃一维线性索引编制,而不愿转向其他方向。 特别是因为可以很容易地获得对共享内存的数组的重塑引用。

-
直接回复此电子邮件,或在GitHub https://github.com/JuliaLang/julia/issues/4774#issuecomment -84805310上查看。

我也认为我们能够分隔语法会很好
从常规索引语法进行线性索引,但我同意
可能是一个巨大的变化。

尽管这个问题似乎引起了很多争论,但在整个0.4周期中,大多数改进索引的实际工作都发生在大量的请求请求中。 例如,现在我们有了CartesianIndex和朋友,我不明白为什么需要分开线性索引和笛卡尔索引的语法-实际上您现在可以将它们组合在一起(#10524)。 我也想摆脱线性索引,但是有时它的性能更高(可能很大程度上是由于#9080; enumeratezip遭受相同的问题)。 我们可能应该实现fastindex作为eachindex的包装器,如#10507所示。

如果您对索引规则感兴趣,那么不要将这个问题付诸东流,让我们关注最有趣的领域。 在这一刻,这无疑是#10525。 特别是https://github.com/JuliaLang/julia/pull/10525#issuecomment -84597488需要某种分辨率。

@timholy我还没有真正了解快速笛卡尔索引的所有发展,并且只看了base / multiDimension.jl,就可以看到很多元编程正在进行中。 您是否有机会撰写(或发表JuliaCon演讲)有关这一切的工作方式?

在某些方面,没有什么要知道的:尽管引擎盖下有相当多的内容正在发生,但其想法是使其使用起来非常简单。 所以从字面上看

k = 0
for I in eachindex(A)
     B[k+=1] = A[I]   # B is being linearly-indexed, A is being cartesian-indexed
end

可能就是您所需要知道的。 (换句话说, eachindex的帮助可能是所有需要的文档。)但是,事实证明,您可以使用此基本范式的一些扩展来编写大量算法。 的确,这些扩展是什么以及它们为何功能强大可能并不那么明显。

我确实计划在未来几个月内将其写下来,但是如果人们确实想知道详细信息,也许进行JuliaCon演讲是合理的。 @Jutho@mbauman可以和我一样讲。

我将添加更多关于歧义的内容,但是我认为一些后续讨论总结了某些关键点。 作为局外人或出于恐惧,我可能会对这种歧义更加敏感,但听起来可能会过于戏剧化。 以我的拙见,许多简单的,关键任务的思想练习都可以带您走上一条简单错误的成本/收益分析不值得的道路。

我们可能应该实现fastindex作为每个索引的包装器,如#10507

@timholy是否有任何理由不让eachindex返回快速线性数组的UnitRange? 它仍然是类型稳定的,并且如果调用者想确保获得CartesianIndex,他们可以手动构造CartesianRange(我们也可以为eachindex(size(A))添加一个方法,因为不会导出CartesianRange )。

刚开始是这样做的,但是我想@Jutho改变了它。 (如果那不是真的,那么我可能没有意识到就进行了更改。)我假设所做的更改是出于一致性的考虑(因此,您可以指望获得CartesianIndex ),这确实有一定意义。 。 但是正如您指出的那样,还有其他方法可以确保这一点。

@Jutho ,有什么想法吗?

我不记得更改eachindex ,但是可能是。 我当然不记得有一个很好的理由,除了获得与数组类型无关的一致结果之外。 但是,如果在文档中清楚地说明了具有有效线性索引和不具有有效线性索引的数组之间的区别,那么我不认为为什么在具有有效线性索引的数组中eachindex无法返回线性范围。

@timholy ,作为对您以前的帖子的回应,我无法参加JuliaCon,所以请随意谈论CartesianIndex内容(无论如何我还是做出了最后的贡献)。 我很期待会议的报告和视频。

一个异想天开的想法:对于任意维度(包括维度> 2)的数组A ,可能会有A'周期性地置换A的索引。 因此, A'[i, j, k] == A[j, k, i] 。 如在MATLAB中那样构造时,这将减少2d数组的常规矩阵转置,以及行和列“向量”的常规转置(即分别为[n,1]和[1,n] 2d数组)。 因此,它将永远不会将2d列“向量”映射到真实的covector或将2d行“向量”映射到真实向量。 (我认为此属性不错,但其他人可能会不同意。)它将为构成数组对象的矩阵和向量赋予标识A'' == A'v'' == v 。 给定上面讨论的类型层次结构的类型(真实矢量和余矢量与抽象数组有足够的区别),对于真实矢量和余矢量, '仍然可以使用完全不同的方法,其中它对应于线性代数概念并不需要满足v'' == v (但是如果这是人们决定的那样就可以)。

需要明确的是:我一秒钟都不认为' _needs_对于维数大于2的数组具有任何方法,但是我没有在上面看到过这个建议(除非那是“反转索引”的意思”),以为我只会提到它。 我所看到的最大危害(表面上)是概念上的:将(略为任意的)组合运算与通常取自线性代数域的一个运算符合并。 为此,我们可能会回答,当我们尝试将线性代数概念扩展为以数据为中心的概念时,至少一定程度的这种合并是不可避免的(正如整个讨论所证明的)。 因此,我们最好为多维数组的循环置换获得一个方便的简写,作为确定'通常应对多维数组维进行什么操作的副产物,只要它减少到预期的情况即可。 2个维度。 甚至可以添加一个采用整数参数的方法,以使A'(k)[I]周期性地对A[I] k的索引进行置换,从而使A'(ndims(A))[I] == A[I]A'(-k)[I]对索引进行置换在相反的方向。

只是一个想法。

如果为多维数组定义了transpose ,我希望它仍然满足A''=A ,即它是它自己的逆。 这与先前的建议直接冲突。

这还算公平。 就像我说的那样,我的建议仅(可能)有吸引力,只要有人愿意在转置的线性代数意义与d> 2情况下施加的任何以数组为中心的意义之间增加裂痕。 我的理由是,只要已经发生了这种裂痕,并且如果方法完全不被使用,那么对置换索引有一个简捷的表达方式-只要不需要任何索引进行特殊处理以使d = 2的情况起作用。 如您(@Jutho)所述,在2d情况下转置维度具有线性代数意义(而且仅在将(协)向量标识为2d数组之后),这是一个方便的巧合,所以也许我们不知道对于d> 2,不必对transpose的数学特性(例如,要求A'' == A )保持挑剔。被分配,例如: A'周期性的置换一次, A'(k)循环的置换k次, A'(I)I::Array{Int, 1}与长度<= ndims(A)循环的置换中列出的指标I ,和A'(p)p::Permutation长度的<= ndims(A)的置换索引根据p 。 但是我想也许最好的办法是制作一个包装,看看它是否足够有用。

我支持已提及的两项更改/说明,例如StefanKarpinski于2014年10月16日和simonbyrne于2015年3月22日提出,但有一项规定:

  1. transpose仅应用于向量和二维矩阵,而不应用于一般张量。
  2. 运算符*transpose应该在计算结束时删除尾随的单例尺寸。 否则,尾随的单例尺寸可能会保留。

这两个变化将为传统线性代数工作提供许多便利并解决许多歧义。 他们故意对通用数组索引或张量代数什么也没说,可以单独考虑。 (特别是,张量转置应被视为与矩阵/向量转置完全独立的操作。)

根据该建议,当使用矩阵乘法和转置时,矢量和单列矩阵之间基本上没有区别,标量,长度为1的矢量和1乘1之间也没有任何有意义的区别。 -1矩阵。 但是, x'将是一个1×n矩阵,而不是一个向量。

另一方面,出于保存数据的目的,可以构造任何大小的数组。 由于不会涉及乘法和转置的线性代数概念,因此不会删除单例维。

以下是茱莉亚会议的假想成绩单,其中包含拟议的变更。

首先,我们定义一个标量,两个向量和一个矩阵。

julia> alpha = 2.0
2.0

julia> x = [1.0; 2.0; 3.0]
3-element Array{Float64,1}:
 1.0
 2.0
 3.0

julia> y = [4.0; 5.0; 6.0]
3-element Array{Float64,1}:
 4.0
 5.0
 6.0

julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
3x3 Array{Float64,2}:
 1.0  2.0  3.0
 4.0  5.0  6.0
 7.0  8.0  9.0

即使存在无关的维数,标量矢量乘法也可以工作,并且结果始终是矢量。

julia> alpha*x
3-element Array{Float64,1}:
 2.0
 4.0
 6.0

julia> alpha*x[:,[1]]
3-element Array{Float64,1}:
 2.0
 4.0
 6.0

转置是对合。

julia> x'
1x3 Array{Float64,2}:
 1.0  2.0  3.0

julia> x''
3-element Array{Float64,1}:
 1.0
 2.0
 3.0

julia> x==x''
true

julia> x'''
1x3 Array{Float64,2}:
 1.0  2.0  3.0

将矩阵与一列矩阵相乘得出的结果与矩阵向量乘积相同。

julia> A*x
3-element Array{Float64,1}:
 14.0
 32.0
 50.0

julia> A*x[:,[1]]
3-element Array{Float64,1}:
 14.0
 32.0
 50.0

一行矩阵乘以一个矩阵等于一行矩阵。

julia> x'*A
1x3 Array{Float64,2}:
 30.0  36.0  42.0

内部乘积是标量,由矩阵向量乘积和矩阵矩阵乘积的更通用规则产生。

julia> x'*y
32.0

julia> x'*y[:,[1]]
32.0

外部产品没什么特别的。

julia> x*y'
3x3 Array{Float64,2}:
  4.0   5.0   6.0
  8.0  10.0  12.0
 12.0  15.0  18.0

julia> x[:,[1]]*y'
3x3 Array{Float64,2}:
  4.0   5.0   6.0
  8.0  10.0  12.0
 12.0  15.0  18.0

向量乘以向量是错误。

julia> x*y
ERROR: `*` has no method matching *(::Array{Float64,1}, ::Array{Float64,1})

向量乘以矩阵是错误的。

julia> x*A
ERROR: DimensionMismatch("*")
 in gemm_wrapper! at linalg/matmul.jl:270
 in * at linalg/matmul.jl:74

矩阵乘法是关联的。

julia> (x*x')*y
3-element Array{Float64,1}:
 32.0
 64.0
 96.0

julia> x'*y
32.0

julia> x*(x'*y)
3-element Array{Float64,1}:
 32.0
 64.0
 96.0

julia> norm((x*x')*y-x*(x'*y))
0.0

编辑:删除了两个涉及产品内部尺寸降低的示例。 他们与当前的讨论关系不大。

恐怕这会丢失所涉及操作的类型稳定性,因为丢弃尾随的单例尺寸不是类型稳定的操作。

从我的角度来看,整个向量矢量业务都涉及一个单例维度,该维度基于将要删除的类型而已知。 另外,我们一直在努力维护向量和恰好只有一列的矩阵之间的区别,但是这种建议在某些情况下会消除这种区别,例如x''

异议建议:如果我们从类型参数中删除维度,并且在运行时进行了所有维度/大小检查,该怎么办? (无论如何,我们最终都会做大量的工作。)并为也会将尺寸编码为类型参数的类型保留尺寸参数。 (鸭子,躲藏了两个星期,并将他的github帐户更改为@SomeoneWhoCertainlyIsntTimHolyUhUhNoWay。)

(当然,仅凭#10525我就真的会对自己抱怨。)

FWIW,我想说的并不是一个疯狂的想法:例如,这就是Torch的工作方式,并且Torch开发人员对类型系统中Julia的维数编码表示不满。

@timholy问题:我们能否使用有关线性代数和向量转置的改进语义?

如果仍然对使用CoVector / DualVector / TransposeVector感兴趣,则仍然必须包装新的TimHolyArray{DataType} _and_我们必须要么理解尺寸大于2(或一个)的数组的转置,否则当tharray的尺寸大于2(或一个)时,禁止构造TransposeVector(tharray) ...实际上,事情必须在运行时级别上给出当前是编译时错误的错误(例如,当前未定义的乘法,因此类型系统禁止使用)。

另一方面,在这个新类中实现一个转置标志可能是不好的……它为应该是高效且轻量级的容器增加了更多的复杂性。 我倾向于排除该选项,而将辛苦的工作留给编译器/类型系统。

我不一定反对您的想法-但这似乎是可以与向量转置同时完成的另一个问题。

@timholy :我真的很确定这是不是要走的路。 在某些情况下,我发现分派维度非常有用。

建议是将此方法应用于所有阵列。 但是我现在反对我自己的建议,仅仅是因为在许多多维情况下,您需要为N维数组生成N循环。 如果N不是类型参数,我们将无法再这样做。

是的,这不是您的笛卡尔宏的全部内容吗(或者我仍然不习惯使用分段功能:-))?
我在C ++中做了类似的事情,将维度作为模板参数确实很痛苦。 但是我遇到了动态变量受限的情况,因为一个变量需要大的if语句来专门化不同的数组维。

提案:

  1. 将当前矩阵乘法行为重命名为timesfast ,并将当前转置行为重命名为transposefast
  2. 修改(*)transpose以截断尾随的单例尺寸,如先前的注释中所述。 例如, u'*v将成为标量, v''将成为向量,并定义(v*v')/(v'*v)

现有行为是类型稳定的。 拟议的行为遵循许多线性代数文本的约定。 它们都很有价值。 也许茱莉亚应该兼而有之。

我想在教室中使用Julia,所以我投票赞成默认行为,以便利性而非效率为重。

感谢几个贡献者让我快速掌握了这一长线程!

@briansutton :我认为您应该重新考虑您的要求。 我建议您等到您对Julia的工作方式有了更深入的了解后,再提出重新定义乘法的含义的建议。

这个问题是关于我们如何避免尽可能多的特殊情况。

当单例维度之一是您真正关心的维度时,允许对单例维度进行篡改的规则就会崩溃。 使用“截断[所有]尾随单例尺寸”规则,则如果v是1元素向量,则v'是标量,因此v''也是标量。 因此,即使在此提议中, v == v''的属性也并不总是成立。

您可以尝试将规则修改为“如果数组具有维度2,则仅截断最后一个尾随的单例维度”。 但是即使使用此修改规则,外部乘积v * w'也不会自动遵循矩阵乘法的定义,而是必须是其自己的特例定义Vector * Matrix ,并且该定义必须为“除非形状为(N)x(1,M),否则引发错误”。

@jiahao

当使用向量和矩阵进行线性代数运算时,我不想在标量,1元素向量和1比1矩阵之间进行区分。 当前,各种操作都会产生这三个对象中的任何一个。 我的建议是,对于矩阵乘法和转置的操作,选择三者之一作为首选表示形式。

关于您的第一个示例( v==v'' ),我想避免一开始构造一个1元素向量v

关于第二个示例,是的,我认为v*w'应该按照您的描述进行处理。 使用矩阵乘法和转置时,我希望向量v和N-by-1矩阵v[:,[1]]表示相同的数学对象,即使它们具有不同的内部表示形式。 这将需要特殊的代码来处理N个矢量乘以M矩阵。

我猜您仍然不相信类型稳定性很重要。

@briansutton :我想您会发现尝试实施使提案以与当前类型系统允许运行Julia代码一样快的速度运行所需的机制非常有用。 我认为,鉴于朱利安提供可预测的性能和C内存布局兼容性的目标,您所说的目标是无法实现的。

我不确定是否理解来回的争论,但是有一件事引起了我的注意。 也许这是一厢情愿的想法,但是如果计算机能够像人脑一样快地思考,那就太好了。 我的意思是,当Brian Sutton谈论“选择首选表示形式”程序时,我设想的是一个程序,可以像做数学一样思考。 也许,用当今的技术是不可行的,它将使速度减慢太多。 但是,那不是很好吗?

我是Julia的物理学家和活跃用户。

我不再对保持天气或放弃所有尾随的单身尺寸有强烈的偏好

但在这里我想提出一个密切相关的问题。

当前的Julia实现:

令V为3-dim rank-1张量(向量)
V [1]将给我们一个缩放器,而不是一维秩1的张量

设A为3x4x5秩3张量
B = A [1,:,:]将给我们一个1x4x5 rank-3张量。

以上两种行为并不完全一致。

我强烈喜欢以下索引/滑动功能:

设A为3x4x5秩3张量
B = A [1,:,:]将给我们一个4x5 rank-2张量。
C = A [1:1,:,:]将给我们一个1x4x5 rank-2张量。
(目前以上两个给出相同的结果)

令V为1级张量
B = V [1]将给我们一个缩放器
C = V [1:1]将为我们提供1-dim rank-1张量。

此功能将帮助我们更轻松地更改张量的形状,并且在允许尾随单例索引时将很有用。

最好

小刚


来自:esd100 [[email protected]]
发送:2015年6月9日,星期二,9:46 PM
至:朱莉娅·朗/朱莉娅
抄送:文小刚
主题:回复:[julia]认真对待向量转置(#4774)

我不确定是否理解来回的争论,但是有一件事引起了我的注意。 也许这是一厢情愿的想法,但是如果计算机能够像人脑一样快地思考,那就太好了。 我的意思是,当Brian Sutton谈论“选择首选表示形式”程序时,我设想的是一个程序,可以像做数学一样思考。 也许,用当今的技术是不可行的,它将使速度减慢太多。 但是,那不是很好吗?

-
直接回复此电子邮件或在Gi tHub上查看它

我的意思是:C = A [1:1,:,:]将给我们一个1x4x5 rank-3张量。

小刚


来自:温晓刚[[email protected]]
发送:2015年6月22日,星期一,12:01 PM
致:朱莉娅·朗/朱莉娅; 朱莉娅·朗/朱莉娅
抄送:文小刚
主题:RE:[julia]认真对待向量转置(#4774)

我是Julia的物理学家和活跃用户。

我不再对保持天气或放弃所有尾随的单身尺寸有强烈的偏好

但在这里我想提出一个密切相关的问题。

当前的Julia实现:

令V为3-dim rank-1张量(向量)
V [1]将给我们一个缩放器,而不是一维秩1的张量

设A为3x4x5秩3张量
B = A [1,:,:]将给我们一个1x4x5 rank-3张量。

以上两种行为并不完全一致。

我强烈喜欢以下索引/滑动功能:

设A为3x4x5秩3张量
B = A [1,:,:]将给我们一个4x5 rank-2张量。
C = A [1:1,:,:]将给我们一个1x4x5 rank-2张量。
(目前以上两个给出相同的结果)

令V为1级张量
B = V [1]将给我们一个缩放器
C = V [1:1]将为我们提供1-dim rank-1张量。

此功能将帮助我们更轻松地更改张量的形状,并且在允许尾随单例索引时将很有用。

最好

小刚


来自:esd100 [[email protected]]
发送:2015年6月9日,星期二,9:46 PM
至:朱莉娅·朗/朱莉娅
抄送:文小刚
主题:回复:[julia]认真对待向量转置(#4774)

我不确定是否理解来回的争论,但是有一件事引起了我的注意。 也许这是一厢情愿的想法,但是如果计算机能够像人脑一样快地思考,那就太好了。 我的意思是,当Brian Sutton谈论“选择首选表示形式”程序时,我设想的是一个程序,可以像做数学一样思考。 也许,用当今的技术是不可行的,它将使速度减慢太多。 但是,那不是很好吗?

-
直接回复此电子邮件或在Gi tHub上查看它

您要的是slice当前的工作方式。 我的感觉是A[stuff]将成为slice(A, stuff)的同义词,因此您可能会如愿以偿。

亲爱的蒂姆:

谢谢你的提示。 我试过切片。 它不符合我的需求。 Slice产生一个新的数据类型“ subarray”,我在使用:: Array数据类型的其他代码中无法使用。

也许我可以更改其他代码,以便它们允许“子数组”数据类型。

小刚


来自:Tim Holy [[email protected]]
发送:2015年6月22日,星期一,下午5:32
至:朱莉娅·朗/朱莉娅
抄送:文小刚
主题:回复:[julia]认真对待向量转置(#4774)

您要的是slice当前的工作方式。 我的感觉是A [stuff]将成为slice(A,stuff)的同义词,因此您可能会如愿以偿。

-
直接回复此电子邮件或在Gi tHub上查看它

是否有什么真正依赖于在代码中使用具体的Array类型而不是AbstractArray ? 它可能只需要用AbstractArray搜索/替换Array AbstractArray来使事情正常工作。

亲爱的斯科特

非常感谢您的提示。

小刚


来自:斯科特·P·琼斯[[email protected]]
发送:2015年6月25日,星期四,9:55 AM
至:朱莉娅·朗/朱莉娅
抄送:文小刚
主题:回复:[julia]认真对待向量转置(#4774)

真的有什么依赖于在代码中使用具体的Array类型而不是AbstractArray的吗? 它可能只需要使用AbstractArray搜索/替换Array即可使工作正常。

-
直接回复此电子邮件或在Gi tHub上查看它

那对您有用吗? 我很乐意提供帮助!

@wenxgwen ,或者,您可以调用copy(slice(A,...))以便在新的Array中获取切片的副本,然后该副本应与您已经存在的函数一起本地运行。

解决此问题现在已成为一项有利可图的工作

由于现在解决此问题对于我获取3个逗号的路径至关重要...
image

但说真的。 我试图完整地阅读讨论内容,但不能保证以前没有建议过这一点:

我们是否可以将AbstractArray定义扩展为具有其他特征(类似于LinearIndexing?),该特征定义基础数据是基于行的存储还是基于列的存储? 此添加将打开以下属性(请不要只关注名称...只是概念):

v --> length-2 Vector{Col}
  [ 1
    2 ]

v'  --> length-2 Vector{Row}
  [ 1 2 ]

m --> 2x2 Matrix{Col}
  [ 1 3 
    2 4 ]

m' --> 2x2 Matrix{Row}
  [ 1 2 
    3 4 ]

Some operations:
v'  --> length-2 Vector{Col}
v'' == v
v*v or v'*v'  --> either error, or do element-wise multiplication
v' * v --> scalar
v * v' --> 2x2 Matrix  (could be Row or Col??)
v' * m --> 2-length Vector{Row}
v * m --> either error or broadcasting operation
m * v --> either error or broadcasting operation
m * v' --> 2-length Vector{Col}

Indexing:
v[2] --> 2
v[1,2] --> error
v'[1,2] --> 2
m[1,2]  --> 3
m'[1,2]  --> 2

Size:
length(v)  --> 2
length(v')  --> 2
size(v)  --> (2,)
size(v')  --> (2,)
length(m)  --> 4
size(m)  --> (2,2)

显然,该建议缺少很多定义,但也许可以开始讨论。 我们可以同时支持列索引存储和行索引存储,并在此过程中“修复”一些问题吗?

我非常希望Julia是基于行的存储,因为这自然是我对循环和许多其他操作的看法。 (而且我不认为我是唯一这样思考的人)请发表评论!

有一个有趣的想法,让构造函数也接受行或列。 我认为它已经在GitHub问题系统中的某个地方进行了讨论。 我可能是错的!

我个人更喜欢基于列的存储,因为我的大部分教科书都将列用于数学运算,然后在Julia中,我不需要将所有内容都改为使用行。 一开始我也确实发现它很奇怪,但是很快就变成了我的工作。 有一些算法更容易以行表示,但是这就是为什么在需要时必须能够使用非标准存储这是一个很好的原因。 我希望约定是,当您导出一个函数时,总是返回一个列主矩阵或列向量,这样,在调用函数时就不会有关于哪种类型的问题。 否则,当您不得不去总是查找返回的类型时,它可能会变得非常凌乱并且变得令人讨厌。

基于列与基于行不在此问题的范围内。

@tbreloff我非常喜欢这个主意。 能够更轻松地与主要行的语言/库进行交互会很棒。

Jiahao是正确的,而不是优先选择行优先和列优先。 它的
以“朱利安”方式解决转置问题的一个很好的副作用
(参数类型和分段功能)使人们在
存储格式。

如果您不喜欢向数组添加行/列特征的想法,则可以
完全相同的事情可以通过TransposeView {T,N}完成,但是我
怀疑实施起来会更加复杂。

其他特征的最大缺点是新用户感到困惑,
这是我很难调和的事情。

2015年9月26日,星期六,Scott P. Jones通知单@ github.com
写道:

@tbreloff https://github.com/tbreloff我非常喜欢这个想法。 它
能够更轻松地与语言/库进行交互将非常好
是行主要的

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/4774#issuecomment -143436947。

呼叫超载是否会完全改变设计空间? 在以上有时, *的含义似乎有些含糊。 特别是,当我们希望v::Covector * w::Vector返回标量时,我们是否真的要使*成为“映射wv下的映射”,而不是矩阵乘法? 如果是这样,那么由于向量本身就是在余矢量上的线性映射,难道不能有人同样地要求w::Vector * v::Covector返回标量吗?

也许这将是有益的,而不是到过载call和写入v(w)表示“正在地图v上操作” w ,同样地,对于w(v) -都将返回标量。 在2d数组的情况下,这是否将为数组操作和线性代数操作共享的语义提供更多的腾出空间?

如果是这样,那么不能同样要求w :: Vector * v :: Covector返回标量,因为矢量本身就是在矢量上的线性映射?

我认为我们正在尝试遵循许多人在一年级大学中看到的矩阵和向量约定,该约定是不可交换的,并且具有向量*转置向量->矩阵(秩为1,即只有一个(非零)奇异点)值)。 您写的内容听起来更像是一个抽象的线性代数概念,其中向量和余/对向量是彼此映射到某个标量的,虽然很好,但与Julia和MATLAB中尝试的(IMHO)略有不同。 我认为我们可以解释您写为内部乘积的内容, dot()作用于两个向量(我觉得应该为向量定义),但有时我们也希望外部产品,目前非累加的*允许我们编写和表达两种行为。

至于调用约定v(w) ,我们同样可以说,对于点积,我们希望vw方向,这建议我们使用索引运算符v[w] 。 (顺便说一句,我并不是说这是一个不错的语言设计选项-只是一个观察!)

我们同样可以说,对于点积,我们希望在w方向上的金额为v ,这表明我们使用索引运算符v[w]

该建议几乎但不符合索引的语义。 如果vVector{<:Integer}这也与我们当前对向量索引的支持相冲突。

考虑到对于v :: Vector{T<:Real}v[1]等效于点积v⋅e₁ ,其中e₁是沿第一轴的规范基矢量。 因此,简单的索引确实是功能

   v[n] : n :: Integer --> y = (v ⋅ eₙ) :: T

对于向量索引, v[[1, 2]]产生[v⋅e₁, v⋅e₂] ,这是将v投影到由{e₁, e₂}跨越的子空间中的结果,或者等效地,这是v' * [e₁ e₂]的结果

因此向量索引就是功能

   v[I] : I :: Vector{<:Integer} --> y = v' * [eₙ for n in I] :: Vector{T}

提出v[w] = (w ⋅ v) v的提议与此定义不一致,因为它消除了从索引n (由w所指定)的集合到规范基础向量的集合的隐式映射。 eₙ ,这对于我们当前的索引规则起作用是必需的。

现在将范围限制为仅矢量转置,我认为我们有两个选择。 我们可以将其设为错误,也可以引入特殊的covector类型。 考虑到Julia中向量的一流性质,我认为前者将是一个非常困难的出售……并且要一贯做到,我们可能应该完全禁止向量完全参与矩阵的代数。

因此,我对covector开了一枪。 这是很多工作,但是不幸的是我将无法花更多的时间。 我将其发布在这里,希望有人能够使用它,或者我们将从困难中汲取教训并决定逃跑。 但是我有一个以转置为视图的分支建筑,并使用协矢量实现了矩阵乘法。 某些选定的测试通过,但仅在没有depwarn=error (例如./julia -e 'using Base.Test; include("test/matmul.jl")' )。 https://github.com/JuliaLang/julia/compare/mb/transpose

我定义了两种新的视图类型,用于transposectranspose的矩阵和向量:

immutable MatrixTranspose{C,T,A} <: AbstractArray{T,2}
    data::A # A <: AbstractMatrix{T}
end
immutable Covector{C,T,V} 
    data::V # V <: AbstractVector{T}
end

C参数是一个简单的布尔值,表示转置是否为转置。 请注意,Covector不是AbstractArray的子类型; 这是使调度合理工作的非常强烈的要求。

一些缺点:

  • 协矢量肯定很复杂,以可访问和严格的方式来记录文档将是一个挑战。 语言在这里可能会有所帮助-我们可以简单地称它们RowVector s。 无论如何,尝试解释此行为时遇到的第一个困难是如何谈论向量的形状( size是未定义的)。 如果(m,n)是矩阵的形状,则(m,)可以表示向量的形状……并且如果我们使用元组符号,则可以口地描述一个向量为(,n)的形状

    • 矩阵*向量为(m,n) × (n,) → (m,)

    • Covector *矩阵为(,m) × (m,n) → (,n)

    • 向量* Covector为(n,) × (,n) → (n,n)

    • Covector *向量为(,n) × (n,) → α (标量)

  • 这里二进制操作和类型的数量导致需要定义的方法数量的巨大组合爆炸……仅对于乘法,我们就有:

    • 变异:(变异,非变异)
    • 转置:(A,Aᵀ,Aᴴ)×(B,Bᵀ,Bᴴ)。
    • 形状:(垫×垫; Vec×垫;垫×Vec,Vec×Vec)。 请注意,并非所有这些转置组合都支持所有这些,但大多数都支持。
    • 实施:(BLAS,交叉,通用,结构矩阵专业化)

    尽管其中一些操作确实与多种调度和回退行为保持了很好的一致性,但对于每个操作员而言,这仍然是一个庞大的方法。 完全删除这里的混合矩阵/矢量支持肯定会帮助简化事情。


  • 他们并没有立即解决降低所有标量尺寸的任何困难。 当我们降低标量维数时,支持任何种类的向量转置都会使我们在复共轭方面陷入歧义(请参阅https://github.com/JuliaLang/julia/pull/13612)。 也许我们可以让A[1,:]返回一个向量,但这并不能很好地推广,并且从切片返回非AbstractArray会很奇怪。 如果人们可以尝试#13612并专门查找向量的共轭转置会引起麻烦的情况,那就太好了–对于当前的向量转置语义来说,这同样很糟糕,并且不需要Covectors公开。

一些优点:

  • 直接使用dispatch而不是对Ax_mul_Bx的特殊解析是一个巨大的胜利。 它与BLAS的API组成很好。 通常,您希望在算法的最内层进行共轭,因此将这些信息与参数一起保留更为有意义。 对于BLAS调用,可以使用一个简单的函数来查找转置字符( ntc )。
  • 向量和辅助向量之间的乘法现在是关联的,因为v'v返回标量。 现在,您可以从左到右评估v'v*v并避免形成矩阵。
  • 这允许删除Vector * Matrix,只有在您将所有矢量都视为具有尾部单身维时,它才起作用……这是朝着完全去除对尾部单身维的支持迈出的一大步。

其他说明:

  • 用布尔类型参数表示转置的复杂性可以,但是我经常觉得我以错误的顺序定义了参数。 实际上,我很少要限制或捕获TC 。 我不确定这样做是否会更好,因为您需要定义占位符typevar的数量,但是至少它会匹配AbstractArray{T}
  • 这确实加剧了StridedArray的困难。 读取的方法表*与类型,如相当大的挑战::Union{DenseArray{T<:Union{Complex{Float32},Complex{Float64},Float32,Float64},2},MatrixTranspose{C,T<:Union{Complex{Float32},Complex{Float64},Float32,Float64},A<:Union{DenseArray{T,1},DenseArray{T,2},SubArray{T,1,A<:DenseArray{T,N},I<:Tuple{Vararg{Union{Colon,Int64,Range{Int64}}}},LD},SubArray{T,2,A<:DenseArray{T,N},I<:Tuple{Vararg{Union{Colon,Int64,Range{Int64}}}},LD}}},SubArray{T<:Union{Complex{Float32},Complex{Float64},Float32,Float64},2,A<:DenseArray{T,N},I<:Tuple{Vararg{Union{Colon,Int64,Range{Int64}}}},LD}} 。 当然,我已经将其写为类型别名,但是即使仅管理所有这些不同的类型别名也很麻烦( StridedMatOrTransQRCompactWYQorTranspose等)。
  • 我还没有机会将其与SparseVector集成在一起,但这将是一个巨大的胜利,因为它将不需要CSR就能高效地表示转置。
  • 我们是否需要在Covector上定义标量和/或非标量索引? 如果是这样, r[:]r[1:end]什么? 它是向量还是余向量? 我认为,如果可以的话,我会尝试不定义此内容而逃脱。 有趣的是,Matlab对于将行向量与其他向量建立索引有非常特殊的规则-他们非常努力地保持行的大小,但要付出一些奇怪的极端情况的代价( r((1:end)')r(1:end)r(:)' )。 我现在在我的分支中定义了标量索引,但是也许也应该删除它。 可以清楚地知道,Covector仅与知道它的线性代数运算一起使用。

最后,我只想指出,我在这里看到的大多数优点本身也适用于MatrixTranspose类型。 我认为,这表明Covector可以很好地工作,而不必深入研究抽象代数以实现矢量/矩阵混合运算。 它肯定增加了一个不错的功能(能够与线性代数一致地使用Vectors的功能),但是我不确定是否值得增加额外的复杂性。

出于好奇,如果您仍然记得@mbauman ,是什么促使您引入布尔参数C ? 你不能只是拥有(伪特征)

transpose(::Matrix) -> MatrixTranspose
transpose(::MatrixTranspose) -> Matrix

? 我不怀疑您在这里的(例外)判断,只是想了解导致您执行此操作的原因。

大概可以将其推广为PermutedDimensionArray类型,并使用元组参数编码排列。 Covector类型显然具有不同的目的,需要单独处理。

您需要某种方式来处理共轭转置(以及(A').'未转置共轭数组)。 我看到三种明显的方法可以做到这一点:

  • 将共轭存储为布尔型字段,仅使用一种MatrixTranspose类型。 经过反思,我认为这绝对是与外部BLAS接口的最佳选择。 但是,就本机JuliaBLAS而言,我想确保Julia / LLVM能够将T.isconjugate ? conj(T[i,j]) : T[i,j]吊出循环。
  • 一种具有共轭参数的类型。 这就是我选择的方式,我认为我受到这样一个事实的影响:我们目前通过Ac_mul_Bt和朋友对共轭性进行伪调度。 它还为Julia能够执行的分支删除优化提供了更有力的保证。 但是我并没有对此进行认真思考……我只是想开始一个草图实现,而我更关心Covector。
  • 两种不同的类型, MatrixTransposeMatrixCTranspose 。 与类型参数同构,但是我发现两个单独的包装器类型令人讨厌。 抽象的超类型和联合别名可以提供帮助,但是我仍然会在该选项上选择参数。

我想现在有了第四个带有类型化函数的选项,可以将任何转换函数存储在转置类型中……但是,这还需要一个类型参数以使该函数快速运行,然后这也是一个不容易分派的类型参数。

我想知道我们是否可以使用ConjugateView包装器,并使用规则conjtranspose确保将ConjugateView放入MatrixTranspose包装器中。 即,对于A a Matrix
A' = conj(transpose(A)) = transpose(conj(A))都产生一个MatrixTranspose{ConjugateView{Matrix}} (丢弃无信息的类型参数)。

嗯,是的,这也是明智的。 我倾向于将共轭转置视为一个原子“事物”,因此我错过了该选择。 我在考虑如何使用特殊的子数组索引类型来表示非共轭转置,就像重塑一样。

我很高兴你们还在努力! 这是史诗般的话题! 三声欢呼!!!

这是针对1.0计划的吗?

在两个问题中(#18056,#18136),我指出了这个巨型线程。
因此,我尝试一下。

现在Julia具有真正的一维向量,例如_ mx[row,:]不再是1xn矩阵。
这是一个可喜的变化!

但是,副作用是,一些现有问题变得更加明显。
我被v*mx不适用于一维矢量这一事实所困扰。
从数学上讲,它应该自然起作用并返回一维向量,
当一般a*b产品通过合同定义时
第一项的最后一个索引和第二项的第一个索引。

目前,Vector-Matrix产品的方法签名为:
(*)(A::AbstractVector, B::AbstractMatrix) = reshape(A,length(A),1)*B
并且此方法用于v*v'情况,而不是用于v*mx
(感谢@andreasnoack指出这一点。)
显然,不能同时使用两种方法。

茱莉亚(Julia)似乎在与Matlab之类的公约作斗争。
但是在Matlab中,没有一维向量,只有1xn和nx1个矩阵,
这么多自然的东西在这里可能会有问题。
朱莉娅拥有真正的一维向量,这应该是一个很大的优势。
达到自己真正稳定的状态将是一件好事。

在这种情况下,Fortran是一个更好且更一致的示例。
transpose操作仅为Fortran中的矩阵定义,
从真正的1维向量创建1xn矩阵根本不是转置。
对于matmul请参阅#18056中引用的Metcalf书的摘录。

我认为@alanedelman的大多数原始观点都是正确的。

因此,这里有一条建议可以简单地解决一些现有问题,
同时尽可能尊重当前状态:

  • 保持v'不变,以便从真实的一维矢量v创建1xn矩阵
  • rowmxcolmx函数会更好,但是v'过于广泛而无法更改
  • 我们已经有vec函数来创建一个真正的一维向量
  • 尽管v'的倒数不是v''而是vec(v') ,但我们可以忍受它
  • a*b产品应始终合同的最后一个索引a和的第一个索引b
  • *运算符不得用于内部或外部产品
  • 对于内部产品,我们已经具有dot函数
  • 对于外部产品,应避免使用* (_i.e._ v*v'语法)
  • 对于外部产品,应使用新功能
  • 相应的中缀运算符可以使语法保持不变

失去外部产品的[简洁的数学语法]是不幸的。 我宁愿亲自放弃vec * mat。

现有的PernutedDimsArray是否可以处理父级和视图具有不同维数的情况? 如果是这样,那么即使对于矢量亲本,它也已经可以用作非共轭转置包装器类型。

我在文档中找不到PermutedDimsArray。

但是我觉得与其发明更多的数据类型,
只有三种类型的阵列产品应该清楚地分开。
内部产品已经分开。
我们只需要将普通产品和外部产品分开即可。
外部产品不会丢失,只会改变其语法。
请仅将v*mx情况视为更深层问题的征兆。

除了“缺失方法问题”之外,它不必担心,它是.'返回惰性包装器类型的实现细节(和'其共轭形式)。 否则,我认为我们无法使用相同的*运算符同时拥有vec*matvec*vec' 。 如果我在纸上看到vec*mat ,对我来说似乎是错的,但我经常看到vec*vec'

不过,考虑得更多,我相信PermutedDimsArray不会递归地将其元素换置为数组数组,因此,它不适合用作此处的包装器类型。

另一种选择是完全禁止向量转置。 我认为这里的讨论已经过分彻底,我们只是在等待一个或两个选项的全面实施,以评估其影响。

非常感谢您准备好在本主题即将结束时进行讨论。

尽管v*mx对您来说可能看起来很奇怪,但它在晶体学代码中大量使用。
Fortran的matmul也可以很好地处理它。 (请参阅#18056)

返回到u*v'产品。
uv都是nx1矩阵时,这是一个普通矩阵乘积,
结果证明与外部产品具有相同的结果。
但这只是使用矩阵乘积来模拟外部乘积。
在一切都是矩阵的Matlab世界中,这是无缝的。

在Julia中,我们有更接近Fortran世界的真实一维向量。
在Julia中,转置向量v'已经是一个问题,
Fortran的选择是禁止这样做,正如其他人已经为Julia所建议的那样。

Fortran没有外部产品的内在功能。
朱莉娅很容易将v'转换为1xn矩阵,
并在1维矢量和1xn矩阵上执行*运算。
由于可以进行多次分派,因此可以执行此操作,
但是这里*肯定不再是矩阵产品。

Fortran和Julia表现相似的地方
它们都为内部产品使用了单独的dot函数。
讨论将dot也捆绑到*运算符中,
但幸运的是没有发生。

因此,我们必须处理线性代数的三种乘积:
普通矩阵乘积,内部乘积和外部乘积。
目前, *运算符是常规产品和外部产品的组合语法。
我所建议的只是将这些分开并达到一个更一致的状态。

(哦,我省略了叉积,但它已经很好地分开了)

我不认为内部乘积,外部乘积和矩阵乘法不是对乘积进行分类/分类的数学方法。 以下内容肯定是各个人在上面所陈述的内容的重复,但是鉴于本主题的篇幅,我希望可以时不时地包含摘要。 这是我的个人总结,我当然不是专家,所以请在错误之处纠正我。

在抽象的线性代数设置中,主要参与者是向量v (居住在向量空间V ),线性映射(作用于一个向量空间V并映射到a可能不同的空间W )以及这些地图,线性形式或向量矢量的组成(生活在双重空间V*并从V映射到标量),内积(从V × V到标量),向量之间的张量积(即kron )。 我更喜欢将外积视为向量和辅助向量之间的张量积。

对于此问题,特别有趣的是,如果定义了内积,则向量与线性形式之间的同构,即对于每个f映射向量v到标量,存在一个w这样,对于任何vf(v) = dot(w,v) v 。 但是,可以不参考内部积(例如,多维函数的梯度)而存在协矢量。

要在计算机上表示所有这些对象,通常可以选择一个基础,然后可以使用矩阵代数表示大多数此类对象。 也就是说,如果您愿意灵活地处理尾随的单例尺寸(向量为nx1矩阵,标量为1x1矩阵等),则仅需要矩阵乘法和转置。 这是Matlab的观点,也是许多书籍和论文的写作方式,尤其是在数值线性代数中。

在那种情况下,前述从向量到辅助向量的同构(隐式假设欧几里得内积)对应于对向量的矩阵表示进行转置(在复杂情况下为厄密共轭)。 但是,从抽象的意义上讲,没有向量转置的概念,这可能就是为什么@GaborOszlanyi指出在Fortran中未定义向量的原因。 仅定义线性图的转置(不需要内积,即使在复杂情况下,它的矩阵表示也对应于转置矩阵),以及线性图的伴随物(这确实需要内积) 。

由于Julia代码中类型稳定性的重要性,因此Matlab的“仅矩阵”(具有灵活的尾随单身维)方法不能很好地工作。 但是我们也不想过渡到完全抽象的设置,而继续在典型的设置中工作(欧几里得内积,从向量到辅向量的琐碎映射,...)。 由于最后我们要编写代码并希望使用ASCII字符,因此我们需要从符号*'.'挤出尽可能多的内容。 幸运的是,在这里方便进行多次调度,并导致上述各种建议。 我对此做了一张表格,即抽象线性代数运算将如何转换为特定的朱莉娅方法(而非函数)。

作为对@GaborOszlanyi的最后说明,我仍然没有找到v*A地方。 这在默认情况下将矢量表示为行矩阵的字段中可能是标准的,但我个人认为这是一个奇怪的选择。 如果线性映射fg充当f(v) = v*Ag(v) = v*B ,则这意味着g(f(v)) = (g ◦ f)(v) = v*A*B自合成顺序以来是奇数互换了。 如果有的话,我可以将其解释为不完整的内部产品,就像链接表的倒数第二行一样。

您的总结是深刻而令人信服的。
感谢您对它的解释。

我只剩下两个问题:

  • 透彻讨论之后,Julia实际上将发生什么变化?
  • 为什么Fortran在matmul实现v*mx matmul

此问题暴露出两个问题:

答:应用数学家已习惯于Householder表示法,该表示法隐含地使用了两种自然同构:

  1. 长度为N的向量与Nx1列矩阵是无法区分的,因为Nx1矩阵形成向量空间。 (“合并”,▯)
  2. 1x1矩阵与标量数是无法区分的,因为可以使用标量的所有代数性质来定义1x1矩阵。 (“标量”,■)

问题在于,这些同构都不自然可以用Julia的类型表示,并且都涉及对数组形状的运行时检查。 MATLAB的尾随单维尺寸规则可以认为是这两种同构的实现。

B.二维数组可以递归定义为数组的数组。 但是,矩阵是一排列或一列列,而不是一排行或一列列。 无法递归定义矩阵这一事实突出了矩阵和n维数组的不同张量结构。 正确地说,多维数组是一种非常有限的张量,并且缺少实现后者所需的全部机制。 因为严格说来,收缩是对向量空间及其对偶进行配对的操作,所以谈论收缩多维数组的索引是不恰当的,它从未调用对偶向量空间的概念。 大多数人确实不希望有完整的机制,这需要担心具有行/列性质的协/逆或上/下索引。 取而代之的是,大多数人希望数组是普通的旧容器,除了二维之外,所有维度都完全相反,因此,大多数用户希望将二维数组视为具有矩阵的代数(即向下张量),并且从不向下,向上或向上的张量。 换句话说,二维数组要特例化。


仍然可以说服我,但是这是我目前的三部分建议:

a)完全禁止转置向量,要求用户将向量显式转换为列矩阵,以便编写诸如u'vu*v'u'*A*vu'*A*v/u'v类的Householder风格的表达式uv是真实向量,则不可能给出u'u'*A类的子表达式,而无需引入特殊的TransposedVector类型,这是逻辑上的结果,需要多维数组担心其张量结构中的上/下索引,这似乎太高了代价。

(a)的局限性在于,所有Householderer风格的表达式都将生成1x1矩阵,而不是真正的标量(与返回u'v 1- u'v相比,仍是一个巨大的改进),因此像(u'*v)*w这样的表达式

b)为向量的类似运算引入替代符号,例如

  • u ⋅ v = scalarize(columnify(u)'*columnify(v))用于内部(点)乘积
  • 外部(Kronecker)产品的u ⊗ v = columnify(u)*columnify(v)'
  • A(u, v) = scalarize(columnify(u)'*A*columnify(v)) ,双线性形式的旧表示法
  • A(u) = A(u, u)为二次形式

(b)中的表达式与(a)中的表达式不同,因为它们会自动对内积和双线性/二次形式的表达式进行标量,从而避免了形成1x1矩阵。

c)使1x1矩阵可转换为真正的标量,因此代码类似于

M = Array(Int, 1, 1, 1)
a::Int = M

可以工作。 所需要做的就是使用类似以下内容的数组大小进行运行时检查:

function convert{T}(::Type{T}, A::Array{T,N})
    if length(A) == 1
        return A[1]
    else
        error()
    end
end

该提议是对Folkmar Bornemann大约两年前提出的提议的小修改。 当我们尝试使用它时,运行时检查的成本不是很高,它只能在类型强制的赋值(实际上是变相的convert调用)中调用,而不是常规赋值。

@jiahao ,此帖子的可读性受到难以渲染的字符的限制。 甚至在OS X上看起来也不正确,因为OS X的字体通常完全是Unicode的。

@jiahao ,尽管我想挑战两点,但我当然可以同意大部分/全部观点:

没有引入特殊的TransposedVector类型,这从逻辑上讲是需要多维数组担心其张量结构中的上/下索引的原因,这似乎太昂贵了。

如果您想将TransposedVector作为AbstractArray层次结构的子类型,这对我来说似乎是合乎逻辑的结果,我认为这不是计划(不像某些LazyTranspose类型只是另一种AbstractMatrix )。

外部(Kronecker)产品的u ⊗ v

如果目标是不依赖于隐式同构并将矩阵运算与矢量运算和更多抽象乘积完全分开,则我认为这样做失败的原因如下(对于以下数学定义的通用协议,我不确定):

  • Kronecker乘积A ⊗ B是在矩阵而不是向量上定义的运算。
  • 因此,取而代之的是将u ⊗ v读取
  • u ⊗ v的第三个名称是通常是草率定义的外部产品,我不确定在向上和向下方面是否存在公认的严格定义。 一些资料指出,它等于两个向量的张量积,因此相当于上一点。 相反,如果您接受一个定义,其中“外部乘积”还意味着将第二个向量隐式映射到一个向量,以获得向下张量,那么就没有问题。

取而代之的是,大多数人希望数组是普通的旧容器,除了二维以外,所有维度都完全相反,

我们是否考虑过核选择? 我们从线性代数开始,完全删除AbstractVectorAbstractMatrix的类型别名,并将其替换为:

abstract AbstractVector{T} <: AbstractArray{T,1}
abstract AbstractMatrix{T} <: AbstractArray{T,2}

# and we could introduce:
abstract AbstractCoVector{T} <: AbstractArray{T,1}

我知道会有很多影响,但最终可能会导致多维存储阵列与线性代数之间的清晰分离。 我们无需实现完整的多维张量代数,对偶向量空间等。我们只需实现许多人想要的位:向量,向量和矩阵。 我们得到内部乘积,外部乘积,covector矩阵乘积,matrix-vector乘积和matrix-matrix乘积。 在以上所有内容上定义了转置。 我们可以实现AbstractMatrix实现,这些实现实际上使用1D数组的1D数组(当然不是默认实现)。 我们不必使用Householder符号(IMHO是MATLAB的弱点之一!),但是我们仍然可以获得线性代数的所有便利。

我对此有点紧张,但我相信这将使Julia能够模仿大多数人在大学一年级线性代数中学习的“正确”模型,而无需回溯到Householder惯例。 清楚地区分也可以使将Base.LinAlg移入“标准库”程序包变得更容易,我认为这对Julia来说是一个长期目标? 它还与新的Buffer更改和Array本机实现一起提供一个新的一维可调整大小的列表状对象的想法很好地吻合,因此这种通用的“列表”类型可以将Vector替换LinAlg包,而在很早之前定义了Array和“ list”。

存在许多已经被“简化”的算法,这些算法是用Fortran数组而不是适当的线性代数表示的。 Julia需要能够实现这些算法,而无需人们在多维数组之上重新发现(或强迫)线性代数结构。

在我的工作中,将张量和协/反变指数等映射到Fortran数组上的合适线性代数可能是最好的。 为了使这项工作(而不是使人感到困惑),我将只保留术语“向量”,“矩阵”和“数组”,将其保持在较低的级别,而将其他(更高级的)术语用于更高的级别。 所述较高级别还应该通过抽象类型或通过参数化来抽象存储选项。 也许前缀LA是表达这一点的方式:

LA.Vector
LA.CoVector
LA.Tensor{... describing co/contravariance of indices ...}

然后,向量和矩阵仅用于存储。 在较低级别上,换位是手动管理的(与BLAS中相同)。 在高层,它是自动处理的。

这与@andyferris的建议很接近,只是它不会破坏向后兼容性,也不会破坏Matlab / Fortran / numpy convert的期望。

@eschnett我认为在该线程的较早版本中,已决定将多线性代数保留给软件包,而不是基础Julia。 我认为可以像您建议的那样将许多这些想法充实在一个新的程序包中,该程序包处理类型系统中的张量,向量空间,协/反方差等,与提供便利功能的_TensorOperations.jl_程序包有些不同用于将数组乘以张量。 我认为开发将具有挑战性,但可能是有价值的抽象!

至于只剩下MatrixVector -那么也许当前的定义是完美的,或者也许我们可以为那些想使用标准数学乘法和转置向量的人们做些更好的事情符号。 我想这可能会给新用户增加一条小的学习曲线,尽管我曾希望系统是否明显且雄辩,并且对其他语言进行改进,那么它应该易于学习。

请注意,一般的非欧几里德复数向量空间V具有4个关联空间: Vconj(V)dual(V)conj(dual(V)) 。 因此,普通张量具有4种索引(通常表示为上或下,禁止或不禁止)。 在复杂的欧几里德空间(例如量子力学)中, dual(V) ≡ conj(V)conj(dual(V)) = V 。 在一个实(非欧几里德)空间(例如广义相对论)中, V ≡ conj(V) 。 在后两种情况下,仅需要向上和向下索引。

在实际的欧几里得空间(大多数应用程序?)中,它们都是等效的,朱莉娅的纯数组足以表示张量。 因此至少对于实数,以下两个运算允许构造一个完整且一致的张量代数。

  • 合同/内部产品 ,可以使用以下规则将其推广到排名N任意数组:将第一个数组的最后一个索引与第二个数组的第一个索引(或numpy的dot约定,尽管我觉得不太直观),例如,如果AB排名M ,则A ∙ B返回一个数组M+N-2 M并排名N
  • 张量积A ⊗ B返回排名数组N+M

专门化为向量vw和矩阵AB ,这允许编写以下所有内容:

  • 向量内部乘积v ∙ w ->例外地返回标量而不是等级0数组
  • 矩阵向量乘法A ∙ v
  • 矩阵矩阵乘法A ∙ B
  • 张量/外积v ⊗ w
  • 向量(===向量)矩阵乘法v ∙ A

虽然可能要为使用* ,但陷阱是上面的定义没有关联性: (A ∙ v) ∙ w ≠ A ∙ (v ∙ w)后者甚至不被定义。

但是,只有当您愿意忽略复杂的数组时,这才是。

关于张量的完全通用实现,我是很久以前开始(并放弃)的,在之前,已经为堆栈分配了元组,生成函数以及所有其他可能使它今天变得可行的东西。

有趣的方法,@ Jutho。 似乎足够直观。 我_guessing_可以添加这些定义,而不管我们对*' ,我会支持的。

但是,只有当您愿意忽略复杂的数组时,这才是。

手动插入conj()解决此问题。 而且我认为Julia不想忽略复杂的矩阵,对吗?

我注意到了将AbstractMatrix作为特殊子类型而不是类型别名的更多注意事项:

matrix[:,i] -> AbstractVector{T}
matrix[i,:] -> AbstractCoVector{T}
array_2d[:,i] -> AbstractArray{T,1}
array_2d[i,:] -> AbstractArray{T,1}

这非常酷-我们可以通过索引矩阵来获取列和行向量。 如果只是2D存储容器,我们将获得1D存储阵列。 应该涵盖所有用例! 由于AbstractVector{T} <: AbstractArray{T,1}AbstractCoVector{T} <: AbstractArray{T,1}都遵循APL切片规则的AbstractArray接口。

一个“有趣”的事实是

array_3d[:,:,i] -> AbstractArray{T,2}
matrix(array_3d[:,:,i]) ->`AbstractMatrix {T}

因此,如果要对结果进行矩阵乘法,则必须手动将其包装为矩阵。 我不知道这是加号还是减号? 但这似乎是一个潜在的复杂问题,并涉及到@eschnett提到的有关使其他语言的用户(将存储和线性代数混淆)变得容易的内容。

这可能是一个愚蠢的问题,但是@jutho前面提到过,编写代码受ASCII限制。 为什么在世界上,我们仍然将自己限制在1位于1963年开发并于1986年(30年前)更新的7位字符集上? 在那个时代,著名的640KB是1981年PC上可用的最大RAM。在当今的计算机市场上,我们现在通常有64位处理器和32GB的RAM出售(以前的最大容量是50,000X),而我们还远远没有达到这个水平。 64位处理器的理论极限。 那么,为什么我们仍将自己限制在40年前开发的字符集上?

我们可以使用unicode,只需记住,不幸的是,在过去40年中,键盘的大小并没有以类似的倍数增长(正常人的手指数也没有增加)(在过去的10000多年中)。

恕我直言,应停止使用ASCII。 快速数学符号编程的专用数学键盘实际上是个好主意! 是否有充分的理由不使用UTF随附的更多符号? 您能证明尝试用ASCII压缩一切可能带来的痛苦吗?

@ esd100 :请不要将此GitHub问题用于此类高度投机的讨论。

@Johnmyleswhite。 我不确定“投机”是什么意思。 您确定这就是您要使用的词吗? 我认为ASCII与其他内容的使用与讨论有关,因为似乎语言,运算符和功能都与所使用的符号绑定在一起。 无论如何,我都不是专家,但似乎与所解决的功能有关,或者至少是明确的,与之相关。

我的意思是说,出于某种原因,我们无法按照我们想要的方式定义语言(牢记基本目标:易用性,速度)? 使用特殊的符号/单词会不会使语言更加丰富和美观?

@ esd100,请注意,例如@Jutho的最新注释(使用2个unicode符号)受到好评,并且@yuyichao同意我们可以使用unicode。 还有其他关于引入更多仅unicode运算符的建议(例如,组成功能的组合符号#17184)。 我不认为人们会不同意您的观点(尽管我们确实要牢记一些实际考虑),但是如果您有特定建议,请就我们的建议与我们联系。

我对v0.5的这种新行为感到有些困惑,我认为这是由于这次讨论引起的:

julia> [ x for x in 1:4 ]' # this is fine
1×4 Array{Int64,2}:
 1  2  3  4

julia> [ Symbol(x) for x in 1:4 ]' # bit this isn't? What is special about symbols?
WARNING: the no-op `transpose` fallback is deprecated, and no more specific
`transpose` method for Symbol exists. Consider `permutedims(x, [2, 1])` or writing
a specific `transpose(x::Symbol)` method if appropriate.

如何从列表理解中制作(非数字)行向量? 它必须是行向量。 (作为一个单独的问题还是在其他地方进行讨论,会更好吗?不确定在哪里发布...)

您可以使用reshape: reshape(v, 1, length(v)) 。 也许在过时警告中也应该提到这一点? 我认为这个想法是转置是一种数学运算,因此仅应为数学向量/矩阵定义。

简短说明中提到了它: permutedims(x, [2, 1])

permutedims不适用于向量。 看到这个全新的问题:#18320

我认为这个想法是转置是一种数学运算,因此仅应为数学向量/矩阵定义。

这对我来说毫无意义。 这可以讨论吗? 除非有很好的理由,否则我真的更喜欢转置在非数字数组上工作。

我认为这个想法是转置是一种数学运算,因此仅应为数学向量/矩阵定义。

这对我来说毫无意义。 这可以讨论吗? 除非有很好的理由,否则我真的更喜欢转置在非数字数组上工作。

我认为满足您的要求并在数学上有意义是相对复杂的。 从数学上讲,转置通常是通过交换向量空间及其向量或矩阵的对偶空间来定义的(当然,转置和共轭转置也存在一些复杂性)。 对于规则数M的矩阵,我们将转置为permutedims(M, (2,1)) ,但是通常,您可以根据以下子矩阵定义“块”矩阵:

M = [A B;
     C D]

其中A等是矩阵本身。 我的理解是茱莉亚喜欢

M' = [A' C';
      B' D']

这是您使用笔和纸进行的数学运算,因此是“理想的语法”。

这意味着矩阵的元素必须接受'.' 。 对于数字,它们分别定义为复共轭和无操作。 恕我直言,我认为这有点方便,但是它可以工作,并且最重要的是,它是通过_simple_规则实现的(“转置是递归的”,而不是需要BlockMatrix类等等)。 但这转置中的双关语已从0.5中的非数字类型中删除,因为它没有多大意义。 Symbol的转置是什么?

如果您有_data_而不是数字,则使用permutedims的含义和行为都可以很好地定义:它是非递归的。 使用.'类的双关语可能会节省一些字符输入-但如果您使用的是其他人(可能对数学或MATLAB不太熟悉的人)来阅读代码,这将使_lot_更有意义reshapepermutedims (如有必要)。 对我来说,这是重要的一点。

恕我直言,这个漫长的问题全是关于为线性代数建立一个数学上一致的接口,在我看来,结果自然会减少数据数组的数学双关。

感谢您的周到答复。 我仍然非常不同意

但这转置中的双关语已从0.5中的非数字类型中删除,因为它没有多大意义。 什么是符号的转置?

我不确定将Symbol的转置定义为无操作比在实数上定义转置没有任何意义。 我知道“ pun”很方便,但是似乎要做的“适当”的事情是只为向量和矩阵定义转置,并让transpose(A::Matrix{Complex}) conj作为对象的一部分应用

使用像.'这样的双关语可能会节省一些字符输入-但是,如果您使用的是其他人(可能对数学或MATLAB不太熟悉的人)来阅读代码,则更有意义。根据需要重塑和排列。 对我来说,这是最重要的一点。

我同意明智地使用.' ,并且更明确地调用transpose通常更好。 我认为reshapepermutedims可能需要大量的认知开销才能读取和编码。 老实说,解析起来更快:

transpose([ f(x) for x = 1:length(A) ])
reshape([ f(x) for x = 1:length(A) ], 1, length(A))

即使在这样的简单情况下,您也需要从行的开头(读取reshape )一直跳到结尾(读取length(A) ),以了解发生了什么。 (然后,您很可能会回到中间,以理解为什么length(A)首先出现在其中。)

对我来说,更大的问题是,如果我是新来的Julia(这意味着我以前可能使用过numpy和MATLAB),并且我认为这样做有效:

[ x for x = 1:10 ]'

我自然会假设相同的东西也适用于字符串,符号等的数组。我不会阅读冗长的在线讨论来弄清楚.'的语义-我是将尝试一些事情,并根据过去的经验进行概括/推断。 也许有更好的错误消息会有所帮助。

总的来说,我看不出在Symbol和其他非数字输入上保持无操作转置会如何干扰这里提出的漂亮的数学框架(一个有价值的目标!)。 但是,保持无操作状态似乎无害。

但是,保持无操作状态似乎无害。

您实际上无法为Any有意义地定义任何内容,因为所有内容都是Any的子类型。 定义transpose(x)=x对于任何类型的矩阵来说都是错误的,为了避免某些无提示的错误,我们必须添加这些定义。 因此,在允许相对怪异的语法'的完全非数学运算的便利性与避免无提示错误之间的权衡取舍。

我不确定将符号的转置定义为无操作是否比在实数上定义转置没有任何意义。 我知道“ pun”很方便,但似乎要做的“适当”的事情是只为向量和矩阵定义转置,并让transpose(A::Matrix{Complex}) conj作为对象的一部分应用

我并不完全不同意,但是我们需要实现某种BlockMatrix或为Matrix{M<:Matrix}定义特殊的方法。 我不确定这是不是一个受欢迎的主意? (这对于一直关注的人来说是一个严重的问题,因为它可以简化其中的一些相关问题)。

老实说,解析起来更快:

transpose([ f(x) for x = 1:length(A) ])
reshape([ f(x) for x = 1:length(A) ], 1, length(A))

第二,因为我不涉及/像Julia的电流矢量的换位(显然,_I取矢量transposes__seriously_ :))如果我不得不做第二我会冗长写rowvec = reshape(colvec, (1, n)) ,或者可能只是[f(x) for _ = 1:1, x = 1:n]可以强制理解以使形状正确开始,或者如果您真的喜欢.'那么map(f, (1:n).')f.((1:n).')目前也可以使用。

在允许相对怪异的语法进行完全非数学运算的便利性与避免无提示错误之间的权衡取舍

如果这导致了静默错误和其他问题,那么我想我可能会失去这个论点。 (我不知道为什么会导致错误-但我相信您。)另一方面...。

我们将需要实现某种BlockMatrix或为Matrix {M <:Matrix}定义特殊的方法

我可能错了,但是我认为您只需要执行第二步,对我来说这似乎是一个合理的选择。 但这已经开始超过我的薪水了。

[_的f(x)= 1:1,x = 1:n

我忘了这个! 这可能就是我最终要做的。 总体而言,我仍然不同意您对可读代码的喜好,但对他/她自己却很喜欢! ¯\_(ツ)_/¯

显然,我对向量转置过于重视

是。 我认同。 😉

这(https://github.com/JuliaLang/julia/issues/16790)也将使用reshape代替transpose对我来说更可口。

我可能是错的,但是我认为您只需要执行第二步(编辑:为Matrix{M<:Matrix}定义了特殊的转置方法),对我来说这似乎是一个合理的选择。

不幸的是,现在我们又回到了数据和线性代数之间的区别。 您希望线性代数块矩阵具有递归转置,而2D数据数组的通用2D数据数组具有递归转置...但是,虽然Matrix{T}Array{T,2}是同一件事,去做。

这(#16790)也会使我更喜欢使用重塑来代替转置。

真正!!

您希望线性代数块矩阵具有递归转置,但2D数据数组的通用2D数据数组不具有递归转置

这似乎不是我显然想要的东西……为什么不只有两个不同的函数来处理这些不同类型的转置呢? 我想我错过了关于线性代数对象和数据对象之间非常严格区分的备忘录。

通读这整个线程似乎是一项艰巨的任务,但也许我应该在进一步评论之前这样做。 我不想添加不明智的声音。

我想我错过了关于线性代数对象和数据对象之间非常严格区分的备忘录。

线性代数对象和数据对象之间没有严格的区别。
目前没有实现,也没有理想使用。

如果存在,则将不支持修改大小(使用push! )甚至线性代数对象的值(并且此类用户将使用StaticArrays.jl等),仅broadcast在线性代数对象上受支持。
数据对象将是可修改的,可扩展的,并且将支持map (和reducefilter ),但不支持broadcast

但是我们生活在人们正在思考数据对象的二进制对象与线性代数对象的世界中。
因此,这个2.5年的340条评论主题。


重新进行no-op转置。
我们可以在类型层次结构的最高层添加ScalarNonscalar抽象类型。
Scalars都回退到无操作转置。
Nonscalars没有回退,(但现在回退到弃用警告)

数字,字符,字符串甚至元组可能是Scalar并且将定义无操作转置。 一些标量,例如复数会覆盖转置的定义。

数组(矩阵,向量等)将是Nonscalar子类型,并且具有递归转置。

我不确定是否喜欢。

最近的大量帖子重新讨论了有关#18320#13171#13157#13157的“矩阵转置”和“标量”类型的讨论。

我真的想消除transpose(x)=x无操作后备无害的观点。 在我看来,后备仍然应该产生正确的结果,只是比优化算法慢。 当fallback方法静默计算出错误答案时非常危险,因为这意味着fallback没有正确的语义: transpose(x)含义取决于x

无操作转置回退不仅对于矩阵矩阵是错误的,而且对于不是AbstractMatrix子类型的所有类似矩阵的对象也是错误的(通过#987表示它们已显式存储了条目_not_他们有矩阵的代数)。 这是一个海报子示例(我们有很多示例):

julia> A = rand(5,5); F = qrfact(A); R = F[:R]; Q = F[:Q] #Q is an example of a matrix-like object
5x5 Base.LinAlg.QRCompactWYQ{Float64,Array{Float64,2}}:
 -0.518817    0.0315127   0.749223    0.410014  -0.0197446
 -0.613422   -0.16763    -0.609716    0.33472   -0.3344   
 -0.0675866   0.686142    0.0724006  -0.302066  -0.654336 
 -0.582362   -0.0570904   0.010695   -0.735632   0.341065 
 -0.104062    0.704881   -0.248103    0.295724   0.585923 

julia> norm(A - Q*R) #Check an identity of the QR factorization
8.576118402884728e-16

julia> norm(Q'A - R) #Q'A is actually an Ac_mul_B in disguise
8.516860792899701e-16

julia> Base.ctranspose(Q::Base.LinAlg.QRCompactWYQ)=Q; #Reintroduce no-op fallback

julia> norm(ctranspose(Q)*A - R) #silently wrong 
4.554067975428161

此示例表明,仅因为某物是Any的子类型,并不意味着您可以假定它是标量并且具有无操作转置。 这也说明了为什么OP中的父级问题如此难以解决。 对于类似矩阵的非数组对象Q, Q'作为数组操作没有实际含义,但是具有明确的代数含义:像Q'A这样的表达式定义明确。 其他使用数组而不是线性代数的人只是想要非递归的置换而不关心像矩阵一样的非数组。 但是,事实仍然是您不能对所有类型的代数语义和轴交换语义都具有一致的拼写。

也许我很稠密,但我会以为比较是这样的:

julia> A = rand(5,5); F = qrfact(A); R = F[:R]; Q = F[:Q]
julia> Base.ctranspose(Q::Any) = Q;
WARNING: Method definition ctranspose(Any) in module Base at operators.jl:300 overwritten in module Main at REPL[6]:1.
julia> norm(ctranspose(Q)*A - R) # still works fine
4.369698239720409e-16

以这种方式覆盖transpose似乎允许transpose([ :x _=1:4 ]) -即我发布的内容。 我以为只要您为需要的所有内容正确实现transpose / ctranspose (例如QRCompactWYQ ),便不会调用后备(因为有一个更具体的调用)可以制作)。

您的代码没有调用您编写的ctranspose方法(可以使用@which )。 它在Julia v0.5(在v0.4中不存在)中调用了一个不同的回退方法,该方法实际上在执行ctranspose(full(Q)) 。 另一个后备方法是正确的,但是却打败了我们拥有这种奇特的Q型的理由(这样就可以精确地相乘)。 我对后备应该正确的看法仍然存在。

是的,只要您对所有
需要它,回退当然不会造成伤害。 伤害是你不
如果忘记执行此操作,则会出现无方法错误,但会默默地提示错误
结果。

谢谢@toivoh ,让它对我来说很点击。 我非常感谢您的解释。

但是,如果您定义了一个后备transpose(X::AbstractMatrix)transpose(X::AbstractVector)函数,那么大概总是会得到正确的结果(即使很慢……例如,调用full )不是吗? 而且,您总是可以编写一个更专业的功能来更好/更快地执行它。 然后,除了前面提到的“双关语”(例如,在处理Complex数字...也许其他我不知道的标量用例时), transpose(::Any)永远不会导致无提示错误。

由于历史原因QRCompactWYQ <: AbstractMatrix ,但根据#987#10064,此子类型关系不正确,应将其删除。

类似于矩阵的非数组的另一个示例是IterativeSolvers.AbstractMatrixFcn ,它不是AbstractMatrix的子类型。 对于这种类型,您引用的后备永远不会被派发,这也是我的主要观点。

来自“团队线性代数”的人需要加倍努力并为此付出0.6的代价,否则它将再次受到冲击。

那么重启,实际的计划是什么?

我的思路将我带到: transpose(v::AbstractVector) = TransposedVector(v)其中TransposedVector <: AbstractVector 。 唯一可以将TransposedVectorAbstractVector区别开来的语义是它在* (以及所有A_mul_B s, \/ ,...)。 也就是说,它是确定在* (例如...)下收缩哪些指数的装饰器。 换位将是线性代数的概念,如果要重新排列“数据”的数组,则应鼓励reshapepermutedims

唯一可以将TransposedVectorAbstractVector区别开的语义事物是它在*下的行为

所以v'==vv'*v != v*v' ? 尽管这可能是有道理的,但看起来也可能令人困惑。

所以v'==vv'*v != v*v' ? 尽管这可能是有道理的,但看起来也可能令人困惑。

海事组织这是不可避免的(尽管可能是不幸的)。

向量的对偶也是向量。 转置仍然是一维数据结构,具有定义明确的元素,这些元素应该能够获取,变异,映射,缩小等。

除非我们从阵列中分离线性代数(如使Matrix{T}两者的亚型的不可变的包装Array{T,2} ,与更多的(线性代数具体)定义的方法),那么我不知道有有很多选择,这与其数组和线性代数性质都一致。

(对我而言)一个令人困惑的问题是它是否像矢量一样广播,还是在其第二维上广播。 如果它的大小是(1, n)那么这个整体变化实际上并没有做太多,只是断言它像一个矩阵,其中第一个维度是长度1Vector{T}的转置必须保持为Array{T,2} (即Matrix ...),但是该转置可能又是Vector (例如我们可以有v'' === v )。

那是一个更好的主意吗? 这样可以减少中断,并且仍然可以改善线性代数的语义。 编辑:它的行为与==因为@martinholters出现了。

对我来说,将TransposedVector{T <: AbstractVector{Tv}} <: AbstractMatrix{Tv} *)与size(::TransposedVector, 1)==1但是transpose(::TransposedVector{T})::T听起来是最明智的方法,但是已经有很多争论,也许对此有一些好的论点?

*)我知道这在语法上是无效的,但是希望希望的语义清楚。

是的,在使用代码中的想法后,我发现我同意@martinholters。

我从https://github.com/andyferris/TransposedVectors.jl开始

一个大问题是我们是否可以不使用CTransposedVector类型进行复杂的共轭,还是可以解决这个问题。

我不会使用单独的CTransposedVector ,因为它将两个概念(共轭和重塑)组合到一个对象中。 将这两个概念实现为单独的实体,则更具可组合性。 例如,使用MappedArrays.jl就像

conjview(A) = mappedarray((conj,conj), A)

而且您也可以获得出色的性能。

是的,谢谢蒂姆。 我正在考虑/希望这样的事情-我唯一关心的是如何确保两个包装程序始终以“正确”的顺序出现,但是我认为到目前为止我已经实现的功能可以解决这个问题。 同样,我们需要为所有这些组合提供BLAS支持,到目前为止,我设法避免了这种接触。

一个美丽的事情是,将conj默认conjview单独更改将独立于此更改(我认为它们都可以在独立的软件包中进行修补,并且它们可以正确地组合在一起)。 有没有胃口让conj视图? 我们是否在等待内联非isbits不变量使这些视图免费? 还是不需要等待?

@andyferris :我认为您的方法是一种很好的方法–感谢您的推进。 如果实施效果良好,我们可以在Base中使用该代码。 要记住的一件事是,我们可能还需要https://github.com/JuliaLang/julia/issues/5332的TransposedMatrix

现在,我们继续进行转换,我会再次提及,我们应该尝试避免转置的向量类型。 上面已经提到过两次,但是我怀疑任何人都不会再次阅读有关此问题的所有评论。 为向量引入转置类型实际上会使事情变得复杂,几乎没有好处。 如果您真的认为向量具有方向,则将其设为矩阵,事情就可以了。 具有不同于矩阵的线性代数向量,然后引入新的包装器类型以使向量表现得像1xn矩阵,这没有多大意义。

现在不对称是x'x提升为矩阵,而A*x并未将x提升为矩阵。 如果线性代数运算仅用于2D,则应提升A*xx'x将是1x1矩阵。 或者,我们可以让vector为vector,因此A*x也为vector,但是x'应该为noop或error。 引入转置向量将需要大量新方法定义,唯一的好处似乎是x'x成为标量。

我认为矩阵转置非常不同。 这将使我们摆脱所有Ax_mul_Bx方法,并且无法以向量转置的行为相同的方式来讨论行为。

与引入TransposedMatrix类型相比,我没有真正看到引入TransposedVector类型如何引起更多的问题。

是的,在此问题中间的一个强烈共识是向量转置很奇怪,而且,如果实现了向量转置,则绝对不应该是AbstractArray的子类型-我什至不允许size完全是上面的摘要总结了包含向量的一些具体细节,其中包括符号(请注意,我建议您采用蒂姆的方法在数组上映射conj而不是将其作为类型进行映射)参数)。

但是有一个更强烈的要求,就是向量转置只是一个错误。 如果是这样,那么我认为它实际上可以存在于包装中。

向量转置是一个错误,好像把婴儿扔进了洗澡水。 不能写v'来转置向量真的很烦。 除了让TransposedVector类型与向量和矩阵相乘之外,我真的没有什么坏处像TransposedVector类型那样。

这取决于您要通过向量转置处理的行为数量。 如果您只是想要v'' == v ,那么typeof(v') <: AbstractMatrix 。 但是,如果您想要我们在该线程中讨论过的其他事情,例如typeof(v'v) <: Scalartypeof(v'A) <: AbstractVector ,则它需要是另一种,更复杂的野兽。

让TransposedVector成为Vector的想法似乎是许多问题的根源。 像现在一样,将其作为一种矩阵并然后具有size(v.') == (1,length(v))似乎没有那么多破坏性。 唯一的不同是,双重转置为您提供了矢量返回,而v'v将产生一个标量。 如果想将转置作为向量,则可以使用length(v')给出正确答案,并且线性索引可以按预期工作。

我同意@StefanKarpinski的110%。 我开玩笑地将其设为一种向量,但是由于该线程前面讨论的种种原因,它没有太大的意义,而是很容易破解。

使其大小(1, n)方法表示,其行为与Array行为完全相同。 将$ _ '.'*\下的行为将使其与1×N Matrix区别开。 和/

这些都不是“类似数组”的运算符。 它们纯粹是在矩阵和向量之间实现线性代数的,直接来自MATLAB(“矩阵实验室”)。 最后的改进只是简单地将size(tvec, 1) = 1静态地传递给编译器_and_,我希望v''v 。 (我有点像StaticArray ,其中一个尺寸是固定的,而另一个尺寸是动态调整的)。

如果只需要v''== v,那么typeof(v')<:AbstractMatrix就可以了。

对。

但是,如果您想要我们在该线程中讨论过的其他内容,例如typeof(v'v)<:标量或typeof(v'A)<:AbstractVector,则它需要是另一种,更复杂的野兽。

为什么? 我们没有破坏其任何类似于数组的属性,因此它仍然可以是数组。 任何打点电话都将像以前一样工作,并且其他矢量化操作将被删除。 我认为*是基于对VectorMatrix的各种形状的了解而被“专业化”的,这是因为我们试图模仿线性代数的行为。 将v' * v设为标量是_very_标准数学,并且我的解释是,从一开始就并非如此,唯一的原因是因为以一致的方式实现并非易事(Julia从中汲取了灵感MATLAB),但Stefan可以对此进行澄清。 使内积成为标量是这里要说的唯一突破性变化(还有其他非常小的事情)-我不明白为什么仅凭这一点就不适合成为Array (有很多类型的Array不具有有效的'.'*\/ _at ALL_定义,尤其是3级以上)

我去年遇到的问题之一就是模棱两可。 IIRC,当向量转置不是数组时,它们更容易解析。

是的,我有点担心这会在我完成之前变得多深... :)

作为一般说明,如果/当我们减少Base整体性并将其分解为标准库时,这将是很棒的。 具有仅定义AbstractArrayArray的自包含包或模块将使这种开发更加简单。

@Jutho的建议到底出了什么问题? *自动(共轭)将向量移到左侧吗?

如果我们有*矩阵乘法, '矩阵(共轭)转置(使向量不变)和两个运算符promote加上尾随的单例和demote删除尾随的单例,然后我们可以构建一个右应用程序*: (n,m) -> (m,) -> (n,) ,一个左应用程序*: (n,) -> (n,m) -> (m,) ,标量乘积*: (n,) -> (m,) -> (,)和一个外部乘积×: (n,) -> (m,) -> (n,m)这样:

(*)(A::AbstractMatrix, v::AbstractVector) = demote(A*promote(v))
(*)(u::AbstractVector, A::AbstractMatrix) = demote((promote(u)'*A)')
(*)(u::AbstractVector, v::AbstractVector) = demote(demote(promote(u)'*promote(v)))
(×)(u::AbstractVector, v::AbstractVector) = promote(u)*promote(v)'

我不确定我是否需要将这些向量转置为向量。 我想念什么吗?

编辑:不完全是@Jutho的建议。

@Armavica ,我不太确定将这个含义分配给* ,而是分配给新的(unicode)运算符 ,这实际上是我的建议,目前相当于dot 。 此外,我认为您无法以类型稳定的方式实现promotedemote方法。

内心的纯粹主义者同意dot(v,w)不是v'wv'*w ),但是我也很感谢以@andyferris提出的方式工作的灵活性。 因此,我不会在此讨论中添加任何其他内容,也不会支持任何使实际实现运行的明智尝试。

是的,@ Jutho。 有趣的是,两者并不是互斥的:默认情况下在向量上不转置就意味着此功能可以合法地存在于单独的程序包,模块或“标准库”中(即没有“盗版”)。

(我个人更喜欢随时在v'w或v'* w上写点(v,w))

Jutho-另一方面,我敢打赌,您虽然不会在纸或白板上写|ψ>⋅|ψ>,而是<ψ|ψ>(并且我们绘制内积的方式也一样)与张量网络图)。

当然,我很乐意将狄拉克的曲折符号视为所有数学甚至科学中线性代数的标准表示形式,并在茱莉亚中得到扩展,但可能不会发生。

现在,您迫使我离开了,我不再打算这样做了。 我当然同意偏爱<ψ|ψ>作为内积,但这与我不考虑<ψ|ψ>的事实无关。 作为|ψ>的Hermitian共轭。 埃尔米特共轭是为运算符定义的。 向量及其相关对偶向量之间的自然同构(共向量,线性泛函等)被称为Riesz表示定理,尽管当将长度n向量解释为线性映射时,前者当然等同于埃尔米特共轭。从C到C ^ n,即大小(n,1)矩阵。

当然,我很乐意将狄拉克的曲折符号视为所有数学甚至科学中线性代数的标准表示形式,并在茱莉亚中得到扩展,但可能不会发生。

大声笑

现在,您迫使我离开了,我不再打算这样做了。

抱歉...我真的不应该提到它... :)

我不认为<ψ| 作为|ψ>的Hermitian共轭。 埃尔米特共轭是为运算符定义的。

是的,我没有考虑过。

向量及其相关对偶向量之间的自然同构(共向量,线性泛函等)被称为Riesz表示定理,尽管当将长度n个向量解释为从C到C的线性映射时,前者当然等同于Hermitian共轭。 n,即大小为(n,1)的矩阵。

我正在尝试不偏离题名(但我对此很不好),但是我认为我们得到了最后一点,因为我们将AbstractVector与1D Array关联。 换句话说,在数学意义上, AbstractVector并不意味着“抽象向量”,而是意味着“具有某种预定基础的向量的数字元素”。 由于向量的大小(n,1) ,因此MATLAB轻松摆脱了这一困境。

值得深思的是,您怎么称呼(反线性)运算符,该运算符采用形式|a>|b><c|所有张量并将它们映射到|c><a|<b| ? 我总是将其与向量对偶和标准运算符共轭捆绑在一起,称为“埃尔米特共轭”,但这也许太过分了。

@alanedelman进行了对话,该对话

  • 介绍CovectorRowVector类型;
  • 用于线性代数运算( *'.'/\ ,也许norm ?其他? )这种类型在线性代数中充当协矢量;
  • 对于数组操作(其他所有操作),此类型充当二维1×n数组;
  • 特别是,向量的size(v')(1, length(v))

这种方法需要将Base中的所有泛型函数归类为线性代数或不归为线性代数。 特别地, size是一个数组函数,在线性代数的范围内没有意义,线性代数本质上只有向量(没有“形状”,只是维数的基数),线性变换(其中可能恰好由二维数组和标量表示。 出现的一个特定示例是cumsum ,它当前在二维数组上运行,就好像提供了维度参数1一样。 这与sum不一致,而不是默认使用维度参数1返回整个数组的总和。 它还可以防止cumsum以与在矢量上进行操作相对应的方式在矢量上进行操作。 我认为cumsum应该通过按N维列优先顺序进行累加求和来对通用数组进行操作。 更一般而言,维度参数不应默认为1。

如果没有用于识别辅助矢量的接口,我会有些担心。 大多数操作(例如大小和ndims)会说它们就像其他2d或1d数组一样。 只有尝试例如点积,您才能看到差异。 在AFAICT中,您必须检查对象是否为RowVector,但是很少有要求对象具有特定类型才能具有某种常规属性的情况。

@StefanKarpinski我同意所有这些。 特别是,这些函数是“数组”运算或“线性代数”运算。

我从这里的大小=(1,n) TransposedVector开始,这正是您所说的。 我花了一些时间才能很好地涵盖测试和所有组合*\/每种可能的ct方法和变异方法,同时避免了与其他方法的歧义。 这是一项缓慢而又系统的工作,尽管如此,我可以在准备就绪时将其拉到基础(可能重命名)。

它们与Covector的区别在于,它实际上应该是共轭转置(或者在一般情况下甚至是更一般的变换!),而RowVectorTransposedVector在概念上更简单。

@JeffBezanson是否可以使用indices()做些“单身”尺寸的事情?

但是很少需要对象具有特定的类型才能具有特定的常规属性。

是的,如果这是特质或其他东西,那就太酷了。 我希望我们可以更一般地从数组中解开线性代数,但这是一个巨大的变化。 如果没有在Julia中实现的漂亮的trait语法,可能无法整齐。 我认为这里的问题是我们将3种事物(向量,向量和矩阵)映射到两种类型( AbstractArray{1}AbstractArray{2} )上,因此其中之一(向量)成为一种特殊的子类型其他的。

如果我可以考虑到某个地方,除了基本的“包装”实现之外,还需要其他任何东西,我会把AbstractTransposedVector放入包装中。

@JeffBezanson :我没有引起您的关注。 这个想法是,除了线性代数运算外,它的行为类似于1×n 2d抽象数组,对于线性代数运算,它的行为类似于列向量的对偶空间(与1×n 2d抽象数组同构)。 如果要检查covector,可以检查类型,例如通过分派它。

我为解决此问题的尝试的最新消息:

我相信TransposedVectors.jl现在“功能完整”。 它包含执行@StefanKarpinski在这里所说的所有必要的机器-向量的转置是向量的包装,其表现为二维,1xn大小的抽象数组-但对于线性代数运算( */\'.'norm )它表现为行向量(或对偶向量)。

您可以像这样检查它:

julia> Pkg.clone("https://github.com/andyferris/TransposedVectors.jl")
...

julia> using TransposedVectors
WARNING: Method definition transpose(AbstractArray{T<:Any, 1}) in module Base at arraymath.jl:416 overwritten in module TransposedVectors at /home/ferris/.julia/v0.5/TransposedVectors/src/TransposedVector.jl:28.
WARNING: Method definition ctranspose(AbstractArray{#T<:Any, 1}) in module Base at arraymath.jl:417 overwritten in module TransposedVectors at /home/ferris/.julia/v0.5/TransposedVectors/src/TransposedVector.jl:29.
WARNING: Method definition *(AbstractArray{T<:Any, 1}, AbstractArray{T<:Any, 2}) in module LinAlg at linalg/matmul.jl:86 overwritten in module TransposedVectors at /home/ferris/.julia/v0.5/TransposedVectors/src/mul.jl:9.
WARNING: Method definition At_mul_B(AbstractArray{#T<:Real, 1}, AbstractArray{#T<:Real, 1}) in module LinAlg at linalg/matmul.jl:74 overwritten in module TransposedVectors at /home/ferris/.julia/v0.5/TransposedVectors/src/mul.jl:37.
WARNING: Method definition Ac_mul_B(AbstractArray{T<:Any, 1}, AbstractArray{T<:Any, 1}) in module LinAlg at linalg/matmul.jl:73 overwritten in module TransposedVectors at /home/ferris/.julia/v0.5/TransposedVectors/src/mul.jl:64.

julia> v = [1,2,3]
3-element Array{Int64,1}:
 1
 2
 3

julia> vt = v'
1×3 TransposedVectors.TransposedVector{Int64,Array{Int64,1}}:
 1  2  3

julia> vt*v
14

julia> vt*eye(3)
1×3 TransposedVectors.TransposedVector{Float64,Array{Float64,1}}:
 1.0  2.0  3.0

性能可能会有所下降-基准测试将很有用。 在某些情况下,它将制作较少的副本(转置是惰性的),而在其他情况下,它将制作更多的副本(例如,在Ac_mul_Bc创建矢量的共轭副本(从不创建矩阵))。 在我们将不可变字段内联到不可变字段中之前,包装器本身会花费很少的成本(对我而言,很幸运,这对于StaticArrays.jl :smile:已经很好用了)。 还有一个问题,就是要在适当的时候确保它是某种StridedArray

如果人们喜欢这种方法,我们可以看到即将进行公关以将代码迁移到Base (该软件包已经进行了单元测试,并且解决了所有歧义)。 (另外, @ jiahao提到要对偶矢量的一维抽象数组版本进行实验,不确定是否取得了进展?)

人们是否认为,如果没有换行矩阵的包装器类型,那么这样的PR在v0.6中是否有意义? 虽然转置向量是语义上的变化,但转置矩阵更像是一种优化,因此我想它们可以分开进入。 当然,如果某些转置是视图,而某些是副本,则似乎是“奇怪的”? 要考虑的另一点是,一旦转置矩阵和向量都进入,则需要完成很多工作以删除所有At_mul_Bt函数,用*上的方法代替,以完成过渡(尽管简化将是有益的!)-我怀疑任何人有能力或愿意在本月底之前完成所有这些工作...

太好了,@ andyferris! 您是否尝试过使用通用的惰性Conjugate包装器?

@andyferris我踢了轮胎,就像这样。 严格来说,这似乎是一种改进,我希望可以在发现问题后对其进行处理。 让我们看看其他人怎么说。

多谢你们 :)

您是否尝试过使用通用的惰性Conjugate包装器?

还没有,尽管没有太多的努力就可能实现,但是老实说,我担心在12月底之前不能完成那么多工作。 展望未来,我考虑到最终我们可能要为线性代数分配以下“ unionall”类型:

  • AbstractVector
  • Conjugate{V where V <: AbstractVector} (或Conj{V} ,甚至ConjArray ?当然可以接受任何维数的数组)
  • TransposedVector (或RowVector ?)
  • TransposedVector{Conjugate{V where V <: AbstractVector}}
  • AbstractMatrix
  • Conjugate{M where M <: AbstractMatrix}
  • TransposedMatrix (或简单地Transpose{M} ?)
  • TransposedMatrix{Conjugate{M where M<:AbstractMatrix}}

(再加上基础存储的所有给定特征,例如我们现在称为DenseArrayStridedArray ,我希望#18457可能也更容易表达这些特征)。

除了命名之外,这听起来是否合理?

至于立即的自行车脱落,对于当前包装中的基础的公关,我们是否希望为向量和矩阵使用TransposedVectorRowVector ,通用Transpose包装器, 或者是其他东西?

我认为@andyferris的实现提供的行为非常好,并且有所改进。 但是,让我尝试更好地表达我的关注。

问题在于定义新的数组类型。 例如DArrayAbstractArray的子类型,因此DArray也可以是AbstractVectorAbstractMatrix 。 理想情况下,我们将Vector / Matrix的区别扩展到Row / Column / Matrix,所以DArray可以是AbstractRowAbstractColAbstractMatrix 。 类型层次结构类似于

AbstractArray
    AbstractVector
        AbstractRowVector
        AbstractColumnVector
    AbstractMatrix
    ...

昨天正在与@jiahao讨论此事。

这里可能有一些优先级,因为不是所有的AbstractVector都可以用作列,例如:

julia> isa(1.0:10.0, AbstractVector)
true

julia> randn(10,10) * 1.0:10.0
ERROR: MethodError: no method matching colon(::Array{Float64,2}, ::Float64)

可以解决我在https://github.com/JuliaLang/julia/issues/4774#issuecomment -59428215中遇到的问题

那只是在范围内缺少括号?

那只是一个优先问题:

julia> randn(10,10) * (1.0:10.0)
10-element Array{Float64,1}:
 -22.4311
  ⋮

我同意,这里的最佳解决方案似乎需要特性,但我认为这不应妨碍@andyferris的工作。

啊,好点。 希望将不正确的空格优先级作为语法错误,类似于Fortress。

MIT小组指出,我上面描述的一种实现类型层次结构的方法是在数组层次结构中添加类型参数:

abstract AbstractArray{T,N,row}

type Array{T,N,row} <: AbstractArray{T,N,row}
end

typealias AbstractVector{T} AbstractArray{T,1}
typealias AbstractRowVector{T} AbstractArray{T,1,true}
typealias AbstractColVector{T} AbstractArray{T,1,false}
typealias AbstractMatrix{T} AbstractMatrix{T,2}

typealias Vector{T} Array{T,1,false}
typealias Matrix{T} Array{T,2,false}

我还没有弄清楚所有的含义-可能最糟糕的是Array{Int,1}变成了抽象类型---但这似乎使类型层次结构和所需的抽象几乎完全正确。

就其价值而言,这正是不完全参数化的超类型的用例。 它们可以成为隐式默认参数。

abstract AbstractArray{T,N,row}

type Array{T,N} <: AbstractArray{T,N}
end

isrow{T,N,row}(::AbstractArray{T,N,row}) = row
isrow{T,N}(::AbstractArray{T,N}) = false

julia> isrow(Array{Int,2}())
false

当然,这会使调度有些混乱……并且可能不值得支持。 这只是个吐痰球。

对我来说,这相当于定义

type Array{T,N} <: AbstractArray{T,N,false}
end

不过,我们可能想要的是某种“默认类型参数”,例如,使类型Array{X,Y}其中X和Y没有自由变量)实际上得到Array{X,Y,false}

另一个想法:保留内部产品v'v Householder符号,并通过将infix '为其自己的运算符,而不是将它们解析为v'*vv'*A而保留余矢量乘以v'A v'*A 。 再加上使v'标识, v*v一个错误和v*A一个错误,这可以提供很多期望的东西。 您将不得不将外部产品写为outer(v,v)

在这样的方案中, v'将是无用的甚至是错误的-因为没有理由在这样的方案中转置向量,所以仅对矩阵有意义。

@JeffBezanson我同意最好使用AbstractRowVector (但老实说我无法想到用例,因此我没有在包中实现它)。 我还想指出,这可能是AbstractMatrix的子类型。 我朝这个方向发展的原因是,与线性代数相比, broadcast似乎对Julia来说更像是一个核心概念,人们会期望行向量是行!

当然,不幸的是使用RowVector <: AbstractMatrix ! 我认为这是由于2D数组和抽象矩阵被赋予了相同的名称而导致的。

我之前已经说过了,但远远超出了上面,但是由于这个问题持续了很长时间,所以我要重申一下:在像Julia这样的通用语言中,“数据数组”属性必须是AbstractArray的主要考虑因素broadcast对“转置”向量表现如何,将告诉您它是1D还是2D。 如果要考虑行和列,则1 x n行向量最有意义。 如果您想考虑对偶向量,那么1D最有意义-我对此也很满意! 但是,采用向量的对偶比重塑数据更复杂(例如,我们至少必须支持共轭)。

我猜想“行”图将符合“典型”程序员的期望,而接受过高级数学训练的人可能会更好地利用对偶向量,因为它是更好的抽象(尽管我确信他们能够同情那些只有基本线性代数知识的人。 那么-朱莉娅针对的受众是-具有比许多典型的MATLAB用户更高的数学精通度的人,或者具有较少数学精通度的人?

(我的看法是,由于Julia的目标是成为一种“通用”编程语言,因此目标受众是后者)。

由于我们正在讨论可能的类型树-将来,使用Buffer和可调整大小的“列表”,我在想象一个抽象的接口树,它沿着

AbstractArray{T,N} # interface includes broadcast, Cartesian, getindex, setindex!, etc.
    AbstractArray{T,1}
        AbstractList{T} # resizeable, overloaded with `push!` and so-on
        AbstractVector{T} # non-resizeable, overloaded for *, /, \, ', .', etc
    AbstractArray{T,2}
        AbstractRowVector{T} # non-resizeable, overloaded for *, /, \, ', .', etc
        AbstractMatrix{T} # non-resizeable, overloaded for *, /, \, ', .', etc

当然,我们同样可以拥有AbstractDualVector{T} <: AbstractArray{T,1}而不是AbstractRowVector

具有灵活,具体的Array类型以适合所有这些类型将很困难(也许是不必要的)。 特性肯定会让我们更轻松地在支持的界面中表达这些差异。

AbstractVector既是C ++ std::vector又是线性代数向量对我来说似乎有点厚脸皮:)(特别是因为数组接口意味着它永远不可能真正成为对象中的“抽象向量”)线性代数意义(例如无基数)

是的,最好将大小调整行为分开。 我为此而努力。

该类型层次结构似乎暗示我们在Base中将有单独的“矩阵”和“ 2-d数组”具体类型。 我们可以尝试解决这个问题,例如,让Array{T,2}的构造函数实际上返回其他矩阵类型,但这看起来很丑陋。 也许我误会了。

该类型层次结构似乎暗示我们在Base中将有单独的“矩阵”和“ 2-d数组”具体类型。

是的...我想很好地做到这一点,我们需要特质。 请记住,我称其为“抽象接口树”。 目前尝试这样做将需要像您所说的那样丑陋的东西。 但是,一旦完成Buffer我们可能会插入AbstractList <: AbstractVector (并移动关联的方法)。

目前,受支持的接口和类型树之间的连接非常松散。 很难在调度时弄清一个(抽象的)数组是否可变(即是否支持setindex! ),它是否可调整大小,stride仅适用于Base定义的类型,等等。很难为用户提供一种功能,该方法可以通过多种输入有效且有效地工作(这是我从StaticArrays )。

关于类型层次结构的数学区域和参数化抽象的合理角色的对话的思考。 在可行的情况下,Julia的一项好政策是简化和减轻代码编写方法之间的任何分离,以实现专门技术的目的和专家表达的意图。

我们可能会选择使用的一种惯例,是使用任何一种抽象类型(不是Any)来构成本体区域的一种常见做法,在这种拓扑结构中,伴随的具体类型发现共享的休憩变得容易。 这将以很少的代价带来更大的跨上下文敏感性-理解类型层次结构的某些部分可以帮助一种方法接近其他部分。

如我所见,这是一个广义的融洽相处者。 阐释性的抽象照亮为附近的凝结点,即构成的生成相似性。 通过带有直觉的简单明了的参数化,Julia可以以更少的废话时间完成许多工作。

好的,对TransposedVectors的更多评论将不胜感激。 我觉得它变得相当扎实,随时可以变成PR,但是也有一些相关问题,我已经在这里列出

(例如: RowVector是比TransposedVector更好的名称吗?是[1 2 3]RowVector还是Matrix ?什么是indices(row, 1) ?)

+1为RowVector

2016年12月20日上午7:01,“ Andy Ferris” [email protected]写道:

好的,对TransposedVectors的更多评论将不胜感激。 我觉得
它已经变得很坚实,准备转变成PR,但是有
我在这里列出的一些相关问题
https://github.com/andyferris/TransposedVectors.jl/issues

(例如:RowVector比TransposedVector更好吗?[1 2
3] RowVector还是矩阵? 什么是索引(行,1)?)

-
您收到此邮件是因为您发表了评论。
直接回复此电子邮件,在GitHub上查看
https://github.com/JuliaLang/julia/issues/4774#issuecomment-268170323
或使线程静音
https://github.com/notifications/unsubscribe-auth/AAm20YYqsXmprI23GgI5PYyWStpTOq5qks5rJ309gaJpZM4BMOXs

我真的很想避免使用行/列约定,而且我认为拥有VectorRowVector (甚至ColVectorRowVector )。 为TransposedVectorDualVector +1

@felixrehren我觉得DualVector语义应与我实现的语义不同,主要是一维的(数学上,向量的对偶是向量),并具有其他对偶性(例如,复杂的共轭) 。 很好,但是我发现实现起来有点困难,向后兼容性也差一些。

名称TransposedVector可以。 但这有点长。 同样,这暗示了只能通过转置Vector获得该类型的对象。 但是我想您也可以通过其他方法制作TransposedVector ,例如,提取矩阵的一行吗?

我认为RowVector是个好名字-简洁,准确和直观。 @felixrehren ,我认为行/列图片很有帮助。 这甚至可能是不可避免的,因为每当您进行串联或其他常见的数组操作(而不是乘法)时,您都需要考虑向量的定向方式。

DualVector也不错,但是CoVector听起来不太正式。

我先前威胁的PR现在提交于#19670。 我现在带着RowVector去了。

但是我想您也可以通过其他方式制作TransposedVector,例如,提取矩阵的一行吗?

这实际上是一个症结-我还没有想到出色的语法。 有任何想法吗?

但是我想您也可以通过其他方式制作TransposedVector,例如,提取矩阵的一行吗?

这实际上是一个症结-我还没有想到出色的语法。 有任何想法吗?

虽然起初看起来确实吸引人的是matrix[scalar,range] (以及其他类似的构造)产生行向量,但这与多维数组的当前索引语义有很大的出入,特殊情况使我感到厌烦。

相反,我希望看到RowVector (和Vector )将任何可迭代的类型转换为适当的向量。 然后,您可以执行类似RowVector(matrix[scalar,range]) ,这很清楚并且不会干扰数组索引的当前行为。

正确,第i行可以由当前在v0.5中的A[i,:].'以行形状提取,并且将来会继续这样做(以生成RowVectorTranspose{V<:AbstractVector}或我们最终达成的任何协议(有关正在进行的讨论,请参见此处)。 也许就是答案。

仅向Base添加两个新功能呢?
row(A,i)col(A,i)
后者不是必需的,只是为了对称。 简洁明了,最多可包含A[i,:].'字符

@benninkrs有意义。 相反,我的直观解释是线性代数,其中向量根本没有方向。 关于此的所有观点都是合理的,我只是不喜欢VectorRowVector在一起,因为命名看起来像是将抽象和具体范式混合在一起。

在这一点上,我认为我们要么需要基于@andyferris的实现,要么决定不认真对待向量转置。 作为替代示例,以下是一种处理最严重症状的方法:保留v'当前状态(即生成行矩阵),但将a'b解析

  1. v'是一个行矩阵(ctranspose)
  2. v'v是标量(点积)
  3. v'*v是1元素向量(行矩阵*向量)
  4. v*v'是一个外部乘积矩阵(向量*行矩阵)
  5. v'A是一个行矩阵(“ vecmat”)
  6. v'*A是一个行矩阵(matmat)
  7. v'A*v是标量(matvec A * v然后是点积)
  8. (v'A)*v是1元素向量(vecmat然后是matvec)
  9. v'*A*v是一个1元素的向量(matmat然后是matvec)
  10. v''是一个列矩阵(向量ctranspose,然后矩阵ctranspose)

在当前设置中,2和3是等效的,而7,8和9是等效的,此更改会中断。 但至关重要的是,大胆的项目是人们通常会喜欢的项目,因为它们是类似形式中最短,最方便的,而且它们都按照我们希望的方式进行。 没有新的类型,只有一个新的中缀运算符。 主要缺点是10 – v''仍然是列矩阵。 可以说,这可以看作是一种好处,因为后缀''是将向量转换为列矩阵的运算符。

退后一步,我认为我们了解到的是,如果没有像Matlab一样可替代的上下或维标注功能或将≤2维视为可替代的,多维数组就无法真正地与线性代数平滑地相吻合。 因此,剩下的就是方便性问题了—我们可以让人们方便地在向量和矩阵上表达常见的线性代数运算,而不会使数组过于复杂吗? 我对这种方法没有犹豫,但我认为我们应该认真考虑这种方法和其他解决语法方便性的方法,而不会过多地破坏我们的数组类型层次结构。

另一种方法是专门分析中缀a'b (略低于* )并让v'返回共轭向量。 更一般地,后缀A'可以共轭数组并延迟反转其维数,而A.'只是延迟地反转数组的维数,从而充当向量的标识。 在此方案中,属性列表可能如下:

  1. v'是向量(共轭)
  2. v'v是标量(点积)
  3. v'*v是错误(内部产品推荐v'v outer(v,v) ,外部产品推荐
  4. v*v'是错误(内部产品推荐v'v outer(v,v) ,外部产品推荐
  5. v'A是向量(vecmat)
  6. v'*A是错误(vecmat推荐v'A
  7. v'A*v是标量(matvec A * v然后是点积)
  8. (v'A)*v是错误(内部产品推荐v'v outer(v,v) ,外部产品推荐
  9. v'A'v是一个标量v'(A'v) –共轭矩阵,然后是内积)
  10. v''是一个向量v'' === vv.' === v

现在,我写出所有这些属性,这可能是更可取的选择:所有错误案例实际上都可以帮助人们发现和使用所使用的语法,当然,它还具有理想的v'' === v属性(并且很好地实现了对接)其中.'是通用尺寸反转运算符)。 具有非常相似的语法但仅稍有不同可能会更加令人困惑。

†如果用户代码使'*从而使这些操作起作用,我们可能会在解析时捕获这些错误以获得更精确的错误,并可能会出错。 我认为可能需要使用惰性共轭包装器来使这些建议正确无误,但是对于#5332,我们仍然需要这样做。

退后一步,我认为我们了解到的是,如果没有像Matlab一样可替代的上下或维标注功能或将≤2维视为可替代的,多维数组就无法真正地与线性代数平滑地相吻合。 因此,剩下的就是方便性问题了—我们可以让人们方便地在向量和矩阵上表达常见的线性代数运算,而不会使数组过于复杂吗? 我对这种方法没有犹豫,但我认为我们应该认真考虑这种方法和其他解决语法方便性的方法,而不会过多地破坏我们的数组类型层次结构。

:100:

明确地使后缀'.'泛型数组(而不是线性代数)很好地避开了存储和线性代数类型的合并加倍的问题,并且为涉及较少妥协的框架敞开了大门。 在此期间,这些操作在大多数情况下可以模拟Householder符号的能力应提供大多数所需的便利。 更少的代码和复杂性。 最好!

与一个问题v.'是一个无操作是A .+ v.'将改变从添加的值意味着v到的每列A于添加的值到A 。 通常,这会使使用向量进行类似行的操作变得更加困难,并且肯定需要一个完整的弃用周期,以免使代码默默地执行错误的操作(在这种情况下, A恰好是平方的)。

在这一点上,我认为我们要么需要基于@andyferris的实现,要么决定不认真对待向量转置。

我知道v0.6的截止日期快到了,但是我要提防不要把婴儿和洗澡水一起扔掉。 在此阶段,似乎提到的RowVector以及矩阵转置和数组共轭的视图将使我们得到:

  • IMO,线性代数类型稍微合理一些(尽管行向量可能被视为对偶向量的一种特殊情况,但我们现在并不否认对偶向量的存在)
  • v'' === v
  • Stefan列表中的某些东西,例如v1'v2是点积
  • 几乎向后兼容的数组语义-例如size(v')的结果不变,但是我们将v''作为一维
  • 惰性conj和转置包装器在某些情况下可能会提高性能,并且仍然很有用
  • 删除所有Ac_mul_Bc函数,仅支持*A_mul_B! (以及类似的\/ )。

老实说,以Array来完成所有这些工作不会花费太多的精力(对我来说,问题是在每年的这个时候找时间,并追逐线性代数套件中的所有其他类型)。 最后一点是松一口气。

另一方面-恕我直言,这些规则似乎使解析稍微复杂化,并且可能有点混乱和/或令人惊讶,它们如何将'* (3、4、6和8可以使用RowVector )。

是的,我们必须弃用v.'或其他东西以突出显示潜在的错误,这时使v.'成为缺少的方法错误似乎更好(我们根本不支持行) /双重向量,但如果愿意的话,也不会阻止软件包这样做)

如果有兴趣将某些东西偷偷带入v0.6,则19670看起来已经准备就绪或接近它。

BAM

这是我们最长的问题线程吗?

不,#11004还有更多

抱歉。 没错,我应该指定公开问题线程。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

iamed2 picture iamed2  ·  3评论

ararslan picture ararslan  ·  3评论

StefanKarpinski picture StefanKarpinski  ·  3评论

TotalVerb picture TotalVerb  ·  3评论

StefanKarpinski picture StefanKarpinski  ·  3评论