Skip to content

Commit d5d79da

Browse files
efaulhaberLasNikas
andauthored
Add proper support for inactive points (#107)
* Remove `requires_resizing` again * Add proper support for inactive points * Fix * Improve error message for domain check on CPUs * Improve error message * Fix `update!` on GPUs * Fix tests * Update docs * Fix GPU update * Fix merge * Fix tests * Remove duplicate function and unused type annotation * Add tests --------- Co-authored-by: Niklas Neher <[email protected]>
1 parent a65dc9a commit d5d79da

File tree

5 files changed

+98
-29
lines changed

5 files changed

+98
-29
lines changed

src/neighborhood_search.jl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ end
1414

1515
"""
1616
initialize!(search::AbstractNeighborhoodSearch, x, y;
17-
parallelization_backend = default_backend(x))
17+
parallelization_backend = default_backend(x),
18+
eachindex_y = axes(y, 2))
1819
1920
Initialize a neighborhood search with the two coordinate arrays `x` and `y`.
2021
@@ -27,16 +28,21 @@ If the neighborhood search type supports parallelization, the keyword argument
2728
`parallelization_backend` can be used to specify a parallelization backend.
2829
See [`@threaded`](@ref) for a list of available backends.
2930
31+
Optionally, when points in `y` are to be ignored, the keyword argument `eachindex_y` can be
32+
passed to specify the indices of the points in `y` that are to be used.
33+
3034
See also [`update!`](@ref).
3135
"""
3236
@inline function initialize!(search::AbstractNeighborhoodSearch, x, y;
33-
parallelization_backend = default_backend(x))
37+
parallelization_backend = default_backend(x),
38+
eachindex_y = axes(y, 2))
3439
return search
3540
end
3641

3742
"""
3843
update!(search::AbstractNeighborhoodSearch, x, y; points_moving = (true, true),
39-
parallelization_backend = default_backend(x))
44+
parallelization_backend = default_backend(x),
45+
eachindex_y = axes(y, 2))
4046
4147
Update an already initialized neighborhood search with the two coordinate arrays `x` and `y`.
4248
@@ -59,11 +65,15 @@ If the neighborhood search type supports parallelization, the keyword argument
5965
`parallelization_backend` can be used to specify a parallelization backend.
6066
See [`@threaded`](@ref) for a list of available backends.
6167
68+
Optionally, when points in `y` are to be ignored, the keyword argument `eachindex_y` can be
69+
passed to specify the indices of the points in `y` that are to be used.
70+
6271
See also [`initialize!`](@ref).
6372
"""
6473
@inline function update!(search::AbstractNeighborhoodSearch, x, y;
6574
points_moving = (true, true),
66-
parallelization_backend = default_backend(x))
75+
parallelization_backend = default_backend(x),
76+
eachindex_y = axes(y, 2))
6777
return search
6878
end
6979

src/nhs_grid.jl

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,14 @@ end
194194

195195
function initialize!(neighborhood_search::GridNeighborhoodSearch,
196196
x::AbstractMatrix, y::AbstractMatrix;
197-
parallelization_backend = default_backend(x))
198-
initialize_grid!(neighborhood_search, y; parallelization_backend)
197+
parallelization_backend = default_backend(x),
198+
eachindex_y = axes(y, 2))
199+
initialize_grid!(neighborhood_search, y; parallelization_backend, eachindex_y)
199200
end
200201

201202
function initialize_grid!(neighborhood_search::GridNeighborhoodSearch, y::AbstractMatrix;
202-
parallelization_backend = default_backend(y))
203+
parallelization_backend = default_backend(y),
204+
eachindex_y = axes(y, 2))
203205
(; cell_list) = neighborhood_search
204206

205207
empty!(cell_list)
@@ -210,8 +212,10 @@ function initialize_grid!(neighborhood_search::GridNeighborhoodSearch, y::Abstra
210212
return neighborhood_search
211213
end
212214

215+
@boundscheck checkbounds(y, eachindex_y)
216+
213217
# Ignore the parallelization backend here. This cannot be parallelized.
214-
for point in axes(y, 2)
218+
for point in eachindex_y
215219
# Get cell index of the point's cell
216220
point_coords = @inbounds extract_svector(y, Val(ndims(neighborhood_search)), point)
217221
cell = cell_coords(point_coords, neighborhood_search)
@@ -225,7 +229,8 @@ end
225229

226230
function initialize_grid!(neighborhood_search::GridNeighborhoodSearch{<:Any,
227231
ParallelUpdate},
228-
y::AbstractMatrix; parallelization_backend = default_backend(y))
232+
y::AbstractMatrix; parallelization_backend = default_backend(y),
233+
eachindex_y = axes(y, 2))
229234
(; cell_list) = neighborhood_search
230235

