I want to solve the fixed radius near neighbor problem, i.e., for a given point set in 2D, find all the point pairs whose length is less than a given radius r.
I use the cell list algorithm, see Cell List algorithm and Cell list algorithm for a reference.
However, my program runs much slower than a double for loop.
I wanna know why and how to modify my program to run faster. Following are my codes.
# fied-radius nearest neighbor
#cell list algorithm
function FRNNMain(NumNod::Int64,Dim::Int64,A::Array{Float64,2},s::Float64)
PoiPair = Array{Tuple{Int64,Int64},1}(undef,1)
popfirst!(PoiPair)
r = s/sqrt(Dim)
Cell = PoiCell(A,NumNod,r)
for key in keys(Cell)
vaule = Cell[key]
InnerCellPair!(vaule,PoiPair)
LoopNeighbor!(key,Cell,PoiPair,A,s)
end
return PoiPair
end
function PoiCell(A::Matrix{Float64},NumNod::Int64,r::Float64)
Cell = Dict{Tuple{Int64,Int64},Array{Int64,1}}()
for i = 1:1:NumNod
x = fld(A[1,i],r)
y = fld(A[2,i],r)
z = (x,y)
if z ∉ keys(Cell)
Cell[z] = [i]
else
push!(Cell[z],i)
end
end
return Cell
end
function GetNeighborCell(key::Tuple{Int64,Int64})
NeiCell = Array{Tuple{Int64,Int64}}(undef,20,1)
NeiCell[1] = (key[1]+1,key[2]+1)
NeiCell[2] = (key[1]+1,key[2])
NeiCell[3] = (key[1]+1,key[2]-1)
NeiCell[4] = (key[1],key[2]+1)
NeiCell[5] = (key[1],key[2]-1)
NeiCell[6] = (key[1]-1,key[2])
NeiCell[7] = (key[1]-1,key[2]+1)
NeiCell[8] = (key[1]-1,key[2]-1)
NeiCell[9] = (key[1]-1,key[2]+2)
NeiCell[10] = (key[1],key[2]+2)
NeiCell[11] = (key[1]+1,key[2]+2)
NeiCell[12] = (key[1]+2,key[2]-1)
NeiCell[13] = (key[1]+2,key[2])
NeiCell[14] = (key[1]+2,key[2]+1)
NeiCell[15] = (key[1]-1,key[2]-2)
NeiCell[16] = (key[1],key[2]-2)
NeiCell[17] = (key[1]+1,key[2]-2)
NeiCell[18] = (key[1]-2,key[2]-1)
NeiCell[19] = (key[1]-2,key[2])
NeiCell[20] = (key[1]-2,key[2]+1)
return NeiCell
end
function InnerCellPair!(value::Array{Int64},pairs::Array{Tuple{Int64,Int64},1})
Len = length(value)
if Len >= 2
for i = 1:Len-1
for j = (i+1):Len
z = (value[i],value[j])
push!(pairs,z)
end
end
end
return pairs
end
function LoopNeighbor!(key::Tuple{Int64,Int64},Cell::Dict{Tuple{Int64,Int64},Array{Int64,1}},PoiPair::Array{Tuple{Int64,Int64},1},A::Array{Float64,2},s::Float64)
NeiCell = Array{Tuple{Int64,Int64}}(undef,20,1)
NeiCell = GetNeighborCell(key)
for i = 1:8
if NeiCell[i] ∈ keys(Cell)
FindPair!(Cell[key],Cell[NeiCell[i]],PoiPair,A,s)
end
end
return PoiPair
end
function FindPair!(value::Array{Int64},value1::Array{Int64},PoiPair::Array{Tuple{Int64,Int64},1},A::Array{Float64,2},s::Float64)
for P1 in value
for P2 in value1
#Dist = Distance(A[P1,:],A[P2,:])
Dist::Float64 = (A[1,P1]-A[1,P2])^2+(A[2,P1]-A[2,P2])^2
if Dist <= s^2
AddPair!(P1,P2,PoiPair)
end
end
end
return PoiPair
end
function Distance(Point1::Array{Float64,1},Point2::Array{Float64,1})
Dist = (Point1[1]-Point2[1])^2+(Point1[2]-Point2[2])^2::Float64
return Dist
end
function AddPair!(P1::Int64,P2::Int64,PoiPair::Array{Tuple{Int64,Int64},1})
pair1 = (P1,P2)
pair2 = (P2,P1)
if pair1 ∉ PoiPair
if pair2 ∉ PoiPair
push!(PoiPair,pair1)
end
end
return PoiPair
end
NumNod = 1000
Dim = 2
A = rand(Dim,NumNod)
s = 0.1;
a = FRNNMain(NumNod,Dim,A,s)
and the running time
@btime FRNNMain(NumNod,Dim,A,s)
182.920 ms (971 allocations: 749.27 KiB)
The doule for loop code
NumNod = 1000
Dim = 2
A = rand(Dim,NumNod)
s = 0.1;
function forloop(A::Matrix{Float64},NumNod::Int64,s::Float64)
PoiPair = Array{Tuple{Int64,Int64},1}(undef,1)
popfirst!(PoiPair)
for i = collect(1:NumNod-1)
for j = collect(i+1:NumNod)
Dist = (A[1,i]-A[1,j])^2+(A[2,i]-A[2,j])^2
if Dist <= s^2
push!(PoiPair,(i,j))
end
end
end
return PoiPair
end
b = forloop(A,NumNod,s)
and the running time
@btime forloop(A,NumNod,s)
1.623 ms (1014 allocations: 4.46 MiB)
The code warntype result is also provided
@code_warntype FRNNMain(NumNod,Dim,A,s)
Variables
#self#::Core.Compiler.Const(FRNNMain, false)
NumNod::Int64
Dim::Int64
A::Array{Float64,2}
s::Float64
PoiPair::Array{Tuple{Int64,Int64},1}
r::Float64
Cell::Dict{Tuple{Int64,Int64},Array{Int64,1}}
@_9::Union{Nothing, Tuple{Tuple{Int64,Int64},Int64}}
key::Tuple{Int64,Int64}
vaule::Array{Int64,1}
Body::Array{Tuple{Int64,Int64},1}
1 ─ %1 = Core.apply_type(Main.Tuple, Main.Int64, Main.Int64)::Core.Compiler.Const(Tuple{Int64,Int64}, false)
│ %2 = Core.apply_type(Main.Array, %1, 1)::Core.Compiler.Const(Array{Tuple{Int64,Int64},1}, false)
│ (PoiPair = (%2)(Main.undef, 1))
│ Main.popfirst!(PoiPair)
│ %5 = Main.sqrt(Dim)::Float64
│ (r = s / %5)
│ (Cell = Main.PoiCell(A, NumNod, r))
│ %8 = Main.keys(Cell)::Base.KeySet{Tuple{Int64,Int64},Dict{Tuple{Int64,Int64},Array{Int64,1}}}
│ (@_9 = Base.iterate(%8))
│ %10 = (@_9 === nothing)::Bool
│ %11 = Base.not_int(%10)::Bool
└── goto #4 if not %11
2 ┄ %13 = @_9::Tuple{Tuple{Int64,Int64},Int64}::Tuple{Tuple{Int64,Int64},Int64}
│ (key = Core.getfield(%13, 1))
│ %15 = Core.getfield(%13, 2)::Int64
│ (vaule = Base.getindex(Cell, key))
│ Main.InnerCellPair!(vaule, PoiPair)
│ Main.LoopNeighbor!(key, Cell, PoiPair, A, s)
│ (@_9 = Base.iterate(%8, %15))
│ %20 = (@_9 === nothing)::Bool
│ %21 = Base.not_int(%20)::Bool
└── goto #4 if not %21
3 ─ goto #2
4 ┄ return PoiPair
Thank you for your kindly help!