Types Julia矩阵乘法型行为

Types Julia矩阵乘法型行为,types,julia,Types,Julia,我想问一下我在朱莉娅身上注意到的一些奇怪的行为。考虑下面的片段: function wut() mat::Array{Number,2}=[1 2 3; 5 6 7] mat2::Array{Number,2}=[1 1; 2 3] return typeof(mat2*mat) end wut() 非常自豪的是,它输出了Array{Any,2}。这是怎么回事?为什么输出不是数组{Number,2}?如果用Float64替换Number,那么输出应该是Array{Float6

我想问一下我在朱莉娅身上注意到的一些奇怪的行为。考虑下面的片段:

function wut()
   mat::Array{Number,2}=[1 2 3; 5 6 7]
   mat2::Array{Number,2}=[1 1; 2 3]
   return typeof(mat2*mat)
end
wut()

非常自豪的是,它输出了
Array{Any,2}
。这是怎么回事?为什么输出不是
数组{Number,2}
?如果用
Float64
替换
Number
,那么输出应该是
Array{Float64,2}
,但是为什么朱莉娅认为由抽象“数字”组成的两个矩阵的乘法应该是由“太阳底下的任何东西”组成的矩阵呢,编译器必须能够证明任何数字子类型乘以任何其他数字子类型仍然是数字的子类型。虽然这可能是真的,但可能有数千个数字子类型(复杂和理性都会破坏类型的数量),因此自动证明这一点基本上是不可能的。

首先,声明一个包含数字类型元素的矩阵通常没有多大意义。您希望将元素指定为某种具体的数字类型,例如在您的示例中的
Int64
,因为这样可以保证类型的稳定性,并大大加快代码的速度。数组中的某些值需要是
Float64
而另一些值需要是
Int8
的情况很少见,而且大多数情况下您不希望出现这种情况。Julia本身从不使用自己的抽象类型,它们只用于方法定义。也就是说,您为数字类型定义了一个函数,为整数类型定义了一个函数,但您只能使用
Float64
Int64
(或任何其他具体类型)调用它们。 如果您想了解更多关于类型层次结构的信息,可以阅读

无论如何,你要做的就是将一个矩阵乘以一个数字类型的元素

A = Array{Number}(undef,1,1)

A[1] = 1

B = Array{Number}(undef,1,1)

B[1] = 1

typeof(A) 
# Array{Number,2}

typeof(A) <: AbstractMatrix
# true
因此,我们可以缩小到
基础。升级(matprod,Number,Number)

然后,该函数使用
Core.Compiler.return\u type
函数,该函数基本上是一个ccall,它试图推断出要提升结果的类型。使用具体类型很容易做到这一点,因为您可以使用
Base.promotion_op(+,Float64,Int8)
尝试使用
Float64
。问题从抽象类型开始,在抽象类型中,它并不总是有多大意义。例如,这有点误导:

julia> Base.promote_op(+,  Complex, Real)
Complex

julia> Base.promote_op(+,  Complex, Complex)
Complex

julia> Base.promote_op(+,  Complex, Number)
Any

julia> Base.promote_op(+,  Real, Real)
Any

所以,你看,在数学中实例化抽象类型并不是你想做的事情。

这里有一个替代答案:这是一个怪癖和实现细节,理想情况下不应该存在。它不是Julia的类型设计或分派的结果,也不是一个真正伟大的设计模式。这只是现状

矩阵乘法基于就地API。也就是说,
A*B
变成
mul!(输出阵列A、B)
。因此,在实际知道会发生什么之前,我们需要预先分配结果。此输出元素类型的计算是通过一个古怪且未正确指定的函数完成的:
promote\u op
。它确实应该被删除,但需要一个巨大而困难的重构。。。因此我们有这样奇怪的案例

有关更多详细信息,请参阅
Base.promotion\u op
的文档字符串(请注意,该文档未报告,甚至未出现在联机手册中):

help?>Base.promote\u op
升级操作(f,argtypes…)
猜猜什么是合适的容器eltype,用于存储
f(::argtypes…)。猜测部分基于类型推断,因此可以
随时更换。
│ 警告
│
│  由于其脆弱性,应避免使用promote_op。它是
│  最好将容器eltype基于实际
│  元素。仅在没有任何元素的情况下(对于空结果
│  容器),调用promote_op可能是不可避免的。

有关
promote\u op
的更多内部详细信息,请参阅问题和。

您可能不想对
数组{Number}
…@MattB做太多工作。这也许是个好建议,但它并没有回答“为什么会发生这种特别的强迫?”。也许讨论类继承权和/或分派方法会有用我很感激@MattB,但重点仍然是。谢谢,我想我可以接受。不过感觉还是有点不稳定。那可能是因为它有点不稳定。这是在实践中不太重要的事情之一,可能需要大量的工作来解决。我不同意这一点。我把这称为一个bug和通用matmul实现的一个怪癖。我们不应该使用推断来确定元素类型。在理想情况下,它应该根据实际结果使用增量加宽。例如,与
*
的行为进行比较-它返回一个
数组{Int}
(一旦您使大小与广播兼容)。我同意@MattB。在这里不管是好是坏,MATLAB(例如)做出决定并强制输入到一个公共类。这可能会迫使用户在执行'A*B'之前预先强制输入
A
B
,但这比进入“该死的如果我知道”类作业要好。Matlab是否允许对异构单元数组进行矩阵乘法?您跳过了
升级\u op
文档中最突出的部分:“警告:由于它的脆弱性,应该避免使用
promote\u op
。”你说得对,我当时很忙,需要做些别的事情,稍后再补充
help?> Base.promote_op
  promote_op(f, argtypes...)

  Guess what an appropriate container eltype would be for storing results of    
  f(::argtypes...). The guess is in part based on type inference, so can
  change any time.
│ Warning
│
│  Due to its fragility, use of promote_op should be avoided. 
|  It is preferable to base the container eltype on the type of the actual elements. 
|  Only in the absence of any elements (for an empty result container), it may be
│  unavoidable to call promote_op.

julia> Base.promote_op(+,  Complex, Real)
Complex

julia> Base.promote_op(+,  Complex, Complex)
Complex

julia> Base.promote_op(+,  Complex, Number)
Any

julia> Base.promote_op(+,  Real, Real)
Any