Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief DBT tensor framework for block-sparse tensor contraction.
10 : !> Representation of n-rank tensors as DBT tall-and-skinny matrices.
11 : !> Support for arbitrary redistribution between different representations.
12 : !> Support for arbitrary tensor contractions
13 : !> \todo implement checks and error messages
14 : !> \author Patrick Seewald
15 : ! **************************************************************************************************
16 : MODULE dbt_methods
17 : #:include "dbt_macros.fypp"
18 : #:set maxdim = maxrank
19 : #:set ndims = range(2,maxdim+1)
20 :
21 : USE cp_dbcsr_api, ONLY: &
22 : dbcsr_type, dbcsr_release, &
23 : dbcsr_iterator_type, dbcsr_iterator_start, dbcsr_iterator_blocks_left, dbcsr_iterator_next_block, &
24 : dbcsr_has_symmetry, dbcsr_desymmetrize, dbcsr_put_block, dbcsr_clear, dbcsr_iterator_stop
25 : USE dbt_allocate_wrap, ONLY: &
26 : allocate_any
27 : USE dbt_array_list_methods, ONLY: &
28 : get_arrays, reorder_arrays, get_ith_array, array_list, array_sublist, check_equal, array_eq_i, &
29 : create_array_list, destroy_array_list, sizes_of_arrays
30 : USE dbm_api, ONLY: &
31 : dbm_clear
32 : USE dbt_tas_types, ONLY: &
33 : dbt_tas_split_info
34 : USE dbt_tas_base, ONLY: &
35 : dbt_tas_copy, dbt_tas_finalize, dbt_tas_get_info, dbt_tas_info
36 : USE dbt_tas_mm, ONLY: &
37 : dbt_tas_multiply, dbt_tas_batched_mm_init, dbt_tas_batched_mm_finalize, &
38 : dbt_tas_batched_mm_complete, dbt_tas_set_batched_state
39 : USE dbt_block, ONLY: &
40 : dbt_iterator_type, dbt_get_block, dbt_put_block, dbt_iterator_start, &
41 : dbt_iterator_blocks_left, dbt_iterator_stop, dbt_iterator_next_block, &
42 : ndims_iterator, dbt_reserve_blocks, block_nd, destroy_block, checker_tr
43 : USE dbt_index, ONLY: &
44 : dbt_get_mapping_info, nd_to_2d_mapping, dbt_inverse_order, permute_index, get_nd_indices_tensor, &
45 : ndims_mapping_row, ndims_mapping_column, ndims_mapping
46 : USE dbt_types, ONLY: &
47 : dbt_create, dbt_type, ndims_tensor, dims_tensor, &
48 : dbt_distribution_type, dbt_distribution, dbt_nd_mp_comm, dbt_destroy, &
49 : dbt_distribution_destroy, dbt_distribution_new_expert, dbt_get_stored_coordinates, &
50 : blk_dims_tensor, dbt_hold, dbt_pgrid_type, mp_environ_pgrid, dbt_filter, &
51 : dbt_clear, dbt_finalize, dbt_get_num_blocks, dbt_scale, &
52 : dbt_get_num_blocks_total, dbt_get_info, ndims_matrix_row, ndims_matrix_column, &
53 : dbt_max_nblks_local, dbt_default_distvec, dbt_contraction_storage, dbt_nblks_total, &
54 : dbt_distribution_new, dbt_copy_contraction_storage, dbt_pgrid_destroy, dbt_get_nze
55 : USE kinds, ONLY: &
56 : dp, default_string_length, int_8, dp
57 : USE message_passing, ONLY: &
58 : mp_cart_type
59 : USE util, ONLY: &
60 : sort
61 : USE dbt_reshape_ops, ONLY: &
62 : dbt_reshape, dbt_reshape_release
63 : USE dbt_tas_split, ONLY: &
64 : dbt_tas_mp_comm, rowsplit, colsplit, dbt_tas_info_hold, dbt_tas_release_info, default_nsplit_accept_ratio, &
65 : default_pdims_accept_ratio, dbt_tas_create_split
66 : USE dbt_split, ONLY: &
67 : dbt_split_copyback, dbt_make_compatible_blocks, dbt_crop
68 : USE dbt_io, ONLY: &
69 : dbt_write_tensor_info, dbt_write_tensor_dist, prep_output_unit, dbt_write_split_info
70 : USE message_passing, ONLY: mp_comm_type
71 :
72 : #include "../base/base_uses.f90"
73 :
74 : IMPLICIT NONE
75 : PRIVATE
76 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'dbt_methods'
77 :
78 : PUBLIC :: &
79 : dbt_contract, &
80 : dbt_copy, &
81 : dbt_get_block, &
82 : dbt_get_stored_coordinates, &
83 : dbt_inverse_order, &
84 : dbt_iterator_blocks_left, &
85 : dbt_iterator_next_block, &
86 : dbt_iterator_start, &
87 : dbt_iterator_stop, &
88 : dbt_iterator_type, &
89 : dbt_put_block, &
90 : dbt_reserve_blocks, &
91 : dbt_copy_matrix_to_tensor, &
92 : dbt_copy_tensor_to_matrix, &
93 : dbt_batched_contract_init, &
94 : dbt_batched_contract_finalize, &
95 : dbt_library_init, &
96 : dbt_library_finalize, &
97 : dbt_library_print_stats, &
98 : dbt_clear_mempools
99 :
100 : ! Per contraction (tensor names): remapped and cropped tensors, i.e., data copied
101 : ! before the multiplication (local to the rank)
102 : TYPE contract_stats_type
103 : CHARACTER(LEN=3*default_string_length + 6) :: names = ""
104 : INTEGER(KIND=int_8) :: ncalls = 0, nempty = 0, flop = 0
105 : INTEGER(KIND=int_8), DIMENSION(3) :: nremap = 0, nzremap = 0
106 : INTEGER(KIND=int_8), DIMENSION(2) :: ncrop = 0, nzcrop = 0, ncrop_remap = 0
107 : END TYPE contract_stats_type
108 :
109 : TYPE(contract_stats_type), DIMENSION(:), ALLOCATABLE, SAVE :: contract_stats
110 : INTEGER, SAVE :: ncontract_stats = 0
111 :
112 : CONTAINS
113 :
114 : ! **************************************************************************************************
115 : !> \brief Initialize the DBT library (after dbm_library_init).
116 : !> \author Hans Pabst
117 : ! **************************************************************************************************
118 1474 : SUBROUTINE dbt_library_init()
119 1474 : END SUBROUTINE dbt_library_init
120 :
121 : ! **************************************************************************************************
122 : !> \brief Finalize the DBT library (before dbm_library_finalize), which releases its buffers.
123 : !> \author Hans Pabst
124 : ! **************************************************************************************************
125 1474 : SUBROUTINE dbt_library_finalize()
126 1474 : CALL dbt_clear_mempools()
127 1474 : IF (ALLOCATED(contract_stats)) DEALLOCATE (contract_stats)
128 1474 : ncontract_stats = 0
129 1474 : END SUBROUTINE dbt_library_finalize
130 :
131 : ! **************************************************************************************************
132 : !> \brief Release the buffers that persist across calls (as dbcsr_clear_mempools), e.g., once a
133 : !> phase with large tensor operations ends. They are allocated again when needed.
134 : !> \author Hans Pabst
135 : ! **************************************************************************************************
136 2910 : SUBROUTINE dbt_clear_mempools()
137 2910 : CALL dbt_reshape_release()
138 2910 : END SUBROUTINE dbt_clear_mempools
139 :
140 : ! **************************************************************************************************
141 : !> \brief Print per-contraction statistics (rank-local table, totals over all ranks).
142 : !> \param mpi_comm communicator of all ranks calling this routine
143 : !> \param output_unit unit to write to (rank-local table is written if positive)
144 : !> \author Hans Pabst
145 : ! **************************************************************************************************
146 11306 : SUBROUTINE dbt_library_print_stats(mpi_comm, output_unit)
147 : TYPE(mp_comm_type), INTENT(IN) :: mpi_comm
148 : INTEGER, INTENT(IN) :: output_unit
149 :
150 : INTEGER :: i, j, k
151 : INTEGER(KIND=int_8) :: ncalls, nzcopied
152 11306 : INTEGER, DIMENSION(:), ALLOCATABLE :: order
153 11306 : REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: copied
154 :
155 11306 : ncalls = 0
156 11306 : nzcopied = 0
157 14324 : DO i = 1, ncontract_stats
158 3018 : ncalls = ncalls + contract_stats(i)%ncalls
159 29414 : nzcopied = nzcopied + SUM(contract_stats(i)%nzremap) + SUM(contract_stats(i)%nzcrop)
160 : END DO
161 11306 : CALL mpi_comm%sum(ncalls)
162 11306 : CALL mpi_comm%sum(nzcopied)
163 :
164 11306 : IF (output_unit > 0 .AND. 0 < ncalls) THEN
165 1285 : ALLOCATE (order(ncontract_stats), copied(ncontract_stats))
166 1766 : DO i = 1, ncontract_stats
167 1509 : order(i) = i
168 9311 : copied(i) = -REAL(SUM(contract_stats(i)%nzremap) + SUM(contract_stats(i)%nzcrop), dp)
169 : END DO
170 257 : CALL sort(copied, ncontract_stats, order) ! ascending: most copied data first
171 257 : WRITE (output_unit, '(/,T2,A)') REPEAT("-", 79)
172 257 : WRITE (output_unit, '(T2,A,T70,A)') "DBT CONTRACTION STATISTICS (rank-local)", "copied [MB]"
173 : WRITE (output_unit, '(T2,A,T47,A,T55,A,T62,A,T68,A,T75,A)') &
174 257 : "A x B = C", "calls", "remap", "crop", "both", "total"
175 1766 : DO k = 1, ncontract_stats
176 1509 : i = order(k)
177 257 : ASSOCIATE (s => contract_stats(i))
178 : WRITE (output_unit, '(T2,A,T44,I8,T52,3I3,T61,2I3,T67,2I3,T73,F8.1)') &
179 1509 : s%names(1:MIN(LEN_TRIM(s%names), 41)), s%ncalls, &
180 12072 : (MIN(s%nremap(j), 99_int_8), j=1, 3), (MIN(s%ncrop(j), 99_int_8), j=1, 2), &
181 4527 : (MIN(s%ncrop_remap(j), 99_int_8), j=1, 2), &
182 25653 : 8.0_dp*REAL(SUM(s%nzremap) + SUM(s%nzcrop), dp)/1.0E6_dp
183 : END ASSOCIATE
184 : END DO
185 257 : WRITE (output_unit, '(T2,A,T44,I8,T73,F8.1)') "all ranks", ncalls, 8.0_dp*REAL(nzcopied, dp)/1.0E6_dp
186 257 : WRITE (output_unit, '(T2,A)') REPEAT("-", 79)
187 : END IF
188 11306 : END SUBROUTINE dbt_library_print_stats
189 :
190 : ! **************************************************************************************************
191 : !> \brief Index of the statistics entry for the given tensor names (created if missing).
192 : !> \author Hans Pabst
193 : ! **************************************************************************************************
194 257420 : FUNCTION contract_stats_index(name_1, name_2, name_3) RESULT(idx)
195 : CHARACTER(LEN=*), INTENT(IN) :: name_1, name_2, name_3
196 : INTEGER :: idx
197 :
198 : CHARACTER(LEN=3*default_string_length + 6) :: names
199 : INTEGER :: i
200 257420 : TYPE(contract_stats_type), DIMENSION(:), ALLOCATABLE :: grown
201 :
202 257420 : names = TRIM(name_1)//" x "//TRIM(name_2)//" = "//TRIM(name_3)
203 257420 : idx = 0
204 1361425 : DO i = 1, ncontract_stats
205 1360923 : IF (contract_stats(i)%names == names) idx = i
206 1361425 : IF (0 < idx) EXIT
207 : END DO
208 257420 : IF (0 == idx) THEN
209 3022 : IF (.NOT. ALLOCATED(contract_stats)) ALLOCATE (contract_stats(16))
210 502 : IF (SIZE(contract_stats) <= ncontract_stats) THEN
211 0 : ALLOCATE (grown(2*SIZE(contract_stats)))
212 0 : grown(1:ncontract_stats) = contract_stats(1:ncontract_stats)
213 0 : CALL MOVE_ALLOC(grown, contract_stats)
214 : END IF
215 502 : ncontract_stats = ncontract_stats + 1
216 502 : idx = ncontract_stats
217 502 : contract_stats(idx)%names = names
218 : END IF
219 257420 : END FUNCTION contract_stats_index
220 :
221 : ! **************************************************************************************************
222 : !> \brief Copy tensor data.
223 : !> Redistributes tensor data according to distributions of target and source tensor.
224 : !> Permutes tensor index according to `order` argument (if present).
225 : !> Source and target tensor formats are arbitrary as long as the following requirements are met:
226 : !> * source and target tensors have the same rank and the same sizes in each dimension in terms
227 : !> of tensor elements (block sizes don't need to be the same).
228 : !> If `order` argument is present, sizes must match after index permutation.
229 : !> OR
230 : !> * target tensor is not yet created, in this case an exact copy of source tensor is returned.
231 : !> \param tensor_in Source
232 : !> \param tensor_out Target
233 : !> \param order Permutation of target tensor index.
234 : !> Exact same convention as order argument of RESHAPE intrinsic.
235 : !> \param bounds crop tensor data: start and end index for each tensor dimension
236 : !> \author Patrick Seewald
237 : ! **************************************************************************************************
238 1165878 : SUBROUTINE dbt_copy(tensor_in, tensor_out, order, summation, bounds, move_data, unit_nr)
239 : TYPE(dbt_type), INTENT(INOUT), TARGET :: tensor_in, tensor_out
240 : INTEGER, DIMENSION(ndims_tensor(tensor_in)), &
241 : INTENT(IN), OPTIONAL :: order
242 : LOGICAL, INTENT(IN), OPTIONAL :: summation, move_data
243 : INTEGER, DIMENSION(2, ndims_tensor(tensor_in)), &
244 : INTENT(IN), OPTIONAL :: bounds
245 : INTEGER, INTENT(IN), OPTIONAL :: unit_nr
246 : INTEGER :: handle
247 :
248 582939 : CALL tensor_in%pgrid%mp_comm_2d%sync()
249 582939 : CALL timeset("dbt_total", handle)
250 :
251 : ! make sure that it is safe to use dbt_copy during a batched contraction
252 582939 : CALL dbt_tas_batched_mm_complete(tensor_in%matrix_rep, warn=.TRUE.)
253 582939 : CALL dbt_tas_batched_mm_complete(tensor_out%matrix_rep, warn=.TRUE.)
254 :
255 582939 : CALL dbt_copy_expert(tensor_in, tensor_out, order, summation, bounds, move_data, unit_nr)
256 582939 : CALL tensor_in%pgrid%mp_comm_2d%sync()
257 582939 : CALL timestop(handle)
258 582939 : END SUBROUTINE dbt_copy
259 :
260 : ! **************************************************************************************************
261 : !> \brief expert routine for copying a tensor. For internal use only.
262 : !> \author Patrick Seewald
263 : ! **************************************************************************************************
264 5772 : SUBROUTINE dbt_copy_expert(tensor_in, tensor_out, order, summation, bounds, move_data, unit_nr)
265 : TYPE(dbt_type), INTENT(INOUT), TARGET :: tensor_in, tensor_out
266 : INTEGER, DIMENSION(ndims_tensor(tensor_in)), &
267 : INTENT(IN), OPTIONAL :: order
268 : LOGICAL, INTENT(IN), OPTIONAL :: summation, move_data
269 : INTEGER, DIMENSION(2, ndims_tensor(tensor_in)), &
270 : INTENT(IN), OPTIONAL :: bounds
271 : INTEGER, INTENT(IN), OPTIONAL :: unit_nr
272 :
273 : TYPE(dbt_type), POINTER :: in_tmp_1, in_tmp_2, &
274 : in_tmp_3, out_tmp_1
275 : INTEGER :: handle, unit_nr_prv
276 641540 : INTEGER, DIMENSION(:), ALLOCATABLE :: map1_in_1, map1_in_2, map2_in_1, map2_in_2
277 :
278 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_copy'
279 : LOGICAL :: dist_compatible_tas, dist_compatible_tensor, &
280 : summation_prv, new_in_1, new_in_2, &
281 : new_in_3, new_out_1, block_compatible, &
282 : move_prv, crop_reshape
283 641540 : TYPE(array_list) :: blk_sizes_in
284 :
285 641540 : CALL timeset(routineN, handle)
286 :
287 641540 : CPASSERT(tensor_out%valid)
288 :
289 641540 : unit_nr_prv = prep_output_unit(unit_nr)
290 :
291 641540 : IF (PRESENT(move_data)) THEN
292 458637 : move_prv = move_data
293 : ELSE
294 182903 : move_prv = .FALSE.
295 : END IF
296 :
297 641540 : dist_compatible_tas = .FALSE.
298 641540 : dist_compatible_tensor = .FALSE.
299 641540 : block_compatible = .FALSE.
300 641540 : new_in_1 = .FALSE.
301 641540 : new_in_2 = .FALSE.
302 641540 : new_in_3 = .FALSE.
303 641540 : new_out_1 = .FALSE.
304 :
305 641540 : IF (PRESENT(summation)) THEN
306 187145 : summation_prv = summation
307 : ELSE
308 : summation_prv = .FALSE.
309 : END IF
310 :
311 : ! crop while reshaping (single copy) if that is the copy anyway
312 641540 : crop_reshape = .FALSE.
313 641540 : IF (PRESENT(bounds) .AND. .NOT. PRESENT(order)) crop_reshape = copy_reshapes(tensor_in, tensor_out)
314 641540 : IF (PRESENT(bounds) .AND. .NOT. crop_reshape) THEN
315 33838 : ALLOCATE (in_tmp_1)
316 4834 : CALL dbt_crop(tensor_in, in_tmp_1, bounds=bounds, move_data=move_prv)
317 4834 : new_in_1 = .TRUE.
318 4834 : move_prv = .TRUE.
319 : ELSE
320 : in_tmp_1 => tensor_in
321 : END IF
322 :
323 641540 : IF (PRESENT(order)) THEN
324 175725 : CALL reorder_arrays(in_tmp_1%blk_sizes, blk_sizes_in, order=order)
325 175725 : block_compatible = check_equal(blk_sizes_in, tensor_out%blk_sizes)
326 : ELSE
327 465815 : block_compatible = check_equal(in_tmp_1%blk_sizes, tensor_out%blk_sizes)
328 : END IF
329 :
330 641540 : IF (.NOT. block_compatible) THEN
331 1075503 : ALLOCATE (in_tmp_2, out_tmp_1)
332 : CALL dbt_make_compatible_blocks(in_tmp_1, tensor_out, in_tmp_2, out_tmp_1, order=order, &
333 82731 : nodata2=.NOT. summation_prv, move_data=move_prv)
334 82731 : new_in_2 = .TRUE.; new_out_1 = .TRUE.
335 82731 : move_prv = .TRUE.
336 : ELSE
337 : in_tmp_2 => in_tmp_1
338 : out_tmp_1 => tensor_out
339 : END IF
340 :
341 641540 : IF (PRESENT(order)) THEN
342 1230075 : ALLOCATE (in_tmp_3)
343 175725 : CALL dbt_permute_index(in_tmp_2, in_tmp_3, order)
344 175725 : new_in_3 = .TRUE.
345 : ELSE
346 : in_tmp_3 => in_tmp_2
347 : END IF
348 :
349 1924620 : ALLOCATE (map1_in_1(ndims_matrix_row(in_tmp_3)))
350 1924620 : ALLOCATE (map1_in_2(ndims_matrix_column(in_tmp_3)))
351 641540 : CALL dbt_get_mapping_info(in_tmp_3%nd_index, map1_2d=map1_in_1, map2_2d=map1_in_2)
352 :
353 1924620 : ALLOCATE (map2_in_1(ndims_matrix_row(out_tmp_1)))
354 1924620 : ALLOCATE (map2_in_2(ndims_matrix_column(out_tmp_1)))
355 641540 : CALL dbt_get_mapping_info(out_tmp_1%nd_index, map1_2d=map2_in_1, map2_2d=map2_in_2)
356 :
357 641540 : IF (.NOT. PRESENT(order)) THEN
358 465815 : IF (array_eq_i(map1_in_1, map2_in_1) .AND. array_eq_i(map1_in_2, map2_in_2)) THEN
359 385512 : dist_compatible_tas = check_equal(in_tmp_3%nd_dist, out_tmp_1%nd_dist)
360 1043763 : ELSE IF (array_eq_i([map1_in_1, map1_in_2], [map2_in_1, map2_in_2])) THEN
361 33900 : dist_compatible_tensor = check_equal(in_tmp_3%nd_dist, out_tmp_1%nd_dist)
362 : END IF
363 : END IF
364 :
365 385512 : IF (dist_compatible_tas) THEN
366 317440 : CALL dbt_tas_copy(out_tmp_1%matrix_rep, in_tmp_3%matrix_rep, summation)
367 317440 : IF (move_prv) CALL dbt_clear(in_tmp_3)
368 324100 : ELSE IF (dist_compatible_tensor) THEN
369 24210 : CALL dbt_copy_nocomm(in_tmp_3, out_tmp_1, summation)
370 24210 : IF (move_prv) CALL dbt_clear(in_tmp_3)
371 299890 : ELSE IF (crop_reshape) THEN
372 938 : CALL dbt_reshape(in_tmp_3, out_tmp_1, summation, move_data=move_prv, bounds=bounds)
373 : ELSE
374 298952 : CALL dbt_reshape(in_tmp_3, out_tmp_1, summation, move_data=move_prv)
375 : END IF
376 :
377 641540 : IF (new_in_1) THEN
378 4834 : CALL dbt_destroy(in_tmp_1)
379 4834 : DEALLOCATE (in_tmp_1)
380 : END IF
381 :
382 641540 : IF (new_in_2) THEN
383 82731 : CALL dbt_destroy(in_tmp_2)
384 82731 : DEALLOCATE (in_tmp_2)
385 : END IF
386 :
387 641540 : IF (new_in_3) THEN
388 175725 : CALL dbt_destroy(in_tmp_3)
389 175725 : DEALLOCATE (in_tmp_3)
390 : END IF
391 :
392 641540 : IF (new_out_1) THEN
393 82731 : IF (unit_nr_prv /= 0) THEN
394 0 : CALL dbt_write_tensor_dist(out_tmp_1, unit_nr)
395 : END IF
396 82731 : CALL dbt_split_copyback(out_tmp_1, tensor_out, summation)
397 82731 : CALL dbt_destroy(out_tmp_1)
398 82731 : DEALLOCATE (out_tmp_1)
399 : END IF
400 :
401 641540 : CALL timestop(handle)
402 :
403 1288852 : END SUBROUTINE dbt_copy_expert
404 :
405 : ! **************************************************************************************************
406 : !> \brief Whether dbt_copy_expert (without order) copies by dbt_reshape: compatible blocks, but
407 : !> neither the matrix layout nor the distribution allow a copy without communication.
408 : !> \author Hans Pabst
409 : ! **************************************************************************************************
410 4622 : FUNCTION copy_reshapes(tensor_in, tensor_out)
411 : TYPE(dbt_type), INTENT(INOUT) :: tensor_in, tensor_out
412 : LOGICAL :: copy_reshapes
413 :
414 9244 : INTEGER, DIMENSION(ndims_matrix_row(tensor_in)) :: map1_in_1
415 9244 : INTEGER, DIMENSION(ndims_matrix_column(tensor_in)) :: map1_in_2
416 9244 : INTEGER, DIMENSION(ndims_matrix_row(tensor_out)) :: map2_in_1
417 9244 : INTEGER, DIMENSION(ndims_matrix_column(tensor_out)) :: map2_in_2
418 :
419 4622 : copy_reshapes = check_equal(tensor_in%blk_sizes, tensor_out%blk_sizes)
420 4622 : IF (copy_reshapes) THEN
421 4622 : CALL dbt_get_mapping_info(tensor_in%nd_index, map1_2d=map1_in_1, map2_2d=map1_in_2)
422 4622 : CALL dbt_get_mapping_info(tensor_out%nd_index, map1_2d=map2_in_1, map2_2d=map2_in_2)
423 60054 : IF (array_eq_i([map1_in_1, map1_in_2], [map2_in_1, map2_in_2])) THEN
424 4618 : copy_reshapes = .NOT. check_equal(tensor_in%nd_dist, tensor_out%nd_dist)
425 : END IF
426 : END IF
427 4622 : END FUNCTION copy_reshapes
428 :
429 : ! **************************************************************************************************
430 : !> \brief copy without communication, requires that both tensors have same process grid and distribution
431 : !> \param summation Whether to sum matrices b = a + b
432 : !> \author Patrick Seewald
433 : ! **************************************************************************************************
434 24210 : SUBROUTINE dbt_copy_nocomm(tensor_in, tensor_out, summation)
435 : TYPE(dbt_type), INTENT(INOUT) :: tensor_in
436 : TYPE(dbt_type), INTENT(INOUT) :: tensor_out
437 : LOGICAL, INTENT(IN), OPTIONAL :: summation
438 : TYPE(dbt_iterator_type) :: iter
439 24210 : INTEGER, DIMENSION(ndims_tensor(tensor_in)) :: ind_nd
440 24210 : TYPE(block_nd) :: blk_data
441 : LOGICAL :: found
442 :
443 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_copy_nocomm'
444 : INTEGER :: handle
445 :
446 24210 : CALL timeset(routineN, handle)
447 24210 : CPASSERT(tensor_out%valid)
448 :
449 24210 : IF (PRESENT(summation)) THEN
450 11376 : IF (.NOT. summation) CALL dbt_clear(tensor_out)
451 : ELSE
452 12834 : CALL dbt_clear(tensor_out)
453 : END IF
454 :
455 24210 : CALL dbt_reserve_blocks(tensor_in, tensor_out)
456 :
457 : !$OMP PARALLEL DEFAULT(NONE) SHARED(tensor_in,tensor_out,summation) &
458 24210 : !$OMP PRIVATE(iter,ind_nd,blk_data,found)
459 : CALL dbt_iterator_start(iter, tensor_in)
460 : DO WHILE (dbt_iterator_blocks_left(iter))
461 : CALL dbt_iterator_next_block(iter, ind_nd)
462 : CALL dbt_get_block(tensor_in, ind_nd, blk_data, found)
463 : CPASSERT(found)
464 : CALL dbt_put_block(tensor_out, ind_nd, blk_data, summation=summation)
465 : CALL destroy_block(blk_data)
466 : END DO
467 : CALL dbt_iterator_stop(iter)
468 : !$OMP END PARALLEL
469 :
470 24210 : CALL timestop(handle)
471 48420 : END SUBROUTINE dbt_copy_nocomm
472 :
473 : ! **************************************************************************************************
474 : !> \brief copy matrix to tensor.
475 : !> \param summation tensor_out = tensor_out + matrix_in
476 : !> \author Patrick Seewald
477 : ! **************************************************************************************************
478 97109 : SUBROUTINE dbt_copy_matrix_to_tensor(matrix_in, tensor_out, summation)
479 : TYPE(dbcsr_type), TARGET, INTENT(IN) :: matrix_in
480 : TYPE(dbt_type), INTENT(INOUT) :: tensor_out
481 : LOGICAL, INTENT(IN), OPTIONAL :: summation
482 : TYPE(dbcsr_type), POINTER :: matrix_in_desym
483 :
484 : INTEGER, DIMENSION(2) :: ind_2d
485 97109 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: block_arr
486 97109 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: block
487 : TYPE(dbcsr_iterator_type) :: iter
488 :
489 : INTEGER :: handle
490 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_copy_matrix_to_tensor'
491 :
492 97109 : CALL timeset(routineN, handle)
493 97109 : CPASSERT(tensor_out%valid)
494 :
495 97109 : NULLIFY (block)
496 :
497 97109 : IF (dbcsr_has_symmetry(matrix_in)) THEN
498 4950 : ALLOCATE (matrix_in_desym)
499 4950 : CALL dbcsr_desymmetrize(matrix_in, matrix_in_desym)
500 : ELSE
501 : matrix_in_desym => matrix_in
502 : END IF
503 :
504 97109 : IF (PRESENT(summation)) THEN
505 0 : IF (.NOT. summation) CALL dbt_clear(tensor_out)
506 : ELSE
507 97109 : CALL dbt_clear(tensor_out)
508 : END IF
509 :
510 97109 : CALL dbt_reserve_blocks(matrix_in_desym, tensor_out)
511 :
512 : !$OMP PARALLEL DEFAULT(NONE) SHARED(matrix_in_desym,tensor_out,summation) &
513 97109 : !$OMP PRIVATE(iter,ind_2d,block,block_arr)
514 : CALL dbcsr_iterator_start(iter, matrix_in_desym)
515 : DO WHILE (dbcsr_iterator_blocks_left(iter))
516 : CALL dbcsr_iterator_next_block(iter, ind_2d(1), ind_2d(2), block)
517 : CALL allocate_any(block_arr, source=block)
518 : CALL dbt_put_block(tensor_out, ind_2d, SHAPE(block_arr), block_arr, summation=summation)
519 : DEALLOCATE (block_arr)
520 : END DO
521 : CALL dbcsr_iterator_stop(iter)
522 : !$OMP END PARALLEL
523 :
524 97109 : IF (dbcsr_has_symmetry(matrix_in)) THEN
525 4950 : CALL dbcsr_release(matrix_in_desym)
526 4950 : DEALLOCATE (matrix_in_desym)
527 : END IF
528 :
529 97109 : CALL timestop(handle)
530 :
531 194218 : END SUBROUTINE dbt_copy_matrix_to_tensor
532 :
533 : ! **************************************************************************************************
534 : !> \brief copy tensor to matrix
535 : !> \param summation matrix_out = matrix_out + tensor_in
536 : !> \author Patrick Seewald
537 : ! **************************************************************************************************
538 65292 : SUBROUTINE dbt_copy_tensor_to_matrix(tensor_in, matrix_out, summation)
539 : TYPE(dbt_type), INTENT(INOUT) :: tensor_in
540 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix_out
541 : LOGICAL, INTENT(IN), OPTIONAL :: summation
542 : TYPE(dbt_iterator_type) :: iter
543 : INTEGER :: handle
544 : INTEGER, DIMENSION(2) :: ind_2d
545 65292 : REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE :: block
546 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_copy_tensor_to_matrix'
547 : LOGICAL :: found
548 :
549 65292 : CALL timeset(routineN, handle)
550 :
551 65292 : IF (PRESENT(summation)) THEN
552 5910 : IF (.NOT. summation) CALL dbcsr_clear(matrix_out)
553 : ELSE
554 59382 : CALL dbcsr_clear(matrix_out)
555 : END IF
556 :
557 65292 : CALL dbt_reserve_blocks(tensor_in, matrix_out)
558 :
559 : !$OMP PARALLEL DEFAULT(NONE) SHARED(tensor_in,matrix_out,summation) &
560 65292 : !$OMP PRIVATE(iter,ind_2d,block,found)
561 : CALL dbt_iterator_start(iter, tensor_in)
562 : DO WHILE (dbt_iterator_blocks_left(iter))
563 : CALL dbt_iterator_next_block(iter, ind_2d)
564 : IF (dbcsr_has_symmetry(matrix_out) .AND. checker_tr(ind_2d(1), ind_2d(2))) CYCLE
565 :
566 : CALL dbt_get_block(tensor_in, ind_2d, block, found)
567 : CPASSERT(found)
568 :
569 : IF (dbcsr_has_symmetry(matrix_out) .AND. ind_2d(1) > ind_2d(2)) THEN
570 : CALL dbcsr_put_block(matrix_out, ind_2d(2), ind_2d(1), TRANSPOSE(block), summation=summation)
571 : ELSE
572 : CALL dbcsr_put_block(matrix_out, ind_2d(1), ind_2d(2), block, summation=summation)
573 : END IF
574 : DEALLOCATE (block)
575 : END DO
576 : CALL dbt_iterator_stop(iter)
577 : !$OMP END PARALLEL
578 :
579 65292 : CALL timestop(handle)
580 :
581 130584 : END SUBROUTINE dbt_copy_tensor_to_matrix
582 :
583 : ! **************************************************************************************************
584 : !> \brief Contract tensors by multiplying matrix representations.
585 : !> tensor_3(map_1, map_2) := alpha * tensor_1(notcontract_1, contract_1)
586 : !> * tensor_2(contract_2, notcontract_2)
587 : !> + beta * tensor_3(map_1, map_2)
588 : !>
589 : !> \note
590 : !> note 1: block sizes of the corresponding indices need to be the same in all tensors.
591 : !>
592 : !> note 2: for best performance the tensors should have been created in matrix layouts
593 : !> compatible with the contraction, e.g. tensor_1 should have been created with either
594 : !> map1_2d == contract_1 and map2_2d == notcontract_1 or map1_2d == notcontract_1 and
595 : !> map2_2d == contract_1 (the same with tensor_2 and contract_2 / notcontract_2 and with
596 : !> tensor_3 and map_1 / map_2).
597 : !> Furthermore the two largest tensors involved in the contraction should map both to either
598 : !> tall or short matrices: the largest matrix dimension should be "on the same side"
599 : !> and should have identical distribution (which is always the case if the distributions were
600 : !> obtained with dbt_default_distvec).
601 : !>
602 : !> note 3: if the same tensor occurs in multiple contractions, a different tensor object should
603 : !> be created for each contraction and the data should be copied between the tensors by use of
604 : !> dbt_copy. If the same tensor object is used in multiple contractions,
605 : !> matrix layouts are not compatible for all contractions (see note 2).
606 : !>
607 : !> note 4: automatic optimizations are enabled by using the feature of batched contraction, see
608 : !> dbt_batched_contract_init, dbt_batched_contract_finalize.
609 : !> The arguments bounds_1, bounds_2, bounds_3 give the index ranges of the batches.
610 : !>
611 : !> \param tensor_1 first tensor (in)
612 : !> \param tensor_2 second tensor (in)
613 : !> \param contract_1 indices of tensor_1 to contract
614 : !> \param contract_2 indices of tensor_2 to contract (1:1 with contract_1)
615 : !> \param map_1 which indices of tensor_3 map to non-contracted indices of tensor_1 (1:1 with notcontract_1)
616 : !> \param map_2 which indices of tensor_3 map to non-contracted indices of tensor_2 (1:1 with notcontract_2)
617 : !> \param notcontract_1 indices of tensor_1 not to contract
618 : !> \param notcontract_2 indices of tensor_2 not to contract
619 : !> \param tensor_3 contracted tensor (out)
620 : !> \param bounds_1 bounds corresponding to contract_1 AKA contract_2:
621 : !> start and end index of an index range over which to contract.
622 : !> For use in batched contraction.
623 : !> \param bounds_2 bounds corresponding to notcontract_1: start and end index of an index range.
624 : !> For use in batched contraction.
625 : !> \param bounds_3 bounds corresponding to notcontract_2: start and end index of an index range.
626 : !> For use in batched contraction.
627 : !> \param optimize_dist Whether distribution should be optimized internally. In the current
628 : !> implementation this guarantees optimal parameters only for dense matrices.
629 : !> \param pgrid_opt_1 Optionally return optimal process grid for tensor_1.
630 : !> This can be used to choose optimal process grids for subsequent tensor
631 : !> contractions with tensors of similar shape and sparsity. Under some conditions,
632 : !> pgrid_opt_1 can not be returned, in this case the pointer is not associated.
633 : !> \param pgrid_opt_2 Optionally return optimal process grid for tensor_2.
634 : !> \param pgrid_opt_3 Optionally return optimal process grid for tensor_3.
635 : !> \param filter_eps As in DBM mm
636 : !> \param flop As in DBM mm
637 : !> \param move_data memory optimization: transfer data such that tensor_1 and tensor_2 are empty on return
638 : !> \param retain_sparsity enforce the sparsity pattern of the existing tensor_3; default is no
639 : !> \param unit_nr output unit for logging
640 : !> set it to -1 on ranks that should not write (and any valid unit number on
641 : !> ranks that should write output) if 0 on ALL ranks, no output is written
642 : !> \param log_verbose verbose logging (for testing only)
643 : !> \author Patrick Seewald
644 : ! **************************************************************************************************
645 514840 : SUBROUTINE dbt_contract(alpha, tensor_1, tensor_2, beta, tensor_3, &
646 257420 : contract_1, notcontract_1, &
647 257420 : contract_2, notcontract_2, &
648 257420 : map_1, map_2, &
649 257420 : bounds_1, bounds_2, bounds_3, &
650 : optimize_dist, pgrid_opt_1, pgrid_opt_2, pgrid_opt_3, &
651 : filter_eps, flop, move_data, retain_sparsity, unit_nr, log_verbose)
652 : REAL(dp), INTENT(IN) :: alpha
653 : TYPE(dbt_type), INTENT(INOUT), TARGET :: tensor_1
654 : TYPE(dbt_type), INTENT(INOUT), TARGET :: tensor_2
655 : REAL(dp), INTENT(IN) :: beta
656 : INTEGER, DIMENSION(:), INTENT(IN) :: contract_1
657 : INTEGER, DIMENSION(:), INTENT(IN) :: contract_2
658 : INTEGER, DIMENSION(:), INTENT(IN) :: map_1
659 : INTEGER, DIMENSION(:), INTENT(IN) :: map_2
660 : INTEGER, DIMENSION(:), INTENT(IN) :: notcontract_1
661 : INTEGER, DIMENSION(:), INTENT(IN) :: notcontract_2
662 : TYPE(dbt_type), INTENT(INOUT), TARGET :: tensor_3
663 : INTEGER, DIMENSION(2, SIZE(contract_1)), &
664 : INTENT(IN), OPTIONAL :: bounds_1
665 : INTEGER, DIMENSION(2, SIZE(notcontract_1)), &
666 : INTENT(IN), OPTIONAL :: bounds_2
667 : INTEGER, DIMENSION(2, SIZE(notcontract_2)), &
668 : INTENT(IN), OPTIONAL :: bounds_3
669 : LOGICAL, INTENT(IN), OPTIONAL :: optimize_dist
670 : TYPE(dbt_pgrid_type), INTENT(OUT), &
671 : POINTER, OPTIONAL :: pgrid_opt_1
672 : TYPE(dbt_pgrid_type), INTENT(OUT), &
673 : POINTER, OPTIONAL :: pgrid_opt_2
674 : TYPE(dbt_pgrid_type), INTENT(OUT), &
675 : POINTER, OPTIONAL :: pgrid_opt_3
676 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: filter_eps
677 : INTEGER(KIND=int_8), INTENT(OUT), OPTIONAL :: flop
678 : LOGICAL, INTENT(IN), OPTIONAL :: move_data
679 : LOGICAL, INTENT(IN), OPTIONAL :: retain_sparsity
680 : INTEGER, OPTIONAL, INTENT(IN) :: unit_nr
681 : LOGICAL, INTENT(IN), OPTIONAL :: log_verbose
682 :
683 : INTEGER :: handle
684 :
685 257420 : CALL tensor_1%pgrid%mp_comm_2d%sync()
686 257420 : CALL timeset("dbt_total", handle)
687 : CALL dbt_contract_expert(alpha, tensor_1, tensor_2, beta, tensor_3, &
688 : contract_1, notcontract_1, &
689 : contract_2, notcontract_2, &
690 : map_1, map_2, &
691 : bounds_1=bounds_1, &
692 : bounds_2=bounds_2, &
693 : bounds_3=bounds_3, &
694 : optimize_dist=optimize_dist, &
695 : pgrid_opt_1=pgrid_opt_1, &
696 : pgrid_opt_2=pgrid_opt_2, &
697 : pgrid_opt_3=pgrid_opt_3, &
698 : filter_eps=filter_eps, &
699 : flop=flop, &
700 : move_data=move_data, &
701 : retain_sparsity=retain_sparsity, &
702 : unit_nr=unit_nr, &
703 257420 : log_verbose=log_verbose)
704 257420 : CALL tensor_1%pgrid%mp_comm_2d%sync()
705 257420 : CALL timestop(handle)
706 :
707 257420 : END SUBROUTINE dbt_contract
708 :
709 : ! **************************************************************************************************
710 : !> \brief expert routine for tensor contraction. For internal use only.
711 : !> \param nblks_local number of local blocks on this MPI rank
712 : !> \author Patrick Seewald
713 : ! **************************************************************************************************
714 257420 : SUBROUTINE dbt_contract_expert(alpha, tensor_1, tensor_2, beta, tensor_3, &
715 257420 : contract_1, notcontract_1, &
716 257420 : contract_2, notcontract_2, &
717 257420 : map_1, map_2, &
718 257420 : bounds_1, bounds_2, bounds_3, &
719 : optimize_dist, pgrid_opt_1, pgrid_opt_2, pgrid_opt_3, &
720 : filter_eps, flop, move_data, retain_sparsity, &
721 : nblks_local, unit_nr, log_verbose)
722 : REAL(dp), INTENT(IN) :: alpha
723 : TYPE(dbt_type), INTENT(INOUT), TARGET :: tensor_1
724 : TYPE(dbt_type), INTENT(INOUT), TARGET :: tensor_2
725 : REAL(dp), INTENT(IN) :: beta
726 : INTEGER, DIMENSION(:), INTENT(IN) :: contract_1
727 : INTEGER, DIMENSION(:), INTENT(IN) :: contract_2
728 : INTEGER, DIMENSION(:), INTENT(IN) :: map_1
729 : INTEGER, DIMENSION(:), INTENT(IN) :: map_2
730 : INTEGER, DIMENSION(:), INTENT(IN) :: notcontract_1
731 : INTEGER, DIMENSION(:), INTENT(IN) :: notcontract_2
732 : TYPE(dbt_type), INTENT(INOUT), TARGET :: tensor_3
733 : INTEGER, DIMENSION(2, SIZE(contract_1)), &
734 : INTENT(IN), OPTIONAL :: bounds_1
735 : INTEGER, DIMENSION(2, SIZE(notcontract_1)), &
736 : INTENT(IN), OPTIONAL :: bounds_2
737 : INTEGER, DIMENSION(2, SIZE(notcontract_2)), &
738 : INTENT(IN), OPTIONAL :: bounds_3
739 : LOGICAL, INTENT(IN), OPTIONAL :: optimize_dist
740 : TYPE(dbt_pgrid_type), INTENT(OUT), &
741 : POINTER, OPTIONAL :: pgrid_opt_1
742 : TYPE(dbt_pgrid_type), INTENT(OUT), &
743 : POINTER, OPTIONAL :: pgrid_opt_2
744 : TYPE(dbt_pgrid_type), INTENT(OUT), &
745 : POINTER, OPTIONAL :: pgrid_opt_3
746 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: filter_eps
747 : INTEGER(KIND=int_8), INTENT(OUT), OPTIONAL :: flop
748 : LOGICAL, INTENT(IN), OPTIONAL :: move_data
749 : LOGICAL, INTENT(IN), OPTIONAL :: retain_sparsity
750 : INTEGER, INTENT(OUT), OPTIONAL :: nblks_local
751 : INTEGER, OPTIONAL, INTENT(IN) :: unit_nr
752 : LOGICAL, INTENT(IN), OPTIONAL :: log_verbose
753 :
754 : TYPE(dbt_type), POINTER :: tensor_contr_1, tensor_contr_2, tensor_contr_3
755 4890980 : TYPE(dbt_type), TARGET :: tensor_algn_1, tensor_algn_2, tensor_algn_3
756 : TYPE(dbt_type), POINTER :: tensor_crop_1, tensor_crop_2
757 : TYPE(dbt_type), POINTER :: tensor_small, tensor_large
758 :
759 : LOGICAL :: assert_stmt, tensors_remapped
760 : INTEGER :: max_mm_dim, max_tensor, &
761 : unit_nr_prv, ref_tensor, handle
762 257420 : TYPE(mp_cart_type) :: mp_comm_opt
763 514840 : INTEGER, DIMENSION(SIZE(contract_1)) :: contract_1_mod
764 514840 : INTEGER, DIMENSION(SIZE(notcontract_1)) :: notcontract_1_mod
765 514840 : INTEGER, DIMENSION(SIZE(contract_2)) :: contract_2_mod
766 514840 : INTEGER, DIMENSION(SIZE(notcontract_2)) :: notcontract_2_mod
767 514840 : INTEGER, DIMENSION(SIZE(map_1)) :: map_1_mod
768 514840 : INTEGER, DIMENSION(SIZE(map_2)) :: map_2_mod
769 : LOGICAL :: trans_1, trans_2, trans_3
770 : LOGICAL :: new_1, new_2, new_3, move_data_1, move_data_2
771 : INTEGER :: ndims1, ndims2, ndims3
772 : INTEGER, DIMENSION(2) :: occ_1, occ_2 ! blocks in bounds, sticking out
773 257420 : INTEGER, DIMENSION(:), ALLOCATABLE :: dims1, dims2, dims3
774 :
775 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_contract'
776 257420 : CHARACTER(LEN=1), DIMENSION(:), ALLOCATABLE :: indchar1, indchar2, indchar3, indchar1_mod, &
777 257420 : indchar2_mod, indchar3_mod
778 : CHARACTER(LEN=1), DIMENSION(15), SAVE :: alph = &
779 : ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o']
780 514840 : INTEGER, DIMENSION(2, ndims_tensor(tensor_1)) :: bounds_t1
781 514840 : INTEGER, DIMENSION(2, ndims_tensor(tensor_2)) :: bounds_t2
782 : LOGICAL :: do_crop_1, do_crop_2, do_write_3, nodata_3, do_batched, pgrid_changed, &
783 : pgrid_changed_any, do_change_pgrid(2)
784 3346460 : TYPE(dbt_tas_split_info) :: split_opt, split, split_opt_avg
785 : INTEGER, DIMENSION(2) :: pdims_2d_opt, pdims_sub, pdims_sub_opt
786 : REAL(dp) :: pdim_ratio, pdim_ratio_opt
787 : INTEGER :: istat
788 : INTEGER(KIND=int_8) :: flop_prv
789 514840 : INTEGER, DIMENSION(ndims_tensor(tensor_1)) :: order_1
790 514840 : INTEGER, DIMENSION(ndims_tensor(tensor_2)) :: order_2
791 257420 : INTEGER, DIMENSION(:, :), ALLOCATABLE :: bounds_a1, bounds_a2 ! unallocated means absent
792 : LOGICAL :: cropped_1, cropped_2
793 :
794 257420 : NULLIFY (tensor_contr_1, tensor_contr_2, tensor_contr_3, tensor_crop_1, tensor_crop_2, &
795 257420 : tensor_small)
796 :
797 257420 : CALL timeset(routineN, handle)
798 :
799 257420 : CPASSERT(tensor_1%valid)
800 257420 : CPASSERT(tensor_2%valid)
801 257420 : CPASSERT(tensor_3%valid)
802 :
803 257420 : assert_stmt = SIZE(contract_1) == SIZE(contract_2)
804 257420 : CPASSERT(assert_stmt)
805 :
806 257420 : assert_stmt = SIZE(map_1) == SIZE(notcontract_1)
807 257420 : CPASSERT(assert_stmt)
808 :
809 257420 : assert_stmt = SIZE(map_2) == SIZE(notcontract_2)
810 257420 : CPASSERT(assert_stmt)
811 :
812 257420 : assert_stmt = SIZE(notcontract_1) + SIZE(contract_1) == ndims_tensor(tensor_1)
813 257420 : CPASSERT(assert_stmt)
814 :
815 257420 : assert_stmt = SIZE(notcontract_2) + SIZE(contract_2) == ndims_tensor(tensor_2)
816 257420 : CPASSERT(assert_stmt)
817 :
818 257420 : assert_stmt = SIZE(map_1) + SIZE(map_2) == ndims_tensor(tensor_3)
819 257420 : CPASSERT(assert_stmt)
820 :
821 257420 : unit_nr_prv = prep_output_unit(unit_nr)
822 :
823 257420 : IF (PRESENT(flop)) flop = 0
824 257420 : IF (PRESENT(nblks_local)) nblks_local = 0
825 :
826 257420 : IF (PRESENT(move_data)) THEN
827 61916 : move_data_1 = move_data
828 61916 : move_data_2 = move_data
829 : ELSE
830 195504 : move_data_1 = .FALSE.
831 195504 : move_data_2 = .FALSE.
832 : END IF
833 :
834 257420 : nodata_3 = .TRUE.
835 257420 : IF (PRESENT(retain_sparsity)) THEN
836 4930 : IF (retain_sparsity) nodata_3 = .FALSE.
837 : END IF
838 :
839 : CALL dbt_map_bounds_to_tensors(tensor_1, tensor_2, &
840 : contract_1, notcontract_1, &
841 : contract_2, notcontract_2, &
842 : bounds_t1, bounds_t2, &
843 : bounds_1=bounds_1, bounds_2=bounds_2, bounds_3=bounds_3, &
844 257420 : do_crop_1=do_crop_1, do_crop_2=do_crop_2)
845 :
846 : ! cropping is deferred: a tensor that is remapped is cropped while copied (single copy)
847 257420 : tensor_crop_1 => tensor_1
848 257420 : tensor_crop_2 => tensor_2
849 257420 : cropped_1 = .FALSE.
850 257420 : cropped_2 = .FALSE.
851 :
852 257420 : istat = contract_stats_index(tensor_1%name, tensor_2%name, tensor_3%name)
853 : ASSOCIATE (s => contract_stats(istat))
854 257420 : s%ncalls = s%ncalls + 1
855 : END ASSOCIATE
856 :
857 : ! shortcut for empty tensors
858 : ! this is needed to avoid unnecessary work in case user contracts different portions of a
859 : ! tensor consecutively to save memory
860 : ASSOCIATE (mp_comm => tensor_crop_1%pgrid%mp_comm_2d)
861 257420 : IF (do_crop_1) THEN
862 86941 : occ_1 = num_blocks_in_bounds(tensor_1, bounds_t1)
863 : ELSE
864 511437 : occ_1 = [dbt_get_num_blocks(tensor_1), 0]
865 : END IF
866 257420 : CALL mp_comm%max(occ_1)
867 257420 : IF (do_crop_2) THEN
868 82861 : occ_2 = num_blocks_in_bounds(tensor_2, bounds_t2)
869 : ELSE
870 523677 : occ_2 = [dbt_get_num_blocks(tensor_2), 0]
871 : END IF
872 514840 : CALL mp_comm%max(occ_2)
873 : END ASSOCIATE
874 :
875 257420 : IF (occ_1(1) == 0 .OR. occ_2(1) == 0) THEN
876 34916 : contract_stats(istat)%nempty = contract_stats(istat)%nempty + 1
877 34916 : CALL dbt_scale(tensor_3, beta)
878 : ! as if cropped with move_data (tensor data was transferred into the crop)
879 34916 : IF (do_crop_1 .AND. move_data_1) CALL dbt_clear(tensor_1)
880 34916 : IF (do_crop_2 .AND. move_data_2) CALL dbt_clear(tensor_2)
881 :
882 34916 : CALL timestop(handle)
883 34916 : RETURN
884 : END IF
885 :
886 : ! no block sticks out on any rank: a crop would copy the tensor as is
887 222504 : do_crop_1 = do_crop_1 .AND. occ_1(2) > 0
888 222504 : do_crop_2 = do_crop_2 .AND. occ_2(2) > 0
889 : ASSOCIATE (s => contract_stats(istat))
890 34900 : IF (do_crop_1) s%ncrop(1) = s%ncrop(1) + 1
891 257404 : IF (do_crop_2) s%ncrop(2) = s%ncrop(2) + 1
892 : END ASSOCIATE
893 :
894 222504 : IF (unit_nr_prv /= 0) THEN
895 55120 : IF (unit_nr_prv > 0) THEN
896 10 : WRITE (unit_nr_prv, '(A)') repeat("-", 80)
897 10 : WRITE (unit_nr_prv, '(A,1X,A,1X,A,1X,A,1X,A,1X,A)') "DBT TENSOR CONTRACTION:", &
898 20 : TRIM(tensor_crop_1%name), 'x', TRIM(tensor_crop_2%name), '=', TRIM(tensor_3%name)
899 10 : WRITE (unit_nr_prv, '(A)') repeat("-", 80)
900 : END IF
901 55120 : CALL dbt_write_tensor_info(tensor_crop_1, unit_nr_prv, full_info=log_verbose)
902 55120 : CALL dbt_write_tensor_dist(tensor_crop_1, unit_nr_prv)
903 55120 : CALL dbt_write_tensor_info(tensor_crop_2, unit_nr_prv, full_info=log_verbose)
904 55120 : CALL dbt_write_tensor_dist(tensor_crop_2, unit_nr_prv)
905 : END IF
906 :
907 : ! align tensor index with data, tensor data is not modified
908 222504 : ndims1 = ndims_tensor(tensor_crop_1)
909 222504 : ndims2 = ndims_tensor(tensor_crop_2)
910 222504 : ndims3 = ndims_tensor(tensor_3)
911 890016 : ALLOCATE (indchar1(ndims1), indchar1_mod(ndims1))
912 890016 : ALLOCATE (indchar2(ndims2), indchar2_mod(ndims2))
913 890016 : ALLOCATE (indchar3(ndims3), indchar3_mod(ndims3))
914 :
915 : ! labeling tensor index with letters
916 :
917 1923816 : indchar1([notcontract_1, contract_1]) = alph(1:ndims1) ! arb. choice
918 535710 : indchar2(notcontract_2) = alph(ndims1 + 1:ndims1 + SIZE(notcontract_2)) ! arb. choice
919 519825 : indchar2(contract_2) = indchar1(contract_1)
920 492287 : indchar3(map_1) = indchar1(notcontract_1)
921 535710 : indchar3(map_2) = indchar2(notcontract_2)
922 :
923 222504 : IF (unit_nr_prv /= 0) CALL dbt_print_contraction_index(tensor_crop_1, indchar1, &
924 : tensor_crop_2, indchar2, &
925 55120 : tensor_3, indchar3, unit_nr_prv)
926 222504 : IF (unit_nr_prv > 0) THEN
927 10 : WRITE (unit_nr_prv, '(T2,A)') "aligning tensor index with data"
928 : END IF
929 :
930 : CALL align_tensor(tensor_crop_1, contract_1, notcontract_1, &
931 222504 : tensor_algn_1, contract_1_mod, notcontract_1_mod, indchar1, indchar1_mod, order_1)
932 :
933 : CALL align_tensor(tensor_crop_2, contract_2, notcontract_2, &
934 222504 : tensor_algn_2, contract_2_mod, notcontract_2_mod, indchar2, indchar2_mod, order_2)
935 :
936 : ! bounds in the aligned index order (index i of tensor_X is index order_X(i) of tensor_algn_X)
937 222504 : IF (do_crop_1) THEN
938 104700 : ALLOCATE (bounds_a1(2, ndims_tensor(tensor_1)))
939 248392 : bounds_a1(:, order_1) = bounds_t1(:, :)
940 : END IF
941 222504 : IF (do_crop_2) THEN
942 120780 : ALLOCATE (bounds_a2(2, ndims_tensor(tensor_2)))
943 366798 : bounds_a2(:, order_2) = bounds_t2(:, :)
944 : END IF
945 :
946 : CALL align_tensor(tensor_3, map_1, map_2, &
947 222504 : tensor_algn_3, map_1_mod, map_2_mod, indchar3, indchar3_mod)
948 :
949 222504 : IF (unit_nr_prv /= 0) CALL dbt_print_contraction_index(tensor_algn_1, indchar1_mod, &
950 : tensor_algn_2, indchar2_mod, &
951 55120 : tensor_algn_3, indchar3_mod, unit_nr_prv)
952 :
953 667512 : ALLOCATE (dims1(ndims1))
954 667512 : ALLOCATE (dims2(ndims2))
955 667512 : ALLOCATE (dims3(ndims3))
956 :
957 : ! ideally we should consider block sizes and occupancy to measure tensor sizes but current solution should work for most
958 : ! cases and is more elegant. Note that we can not easily consider occupancy since it is unknown for result tensor
959 222504 : CALL blk_dims_tensor(tensor_crop_1, dims1)
960 222504 : CALL blk_dims_tensor(tensor_crop_2, dims2)
961 222504 : CALL blk_dims_tensor(tensor_3, dims3)
962 :
963 : max_mm_dim = MAXLOC([PRODUCT(INT(dims1(notcontract_1), int_8)), &
964 : PRODUCT(INT(dims1(contract_1), int_8)), &
965 1770326 : PRODUCT(INT(dims2(notcontract_2), int_8))], DIM=1)
966 2650636 : max_tensor = MAXLOC([PRODUCT(INT(dims1, int_8)), PRODUCT(INT(dims2, int_8)), PRODUCT(INT(dims3, int_8))], DIM=1)
967 57065 : SELECT CASE (max_mm_dim)
968 : CASE (1)
969 57065 : IF (unit_nr_prv > 0) THEN
970 3 : WRITE (unit_nr_prv, '(T2,A)') "large tensors: 1, 3; small tensor: 2"
971 3 : WRITE (unit_nr_prv, '(T2,A)') "sorting contraction indices"
972 : END IF
973 57065 : CALL index_linked_sort(contract_1_mod, contract_2_mod)
974 57065 : CALL index_linked_sort(map_2_mod, notcontract_2_mod)
975 56577 : SELECT CASE (max_tensor)
976 : CASE (1)
977 56577 : CALL index_linked_sort(notcontract_1_mod, map_1_mod)
978 : CASE (3)
979 488 : CALL index_linked_sort(map_1_mod, notcontract_1_mod)
980 : CASE DEFAULT
981 57065 : CPABORT("should not happen")
982 : END SELECT
983 :
984 : CALL reshape_mm_compatible(tensor_algn_1, tensor_algn_3, tensor_contr_1, tensor_contr_3, &
985 : contract_1_mod, notcontract_1_mod, map_2_mod, map_1_mod, &
986 : trans_1, trans_3, new_1, new_3, ref_tensor, nodata2=nodata_3, optimize_dist=optimize_dist, &
987 : move_data_1=move_data_1, unit_nr=unit_nr_prv, &
988 57065 : bounds1=bounds_a1, crop1=cropped_1)
989 :
990 : CALL reshape_mm_small(tensor_algn_2, contract_2_mod, notcontract_2_mod, tensor_contr_2, trans_2, &
991 : new_2, move_data=move_data_2, unit_nr=unit_nr_prv, &
992 57065 : bounds=bounds_a2, crop=cropped_2)
993 :
994 56577 : SELECT CASE (ref_tensor)
995 : CASE (1)
996 56577 : tensor_large => tensor_contr_1
997 : CASE (2)
998 57065 : tensor_large => tensor_contr_3
999 : END SELECT
1000 57065 : tensor_small => tensor_contr_2
1001 :
1002 : CASE (2)
1003 75589 : IF (unit_nr_prv > 0) THEN
1004 5 : WRITE (unit_nr_prv, '(T2,A)') "large tensors: 1, 2; small tensor: 3"
1005 5 : WRITE (unit_nr_prv, '(T2,A)') "sorting contraction indices"
1006 : END IF
1007 :
1008 75589 : CALL index_linked_sort(notcontract_1_mod, map_1_mod)
1009 75589 : CALL index_linked_sort(notcontract_2_mod, map_2_mod)
1010 74403 : SELECT CASE (max_tensor)
1011 : CASE (1)
1012 74403 : CALL index_linked_sort(contract_1_mod, contract_2_mod)
1013 : CASE (2)
1014 1186 : CALL index_linked_sort(contract_2_mod, contract_1_mod)
1015 : CASE DEFAULT
1016 75589 : CPABORT("should not happen")
1017 : END SELECT
1018 :
1019 : CALL reshape_mm_compatible(tensor_algn_1, tensor_algn_2, tensor_contr_1, tensor_contr_2, &
1020 : notcontract_1_mod, contract_1_mod, notcontract_2_mod, contract_2_mod, &
1021 : trans_1, trans_2, new_1, new_2, ref_tensor, optimize_dist=optimize_dist, &
1022 : move_data_1=move_data_1, move_data_2=move_data_2, unit_nr=unit_nr_prv, &
1023 75589 : bounds1=bounds_a1, bounds2=bounds_a2, crop1=cropped_1, crop2=cropped_2)
1024 75589 : trans_1 = .NOT. trans_1
1025 :
1026 : CALL reshape_mm_small(tensor_algn_3, map_1_mod, map_2_mod, tensor_contr_3, trans_3, &
1027 75589 : new_3, nodata=nodata_3, unit_nr=unit_nr_prv)
1028 :
1029 74403 : SELECT CASE (ref_tensor)
1030 : CASE (1)
1031 74403 : tensor_large => tensor_contr_1
1032 : CASE (2)
1033 75589 : tensor_large => tensor_contr_2
1034 : END SELECT
1035 75589 : tensor_small => tensor_contr_3
1036 :
1037 : CASE (3)
1038 89850 : IF (unit_nr_prv > 0) THEN
1039 2 : WRITE (unit_nr_prv, '(T2,A)') "large tensors: 2, 3; small tensor: 1"
1040 2 : WRITE (unit_nr_prv, '(T2,A)') "sorting contraction indices"
1041 : END IF
1042 89850 : CALL index_linked_sort(map_1_mod, notcontract_1_mod)
1043 89850 : CALL index_linked_sort(contract_2_mod, contract_1_mod)
1044 89390 : SELECT CASE (max_tensor)
1045 : CASE (2)
1046 89390 : CALL index_linked_sort(notcontract_2_mod, map_2_mod)
1047 : CASE (3)
1048 460 : CALL index_linked_sort(map_2_mod, notcontract_2_mod)
1049 : CASE DEFAULT
1050 89850 : CPABORT("should not happen")
1051 : END SELECT
1052 :
1053 : CALL reshape_mm_compatible(tensor_algn_2, tensor_algn_3, tensor_contr_2, tensor_contr_3, &
1054 : contract_2_mod, notcontract_2_mod, map_1_mod, map_2_mod, &
1055 : trans_2, trans_3, new_2, new_3, ref_tensor, nodata2=nodata_3, optimize_dist=optimize_dist, &
1056 : move_data_1=move_data_2, unit_nr=unit_nr_prv, &
1057 89850 : bounds1=bounds_a2, crop1=cropped_2)
1058 :
1059 89850 : trans_2 = .NOT. trans_2
1060 89850 : trans_3 = .NOT. trans_3
1061 :
1062 : CALL reshape_mm_small(tensor_algn_1, notcontract_1_mod, contract_1_mod, tensor_contr_1, &
1063 : trans_1, new_1, move_data=move_data_1, unit_nr=unit_nr_prv, &
1064 89850 : bounds=bounds_a1, crop=cropped_1)
1065 :
1066 89390 : SELECT CASE (ref_tensor)
1067 : CASE (1)
1068 89390 : tensor_large => tensor_contr_2
1069 : CASE (2)
1070 89850 : tensor_large => tensor_contr_3
1071 : END SELECT
1072 312354 : tensor_small => tensor_contr_1
1073 :
1074 : END SELECT
1075 :
1076 222504 : IF (unit_nr_prv /= 0) CALL dbt_print_contraction_index(tensor_contr_1, indchar1_mod, &
1077 : tensor_contr_2, indchar2_mod, &
1078 55120 : tensor_contr_3, indchar3_mod, unit_nr_prv)
1079 222504 : IF (unit_nr_prv /= 0) THEN
1080 55120 : IF (new_1) CALL dbt_write_tensor_info(tensor_contr_1, unit_nr_prv, full_info=log_verbose)
1081 55120 : IF (new_1) CALL dbt_write_tensor_dist(tensor_contr_1, unit_nr_prv)
1082 55120 : IF (new_2) CALL dbt_write_tensor_info(tensor_contr_2, unit_nr_prv, full_info=log_verbose)
1083 55120 : IF (new_2) CALL dbt_write_tensor_dist(tensor_contr_2, unit_nr_prv)
1084 : END IF
1085 :
1086 : ASSOCIATE (s => contract_stats(istat))
1087 34900 : IF (do_crop_1) s%nzcrop(1) = s%nzcrop(1) + dbt_get_nze(tensor_contr_1)
1088 222504 : IF (do_crop_2) s%nzcrop(2) = s%nzcrop(2) + dbt_get_nze(tensor_contr_2)
1089 222504 : IF (new_1) THEN
1090 23228 : s%nremap(1) = s%nremap(1) + 1
1091 23228 : s%nzremap(1) = s%nzremap(1) + dbt_get_nze(tensor_contr_1)
1092 23228 : IF (do_crop_1) s%ncrop_remap(1) = s%ncrop_remap(1) + 1
1093 : END IF
1094 445008 : IF (new_2) THEN
1095 11157 : s%nremap(2) = s%nremap(2) + 1
1096 11157 : s%nzremap(2) = s%nzremap(2) + dbt_get_nze(tensor_contr_2)
1097 11157 : IF (do_crop_2) s%ncrop_remap(2) = s%ncrop_remap(2) + 1
1098 : END IF
1099 : END ASSOCIATE
1100 :
1101 : CALL dbt_tas_multiply(trans_1, trans_2, trans_3, alpha, &
1102 : tensor_contr_1%matrix_rep, tensor_contr_2%matrix_rep, &
1103 : beta, &
1104 : tensor_contr_3%matrix_rep, filter_eps=filter_eps, flop=flop_prv, &
1105 : unit_nr=unit_nr_prv, log_verbose=log_verbose, &
1106 : split_opt=split_opt, &
1107 222504 : move_data_a=move_data_1, move_data_b=move_data_2, retain_sparsity=retain_sparsity)
1108 222504 : IF (PRESENT(flop)) flop = flop_prv
1109 : ASSOCIATE (s => contract_stats(istat))
1110 222504 : s%flop = s%flop + flop_prv
1111 222504 : IF (new_3) THEN ! result is copied back into tensor_3
1112 23276 : s%nremap(3) = s%nremap(3) + 1
1113 23276 : s%nzremap(3) = s%nzremap(3) + dbt_get_nze(tensor_contr_3)
1114 : END IF
1115 : END ASSOCIATE
1116 :
1117 222504 : IF (PRESENT(pgrid_opt_1)) THEN
1118 0 : IF (.NOT. new_1) THEN
1119 0 : ALLOCATE (pgrid_opt_1)
1120 0 : pgrid_opt_1 = opt_pgrid(tensor_1, split_opt)
1121 : END IF
1122 : END IF
1123 :
1124 222504 : IF (PRESENT(pgrid_opt_2)) THEN
1125 0 : IF (.NOT. new_2) THEN
1126 0 : ALLOCATE (pgrid_opt_2)
1127 0 : pgrid_opt_2 = opt_pgrid(tensor_2, split_opt)
1128 : END IF
1129 : END IF
1130 :
1131 222504 : IF (PRESENT(pgrid_opt_3)) THEN
1132 0 : IF (.NOT. new_3) THEN
1133 0 : ALLOCATE (pgrid_opt_3)
1134 0 : pgrid_opt_3 = opt_pgrid(tensor_3, split_opt)
1135 : END IF
1136 : END IF
1137 :
1138 222504 : do_batched = tensor_small%matrix_rep%do_batched > 0
1139 :
1140 222504 : tensors_remapped = .FALSE.
1141 222504 : IF (new_1 .OR. new_2 .OR. new_3) tensors_remapped = .TRUE.
1142 :
1143 45627 : IF (tensors_remapped .AND. do_batched) THEN
1144 : CALL cp_warn(__LOCATION__, &
1145 0 : "Internal process grid optimization disabled because tensors are not in contraction-compatible format")
1146 : END IF
1147 :
1148 : ! optimize process grid during batched contraction
1149 222504 : do_change_pgrid(:) = .FALSE.
1150 222504 : IF ((.NOT. tensors_remapped) .AND. do_batched) THEN
1151 : ASSOCIATE (storage => tensor_small%contraction_storage)
1152 0 : CPASSERT(storage%static)
1153 93532 : split = dbt_tas_info(tensor_large%matrix_rep)
1154 : do_change_pgrid(:) = &
1155 93532 : update_contraction_storage(storage, split_opt, split)
1156 :
1157 372780 : IF (ANY(do_change_pgrid)) THEN
1158 674 : mp_comm_opt = dbt_tas_mp_comm(tensor_small%pgrid%mp_comm_2d, split_opt%split_rowcol, NINT(storage%nsplit_avg))
1159 : CALL dbt_tas_create_split(split_opt_avg, mp_comm_opt, split_opt%split_rowcol, &
1160 674 : NINT(storage%nsplit_avg), own_comm=.TRUE.)
1161 2022 : pdims_2d_opt = split_opt_avg%mp_comm%num_pe_cart
1162 : END IF
1163 :
1164 : END ASSOCIATE
1165 :
1166 93532 : IF (do_change_pgrid(1) .AND. .NOT. do_change_pgrid(2)) THEN
1167 : ! check if new grid has better subgrid, if not there is no need to change process grid
1168 2022 : pdims_sub_opt = split_opt_avg%mp_comm_group%num_pe_cart
1169 2022 : pdims_sub = split%mp_comm_group%num_pe_cart
1170 :
1171 2696 : pdim_ratio = MAXVAL(REAL(pdims_sub, dp))/MINVAL(pdims_sub)
1172 2696 : pdim_ratio_opt = MAXVAL(REAL(pdims_sub_opt, dp))/MINVAL(pdims_sub_opt)
1173 674 : IF (pdim_ratio/pdim_ratio_opt <= default_pdims_accept_ratio**2) THEN
1174 0 : do_change_pgrid(1) = .FALSE.
1175 0 : CALL dbt_tas_release_info(split_opt_avg)
1176 : END IF
1177 : END IF
1178 : END IF
1179 :
1180 222504 : IF (unit_nr_prv /= 0) THEN
1181 55120 : do_write_3 = .TRUE.
1182 55120 : IF (tensor_contr_3%matrix_rep%do_batched > 0) THEN
1183 23146 : IF (tensor_contr_3%matrix_rep%mm_storage%batched_out) do_write_3 = .FALSE.
1184 : END IF
1185 : IF (do_write_3) THEN
1186 32034 : CALL dbt_write_tensor_info(tensor_contr_3, unit_nr_prv, full_info=log_verbose)
1187 32034 : CALL dbt_write_tensor_dist(tensor_contr_3, unit_nr_prv)
1188 : END IF
1189 : END IF
1190 :
1191 222504 : IF (new_3) THEN
1192 : ! need redistribute if we created new tensor for tensor 3
1193 23276 : CALL dbt_scale(tensor_algn_3, beta)
1194 23276 : CALL dbt_copy_expert(tensor_contr_3, tensor_algn_3, summation=.TRUE., move_data=.TRUE.)
1195 23276 : IF (PRESENT(filter_eps)) CALL dbt_filter(tensor_algn_3, filter_eps)
1196 : ! tensor_3 automatically has correct data because tensor_algn_3 contains a matrix
1197 : ! pointer to data of tensor_3
1198 : END IF
1199 :
1200 : ! transfer contraction storage
1201 222504 : CALL dbt_copy_contraction_storage(tensor_contr_1, tensor_1)
1202 222504 : CALL dbt_copy_contraction_storage(tensor_contr_2, tensor_2)
1203 222504 : CALL dbt_copy_contraction_storage(tensor_contr_3, tensor_3)
1204 :
1205 222504 : IF (unit_nr_prv /= 0) THEN
1206 55120 : IF (new_3 .AND. do_write_3) CALL dbt_write_tensor_info(tensor_3, unit_nr_prv, full_info=log_verbose)
1207 55120 : IF (new_3 .AND. do_write_3) CALL dbt_write_tensor_dist(tensor_3, unit_nr_prv)
1208 : END IF
1209 :
1210 222504 : CALL dbt_destroy(tensor_algn_1)
1211 222504 : CALL dbt_destroy(tensor_algn_2)
1212 222504 : CALL dbt_destroy(tensor_algn_3)
1213 :
1214 222504 : IF (new_1 .OR. cropped_1) THEN
1215 58126 : CALL dbt_destroy(tensor_contr_1)
1216 58126 : DEALLOCATE (tensor_contr_1)
1217 : END IF
1218 222504 : IF (new_2 .OR. cropped_2) THEN
1219 51415 : CALL dbt_destroy(tensor_contr_2)
1220 51415 : DEALLOCATE (tensor_contr_2)
1221 : END IF
1222 222504 : IF (new_3) THEN
1223 23276 : CALL dbt_destroy(tensor_contr_3)
1224 23276 : DEALLOCATE (tensor_contr_3)
1225 : END IF
1226 :
1227 222504 : IF (PRESENT(move_data)) THEN
1228 56059 : IF (move_data) THEN
1229 39640 : CALL dbt_clear(tensor_1)
1230 39640 : CALL dbt_clear(tensor_2)
1231 : END IF
1232 : END IF
1233 :
1234 222504 : IF (unit_nr_prv > 0) THEN
1235 10 : WRITE (unit_nr_prv, '(A)') repeat("-", 80)
1236 10 : WRITE (unit_nr_prv, '(A)') "TENSOR CONTRACTION DONE"
1237 10 : WRITE (unit_nr_prv, '(A)') repeat("-", 80)
1238 : END IF
1239 :
1240 666164 : IF (ANY(do_change_pgrid)) THEN
1241 674 : pgrid_changed_any = .FALSE.
1242 0 : SELECT CASE (max_mm_dim)
1243 : CASE (1)
1244 0 : IF (ALLOCATED(tensor_1%contraction_storage) .AND. ALLOCATED(tensor_3%contraction_storage)) THEN
1245 : CALL dbt_change_pgrid_2d(tensor_1, tensor_1%pgrid%mp_comm_2d, pdims=pdims_2d_opt, &
1246 : nsplit=split_opt_avg%ngroup, dimsplit=split_opt_avg%split_rowcol, &
1247 : pgrid_changed=pgrid_changed, &
1248 0 : unit_nr=unit_nr_prv)
1249 0 : IF (pgrid_changed) pgrid_changed_any = .TRUE.
1250 : CALL dbt_change_pgrid_2d(tensor_3, tensor_3%pgrid%mp_comm_2d, pdims=pdims_2d_opt, &
1251 : nsplit=split_opt_avg%ngroup, dimsplit=split_opt_avg%split_rowcol, &
1252 : pgrid_changed=pgrid_changed, &
1253 0 : unit_nr=unit_nr_prv)
1254 0 : IF (pgrid_changed) pgrid_changed_any = .TRUE.
1255 : END IF
1256 0 : IF (pgrid_changed_any) THEN
1257 0 : IF (tensor_2%matrix_rep%do_batched == 3) THEN
1258 : ! set flag that process grid has been optimized to make sure that no grid optimizations are done
1259 : ! in TAS multiply algorithm
1260 0 : CALL dbt_tas_batched_mm_complete(tensor_2%matrix_rep)
1261 : END IF
1262 : END IF
1263 : CASE (2)
1264 184 : IF (ALLOCATED(tensor_1%contraction_storage) .AND. ALLOCATED(tensor_2%contraction_storage)) THEN
1265 : CALL dbt_change_pgrid_2d(tensor_1, tensor_1%pgrid%mp_comm_2d, pdims=pdims_2d_opt, &
1266 : nsplit=split_opt_avg%ngroup, dimsplit=split_opt_avg%split_rowcol, &
1267 : pgrid_changed=pgrid_changed, &
1268 184 : unit_nr=unit_nr_prv)
1269 184 : IF (pgrid_changed) pgrid_changed_any = .TRUE.
1270 : CALL dbt_change_pgrid_2d(tensor_2, tensor_2%pgrid%mp_comm_2d, pdims=pdims_2d_opt, &
1271 : nsplit=split_opt_avg%ngroup, dimsplit=split_opt_avg%split_rowcol, &
1272 : pgrid_changed=pgrid_changed, &
1273 184 : unit_nr=unit_nr_prv)
1274 184 : IF (pgrid_changed) pgrid_changed_any = .TRUE.
1275 : END IF
1276 8 : IF (pgrid_changed_any) THEN
1277 184 : IF (tensor_3%matrix_rep%do_batched == 3) THEN
1278 172 : CALL dbt_tas_batched_mm_complete(tensor_3%matrix_rep)
1279 : END IF
1280 : END IF
1281 : CASE (3)
1282 490 : IF (ALLOCATED(tensor_2%contraction_storage) .AND. ALLOCATED(tensor_3%contraction_storage)) THEN
1283 : CALL dbt_change_pgrid_2d(tensor_2, tensor_2%pgrid%mp_comm_2d, pdims=pdims_2d_opt, &
1284 : nsplit=split_opt_avg%ngroup, dimsplit=split_opt_avg%split_rowcol, &
1285 : pgrid_changed=pgrid_changed, &
1286 222 : unit_nr=unit_nr_prv)
1287 222 : IF (pgrid_changed) pgrid_changed_any = .TRUE.
1288 : CALL dbt_change_pgrid_2d(tensor_3, tensor_3%pgrid%mp_comm_2d, pdims=pdims_2d_opt, &
1289 : nsplit=split_opt_avg%ngroup, dimsplit=split_opt_avg%split_rowcol, &
1290 : pgrid_changed=pgrid_changed, &
1291 222 : unit_nr=unit_nr_prv)
1292 222 : IF (pgrid_changed) pgrid_changed_any = .TRUE.
1293 : END IF
1294 674 : IF (pgrid_changed_any) THEN
1295 222 : IF (tensor_1%matrix_rep%do_batched == 3) THEN
1296 222 : CALL dbt_tas_batched_mm_complete(tensor_1%matrix_rep)
1297 : END IF
1298 : END IF
1299 : END SELECT
1300 674 : CALL dbt_tas_release_info(split_opt_avg)
1301 : END IF
1302 :
1303 222504 : IF ((.NOT. tensors_remapped) .AND. do_batched) THEN
1304 : ! freeze TAS process grids if tensor grids were optimized
1305 93532 : CALL dbt_tas_set_batched_state(tensor_1%matrix_rep, opt_grid=.TRUE.)
1306 93532 : CALL dbt_tas_set_batched_state(tensor_2%matrix_rep, opt_grid=.TRUE.)
1307 93532 : CALL dbt_tas_set_batched_state(tensor_3%matrix_rep, opt_grid=.TRUE.)
1308 : END IF
1309 :
1310 222504 : CALL dbt_tas_release_info(split_opt)
1311 :
1312 222504 : CALL timestop(handle)
1313 :
1314 737344 : END SUBROUTINE dbt_contract_expert
1315 :
1316 : ! **************************************************************************************************
1317 : !> \brief align tensor index with data
1318 : !> \author Patrick Seewald
1319 : ! **************************************************************************************************
1320 6007608 : SUBROUTINE align_tensor(tensor_in, contract_in, notcontract_in, &
1321 667512 : tensor_out, contract_out, notcontract_out, indp_in, indp_out, order)
1322 : TYPE(dbt_type), INTENT(INOUT) :: tensor_in
1323 : INTEGER, DIMENSION(:), INTENT(IN) :: contract_in, notcontract_in
1324 : TYPE(dbt_type), INTENT(OUT) :: tensor_out
1325 : INTEGER, DIMENSION(SIZE(contract_in)), &
1326 : INTENT(OUT) :: contract_out
1327 : INTEGER, DIMENSION(SIZE(notcontract_in)), &
1328 : INTENT(OUT) :: notcontract_out
1329 : CHARACTER(LEN=1), DIMENSION(ndims_tensor(tensor_in)), INTENT(IN) :: indp_in
1330 : CHARACTER(LEN=1), DIMENSION(ndims_tensor(tensor_in)), INTENT(OUT) :: indp_out
1331 : INTEGER, DIMENSION(ndims_tensor(tensor_in)), INTENT(OUT), OPTIONAL :: order
1332 667512 : INTEGER, DIMENSION(ndims_tensor(tensor_in)) :: align
1333 :
1334 667512 : CALL dbt_align_index(tensor_in, tensor_out, order=align)
1335 1845143 : IF (PRESENT(order)) order = align
1336 1531937 : contract_out = align(contract_in)
1337 1563707 : notcontract_out = align(notcontract_in)
1338 2428132 : indp_out(align) = indp_in
1339 :
1340 667512 : END SUBROUTINE align_tensor
1341 :
1342 : ! **************************************************************************************************
1343 : !> \brief Prepare tensor for contraction: redistribute to a 2d format which can be contracted by
1344 : !> matrix multiplication. This routine reshapes the two largest of the three tensors.
1345 : !> Redistribution is avoided if tensors already in a consistent layout.
1346 : !> \param ind1_free indices of tensor 1 that are "free" (not linked to any index of tensor 2)
1347 : !> \param ind1_linked indices of tensor 1 that are linked to indices of tensor 2
1348 : !> 1:1 correspondence with ind1_linked
1349 : !> \param trans1 transpose flag of matrix rep. tensor 1
1350 : !> \param trans2 transpose flag of matrix rep. tensor 2
1351 : !> \param new1 whether a new tensor 1 was created
1352 : !> \param new2 whether a new tensor 2 was created
1353 : !> \param nodata1 don't copy data of tensor 1
1354 : !> \param nodata2 don't copy data of tensor 2
1355 : !> \param move_data_1 memory optimization: transfer data s.t. tensor1 may be empty on return
1356 : !> \param move_data_2 memory optimization: transfer data s.t. tensor2 may be empty on return
1357 : !> \param optimize_dist experimental: optimize distribution
1358 : !> \param unit_nr output unit
1359 : !> \author Patrick Seewald
1360 : ! **************************************************************************************************
1361 222504 : SUBROUTINE reshape_mm_compatible(tensor1, tensor2, tensor1_out, tensor2_out, ind1_free, ind1_linked, &
1362 222504 : ind2_free, ind2_linked, trans1, trans2, new1, new2, ref_tensor, &
1363 : nodata1, nodata2, move_data_1, &
1364 : move_data_2, optimize_dist, unit_nr, bounds1, bounds2, crop1, crop2)
1365 : TYPE(dbt_type), TARGET, INTENT(INOUT) :: tensor1
1366 : TYPE(dbt_type), TARGET, INTENT(INOUT) :: tensor2
1367 : TYPE(dbt_type), POINTER, INTENT(OUT) :: tensor1_out, tensor2_out
1368 : INTEGER, DIMENSION(:), INTENT(IN) :: ind1_free, ind2_free
1369 : INTEGER, DIMENSION(:), INTENT(IN) :: ind1_linked, ind2_linked
1370 : LOGICAL, INTENT(OUT) :: trans1, trans2
1371 : LOGICAL, INTENT(OUT) :: new1, new2
1372 : INTEGER, INTENT(OUT) :: ref_tensor
1373 : LOGICAL, INTENT(IN), OPTIONAL :: nodata1, nodata2
1374 : LOGICAL, INTENT(INOUT), OPTIONAL :: move_data_1, move_data_2
1375 : LOGICAL, INTENT(IN), OPTIONAL :: optimize_dist
1376 : INTEGER, INTENT(IN), OPTIONAL :: unit_nr
1377 : INTEGER, DIMENSION(2, ndims_tensor(tensor1)), INTENT(IN), OPTIONAL :: bounds1
1378 : INTEGER, DIMENSION(2, ndims_tensor(tensor2)), INTENT(IN), OPTIONAL :: bounds2
1379 : LOGICAL, INTENT(OUT), OPTIONAL :: crop1, crop2
1380 : LOGICAL :: crop1_prv, crop2_prv
1381 : INTEGER :: compat1, compat1_old, compat2, compat2_old, &
1382 : unit_nr_prv
1383 222504 : TYPE(mp_cart_type) :: comm_2d
1384 222504 : TYPE(array_list) :: dist_list
1385 222504 : INTEGER, DIMENSION(:), ALLOCATABLE :: mp_dims
1386 1557528 : TYPE(dbt_distribution_type) :: dist_in
1387 : INTEGER(KIND=int_8) :: nblkrows, nblkcols
1388 : LOGICAL :: optimize_dist_prv
1389 445008 : INTEGER, DIMENSION(ndims_tensor(tensor1)) :: dims1
1390 222504 : INTEGER, DIMENSION(ndims_tensor(tensor2)) :: dims2
1391 :
1392 222504 : NULLIFY (tensor1_out, tensor2_out)
1393 :
1394 222504 : unit_nr_prv = prep_output_unit(unit_nr)
1395 :
1396 222504 : CALL blk_dims_tensor(tensor1, dims1)
1397 222504 : CALL blk_dims_tensor(tensor2, dims2)
1398 :
1399 1535832 : IF (PRODUCT(int(dims1, int_8)) >= PRODUCT(int(dims2, int_8))) THEN
1400 220370 : ref_tensor = 1
1401 : ELSE
1402 2134 : ref_tensor = 2
1403 : END IF
1404 :
1405 222504 : IF (PRESENT(optimize_dist)) THEN
1406 476 : optimize_dist_prv = optimize_dist
1407 : ELSE
1408 : optimize_dist_prv = .FALSE.
1409 : END IF
1410 :
1411 222504 : compat1 = compat_map(tensor1%nd_index, ind1_linked)
1412 222504 : compat2 = compat_map(tensor2%nd_index, ind2_linked)
1413 222504 : compat1_old = compat1
1414 222504 : compat2_old = compat2
1415 :
1416 222504 : IF (unit_nr_prv > 0) THEN
1417 10 : WRITE (unit_nr_prv, '(T2,A,1X,A,A,1X)', advance='no') "compatibility of", TRIM(tensor1%name), ":"
1418 6 : SELECT CASE (compat1)
1419 : CASE (0)
1420 6 : WRITE (unit_nr_prv, '(A)') "Not compatible"
1421 : CASE (1)
1422 3 : WRITE (unit_nr_prv, '(A)') "Normal"
1423 : CASE (2)
1424 10 : WRITE (unit_nr_prv, '(A)') "Transposed"
1425 : END SELECT
1426 10 : WRITE (unit_nr_prv, '(T2,A,1X,A,A,1X)', advance='no') "compatibility of", TRIM(tensor2%name), ":"
1427 5 : SELECT CASE (compat2)
1428 : CASE (0)
1429 5 : WRITE (unit_nr_prv, '(A)') "Not compatible"
1430 : CASE (1)
1431 4 : WRITE (unit_nr_prv, '(A)') "Normal"
1432 : CASE (2)
1433 10 : WRITE (unit_nr_prv, '(A)') "Transposed"
1434 : END SELECT
1435 : END IF
1436 :
1437 222504 : new1 = .FALSE.
1438 222504 : new2 = .FALSE.
1439 222504 : crop1_prv = .FALSE.
1440 222504 : crop2_prv = .FALSE.
1441 :
1442 222504 : IF (compat1 == 0 .OR. optimize_dist_prv) THEN
1443 34103 : new1 = .TRUE.
1444 : END IF
1445 :
1446 222504 : IF (compat2 == 0 .OR. optimize_dist_prv) THEN
1447 23456 : new2 = .TRUE.
1448 : END IF
1449 :
1450 222504 : IF (ref_tensor == 1) THEN ! tensor 1 is reference and tensor 2 is reshaped compatible with tensor 1
1451 220370 : IF (compat1 == 0 .OR. optimize_dist_prv) THEN ! tensor 1 is not contraction compatible --> reshape
1452 33833 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A)') "Redistribution of", TRIM(tensor1%name)
1453 101499 : nblkrows = PRODUCT(INT(dims1(ind1_linked), KIND=int_8))
1454 67668 : nblkcols = PRODUCT(INT(dims1(ind1_free), KIND=int_8))
1455 33833 : comm_2d = dbt_tas_mp_comm(tensor1%pgrid%mp_comm_2d, nblkrows, nblkcols)
1456 236831 : ALLOCATE (tensor1_out)
1457 : CALL dbt_remap(tensor1, ind1_linked, ind1_free, tensor1_out, comm_2d=comm_2d, &
1458 : nodata=nodata1, move_data=move_data_1, &
1459 33833 : bounds=bounds1)
1460 33833 : CALL comm_2d%free()
1461 33833 : compat1 = 1
1462 : ELSE
1463 186537 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A)') "No redistribution of", TRIM(tensor1%name)
1464 186537 : CALL crop_or_point(tensor1, tensor1_out, crop1_prv, bounds1, move_data_1)
1465 : END IF
1466 220370 : IF (compat2 == 0 .OR. optimize_dist_prv) THEN ! tensor 2 is not contraction compatible --> reshape
1467 23126 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A,1X,A,1X,A)') "Redistribution of", &
1468 8 : TRIM(tensor2%name), "compatible with", TRIM(tensor1%name)
1469 23122 : dist_in = dbt_distribution(tensor1_out)
1470 23122 : dist_list = array_sublist(dist_in%nd_dist, ind1_linked)
1471 23122 : IF (compat1 == 1) THEN ! linked index is first 2d dimension
1472 : ! get distribution of linked index, tensor 2 must adopt this distribution
1473 : ! get grid dimensions of linked index
1474 35220 : ALLOCATE (mp_dims(ndims_mapping_row(dist_in%pgrid%nd_index_grid)))
1475 11740 : CALL dbt_get_mapping_info(dist_in%pgrid%nd_index_grid, dims1_2d=mp_dims)
1476 82180 : ALLOCATE (tensor2_out)
1477 : CALL dbt_remap(tensor2, ind2_linked, ind2_free, tensor2_out, comm_2d=dist_in%pgrid%mp_comm_2d, &
1478 : dist1=dist_list, mp_dims_1=mp_dims, nodata=nodata2, move_data=move_data_2, &
1479 11740 : bounds=bounds2)
1480 11382 : ELSE IF (compat1 == 2) THEN ! linked index is second 2d dimension
1481 : ! get distribution of linked index, tensor 2 must adopt this distribution
1482 : ! get grid dimensions of linked index
1483 34146 : ALLOCATE (mp_dims(ndims_mapping_column(dist_in%pgrid%nd_index_grid)))
1484 11382 : CALL dbt_get_mapping_info(dist_in%pgrid%nd_index_grid, dims2_2d=mp_dims)
1485 79674 : ALLOCATE (tensor2_out)
1486 : CALL dbt_remap(tensor2, ind2_free, ind2_linked, tensor2_out, comm_2d=dist_in%pgrid%mp_comm_2d, &
1487 : dist2=dist_list, mp_dims_2=mp_dims, nodata=nodata2, move_data=move_data_2, &
1488 11382 : bounds=bounds2)
1489 : ELSE
1490 0 : CPABORT("should not happen")
1491 : END IF
1492 23122 : compat2 = compat1
1493 : ELSE
1494 197248 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A)') "No redistribution of", TRIM(tensor2%name)
1495 197248 : CALL crop_or_point(tensor2, tensor2_out, crop2_prv, bounds2, move_data_2)
1496 : END IF
1497 : ELSE ! tensor 2 is reference and tensor 1 is reshaped compatible with tensor 2
1498 2134 : IF (compat2 == 0 .OR. optimize_dist_prv) THEN ! tensor 2 is not contraction compatible --> reshape
1499 334 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A)') "Redistribution of", TRIM(tensor2%name)
1500 754 : nblkrows = PRODUCT(INT(dims2(ind2_linked), KIND=int_8))
1501 730 : nblkcols = PRODUCT(INT(dims2(ind2_free), KIND=int_8))
1502 334 : comm_2d = dbt_tas_mp_comm(tensor2%pgrid%mp_comm_2d, nblkrows, nblkcols)
1503 2338 : ALLOCATE (tensor2_out)
1504 : CALL dbt_remap(tensor2, ind2_linked, ind2_free, tensor2_out, nodata=nodata2, move_data=move_data_2, &
1505 334 : bounds=bounds2)
1506 334 : CALL comm_2d%free()
1507 334 : compat2 = 1
1508 : ELSE
1509 1800 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A)') "No redistribution of", TRIM(tensor2%name)
1510 1800 : CALL crop_or_point(tensor2, tensor2_out, crop2_prv, bounds2, move_data_2)
1511 : END IF
1512 2134 : IF (compat1 == 0 .OR. optimize_dist_prv) THEN ! tensor 1 is not contraction compatible --> reshape
1513 273 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A,1X,A,1X,A)') "Redistribution of", TRIM(tensor1%name), &
1514 6 : "compatible with", TRIM(tensor2%name)
1515 270 : dist_in = dbt_distribution(tensor2_out)
1516 270 : dist_list = array_sublist(dist_in%nd_dist, ind2_linked)
1517 270 : IF (compat2 == 1) THEN
1518 804 : ALLOCATE (mp_dims(ndims_mapping_row(dist_in%pgrid%nd_index_grid)))
1519 268 : CALL dbt_get_mapping_info(dist_in%pgrid%nd_index_grid, dims1_2d=mp_dims)
1520 1876 : ALLOCATE (tensor1_out)
1521 : CALL dbt_remap(tensor1, ind1_linked, ind1_free, tensor1_out, comm_2d=dist_in%pgrid%mp_comm_2d, &
1522 : dist1=dist_list, mp_dims_1=mp_dims, nodata=nodata1, move_data=move_data_1, &
1523 268 : bounds=bounds1)
1524 2 : ELSE IF (compat2 == 2) THEN
1525 6 : ALLOCATE (mp_dims(ndims_mapping_column(dist_in%pgrid%nd_index_grid)))
1526 2 : CALL dbt_get_mapping_info(dist_in%pgrid%nd_index_grid, dims2_2d=mp_dims)
1527 14 : ALLOCATE (tensor1_out)
1528 : CALL dbt_remap(tensor1, ind1_free, ind1_linked, tensor1_out, comm_2d=dist_in%pgrid%mp_comm_2d, &
1529 : dist2=dist_list, mp_dims_2=mp_dims, nodata=nodata1, move_data=move_data_1, &
1530 2 : bounds=bounds1)
1531 : ELSE
1532 0 : CPABORT("should not happen")
1533 : END IF
1534 270 : compat1 = compat2
1535 : ELSE
1536 1864 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A)') "No redistribution of", TRIM(tensor1%name)
1537 1864 : CALL crop_or_point(tensor1, tensor1_out, crop1_prv, bounds1, move_data_1)
1538 : END IF
1539 : END IF
1540 :
1541 156549 : SELECT CASE (compat1)
1542 : CASE (1)
1543 156549 : trans1 = .FALSE.
1544 : CASE (2)
1545 65955 : trans1 = .TRUE.
1546 : CASE DEFAULT
1547 222504 : CPABORT("should not happen")
1548 : END SELECT
1549 :
1550 149701 : SELECT CASE (compat2)
1551 : CASE (1)
1552 149701 : trans2 = .FALSE.
1553 : CASE (2)
1554 72803 : trans2 = .TRUE.
1555 : CASE DEFAULT
1556 222504 : CPABORT("should not happen")
1557 : END SELECT
1558 :
1559 222504 : IF (unit_nr_prv > 0) THEN
1560 10 : IF (compat1 /= compat1_old) THEN
1561 6 : WRITE (unit_nr_prv, '(T2,A,1X,A,A,1X)', advance='no') "compatibility of", TRIM(tensor1_out%name), ":"
1562 0 : SELECT CASE (compat1)
1563 : CASE (0)
1564 0 : WRITE (unit_nr_prv, '(A)') "Not compatible"
1565 : CASE (1)
1566 5 : WRITE (unit_nr_prv, '(A)') "Normal"
1567 : CASE (2)
1568 6 : WRITE (unit_nr_prv, '(A)') "Transposed"
1569 : END SELECT
1570 : END IF
1571 10 : IF (compat2 /= compat2_old) THEN
1572 5 : WRITE (unit_nr_prv, '(T2,A,1X,A,A,1X)', advance='no') "compatibility of", TRIM(tensor2_out%name), ":"
1573 0 : SELECT CASE (compat2)
1574 : CASE (0)
1575 0 : WRITE (unit_nr_prv, '(A)') "Not compatible"
1576 : CASE (1)
1577 4 : WRITE (unit_nr_prv, '(A)') "Normal"
1578 : CASE (2)
1579 5 : WRITE (unit_nr_prv, '(A)') "Transposed"
1580 : END SELECT
1581 : END IF
1582 : END IF
1583 :
1584 222504 : IF (new1 .AND. PRESENT(move_data_1)) move_data_1 = .TRUE.
1585 222504 : IF (new2 .AND. PRESENT(move_data_2)) move_data_2 = .TRUE.
1586 :
1587 222504 : IF (PRESENT(crop1)) crop1 = crop1_prv
1588 222504 : IF (PRESENT(crop2)) crop2 = crop2_prv
1589 :
1590 222504 : END SUBROUTINE reshape_mm_compatible
1591 :
1592 : ! **************************************************************************************************
1593 : !> \brief Prepare tensor for contraction: redistribute to a 2d format which can be contracted by
1594 : !> matrix multiplication. This routine reshapes the smallest of the three tensors.
1595 : !> \param ind1 index that should be mapped to first matrix dimension
1596 : !> \param ind2 index that should be mapped to second matrix dimension
1597 : !> \param trans transpose flag of matrix rep.
1598 : !> \param new whether a new tensor was created for tensor_out
1599 : !> \param nodata don't copy tensor data
1600 : !> \param move_data memory optimization: transfer data s.t. tensor_in may be empty on return
1601 : !> \param unit_nr output unit
1602 : !> \author Patrick Seewald
1603 : ! **************************************************************************************************
1604 222504 : SUBROUTINE reshape_mm_small(tensor_in, ind1, ind2, tensor_out, trans, new, nodata, move_data, unit_nr, &
1605 : bounds, crop)
1606 : TYPE(dbt_type), TARGET, INTENT(INOUT) :: tensor_in
1607 : INTEGER, DIMENSION(:), INTENT(IN) :: ind1, ind2
1608 : TYPE(dbt_type), POINTER, INTENT(OUT) :: tensor_out
1609 : LOGICAL, INTENT(OUT) :: trans
1610 : LOGICAL, INTENT(OUT) :: new
1611 : LOGICAL, INTENT(IN), OPTIONAL :: nodata
1612 : LOGICAL, INTENT(INOUT), OPTIONAL :: move_data
1613 : INTEGER, INTENT(IN), OPTIONAL :: unit_nr
1614 : INTEGER, DIMENSION(2, ndims_tensor(tensor_in)), INTENT(IN), OPTIONAL :: bounds
1615 : LOGICAL, INTENT(OUT), OPTIONAL :: crop
1616 : LOGICAL :: crop_prv
1617 : INTEGER :: compat1, compat2, compat1_old, compat2_old, unit_nr_prv
1618 : LOGICAL :: nodata_prv
1619 :
1620 222504 : NULLIFY (tensor_out)
1621 : IF (PRESENT(nodata)) THEN
1622 222504 : nodata_prv = nodata
1623 : ELSE
1624 222504 : nodata_prv = .FALSE.
1625 : END IF
1626 :
1627 222504 : unit_nr_prv = prep_output_unit(unit_nr)
1628 :
1629 222504 : new = .FALSE.
1630 222504 : crop_prv = .FALSE.
1631 222504 : compat1 = compat_map(tensor_in%nd_index, ind1)
1632 222504 : compat2 = compat_map(tensor_in%nd_index, ind2)
1633 222504 : compat1_old = compat1; compat2_old = compat2
1634 222504 : IF (unit_nr_prv > 0) THEN
1635 10 : WRITE (unit_nr_prv, '(T2,A,1X,A,A,1X)', advance='no') "compatibility of", TRIM(tensor_in%name), ":"
1636 10 : IF (compat1 == 1 .AND. compat2 == 2) THEN
1637 4 : WRITE (unit_nr_prv, '(A)') "Normal"
1638 6 : ELSE IF (compat1 == 2 .AND. compat2 == 1) THEN
1639 2 : WRITE (unit_nr_prv, '(A)') "Transposed"
1640 : ELSE
1641 4 : WRITE (unit_nr_prv, '(A)') "Not compatible"
1642 : END IF
1643 : END IF
1644 222504 : IF (compat1 == 0 .or. compat2 == 0) THEN ! index mapping not compatible with contract index
1645 :
1646 102 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A)') "Redistribution of", TRIM(tensor_in%name)
1647 :
1648 714 : ALLOCATE (tensor_out)
1649 102 : CALL dbt_remap(tensor_in, ind1, ind2, tensor_out, nodata=nodata, move_data=move_data, bounds=bounds)
1650 102 : CALL dbt_copy_contraction_storage(tensor_in, tensor_out)
1651 102 : compat1 = 1
1652 102 : compat2 = 2
1653 102 : new = .TRUE.
1654 : ELSE
1655 222402 : IF (unit_nr_prv > 0) WRITE (unit_nr_prv, '(T2,A,1X,A)') "No redistribution of", TRIM(tensor_in%name)
1656 222402 : CALL crop_or_point(tensor_in, tensor_out, crop_prv, bounds, move_data)
1657 : END IF
1658 :
1659 222504 : IF (compat1 == 1 .AND. compat2 == 2) THEN
1660 151378 : trans = .FALSE.
1661 71126 : ELSE IF (compat1 == 2 .AND. compat2 == 1) THEN
1662 71126 : trans = .TRUE.
1663 : ELSE
1664 0 : CPABORT("this should not happen")
1665 : END IF
1666 :
1667 222504 : IF (unit_nr_prv > 0) THEN
1668 10 : IF (compat1_old /= compat1 .OR. compat2_old /= compat2) THEN
1669 4 : WRITE (unit_nr_prv, '(T2,A,1X,A,A,1X)', advance='no') "compatibility of", TRIM(tensor_out%name), ":"
1670 4 : IF (compat1 == 1 .AND. compat2 == 2) THEN
1671 4 : WRITE (unit_nr_prv, '(A)') "Normal"
1672 0 : ELSE IF (compat1 == 2 .AND. compat2 == 1) THEN
1673 0 : WRITE (unit_nr_prv, '(A)') "Transposed"
1674 : ELSE
1675 0 : WRITE (unit_nr_prv, '(A)') "Not compatible"
1676 : END IF
1677 : END IF
1678 : END IF
1679 :
1680 222504 : IF (PRESENT(crop)) crop = crop_prv
1681 :
1682 222504 : END SUBROUTINE reshape_mm_small
1683 :
1684 : ! **************************************************************************************************
1685 : !> \brief Crop tensor_in into a new tensor_out if bounds are present, otherwise point to tensor_in.
1686 : !> \param cropped whether tensor_out is a new tensor
1687 : !> \param move_data memory optimization: transfer data s.t. tensor_in may be empty on return
1688 : !> \author Hans Pabst
1689 : ! **************************************************************************************************
1690 609851 : SUBROUTINE crop_or_point(tensor_in, tensor_out, cropped, bounds, move_data)
1691 : TYPE(dbt_type), TARGET, INTENT(INOUT) :: tensor_in
1692 : TYPE(dbt_type), POINTER, INTENT(OUT) :: tensor_out
1693 : LOGICAL, INTENT(OUT) :: cropped
1694 : INTEGER, DIMENSION(2, ndims_tensor(tensor_in)), INTENT(IN), OPTIONAL :: bounds
1695 : LOGICAL, INTENT(INOUT), OPTIONAL :: move_data
1696 :
1697 609851 : cropped = PRESENT(bounds)
1698 609851 : IF (cropped) THEN
1699 751560 : ALLOCATE (tensor_out)
1700 75156 : CALL dbt_crop(tensor_in, tensor_out, bounds, move_data=move_data)
1701 75156 : IF (PRESENT(move_data)) move_data = .TRUE. ! tensor_out is temporary
1702 : ELSE
1703 534695 : tensor_out => tensor_in
1704 : END IF
1705 609851 : END SUBROUTINE crop_or_point
1706 :
1707 : ! **************************************************************************************************
1708 : !> \brief Number of local blocks overlapping the bounds (blocks dbt_crop would keep), and number
1709 : !> of local blocks not entirely within the bounds (none: dbt_crop would copy as is).
1710 : !> \author Hans Pabst
1711 : ! **************************************************************************************************
1712 339604 : FUNCTION num_blocks_in_bounds(tensor, bounds) RESULT(nblocks)
1713 : TYPE(dbt_type), INTENT(INOUT) :: tensor
1714 : INTEGER, DIMENSION(2, ndims_tensor(tensor)), INTENT(IN) :: bounds
1715 : INTEGER, DIMENSION(2) :: nblocks
1716 :
1717 : TYPE(dbt_iterator_type) :: iter
1718 169802 : INTEGER, DIMENSION(ndims_tensor(tensor)) :: blk_index, blk_offset, blk_size
1719 : INTEGER :: nin, nout
1720 :
1721 169802 : nin = 0
1722 169802 : nout = 0
1723 : !$OMP PARALLEL DEFAULT(NONE) SHARED(tensor,bounds) PRIVATE(iter,blk_index,blk_size,blk_offset) &
1724 169802 : !$OMP REDUCTION(+:nin,nout)
1725 : CALL dbt_iterator_start(iter, tensor)
1726 : DO WHILE (dbt_iterator_blocks_left(iter))
1727 : CALL dbt_iterator_next_block(iter, blk_index, blk_size=blk_size, blk_offset=blk_offset)
1728 : IF (ALL(blk_offset <= bounds(2, :) .AND. bounds(1, :) <= blk_offset + blk_size - 1)) THEN
1729 : nin = nin + 1
1730 : END IF
1731 : IF (ANY(blk_offset < bounds(1, :) .OR. bounds(2, :) < blk_offset + blk_size - 1)) THEN
1732 : nout = nout + 1
1733 : END IF
1734 : END DO
1735 : CALL dbt_iterator_stop(iter)
1736 : !$OMP END PARALLEL
1737 509406 : nblocks = [nin, nout]
1738 169802 : END FUNCTION num_blocks_in_bounds
1739 :
1740 : ! **************************************************************************************************
1741 : !> \brief update contraction storage that keeps track of process grids during a batched contraction
1742 : !> and decide if tensor process grid needs to be optimized
1743 : !> \param split_opt optimized TAS process grid
1744 : !> \param split current TAS process grid
1745 : !> \author Patrick Seewald
1746 : ! **************************************************************************************************
1747 93532 : FUNCTION update_contraction_storage(storage, split_opt, split) RESULT(do_change_pgrid)
1748 : TYPE(dbt_contraction_storage), INTENT(INOUT) :: storage
1749 : TYPE(dbt_tas_split_info), INTENT(IN) :: split_opt
1750 : TYPE(dbt_tas_split_info), INTENT(IN) :: split
1751 : INTEGER, DIMENSION(2) :: pdims, pdims_sub
1752 : LOGICAL, DIMENSION(2) :: do_change_pgrid
1753 : REAL(kind=dp) :: change_criterion, pdims_ratio
1754 : INTEGER :: nsplit_opt, nsplit
1755 :
1756 93532 : CPASSERT(ALLOCATED(split_opt%ngroup_opt))
1757 93532 : nsplit_opt = split_opt%ngroup_opt
1758 93532 : nsplit = split%ngroup
1759 :
1760 280596 : pdims = split%mp_comm%num_pe_cart
1761 :
1762 93532 : storage%ibatch = storage%ibatch + 1
1763 :
1764 : storage%nsplit_avg = (storage%nsplit_avg*REAL(storage%ibatch - 1, dp) + REAL(nsplit_opt, dp)) &
1765 93532 : /REAL(storage%ibatch, dp)
1766 :
1767 93532 : SELECT CASE (split_opt%split_rowcol)
1768 : CASE (rowsplit)
1769 93532 : pdims_ratio = REAL(pdims(1), dp)/pdims(2)
1770 : CASE (colsplit)
1771 93532 : pdims_ratio = REAL(pdims(2), dp)/pdims(1)
1772 : END SELECT
1773 :
1774 280596 : do_change_pgrid(:) = .FALSE.
1775 :
1776 : ! check for process grid dimensions
1777 280596 : pdims_sub = split%mp_comm_group%num_pe_cart
1778 467660 : change_criterion = MAXVAL(REAL(pdims_sub, dp))/MINVAL(pdims_sub)
1779 93532 : IF (change_criterion > default_pdims_accept_ratio**2) do_change_pgrid(1) = .TRUE.
1780 :
1781 : ! check for split factor
1782 93532 : change_criterion = MAX(REAL(nsplit, dp)/storage%nsplit_avg, REAL(storage%nsplit_avg, dp)/nsplit)
1783 93532 : IF (change_criterion > default_nsplit_accept_ratio) do_change_pgrid(2) = .TRUE.
1784 :
1785 93532 : END FUNCTION update_contraction_storage
1786 :
1787 : ! **************************************************************************************************
1788 : !> \brief Check if 2d index is compatible with tensor index
1789 : !> \author Patrick Seewald
1790 : ! **************************************************************************************************
1791 890016 : FUNCTION compat_map(nd_index, compat_ind)
1792 : TYPE(nd_to_2d_mapping), INTENT(IN) :: nd_index
1793 : INTEGER, DIMENSION(:), INTENT(IN) :: compat_ind
1794 1780032 : INTEGER, DIMENSION(ndims_mapping_row(nd_index)) :: map1
1795 1780032 : INTEGER, DIMENSION(ndims_mapping_column(nd_index)) :: map2
1796 : INTEGER :: compat_map
1797 :
1798 890016 : CALL dbt_get_mapping_info(nd_index, map1_2d=map1, map2_2d=map2)
1799 :
1800 890016 : compat_map = 0
1801 890016 : IF (array_eq_i(map1, compat_ind)) THEN
1802 : compat_map = 1
1803 407349 : ELSE IF (array_eq_i(map2, compat_ind)) THEN
1804 350548 : compat_map = 2
1805 : END IF
1806 :
1807 890016 : END FUNCTION compat_map
1808 :
1809 : ! **************************************************************************************************
1810 : !> \brief
1811 : !> \author Patrick Seewald
1812 : ! **************************************************************************************************
1813 667512 : SUBROUTINE index_linked_sort(ind_ref, ind_dep)
1814 : INTEGER, DIMENSION(:), INTENT(INOUT) :: ind_ref, ind_dep
1815 1335024 : INTEGER, DIMENSION(SIZE(ind_ref)) :: sort_indices
1816 :
1817 667512 : CALL sort(ind_ref, SIZE(ind_ref), sort_indices)
1818 3095644 : ind_dep(:) = ind_dep(sort_indices)
1819 :
1820 667512 : END SUBROUTINE index_linked_sort
1821 :
1822 : ! **************************************************************************************************
1823 : !> \brief
1824 : !> \author Patrick Seewald
1825 : ! **************************************************************************************************
1826 0 : FUNCTION opt_pgrid(tensor, tas_split_info)
1827 : TYPE(dbt_type), INTENT(IN) :: tensor
1828 : TYPE(dbt_tas_split_info), INTENT(IN) :: tas_split_info
1829 0 : INTEGER, DIMENSION(ndims_matrix_row(tensor)) :: map1
1830 0 : INTEGER, DIMENSION(ndims_matrix_column(tensor)) :: map2
1831 : TYPE(dbt_pgrid_type) :: opt_pgrid
1832 0 : INTEGER, DIMENSION(ndims_tensor(tensor)) :: dims
1833 :
1834 0 : CALL dbt_get_mapping_info(tensor%pgrid%nd_index_grid, map1_2d=map1, map2_2d=map2)
1835 0 : CALL blk_dims_tensor(tensor, dims)
1836 0 : opt_pgrid = dbt_nd_mp_comm(tas_split_info%mp_comm, map1, map2, tdims=dims)
1837 :
1838 0 : ALLOCATE (opt_pgrid%tas_split_info, SOURCE=tas_split_info)
1839 0 : CALL dbt_tas_info_hold(opt_pgrid%tas_split_info)
1840 0 : END FUNCTION opt_pgrid
1841 :
1842 : ! **************************************************************************************************
1843 : !> \brief Copy tensor to tensor with modified index mapping
1844 : !> \param map1_2d new index mapping
1845 : !> \param map2_2d new index mapping
1846 : !> \author Patrick Seewald
1847 : ! **************************************************************************************************
1848 518949 : SUBROUTINE dbt_remap(tensor_in, map1_2d, map2_2d, tensor_out, comm_2d, dist1, dist2, &
1849 57661 : mp_dims_1, mp_dims_2, name, nodata, move_data, bounds)
1850 : TYPE(dbt_type), INTENT(INOUT) :: tensor_in
1851 : INTEGER, DIMENSION(2, ndims_tensor(tensor_in)), INTENT(IN), OPTIONAL :: bounds
1852 : INTEGER, DIMENSION(:), INTENT(IN) :: map1_2d, map2_2d
1853 : TYPE(dbt_type), INTENT(OUT) :: tensor_out
1854 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: name
1855 : LOGICAL, INTENT(IN), OPTIONAL :: nodata, move_data
1856 : CLASS(mp_comm_type), INTENT(IN), OPTIONAL :: comm_2d
1857 : TYPE(array_list), INTENT(IN), OPTIONAL :: dist1, dist2
1858 : INTEGER, DIMENSION(SIZE(map1_2d)), OPTIONAL :: mp_dims_1
1859 : INTEGER, DIMENSION(SIZE(map2_2d)), OPTIONAL :: mp_dims_2
1860 : CHARACTER(len=default_string_length) :: name_tmp
1861 57661 : INTEGER, DIMENSION(:), ALLOCATABLE :: ${varlist("blk_sizes")}$, &
1862 57661 : ${varlist("nd_dist")}$
1863 403627 : TYPE(dbt_distribution_type) :: dist
1864 57661 : TYPE(mp_cart_type) :: comm_2d_prv
1865 : INTEGER :: handle, i
1866 57661 : INTEGER, DIMENSION(ndims_tensor(tensor_in)) :: pdims, myploc
1867 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_remap'
1868 : LOGICAL :: nodata_prv
1869 172983 : TYPE(dbt_pgrid_type) :: comm_nd
1870 :
1871 57661 : CALL timeset(routineN, handle)
1872 :
1873 57661 : IF (PRESENT(name)) THEN
1874 0 : name_tmp = name
1875 : ELSE
1876 57661 : name_tmp = tensor_in%name
1877 : END IF
1878 57661 : IF (PRESENT(dist1)) THEN
1879 12008 : CPASSERT(PRESENT(mp_dims_1))
1880 : END IF
1881 :
1882 57661 : IF (PRESENT(dist2)) THEN
1883 11384 : CPASSERT(PRESENT(mp_dims_2))
1884 : END IF
1885 :
1886 57661 : IF (PRESENT(comm_2d)) THEN
1887 57225 : comm_2d_prv = comm_2d
1888 : ELSE
1889 436 : comm_2d_prv = tensor_in%pgrid%mp_comm_2d
1890 : END IF
1891 :
1892 57661 : comm_nd = dbt_nd_mp_comm(comm_2d_prv, map1_2d, map2_2d, dims1_nd=mp_dims_1, dims2_nd=mp_dims_2)
1893 57661 : CALL mp_environ_pgrid(comm_nd, pdims, myploc)
1894 :
1895 : #:for ndim in ndims
1896 115318 : IF (ndims_tensor(tensor_in) == ${ndim}$) THEN
1897 57661 : CALL get_arrays(tensor_in%blk_sizes, ${varlist("blk_sizes", nmax=ndim)}$)
1898 : END IF
1899 : #:endfor
1900 :
1901 : #:for ndim in ndims
1902 115318 : IF (ndims_tensor(tensor_in) == ${ndim}$) THEN
1903 : #:for idim in range(1, ndim+1)
1904 172799 : IF (PRESENT(dist1)) THEN
1905 71860 : IF (ANY(map1_2d == ${idim}$)) THEN
1906 71296 : i = MINLOC(map1_2d, dim=1, mask=map1_2d == ${idim}$) ! i is location of idim in map1_2d
1907 23828 : CALL get_ith_array(dist1, i, nd_dist_${idim}$)
1908 : END IF
1909 : END IF
1910 :
1911 172799 : IF (PRESENT(dist2)) THEN
1912 68276 : IF (ANY(map2_2d == ${idim}$)) THEN
1913 68192 : i = MINLOC(map2_2d, dim=1, mask=map2_2d == ${idim}$) ! i is location of idim in map2_2d
1914 22740 : CALL get_ith_array(dist2, i, nd_dist_${idim}$)
1915 : END IF
1916 : END IF
1917 :
1918 172799 : IF (.NOT. ALLOCATED(nd_dist_${idim}$)) THEN
1919 378693 : ALLOCATE (nd_dist_${idim}$ (SIZE(blk_sizes_${idim}$)))
1920 126231 : CALL dbt_default_distvec(SIZE(blk_sizes_${idim}$), pdims(${idim}$), blk_sizes_${idim}$, nd_dist_${idim}$)
1921 : END IF
1922 : #:endfor
1923 : CALL dbt_distribution_new_expert(dist, comm_nd, map1_2d, map2_2d, &
1924 57661 : ${varlist("nd_dist", nmax=ndim)}$, own_comm=.TRUE.)
1925 : END IF
1926 : #:endfor
1927 :
1928 : #:for ndim in ndims
1929 115318 : IF (ndims_tensor(tensor_in) == ${ndim}$) THEN
1930 : CALL dbt_create(tensor_out, name_tmp, dist, map1_2d, map2_2d, &
1931 57661 : ${varlist("blk_sizes", nmax=ndim)}$)
1932 : END IF
1933 : #:endfor
1934 :
1935 57661 : IF (PRESENT(nodata)) THEN
1936 23276 : nodata_prv = nodata
1937 : ELSE
1938 : nodata_prv = .FALSE.
1939 : END IF
1940 :
1941 57661 : IF (.NOT. nodata_prv) CALL dbt_copy_expert(tensor_in, tensor_out, move_data=move_data, bounds=bounds)
1942 57661 : CALL dbt_distribution_destroy(dist)
1943 :
1944 57661 : CALL timestop(handle)
1945 172983 : END SUBROUTINE dbt_remap
1946 :
1947 : ! **************************************************************************************************
1948 : !> \brief Align index with data
1949 : !> \param order permutation resulting from alignment
1950 : !> \author Patrick Seewald
1951 : ! **************************************************************************************************
1952 5340096 : SUBROUTINE dbt_align_index(tensor_in, tensor_out, order)
1953 : TYPE(dbt_type), INTENT(INOUT) :: tensor_in
1954 : TYPE(dbt_type), INTENT(OUT) :: tensor_out
1955 1335024 : INTEGER, DIMENSION(ndims_matrix_row(tensor_in)) :: map1_2d
1956 1335024 : INTEGER, DIMENSION(ndims_matrix_column(tensor_in)) :: map2_2d
1957 : INTEGER, DIMENSION(ndims_tensor(tensor_in)), &
1958 : INTENT(OUT), OPTIONAL :: order
1959 667512 : INTEGER, DIMENSION(ndims_tensor(tensor_in)) :: order_prv
1960 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_align_index'
1961 : INTEGER :: handle
1962 :
1963 667512 : CALL timeset(routineN, handle)
1964 :
1965 667512 : CALL dbt_get_mapping_info(tensor_in%nd_index_blk, map1_2d=map1_2d, map2_2d=map2_2d)
1966 4188752 : order_prv = dbt_inverse_order([map1_2d, map2_2d])
1967 667512 : CALL dbt_permute_index(tensor_in, tensor_out, order=order_prv)
1968 :
1969 2428132 : IF (PRESENT(order)) order = order_prv
1970 :
1971 667512 : CALL timestop(handle)
1972 667512 : END SUBROUTINE dbt_align_index
1973 :
1974 : ! **************************************************************************************************
1975 : !> \brief Create new tensor by reordering index, data is copied exactly (shallow copy)
1976 : !> \author Patrick Seewald
1977 : ! **************************************************************************************************
1978 7589133 : SUBROUTINE dbt_permute_index(tensor_in, tensor_out, order)
1979 : TYPE(dbt_type), INTENT(INOUT) :: tensor_in
1980 : TYPE(dbt_type), INTENT(OUT) :: tensor_out
1981 : INTEGER, DIMENSION(ndims_tensor(tensor_in)), &
1982 : INTENT(IN) :: order
1983 :
1984 4216185 : TYPE(nd_to_2d_mapping) :: nd_index_blk_rs, nd_index_rs
1985 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_permute_index'
1986 : INTEGER :: handle
1987 : INTEGER :: ndims
1988 :
1989 843237 : CALL timeset(routineN, handle)
1990 :
1991 843237 : ndims = ndims_tensor(tensor_in)
1992 :
1993 843237 : CALL permute_index(tensor_in%nd_index, nd_index_rs, order)
1994 843237 : CALL permute_index(tensor_in%nd_index_blk, nd_index_blk_rs, order)
1995 843237 : CALL permute_index(tensor_in%pgrid%nd_index_grid, tensor_out%pgrid%nd_index_grid, order)
1996 :
1997 843237 : tensor_out%matrix_rep => tensor_in%matrix_rep
1998 843237 : tensor_out%owns_matrix = .FALSE.
1999 :
2000 843237 : tensor_out%nd_index = nd_index_rs
2001 843237 : tensor_out%nd_index_blk = nd_index_blk_rs
2002 843237 : tensor_out%pgrid%mp_comm_2d = tensor_in%pgrid%mp_comm_2d
2003 843237 : IF (ALLOCATED(tensor_in%pgrid%tas_split_info)) THEN
2004 843237 : ALLOCATE (tensor_out%pgrid%tas_split_info, SOURCE=tensor_in%pgrid%tas_split_info)
2005 : END IF
2006 843237 : tensor_out%refcount => tensor_in%refcount
2007 843237 : CALL dbt_hold(tensor_out)
2008 :
2009 843237 : CALL reorder_arrays(tensor_in%blk_sizes, tensor_out%blk_sizes, order)
2010 843237 : CALL reorder_arrays(tensor_in%blk_offsets, tensor_out%blk_offsets, order)
2011 843237 : CALL reorder_arrays(tensor_in%nd_dist, tensor_out%nd_dist, order)
2012 843237 : CALL reorder_arrays(tensor_in%blks_local, tensor_out%blks_local, order)
2013 2529711 : ALLOCATE (tensor_out%nblks_local(ndims))
2014 1686474 : ALLOCATE (tensor_out%nfull_local(ndims))
2015 3097470 : tensor_out%nblks_local(order) = tensor_in%nblks_local(:)
2016 3097470 : tensor_out%nfull_local(order) = tensor_in%nfull_local(:)
2017 843237 : tensor_out%name = tensor_in%name
2018 843237 : tensor_out%valid = .TRUE.
2019 :
2020 843237 : IF (ALLOCATED(tensor_in%contraction_storage)) THEN
2021 324663 : ALLOCATE (tensor_out%contraction_storage, SOURCE=tensor_in%contraction_storage)
2022 324663 : CALL destroy_array_list(tensor_out%contraction_storage%batch_ranges)
2023 324663 : CALL reorder_arrays(tensor_in%contraction_storage%batch_ranges, tensor_out%contraction_storage%batch_ranges, order)
2024 : END IF
2025 :
2026 843237 : CALL timestop(handle)
2027 1686474 : END SUBROUTINE dbt_permute_index
2028 :
2029 : ! **************************************************************************************************
2030 : !> \brief Map contraction bounds to bounds referring to tensor indices
2031 : !> see dbt_contract for docu of dummy arguments
2032 : !> \param bounds_t1 bounds mapped to tensor_1
2033 : !> \param bounds_t2 bounds mapped to tensor_2
2034 : !> \param do_crop_1 whether tensor 1 should be cropped
2035 : !> \param do_crop_2 whether tensor 2 should be cropped
2036 : !> \author Patrick Seewald
2037 : ! **************************************************************************************************
2038 257420 : SUBROUTINE dbt_map_bounds_to_tensors(tensor_1, tensor_2, &
2039 257420 : contract_1, notcontract_1, &
2040 514840 : contract_2, notcontract_2, &
2041 257420 : bounds_t1, bounds_t2, &
2042 162026 : bounds_1, bounds_2, bounds_3, &
2043 : do_crop_1, do_crop_2)
2044 :
2045 : TYPE(dbt_type), INTENT(IN) :: tensor_1, tensor_2
2046 : INTEGER, DIMENSION(:), INTENT(IN) :: contract_1, contract_2, &
2047 : notcontract_1, notcontract_2
2048 : INTEGER, DIMENSION(2, ndims_tensor(tensor_1)), &
2049 : INTENT(OUT) :: bounds_t1
2050 : INTEGER, DIMENSION(2, ndims_tensor(tensor_2)), &
2051 : INTENT(OUT) :: bounds_t2
2052 : INTEGER, DIMENSION(2, SIZE(contract_1)), &
2053 : INTENT(IN), OPTIONAL :: bounds_1
2054 : INTEGER, DIMENSION(2, SIZE(notcontract_1)), &
2055 : INTENT(IN), OPTIONAL :: bounds_2
2056 : INTEGER, DIMENSION(2, SIZE(notcontract_2)), &
2057 : INTENT(IN), OPTIONAL :: bounds_3
2058 : LOGICAL, INTENT(OUT), OPTIONAL :: do_crop_1, do_crop_2
2059 : LOGICAL, DIMENSION(2) :: do_crop
2060 :
2061 257420 : do_crop = .FALSE.
2062 :
2063 913027 : bounds_t1(1, :) = 1
2064 913027 : CALL dbt_get_info(tensor_1, nfull_total=bounds_t1(2, :))
2065 :
2066 963356 : bounds_t2(1, :) = 1
2067 963356 : CALL dbt_get_info(tensor_2, nfull_total=bounds_t2(2, :))
2068 :
2069 257420 : IF (PRESENT(bounds_1)) THEN
2070 239588 : bounds_t1(:, contract_1) = bounds_1
2071 105770 : do_crop(1) = .TRUE.
2072 239588 : bounds_t2(:, contract_2) = bounds_1
2073 257420 : do_crop(2) = .TRUE.
2074 : END IF
2075 :
2076 257420 : IF (PRESENT(bounds_2)) THEN
2077 314774 : bounds_t1(:, notcontract_1) = bounds_2
2078 257420 : do_crop(1) = .TRUE.
2079 : END IF
2080 :
2081 257420 : IF (PRESENT(bounds_3)) THEN
2082 329482 : bounds_t2(:, notcontract_2) = bounds_3
2083 257420 : do_crop(2) = .TRUE.
2084 : END IF
2085 :
2086 257420 : IF (PRESENT(do_crop_1)) do_crop_1 = do_crop(1)
2087 257420 : IF (PRESENT(do_crop_2)) do_crop_2 = do_crop(2)
2088 :
2089 578355 : END SUBROUTINE dbt_map_bounds_to_tensors
2090 :
2091 : ! **************************************************************************************************
2092 : !> \brief print tensor contraction indices in a human readable way
2093 : !> \param indchar1 characters printed for index of tensor 1
2094 : !> \param indchar2 characters printed for index of tensor 2
2095 : !> \param indchar3 characters printed for index of tensor 3
2096 : !> \param unit_nr output unit
2097 : !> \author Patrick Seewald
2098 : ! **************************************************************************************************
2099 165360 : SUBROUTINE dbt_print_contraction_index(tensor_1, indchar1, tensor_2, indchar2, tensor_3, indchar3, unit_nr)
2100 : TYPE(dbt_type), INTENT(IN) :: tensor_1, tensor_2, tensor_3
2101 : CHARACTER(LEN=1), DIMENSION(ndims_tensor(tensor_1)), INTENT(IN) :: indchar1
2102 : CHARACTER(LEN=1), DIMENSION(ndims_tensor(tensor_2)), INTENT(IN) :: indchar2
2103 : CHARACTER(LEN=1), DIMENSION(ndims_tensor(tensor_3)), INTENT(IN) :: indchar3
2104 : INTEGER, INTENT(IN) :: unit_nr
2105 330720 : INTEGER, DIMENSION(ndims_matrix_row(tensor_1)) :: map11
2106 330720 : INTEGER, DIMENSION(ndims_matrix_column(tensor_1)) :: map12
2107 330720 : INTEGER, DIMENSION(ndims_matrix_row(tensor_2)) :: map21
2108 330720 : INTEGER, DIMENSION(ndims_matrix_column(tensor_2)) :: map22
2109 330720 : INTEGER, DIMENSION(ndims_matrix_row(tensor_3)) :: map31
2110 330720 : INTEGER, DIMENSION(ndims_matrix_column(tensor_3)) :: map32
2111 : INTEGER :: ichar1, ichar2, ichar3, unit_nr_prv
2112 :
2113 165360 : unit_nr_prv = prep_output_unit(unit_nr)
2114 :
2115 165360 : IF (unit_nr_prv /= 0) THEN
2116 165360 : CALL dbt_get_mapping_info(tensor_1%nd_index_blk, map1_2d=map11, map2_2d=map12)
2117 165360 : CALL dbt_get_mapping_info(tensor_2%nd_index_blk, map1_2d=map21, map2_2d=map22)
2118 165360 : CALL dbt_get_mapping_info(tensor_3%nd_index_blk, map1_2d=map31, map2_2d=map32)
2119 : END IF
2120 :
2121 165360 : IF (unit_nr_prv > 0) THEN
2122 30 : WRITE (unit_nr_prv, '(T2,A)') "INDEX INFO"
2123 30 : WRITE (unit_nr_prv, '(T15,A)', advance='no') "tensor index: ("
2124 123 : DO ichar1 = 1, SIZE(indchar1)
2125 123 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar1(ichar1)
2126 : END DO
2127 30 : WRITE (unit_nr_prv, '(A)', advance='no') ") x ("
2128 120 : DO ichar2 = 1, SIZE(indchar2)
2129 120 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar2(ichar2)
2130 : END DO
2131 30 : WRITE (unit_nr_prv, '(A)', advance='no') ") = ("
2132 123 : DO ichar3 = 1, SIZE(indchar3)
2133 123 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar3(ichar3)
2134 : END DO
2135 30 : WRITE (unit_nr_prv, '(A)') ")"
2136 :
2137 30 : WRITE (unit_nr_prv, '(T15,A)', advance='no') "matrix index: ("
2138 82 : DO ichar1 = 1, SIZE(map11)
2139 82 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar1(map11(ichar1))
2140 : END DO
2141 30 : WRITE (unit_nr_prv, '(A1)', advance='no') "|"
2142 71 : DO ichar1 = 1, SIZE(map12)
2143 71 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar1(map12(ichar1))
2144 : END DO
2145 30 : WRITE (unit_nr_prv, '(A)', advance='no') ") x ("
2146 76 : DO ichar2 = 1, SIZE(map21)
2147 76 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar2(map21(ichar2))
2148 : END DO
2149 30 : WRITE (unit_nr_prv, '(A1)', advance='no') "|"
2150 74 : DO ichar2 = 1, SIZE(map22)
2151 74 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar2(map22(ichar2))
2152 : END DO
2153 30 : WRITE (unit_nr_prv, '(A)', advance='no') ") = ("
2154 79 : DO ichar3 = 1, SIZE(map31)
2155 79 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar3(map31(ichar3))
2156 : END DO
2157 30 : WRITE (unit_nr_prv, '(A1)', advance='no') "|"
2158 74 : DO ichar3 = 1, SIZE(map32)
2159 74 : WRITE (unit_nr_prv, '(A1)', advance='no') indchar3(map32(ichar3))
2160 : END DO
2161 30 : WRITE (unit_nr_prv, '(A)') ")"
2162 : END IF
2163 :
2164 165360 : END SUBROUTINE dbt_print_contraction_index
2165 :
2166 : ! **************************************************************************************************
2167 : !> \brief Initialize batched contraction for this tensor.
2168 : !>
2169 : !> Explanation: A batched contraction is a contraction performed in several consecutive steps
2170 : !> by specification of bounds in dbt_contract. This can be used to reduce memory by
2171 : !> a large factor. The routines dbt_batched_contract_init and
2172 : !> dbt_batched_contract_finalize should be called to define the scope of a batched
2173 : !> contraction as this enables important optimizations (adapting communication scheme to
2174 : !> batches and adapting process grid to multiplication algorithm). The routines
2175 : !> dbt_batched_contract_init and dbt_batched_contract_finalize must be
2176 : !> called before the first and after the last contraction step on all 3 tensors.
2177 : !>
2178 : !> Requirements:
2179 : !> - the tensors are in a compatible matrix layout (see documentation of
2180 : !> `dbt_contract`, note 2 & 3). If they are not, process grid optimizations are
2181 : !> disabled and a warning is issued.
2182 : !> - within the scope of a batched contraction, it is not allowed to access or change tensor
2183 : !> data except by calling the routines dbt_contract & dbt_copy.
2184 : !> - the bounds affecting indices of the smallest tensor must not change in the course of a
2185 : !> batched contraction (todo: get rid of this requirement).
2186 : !>
2187 : !> Side effects:
2188 : !> - the parallel layout (process grid and distribution) of all tensors may change. In order
2189 : !> to disable the process grid optimization including this side effect, call this routine
2190 : !> only on the smallest of the 3 tensors.
2191 : !>
2192 : !> \note
2193 : !> Note 1: for an example of batched contraction see `examples/dbt_example.F`.
2194 : !> (todo: the example is outdated and should be updated).
2195 : !>
2196 : !> Note 2: it is meaningful to use this feature if the contraction consists of one batch only
2197 : !> but if multiple contractions involving the same 3 tensors are performed
2198 : !> (batched_contract_init and batched_contract_finalize must then be called before/after each
2199 : !> contraction call). The process grid is then optimized after the first contraction
2200 : !> and future contraction may profit from this optimization.
2201 : !>
2202 : !> \param batch_range_i refers to the ith tensor dimension and contains all block indices starting
2203 : !> a new range. The size should be the number of ranges plus one, the last
2204 : !> element being the block index plus one of the last block in the last range.
2205 : !> For internal load balancing optimizations, optionally specify the index
2206 : !> ranges of batched contraction.
2207 : !> \author Patrick Seewald
2208 : ! **************************************************************************************************
2209 127879 : SUBROUTINE dbt_batched_contract_init(tensor, ${varlist("batch_range")}$)
2210 : TYPE(dbt_type), INTENT(INOUT) :: tensor
2211 : INTEGER, DIMENSION(:), OPTIONAL, INTENT(IN) :: ${varlist("batch_range")}$
2212 255758 : INTEGER, DIMENSION(ndims_tensor(tensor)) :: tdims
2213 127879 : INTEGER, DIMENSION(:), ALLOCATABLE :: ${varlist("batch_range_prv")}$
2214 : LOGICAL :: static_range
2215 :
2216 127879 : CALL dbt_get_info(tensor, nblks_total=tdims)
2217 :
2218 127879 : static_range = .TRUE.
2219 : #:for idim in range(1, maxdim+1)
2220 127879 : IF (ndims_tensor(tensor) >= ${idim}$) THEN
2221 303330 : IF (PRESENT(batch_range_${idim}$)) THEN
2222 460972 : ALLOCATE (batch_range_prv_${idim}$, source=batch_range_${idim}$)
2223 303330 : static_range = .FALSE.
2224 : ELSE
2225 232704 : ALLOCATE (batch_range_prv_${idim}$ (2))
2226 232704 : batch_range_prv_${idim}$ (1) = 1
2227 232704 : batch_range_prv_${idim}$ (2) = tdims(${idim}$) + 1
2228 : END IF
2229 : END IF
2230 : #:endfor
2231 :
2232 127879 : ALLOCATE (tensor%contraction_storage)
2233 127879 : tensor%contraction_storage%static = static_range
2234 127879 : IF (static_range) THEN
2235 86249 : CALL dbt_tas_batched_mm_init(tensor%matrix_rep)
2236 : END IF
2237 127879 : tensor%contraction_storage%nsplit_avg = 0.0_dp
2238 127879 : tensor%contraction_storage%ibatch = 0
2239 :
2240 : #:for ndim in range(1, maxdim+1)
2241 255758 : IF (ndims_tensor(tensor) == ${ndim}$) THEN
2242 : CALL create_array_list(tensor%contraction_storage%batch_ranges, ${ndim}$, &
2243 127879 : ${varlist("batch_range_prv", nmax=ndim)}$)
2244 : END IF
2245 : #:endfor
2246 :
2247 127879 : END SUBROUTINE dbt_batched_contract_init
2248 :
2249 : ! **************************************************************************************************
2250 : !> \brief finalize batched contraction. This performs all communication that has been postponed in
2251 : !> the contraction calls.
2252 : !> \author Patrick Seewald
2253 : ! **************************************************************************************************
2254 255758 : SUBROUTINE dbt_batched_contract_finalize(tensor, unit_nr)
2255 : TYPE(dbt_type), INTENT(INOUT) :: tensor
2256 : INTEGER, INTENT(IN), OPTIONAL :: unit_nr
2257 : LOGICAL :: do_write
2258 : INTEGER :: unit_nr_prv, handle
2259 :
2260 127879 : CALL tensor%pgrid%mp_comm_2d%sync()
2261 127879 : CALL timeset("dbt_total", handle)
2262 127879 : unit_nr_prv = prep_output_unit(unit_nr)
2263 :
2264 127879 : do_write = .FALSE.
2265 :
2266 127879 : IF (tensor%contraction_storage%static) THEN
2267 86249 : IF (tensor%matrix_rep%do_batched > 0) THEN
2268 86249 : IF (tensor%matrix_rep%mm_storage%batched_out) do_write = .TRUE.
2269 : END IF
2270 86249 : CALL dbt_tas_batched_mm_finalize(tensor%matrix_rep)
2271 : END IF
2272 :
2273 127879 : IF (do_write .AND. unit_nr_prv /= 0) THEN
2274 16666 : IF (unit_nr_prv > 0) THEN
2275 : WRITE (unit_nr_prv, "(T2,A)") &
2276 0 : "FINALIZING BATCHED PROCESSING OF MATMUL"
2277 : END IF
2278 16666 : CALL dbt_write_tensor_info(tensor, unit_nr_prv)
2279 16666 : CALL dbt_write_tensor_dist(tensor, unit_nr_prv)
2280 : END IF
2281 :
2282 127879 : CALL destroy_array_list(tensor%contraction_storage%batch_ranges)
2283 127879 : DEALLOCATE (tensor%contraction_storage)
2284 127879 : CALL tensor%pgrid%mp_comm_2d%sync()
2285 127879 : CALL timestop(handle)
2286 :
2287 127879 : END SUBROUTINE dbt_batched_contract_finalize
2288 :
2289 : ! **************************************************************************************************
2290 : !> \brief change the process grid of a tensor
2291 : !> \param nodata optionally don't copy the tensor data (then tensor is empty on returned)
2292 : !> \param batch_range_i refers to the ith tensor dimension and contains all block indices starting
2293 : !> a new range. The size should be the number of ranges plus one, the last
2294 : !> element being the block index plus one of the last block in the last range.
2295 : !> For internal load balancing optimizations, optionally specify the index
2296 : !> ranges of batched contraction.
2297 : !> \author Patrick Seewald
2298 : ! **************************************************************************************************
2299 812 : SUBROUTINE dbt_change_pgrid(tensor, pgrid, ${varlist("batch_range")}$, &
2300 : nodata, pgrid_changed, unit_nr)
2301 : TYPE(dbt_type), INTENT(INOUT) :: tensor
2302 : TYPE(dbt_pgrid_type), INTENT(IN) :: pgrid
2303 : INTEGER, DIMENSION(:), OPTIONAL, INTENT(IN) :: ${varlist("batch_range")}$
2304 : !!
2305 : LOGICAL, INTENT(IN), OPTIONAL :: nodata
2306 : LOGICAL, INTENT(OUT), OPTIONAL :: pgrid_changed
2307 : INTEGER, INTENT(IN), OPTIONAL :: unit_nr
2308 : CHARACTER(LEN=*), PARAMETER :: routineN = 'dbt_change_pgrid'
2309 : CHARACTER(default_string_length) :: name
2310 : INTEGER :: handle
2311 812 : INTEGER, ALLOCATABLE, DIMENSION(:) :: ${varlist("bs")}$, &
2312 812 : ${varlist("dist")}$
2313 1624 : INTEGER, DIMENSION(ndims_tensor(tensor)) :: pcoord, pcoord_ref, pdims, pdims_ref, &
2314 1624 : tdims
2315 5684 : TYPE(dbt_type) :: t_tmp
2316 5684 : TYPE(dbt_distribution_type) :: dist
2317 1624 : INTEGER, DIMENSION(ndims_matrix_row(tensor)) :: map1
2318 : INTEGER, &
2319 1624 : DIMENSION(ndims_matrix_column(tensor)) :: map2
2320 1624 : LOGICAL, DIMENSION(ndims_tensor(tensor)) :: mem_aware
2321 812 : INTEGER, DIMENSION(ndims_tensor(tensor)) :: nbatch
2322 : INTEGER :: ind1, ind2, batch_size, ibatch
2323 :
2324 812 : IF (PRESENT(pgrid_changed)) pgrid_changed = .FALSE.
2325 812 : CALL mp_environ_pgrid(pgrid, pdims, pcoord)
2326 812 : CALL mp_environ_pgrid(tensor%pgrid, pdims_ref, pcoord_ref)
2327 :
2328 836 : IF (ALL(pdims == pdims_ref)) THEN
2329 8 : IF (ALLOCATED(pgrid%tas_split_info) .AND. ALLOCATED(tensor%pgrid%tas_split_info)) THEN
2330 8 : IF (pgrid%tas_split_info%ngroup == tensor%pgrid%tas_split_info%ngroup) THEN
2331 : RETURN
2332 : END IF
2333 : END IF
2334 : END IF
2335 :
2336 804 : CALL timeset(routineN, handle)
2337 :
2338 : #:for idim in range(1, maxdim+1)
2339 3216 : IF (ndims_tensor(tensor) >= ${idim}$) THEN
2340 2412 : mem_aware(${idim}$) = PRESENT(batch_range_${idim}$)
2341 2412 : IF (mem_aware(${idim}$)) nbatch(${idim}$) = SIZE(batch_range_${idim}$) - 1
2342 : END IF
2343 : #:endfor
2344 :
2345 804 : CALL dbt_get_info(tensor, nblks_total=tdims, name=name)
2346 :
2347 : #:for idim in range(1, maxdim+1)
2348 3216 : IF (ndims_tensor(tensor) >= ${idim}$) THEN
2349 7236 : ALLOCATE (bs_${idim}$ (dbt_nblks_total(tensor, ${idim}$)))
2350 2412 : CALL get_ith_array(tensor%blk_sizes, ${idim}$, bs_${idim}$)
2351 7236 : ALLOCATE (dist_${idim}$ (tdims(${idim}$)))
2352 2412 : dist_${idim}$ = 0
2353 2412 : IF (mem_aware(${idim}$)) THEN
2354 6556 : DO ibatch = 1, nbatch(${idim}$)
2355 4144 : ind1 = batch_range_${idim}$ (ibatch)
2356 4144 : ind2 = batch_range_${idim}$ (ibatch + 1) - 1
2357 4144 : batch_size = ind2 - ind1 + 1
2358 : CALL dbt_default_distvec(batch_size, pdims(${idim}$), &
2359 6556 : bs_${idim}$ (ind1:ind2), dist_${idim}$ (ind1:ind2))
2360 : END DO
2361 : ELSE
2362 0 : CALL dbt_default_distvec(tdims(${idim}$), pdims(${idim}$), bs_${idim}$, dist_${idim}$)
2363 : END IF
2364 : END IF
2365 : #:endfor
2366 :
2367 804 : CALL dbt_get_mapping_info(tensor%nd_index_blk, map1_2d=map1, map2_2d=map2)
2368 : #:for ndim in ndims
2369 1608 : IF (ndims_tensor(tensor) == ${ndim}$) THEN
2370 804 : CALL dbt_distribution_new(dist, pgrid, ${varlist("dist", nmax=ndim)}$)
2371 804 : CALL dbt_create(t_tmp, name, dist, map1, map2, ${varlist("bs", nmax=ndim)}$)
2372 : END IF
2373 : #:endfor
2374 804 : CALL dbt_distribution_destroy(dist)
2375 :
2376 804 : IF (PRESENT(nodata)) THEN
2377 0 : IF (.NOT. nodata) CALL dbt_copy_expert(tensor, t_tmp, move_data=.TRUE.)
2378 : ELSE
2379 804 : CALL dbt_copy_expert(tensor, t_tmp, move_data=.TRUE.)
2380 : END IF
2381 :
2382 804 : CALL dbt_copy_contraction_storage(tensor, t_tmp)
2383 :
2384 804 : CALL dbt_destroy(tensor)
2385 804 : tensor = t_tmp
2386 :
2387 804 : IF (PRESENT(unit_nr)) THEN
2388 804 : IF (unit_nr > 0) THEN
2389 0 : WRITE (unit_nr, "(T2,A,1X,A)") "OPTIMIZED PGRID INFO FOR", TRIM(tensor%name)
2390 0 : WRITE (unit_nr, "(T4,A,1X,3I6)") "process grid dimensions:", pdims
2391 0 : CALL dbt_write_split_info(pgrid, unit_nr)
2392 : END IF
2393 : END IF
2394 :
2395 804 : IF (PRESENT(pgrid_changed)) pgrid_changed = .TRUE.
2396 :
2397 804 : CALL timestop(handle)
2398 812 : END SUBROUTINE dbt_change_pgrid
2399 :
2400 : ! **************************************************************************************************
2401 : !> \brief map tensor to a new 2d process grid for the matrix representation.
2402 : !> \author Patrick Seewald
2403 : ! **************************************************************************************************
2404 812 : SUBROUTINE dbt_change_pgrid_2d(tensor, mp_comm, pdims, nodata, nsplit, dimsplit, pgrid_changed, unit_nr)
2405 : TYPE(dbt_type), INTENT(INOUT) :: tensor
2406 : TYPE(mp_cart_type), INTENT(IN) :: mp_comm
2407 : INTEGER, DIMENSION(2), INTENT(IN), OPTIONAL :: pdims
2408 : LOGICAL, INTENT(IN), OPTIONAL :: nodata
2409 : INTEGER, INTENT(IN), OPTIONAL :: nsplit, dimsplit
2410 : LOGICAL, INTENT(OUT), OPTIONAL :: pgrid_changed
2411 : INTEGER, INTENT(IN), OPTIONAL :: unit_nr
2412 1624 : INTEGER, DIMENSION(ndims_matrix_row(tensor)) :: map1
2413 1624 : INTEGER, DIMENSION(ndims_matrix_column(tensor)) :: map2
2414 1624 : INTEGER, DIMENSION(ndims_tensor(tensor)) :: dims, nbatches
2415 2436 : TYPE(dbt_pgrid_type) :: pgrid
2416 812 : INTEGER, DIMENSION(:), ALLOCATABLE :: ${varlist("batch_range")}$
2417 812 : INTEGER, DIMENSION(:), ALLOCATABLE :: array
2418 : INTEGER :: idim
2419 :
2420 812 : CALL dbt_get_mapping_info(tensor%pgrid%nd_index_grid, map1_2d=map1, map2_2d=map2)
2421 812 : CALL blk_dims_tensor(tensor, dims)
2422 :
2423 812 : IF (ALLOCATED(tensor%contraction_storage)) THEN
2424 : ASSOCIATE (batch_ranges => tensor%contraction_storage%batch_ranges)
2425 3248 : nbatches = sizes_of_arrays(tensor%contraction_storage%batch_ranges) - 1
2426 : ! for good load balancing the process grid dimensions should be chosen adapted to the
2427 : ! tensor dimenions. For batched contraction the tensor dimensions should be divided by
2428 : ! the number of batches (number of index ranges).
2429 4060 : DO idim = 1, ndims_tensor(tensor)
2430 2436 : CALL get_ith_array(tensor%contraction_storage%batch_ranges, idim, array)
2431 2436 : dims(idim) = array(nbatches(idim) + 1) - array(1)
2432 2436 : DEALLOCATE (array)
2433 2436 : dims(idim) = dims(idim)/nbatches(idim)
2434 3248 : IF (dims(idim) <= 0) dims(idim) = 1
2435 : END DO
2436 : END ASSOCIATE
2437 : END IF
2438 :
2439 812 : pgrid = dbt_nd_mp_comm(mp_comm, map1, map2, pdims_2d=pdims, tdims=dims, nsplit=nsplit, dimsplit=dimsplit)
2440 812 : IF (ALLOCATED(tensor%contraction_storage)) THEN
2441 : #:for ndim in range(1, maxdim+1)
2442 1624 : IF (ndims_tensor(tensor) == ${ndim}$) THEN
2443 812 : CALL get_arrays(tensor%contraction_storage%batch_ranges, ${varlist("batch_range", nmax=ndim)}$)
2444 : CALL dbt_change_pgrid(tensor, pgrid, ${varlist("batch_range", nmax=ndim)}$, &
2445 812 : nodata=nodata, pgrid_changed=pgrid_changed, unit_nr=unit_nr)
2446 : END IF
2447 : #:endfor
2448 : ELSE
2449 0 : CALL dbt_change_pgrid(tensor, pgrid, nodata=nodata, pgrid_changed=pgrid_changed, unit_nr=unit_nr)
2450 : END IF
2451 812 : CALL dbt_pgrid_destroy(pgrid)
2452 :
2453 812 : END SUBROUTINE dbt_change_pgrid_2d
2454 :
2455 81865 : END MODULE dbt_methods
|