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

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LAGr_MaximumMatching: maximum matching between nodes of disjoint sets
       3             : //                          in bipartite graphs
       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 Christina Koutsou, Aristotle University of Thessaloniki
      16             : 
      17             : //------------------------------------------------------------------------------
      18             : 
      19             : // This implmentation is based on the algorithm described in the following
      20             : // paper: "Distributed-Memory Algorithms for Maximum Cardinality Matching in
      21             : // Bipartite Graphs" by A. Azad and A. Buluç.
      22             : //
      23             : // In brief, this algorithm explores alternate paths by reversing an already
      24             : // traversed path to see if one of the already matched columns encountered in
      25             : // the path has at least one free children to be matched with instead. If so, it
      26             : // concedes its previous match to another previously matched parent or to the
      27             : // unmatched root of the path.
      28             : 
      29             : // More detailed explanation of the algorithm and its implementation can be
      30             : // found in the LAGraph "papers" folder, in file
      31             : // "MaximumMatching_Report.pdf".
      32             : 
      33             : // This is an Advanced method, G->A is required as input instead of G due to
      34             : // lack of a Bipartite graph kind in GraphBLAS.
      35             : 
      36             : //------------------------------------------------------------------------------
      37             : 
      38             : #include "LAGraphX.h"
      39             : #include "LG_internal.h"
      40             : 
      41             : //------------------------------------------------------------------------------
      42             : // the Vertex tuple: (parentC, rootC)
      43             : //------------------------------------------------------------------------------
      44             : 
      45             : typedef struct
      46             : {
      47             :     uint64_t parentC;
      48             :     uint64_t rootC;
      49             : } vertex;
      50             : 
      51             : // repeat the typedef as a string, to give to GraphBLAS
      52             : #define VERTEX_DEFN         \
      53             : "typedef struct         \n" \
      54             : "{                      \n" \
      55             : "    uint64_t parentC;  \n" \
      56             : "    uint64_t rootC;    \n" \
      57             : "} vertex;"
      58             : 
      59       73362 : void initFrontier(vertex *z, const bool *x, uint64_t i, uint64_t j, const bool *y)
      60             : {
      61       73362 :     z->parentC = i;
      62       73362 :     z->rootC = i;
      63       73362 : }
      64             : 
      65             : #define INIT_FRONTIER_DEFN  \
      66             : "void initFrontier(vertex *z, const bool *x, uint64_t i, uint64_t j, const bool *y) \n" \
      67             : "{                      \n" \
      68             : "    z->parentC = i;    \n" \
      69             : "    z->rootC = i;      \n" \
      70             : "}"
      71             : 
      72       32261 : void minparent(vertex *z, const vertex *x, const vertex *y)
      73             : {
      74       32261 :     *z = x->parentC < y->parentC ? *x : *y;
      75       32261 : }
      76             : 
      77             : #define MIN_PARENT_DEFN                                             \
      78             : "void minparent(vertex *z, const vertex *x, const vertex *y)    \n" \
      79             : "{                                                              \n" \
      80             : "    *z = x->parentC < y->parentC ? *x : *y;                    \n" \
      81             : "}"
      82             : 
      83             : // TODO: revise GraphBLAS so we can tell it that the select2nd operator
      84             : // does not use the 'x' input.
      85       33420 : void select2nd(vertex *z, const bool *x, const vertex *y)
      86             : {
      87       33420 :     z->parentC = y->parentC;
      88       33420 :     z->rootC = y->rootC;
      89       33420 : }
      90             : 
      91             : #define SELECT_2ND_DEFN                                             \
      92             : "void select2nd(vertex *z, const bool *x, const vertex *y)      \n" \
      93             : "{                                                              \n" \
      94             : "    z->parentC = y->parentC;                                   \n" \
      95             : "    z->rootC = y->rootC;                                       \n" \
      96             : "}"
      97             : 
      98       23430 : void select1st(vertex *z, const vertex *x, const bool *y)
      99             : {
     100       23430 :     z->parentC = x->parentC;
     101       23430 :     z->rootC = x->rootC;
     102       23430 : }
     103             : 
     104             : #define SELECT_1ST_DEFN                                             \
     105             : "void select1st(vertex *z, const vertex *x, const bool *y)      \n" \
     106             : "{                                                              \n" \
     107             : "    z->parentC = x->parentC;                                   \n" \
     108             : "    z->rootC = x->rootC;                                       \n" \
     109             : "}"
     110             : 
     111       20280 : void keepParents(uint64_t *z, const vertex *x) { *z = x->parentC; }
     112             : 
     113             : #define KEEP_PARENTS_DEFN                                           \
     114             : "void keepParents(uint64_t *z, const vertex *x) { *z = x->parentC; }\n"
     115             : 
     116       14199 : void keepRoots(uint64_t *z, const vertex *x) { *z = x->rootC; }
     117             : 
     118             : #define KEEP_ROOTS_DEFN                                                        \
     119             : "void keepRoots(uint64_t *z, const vertex *x) { *z = x->rootC; }\n"
     120             : 
     121        2277 : void buildfCTuples(vertex *z, const uint64_t *x, uint64_t i, uint64_t j,
     122             :                    const bool *y)
     123             : {
     124        2277 :     z->parentC = i;
     125        2277 :     z->rootC = *x;
     126        2277 : }
     127             : 
     128             : #define BUILT_FC_TUPLES_DEFN                                        \
     129             : "void buildfCTuples(vertex *z, const uint64_t *x, uint64_t i, uint64_t j,   \n" \
     130             : "                   const bool *y)                              \n" \
     131             : "{                                                              \n" \
     132             : "    z->parentC = i;                                            \n" \
     133             : "    z->rootC = *x;                                             \n" \
     134             : "}"
     135             : 
     136        6081 : void vertexTypecast(vertex *z, const uint64_t *x)
     137             : {
     138        6081 :     z->parentC = *x;
     139        6081 :     z->rootC = *x;
     140        6081 : }
     141             : 
     142             : #define VERTEX_TYPECAST_DEFN                                        \
     143             : "void vertexTypecast(vertex *z, const uint64_t *x)              \n" \
     144             : "{                                                              \n" \
     145             : "    z->parentC = *x;                                           \n" \
     146             : "    z->rootC = *x;                                             \n" \
     147             : "}"
     148             : 
     149        6081 : void setParentsMates(vertex *z, const vertex *x, const vertex *y)
     150             : {
     151        6081 :     z->parentC = y->parentC;
     152        6081 :     z->rootC = x->rootC;
     153        6081 : }
     154             : 
     155             : #define SET_PARENTS_MATES_DEFN                                          \
     156             : "void setParentsMates(vertex *z, const vertex *x, const vertex *y)  \n" \
     157             : "{                                                                  \n" \
     158             : "    z->parentC = y->parentC;                                       \n" \
     159             : "    z->rootC = x->rootC;                                           \n" \
     160             : "}"
     161             : 
     162             : //------------------------------------------------------------------------------
     163             : // invert
     164             : //------------------------------------------------------------------------------
     165             : 
     166             : // This function "inverts" an input vector by swapping its row indices
     167             : // and its values, returning the result in an output vector.
     168             : //
     169             : // For example, for the indices/values of an input vector (in) with 5 entries
     170             : // and length 100:
     171             : //
     172             : //      indices: 0  3  5 42 99
     173             : //      values:  4 98  1  3 12
     174             : //
     175             : // on output, the out vector will contain:
     176             : //
     177             : //      indices: 4 98  1  3 12
     178             : //      values:  0  3  5 42 99
     179             : //
     180             : // The output vector will normally be jumbled since the values will not appear
     181             : // in any particular order.  The method assumes that the input values are in
     182             : // range 0 to n-1 where n = length(out). The values in the input vector
     183             : // may be duplicated and this argument of the function must be set accordingly.
     184             : // Both the in vector and out vector must have the same type (GrB_UINT64).  The
     185             : // lengths of the two vectors need not be the same, so long as the indices
     186             : // remain in range.  Results are undefined if these conditions do not hold.
     187             : //
     188             : // The in and out vectors may be aliased.  If not aliased, the input vector is
     189             : // cleared of all entries on output.  If in and out are aliased, then the
     190             : // inversion is performed in-place.
     191             : //
     192             : // In SuiteSparse:GraphBLAS, this method takes O(1) time if the in vector is in
     193             : // CSC (sparse, by column) format.  Otherwise it can take O(e) time if e =
     194             : // nvals(in), because the unpack below will convert the in vector to CSC and
     195             : // then unpack it.
     196             : 
     197             : #undef LG_FREE_ALL
     198             : #define LG_FREE_ALL                                                            \
     199             :     {                                                                          \
     200             :         LAGraph_Free((void *)&I, NULL);                                        \
     201             :         LAGraph_Free((void *)&X1, NULL);                                       \
     202             :         LAGraph_Free((void *)&X2, NULL);                                       \
     203             :     }
     204             : 
     205           6 : static inline GrB_Info invert_nondestructive(
     206             :     GrB_Vector out, // input/output.  On input, only the size and type are
     207             :                     // kept; any entries in the 'out' vector cleared.  It is
     208             :                     // then replaced with the inversion of the input vector.
     209             :     GrB_Vector in,  // input vector, not modified.  There must be no duplicate
     210             :                     // values in the input vector.
     211             :     char *msg)
     212             : {
     213           6 :     bool jumbled = 1;
     214           6 :     GrB_Index *I = NULL;
     215           6 :     GrB_Index *X1 = NULL;
     216           6 :     GrB_Index *X2 = NULL; // not used
     217           6 :     GrB_Index IBytes = 0, XBytes = 0;
     218           6 :     uint64_t nvals = 0;
     219             : 
     220             :     // the input and output vectors cannot be the same vector
     221             :     ASSERT(in != out);
     222             : 
     223             :     // All input/output vectors must be of type GrB_UINT64.
     224             : #if LAGRAPH_SUITESPARSE
     225           6 :     GRB_TRY(
     226             :         GxB_Vector_unpack_CSC(in, (GrB_Index **)&I, (void **)&X1, &IBytes,
     227             :                               &XBytes, NULL, &nvals, &jumbled,
     228             :                               NULL)); // the output and input should have no
     229             :                                       // duplicates, so the order doesn't matter
     230             : #else
     231             :     GRB_TRY(GrB_Vector_nvals(&nvals, in));
     232             :     LG_TRY(LAGraph_Malloc((void **)&I, nvals, sizeof(GrB_Index), msg));
     233             :     LG_TRY(LAGraph_Malloc((void **)&X1, nvals, sizeof(GrB_Index), msg));
     234             :     GRB_TRY(GrB_Vector_wait(in, GrB_MATERIALIZE));
     235             :     GRB_TRY(GrB_Vector_extractTuples_UINT64(
     236             :         I, X1, &nvals, in)); // the output and input should have no
     237             :                              // duplicates, so the order doesn't matter
     238             : #endif
     239             : 
     240           6 :     GRB_TRY(GrB_Vector_clear(out)); // clear the output first as a prerequisite
     241             :                                     // of the build method
     242           6 :     GRB_TRY(GrB_Vector_build_UINT64(
     243             :         out, X1, I, nvals,
     244             :         NULL)); // build does not take ownership of the lists I and X,
     245             :                 // but only copies them, these lists will be given
     246             :                 // again to the input
     247             :                 // the input should have no duplicates in the
     248             :                 // values list, so dups are not handled
     249             : #if LAGRAPH_SUITESPARSE
     250           6 :     GRB_TRY(GxB_Vector_pack_CSC(in, (GrB_Index **)&I, (void **)&X1, IBytes,
     251             :                                 XBytes, false, nvals, jumbled, NULL));
     252             : #endif
     253           6 :     return (GrB_SUCCESS);
     254             : }
     255             : 
     256             : static inline GrB_Info
     257        1650 : invert(GrB_Vector out,  // input/output.  Same as invert_nondescructive above.
     258             :        GrB_Vector in,   // input vector, empty on output (unless in == out)
     259             :        const bool dups, // flag that indicates if duplicates exist in the input
     260             :                         // vector's values
     261             :        char *msg)
     262             : {
     263             :     // The input and output vectors can be the same vector
     264             :     // that is, in == out is OK.
     265             :     // All input/output vectors must be of type GrB_UINT64.
     266             : 
     267             :     // the output vector will normally be returned in a jumbled state
     268        1650 :     bool jumbled = dups ? 0 : 1; // if there are duplicates, we want the indices
     269             :                                  // to be ordered so we can keep the min child
     270        1650 :     GrB_Index *I = NULL;         // unpack allocates space for these lists
     271        1650 :     GrB_Index *X1 = NULL;
     272        1650 :     GrB_Index *X2 = NULL; // not used
     273        1650 :     GrB_Index IBytes = 0, XBytes = 0;
     274        1650 :     uint64_t nvals = 0;
     275             : 
     276             : #if LAGRAPH_SUITESPARSE
     277        1650 :     GRB_TRY(GxB_Vector_unpack_CSC(in, (GrB_Index **)&I, (void **)&X1, &IBytes,
     278             :                                   &XBytes, NULL, &nvals, &jumbled, NULL));
     279             : #else
     280             :     // vanilla case using extractTuples and build:
     281             :     // allocate I and X for GrB_extractTuples
     282             :     GRB_TRY(GrB_Vector_nvals(&nvals, in));
     283             :     LG_TRY(LAGraph_Malloc((void **)&I, nvals, sizeof(GrB_Index), msg));
     284             :     LG_TRY(LAGraph_Malloc((void **)&X1, nvals, sizeof(GrB_Index), msg));
     285             :     GRB_TRY(GrB_Vector_wait(in, GrB_MATERIALIZE));
     286             :     GRB_TRY(GrB_Vector_extractTuples_UINT64(
     287             :         I, X1, &nvals, in)); // the output and input should have no
     288             :                              // duplicates, so the order doesn't matter
     289             :     GRB_TRY(GrB_Vector_clear(in));
     290             : #endif
     291        1650 :     if (!dups)
     292             :     {
     293             : #if LAGRAPH_SUITESPARSE
     294        1386 :         GRB_TRY(GxB_Vector_pack_CSC(out, (GrB_Index **)&X1, (void **)&I, XBytes,
     295             :                                     IBytes, false, nvals, true, NULL));
     296             : #else
     297             :         GRB_TRY(GrB_Vector_clear(out));
     298             :         // GrB_MIN_UINT64 is used instead of first because first is an extension
     299             :         GRB_TRY(GrB_Vector_build_UINT64(out, X1, I, nvals, GrB_MIN_UINT64));
     300             :         // build copies the lists so they need to be freed in LG_FREE_ALL
     301             :         LG_FREE_ALL;
     302             : #endif
     303             :     }
     304             :     else
     305             :     {
     306         264 :         GRB_TRY(GrB_Vector_clear(out));
     307             : #if LAGRAPH_SUITESPARSE
     308         264 :         GRB_TRY(GrB_Vector_build_UINT64(out, X1, I, nvals, GrB_FIRST_UINT64));
     309             : #else
     310             :         GRB_TRY(GrB_Vector_build_UINT64(out, X1, I, nvals, GrB_MIN_UINT64));
     311             : #endif
     312             :         // build copies the lists so they need to be freed in LG_FREE_ALL
     313         264 :         LG_FREE_ALL;
     314             :     }
     315        1650 :     return (GrB_SUCCESS);
     316             : }
     317             : 
     318             : static inline GrB_Info
     319         810 : invert_2(GrB_Vector out,  // input/output
     320             :          GrB_Vector in1,  // input vector, empty on output (unless in1 == out)
     321             :          GrB_Vector in2,  // input vector, empty on output (unless in2 == out)
     322             :          const bool dups, // flag that indicates if duplicates exist in the
     323             :                           // input vector's values
     324             :          const bool udt,  // type of the first input, in1, and out vectors
     325             :          char *msg)
     326             : {
     327             :     // The input vectors cannot be aliased.  However in1==out or in2==out is
     328             :     // OK.  The two input vectors must have the same # of entries.
     329             :     // All input/output vectors must be of type GrB_UINT64.
     330             :     ASSERT(in1 != in2);
     331             : 
     332         810 :     GrB_Index *I = NULL;
     333         810 :     GrB_Index *X1 = NULL;
     334         810 :     GrB_Index *X2 = NULL;
     335         810 :     GrB_Index IBytes = 0, X1Bytes = 0, X2Bytes = 0;
     336         810 :     uint64_t nvals1 = 0, nvals2 = 0;
     337             : 
     338             : #if LAGRAPH_SUITESPARSE
     339         810 :     GRB_TRY(GxB_Vector_unpack_CSC(in1, (GrB_Index **)&I, (void **)&X1, &IBytes,
     340             :                                   &X1Bytes, NULL, &nvals1, NULL, NULL));
     341         810 :     LAGraph_Free((void *)&I, NULL);
     342         810 :     GRB_TRY(GxB_Vector_unpack_CSC(in2, (GrB_Index **)&I, (void **)&X2, &IBytes,
     343             :                                   &X2Bytes, NULL, &nvals2, NULL, NULL));
     344             :     ASSERT(nvals1 == nvals2);
     345         810 :     if (!dups)
     346             :     {
     347         576 :         LAGraph_Free((void *)&I, NULL);
     348         576 :         GRB_TRY(GxB_Vector_pack_CSC(out, (GrB_Index **)&X2, (void **)&X1,
     349             :                                     X2Bytes, X1Bytes, false, nvals2, true,
     350             :                                     NULL)); // the values are not ordered,
     351             :                                             // so the indices are jumbled
     352             :     }
     353             :     else
     354             :     {
     355         234 :         GRB_TRY(GrB_Vector_clear(out));
     356         234 :         GRB_TRY(GrB_Vector_build_UINT64(out, X2, X1, nvals2, GrB_FIRST_UINT64));
     357         234 :         LG_FREE_ALL;
     358             :     }
     359             : #else
     360             :     // vanilla case using extractTuples and build:
     361             :     if (dups)
     362             :     {
     363             :         // invert in2 to eliminate dups and get the final indices of out (we
     364             :         // cannot invert into out if out is of type udt, because we would have a
     365             :         // domain mismatch with in2)
     366             :         GrB_Vector in_rev = NULL;
     367             :         GrB_Index out_size = 0;
     368             :         GRB_TRY(GrB_Vector_size(&out_size, out));
     369             :         GRB_TRY(GrB_Vector_new(&in_rev, GrB_UINT64, out_size));
     370             :         LG_TRY(invert(in_rev, in2, true, msg));
     371             :         GRB_TRY(GrB_Vector_nvals(
     372             :             &nvals2,
     373             :             in_rev)); // out may have less entries than in2 and, thus, in1
     374             :         LG_TRY(LAGraph_Malloc((void **)&I, nvals2, sizeof(GrB_Index), msg));
     375             :         LG_TRY(LAGraph_Malloc((void **)&X2, nvals2, sizeof(GrB_Index), msg));
     376             :         // get indices of out
     377             :         GRB_TRY(GrB_Vector_extractTuples_UINT64(
     378             :             X2, I, &nvals2,
     379             :             in_rev)); // X2 are the final values of in2 and will be the indices
     380             :                       // of out. I are the indices of in2 (which are a subset of
     381             :                       // the indices of in1)
     382             :         GRB_TRY(GrB_Vector_free(&in_rev));
     383             :     }
     384             :     else
     385             :     {
     386             :         GRB_TRY(GrB_Vector_nvals(&nvals2, in2));
     387             :         LG_TRY(LAGraph_Malloc((void **)&I, nvals2, sizeof(GrB_Index), msg));
     388             :         LG_TRY(LAGraph_Malloc((void **)&X2, nvals2, sizeof(GrB_Index), msg));
     389             :         GRB_TRY(GrB_Vector_wait(in2, GrB_MATERIALIZE));
     390             :         GRB_TRY(GrB_Vector_extractTuples_UINT64(
     391             :             I, X2, &nvals2, in2)); // X2 will be the indices of out and I are
     392             :                                    // the indices of in2 (which are the indices
     393             :                                    // of in1 as well)
     394             :     }
     395             : 
     396             :     GRB_TRY(GrB_Vector_clear(out));
     397             :     GRB_TRY(GrB_Vector_wait(in1, GrB_MATERIALIZE));
     398             :     if (udt)
     399             :     {
     400             :         vertex *X1_V = NULL;
     401             :         LG_TRY(LAGraph_Malloc((void **)&X1_V, nvals2, sizeof(vertex), msg));
     402             :         for (uint64_t i = 0; i < nvals2; i++)
     403             :         {
     404             :             GRB_TRY(GrB_Vector_extractElement_UDT(
     405             :                 (void *)&X1_V[i], in1,
     406             :                 (GrB_Index)I[i])); // value I[i] corresponds to pos X2[i] in
     407             :                                    // out due to the tuple extraction and X[i]
     408             :                                    // will replace it
     409             :         }
     410             :         GRB_TRY(GrB_Vector_clear(in1));
     411             :         GRB_TRY(GrB_Vector_build_UDT(out, X2, (void *)X1_V, nvals2,
     412             :                                      NULL)); // no dups are left
     413             :         LAGRAPH_TRY(LAGraph_Free((void **)&X1_V, msg));
     414             :     }
     415             :     else
     416             :     {
     417             :         LG_TRY(LAGraph_Malloc((void **)&X1, nvals2, sizeof(GrB_UINT64), msg));
     418             :         for (GrB_Index i = 0; i < nvals2; i++)
     419             :         {
     420             :             GRB_TRY(GrB_Vector_extractElement_UINT64(&X1[i], in1, I[i]));
     421             :         }
     422             :         GRB_TRY(GrB_Vector_clear(in1));
     423             :         GRB_TRY(GrB_Vector_build_UINT64(out, X2, X1, nvals2, NULL));
     424             :     }
     425             :     LG_FREE_ALL;
     426             : #endif
     427         810 :     return (GrB_SUCCESS);
     428             : }
     429             : 
     430             : //------------------------------------------------------------------------------
     431             : // LAGr_MaximumMatching
     432             : //------------------------------------------------------------------------------
     433             : 
     434             : #undef LG_FREE_WORK
     435             : #define LG_FREE_WORK                                                           \
     436             :     {                                                                          \
     437             :         GrB_free(&pathC);                                                      \
     438             :         GrB_free(&parentsR);                                                   \
     439             :         GrB_free(&Vertex);                                                     \
     440             :         GrB_free(&frontierC);                                                  \
     441             :         GrB_free(&frontierR);                                                  \
     442             :         GrB_free(&initFrontierOp);                                             \
     443             :         GrB_free(&I);                                                          \
     444             :         GrB_free(&MinParent);                                                  \
     445             :         GrB_free(&MinParent_Monoid);                                           \
     446             :         GrB_free(&Select2ndOp);                                                \
     447             :         GrB_free(&Select1stOp);                                                \
     448             :         GrB_free(&MinParent_2nd_Semiring);                                     \
     449             :         GrB_free(&MinParent_1st_Semiring);                                     \
     450             :         GrB_free(&getParentsOp);                                               \
     451             :         GrB_free(&getRootsOp);                                                 \
     452             :         GrB_free(&parentsUpdate);                                              \
     453             :         GrB_free(&ufrontierR);                                                 \
     454             :         GrB_free(&mateC);                                                      \
     455             :         GrB_free(&mateR);                                                      \
     456             :         GrB_free(&rootsufR);                                                   \
     457             :         GrB_free(&pathUpdate);                                                 \
     458             :         GrB_free(&rootufRIndexes);                                             \
     459             :         GrB_free(&rootsfR);                                                    \
     460             :         GrB_free(&rootfRIndexes);                                              \
     461             :         GrB_free(&buildfCTuplesOp);                                            \
     462             :         GrB_free(&vertexTypecastOp);                                           \
     463             :         GrB_free(&setParentsMatesOp);                                          \
     464             :         GrB_free(&vr);                                                         \
     465             :         GrB_free(&pathCopy);                                                   \
     466             :         GrB_free(&currentMatesR);                                              \
     467             :     }
     468             : 
     469             : #undef LG_FREE_ALL
     470             : #define LG_FREE_ALL                                                            \
     471             :     {                                                                          \
     472             :         LG_FREE_WORK;                                                          \
     473             :         GrB_free(&mateC);                                                      \
     474             :     }
     475             : 
     476          30 : int LAGr_MaximumMatching(
     477             :     // outputs:
     478             :     GrB_Vector
     479             :         *mateC_handle, // mateC(j) = i : Column j of the C subset is matched
     480             :                        // to row i of the R subset (ignored on input)
     481             :     GrB_Vector *mateR_handle, // mateR(i) = j : Row i of the R subset is matched
     482             :                               // to column j of the C subset (ignored on input)
     483             :     // inputs:
     484             :     GrB_Matrix A,  // input adjacency matrix, TODO: this should be a LAGraph
     485             :                    // of a BIPARTITE kind
     486             :     GrB_Matrix AT, // transpose of the input adjacency matrix, necessary to
     487             :                    // perform push-pull optimization
     488             :                    // if NULL, the push-pull optimization is not performed
     489             :     GrB_Vector mate_init, // input only, not modified, ignored if NULL
     490             :     bool col_init, // flag to indicate if the initial matching is provided from
     491             :                    // the columns' or from the rows' perspective, ignored if
     492             :                    // mate_init is NULL
     493             :     char *msg)
     494             : {
     495             : 
     496             :     //--------------------------------------------------------------------------
     497             :     // check inputs
     498             :     //--------------------------------------------------------------------------
     499             : 
     500          30 :     GrB_Vector pathC = NULL;    // make this bitmap/sparse,
     501             :                                 // if dense I would have to give
     502             :                                 //  all the entries and make the matrix 1-based
     503          30 :     GrB_Vector parentsR = NULL; // parents of row nodes that are reachable
     504             :                                 // from paths of the initial column frontier
     505          30 :     GrB_Type Vertex = NULL;
     506          30 :     GrB_Vector frontierC = NULL;
     507          30 :     GrB_Vector frontierR = NULL;
     508          30 :     GrB_IndexUnaryOp initFrontierOp = NULL;
     509          30 :     GrB_Vector I = NULL; // dense vector of 1's
     510          30 :     GrB_BinaryOp MinParent = NULL;
     511          30 :     GrB_Monoid MinParent_Monoid = NULL;
     512          30 :     GrB_BinaryOp Select2ndOp = NULL;
     513          30 :     GrB_BinaryOp Select1stOp = NULL;
     514          30 :     GrB_Semiring MinParent_2nd_Semiring = NULL;
     515          30 :     GrB_Semiring MinParent_1st_Semiring = NULL;
     516          30 :     GrB_UnaryOp getParentsOp = NULL;
     517          30 :     GrB_UnaryOp getRootsOp = NULL;
     518          30 :     GrB_Vector parentsUpdate = NULL; // unmatched rows of R frontier
     519          30 :     GrB_Vector ufrontierR = NULL;    // unmatched rows of R frontier
     520          30 :     GrB_Vector mateR = NULL;         // mateR(i) = j : Row i of the R subset is
     521             :                                      // matched to column j of the C subset
     522          30 :     GrB_Vector rootsufR = NULL;
     523          30 :     GrB_Vector pathUpdate = NULL;
     524          30 :     GrB_Vector rootufRIndexes = NULL;
     525          30 :     GrB_Vector rootsfR = NULL;
     526          30 :     GrB_Vector rootfRIndexes = NULL;
     527          30 :     GrB_IndexUnaryOp buildfCTuplesOp = NULL;
     528          30 :     GrB_UnaryOp vertexTypecastOp = NULL;
     529          30 :     GrB_BinaryOp setParentsMatesOp = NULL;
     530          30 :     GrB_Vector vr = NULL;
     531          30 :     GrB_Vector pathCopy = NULL;
     532          30 :     GrB_Vector currentMatesR = NULL;
     533             : 
     534          30 :     GrB_Vector mateC = NULL;
     535             : 
     536          30 :     LG_CLEAR_MSG;
     537             : 
     538          30 :     LG_ASSERT_MSG(mateC_handle != NULL || mateR_handle != NULL,
     539             :                   GrB_NULL_POINTER, "At least one output must be provided\n");
     540          30 :     LG_ASSERT_MSG(A != NULL || AT != NULL, GrB_NULL_POINTER,
     541             :                   "A matrix is NULL\n");
     542             : 
     543          30 :     if (mateC_handle != NULL)
     544             :     {
     545          30 :         (*mateC_handle) = NULL;
     546             :     }
     547          30 :     if (mateR_handle != NULL)
     548             :     {
     549          20 :         (*mateR_handle) = NULL;
     550             :     }
     551             : 
     552          30 :     bool do_pushpull = (AT != NULL) && (A != NULL);
     553             : 
     554          30 :     uint64_t ncols = 0;
     555          30 :     uint64_t nrows = 0;
     556             : 
     557          30 :     if (A != NULL)
     558             :     {
     559          20 :         GRB_TRY(GrB_Matrix_ncols(&ncols, A));
     560          20 :         GRB_TRY(GrB_Matrix_nrows(&nrows, A));
     561             :     }
     562             :     else
     563             :     {
     564          10 :         GRB_TRY(GrB_Matrix_nrows(&ncols, AT));
     565          10 :         GRB_TRY(GrB_Matrix_ncols(&nrows, AT));
     566             :     }
     567             : 
     568          30 :     GRB_TRY(GrB_Vector_new(&pathC, GrB_UINT64, ncols));
     569             : 
     570          30 :     GRB_TRY(GrB_Vector_new(&parentsR, GrB_UINT64, nrows));
     571             : 
     572             : #if LAGRAPH_SUITESPARSE
     573          30 :     GRB_TRY(GxB_Type_new(&Vertex, sizeof(vertex), "vertex", VERTEX_DEFN));
     574             : #else
     575             :     GRB_TRY(GrB_Type_new(&Vertex, sizeof(vertex)));
     576             : #endif
     577             : 
     578          30 :     GRB_TRY(GrB_Vector_new(&frontierC, Vertex, ncols));
     579             : 
     580          30 :     GRB_TRY(GrB_Vector_new(&frontierR, Vertex, nrows));
     581             : 
     582             : #if LAGRAPH_SUITESPARSE
     583          30 :     GRB_TRY(GxB_IndexUnaryOp_new(&initFrontierOp, (void *)initFrontier, Vertex,
     584             :                                  GrB_BOOL, GrB_BOOL, "initFrontier",
     585             :                                  INIT_FRONTIER_DEFN));
     586             : #else
     587             :     GRB_TRY(GrB_IndexUnaryOp_new(&initFrontierOp, (void *)initFrontier, Vertex,
     588             :                                  GrB_BOOL, GrB_BOOL));
     589             : #endif
     590             : 
     591          30 :     GRB_TRY(GrB_Vector_new(&I, GrB_BOOL, ncols));
     592          30 :     GRB_TRY(GrB_Vector_assign_BOOL(I, NULL, NULL, 1, GrB_ALL, ncols, NULL));
     593             : 
     594             : #if LAGRAPH_SUITESPARSE
     595          30 :     GRB_TRY(GxB_BinaryOp_new(&MinParent, (void *)minparent, Vertex, Vertex,
     596             :                              Vertex, "minparent", MIN_PARENT_DEFN));
     597             : #else
     598             :     GRB_TRY(GrB_BinaryOp_new(&MinParent, (void *)minparent, Vertex, Vertex,
     599             :                              Vertex));
     600             : #endif
     601          30 :     vertex infinityParent = {GrB_INDEX_MAX + 1, 0};
     602          30 :     GRB_TRY(GrB_Monoid_new_UDT(&MinParent_Monoid, MinParent, &infinityParent));
     603             : 
     604             : #if LAGRAPH_SUITESPARSE
     605          30 :     GRB_TRY(GxB_BinaryOp_new(&Select2ndOp, (void *)select2nd, Vertex, GrB_BOOL,
     606             :                              Vertex, "select2nd", SELECT_2ND_DEFN));
     607             : #else
     608             :     GRB_TRY(GrB_BinaryOp_new(&Select2ndOp, (void *)select2nd, Vertex, GrB_BOOL,
     609             :                              Vertex));
     610             : #endif
     611             : 
     612          30 :     GRB_TRY(GrB_Semiring_new(&MinParent_2nd_Semiring, MinParent_Monoid,
     613             :                              Select2ndOp));
     614             : 
     615             : #if LAGRAPH_SUITESPARSE
     616          30 :     GRB_TRY(GxB_BinaryOp_new(&Select1stOp, (void *)select1st, Vertex, Vertex,
     617             :                              GrB_BOOL, "select1st", SELECT_1ST_DEFN));
     618             : #else
     619             :     GRB_TRY(GrB_BinaryOp_new(&Select1stOp, (void *)select1st, Vertex, Vertex,
     620             :                              GrB_BOOL));
     621             : #endif
     622             : 
     623          30 :     GRB_TRY(GrB_Semiring_new(&MinParent_1st_Semiring, MinParent_Monoid,
     624             :                              Select1stOp));
     625             : 
     626             : #if LAGRAPH_SUITESPARSE
     627          30 :     GRB_TRY(GxB_UnaryOp_new(&getParentsOp, (void *)keepParents, GrB_UINT64,
     628             :                             Vertex, "keepParents", KEEP_PARENTS_DEFN));
     629             : #else
     630             :     GRB_TRY(GrB_UnaryOp_new(&getParentsOp, (void *)keepParents, GrB_UINT64,
     631             :                             Vertex));
     632             : #endif
     633             : 
     634             : #if LAGRAPH_SUITESPARSE
     635          30 :     GRB_TRY(GxB_UnaryOp_new(&getRootsOp, (void *)keepRoots, GrB_UINT64, Vertex,
     636             :                             "keepRoots", KEEP_ROOTS_DEFN));
     637             : #else
     638             :     GRB_TRY(
     639             :         GrB_UnaryOp_new(&getRootsOp, (void *)keepRoots, GrB_UINT64, Vertex));
     640             : #endif
     641             : 
     642          30 :     GRB_TRY(GrB_Vector_new(&parentsUpdate, GrB_UINT64, nrows));
     643             : 
     644          30 :     GRB_TRY(GrB_Vector_new(&ufrontierR, Vertex, nrows));
     645             : 
     646          30 :     GRB_TRY(GrB_Vector_new(&rootsufR, GrB_UINT64, nrows));
     647             : 
     648          30 :     GRB_TRY(GrB_Vector_new(&pathUpdate, GrB_UINT64, ncols));
     649             : 
     650          30 :     GRB_TRY(GrB_Vector_new(&rootufRIndexes, GrB_UINT64, ncols));
     651             : 
     652          30 :     GRB_TRY(GrB_Vector_new(&rootsfR, GrB_UINT64, nrows));
     653             : 
     654          30 :     GRB_TRY(GrB_Vector_new(&rootfRIndexes, GrB_UINT64, ncols));
     655             : 
     656             : #if LAGRAPH_SUITESPARSE
     657          30 :     GRB_TRY(GxB_IndexUnaryOp_new(&buildfCTuplesOp, (void *)buildfCTuples,
     658             :                                  Vertex, GrB_UINT64, GrB_BOOL, "buildfCTuples",
     659             :                                  BUILT_FC_TUPLES_DEFN));
     660             : #else
     661             :     GRB_TRY(GrB_IndexUnaryOp_new(&buildfCTuplesOp, (void *)buildfCTuples,
     662             :                                  Vertex, GrB_UINT64, GrB_BOOL));
     663             : #endif
     664             : 
     665             : #if LAGRAPH_SUITESPARSE
     666          30 :     GRB_TRY(GxB_UnaryOp_new(&vertexTypecastOp, (void *)vertexTypecast, Vertex,
     667             :                             GrB_UINT64, "vertexTypecast",
     668             :                             VERTEX_TYPECAST_DEFN));
     669             : #else
     670             :     GRB_TRY(GrB_UnaryOp_new(&vertexTypecastOp, (void *)vertexTypecast, Vertex,
     671             :                             GrB_UINT64));
     672             : #endif
     673             : 
     674             : #if LAGRAPH_SUITESPARSE
     675          30 :     GRB_TRY(GxB_BinaryOp_new(&setParentsMatesOp, (void *)setParentsMates,
     676             :                              Vertex, Vertex, Vertex, "setParentsMates",
     677             :                              SET_PARENTS_MATES_DEFN));
     678             : #else
     679             :     GRB_TRY(GrB_BinaryOp_new(&setParentsMatesOp, (void *)setParentsMates,
     680             :                              Vertex, Vertex, Vertex));
     681             : #endif
     682             : 
     683          30 :     GRB_TRY(GrB_Vector_new(&vr, GrB_UINT64, nrows));
     684             : 
     685          30 :     GRB_TRY(GrB_Vector_new(&pathCopy, GrB_UINT64, ncols));
     686             : 
     687          30 :     GRB_TRY(GrB_Vector_new(&currentMatesR, GrB_UINT64, nrows));
     688             : 
     689          30 :     uint64_t npath = 0;
     690          30 :     bool y = 0;
     691             : 
     692          30 :     GRB_TRY(GrB_Vector_new(&mateC, GrB_UINT64, ncols));
     693          30 :     GRB_TRY(GrB_Vector_new(&mateR, GrB_UINT64, nrows));
     694             : 
     695          30 :     if (mate_init != NULL)
     696             :     {
     697           6 :         uint64_t nmatched = 0;
     698           6 :         GRB_TRY(GrB_Vector_nvals(&nmatched, mate_init));
     699           6 :         if (nmatched)
     700             :         {
     701           6 :             if (col_init)
     702             :             {
     703             :                 // mateC = (uint64_t) mate_init
     704           4 :                 GRB_TRY(GrB_assign(mateC, NULL, NULL, mate_init, GrB_ALL, ncols,
     705             :                                    NULL));
     706             :                 // mateR = invert (mateC), but do not clear the input
     707           4 :                 LAGRAPH_TRY(invert_nondestructive(mateR, mateC, msg));
     708             :             }
     709             :             else
     710             :             {
     711             :                 // mateR = (uint64_t) mate_init
     712           2 :                 GRB_TRY(GrB_assign(mateR, NULL, NULL, mate_init, GrB_ALL, nrows,
     713             :                                    NULL));
     714             :                 // mateC = invert (mateR), but do not clear the input
     715           2 :                 LAGRAPH_TRY(invert_nondestructive(mateC, mateR, msg));
     716             :             }
     717             :         }
     718             :     }
     719             : 
     720             :     do
     721             :     {
     722         126 :         GRB_TRY(GrB_Vector_clear(parentsR));
     723             :         // for every col j not matched, assign f(j) = VERTEX(j,j)
     724         126 :         GRB_TRY(GrB_Vector_apply_IndexOp_BOOL(
     725             :             frontierC, mateC, NULL, initFrontierOp, I, true, GrB_DESC_RSC));
     726             : 
     727         126 :         uint64_t nfC = 0;
     728             : 
     729             :         do
     730             :         {
     731             :             //----------------------------------------------------------------------
     732             :             // STEPS 1,2: Explore neighbors of column frontier (one step of
     733             :             // BFS), keeping only unvisited rows in the frontierR
     734             :             //----------------------------------------------------------------------
     735         840 :             if (do_pushpull)
     736             :             {
     737             :                 int32_t kind;
     738          36 :                 LAGRAPH_TRY(LG_GET_FORMAT_HINT(frontierC, &kind));
     739          36 :                 if (kind == LG_BITMAP || kind == LG_FULL)
     740             :                 {
     741             :                     // the frontierC vector is bitmap or full
     742             :                     // pull (vector's values are pulled into A)
     743          16 :                     GRB_TRY(GrB_mxv(frontierR, parentsR, NULL,
     744             :                                     MinParent_2nd_Semiring, A, frontierC,
     745             :                                     GrB_DESC_RSC));
     746             :                 }
     747             :                 else
     748             :                 {
     749             :                     // the frontierC vector is sparse or hypersparse
     750             :                     // push (vector's values are pushed to A)
     751          20 :                     GRB_TRY(GrB_vxm(frontierR, NULL, NULL,
     752             :                                     MinParent_1st_Semiring, frontierC, AT,
     753             :                                     GrB_DESC_R));
     754          20 :                     GRB_TRY(GrB_Vector_assign(frontierR, parentsR, NULL,
     755             :                                               frontierR, GrB_ALL, nrows,
     756             :                                               GrB_DESC_RSC));
     757             :                 }
     758             :             }
     759             :             else
     760             :             {
     761         804 :                 if (A != NULL)
     762             :                 {
     763             :                     // Only the pull method can be used if AT is not given
     764         524 :                     GRB_TRY(GrB_mxv(frontierR, parentsR, NULL,
     765             :                                     MinParent_2nd_Semiring, A, frontierC,
     766             :                                     GrB_DESC_RSC));
     767             :                 }
     768             :                 else
     769             :                 {
     770             :                     // Only the push method can be used if A is not given
     771         280 :                     GRB_TRY(GrB_vxm(frontierR, NULL, NULL,
     772             :                                     MinParent_1st_Semiring, frontierC, AT,
     773             :                                     GrB_DESC_R));
     774         280 :                     GRB_TRY(GrB_Vector_assign(frontierR, parentsR, NULL,
     775             :                                               frontierR, GrB_ALL, nrows,
     776             :                                               GrB_DESC_RSC));
     777             :                 }
     778             :             }
     779             : 
     780             :             //----------------------------------------------------------------------
     781             :             // STEPS 3,4: Select univisited, matched and unmatched row vertices
     782             :             //----------------------------------------------------------------------
     783             :             // set parents of row frontier
     784         840 :             GRB_TRY(GrB_Vector_apply(
     785             :                 parentsR, frontierR, NULL, getParentsOp, frontierR,
     786             :                 GrB_DESC_S)); // use input as mask to only update or insert
     787             :                               // parents without deleting the ones not
     788             :                               // updated
     789             : 
     790             :             // select unmatched rows of the R frontier
     791         840 :             GRB_TRY(GrB_Vector_assign(ufrontierR, mateR, NULL, frontierR,
     792             :                                       GrB_ALL, nrows, GrB_DESC_RSC));
     793             :             // select matched rows of the R frontier
     794         840 :             GRB_TRY(GrB_Vector_assign(frontierR, mateR, NULL, frontierR,
     795             :                                       GrB_ALL, nrows, GrB_DESC_RS));
     796             : 
     797             :             // keep only mates of rows in frontierR
     798         840 :             GRB_TRY(GrB_Vector_assign(currentMatesR, frontierR, NULL, mateR,
     799             :                                       GrB_ALL, nrows, GrB_DESC_RS));
     800             : 
     801         840 :             uint64_t nUfR = 0, nfR = 0;
     802         840 :             GRB_TRY(GrB_Vector_nvals(&nUfR, ufrontierR));
     803         840 :             GRB_TRY(GrB_Vector_nvals(&nfR, frontierR));
     804             : 
     805         840 :             if (nUfR)
     806             :             {
     807             :                 //----------------------------------------------------------------------
     808             :                 // STEP 5: Store endpoints of newly discovered augmenting paths
     809             :                 //----------------------------------------------------------------------
     810             :                 // get roots of unmatched row nodes in the R frontier
     811         264 :                 GRB_TRY(GrB_Vector_apply(rootsufR, NULL, NULL, getRootsOp,
     812             :                                          ufrontierR, NULL));
     813             : 
     814             :                 // pathUpdate = invert (rootsufR), but need to handle
     815             :                 // duplicates
     816         264 :                 LAGRAPH_TRY(invert(pathUpdate, rootsufR, /*dups=*/true, msg));
     817             : 
     818         264 :                 GRB_TRY(GrB_Vector_assign(
     819             :                     pathC, pathUpdate, NULL, pathUpdate, GrB_ALL, ncols,
     820             :                     GrB_DESC_S)); // update path without deleting the values
     821             :                                   // not updated when GrB_ALL is used, ni is
     822             :                                   // the number of rows of the vector
     823             : 
     824             :                 //----------------------------------------------------------------------
     825             :                 // STEP 6: Prune vertices in trees yielding augmenting paths
     826             :                 //----------------------------------------------------------------------
     827         264 :                 GRB_TRY(GrB_Vector_clear(rootfRIndexes));
     828             : 
     829         264 :                 if (nfR)
     830             :                 {
     831             :                     // get roots of row nodes in the current R frontier
     832         234 :                     GRB_TRY(GrB_Vector_apply(rootsfR, NULL, NULL, getRootsOp,
     833             :                                              frontierR, NULL));
     834             : 
     835             :                     // keep mates and roots of the R frontier (ordered indices)
     836         234 :                     LAGRAPH_TRY(
     837             :                         invert_2(rootfRIndexes, currentMatesR, rootsfR,
     838             :                                  /*dups=*/true, /*udt=*/false,
     839             :                                  msg)); // rootsfRIndexes(j) = i, where i
     840             :                                         // is the col mate of the first
     841             :                                         // row included in the current R
     842             :                                         // frontier with a col root of j
     843             : 
     844             :                     // keep only col roots that are not included in ufR
     845         234 :                     GRB_TRY(GrB_Vector_assign(rootfRIndexes, pathUpdate, NULL,
     846             :                                               rootfRIndexes, GrB_ALL, ncols,
     847             :                                               GrB_DESC_RSC));
     848             : 
     849             :                     //----------------------------------------------------------------------
     850             :                     // STEP 7a (ufrontierR not empty): Move values in the
     851             :                     // correct positions for the C frontier
     852             :                     //----------------------------------------------------------------------
     853             :                     // rootfRIndexes = invert (rootfRIndexes), so that:
     854             :                     // if LAGRAPH_SUITESPARSE: rootfRIndexes(i) = j,
     855             :                     // where (i,j) = (parentC, rootC) of the new frontier C
     856             :                     // else: rootfRIndexes(i) = j, where i is the matched child
     857             :                     // of the current frontier R that stems from root path of j
     858         234 :                     LAGRAPH_TRY(invert(rootfRIndexes, rootfRIndexes,
     859             :                                        /*dups=*/false, msg));
     860             :                 }
     861             : 
     862             :                 //----------------------------------------------------------------------
     863             :                 // STEP 7b (ufrontierR not empty): Build tuple of (parentC,
     864             :                 // rootC)
     865             :                 //----------------------------------------------------------------------
     866         264 :                 GRB_TRY(GrB_Vector_clear(frontierC));
     867         264 :                 GRB_TRY(GrB_Vector_apply_IndexOp_BOOL(frontierC, NULL, NULL,
     868             :                                                      buildfCTuplesOp,
     869             :                                                      rootfRIndexes, true, NULL));
     870             :             }
     871             :             else
     872             :             {
     873             :                 //----------------------------------------------------------------------
     874             :                 // STEP 7a (ufrontierR is empty): Set parents of the R frontier
     875             :                 // to their mates
     876             :                 //----------------------------------------------------------------------
     877             :                 // typecast mateR to ensure domain match with frontier R and
     878             :                 // apply op on frontier to set parents to mates
     879         576 :                 GRB_TRY(
     880             :                     GrB_Vector_apply(frontierR, NULL, setParentsMatesOp,
     881             :                                      vertexTypecastOp, currentMatesR,
     882             :                                      NULL)); // fR(i) = (column mate of i,
     883             :                                              // rootC) add the structural mask
     884             : 
     885             :                 //----------------------------------------------------------------------
     886             :                 // STEP 7b (ufrontierR is empty): Move values in the correct
     887             :                 // positions for the C frontier
     888             :                 //----------------------------------------------------------------------
     889             :                 // invert fr and assign to fC
     890             :                 // (currentMatesR already contains only the rows of fR)
     891         576 :                 LAGRAPH_TRY(invert_2(frontierC, frontierR, currentMatesR,
     892             :                                      /*dups=*/false, /*udt=*/true, msg));
     893             :             }
     894             : 
     895         840 :             GRB_TRY(GrB_Vector_nvals(&nfC, frontierC));
     896             : 
     897         840 :         } while (nfC);
     898             : 
     899             :         //----------------------------------------------------------------------
     900             :         // STEP 8: Augment matching by all augmenting paths discovered in
     901             :         // this phase
     902             :         //----------------------------------------------------------------------
     903         126 :         GRB_TRY(GrB_Vector_nvals(&npath, pathC));
     904         126 :         uint64_t npathCopy = npath;
     905         702 :         while (npath)
     906             :         {
     907             :             // vr = invert (pathC), leaving pathC empty
     908             :             // pathC doesn't have dup values as it stems from an invertion
     909         576 :             LAGRAPH_TRY(invert(vr, pathC, /*dups=*/false, msg));
     910             : 
     911             :             // assign parents of rows to rows
     912         576 :             GRB_TRY(GrB_Vector_assign(
     913             :                 vr, vr, NULL, parentsR, GrB_ALL, nrows,
     914             :                 GrB_DESC_S)); // update the values of vr (descriptor needed
     915             :                               // to use mask's structure and not values)
     916             : 
     917             :             // update mateR:  mateR<vr> = vr
     918         576 :             GRB_TRY(GrB_Vector_assign(mateR, vr, NULL, vr, GrB_ALL, nrows,
     919             :                                       GrB_DESC_S));
     920             : 
     921             :             // pathC = invert (vr), leaving vr empty
     922         576 :             LAGRAPH_TRY(invert(pathC, vr, /*dups=*/false, msg));
     923             : 
     924             :             // keep a copy of the previous row matches of the matched cols
     925             :             // that will alter mates
     926         576 :             GRB_TRY(GrB_Vector_assign(pathCopy, pathC, NULL, mateC, GrB_ALL,
     927             :                                       ncols, GrB_DESC_RS));
     928             : 
     929             :             // update mateC
     930         576 :             GRB_TRY(GrB_Vector_assign(mateC, pathC, NULL, pathC, GrB_ALL, ncols,
     931             :                                       GrB_DESC_S));
     932             : 
     933             :             // At this point, mateR and mateC are in sync.  That is, they
     934             :             // are inversions of each other (mateR == invert(mateC) and
     935             :             // mateC == invert (mateR) both hold).
     936             : 
     937             :             // swap path and pathCopy
     938         576 :             GrB_Vector tmp = pathC;
     939         576 :             pathC = pathCopy;
     940         576 :             pathCopy = tmp;
     941             : 
     942         576 :             GRB_TRY(GrB_Vector_nvals(&npath, pathC));
     943             :         }
     944             : 
     945         126 :         npath = npathCopy;
     946         126 :     } while (npath); // only in the first and last iteration should this
     947             :                      // condition be false
     948             : 
     949             :     // return result
     950          30 :     if (mateC_handle != NULL)
     951             :     {
     952          30 :         (*mateC_handle) = mateC;
     953          30 :         mateC = NULL ;
     954             :     }
     955          30 :     if (mateR_handle != NULL)
     956             :     {
     957          20 :         (*mateR_handle) = mateR;
     958          20 :         mateR = NULL ;
     959             :     }
     960          30 :     LG_FREE_WORK;
     961             : 
     962          30 :     return (GrB_SUCCESS);
     963             : }

Generated by: LCOV version 1.14