Octopus
heap.F90
Go to the documentation of this file.
1!! Copyright (C) 2021 S. Ohlmann
2!!
3!! This Source Code Form is subject to the terms of the Mozilla Public
4!! License, v. 2.0. If a copy of the MPL was not distributed with this
5!! file, You can obtain one at https://mozilla.org/MPL/2.0/.
6!!
7#include "global.h"
8
9module heap_oct_m
10 use debug_oct_m
11 use global_oct_m
14 implicit none
15 private
16 public :: heap_t
17
18 ! A min-heap type for implementing a k-way merge
19 ! The min-heap is built on the first indices of the sorted arrays.
20 ! It allows to always extract the minimum element of the sorted arrays
21 ! and the heap structure is then maintained by swapping the indices and
22 ! the corresponding sizes.
23 ! To merge the sorted arrays, the minimum of the heap is extracted and
24 ! replaced by the next element of that array; then the min-heap property
25 ! is restored by down-sifting (minheapify).
26 ! The time complexity of this merge is O(n log k) where n is the full size
27 ! of the arrays and k is the number of arrays.
28 type :: heap_t
29 private
30 integer(int64), pointer :: a(:) => null()
31 integer :: length
32 integer, allocatable :: indices(:)
33 integer, allocatable :: sizes(:)
34 contains
35 procedure :: init => heap_init
36 procedure :: end => heap_end
37 procedure :: merge => heap_merge
38 end type heap_t
39
40contains
41 ! Initialize the heap
42 subroutine heap_init(heap, list, sizes)
43 class(heap_t), intent(inout) :: heap
44 integer(int64), target, intent(in) :: list(:)
45 integer, intent(in) :: sizes(:)
46 integer :: i, j
47
48 push_sub(heap_init)
49 ! we keep a pointer to access the data; we do not need to modify it
50 heap%a => list
51
52 ! get the number of arrays to merge
53 heap%length = ubound(sizes, dim=1)
54 ! ignore empty parts
55 do i = 1, ubound(sizes, dim=1)
56 if (sizes(i) == 0) then
57 heap%length = heap%length - 1
58 end if
59 end do
60 safe_allocate(heap%sizes(heap%length))
61 safe_allocate(heap%indices(heap%length))
62 ! get the sizes of the non-empty arrays
63 j = 1
64 do i = 1, ubound(sizes, dim=1)
65 if (sizes(i) == 0) cycle
66 heap%sizes(j) = sizes(i)
67 j = j + 1
68 end do
69 ! compute the starting indices
70 heap%indices(1) = 1
71 do i = 2, heap%length
72 heap%indices(i) = heap%indices(i-1) + heap%sizes(i-1)
73 end do
74 ! sanity check
75 if (sum(heap%sizes) /= ubound(list, dim=1)) then
76 message(1) = "Error! Mismatch in size of array when initializing heap!"
77 call messages_fatal(1)
78 end if
79
80 ! build the heap
81 call build_minheap(heap)
82 pop_sub(heap_init)
83 end subroutine
84
85 subroutine heap_end(heap)
86 class(heap_t), intent(inout) :: heap
87
88 push_sub(heap_end)
89 nullify(heap%a)
90 heap%length = 0
91 safe_deallocate_a(heap%sizes)
92 safe_deallocate_a(heap%indices)
93 pop_sub(heap_end)
94 end subroutine
95
96 subroutine heap_merge(heap, merged, index_map)
97 class(heap_t), intent(inout) :: heap
98 integer(int64), intent(inout) :: merged(:)
99 integer, optional, intent(inout) :: index_map(:)
100
101 integer :: i
103 push_sub(heap_merge)
104
105 ! make sure the heap was initialized
106 assert(associated(heap%a))
107
108 if (sum(heap%sizes) /= ubound(merged, dim=1)) then
109 message(1) = "Error! Mismatch in size of array when doing k-way merge!"
110 call messages_fatal(1)
111 end if
112
113 do i = 1, sum(heap%sizes)
114 ! extract the minimum element of the heap
115 merged(i) = heap%a(heap%indices(1))
116 if (present(index_map)) then
117 index_map(i) = heap%indices(1)
118 end if
119 ! replace by the next element of that array and reduce its size
120 heap%indices(1) = heap%indices(1) + 1
121 heap%sizes(1) = heap%sizes(1) - 1
122 if (heap%sizes(1) == 0) then
123 ! if this array is already empty, swap it with the last one and
124 ! reduce the size of the heap by one
125 call swap(heap, 1_4, heap%length)
126 heap%length = heap%length - 1
127 end if
128 ! down-sift (restore min-heap property)
129 call minheapify(heap, 1_4)
130 end do
131 pop_sub(heap_merge)
132 end subroutine
133
134 ! build the minheap by downsifting all parent nodes
135 subroutine build_minheap(heap)
136 class(heap_t), intent(inout) :: heap
137
138 integer :: i
139
140 ! this goes bottom up for all parent nodes
141 do i = heap%length/2, 1, -1
142 call minheapify(heap, i)
143 end do
144 end subroutine
145
146 ! downsifting from index i (restore minheap property)
147 recursive subroutine minheapify(heap, i)
148 class(heap_t), intent(inout) :: heap
149 integer, intent(in) :: i
150
151 integer :: left, right, smallest
152
153 left = 2*i
154 right = 2*i + 1
155 smallest = i
156
157 ! get the smallest value of both children (if they are still in the heap)
158 if (left <= heap%length) then
159 if (is_smaller(heap, left, smallest)) then
160 smallest = left
161 end if
162 end if
163 if (right <= heap%length) then
164 if (is_smaller(heap, right, smallest)) then
165 smallest = right
166 end if
167 end if
168 ! we only swap and downsift if one of the children is smaller
169 if (smallest /= i) then
170 call swap(heap, i, smallest)
171 call minheapify(heap, smallest)
172 end if
173 end subroutine
174
175 ! swap two of the sorted arrays in the heap
176 subroutine swap(heap, i, j)
177 class(heap_t), intent(inout) :: heap
178 integer, intent(in) :: i
179 integer, intent(in) :: j
180
181 ! swap index and associated size
182 call swap_int(heap%indices, i, j)
183 call swap_int(heap%sizes, i, j)
184 end subroutine
185
186 ! swap two integers
187 subroutine swap_int(a, i, j)
188 integer, intent(inout) :: a(:)
189 integer, intent(in) :: i
190 integer, intent(in) :: j
191
192 integer :: tmp
193
194 tmp = a(i)
195 a(i) = a(j)
196 a(j) = tmp
197 end subroutine
198
199 ! compare the current value of two arrays in the heap
200 logical function is_smaller(heap, i, j)
201 class(heap_t), intent(inout) :: heap
202 integer, intent(in) :: i
203 integer, intent(in) :: j
204
205 is_smaller = heap%a(heap%indices(i)) < heap%a(heap%indices(j))
206 end function
207end module heap_oct_m
subroutine heap_end(heap)
Definition: heap.F90:179
subroutine heap_init(heap, list, sizes)
Definition: heap.F90:136
logical function is_smaller(heap, i, j)
Definition: heap.F90:294
recursive subroutine minheapify(heap, i)
Definition: heap.F90:241
subroutine heap_merge(heap, merged, index_map)
Definition: heap.F90:190
subroutine swap(heap, i, j)
Definition: heap.F90:270
subroutine build_minheap(heap)
Definition: heap.F90:229
subroutine swap_int(a, i, j)
Definition: heap.F90:281
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:420