231236
empty!(cell_list)
@@ -236,7 +241,9 @@ function initialize_grid!(neighborhood_search::GridNeighborhoodSearch{<:Any,
236241
return neighborhood_search
237242
end
238243

239-
@threaded parallelization_backend for point in axes(y, 2)
244+
@boundscheck checkbounds(y, eachindex_y)
245+
246+
@threaded parallelization_backend for point in eachindex_y
240247
# Get cell index of the point's cell
241248
point_coords = @inbounds extract_svector(y, Val(ndims(neighborhood_search)), point)
242249
cell = cell_coords(point_coords, neighborhood_search)
@@ -250,19 +257,30 @@ end
250257

251258
function update!(neighborhood_search::GridNeighborhoodSearch,
252259
x::AbstractMatrix, y::AbstractMatrix;
253-
points_moving = (true, true), parallelization_backend = default_backend(x))
260+
points_moving = (true, true), parallelization_backend = default_backend(x),
261+
eachindex_y = axes(y, 2))
254262
# The coordinates of the first set of points are irrelevant for this NHS.
255263
# Only update when the second set is moving.
256264
points_moving[2] || return neighborhood_search
257265

258-
update_grid!(neighborhood_search, y; parallelization_backend)
266+
update_grid!(neighborhood_search, y; eachindex_y, parallelization_backend)
259267
end
260268

261269
# Update only with neighbor coordinates
262-
function update_grid!(neighborhood_search::GridNeighborhoodSearch, y::AbstractMatrix;
263-
parallelization_backend = default_backend(y))
270+
function update_grid!(neighborhood_search::Union{GridNeighborhoodSearch{<:Any,
271+
SerialIncrementalUpdate},
272+
GridNeighborhoodSearch{<:Any,
273+
SemiParallelUpdate}},
274+
y::AbstractMatrix;
275+
parallelization_backend = default_backend(y),
276+
eachindex_y = axes(y, 2))
264277
(; cell_list, update_buffer) = neighborhood_search
265278

279+
if eachindex_y != axes(y, 2)
280+
# Incremental update doesn't support inactive points
281+
error("this neighborhood search/update strategy does not support inactive points")
282+
end
283+
266284
# Empty each thread's list
267285
@threaded parallelization_backend for i in eachindex(update_buffer)
268286
emptyat!(update_buffer, i)
@@ -362,9 +380,15 @@ end
362380
# Fully parallel incremental update with atomic push.
363381
function update_grid!(neighborhood_search::GridNeighborhoodSearch{<:Any,
364382
ParallelIncrementalUpdate},
365-
y::AbstractMatrix; parallelization_backend = default_backend(y))
383+
y::AbstractMatrix; parallelization_backend = default_backend(y),
384+
eachindex_y = axes(y, 2))
366385
(; cell_list, update_buffer) = neighborhood_search
367386

387+
if eachindex_y != axes(y, 2)
388+
# Incremental update doesn't support inactive points
389+
error("this neighborhood search/update strategy does not support inactive points")
390+
end
391+
368392
# Note that we need two separate loops for adding and removing points.
369393
# `push_cell_atomic!` only guarantees thread-safety when different threads push
370394
# simultaneously, but it does not work when `deleteat_cell!` is called at the same time.
@@ -420,8 +444,9 @@ function update_grid!(neighborhood_search::Union{GridNeighborhoodSearch{<:Any,
420444
ParallelUpdate},
421445
GridNeighborhoodSearch{<:Any,
422446
SerialUpdate}},
423-
y::AbstractMatrix; parallelization_backend = default_backend(y))
424-
initialize_grid!(neighborhood_search, y; parallelization_backend)
447+
y::AbstractMatrix; parallelization_backend = default_backend(y),
448+
eachindex_y = axes(y, 2))
449+
initialize_grid!(neighborhood_search, y; parallelization_backend, eachindex_y)
425450
end
426451

427452
# Specialized version of the function in `neighborhood_search.jl`, which is faster

src/nhs_precomputed.jl

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,15 @@ end
5353

5454
function initialize!(search::PrecomputedNeighborhoodSearch,
5555
x::AbstractMatrix, y::AbstractMatrix;
56-
parallelization_backend = default_backend(x))
56+
parallelization_backend = default_backend(x),
57+
eachindex_y = axes(y, 2))
5758
(; neighborhood_search, neighbor_lists) = search
5859

5960
# Initialize grid NHS
60-
initialize!(neighborhood_search, x, y; parallelization_backend)
61+
initialize!(neighborhood_search, x, y; eachindex_y, parallelization_backend)
6162

6263
initialize_neighbor_lists!(neighbor_lists, neighborhood_search, x, y,
63-
parallelization_backend)
64+
parallelization_backend, eachindex_y)
6465
end
6566

