什么是Julia在所需尺寸上点缀产品的方法

时间:2018-02-27 18:31:06

标签: multidimensional-array julia

我有一个带有两个向量字段uv的xy网格。我将矢量字段表示为Array{Float64, 3},其尺寸为nx×ny×2。 我想将点积u.v作为标量字段(Array{Float64,2},尺寸为nx×ny)。实现这一目标的最佳方法是什么?

dot(u,v,3)这样的东西是完美的,其中3是点积的维度。

nx, ny = 3, 4

u = Array{Float64,3}(rand(0:1, nx, ny, 2))
#[0.0 1.0 0.0 1.0; 1.0 0.0 1.0 0.0; 1.0 0.0 1.0 0.0]
#[1.0 0.0 1.0 0.0; 1.0 0.0 0.0 1.0; 1.0 0.0 1.0 1.0]

v = Array{Float64,3}(rand(0:1, nx, ny, 2))
#[1.0 1.0 1.0 1.0; 0.0 1.0 0.0 0.0; 0.0 1.0 1.0 0.0]
#[1.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0; 1.0 1.0 1.0 0.0]

[dot(u[i,j,:], v[i,j,:]) for i in 1:nx, j in 1:ny]

3×4 Array{Float64,2}:
 1.0  1.0  0.0  1.0
 0.0  0.0  0.0  0.0
 1.0  0.0  2.0  0.0

3 个答案:

答案 0 :(得分:5)

sum(u.*v,3)(合理)快速和短暂。

要明确获得矩阵,您可以像第squeeze(sum(u.*v,3), 3)

那样挤压第三维

更新:当然,这有分配,如果速度就是一切,则不是最佳答案。在这种情况下,请参阅@ DNF的直接循环实现,它基本上与您可以获得的速度一样快。

答案 1 :(得分:4)

squeeze(sum(u .* v, 3), 3)干净简单,但如果你需要更快的速度,这在我的电脑上快了大约20倍:

a = fill(zero(eltype(u)), nx, ny)
@inbounds for k in indices(u, 3)
    for j in indices(u, 2)
        for i in indices(u, 1)
            a[i, j] += u[i, j, k] * v[i, j, k]
        end
    end
end

修改:为了更轻松地进行基准比较,请尝试以下代码:

function dotsum3(u, v)
    a = fill(zero(eltype(u)), size(u, 1), size(u, 2))
    @inbounds for k in indices(u, 3)
        for j in indices(u, 2)
            for i in indices(u, 1)
                a[i, j] += u[i, j, k] * v[i, j, k]
            end
        end
    end
    return a
end

另请注意,如果使用@inbounds,则应明确检查uv的尺寸是否兼容。

答案 2 :(得分:1)

另一个版本是

a = copy(view(u,:,:,1))
a .*= view(v,:,:,1)
a .+= @views u[:,:,2].*v[:,:,2]

可以轻松扩展到任意第三维长度。我机器的基准测试:

julia> using BenchmarkTools

julia> function f1(u,v)
       a = fill(zero(eltype(u)), nx, ny)
       @inbounds for k in indices(u, 3)
           for j in indices(u, 2)
               for i in indices(u, 1)
                   a[i, j] += u[i, j, k] * v[i, j, k]
               end
           end
       end
       return a
       end
f1 (generic function with 1 method)

julia> f2(u,v) = squeeze(sum(u .* v, 3), 3)
f2 (generic function with 1 method)

julia> function f3(u,v)
       a = copy(view(u,:,:,1))
       a .*= view(v,:,:,1)
       a .+= @views u[:,:,2].*v[:,:,2]
       return a
       end
f3 (generic function with 1 method)

julia> nx, ny = 3, 4
(3, 4)

julia> u = Array{Float64,3}(rand(0:1, nx, ny, 2));

julia> v = Array{Float64,3}(rand(0:1, nx, ny, 2));

计时结果:

julia> @btime f1($u,$v);   # DNF suggestion
  1.016 μs (1 allocation: 176 bytes)

julia> @btime f2($u,$v);   # original answer
  1.263 μs (14 allocations: 816 bytes)

julia> @btime f3($u,$v);   # this answer
  168.591 ns (5 allocations: 432 bytes)

建议的版本更快(考虑到Julia Arrays的内存排序,这是合乎逻辑的)。