(git:374b731)
Loading...
Searching...
No Matches
qs_fb_hash_table_types.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief A simple hash table of integer keys, using hash function:
10!> H(k) = (k*p) mod n + 1
11!> where:
12!> k = key
13!> p = a prime number >= n
14!> n = size of the hash table
15!> And collision resolvation is done by open addressing with linear
16!> probing.
17!>
18!> The table consists of an array of (key,val) pairs, and
19!> there are no intermediate buckets. For every new entry (k,v):
20!> We first look up slot H(k), and if it already contains an entry,
21!> then move to the next empty slot using a predefined linear probing
22!> sequence (e.g. iterate from slots H(k) to n, and then 1 to H(k)-1).
23!> When we look up, we use the same probing sequence.
24! **************************************************************************************************
26
27 USE kinds, ONLY: int_8
29#include "./base/base_uses.f90"
30
31 IMPLICIT NONE
32
33 PRIVATE
34
35! public types
36 PUBLIC :: fb_hash_table_obj
37
38! public methods
39!API
40 PUBLIC :: fb_hash_table_add, &
46
47 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_fb_hash_table_types'
48
49! key value indicating an empty slot
50 INTEGER(KIND=int_8), PARAMETER, PRIVATE :: EMPTY_KEY = -1_int_8
51! Parameters related to automatic resizing of the hash_table:
52! Resize by EXPAND_FACTOR if total no. slots / no. of filled slots < ENLARGE_RATIO
53 INTEGER, PARAMETER, PRIVATE :: ENLARGE_RATIO = 1
54 INTEGER, PARAMETER, PRIVATE :: REDUCE_RATIO = 3
55 INTEGER, PARAMETER, PRIVATE :: EXPAND_FACTOR = 2
56 INTEGER, PARAMETER, PRIVATE :: SHRINK_FACTOR = 2
57
58! **************************************************************************************************
59!> \brief hash table entry data type
60!> \param key : key of the entry
61!> \param val : value of the entry
62!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
63! **************************************************************************************************
64 TYPE fb_hash_table_element
65 INTEGER(KIND=int_8) :: key
66 INTEGER :: val
67 END TYPE fb_hash_table_element
68
69! **************************************************************************************************
70!> \brief data defining a hash table using open addressing for collision
71!> resolvation. Uses simple entry structure to be memory efficient
72!> as well as small overhead
73!> \param table : hash table data area
74!> \param nelements : number of non-empty slots in table
75!> \param nmax : max number of slots in table
76!> \param prime : prime number used in the hash function
77!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
78! **************************************************************************************************
79 TYPE fb_hash_table_data
80 TYPE(fb_hash_table_element), DIMENSION(:), POINTER :: table => null()
81 INTEGER :: nelements
82 INTEGER :: nmax
83 INTEGER :: prime
84 END TYPE fb_hash_table_data
85
86! **************************************************************************************************
87!> \brief the object container which allows for the creation of an array
88!> of pointers to fb_hash_table objects
89!> \param obj : pointer to the fb_hash_table object
90!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
91! **************************************************************************************************
93 TYPE(fb_hash_table_data), POINTER, PRIVATE :: obj => null()
94 END TYPE fb_hash_table_obj
95
96CONTAINS
97
98! **************************************************************************************************
99!> \brief Add element to a hash table, auto resize if necessary
100!> \param hash_table : the fb_hash_table object
101!> \param key : key of the element
102!> \param val : value of the element
103!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
104! **************************************************************************************************
105 RECURSIVE SUBROUTINE fb_hash_table_add(hash_table, key, val)
106 TYPE(fb_hash_table_obj), INTENT(INOUT) :: hash_table
107 INTEGER(KIND=int_8), INTENT(IN) :: key
108 INTEGER, INTENT(IN) :: val
109
110 INTEGER :: islot
111 LOGICAL :: check_ok
112
113 check_ok = fb_hash_table_has_data(hash_table)
114 cpassert(check_ok)
115 ! check hash table size, if too small rehash in a larger table
116 IF (hash_table%obj%nelements*enlarge_ratio .GE. &
117 hash_table%obj%nmax) THEN
118 CALL fb_hash_table_rehash(hash_table=hash_table, &
119 nmax=hash_table%obj%nmax*expand_factor)
120 END IF
121 ! find the right slot for the given key
122 islot = fb_hash_table_linear_probe(hash_table, key)
123 cpassert(islot > 0)
124 ! we are adding a new entry only if islot points to an empty slot,
125 ! otherwise just change the val of the existing entry
126 IF (hash_table%obj%table(islot)%key == empty_key) THEN
127 hash_table%obj%nelements = hash_table%obj%nelements + 1
128 hash_table%obj%table(islot)%key = key
129 END IF
130 hash_table%obj%table(islot)%val = val
131 END SUBROUTINE fb_hash_table_add
132
133! **************************************************************************************************
134!> \brief Creates and initialises an empty fb_hash_table object
135!> \param hash_table : the fb_hash_table object, its content must be NULL
136!> and cannot be UNDEFINED
137!> \param nmax : total size of the table, optional. If absent default
138!> size is 1.
139!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
140! **************************************************************************************************
141 SUBROUTINE fb_hash_table_create(hash_table, nmax)
142 TYPE(fb_hash_table_obj), INTENT(INOUT) :: hash_table
143 INTEGER, INTENT(IN), OPTIONAL :: nmax
144
145 INTEGER :: my_nmax
146 LOGICAL :: check_ok
147
148 check_ok = .NOT. fb_hash_table_has_data(hash_table)
149 cpassert(check_ok)
150 ALLOCATE (hash_table%obj)
151 NULLIFY (hash_table%obj%table)
152 hash_table%obj%nmax = 0
153 hash_table%obj%nelements = 0
154 hash_table%obj%prime = 2
155 my_nmax = 1
156 IF (PRESENT(nmax)) my_nmax = nmax
157 CALL fb_hash_table_init(hash_table=hash_table, &
158 nmax=my_nmax)
159
160 END SUBROUTINE fb_hash_table_create
161
162! **************************************************************************************************
163!> \brief Retrieve value from a key from a hash table
164!> \param hash_table : the fb_hash_table object
165!> \param key : input key
166!> \param val : output value, equals to 0 if key not found
167!> \param found : .TRUE. if key is found, .FALSE. otherwise
168!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
169! **************************************************************************************************
170 SUBROUTINE fb_hash_table_get(hash_table, key, val, found)
171 TYPE(fb_hash_table_obj), INTENT(IN) :: hash_table
172 INTEGER(KIND=int_8), INTENT(IN) :: key
173 INTEGER, INTENT(OUT) :: val
174 LOGICAL, INTENT(OUT) :: found
175
176 INTEGER :: islot
177 LOGICAL :: check_ok
178
179 check_ok = fb_hash_table_has_data(hash_table)
180 cpassert(check_ok)
181 found = .false.
182 val = 0
183 islot = fb_hash_table_linear_probe(hash_table, key)
184 IF (islot > 0) THEN
185 IF (hash_table%obj%table(islot)%key == key) THEN
186 val = hash_table%obj%table(islot)%val
187 found = .true.
188 END IF
189 END IF
190 END SUBROUTINE fb_hash_table_get
191
192! **************************************************************************************************
193!> \brief check if the object has data associated to it
194!> \param hash_table : the fb_hash_table object in question
195!> \return : true if hash_table%obj is associated, false otherwise
196!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
197! **************************************************************************************************
198 PURE FUNCTION fb_hash_table_has_data(hash_table) RESULT(res)
199 TYPE(fb_hash_table_obj), INTENT(IN) :: hash_table
200 LOGICAL :: res
201
202 res = ASSOCIATED(hash_table%obj)
203 END FUNCTION fb_hash_table_has_data
204
205! **************************************************************************************************
206!> \brief Initialises a fb_hash_table object
207!> \param hash_table : the fb_hash_table object, its content must be NULL
208!> and cannot be UNDEFINED
209!> \param nmax : new size of the table, optional. If absent use the
210!> old size
211!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
212! **************************************************************************************************
213 SUBROUTINE fb_hash_table_init(hash_table, nmax)
214 TYPE(fb_hash_table_obj), INTENT(INOUT) :: hash_table
215 INTEGER, INTENT(IN), OPTIONAL :: nmax
216
217 INTEGER :: ii, my_nmax, power
218 LOGICAL :: check_ok
219
220 check_ok = fb_hash_table_has_data(hash_table)
221 cpassert(check_ok)
222 my_nmax = hash_table%obj%nmax
223 IF (PRESENT(nmax)) my_nmax = nmax
224 ! table length should always be power of 2. Find the least
225 ! power that is greater or equal to my_nmax
226 power = 0
227 DO WHILE (2**power .LT. my_nmax)
228 power = power + 1
229 END DO
230 my_nmax = 2**power
231 IF (ASSOCIATED(hash_table%obj%table)) THEN
232 IF (SIZE(hash_table%obj%table) .NE. my_nmax) THEN
233 DEALLOCATE (hash_table%obj%table)
234 ALLOCATE (hash_table%obj%table(my_nmax))
235 END IF
236 ELSE
237 ALLOCATE (hash_table%obj%table(my_nmax))
238 END IF
239 hash_table%obj%nmax = my_nmax
240 hash_table%obj%prime = hash_table_matching_prime(my_nmax)
241 ! initiate element to be "empty"
242 DO ii = 1, hash_table%obj%nmax
243 hash_table%obj%table(ii)%key = empty_key
244 hash_table%obj%table(ii)%val = 0
245 END DO
246 hash_table%obj%nelements = 0
247 END SUBROUTINE fb_hash_table_init
248
249! **************************************************************************************************
250!> \brief Nullifies a fb_hash_table object
251!> \param hash_table : the fb_hash_table object
252!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
253! **************************************************************************************************
254 PURE SUBROUTINE fb_hash_table_nullify(hash_table)
255 TYPE(fb_hash_table_obj), INTENT(INOUT) :: hash_table
256
257 NULLIFY (hash_table%obj)
258 END SUBROUTINE fb_hash_table_nullify
259
260! **************************************************************************************************
261!> \brief Rehash table. If nmax is present, then also change the table size
262!> to MAX(nmax, number_of_non_empty_elements).
263!> \param hash_table : the fb_hash_table object
264!> \param nmax [OPTIONAL] : maximum size of the rehashed table
265!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
266! **************************************************************************************************
267 RECURSIVE SUBROUTINE fb_hash_table_rehash(hash_table, nmax)
268 TYPE(fb_hash_table_obj), INTENT(INOUT) :: hash_table
269 INTEGER, INTENT(IN), OPTIONAL :: nmax
270
271 INTEGER :: ii, my_nmax
272 TYPE(fb_hash_table_element), ALLOCATABLE, &
273 DIMENSION(:) :: tmp_table
274
275 IF (.NOT. fb_hash_table_has_data(hash_table)) THEN
276 CALL fb_hash_table_create(hash_table, nmax)
277 RETURN
278 END IF
279 IF (PRESENT(nmax)) THEN
280 my_nmax = max(nmax, hash_table%obj%nelements)
281 ELSE
282 my_nmax = hash_table%obj%nmax
283 END IF
284 ALLOCATE (tmp_table(hash_table%obj%nmax))
285 tmp_table(:) = hash_table%obj%table(:)
286 CALL fb_hash_table_release(hash_table)
287 CALL fb_hash_table_create(hash_table=hash_table, &
288 nmax=my_nmax)
289 DO ii = 1, SIZE(tmp_table)
290 IF (tmp_table(ii)%key .NE. empty_key) THEN
291 CALL fb_hash_table_add(hash_table=hash_table, &
292 key=tmp_table(ii)%key, &
293 val=tmp_table(ii)%val)
294 END IF
295 END DO
296 DEALLOCATE (tmp_table)
297 END SUBROUTINE fb_hash_table_rehash
298
299! **************************************************************************************************
300!> \brief releases given object
301!> \param hash_table : the fb_hash_table object in question
302!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
303! **************************************************************************************************
304 SUBROUTINE fb_hash_table_release(hash_table)
305 TYPE(fb_hash_table_obj), INTENT(INOUT) :: hash_table
306
307 IF (ASSOCIATED(hash_table%obj)) THEN
308 IF (ASSOCIATED(hash_table%obj%table)) THEN
309 DEALLOCATE (hash_table%obj%table)
310 END IF
311 DEALLOCATE (hash_table%obj)
312 ELSE
313 NULLIFY (hash_table%obj)
314 END IF
315 END SUBROUTINE fb_hash_table_release
316
317! **************************************************************************************************
318!> \brief Remove element from a table, automatic resize if necessary
319!> \param hash_table : the fb_hash_table object
320!> \param key : key of the element to be removed
321!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
322! **************************************************************************************************
323 SUBROUTINE fb_hash_table_remove(hash_table, key)
324 TYPE(fb_hash_table_obj), INTENT(INOUT) :: hash_table
325 INTEGER(KIND=int_8), INTENT(IN) :: key
326
327 INTEGER :: islot
328 LOGICAL :: check_ok
329
330 check_ok = fb_hash_table_has_data(hash_table)
331 cpassert(check_ok)
332 islot = fb_hash_table_linear_probe(hash_table, key)
333 ! we are only removing an entry if the key is found
334 IF (islot > 0) THEN
335 IF (hash_table%obj%table(islot)%key == key) THEN
336 hash_table%obj%table(islot)%key = empty_key
337 hash_table%obj%nelements = hash_table%obj%nelements - 1
338 ! must rehash after setting a filled slot to empty, otherwise the
339 ! table will not work. Automatic resize if required
340 IF (hash_table%obj%nelements*reduce_ratio .LT. &
341 hash_table%obj%nmax) THEN
342 CALL fb_hash_table_rehash(hash_table=hash_table, &
343 nmax=hash_table%obj%nmax/shrink_factor)
344 ELSE
345 CALL fb_hash_table_rehash(hash_table=hash_table)
346 END IF
347 END IF
348 END IF
349 END SUBROUTINE fb_hash_table_remove
350
351! **************************************************************************************************
352!> \brief outputs the current information about the table
353!> \param hash_table : the fb_hash_table object in question
354!> \param nelements : number of non-empty slots in the table
355!> \param nmax : maximum number of slots in the table
356!> \param prime : the prime used in the hash function
357!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
358! **************************************************************************************************
359 SUBROUTINE fb_hash_table_status(hash_table, nelements, nmax, prime)
360 TYPE(fb_hash_table_obj), INTENT(INOUT) :: hash_table
361 INTEGER, INTENT(OUT), OPTIONAL :: nelements, nmax, prime
362
363 LOGICAL :: check_ok
364
365 check_ok = fb_hash_table_has_data(hash_table)
366 cpassert(check_ok)
367 IF (PRESENT(nelements)) nelements = hash_table%obj%nelements
368 IF (PRESENT(nmax)) nmax = hash_table%obj%nmax
369 IF (PRESENT(prime)) prime = hash_table%obj%prime
370 END SUBROUTINE fb_hash_table_status
371
372! **************************************************************************************************
373!> \brief Linear probing algorithm for the hash table
374!> \param hash_table : the fb_hash_table object
375!> \param key : key to locate
376!> \return : slot location in the table correspond to key, 0 if key
377!> not found
378!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
379! **************************************************************************************************
380 PURE FUNCTION fb_hash_table_linear_probe(hash_table, key) &
381 result(islot)
382 TYPE(fb_hash_table_obj), INTENT(IN) :: hash_table
383 INTEGER(KIND=int_8), INTENT(IN) :: key
384 INTEGER :: islot
385
386 INTEGER :: guess
387
388! first guess is mapped by the hash_function
389
390 guess = fb_hash_table_hash_function(hash_table, key)
391 ! then search for key and stop at first empty slot from guess to
392 ! nmax. using the same linear probe for adding and retrieving
393 ! makes all non-empty keys being put before the first empty slot.
394 DO islot = guess, hash_table%obj%nmax
395 IF ((hash_table%obj%table(islot)%key == key) .OR. &
396 (hash_table%obj%table(islot)%key == empty_key)) RETURN
397 END DO
398 ! if unsuccessful, search from 1 to guess
399 DO islot = 1, guess - 1
400 IF ((hash_table%obj%table(islot)%key == key) .OR. &
401 (hash_table%obj%table(islot)%key == empty_key)) RETURN
402 END DO
403 ! if not found and table is full set islot to 0
404 islot = 0
405 END FUNCTION fb_hash_table_linear_probe
406
407! **************************************************************************************************
408!> \brief Hash function
409!> \param hash_table : the fb_hash_table object
410!> \param key : key to locate
411!> \return : slot location in the table correspond to key, 0 if key
412!> not found
413!> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
414! **************************************************************************************************
415 PURE FUNCTION fb_hash_table_hash_function(hash_table, key) RESULT(hash)
416 TYPE(fb_hash_table_obj), INTENT(IN) :: hash_table
417 INTEGER(KIND=int_8), INTENT(IN) :: key
418 INTEGER :: hash
419
420 INTEGER(KIND=int_8) :: hash_8, nmax_8, prime_8
421
422 nmax_8 = int(hash_table%obj%nmax, int_8)
423 prime_8 = int(hash_table%obj%prime, int_8)
424 ! IAND with nmax-1 is equivalent to MOD nmax if nmax is alway a power of 2.
425 hash_8 = iand(key*prime_8, nmax_8 - 1) + 1_int_8
426 hash = int(hash_8)
427 END FUNCTION fb_hash_table_hash_function
428
429END MODULE qs_fb_hash_table_types
static unsigned int hash(const dbm_task_t task)
Private hash function based on Szudzik's elegant pairing. Using unsigned int to return a positive num...
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public int_8
Definition kinds.F:54
A simple hash table of integer keys, using hash function: H(k) = (k*p) mod n + 1 where: k = key p = a...
pure logical function, public fb_hash_table_has_data(hash_table)
check if the object has data associated to it
recursive subroutine, public fb_hash_table_add(hash_table, key, val)
Add element to a hash table, auto resize if necessary.
subroutine, public fb_hash_table_release(hash_table)
releases given object
subroutine, public fb_hash_table_get(hash_table, key, val, found)
Retrieve value from a key from a hash table.
subroutine, public fb_hash_table_create(hash_table, nmax)
Creates and initialises an empty fb_hash_table object.
pure subroutine, public fb_hash_table_nullify(hash_table)
Nullifies a fb_hash_table object.
Functions which are common to different hash tables Derived from qs_fb_hash_table_types and qs_fb_has...
pure integer function, public hash_table_matching_prime(ii)
Find a prime number equal or larger than ii.
the object container which allows for the creation of an array of pointers to fb_hash_table objects