6667
# WARNING! Experimental feature:
@@ -70,21 +71,22 @@ end
7071
# `parallelization_backend = KernelAbstractions.CPU()`, even though `x isa Array`.
7172
function update!(search::PrecomputedNeighborhoodSearch,
7273
x::AbstractMatrix, y::AbstractMatrix;
73-
points_moving = (true, true), parallelization_backend = default_backend(x))
74+
points_moving = (true, true), parallelization_backend = default_backend(x),
75+
eachindex_y = axes(y, 2))
7476
(; neighborhood_search, neighbor_lists) = search
7577

7678
# Update grid NHS
77-
update!(neighborhood_search, x, y; points_moving, parallelization_backend)
79+
update!(neighborhood_search, x, y; eachindex_y, points_moving, parallelization_backend)
7880

7981
# Skip update if both point sets are static
8082
if any(points_moving)
8183
initialize_neighbor_lists!(neighbor_lists, neighborhood_search, x, y,
82-
parallelization_backend)
84+
parallelization_backend, eachindex_y)
8385
end
8486
end
8587

8688
function initialize_neighbor_lists!(neighbor_lists, neighborhood_search, x, y,
87-
parallelization_backend)
89+
parallelization_backend, eachindex_y)
8890
# Initialize neighbor lists
8991
empty!(neighbor_lists)
9092
resize!(neighbor_lists, size(x, 2))
@@ -93,8 +95,8 @@ function initialize_neighbor_lists!(neighbor_lists, neighborhood_search, x, y,
9395
end
9496

9597
# Fill neighbor lists
96-
foreach_point_neighbor(x, y, neighborhood_search;
97-
parallelization_backend) do point, neighbor, _, _
98+
foreach_point_neighbor(x, y, neighborhood_search; parallelization_backend,
99+
points = eachindex_y) do point, neighbor, _, _
98100
push!(neighbor_lists[point], neighbor)
99101
end
100102
end

src/nhs_trivial.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ end
3333
@inline requires_update(::TrivialNeighborhoodSearch) = (false, false)
3434

3535
@inline function initialize!(search::TrivialNeighborhoodSearch, x, y;
36-
parallelization_backend = default_backend(x))
36+
parallelization_backend = default_backend(x),
37+
eachindex_y = axes(y, 2))
3738
return search
3839
end
3940

4041
@inline function update!(search::TrivialNeighborhoodSearch, x, y;
4142
points_moving = (true, true),
42-
parallelization_backend = default_backend(x))
43+
parallelization_backend = default_backend(x),
44+
eachindex_y = axes(y, 2))
4345
return search
4446
end
4547

test/nhs_grid.jl

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@
145145
@test neighbors5 == [36, 37, 38, 43, 44, 45]
146146
end
147147

148-
@testset "Rectangular Point Cloud 3D" begin
148+
@testset verbose=true "Rectangular Point Cloud 3D" begin
149149
#### Setup
150150
# Rectangle of equidistantly spaced points
151151
# from (x, y, z) = (-0.25, -0.25, -0.25) to (x, y, z) = (0.35, 0.35, 0.35).
@@ -188,6 +188,36 @@
188188
@test neighbors3 ==
189189
[115, 116, 117, 122, 123, 124, 129, 130, 131, 164, 165, 166, 171, 172,
190190
173, 178, 179, 180, 213, 214, 215, 220, 221, 222, 227, 228, 229]
191+
192+
update_strategies = (SerialUpdate(), ParallelUpdate())
193+
@testset verbose=true "eachindex_y $update_strategy" for update_strategy in
194+
update_strategies
195+
# Test that `eachindex_y` is passed correctly to the neighborhood search.
196+
# This requires `SerialUpdate` or `ParallelUpdate`.
197+
min_corner = min.(minimum(coordinates1, dims = 2),
198+
minimum(coordinates2, dims = 2))
199+
max_corner = max.(maximum(coordinates1, dims = 2),
200+
maximum(coordinates2, dims = 2))
201+
cell_list = FullGridCellList(; min_corner, max_corner, search_radius)
202+
nhs2 = GridNeighborhoodSearch{3}(; search_radius, n_points, update_strategy,
203+
cell_list)
204+
205+
# Initialize with all points
206+
initialize!(nhs2, coordinates1, coordinates1)
207+
208+
# Update with a subset of points
209+
update!(nhs2, coordinates2, coordinates2; eachindex_y = 120:220)
210+
211+
neighbors2 = sort(collect(PointNeighbors.eachneighbor(point_position1, nhs2)))
212+
neighbors3 = sort(collect(PointNeighbors.eachneighbor(point_position2, nhs2)))
213+
214+
# Check that the neighbors are the intersection of the previous neighbors
215+
# with the `eachindex_y` range.
216+
@test neighbors2 == Int[]
217+
@test neighbors3 ==
218+
[122, 123, 124, 129, 130, 131, 164, 165, 166, 171, 172, 173,
219+
178, 179, 180, 213, 214, 215, 220]
220+
end
191221
end
192222

193223
@testset verbose=true "Periodicity" begin

0 commit comments

Comments
 (0)