FV3 Bundle
tools_kdtree2_pq.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: tools_kdtree2_pq
3 ! Purpose: K-d tree priority queue routines
4 ! Source: https://github.com/jmhodges/kdtree2
5 ! Author: Matthew Kennel, Institute for Nonlinear Science (2004)
6 ! Original licensing: Academic Free License version 1.1
7 ! Modified by Benjamin Menetrier for BUMP
8 ! Licensing: this code is distributed under the CeCILL-C license
9 ! Copyright © 2015-... UCAR, CERFACS, METEO-FRANCE and IRIT
10 !----------------------------------------------------------------------
12  use tools_kinds, only: kind_real
13  use tools_repro, only: inf,sup,supeq
14  !
15  ! maintain a priority queue (PQ) of data, pairs of 'priority/payload',
16  ! implemented with a binary heap. This is the type, and the 'dis' field
17  ! is the priority.
18  !
20  ! a pair of distances, indexes
21  real(kind_real) :: dis!=0.0
22  real(kind_real) :: sdis!=0.0
23  integer :: idx!=-1 Initializers cause some bugs in compilers.
24  end type kdtree2_result
25  !
26  ! A heap-based priority queue lets one efficiently implement the following
27  ! operations, each in log(N) time, as opposed to linear time.
28  !
29  ! 1) add a datum (push a datum onto the queue, increasing its length)
30  ! 2) return the priority value of the maximum priority element
31  ! 3) pop-off (and delete) the element with the maximum priority, decreasing
32  ! the size of the queue.
33  ! 4) replace the datum with the maximum priority with a supplied datum
34  ! (of either higher or lower priority), maintaining the size of the
35  ! queue.
36  !
37  !
38  ! In the k-d tree case, the 'priority' is the square distance of a point in
39  ! the data set to a reference point. The goal is to keep the smallest M
40  ! distances to a reference point. The tree algorithm searches terminal
41  ! nodes to decide whether to add points under consideration.
42  !
43  ! A priority queue is useful here because it lets one quickly return the
44  ! largest distance currently existing in the list. If a new candidate
45  ! distance is smaller than this, then the new candidate ought to replace
46  ! the old candidate. In priority queue terms, this means removing the
47  ! highest priority element, and inserting the new one.
48  !
49  ! Algorithms based on Cormen, Leiserson, Rivest, _Introduction
50  ! to Algorithms_, 1990, with further optimization by the author.
51  !
52  ! Originally informed by a C implementation by Sriranga Veeraraghavan.
53  !
54  ! This module is not written in the most clear way, but is implemented such
55  ! for speed, as it its operations will be called many times during searches
56  ! of large numbers of neighbors.
57  !
58  type pq
59  !
60  ! The priority queue consists of elements
61  ! priority(1:heap_size), with associated payload(:).
62  !
63  ! There are heap_size active elements.
64  ! Assumes the allocation is always sufficient. Will NOT increase it
65  ! to match.
66  integer :: heap_size = 0
67  type(kdtree2_result), pointer :: elems(:)
68  end type pq
69 
70  public :: kdtree2_result
71 
72  public :: pq
73  public :: pq_create
74  public :: pq_delete, pq_insert
76  private
77 
78 contains
79 
80 
81  function pq_create(results_in) result(res)
82 ! Function: pq_create
83 ! Purpose: create a priority queue from ALREADY allocated array pointers for storage
84 
85  ! NOTE! It will NOT add any alements to the heap, i.e. any existing
86  ! data in the input arrays will NOT be used and may be overwritten.
87  !
88  ! usage:
89  ! real(kind_real), pointer :: x(:)
90  ! integer, pointer :: k(:)
91  ! allocate(x(1000),k(1000))
92  ! pq => pq_create(x,k)
93  !
94  type(kdtree2_result), target:: results_in(:)
95  type(pq) :: res
96  !
97  !
98  integer :: nalloc
99 
100  nalloc = size(results_in,1)
101  if (nalloc .lt. 1) then
102  write (*,*) 'PQ_CREATE: error, input arrays must be allocated.'
103  end if
104  res%elems => results_in
105  res%heap_size = 0
106  return
107  end function pq_create
108 
109  subroutine heapify(a,i_in)
110 ! Subroutine: heapify
111 ! Purpose: take a heap rooted at 'i' and force it to be in the heap canonical form
112 
113  ! This is performance critical and has been tweaked a little to reflect this.
114  !
115  type(pq),pointer :: a
116  integer, intent(in) :: i_in
117  !
118  integer :: i, l, r, largest
119 
120  real(kind_real) :: pri_i, pri_l, pri_r, pri_largest
121 
122 
123  type(kdtree2_result) :: temp
124 
125  i = i_in
126 
127 bigloop: do
128  l = 2*i ! left(i)
129  r = l+1 ! right(i)
130  !
131  ! set 'largest' to the index of either i, l, r
132  ! depending on whose priority is largest.
133  !
134  ! note that l or r can be larger than the heap size
135  ! in which case they do not count.
136 
137 
138  ! does left child have higher priority?
139  if (l .gt. a%heap_size) then
140  ! we know that i is the largest as both l and r are invalid.
141  exit
142  else
143  pri_i = a%elems(i)%dis
144  pri_l = a%elems(l)%dis
145  if (sup(pri_l,pri_i)) then
146  largest = l
147  pri_largest = pri_l
148  else
149  largest = i
150  pri_largest = pri_i
151  endif
152 
153  !
154  ! between i and l we have a winner
155  ! now choose between that and r.
156  !
157  if (r .le. a%heap_size) then
158  pri_r = a%elems(r)%dis
159  if (sup(pri_r,pri_largest)) then
160  largest = r
161  endif
162  endif
163  endif
164 
165  if (largest .ne. i) then
166  ! swap data in nodes largest and i, then heapify
167 
168  temp = a%elems(i)
169  a%elems(i) = a%elems(largest)
170  a%elems(largest) = temp
171  !
172  ! Canonical heapify() algorithm has tail-ecursive call:
173  !
174  ! call heapify(a,largest)
175  ! we will simulate with cycle
176  !
177  i = largest
178  cycle bigloop ! continue the loop
179  else
180  return ! break from the loop
181  end if
182  enddo bigloop
183  return
184  end subroutine heapify
185 
186  subroutine pq_max(a,e)
187 ! Subroutine: pq_max
188 ! Purpose: return the priority and its payload of the maximum priority element on the queue, which should be the first one, if it is in heapified form
189 
190  type(pq),pointer :: a
191  type(kdtree2_result),intent(out) :: e
192 
193  if (a%heap_size .gt. 0) then
194  e = a%elems(1)
195  else
196  write (*,*) 'PQ_MAX: ERROR, heap_size < 1'
197  stop
198  endif
199  return
200  end subroutine pq_max
201 
202  real(kind_real) function pq_maxpri(a)
203 ! Function: pq_maxpri
204 ! Purpose: unknown
205 
206  type(pq), pointer :: a
207 
208  if (a%heap_size .gt. 0) then
209  pq_maxpri = a%elems(1)%dis
210  else
211  write (*,*) 'PQ_MAX_PRI: ERROR, heapsize < 1'
212  stop
213  endif
214  return
215  end function pq_maxpri
216 
217  subroutine pq_extract_max(a,e)
218 ! Subroutine: pq_extract_max
219 ! Purpose: return the priority and payload of maximum priority element, and remove it from the queue
220 
221  ! (equivalent to 'pop()' on a stack)
222  !
223  type(pq),pointer :: a
224  type(kdtree2_result), intent(out) :: e
225 
226  if (a%heap_size .ge. 1) then
227  !
228  ! return max as first element
229  !
230  e = a%elems(1)
231 
232  !
233  ! move last element to first
234  !
235  a%elems(1) = a%elems(a%heap_size)
236  a%heap_size = a%heap_size-1
237  call heapify(a,1)
238  return
239  else
240  write (*,*) 'PQ_EXTRACT_MAX: error, attempted to pop non-positive PQ'
241  stop
242  end if
243 
244  end subroutine pq_extract_max
245 
246 
247  real(kind_real) function pq_insert(a,dis,sdis,idx)
248 ! Function: pq_insert
249 ! Purpose: insert a new element and return the new maximum priority, which may or may not be the same as the old maximum priority
250 
251  type(pq),pointer :: a
252  real(kind_real), intent(in) :: dis
253  real(kind_real), intent(in) :: sdis
254  integer, intent(in) :: idx
255  ! type(kdtree2_result), intent(in) :: e
256  !
257  integer :: i, isparent
258  real(kind_real) :: parentdis,parentsdis
259  !
260 
261  ! if (a%heap_size .ge. a%max_elems) then
262  ! write (*,*) 'PQ_INSERT: error, attempt made to insert element on full PQ'
263  ! stop
264  ! else
265  a%heap_size = a%heap_size + 1
266  i = a%heap_size
267 
268  do while (i .gt. 1)
269  isparent = int(i/2)
270  parentdis = a%elems(isparent)%dis
271  parentsdis = a%elems(isparent)%sdis
272  if (sup(dis,parentdis)) then
273  ! move what was in i's parent into i.
274  a%elems(i)%dis = parentdis
275  a%elems(i)%sdis = parentsdis
276  a%elems(i)%idx = a%elems(isparent)%idx
277  i = isparent
278  else
279  exit
280  endif
281  end do
282 
283  ! insert the element at the determined position
284  a%elems(i)%dis = dis
285  a%elems(i)%sdis = sdis
286  a%elems(i)%idx = idx
287 
288  pq_insert = a%elems(1)%dis
289  return
290  ! end if
291 
292  end function pq_insert
293 
294  real(kind_real) function pq_replace_max(a,dis,sdis,idx)
295 ! Function: pq_replace_max
296 ! Purpose: replace the extant maximum priority element in the PQ with (dis,sdis,idx)
297 
298  ! Return the new maximum priority, which may be larger or smaller than the old one.
299  !
300  type(pq),pointer :: a
301  real(kind_real), intent(in) :: dis
302  real(kind_real), intent(in) :: sdis
303  integer, intent(in) :: idx
304 ! type(kdtree2_result), intent(in) :: e
305  ! not tested as well!
306 
307  integer :: parent, child, n
308  real(kind_real) :: prichild, prichildp1
309 
310  type(kdtree2_result) :: etmp
311 
312  if (.true.) then
313  n=a%heap_size
314  if (n .ge. 1) then
315  parent =1
316  child=2
317 
318  loop: do while (child .le. n)
319  prichild = a%elems(child)%dis
320 
321  !
322  ! posibly child+1 has higher priority, and if
323  ! so, get it, and increment child.
324  !
325 
326  if (child .lt. n) then
327  prichildp1 = a%elems(child+1)%dis
328  if (inf(prichild,prichildp1)) then
329  child = child+1
330  prichild = prichildp1
331  endif
332  endif
333 
334  if (supeq(dis,prichild)) then
335  exit loop
336  ! we have a proper place for our new element,
337  ! bigger than either children's priority.
338  else
339  ! move child into parent.
340  a%elems(parent) = a%elems(child)
341  parent = child
342  child = 2*parent
343  end if
344  end do loop
345  a%elems(parent)%dis = dis
346  a%elems(parent)%sdis = sdis
347  a%elems(parent)%idx = idx
348  pq_replace_max = a%elems(1)%dis
349  else
350  a%elems(1)%dis = dis
351  a%elems(1)%sdis = sdis
352  a%elems(1)%idx = idx
353  pq_replace_max = dis
354  endif
355  else
356  !
357  ! slower version using elementary pop and push operations.
358  !
359  call pq_extract_max(a,etmp)
360  etmp%dis = dis
361  etmp%sdis = sdis
362  etmp%idx = idx
363  pq_replace_max = pq_insert(a,dis,sdis,idx)
364  endif
365  return
366  end function pq_replace_max
367 
368  subroutine pq_delete(a,i)
369 ! Subroutine: pq_delete
370 ! Purpose: delete item with index 'i'
371 
372  type(pq),pointer :: a
373  integer :: i
374 
375  if ((i .lt. 1) .or. (i .gt. a%heap_size)) then
376  write (*,*) 'PQ_DELETE: error, attempt to remove out of bounds element.'
377  stop
378  endif
379 
380  ! swap the item to be deleted with the last element
381  ! and shorten heap by one.
382  a%elems(i) = a%elems(a%heap_size)
383  a%heap_size = a%heap_size - 1
384 
385  call heapify(a,i)
386 
387  end subroutine pq_delete
388 
389 end module tools_kdtree2_pq
subroutine, public pq_extract_max(a, e)
logical function, public sup(x, y)
Definition: tools_repro.F90:85
subroutine, public pq_delete(a, i)
real(kind_real) function, public pq_insert(a, dis, sdis, idx)
subroutine, public pq_max(a, e)
logical function, public supeq(x, y)
subroutine heapify(a, i_in)
real(kind_real) function, public pq_replace_max(a, dis, sdis, idx)
type(pq) function, public pq_create(results_in)
real(kind_real) function, public pq_maxpri(a)
integer, parameter, public kind_real
logical function, public inf(x, y)
Definition: tools_repro.F90:47