LCOV - code coverage report
Current view: top level - src/dbm - dbm_matrix.c (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 96.7 % 338 327
Test Date: 2026-11-04 00:58:17 Functions: 96.7 % 30 29

            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: BSD-3-Clause                                     */
       6              : /*----------------------------------------------------------------------------*/
       7              : #include "dbm_matrix.h"
       8              : #include "dbm_hyperparams.h"
       9              : 
      10              : #include <assert.h>
      11              : #include <math.h>
      12              : #include <omp.h>
      13              : #include <stdbool.h>
      14              : #include <stddef.h>
      15              : #include <stdlib.h>
      16              : #include <string.h>
      17              : 
      18              : /*******************************************************************************
      19              :  * \brief Creates a new matrix.
      20              :  * \author Ole Schuett
      21              :  ******************************************************************************/
      22      2692336 : void dbm_create(dbm_matrix_t **matrix_out, dbm_distribution_t *dist,
      23              :                 const char name[], const int nrows, const int ncols,
      24              :                 const int row_sizes[nrows], const int col_sizes[ncols]) {
      25      2692336 :   assert(omp_get_num_threads() == 1);
      26              : 
      27      2692336 :   dbm_matrix_t *const matrix = calloc(1, sizeof(dbm_matrix_t));
      28              : 
      29      2692336 :   assert(dist->rows.length == nrows);
      30      2692336 :   assert(dist->cols.length == ncols);
      31      2692336 :   dbm_distribution_hold(dist);
      32      2692336 :   matrix->dist = dist;
      33              : 
      34      2692336 :   size_t size = (strlen(name) + 1) * sizeof(char);
      35      2692336 :   matrix->name = malloc(size);
      36      2692336 :   assert(matrix->name != NULL && name != NULL);
      37      2692336 :   memcpy(matrix->name, name, size);
      38              : 
      39      2692336 :   matrix->nrows = nrows;
      40      2692336 :   matrix->ncols = ncols;
      41              : 
      42      2692336 :   size = nrows * sizeof(int);
      43      2692336 :   matrix->row_sizes = malloc(size);
      44      2692336 :   assert(matrix->row_sizes != NULL || size == 0);
      45      2692336 :   if (size != 0) {
      46      2691394 :     memcpy(matrix->row_sizes, row_sizes, size);
      47              :   }
      48              : 
      49      2692336 :   size = ncols * sizeof(int);
      50      2692336 :   matrix->col_sizes = malloc(size);
      51      2692336 :   assert(matrix->col_sizes != NULL || size == 0);
      52      2692336 :   if (size != 0) {
      53      2686091 :     memcpy(matrix->col_sizes, col_sizes, size);
      54              :   }
      55              : 
      56      2692336 :   const int num_shards = dbm_get_num_shards(matrix);
      57      2692336 :   matrix->shards = malloc(num_shards * sizeof(dbm_shard_t));
      58      2692336 :   assert(matrix->shards != NULL || num_shards == 0);
      59      2692336 : #pragma omp parallel for
      60              :   for (int ishard = 0; ishard < num_shards; ishard++) {
      61              :     dbm_shard_init(&matrix->shards[ishard]);
      62              :   }
      63              : 
      64      2692336 :   assert(*matrix_out == NULL);
      65      2692336 :   *matrix_out = matrix;
      66      2692336 : }
      67              : 
      68              : /*******************************************************************************
      69              :  * \brief Releases a matrix and all its ressources.
      70              :  * \author Ole Schuett
      71              :  ******************************************************************************/
      72      2692336 : void dbm_release(dbm_matrix_t *matrix) {
      73      2692336 :   assert(omp_get_num_threads() == 1);
      74      5384672 :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
      75      2692336 :     dbm_shard_release(&matrix->shards[ishard]);
      76              :   }
      77      2692336 :   dbm_distribution_release(matrix->dist);
      78      2692336 :   free(matrix->row_sizes);
      79      2692336 :   free(matrix->col_sizes);
      80      2692336 :   free(matrix->shards);
      81      2692336 :   free(matrix->name);
      82      2692336 :   free(matrix);
      83      2692336 : }
      84              : 
      85              : /*******************************************************************************
      86              :  * \brief Copies content of matrix_b into matrix_a.
      87              :  *        Matrices must have the same row/col block sizes and distribution.
      88              :  * \author Ole Schuett
      89              :  ******************************************************************************/
      90       704694 : void dbm_copy(dbm_matrix_t *matrix_a, const dbm_matrix_t *matrix_b) {
      91       704694 :   assert(omp_get_num_threads() == 1);
      92              : 
      93       704694 :   assert(matrix_b->nrows == matrix_a->nrows);
      94      6779872 :   for (int i = 0; i < matrix_b->nrows; i++) {
      95      6075178 :     assert(matrix_b->row_sizes[i] == matrix_a->row_sizes[i]);
      96              :   }
      97       704694 :   assert(matrix_b->ncols == matrix_a->ncols);
      98     32343650 :   for (int i = 0; i < matrix_b->ncols; i++) {
      99     31638956 :     assert(matrix_b->col_sizes[i] == matrix_a->col_sizes[i]);
     100              :   }
     101              : 
     102       704694 :   assert(matrix_a->dist == matrix_b->dist);
     103              : 
     104       704694 : #pragma omp parallel for DBM_OMP_SCHEDULE
     105              :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix_a); ishard++) {
     106              :     dbm_shard_copy(&matrix_a->shards[ishard], &matrix_b->shards[ishard]);
     107              :   }
     108       704694 : }
     109              : 
     110              : /*******************************************************************************
     111              :  * \brief Copies content of matrix_b into matrix_a.
     112              :  *        Matrices may have different distributions.
     113              :  * \author Ole Schuett
     114              :  ******************************************************************************/
     115          144 : void dbm_redistribute(const dbm_matrix_t *matrix, dbm_matrix_t *redist) {
     116          144 :   assert(omp_get_num_threads() == 1);
     117          144 :   assert(matrix->nrows == redist->nrows);
     118         6384 :   for (int i = 0; i < matrix->nrows; i++) {
     119         6240 :     assert(matrix->row_sizes[i] == redist->row_sizes[i]);
     120              :   }
     121          144 :   assert(matrix->ncols == redist->ncols);
     122         6384 :   for (int i = 0; i < matrix->ncols; i++) {
     123         6240 :     assert(matrix->col_sizes[i] == redist->col_sizes[i]);
     124              :   }
     125              : 
     126          144 :   assert(cp_mpi_comms_are_similar(matrix->dist->comm, redist->dist->comm));
     127          144 :   const cp_mpi_comm_t comm = redist->dist->comm;
     128          144 :   const int nranks = cp_mpi_comm_size(comm);
     129              : 
     130              :   // 1st pass: Compute send_count.
     131          144 :   int send_count[nranks];
     132          144 :   memset(send_count, 0, nranks * sizeof(int));
     133          288 :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     134          144 :     const dbm_shard_t *const shard = &matrix->shards[ishard];
     135         8936 :     for (int iblock = 0; iblock < shard->nblocks; iblock++) {
     136         8792 :       const dbm_block_t *const blk = &shard->blocks[iblock];
     137         8792 :       const int row_size = matrix->row_sizes[blk->row];
     138         8792 :       const int col_size = matrix->col_sizes[blk->col];
     139         8792 :       const int block_size = row_size * col_size;
     140         8792 :       const int rank = dbm_get_stored_coordinates(redist, blk->row, blk->col);
     141         8792 :       assert(0 <= rank && rank < nranks);
     142         8792 :       send_count[rank] += 2 + block_size;
     143              :     }
     144              :   }
     145              : 
     146              :   // 1st communication: Exchange counts.
     147          144 :   int recv_count[nranks];
     148          144 :   cp_mpi_alltoall_int(send_count, 1, recv_count, 1, comm);
     149              : 
     150              :   // Compute displacements and allocate data buffers.
     151          144 :   int send_displ[nranks + 1], recv_displ[nranks + 1];
     152          144 :   send_displ[0] = recv_displ[0] = 0;
     153          432 :   for (int irank = 1; irank <= nranks; irank++) {
     154          288 :     send_displ[irank] = send_displ[irank - 1] + send_count[irank - 1];
     155          288 :     recv_displ[irank] = recv_displ[irank - 1] + recv_count[irank - 1];
     156              :   }
     157          144 :   const int total_send_count = send_displ[nranks];
     158          144 :   const int total_recv_count = recv_displ[nranks];
     159          144 :   double *const data_send = cp_mpi_alloc_mem(total_send_count * sizeof(double));
     160          144 :   double *const data_recv = cp_mpi_alloc_mem(total_recv_count * sizeof(double));
     161              : 
     162              :   // 2nd pass: Fill send_data.
     163          144 :   int send_data_positions[nranks];
     164          144 :   memcpy(send_data_positions, send_displ, nranks * sizeof(int));
     165          288 :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     166          144 :     const dbm_shard_t *const shard = &matrix->shards[ishard];
     167         8936 :     for (int iblock = 0; iblock < shard->nblocks; iblock++) {
     168         8792 :       const dbm_block_t *const blk = &shard->blocks[iblock];
     169         8792 :       const double *const blk_data = &shard->data[blk->offset];
     170         8792 :       const int row_size = matrix->row_sizes[blk->row];
     171         8792 :       const int col_size = matrix->col_sizes[blk->col];
     172         8792 :       const int block_size = row_size * col_size;
     173         8792 :       const int rank = dbm_get_stored_coordinates(redist, blk->row, blk->col);
     174         8792 :       const int pos = send_data_positions[rank];
     175         8792 :       data_send[pos + 0] = blk->row; // send integers as doubles
     176         8792 :       data_send[pos + 1] = blk->col;
     177         8792 :       memcpy(&data_send[pos + 2], blk_data, block_size * sizeof(double));
     178         8792 :       send_data_positions[rank] += 2 + block_size;
     179              :     }
     180              :   }
     181          432 :   for (int irank = 0; irank < nranks; irank++) {
     182          288 :     assert(send_data_positions[irank] == send_displ[irank + 1]);
     183              :   }
     184              : 
     185              :   // 2nd communication: Exchange data.
     186          144 :   cp_mpi_alltoallv_double(data_send, send_count, send_displ, data_recv,
     187              :                           recv_count, recv_displ, comm);
     188          144 :   cp_mpi_free_mem(data_send);
     189              : 
     190              :   // 3rd pass: Unpack data.
     191          144 :   dbm_clear(redist);
     192          144 :   int recv_data_pos = 0;
     193         8936 :   while (recv_data_pos < total_recv_count) {
     194         8792 :     const int row = (int)data_recv[recv_data_pos + 0];
     195         8792 :     const int col = (int)data_recv[recv_data_pos + 1];
     196         8792 :     assert(data_recv[recv_data_pos + 0] == (double)row);
     197         8792 :     assert(data_recv[recv_data_pos + 1] == (double)col);
     198         8792 :     dbm_put_block(redist, row, col, false, &data_recv[recv_data_pos + 2]);
     199         8792 :     const int row_size = matrix->row_sizes[row];
     200         8792 :     const int col_size = matrix->col_sizes[col];
     201         8792 :     const int block_size = row_size * col_size;
     202         8792 :     recv_data_pos += 2 + block_size;
     203              :   }
     204          144 :   assert(recv_data_pos == total_recv_count);
     205          144 :   cp_mpi_free_mem(data_recv);
     206          144 : }
     207              : 
     208              : /*******************************************************************************
     209              :  * \brief Looks up a block from given matrics. This routine is thread-safe.
     210              :  *        If the block is not found then a null pointer is returned.
     211              :  * \author Ole Schuett
     212              :  ******************************************************************************/
     213     19352608 : void dbm_get_block_p(dbm_matrix_t *matrix, const int row, const int col,
     214              :                      double **block, int *row_size, int *col_size) {
     215     19352608 :   assert(0 <= row && row < matrix->nrows);
     216     19352608 :   assert(0 <= col && col < matrix->ncols);
     217     19352608 :   assert(dbm_get_stored_coordinates(matrix, row, col) == matrix->dist->my_rank);
     218     19352608 :   *row_size = matrix->row_sizes[row];
     219     19352608 :   *col_size = matrix->col_sizes[col];
     220     19352608 :   *block = NULL;
     221              : 
     222     19352608 :   const int ishard = dbm_get_shard_index(matrix, row, col);
     223     19352608 :   const dbm_shard_t *const shard = &matrix->shards[ishard];
     224     19352608 :   const dbm_block_t *const blk = dbm_shard_lookup(shard, row, col);
     225     19352608 :   if (blk != NULL) {
     226     17727682 :     *block = &shard->data[blk->offset];
     227              :   }
     228     19352608 : }
     229              : 
     230              : /*******************************************************************************
     231              :  * \brief Adds a block to given matrix. This routine is thread-safe.
     232              :  *        If block already exist then it gets overwritten (or summed).
     233              :  * \author Ole Schuett
     234              :  ******************************************************************************/
     235     36328463 : void dbm_put_block(dbm_matrix_t *matrix, const int row, const int col,
     236              :                    const bool summation, const double *block) {
     237     36328463 :   assert(0 <= row && row < matrix->nrows);
     238     36328463 :   assert(0 <= col && col < matrix->ncols);
     239     36328463 :   assert(dbm_get_stored_coordinates(matrix, row, col) == matrix->dist->my_rank);
     240     36328463 :   const int row_size = matrix->row_sizes[row];
     241     36328463 :   const int col_size = matrix->col_sizes[col];
     242     36328463 :   const int block_size = row_size * col_size;
     243              : 
     244     36328463 :   const int ishard = dbm_get_shard_index(matrix, row, col);
     245     36328463 :   dbm_shard_t *const shard = &matrix->shards[ishard];
     246     36328463 :   omp_set_lock(&shard->lock);
     247     36328463 :   const dbm_block_t *const blk =
     248     36328463 :       dbm_shard_get_or_allocate_block(shard, row, col, block_size);
     249     36328463 :   double *const blk_data = &shard->data[blk->offset];
     250     36328463 :   if (summation) {
     251      4531254 :     assert(blk_data != NULL || 0 == block_size);
     252   1615089585 :     for (int i = 0; i < block_size; i++) {
     253   1610558331 :       blk_data[i] += block[i];
     254              :     }
     255     31797209 :   } else if (block_size != 0) {
     256     31792110 :     memcpy(blk_data, block, block_size * sizeof(double));
     257              :   }
     258     36328463 :   omp_unset_lock(&shard->lock);
     259     36328463 : }
     260              : 
     261              : /*******************************************************************************
     262              :  * \brief Remove all blocks from matrix, but does not release underlying memory.
     263              :  * \author Ole Schuett
     264              :  ******************************************************************************/
     265      3063996 : void dbm_clear(dbm_matrix_t *matrix) {
     266      3063996 :   assert(omp_get_num_threads() == 1);
     267              : 
     268      3063996 : #pragma omp parallel for DBM_OMP_SCHEDULE
     269              :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     270              :     dbm_shard_t *const shard = &matrix->shards[ishard];
     271              :     shard->data_promised = shard->data_size = shard->nblocks = 0;
     272              :     // Does not deallocate memory, hence data_allocated remains unchanged.
     273              :     memset(shard->hashtable, 0, shard->hashtable_size * sizeof(int));
     274              :   }
     275      3063996 : }
     276              : 
     277              : /*******************************************************************************
     278              :  * \brief Removes all blocks from the matrix whose norm is below the threshold.
     279              :  *        Blocks of size zero are always kept.
     280              :  * \author Ole Schuett
     281              :  ******************************************************************************/
     282       939434 : void dbm_filter(dbm_matrix_t *matrix, const double eps) {
     283       939434 :   assert(omp_get_num_threads() == 1);
     284              : 
     285       939434 :   if (eps == 0.0) {
     286              :     return;
     287              :   }
     288       862416 :   const double eps2 = eps * eps;
     289              : 
     290       862416 : #pragma omp parallel for DBM_OMP_SCHEDULE
     291              :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     292              :     dbm_shard_t *const shard = &matrix->shards[ishard];
     293              :     const int old_nblocks = shard->nblocks;
     294              :     shard->nblocks = shard->data_promised = 0;
     295              :     memset(shard->hashtable, 0, shard->hashtable_size * sizeof(int));
     296              : 
     297              :     for (int iblock = 0; iblock < old_nblocks; iblock++) {
     298              :       const dbm_block_t old_blk = shard->blocks[iblock];
     299              :       const double *old_blk_data = &shard->data[old_blk.offset];
     300              :       const int row_size = matrix->row_sizes[old_blk.row];
     301              :       const int col_size = matrix->col_sizes[old_blk.col];
     302              :       const int block_size = row_size * col_size;
     303              :       double norm = 0.0;
     304              :       for (int i = 0; i < block_size; i++) {
     305              :         const double value = old_blk_data[i];
     306              :         norm += value * value;
     307              :         if (eps2 <= norm) {
     308              :           break;
     309              :         }
     310              :       }
     311              :       // For historic reasons zero-sized blocks are never filtered.
     312              :       if (block_size > 0 && norm < eps2) {
     313              :         continue; // filter the block
     314              :       }
     315              :       // Re-create block.
     316              :       dbm_block_t *const new_blk = dbm_shard_promise_new_block(
     317              :           shard, old_blk.row, old_blk.col, block_size);
     318              :       assert(new_blk->offset <= old_blk.offset);
     319              :       if (new_blk->offset != old_blk.offset) {
     320              :         // Using memmove because it can handle overlapping buffers.
     321              :         double *new_blk_data = &shard->data[new_blk->offset];
     322              :         memmove(new_blk_data, old_blk_data, block_size * sizeof(double));
     323              :       }
     324              :     }
     325              :     shard->data_size = shard->data_promised;
     326              :     // TODO: Could call realloc to release excess memory.
     327              :   }
     328              : }
     329              : 
     330              : /*******************************************************************************
     331              :  * \brief Adds list of blocks efficiently. The blocks will be filled with zeros.
     332              :  *        This routine must always be called within an OpenMP parallel region.
     333              :  * \author Ole Schuett and Hans Pabst
     334              :  ******************************************************************************/
     335      2048114 : void dbm_reserve_blocks(dbm_matrix_t *matrix, const int nblocks,
     336              :                         const int rows[], const int cols[]) {
     337      2048114 :   assert(omp_get_num_threads() == omp_get_max_threads() &&
     338              :          "Please call dbm_reserve_blocks within an OpenMP parallel region.");
     339      2048114 :   const int my_rank = matrix->dist->my_rank;
     340      2048114 :   const int nshards = dbm_get_num_shards(matrix);
     341      2048114 :   const int nthreads = omp_get_num_threads();
     342      2048114 :   const int ithread = omp_get_thread_num();
     343              :   // Shared by the threads: blocks are binned by shard such that a single
     344              :   // thread owns a shard (no locks).
     345      2048114 :   static int *reserve_offset = NULL; // per thread and shard
     346      2048114 :   static int *reserve_start = NULL;  // per shard (and end)
     347      2048114 :   static int *reserve_rowcol = NULL; // row/col pairs grouped by shard
     348              : 
     349              : #pragma omp single
     350              :   {
     351      2048114 :     reserve_offset = calloc((size_t)nthreads * nshards, sizeof(int));
     352      2048114 :     reserve_start = malloc((nshards + 1) * sizeof(int));
     353      2048114 :     assert(reserve_offset != NULL && reserve_start != NULL);
     354              :   }
     355      2048114 :   int *const offset = reserve_offset + (size_t)ithread * nshards;
     356     34245834 :   for (int i = 0; i < nblocks; i++) {
     357     32197720 :     const int row = rows[i], col = cols[i];
     358     32197720 :     assert(0 <= row && row < matrix->nrows);
     359     32197720 :     assert(0 <= col && col < matrix->ncols);
     360     32197720 :     assert(dbm_get_stored_coordinates(matrix, row, col) == my_rank);
     361     32197720 :     ++offset[dbm_get_shard_index(matrix, row, col)];
     362              :   }
     363      2048114 : #pragma omp barrier
     364              : 
     365              : #pragma omp single
     366              :   { // exclusive prefix sum over shards (major) and threads (minor)
     367              :     int total = 0;
     368      4096228 :     for (int ishard = 0; ishard < nshards; ishard++) {
     369      2048114 :       reserve_start[ishard] = total;
     370      4096228 :       for (int i = 0; i < nthreads; i++) {
     371      2048114 :         int *const count = reserve_offset + (size_t)i * nshards + ishard;
     372      2048114 :         const int n = *count;
     373      2048114 :         *count = total;
     374      2048114 :         total += n;
     375              :       }
     376              :     }
     377      2048114 :     reserve_start[nshards] = total;
     378      4096228 :     reserve_rowcol =
     379      2048114 :         (0 < total ? malloc(2 * (size_t)total * sizeof(int)) : NULL);
     380      2048114 :     assert(reserve_rowcol != NULL || 0 == total);
     381              :   }
     382     34245834 :   for (int i = 0; i < nblocks; i++) {
     383     32197720 :     const int j = offset[dbm_get_shard_index(matrix, rows[i], cols[i])]++;
     384     32197720 :     reserve_rowcol[2 * j + 0] = rows[i];
     385     32197720 :     reserve_rowcol[2 * j + 1] = cols[i];
     386              :   }
     387      2048114 : #pragma omp barrier
     388              : 
     389              : #pragma omp for DBM_OMP_SCHEDULE
     390      2048114 :   for (int ishard = 0; ishard < nshards; ishard++) {
     391      2048114 :     dbm_shard_t *const shard = &matrix->shards[ishard];
     392              :     // one rebuild of the hashtable rather than one per growth
     393      2048114 :     dbm_shard_reserve(shard, shard->nblocks + reserve_start[ishard + 1] -
     394      2048114 :                                  reserve_start[ishard]);
     395     34245834 :     for (int j = reserve_start[ishard]; j < reserve_start[ishard + 1]; j++) {
     396     32197720 :       const int row = reserve_rowcol[2 * j + 0];
     397     32197720 :       const int col = reserve_rowcol[2 * j + 1];
     398     32197720 :       const int block_size = matrix->row_sizes[row] * matrix->col_sizes[col];
     399     32197720 :       dbm_shard_get_or_promise_block(shard, row, col, block_size);
     400              :     }
     401      2048114 :     dbm_shard_allocate_promised_blocks(shard);
     402              :   }
     403              : 
     404              : #pragma omp single
     405              :   {
     406      2048114 :     free(reserve_rowcol);
     407      2048114 :     free(reserve_start);
     408      2048114 :     free(reserve_offset);
     409      2048114 :     reserve_rowcol = reserve_start = reserve_offset = NULL;
     410              :   }
     411      2048114 : }
     412              : 
     413              : /*******************************************************************************
     414              :  * \brief Multiplies all entries in the given matrix by the given factor alpha.
     415              :  * \author Ole Schuett
     416              :  ******************************************************************************/
     417       768009 : void dbm_scale(dbm_matrix_t *matrix, const double alpha) {
     418       768009 :   assert(omp_get_num_threads() == 1);
     419       768009 :   if (alpha == 1.0) {
     420              :     return;
     421              :   }
     422       581123 :   if (alpha == 0.0) {
     423       567003 :     dbm_zero(matrix);
     424       567003 :     return;
     425              :   }
     426              : 
     427        14120 : #pragma omp parallel for DBM_OMP_SCHEDULE
     428              :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     429              :     dbm_shard_t *const shard = &matrix->shards[ishard];
     430              :     for (int i = 0; i < shard->data_size; i++) {
     431              :       shard->data[i] *= alpha;
     432              :     }
     433              :   }
     434              : }
     435              : 
     436              : /*******************************************************************************
     437              :  * \brief Sets all blocks in the given matrix to zero.
     438              :  * \author Ole Schuett
     439              :  ******************************************************************************/
     440       567003 : void dbm_zero(dbm_matrix_t *matrix) {
     441       567003 :   assert(omp_get_num_threads() == 1);
     442              : 
     443       567003 : #pragma omp parallel for DBM_OMP_SCHEDULE
     444              :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     445              :     dbm_shard_t *const shard = &matrix->shards[ishard];
     446              :     if (shard->data != NULL) {
     447              :       memset(shard->data, 0, shard->data_size * sizeof(double));
     448              :     }
     449              :   }
     450       567003 : }
     451              : 
     452              : /*******************************************************************************
     453              :  * \brief Adds matrix_b to matrix_a.
     454              :  * \author Ole Schuett
     455              :  ******************************************************************************/
     456       349908 : void dbm_add(dbm_matrix_t *matrix_a, const dbm_matrix_t *matrix_b) {
     457       349908 :   assert(omp_get_num_threads() == 1);
     458       349908 :   assert(matrix_a->dist == matrix_b->dist);
     459              : 
     460       349908 : #pragma omp parallel for DBM_OMP_SCHEDULE
     461              :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix_b); ishard++) {
     462              :     dbm_shard_t *const shard_a = &matrix_a->shards[ishard];
     463              :     const dbm_shard_t *shard_b = &matrix_b->shards[ishard];
     464              :     for (int iblock = 0; iblock < shard_b->nblocks; iblock++) {
     465              :       const dbm_block_t blk_b = shard_b->blocks[iblock];
     466              :       const int row_size = matrix_b->row_sizes[blk_b.row];
     467              :       const int col_size = matrix_b->col_sizes[blk_b.col];
     468              :       assert(row_size == matrix_a->row_sizes[blk_b.row]);
     469              :       assert(col_size == matrix_a->col_sizes[blk_b.col]);
     470              :       const int block_size = row_size * col_size;
     471              :       const dbm_block_t *const blk_a = dbm_shard_get_or_allocate_block(
     472              :           shard_a, blk_b.row, blk_b.col, block_size);
     473              :       double *const data_a = &shard_a->data[blk_a->offset];
     474              :       const double *const data_b = &shard_b->data[blk_b.offset];
     475              :       for (int i = 0; i < block_size; i++) {
     476              :         data_a[i] += data_b[i];
     477              :       }
     478              :     }
     479              :   }
     480       349908 : }
     481              : 
     482              : /*******************************************************************************
     483              :  * \brief Creates an iterator for the blocks of the given matrix.
     484              :  *        The iteration order is not stable.
     485              :  *        This routine must always be called within an OpenMP parallel region.
     486              :  * \author Ole Schuett
     487              :  ******************************************************************************/
     488      5044712 : void dbm_iterator_start(dbm_iterator_t **iter_out, const dbm_matrix_t *matrix) {
     489      5044712 :   assert(omp_get_num_threads() == omp_get_max_threads() &&
     490              :          "Please call dbm_iterator_start within an OpenMP parallel region.");
     491      5044712 :   dbm_iterator_t *iter = malloc(sizeof(dbm_iterator_t));
     492      5044712 :   assert(iter != NULL);
     493      5044712 :   iter->matrix = matrix;
     494      5044712 :   iter->next_block = 0;
     495      5044712 :   iter->next_shard = omp_get_thread_num();
     496      5796353 :   while (iter->next_shard < dbm_get_num_shards(matrix) &&
     497      5044712 :          matrix->shards[iter->next_shard].nblocks == 0) {
     498       751641 :     iter->next_shard += omp_get_num_threads();
     499              :   }
     500      5044712 :   assert(*iter_out == NULL);
     501      5044712 :   *iter_out = iter;
     502      5044712 : }
     503              : 
     504              : /*******************************************************************************
     505              :  * \brief Returns number of blocks the iterator will provide to calling thread.
     506              :  * \author Ole Schuett
     507              :  ******************************************************************************/
     508       787744 : int dbm_iterator_num_blocks(const dbm_iterator_t *iter) {
     509       787744 :   int num_blocks = 0;
     510       787744 :   for (int ishard = omp_get_thread_num();
     511      1575488 :        ishard < dbm_get_num_shards(iter->matrix);
     512       787744 :        ishard += omp_get_num_threads()) {
     513       787744 :     num_blocks += iter->matrix->shards[ishard].nblocks;
     514              :   }
     515       787744 :   return num_blocks;
     516              : }
     517              : 
     518              : /*******************************************************************************
     519              :  * \brief Tests whether the given iterator has any block left.
     520              :  * \author Ole Schuett
     521              :  ******************************************************************************/
     522     85443316 : bool dbm_iterator_blocks_left(const dbm_iterator_t *iter) {
     523     85443316 :   return iter->next_shard < dbm_get_num_shards(iter->matrix);
     524              : }
     525              : 
     526              : /*******************************************************************************
     527              :  * \brief Returns the next block from the given iterator.
     528              :  * \author Ole Schuett
     529              :  ******************************************************************************/
     530     91330041 : void dbm_iterator_next_block(dbm_iterator_t *iter, int *row, int *col,
     531              :                              double **block, int *row_size, int *col_size) {
     532     91330041 :   const dbm_matrix_t *matrix = iter->matrix;
     533     91330041 :   assert(iter->next_shard < dbm_get_num_shards(matrix));
     534     91330041 :   const dbm_shard_t *shard = &matrix->shards[iter->next_shard];
     535     91330041 :   assert(iter->next_block < shard->nblocks);
     536     91330041 :   dbm_block_t *blk = &shard->blocks[iter->next_block];
     537              : 
     538     91330041 :   *row = blk->row;
     539     91330041 :   *col = blk->col;
     540     91330041 :   *row_size = matrix->row_sizes[blk->row];
     541     91330041 :   *col_size = matrix->col_sizes[blk->col];
     542     91330041 :   *block = &shard->data[blk->offset];
     543              : 
     544     91330041 :   iter->next_block++;
     545     91330041 :   if (iter->next_block >= shard->nblocks) {
     546              :     // Advance to the next non-empty shard...
     547      4293071 :     iter->next_shard += omp_get_num_threads();
     548      4293071 :     while (iter->next_shard < dbm_get_num_shards(matrix) &&
     549            0 :            matrix->shards[iter->next_shard].nblocks == 0) {
     550            0 :       iter->next_shard += omp_get_num_threads();
     551              :     }
     552      4293071 :     iter->next_block = 0; // ...and continue with its first block.
     553              :   }
     554     91330041 : }
     555              : 
     556              : /*******************************************************************************
     557              :  * \brief Releases the given iterator.
     558              :  * \author Ole Schuett
     559              :  ******************************************************************************/
     560      5044712 : void dbm_iterator_stop(dbm_iterator_t *iter) { free(iter); }
     561              : 
     562              : /*******************************************************************************
     563              :  * \brief Private routine to accumulate using Kahan's summation.
     564              :  * \author Hans Pabst
     565              :  ******************************************************************************/
     566     14383468 : static double kahan_sum(double value, double *accumulator,
     567              :                         double *compensation) {
     568     14383468 :   double r, c;
     569     14383468 :   assert(NULL != accumulator && NULL != compensation);
     570     14383468 :   c = value - *compensation;
     571     14383468 :   r = *accumulator + c;
     572     14383468 :   *compensation = (r - *accumulator) - c;
     573     14383468 :   *accumulator = r;
     574     14383468 :   return r;
     575              : }
     576              : 
     577              : /*******************************************************************************
     578              :  * \brief Computes a checksum of the given matrix.
     579              :  * \author Ole Schuett
     580              :  ******************************************************************************/
     581          190 : double dbm_checksum(const dbm_matrix_t *matrix) {
     582          190 :   double checksum = 0.0, compensation = 0.0;
     583          380 :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     584          190 :     const dbm_shard_t *shard = &matrix->shards[ishard];
     585     14383658 :     for (int i = 0; i < shard->data_size; i++) {
     586     14383468 :       const double value = shard->data[i];
     587     14383468 :       kahan_sum(value * value, &checksum, &compensation);
     588              :     }
     589              :   }
     590          190 :   cp_mpi_sum_double(&checksum, 1, matrix->dist->comm);
     591          190 :   return checksum;
     592              : }
     593              : 
     594              : /*******************************************************************************
     595              :  * \brief Returns the largest absolute value of all matrix elements.
     596              :  * \author Ole Schuett
     597              :  ******************************************************************************/
     598           48 : double dbm_maxabs(const dbm_matrix_t *matrix) {
     599           48 :   double maxabs = 0.0;
     600           96 :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     601           48 :     const dbm_shard_t *shard = &matrix->shards[ishard];
     602      1599050 :     for (int i = 0; i < shard->data_size; i++) {
     603      1599002 :       maxabs = fmax(maxabs, fabs(shard->data[i]));
     604              :     }
     605              :   }
     606           48 :   cp_mpi_max_double(&maxabs, 1, matrix->dist->comm);
     607           48 :   return maxabs;
     608              : }
     609              : 
     610              : /*******************************************************************************
     611              :  * \brief Calculates maximum relative difference between matrix_a and matrix_b.
     612              :  * \author Hans Pabst
     613              :  ******************************************************************************/
     614            0 : double dbm_maxeps(const dbm_matrix_t *matrix_a, const dbm_matrix_t *matrix_b) {
     615            0 :   const int num_shards = dbm_get_num_shards(matrix_a);
     616            0 :   double epsilon = 0;
     617              : 
     618            0 :   assert(omp_get_num_threads() == 1);
     619            0 :   assert(matrix_a->dist == matrix_b->dist);
     620            0 :   assert(num_shards == dbm_get_num_shards(matrix_b));
     621              : 
     622              :   // Handles asymmetric sparsity: blocks in A but not B are compared against
     623              :   // zero, and blocks in B but not A are checked in a second pass. The
     624              :   // reduction(max) avoids the previous data race on the shared epsilon.
     625            0 : #pragma omp parallel for reduction(max : epsilon) DBM_OMP_SCHEDULE
     626              :   for (int ishard = 0; ishard < num_shards; ++ishard) {
     627              :     const dbm_shard_t *const shard_a = &matrix_a->shards[ishard];
     628              :     const dbm_shard_t *const shard_b = &matrix_b->shards[ishard];
     629              :     for (int iblock = 0; iblock < shard_a->nblocks; ++iblock) {
     630              :       const dbm_block_t *const blk_a = &shard_a->blocks[iblock];
     631              :       const dbm_block_t *const blk_b =
     632              :           dbm_shard_lookup(shard_b, blk_a->row, blk_a->col);
     633              :       const int row_size = matrix_a->row_sizes[blk_a->row];
     634              :       const int col_size = matrix_a->col_sizes[blk_a->col];
     635              :       assert(NULL == blk_b || row_size == matrix_b->row_sizes[blk_b->row]);
     636              :       assert(NULL == blk_b || col_size == matrix_b->col_sizes[blk_b->col]);
     637              :       const double *const data_a = &shard_a->data[blk_a->offset];
     638              :       const double *const data_b =
     639              :           (NULL == blk_b) ? NULL : &shard_b->data[blk_b->offset];
     640              :       const int block_size = row_size * col_size;
     641              :       for (int i = 0; i < block_size; ++i) {
     642              :         const double d = data_a[i] - (NULL == data_b ? 0.0 : data_b[i]);
     643              :         const double e = fabs(0 != data_a[i] ? (d / data_a[i]) : d);
     644              :         if (epsilon < e) {
     645              :           epsilon = e;
     646              :         }
     647              :       }
     648              :     }
     649              :     // Second pass: blocks present in B but absent from A (treat A as zero).
     650              :     for (int iblock = 0; iblock < shard_b->nblocks; ++iblock) {
     651              :       const dbm_block_t *const blk_b = &shard_b->blocks[iblock];
     652              :       if (NULL != dbm_shard_lookup(shard_a, blk_b->row, blk_b->col)) {
     653              :         continue;
     654              :       }
     655              :       const int row_size = matrix_b->row_sizes[blk_b->row];
     656              :       const int col_size = matrix_b->col_sizes[blk_b->col];
     657              :       const double *const data_b = &shard_b->data[blk_b->offset];
     658              :       const int block_size = row_size * col_size;
     659              :       for (int i = 0; i < block_size; ++i) {
     660              :         const double e = fabs(data_b[i]);
     661              :         if (epsilon < e) {
     662              :           epsilon = e;
     663              :         }
     664              :       }
     665              :     }
     666              :   }
     667              : 
     668            0 :   cp_mpi_max_double(&epsilon, 1, matrix_a->dist->comm);
     669            0 :   return epsilon;
     670              : }
     671              : 
     672              : /*******************************************************************************
     673              :  * \brief Returns the name of the matrix of the given matrix.
     674              :  * \author Ole Schuett
     675              :  ******************************************************************************/
     676      2899377 : const char *dbm_get_name(const dbm_matrix_t *matrix) { return matrix->name; }
     677              : 
     678              : /*******************************************************************************
     679              :  * \brief Returns the number of local Non-Zero Elements of the given matrix.
     680              :  * \author Ole Schuett
     681              :  ******************************************************************************/
     682      2794300 : int dbm_get_nze(const dbm_matrix_t *matrix) {
     683      2794300 :   int nze = 0;
     684      5588600 :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     685      2794300 :     nze += matrix->shards[ishard].data_size;
     686              :   }
     687      2794300 :   return nze;
     688              : }
     689              : 
     690              : /*******************************************************************************
     691              :  * \brief Returns the number of local blocks of the given matrix.
     692              :  * \author Ole Schuett
     693              :  ******************************************************************************/
     694      1083840 : int dbm_get_num_blocks(const dbm_matrix_t *matrix) {
     695      1083840 :   int nblocks = 0;
     696      2167680 :   for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
     697      1083840 :     nblocks += matrix->shards[ishard].nblocks;
     698              :   }
     699      1083840 :   return nblocks;
     700              : }
     701              : 
     702              : /*******************************************************************************
     703              :  * \brief Returns the row block sizes of the given matrix.
     704              :  * \author Ole Schuett
     705              :  ******************************************************************************/
     706      5830177 : void dbm_get_row_sizes(const dbm_matrix_t *matrix, int *nrows,
     707              :                        const int **row_sizes) {
     708      5830177 :   *nrows = matrix->nrows;
     709      5830177 :   *row_sizes = matrix->row_sizes;
     710      5830177 : }
     711              : 
     712              : /*******************************************************************************
     713              :  * \brief Returns the column block sizes of the given matrix.
     714              :  * \author Ole Schuett
     715              :  ******************************************************************************/
     716      3130537 : void dbm_get_col_sizes(const dbm_matrix_t *matrix, int *ncols,
     717              :                        const int **col_sizes) {
     718      3130537 :   *ncols = matrix->ncols;
     719      3130537 :   *col_sizes = matrix->col_sizes;
     720      3130537 : }
     721              : 
     722              : /*******************************************************************************
     723              :  * \brief Returns the local row block sizes of the given matrix.
     724              :  * \author Ole Schuett
     725              :  ******************************************************************************/
     726       468392 : void dbm_get_local_rows(const dbm_matrix_t *matrix, int *nlocal_rows,
     727              :                         const int **local_rows) {
     728       468392 :   *nlocal_rows = matrix->dist->rows.nlocals;
     729       468392 :   *local_rows = matrix->dist->rows.local_indicies;
     730       468392 : }
     731              : 
     732              : /*******************************************************************************
     733              :  * \brief Returns the local column block sizes of the given matrix.
     734              :  * \author Ole Schuett
     735              :  ******************************************************************************/
     736       169160 : void dbm_get_local_cols(const dbm_matrix_t *matrix, int *nlocal_cols,
     737              :                         const int **local_cols) {
     738       169160 :   *nlocal_cols = matrix->dist->cols.nlocals;
     739       169160 :   *local_cols = matrix->dist->cols.local_indicies;
     740       169160 : }
     741              : 
     742              : /*******************************************************************************
     743              :  * \brief Returns the MPI rank on which the given block should be stored.
     744              :  * \author Ole Schuett
     745              :  ******************************************************************************/
     746     96061266 : int dbm_get_stored_coordinates(const dbm_matrix_t *matrix, const int row,
     747              :                                const int col) {
     748     96061266 :   return dbm_distribution_stored_coords(matrix->dist, row, col);
     749              : }
     750              : 
     751              : /*******************************************************************************
     752              :  * \brief Returns the distribution of the given matrix.
     753              :  * \author Ole Schuett
     754              :  ******************************************************************************/
     755      1839883 : const dbm_distribution_t *dbm_get_distribution(const dbm_matrix_t *matrix) {
     756      1839883 :   return matrix->dist;
     757              : }
     758              : 
     759              : // EOF
        

Generated by: LCOV version 2.0-1