1 //------------------------------------------------------------------------------
2 // GB_masker_phase2: phase2 for R = masker (C,M,Z)
3 //------------------------------------------------------------------------------
4 
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7 
8 //------------------------------------------------------------------------------
9 
10 // GB_masker_phase2 computes R = masker (C,M,Z).  It is preceded first by
11 // GB_add_phase0, which computes the list of vectors of R to compute (Rh) and
12 // their location in C and Z (R_to_[CZ]).  Next, GB_masker_phase1 counts the
13 // entries in each vector R(:,j) and computes Rp.
14 
15 // GB_masker_phase2 computes the pattern and values of each vector of R(:,j),
16 // entirely in parallel.
17 
18 // R, M, C, and Z can be standard sparse or hypersparse, as determined by
19 // GB_add_phase0.  All cases of the mask M are handled: present and not
20 // complemented, and present and complemented.  The mask is always present.
21 
22 // This function either frees Rp and Rh, or transplants then into R, as R->p
23 // and R->h.  Either way, the caller must not free them.
24 
25 #include "GB_mask.h"
26 #include "GB_ek_slice.h"
27 #include "GB_unused.h"
28 
29 #undef  GB_FREE_WORK
30 #define GB_FREE_WORK                        \
31 {                                           \
32     GB_WERK_POP (M_ek_slicing, int64_t) ;   \
33     GB_WERK_POP (C_ek_slicing, int64_t) ;   \
34 }
35 
36 #undef  GB_FREE_ALL
37 #define GB_FREE_ALL                         \
38 {                                           \
39     GB_FREE_WORK ;                          \
40     GB_phbix_free (R) ;                     \
41 }
42 
GB_masker_phase2(GrB_Matrix R,const bool R_is_csc,int64_t ** Rp_handle,size_t Rp_size,const int64_t Rnvec_nonempty,const GB_task_struct * restrict TaskList,const int R_ntasks,const int R_nthreads,const int64_t Rnvec,int64_t ** Rh_handle,size_t Rh_size,const int64_t * restrict R_to_M,const int64_t * restrict R_to_C,const int64_t * restrict R_to_Z,const int R_sparsity,const GrB_Matrix M,const bool Mask_comp,const bool Mask_struct,const GrB_Matrix C,const GrB_Matrix Z,GB_Context Context)43 GrB_Info GB_masker_phase2           // phase2 for R = masker (C,M,Z)
44 (
45     GrB_Matrix R,                   // output matrix, static header
46     const bool R_is_csc,            // format of output matrix R
47     // from phase1:
48     int64_t **Rp_handle,            // vector pointers for R
49     size_t Rp_size,
50     const int64_t Rnvec_nonempty,   // # of non-empty vectors in R
51     // tasks from phase1a:
52     const GB_task_struct *restrict TaskList,     // array of structs
53     const int R_ntasks,               // # of tasks
54     const int R_nthreads,             // # of threads to use
55     // analysis from phase0:
56     const int64_t Rnvec,
57     int64_t **Rh_handle,
58     size_t Rh_size,
59     const int64_t *restrict R_to_M,
60     const int64_t *restrict R_to_C,
61     const int64_t *restrict R_to_Z,
62     const int R_sparsity,
63     // original input:
64     const GrB_Matrix M,             // required mask
65     const bool Mask_comp,           // if true, then M is complemented
66     const bool Mask_struct,         // if true, use the only structure of M
67     const GrB_Matrix C,
68     const GrB_Matrix Z,
69     GB_Context Context
70 )
71 {
72 
73     //--------------------------------------------------------------------------
74     // check inputs
75     //--------------------------------------------------------------------------
76 
77     ASSERT_MATRIX_OK (M, "M for mask phase2", GB0) ;
78     ASSERT (!GB_ZOMBIES (M)) ;
79     ASSERT (!GB_JUMBLED (M)) ;
80     ASSERT (!GB_PENDING (M)) ;
81 
82     ASSERT_MATRIX_OK (C, "C for mask phase2", GB0) ;
83     ASSERT (!GB_ZOMBIES (C)) ;
84     ASSERT (!GB_JUMBLED (C)) ;
85     ASSERT (!GB_PENDING (C)) ;
86 
87     ASSERT_MATRIX_OK (Z, "Z for mask phase2", GB0) ;
88     ASSERT (!GB_ZOMBIES (Z)) ;
89     ASSERT (!GB_JUMBLED (Z)) ;
90     ASSERT (!GB_PENDING (Z)) ;
91 
92     ASSERT (!GB_IS_BITMAP (C)) ;        // not used if C is bitmap
93 
94     ASSERT (C->vdim == Z->vdim && C->vlen == Z->vlen) ;
95     ASSERT (C->vdim == M->vdim && C->vlen == M->vlen) ;
96     ASSERT (C->type == Z->type) ;
97 
98     ASSERT (R != NULL && R->static_header) ;
99 
100     GB_WERK_DECLARE (C_ek_slicing, int64_t) ;
101     GB_WERK_DECLARE (M_ek_slicing, int64_t) ;
102 
103     ASSERT (Rp_handle != NULL) ;
104     ASSERT (Rh_handle != NULL) ;
105     int64_t *Rp = (*Rp_handle) ;
106     int64_t *Rh = (*Rh_handle) ;
107 
108     //--------------------------------------------------------------------------
109     // allocate the output matrix R
110     //--------------------------------------------------------------------------
111 
112     bool R_is_hyper = (R_sparsity == GxB_HYPERSPARSE) ;
113     bool R_is_sparse_or_hyper = (R_sparsity == GxB_SPARSE) || R_is_hyper ;
114     ASSERT (R_is_sparse_or_hyper == (Rp != NULL)) ;
115     ASSERT (R_is_hyper == (Rh != NULL)) ;
116 
117     int64_t rnz = (R_is_sparse_or_hyper) ? Rp [Rnvec] : C->vlen*C->vdim ;
118 
119     // allocate the result R (but do not allocate R->p or R->h)
120     GrB_Info info = GB_new_bix (&R, true, // any sparsity, static header
121         C->type, C->vlen, C->vdim, GB_Ap_null, R_is_csc,
122         R_sparsity, true, C->hyper_switch, Rnvec, rnz, true, Context) ;
123     if (info != GrB_SUCCESS)
124     {
125         // out of memory; caller must free R_to_M, R_to_C, R_to_Z
126         GB_FREE (Rp_handle, Rp_size) ;
127         GB_FREE (Rh_handle, Rh_size) ;
128         return (info) ;
129     }
130 
131     // add Rp as the vector pointers for R, from GB_masker_phase1
132     if (R_is_sparse_or_hyper)
133     {
134         R->nvec_nonempty = Rnvec_nonempty ;
135         R->p = (int64_t *) Rp ; R->p_size = Rp_size ;
136         (*Rp_handle) = NULL ;
137     }
138 
139     // add Rh as the hypersparse list for R, from GB_add_phase0
140     if (R_is_hyper)
141     {
142         R->h = (int64_t *) Rh ; R->h_size = Rh_size ;
143         R->nvec = Rnvec ;
144         (*Rh_handle) = NULL ;
145     }
146 
147     // now Rp and Rh have been transplanted into R, so they must not be freed.
148     ASSERT ((*Rp_handle) == NULL) ;
149     ASSERT ((*Rh_handle) == NULL) ;
150     R->magic = GB_MAGIC ;
151 
152     //--------------------------------------------------------------------------
153     // generic worker
154     //--------------------------------------------------------------------------
155 
156     #define GB_PHASE_2_OF_2
157     #include "GB_masker_template.c"
158 
159     //--------------------------------------------------------------------------
160     // prune empty vectors from Rh
161     //--------------------------------------------------------------------------
162 
163     GB_OK (GB_hypermatrix_prune (R, Context)) ;
164 
165     //--------------------------------------------------------------------------
166     // free workspace and return result
167     //--------------------------------------------------------------------------
168 
169     // caller must free R_to_M, R_to_C, and R_to_Z, but not Rp or Rh
170     GB_FREE_WORK ;
171     ASSERT_MATRIX_OK (R, "R output for mask phase2", GB0) ;
172     ASSERT (!GB_ZOMBIES (R)) ;
173     ASSERT (!GB_JUMBLED (R)) ;
174     ASSERT (!GB_PENDING (R)) ;
175     return (GrB_SUCCESS) ;
176 }
177 
178