如何创建具有函数和任意索引的 SVector/SMatrix?
How do I create an SVector/SMatrix with a function and arbitrary indices?
教授演示了使用 SMatrix 的二维高斯核(见下图)。我正在尝试以类似的方式实现任意大小的一维内核。我尝试了如下代码,但无法以正确的方式在网上找到任何好的资源来初始化它。所有示例都是静态的,在初始化时具有非常静态的元素。
begin
G_1D(i, σ) = ℯ^(-(i^2)/(2*(σ^2)))/sqrt(2*π*(σ^2))
G_2D(x, y, σ) = ℯ^(-((x^2)+(y^2))/(2*(σ^2)))/sqrt(2*π*(σ^2))
end
begin
function gaussian_kernel_1D(n, σ = 1)
if iseven(n)
mid = n÷2
kernel = SVector{n}(map(i -> G_1D(i, σ)), CartesianIndex(-mid:mid-1))
kernel ./ sum(kernel)
return kernel
else
mid = n÷2
kernel = SVector{n}(map(i -> G_1D(i, σ)), CartesianIndex(-mid:mid))
kernel ./ sum(kernel)
return kernel
end
end
function gaussian_kernel_2D(n, σ = 1)
if iseven(n)
mid = n÷2
kernel = SMatrix{n,n}(map(x,y -> G_2D(x,y, σ)), CartesianIndices((-mid:mid-1, -mid:mid-1)))
kernel ./ sum(kernel)
return kernel
else
mid = n÷2
kernel = SMatrix{n,n}(map(x,y -> G_2D(x,y, σ)), CartesianIndices((-mid:mid-1, -mid:mid-1)))
kernel ./ sum(kernel)
return kernel
end
end
end
- 使用 SVector 是正确的解决方案吗?如果是这样,你能帮我找到正确的初始化方法吗?
- 如果 SVector 不是正确的想法,您会推荐什么?
这里是调用高斯核函数的错误详情。
MethodError: no method matching (::Main.workspace68.var"#2#6"{Int64})()
Closest candidates are:
#2(!Matched::Any) at /Users/
map(::Main.workspace68.var"#2#6"{Int64})@abstractarray.jl:2247
gaussian_kernel_1D(::Int64, ::Int64)@Other: 10
gaussian_kernel_1D(::Int64)
我是一个比较新的学习者,所以如果代码不正确请指出,这将非常有帮助。
您的问题不在于 SVector
,而在于您调用 G_1D
的方式,更具体地说,您在此处遇到错误:
julia> CartesianIndex(-mid:mid)
ERROR: MethodError: no method matching CartesianIndex(::UnitRange{Int64})
您可以使用 CartesianIndices
,但我不明白您为什么要这样做。直接使用 -mid:mid
,广播。像这样:
G_1D.(-mid:mid, σ)
那么
kernel = SVector{n}(G_1D.(-mid:mid, σ))
对于 2D,您可以这样做:
kernel = SMatrix{n,n}(G_2D.(-mid:mid, (-mid:mid)', σ))
这里还有另一个问题:
kernel ./ sum(kernel)
return kernel
这不会更改 kernel
的值。你基本上是这样做的:
kernel_nonnormalized = kernel
kernel_normalized = kernel ./ sum(kernel)
return kernel_nonnormalized
如果你只写
return kernel ./ sum(kernel)
然后你得到一个标准化的值。
很难说您是否有理由使用 StaticArrays。维度应该很小,理想情况下它们的大小应该在编译时已知,而在您的代码中它是在运行时确定的。但这可能不是问题,只要这发生在一个孤立的地方,例如在你计算的开始。试试看吧。
编辑: 顺便说一句,如果你不介意我这么说,你就不必要地违反了 DRY(不要重复自己)原则,并且错过了使用多个派遣。这是避免这种情况的一种方法(可能不是最佳方法):
gauss(i, σ) = ℯ^(-(i^2)/(2*(σ^2)))/sqrt(2*π*(σ^2))
gauss(x, y, σ) = ℯ^(-((x^2)+(y^2))/(2*(σ^2)))/sqrt(2*π*(σ^2))
function makekernel(f, n, σ=1)
ind = (0:n-1) .- (n÷2)
kernel = SVector{n}(f.(ind, σ))
return kernel ./ sum(kernel)
end
function makekernel(f, n::NTuple{2}, σ=1)
ind1 = (0:n[1]-1) .- (n[1]÷2)
ind2 = (0:n[2]-1) .- (n[2]÷2)
kernel = SMatrix{n[1], n[2]}(f.(ind1, ind2', σ))
return kernel ./ sum(kernel)
end
您只需要 一个 高斯函数的名称,一维和二维版本不需要单独的名称。 makekernel
在这两种情况下也可以具有相同的名称。无需创建单独的函数名称,名称中包含 1D 和 2D。
另请注意,偶数和奇数长度不需要单独的代码路径。
您可以这样称呼它们:
kernel1D = makekernel(gauss, 7) # here's 1D kernel
kernel2D = makekernel(gauss, (7, 6)) # here's a 7x6 2D kernel
现在也有使用不同于 gauss
的函数的空间,您还会看到将其扩展到更高维度的模式。
教授演示了使用 SMatrix 的二维高斯核(见下图)。我正在尝试以类似的方式实现任意大小的一维内核。我尝试了如下代码,但无法以正确的方式在网上找到任何好的资源来初始化它。所有示例都是静态的,在初始化时具有非常静态的元素。
begin
G_1D(i, σ) = ℯ^(-(i^2)/(2*(σ^2)))/sqrt(2*π*(σ^2))
G_2D(x, y, σ) = ℯ^(-((x^2)+(y^2))/(2*(σ^2)))/sqrt(2*π*(σ^2))
end
begin
function gaussian_kernel_1D(n, σ = 1)
if iseven(n)
mid = n÷2
kernel = SVector{n}(map(i -> G_1D(i, σ)), CartesianIndex(-mid:mid-1))
kernel ./ sum(kernel)
return kernel
else
mid = n÷2
kernel = SVector{n}(map(i -> G_1D(i, σ)), CartesianIndex(-mid:mid))
kernel ./ sum(kernel)
return kernel
end
end
function gaussian_kernel_2D(n, σ = 1)
if iseven(n)
mid = n÷2
kernel = SMatrix{n,n}(map(x,y -> G_2D(x,y, σ)), CartesianIndices((-mid:mid-1, -mid:mid-1)))
kernel ./ sum(kernel)
return kernel
else
mid = n÷2
kernel = SMatrix{n,n}(map(x,y -> G_2D(x,y, σ)), CartesianIndices((-mid:mid-1, -mid:mid-1)))
kernel ./ sum(kernel)
return kernel
end
end
end
- 使用 SVector 是正确的解决方案吗?如果是这样,你能帮我找到正确的初始化方法吗?
- 如果 SVector 不是正确的想法,您会推荐什么?
这里是调用高斯核函数的错误详情。
MethodError: no method matching (::Main.workspace68.var"#2#6"{Int64})()
Closest candidates are:
#2(!Matched::Any) at /Users/
map(::Main.workspace68.var"#2#6"{Int64})@abstractarray.jl:2247
gaussian_kernel_1D(::Int64, ::Int64)@Other: 10
gaussian_kernel_1D(::Int64)
我是一个比较新的学习者,所以如果代码不正确请指出,这将非常有帮助。
您的问题不在于 SVector
,而在于您调用 G_1D
的方式,更具体地说,您在此处遇到错误:
julia> CartesianIndex(-mid:mid)
ERROR: MethodError: no method matching CartesianIndex(::UnitRange{Int64})
您可以使用 CartesianIndices
,但我不明白您为什么要这样做。直接使用 -mid:mid
,广播。像这样:
G_1D.(-mid:mid, σ)
那么
kernel = SVector{n}(G_1D.(-mid:mid, σ))
对于 2D,您可以这样做:
kernel = SMatrix{n,n}(G_2D.(-mid:mid, (-mid:mid)', σ))
这里还有另一个问题:
kernel ./ sum(kernel)
return kernel
这不会更改 kernel
的值。你基本上是这样做的:
kernel_nonnormalized = kernel
kernel_normalized = kernel ./ sum(kernel)
return kernel_nonnormalized
如果你只写
return kernel ./ sum(kernel)
然后你得到一个标准化的值。
很难说您是否有理由使用 StaticArrays。维度应该很小,理想情况下它们的大小应该在编译时已知,而在您的代码中它是在运行时确定的。但这可能不是问题,只要这发生在一个孤立的地方,例如在你计算的开始。试试看吧。
编辑: 顺便说一句,如果你不介意我这么说,你就不必要地违反了 DRY(不要重复自己)原则,并且错过了使用多个派遣。这是避免这种情况的一种方法(可能不是最佳方法):
gauss(i, σ) = ℯ^(-(i^2)/(2*(σ^2)))/sqrt(2*π*(σ^2))
gauss(x, y, σ) = ℯ^(-((x^2)+(y^2))/(2*(σ^2)))/sqrt(2*π*(σ^2))
function makekernel(f, n, σ=1)
ind = (0:n-1) .- (n÷2)
kernel = SVector{n}(f.(ind, σ))
return kernel ./ sum(kernel)
end
function makekernel(f, n::NTuple{2}, σ=1)
ind1 = (0:n[1]-1) .- (n[1]÷2)
ind2 = (0:n[2]-1) .- (n[2]÷2)
kernel = SMatrix{n[1], n[2]}(f.(ind1, ind2', σ))
return kernel ./ sum(kernel)
end
您只需要 一个 高斯函数的名称,一维和二维版本不需要单独的名称。 makekernel
在这两种情况下也可以具有相同的名称。无需创建单独的函数名称,名称中包含 1D 和 2D。
另请注意,偶数和奇数长度不需要单独的代码路径。
您可以这样称呼它们:
kernel1D = makekernel(gauss, 7) # here's 1D kernel
kernel2D = makekernel(gauss, (7, 6)) # here's a 7x6 2D kernel
现在也有使用不同于 gauss
的函数的空间,您还会看到将其扩展到更高维度的模式。