LCOV - code coverage report
Current view: top level - experimental/test - LG_check_coarsen.c (source / functions) Hit Total Coverage
Test: LAGraph code coverage report. Commit id: 7b9d2ee. Current time (UTC): 2025-06-03T21:57:17Z Lines: 73 73 100.0 %
Date: 2025-06-03 22:02:40 Functions: 1 1 100.0 %

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LG_check_coarsen: naive implementation of coarsening (edge by edge traversal), also checks
       3             : // parent and mapping vectors for correctness
       4             : //------------------------------------------------------------------------------
       5             : 
       6             : // LAGraph, (c) 2019-2022 by The LAGraph Contributors, All Rights Reserved.
       7             : // SPDX-License-Identifier: BSD-2-Clause
       8             : //
       9             : // For additional details (including references to third party source code and
      10             : // other files) see the LICENSE file or contact permission@sei.cmu.edu. See
      11             : // Contributors.txt for a full list of contributors. Created, in part, with
      12             : // funding and support from the U.S. Government (see Acknowledgments.txt file).
      13             : // DM22-0790
      14             : 
      15             : // Contributed by Vidith Madhu, Texas A&M University
      16             : 
      17             : //------------------------------------------------------------------------------
      18             : 
      19             : // A naive coarsening implementation that individually traverses edges in the original graph 
      20             : // and updates the corresponding edge in the coarsened graph
      21             : // In addition, verifies that the given parent and mapping vectors are correct for the general coarsening case.
      22             : // there may be additional constraints that must be observed for specific types of coarsenings; for example,
      23             : // the parent vector for a matching-based coarsening must come from a valid matching. Such properties should be checked
      24             : // in the respective coarsening tests.
      25             : 
      26             : // NOTE: The input adjacency matrix A must be from an undirected graph
      27             : 
      28             : #include "LG_internal.h"
      29             : #include "LG_test.h"
      30             : #include "LG_test.h"
      31             : #include "LG_Xtest.h"
      32             : 
      33             : // #undef LG_FREE_ALL
      34             : // #undef LG_FREE_WORK
      35             : 
      36          48 : int LG_check_coarsen
      37             : (
      38             :     // outputs:
      39             :     GrB_Matrix *coarsened,    // coarsened adjacency
      40             :     // inputs:
      41             :     GrB_Matrix A,               // input adjacency (for the purposes of testing, is FP64)
      42             :     GrB_Vector parent,          // parent mapping. Must not be NULL.
      43             :     GrB_Vector newlabel,       // new labels of nodes, used to populate resulting adjacency matrix, can be NULL if preserve_mapping = 1, else must be a valid result
      44             :     GrB_Vector inv_newlabel,   // inverse of newlabel, can be NULL if preserve_mapping = 1, else must be a valid result
      45             :     int preserve_mapping,       // whether to preserve the original namespace of nodes
      46             :     int combine_weights,        // whether to combine the weights of edges that collapse together
      47             :     char *msg
      48             : )
      49             : {
      50          48 :     GrB_Matrix result = NULL ;
      51             : 
      52             :     GrB_Index n ;
      53             :     GrB_Index nvals ;
      54             : 
      55          48 :     GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
      56          48 :     GRB_TRY (GrB_Matrix_nvals (&nvals, A)) ;
      57             : 
      58          48 :     GrB_Index n_new = n ;
      59             :     
      60             :     // check that parent mapping is well-formed and is compressed (i.e. p[p[i]] = p[i] for all i)
      61             :     // also calculate n_new
      62       12288 :     for (GrB_Index i = 0 ; i < n ; i++) {
      63             :         uint64_t par ;
      64       12240 :         GrB_Info status = GrB_Vector_extractElement (&par, parent, i) ;
      65       12240 :         LG_ASSERT (status == GrB_SUCCESS, GrB_INVALID_VALUE) ;
      66       12240 :         LG_ASSERT (par >= 0 && par < n, GrB_INVALID_INDEX) ;
      67             : 
      68             :         uint64_t grandpa ;
      69       12240 :         GRB_TRY (GrB_Vector_extractElement (&grandpa, parent, par)) ;
      70       12240 :         LG_ASSERT (grandpa == par, GrB_INVALID_VALUE) ;
      71             : 
      72       12240 :         if (par != i && (!preserve_mapping)) {
      73             :             // make sure that there is no new label for nodes that get discarded
      74             :             uint64_t dummy ;
      75        3031 :             status = GrB_Vector_extractElement (&dummy, newlabel, i) ;
      76        3031 :             LG_ASSERT (status == GrB_NO_VALUE, GrB_INVALID_VALUE) ;
      77             :             // also update new number of nodes
      78        3031 :             n_new-- ;
      79             :         }
      80             :     }
      81             : 
      82          48 :     if (!preserve_mapping) {
      83             :         // if preserve_mapping = false, newlabel is not the identity and must be checked
      84             :         bool *occ ;
      85          24 :         GrB_Index num_entries = 0 ;
      86          24 :         LG_TRY (LAGraph_Malloc ((void**)(&occ), n, sizeof(bool), msg)) ;
      87          24 :         memset (occ, 0, n * sizeof(bool)) ;
      88             : 
      89             :         // check that newlabel vector is well-formed (entries must form a permutation of [0...(n_new - 1)])
      90        6144 :         for (GrB_Index i = 0 ; i < n ; i++) {
      91             :             uint64_t new_label ;
      92        6120 :             GrB_Info status = GrB_Vector_extractElement (&new_label, newlabel, i) ;
      93        6120 :             GRB_TRY (status) ; // check for errors
      94        6120 :             if (status == GrB_NO_VALUE) {
      95        3031 :                 continue ;
      96             :             }
      97        3089 :             num_entries++ ;
      98        3089 :             GRB_TRY (GrB_Vector_extractElement (&new_label, newlabel, i)) ;
      99             : 
     100        3089 :             LG_ASSERT (new_label >= 0 && new_label < n_new, GrB_INVALID_INDEX) ;
     101        3089 :             LG_ASSERT (!occ[new_label], GrB_INVALID_VALUE) ;
     102        3089 :             occ[new_label] = 1 ;
     103             :         }
     104          24 :         LG_ASSERT (num_entries == n_new, GrB_INVALID_VALUE) ;
     105          24 :         LG_TRY (LAGraph_Free ((void**)(&occ), msg)) ;
     106             : 
     107             :         // check inv_newlabel
     108        3113 :         for (GrB_Index i = 0 ; i < n_new ; i++) {
     109             :             uint64_t old_label ;
     110        3089 :             GrB_Info status=  GrB_Vector_extractElement (&old_label, inv_newlabel, i) ;
     111        3089 :             LG_ASSERT (status == GrB_SUCCESS, GrB_INVALID_VALUE) ;
     112             : 
     113             :             uint64_t new_label ; // entry in newlabel, check that it matches i
     114        3089 :             status = GrB_Vector_extractElement (&new_label, newlabel, old_label) ;
     115        3089 :             LG_ASSERT (status == GrB_SUCCESS, GrB_INVALID_VALUE) ;
     116             : 
     117        3089 :             LG_ASSERT (new_label == i, GrB_INVALID_VALUE) ;
     118             :         }
     119             :     }
     120             :     
     121          48 :     GRB_TRY (GrB_Matrix_new (&result, GrB_FP64, n_new, n_new)) ;
     122             : 
     123          48 :     GrB_Index *rows = NULL, *cols = NULL ;
     124          48 :     double *vals = NULL ;
     125             : 
     126          48 :     LG_TRY (LAGraph_Malloc ((void**)(&rows), nvals, sizeof(GrB_Index), msg)) ;
     127          48 :     LG_TRY (LAGraph_Malloc ((void**)(&cols), nvals, sizeof(GrB_Index), msg)) ;
     128          48 :     LG_TRY (LAGraph_Malloc ((void**)(&vals), nvals, sizeof(double), msg)) ;
     129             : 
     130          48 :     GRB_TRY (GrB_Matrix_extractTuples (rows, cols, vals, &nvals, A)) ;
     131             : 
     132     1981872 :     for (GrB_Index i = 0 ; i < nvals ; i++) {
     133     1981824 :         GrB_Index u = rows [i] ;
     134     1981824 :         GrB_Index v = cols [i] ;
     135             : 
     136     1981824 :         if (u > v) {
     137             :             // only consider upper-triangular entries (don't duplicate edges)
     138      996979 :             continue ;
     139             :         }
     140             : 
     141             :         uint64_t u_par, v_par ;
     142             : 
     143      990912 :         GRB_TRY (GrB_Vector_extractElement (&u_par, parent, u)) ;
     144      990912 :         GRB_TRY (GrB_Vector_extractElement (&v_par, parent, v)) ;
     145             : 
     146      990912 :         if (u_par == v_par) {
     147             :             // both nodes part of same coarsened component
     148        6067 :             continue ;
     149             :         }
     150             :         uint64_t u_par_newlabel, v_par_newlabel ;
     151      984845 :         if (preserve_mapping) {
     152             :             // new labels are the same as old labels
     153      492408 :             u_par_newlabel = u_par ;
     154      492408 :             v_par_newlabel = v_par ;
     155             :         } else {
     156             :             // find new labels
     157      492437 :             GRB_TRY (GrB_Vector_extractElement (&u_par_newlabel, newlabel, u_par)) ;
     158      492437 :             GRB_TRY (GrB_Vector_extractElement (&v_par_newlabel, newlabel, v_par)) ;
     159             :         }
     160      984845 :         double res_weight = 1 ;
     161             : 
     162      984845 :         if (combine_weights) {
     163             :             // current weight of edge between u_par and v_par
     164             :             double curr_weight ;
     165             : 
     166      492401 :             GrB_Info status = GrB_Matrix_extractElement (&curr_weight, result, u_par_newlabel, v_par_newlabel) ;
     167      492401 :             GRB_TRY (status) ; // check for errors
     168             : 
     169      492401 :             if (status == GrB_NO_VALUE) {
     170      299296 :                 curr_weight = 0 ;
     171             :             }
     172             : 
     173             :             // weight of the current edge being added
     174      492401 :             double my_weight = vals [i] ;
     175      492401 :             res_weight = curr_weight + my_weight ;
     176             :         }
     177             : 
     178      984845 :         GRB_TRY (GrB_Matrix_setElement (result, res_weight, u_par_newlabel, v_par_newlabel)) ;
     179      984845 :         GRB_TRY (GrB_Matrix_setElement (result, res_weight, v_par_newlabel, u_par_newlabel)) ;
     180             :     }
     181          48 :     (*coarsened) = result ;
     182             : 
     183          48 :     LG_TRY (LAGraph_Free ((void**)(&rows), msg)) ;
     184          48 :     LG_TRY (LAGraph_Free ((void**)(&cols), msg)) ;
     185          48 :     LG_TRY (LAGraph_Free ((void**)(&vals), msg)) ;
     186             : 
     187          48 :     return (GrB_SUCCESS) ;
     188             : }

Generated by: LCOV version 1.14