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

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LAGraph/experimental/test/test_CoarsenMatching
       3             : //------------------------------------------------------------------------------
       4             : 
       5             : // LAGraph, (c) 2019-2022 by The LAGraph Contributors, All Rights Reserved.
       6             : // SPDX-License-Identifier: BSD-2-Clause
       7             : //
       8             : // For additional details (including references to third party source code and
       9             : // other files) see the LICENSE file or contact permission@sei.cmu.edu. See
      10             : // Contributors.txt for a full list of contributors. Created, in part, with
      11             : // funding and support from the U.S. Government (see Acknowledgments.txt file).
      12             : // DM22-0790
      13             : 
      14             : // Contributed by Vidith Madhu, Texas A&M University
      15             : 
      16             : //------------------------------------------------------------------------------
      17             : 
      18             : /*
      19             : NOTE: Unlike the other tests, this does not use .mtx files, but rather generates the test
      20             : matrices using specified configurations and seeds with LAGraph_Random_Matrix
      21             : */
      22             : 
      23             : #include <stdio.h>
      24             : #include <acutest.h>
      25             : #include "LAGraphX.h"
      26             : #include "LAGraph_test.h"
      27             : #include "LG_Xtest.h"
      28             : #include "LG_internal.h"
      29             : 
      30             : char msg [LAGRAPH_MSG_LEN] ;
      31             : 
      32             : GrB_Matrix A = NULL, A_coarse_LAGraph = NULL, A_coarse_naive = NULL ;
      33             : GrB_Vector parent = NULL, newlabel = NULL, inv_newlabel = NULL ;    // outputs from the Coarsen_Matching function
      34             : LAGraph_Graph G = NULL ;
      35             : 
      36             : typedef struct
      37             : {
      38             :     // options for building graph:
      39             :     GrB_Index n ;         // number of nodes in the graph
      40             :     double density ;      // density of the matrix
      41             :     uint64_t seed ;       // seed used to generate the graph for this test
      42             :     
      43             :     // options for coarsening (see Coarsen_Matching function for details):
      44             :     LAGraph_Matching_kind matching_type ;
      45             :     int preserve_mapping ;
      46             :     int combine_weights ;
      47             : 
      48             :     const char *name ;
      49             : }
      50             : matrix_info ;
      51             : 
      52             : const matrix_info tests [ ] = {
      53             :     // random, preserve, combine
      54             :     {10, 0.3, 55, LAGraph_Matching_unweighted, 1, 1, "small-random-preserve-combine"},
      55             :     {500, 0.4, 16, LAGraph_Matching_unweighted, 1, 1, "large-random-preserve-combine"},
      56             :     // random, preserve, nocombine
      57             :     {10, 0.3, 62, LAGraph_Matching_unweighted, 1, 0, "small-random-preserve-nocombine"},
      58             :     {500, 0.4, 21, LAGraph_Matching_unweighted, 1, 0, "large-random-preserve-nocombine"},
      59             :     // random, nopreserve, combine
      60             :     {10, 0.3, 23, LAGraph_Matching_unweighted, 0, 1, "small-random-nopreserve-combine"},
      61             :     {500, 0.4, 31, LAGraph_Matching_unweighted, 0, 1, "large-random-nopreserve-combine"},
      62             :     // random, nopreserve, nocombine
      63             :     {10, 0.3, 92, LAGraph_Matching_unweighted, 0, 0, "small-random-nopreserve-nocombine"},
      64             :     {500, 0.4, 44, LAGraph_Matching_unweighted, 0, 0, "large-random-nopreserve-nocombine"},
      65             : 
      66             :     // same as above except weighted matching (mix of light and heavy)
      67             :     // random, preserve, combine
      68             :     {10, 0.3, 55, LAGraph_Matching_heavy, 1, 1, "small-random-preserve-combine"},
      69             :     {500, 0.4, 16, LAGraph_Matching_light, 1, 1, "large-random-preserve-combine"},
      70             :     // random, preserve, nocombine
      71             :     {10, 0.3, 62, LAGraph_Matching_light, 1, 0, "small-random-preserve-nocombine"},
      72             :     {500, 0.4, 21, LAGraph_Matching_heavy, 1, 0, "large-random-preserve-nocombine"},
      73             :     // random, nopreserve, combine
      74             :     {10, 0.3, 23, LAGraph_Matching_light, 0, 1, "small-random-nopreserve-combine"},
      75             :     {500, 0.4, 31, LAGraph_Matching_heavy, 0, 1, "large-random-nopreserve-combine"},
      76             :     // random, nopreserve, nocombine
      77             :     {10, 0.3, 92, LAGraph_Matching_heavy, 0, 0, "small-random-nopreserve-nocombine"},
      78             :     {500, 0.4, 44, LAGraph_Matching_light, 0, 0, "large-random-nopreserve-nocombine"},
      79             :     {0, 0, 0, 0, 0, 0, ""}
      80             : } ;
      81             : 
      82             : #define LEN 512
      83             : #define SEEDS_PER_TEST 3
      84             : 
      85             : char filename [LEN+1] ;
      86             : char msg [LAGRAPH_MSG_LEN] ;
      87             : 
      88           1 : void test_Coarsen_Matching (void) {
      89             : #if LAGRAPH_SUITESPARSE
      90             : 
      91           1 :     OK (LAGraph_Init (msg)) ;
      92             : //  OK (LG_SET_BURBLE (true)) ;
      93             : 
      94           1 :     for (int k = 0 ; ; k++)
      95          16 :     {
      96          17 :         const char *aname = tests [k].name ;
      97          17 :         if (strlen (aname) == 0) { break ; }
      98          16 :         TEST_CASE (aname) ;
      99             : 
     100             :         // ================= first generate graph (code from test_MaximalMatching) =================
     101          16 :         GrB_Index n = tests [k].n ;
     102             : 
     103          16 :         GrB_Matrix A_dup = NULL ;
     104          16 :         A = NULL ;
     105             :         GrB_Index nvals ;
     106             :         GrB_Index *rows, *cols ;
     107             :         double *vals ;
     108             : 
     109          16 :         OK (LAGraph_Random_Matrix (&A_dup, GrB_FP64, n, n, tests [k].density, tests [k].seed, msg)) ;
     110          16 :         OK (GrB_Matrix_new (&A, GrB_FP64, n, n)) ;
     111             : 
     112          16 :         OK (GrB_Matrix_nvals (&nvals, A_dup)) ;
     113             : 
     114          16 :         OK (LAGraph_Malloc ((void**)(&rows), nvals, sizeof(GrB_Index), msg)) ;
     115          16 :         OK (LAGraph_Malloc ((void**)(&cols), nvals, sizeof(GrB_Index), msg)) ;
     116          16 :         OK (LAGraph_Malloc ((void**)(&vals), nvals, sizeof(double), msg)) ;
     117             : 
     118          16 :         OK (GrB_Matrix_extractTuples (rows, cols, vals, &nvals, A_dup)) ;
     119             : 
     120      659640 :         for (GrB_Index i = 0; i < nvals; i++) {
     121      659624 :             GrB_Index row = rows [i];
     122      659624 :             GrB_Index col = cols [i];
     123      659624 :             double val = vals [i];
     124      659624 :             if (col < row){
     125             :                 // use lower triangular entries for the entire matrix
     126      330304 :                 OK (GrB_Matrix_setElement (A, val, col, row)) ;
     127      330304 :                 OK (GrB_Matrix_setElement (A, val, row, col)) ;
     128             :             }
     129             :         }
     130             : 
     131          16 :         OK (GrB_free (&A_dup)) ;
     132          16 :         OK (LAGraph_Free ((void**)(&rows), msg)) ;
     133          16 :         OK (LAGraph_Free ((void**)(&cols), msg)) ;
     134          16 :         OK (LAGraph_Free ((void**)(&vals), msg)) ;
     135          16 :         OK (GrB_wait (A, GrB_MATERIALIZE)) ;
     136             : 
     137             :         // ================== graph generation done ======================================
     138             : 
     139          16 :         TEST_CHECK (A != NULL) ;
     140          16 :         TEST_MSG ("Building of adjacency matrix failed") ;
     141             : 
     142          16 :         OK (LAGraph_New (&G, &A, LAGraph_ADJACENCY_DIRECTED, msg)) ;
     143          16 :         TEST_CHECK (A == NULL) ;
     144          16 :         TEST_CHECK (G->A != NULL) ;
     145          16 :         OK (LAGraph_Cached_NSelfEdges (G, msg)) ;
     146          16 :         OK (LAGraph_Cached_AT (G, msg)) ;
     147             : 
     148             :         // remove self-edges
     149          16 :         OK (LAGraph_DeleteSelfEdges (G, msg)) ;
     150          16 :         TEST_CHECK (G->nself_edges == 0) ;
     151             : 
     152          16 :         bool ok = 0;
     153          16 :         OK (LAGraph_Matrix_IsEqual (&ok, G->A, G->AT, msg)) ;
     154          16 :         TEST_CHECK (ok) ;
     155          16 :         TEST_MSG ("Input graph is not undirected") ;
     156          16 :         G->kind = LAGraph_ADJACENCY_UNDIRECTED ;
     157             : 
     158          16 :         uint64_t matching_seed = 0 ;
     159          64 :         for (int i = 0; i < SEEDS_PER_TEST ; i++) {
     160             : 
     161          48 :             if (i == SEEDS_PER_TEST-1)
     162             :             {
     163             :                 // convert graph to FP32 for the last test
     164          16 :                 GrB_Matrix A_fp32 = NULL ;
     165          16 :                 OK (GrB_Matrix_new (&A_fp32, GrB_FP32, n, n)) ;
     166          16 :                 OK (GrB_assign (A_fp32, NULL, NULL, G->A, GrB_ALL, n, GrB_ALL, n, NULL)) ;
     167          16 :                 OK (GrB_free (&(G->A))) ;
     168          16 :                 OK (GrB_free (&(G->AT))) ;
     169          16 :                 G->A = A_fp32 ;
     170          16 :                 A_fp32 = NULL ;
     171             :             }
     172             : 
     173          48 :             OK (LAGraph_Coarsen_Matching (
     174             :                 &A_coarse_LAGraph,
     175             :                 &parent,
     176             :                 &newlabel,
     177             :                 &inv_newlabel,
     178             :                 G,
     179             :                 tests [k].matching_type, 
     180             :                 tests [k].preserve_mapping,
     181             :                 tests [k].combine_weights,
     182             :                 matching_seed,
     183             :                 msg
     184             :             )) ;
     185          48 :             OK (LG_check_coarsen (
     186             :                 &A_coarse_naive,
     187             :                 G->A,
     188             :                 parent,
     189             :                 (newlabel == NULL ? NULL : newlabel),
     190             :                 (inv_newlabel == NULL ? NULL : inv_newlabel),
     191             :                 tests [k].preserve_mapping,
     192             :                 tests [k].combine_weights,
     193             :                 msg
     194             :             )) ;
     195             : 
     196          48 :             if (newlabel != NULL) {
     197          24 :                 GrB_free (&newlabel) ;
     198          24 :                 GrB_free (&inv_newlabel) ;
     199             :             }
     200             :             // Check parent vector for matching-specific correctness (must be derived from a valid matching)
     201             :             // requirements: no node is the parent of more than 2 nodes, and if p[i] != i, then A[i][p[i]] exists
     202             :             int8_t *freq ;
     203          48 :             OK (LAGraph_Malloc ((void**)(&freq), n, sizeof(int8_t), msg)) ;
     204          48 :             memset(freq, 0, n * sizeof(int8_t)) ;
     205             : 
     206       12288 :             for (GrB_Index i = 0 ; i < n ; i++) {
     207             :                 uint64_t par ;
     208       12240 :                 OK (GrB_Vector_extractElement (&par, parent, i)) ;
     209       12240 :                 freq [par]++ ;
     210       12240 :                 TEST_CHECK (freq [par] <= 2) ;
     211       12240 :                 TEST_MSG ("Parent vector not from a valid matching for test: %s\n", tests [k].name) ;
     212             : 
     213       12240 :                 if (par != i) {
     214             :                     // make sure that (i, par) is a edge in the graph
     215        6067 :                     TEST_CHECK (GxB_Matrix_isStoredElement (G->A, i, par) == GrB_SUCCESS) ;
     216        6067 :                     TEST_MSG ("Parent vector not from a valid matching for test: %s\n", tests [k].name) ;
     217             :                 }
     218             :             }
     219          48 :             GrB_free (&parent) ;
     220             :             
     221          48 :             OK (LAGraph_Free ((void**)(&freq), msg)) ;
     222             : 
     223             : #if 0
     224             : //          OK (LAGraph_Matrix_Print (G->A, LAGraph_COMPLETE, stdout, msg)) ;
     225             :             OK (LAGraph_Matrix_Print (A_coarse_LAGraph, LAGraph_COMPLETE, stdout, msg)) ;
     226             :             OK (LAGraph_Matrix_Print (A_coarse_naive, LAGraph_COMPLETE, stdout, msg)) ;
     227             : //          OK (LAGraph_Vector_Print (parent[0], LAGraph_COMPLETE, stdout, msg)) ;
     228             : //          OK (LAGraph_Vector_Print (newlabels[0], LAGraph_COMPLETE, stdout, msg)) ;
     229             : #endif
     230             : 
     231             :             GrB_Matrix Delta ;
     232             :             GrB_Index ncoarse ;
     233          48 :             GrB_Matrix_nrows (&ncoarse, A_coarse_LAGraph) ;
     234          48 :             GrB_Matrix_new (&Delta, GrB_FP64, ncoarse, ncoarse) ;
     235          48 :             GrB_eWiseAdd (Delta, NULL, NULL, GrB_MINUS_FP64, A_coarse_LAGraph, A_coarse_naive,
     236             :                 NULL) ;
     237          48 :             GrB_apply (Delta, NULL, NULL, GrB_ABS_FP64, Delta, NULL) ;
     238             : //          OK (LAGraph_Matrix_Print (Delta, LAGraph_COMPLETE, stdout, msg)) ;
     239          48 :             double error = 0 ;
     240          48 :             GrB_reduce (&error, NULL, GrB_MAX_MONOID_FP64, Delta, NULL) ;
     241             : 
     242             : //          this test is wrong, it does not allow for floating point roundoff
     243             : //          OK (LAGraph_Matrix_IsEqual (&ok, A_coarse_LAGraph, A_coarse_naive, msg)) ;
     244             : //          TEST_CHECK (ok) ;
     245             : 
     246             : //          printf ("error: %g\n", error) ;
     247          48 :             TEST_CHECK (error < 1e-12) ;
     248          48 :             TEST_MSG ("Coarsened matrices do not match for test: %s", tests [k].name) ;
     249             : 
     250          48 :             OK (GrB_free (&Delta)) ;
     251          48 :             OK (GrB_free (&A_coarse_LAGraph)) ;
     252          48 :             OK (GrB_free (&A_coarse_naive)) ;
     253             : 
     254          48 :             matching_seed += tests [k].n ;
     255             :         }
     256          16 :         OK (LAGraph_Delete (&G, msg)) ;
     257             :     }
     258             : 
     259           1 :     OK (LAGraph_Finalize (msg)) ;
     260             : #endif
     261           1 : }
     262             : 
     263           1 : void test_Coarsen_Matching_Errors(void) {
     264             : #if LAGRAPH_SUITESPARSE
     265             : 
     266           1 :     OK (LAGraph_Init (msg)) ;
     267             : 
     268           1 :     GrB_Matrix C = NULL ;
     269           1 :     OK (GrB_Matrix_new (&A, GrB_FP64, 5, 5)) ;
     270           1 :     OK (LAGraph_New (&G, &A, LAGraph_ADJACENCY_UNDIRECTED, msg)) ;
     271             : 
     272           1 :     G->kind = LAGraph_ADJACENCY_DIRECTED ;
     273             : 
     274           1 :     OK (LAGraph_DeleteSelfEdges (G, msg)) ;
     275           1 :     TEST_CHECK (G->nself_edges == 0) ;
     276             : 
     277             :     // directed graph
     278           1 :     GrB_Info result = LAGraph_Coarsen_Matching (&C, NULL, NULL, NULL, G, 0, 0, 0, 0, msg) ;
     279           1 :     printf ("\nresult: %d %s\n", result, msg) ;
     280           1 :     TEST_CHECK (result == LAGRAPH_INVALID_GRAPH) ;
     281           1 :     TEST_CHECK (C == NULL) ;
     282             : 
     283           1 :     G->kind = LAGraph_ADJACENCY_UNDIRECTED ;
     284           1 :     G->nself_edges = 1 ;
     285             : 
     286             :     // non-zero self-loops
     287           1 :     result = LAGraph_Coarsen_Matching (&C, NULL, NULL, NULL, G, 0, 0, 0, 0, msg) ;
     288           1 :     printf ("\nresult: %d %s\n", result, msg) ;
     289           1 :     TEST_CHECK (result == LAGRAPH_NO_SELF_EDGES_ALLOWED) ;
     290           1 :     TEST_CHECK (C == NULL) ;
     291             : 
     292           1 :     G->nself_edges = 0;
     293             : 
     294             :     // output coarsened matrix pointer is NULL
     295           1 :     result = LAGraph_Coarsen_Matching (NULL, NULL, NULL, NULL, G, 0, 0, 0, 0, msg) ;
     296           1 :     printf ("\nresult: %d %s\n", result, msg) ;
     297           1 :     TEST_CHECK (result == GrB_NULL_POINTER) ;
     298             : 
     299           1 :     OK (LAGraph_Delete (&G, msg)) ;
     300           1 :     OK (LAGraph_Finalize (msg)) ;
     301             : #endif
     302           1 : }
     303             : 
     304             : 
     305           1 : void test_Coarsen_Matching_NullInputs(void) {
     306             : #if LAGRAPH_SUITESPARSE
     307             : 
     308           1 :     OK (LAGraph_Init (msg)) ;
     309             : 
     310             : 
     311           1 :     GrB_Matrix C = NULL ;
     312           1 :     OK (GrB_Matrix_new (&A, GrB_FP64, 5, 5)) ;
     313             : 
     314           1 :     OK (LAGraph_New (&G, &A, LAGraph_ADJACENCY_UNDIRECTED, msg)) ;
     315           1 :     OK (LAGraph_Cached_NSelfEdges (G, msg)) ;
     316           1 :     OK (LAGraph_Cached_AT (G, msg) < 0) ; // warning is expected; check for error
     317             : 
     318             :     // do this to get full code coverage and catch any unexpected behavior
     319           1 :     OK (LAGraph_Coarsen_Matching (&C, NULL, NULL, NULL, G, 0, 0, 1, 42, msg)) ;
     320             : 
     321           1 :     OK (GrB_free (&C)) ;
     322             :     // do it with parent, inv_newlabels NULL but newlabels not NULL
     323           1 :     OK (LAGraph_Coarsen_Matching (&C, NULL, &newlabel, NULL, G, 0, 0, 1, 42, msg)) ;
     324             : 
     325           1 :     OK (GrB_free (&C)) ;
     326           1 :     OK (LAGraph_Delete (&G, msg)) ;
     327             : 
     328           1 :     TEST_CHECK (newlabel != NULL) ;
     329           1 :     TEST_MSG ("Null input check failed!\n") ;
     330             : 
     331           1 :     OK (GrB_free (&newlabel)) ;
     332           1 :     OK (LAGraph_Finalize (msg)) ;
     333             : #endif
     334           1 : }
     335             : 
     336             : TEST_LIST = {
     337             :     {"Coarsen_Matching", test_Coarsen_Matching},
     338             :     {"Coarsen_Matching_Errors", test_Coarsen_Matching_Errors},
     339             :     {"Coarsen_Matching_NullInputs", test_Coarsen_Matching_NullInputs},
     340             :     {NULL, NULL}
     341             : } ;

Generated by: LCOV version 1.14