1 //------------------------------------------------------------------------------
2 // GB_masker: R = masker (C, M, Z) constructs R for 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 (R, C, M, Z), does R=C ; R<M>=Z. No typecasting is performed.
11 // The operation is similar to both R=C+Z via GB_add and R=C.*Z via GB_emult,
12 // depending on the value of the mask.
13
14 // GB_masker is only called by GB_mask, which itself is only called by
15 // GB_accum_mask.
16
17 // Let R be the result of the mask. In the caller, R is written back into the
18 // final C matrix, but in GB_masker, C is a read-only matrix. Consider the
19 // following table, where "add" is the result of C+Z, an "emult" is the result
20 // of C.*Z.
21
22 // R = masker (C,M,Z)
23
24 // C(i,j) Z(i,j) add emult M(i,j)=1 M(i,j)=0
25
26 // ------ ------ ------ ------ -------- --------
27
28 // cij zij cij+zij cij*zij zij cij
29
30 // - zij zij - zij -
31
32 // cij - cij - - cij
33
34 // - - - - - -
35
36 // As a result, GB_masker is very similar to GB_add and GB_emult. The
37 // vectors that appear in R are bounded by the set union of C and Z, just
38 // like GB_add when the mask is *not* present. The pattern of R is bounded
39 // by the pattern of C+Z, also ignoring the mask.
40
41 // C is always sparse or hypersparse; if C is bitmap or full, GB_subassign is
42 // used instead, since C(:,:)<M>=Z can directly modify C in that case, without
43 // creating zombies or pending tuples, in GB_bitmap_assign.
44
45 // M and Z can have any sparsity structure: sparse, hypersparse, bitmap, or
46 // full. R is constructed as sparse, hypersparse, or bitmap, depending on
47 // the sparsity of M and Z, as determined by GB_masker_sparsity.
48
49 #include "GB_mask.h"
50 #include "GB_add.h"
51 #define GB_FREE_ALL ;
52
GB_masker(GrB_Matrix R,const bool R_is_csc,const GrB_Matrix M,const bool Mask_comp,const bool Mask_struct,const GrB_Matrix C,const GrB_Matrix Z,GB_Context Context)53 GrB_Info GB_masker // R = masker (C, M, Z)
54 (
55 GrB_Matrix R, // output matrix, static header
56 const bool R_is_csc, // format of output matrix R
57 const GrB_Matrix M, // required input mask
58 const bool Mask_comp, // descriptor for M
59 const bool Mask_struct, // if true, use the only structure of M
60 const GrB_Matrix C, // input C matrix
61 const GrB_Matrix Z, // input Z matrix
62 GB_Context Context
63 )
64 {
65
66 //--------------------------------------------------------------------------
67 // check inputs
68 //--------------------------------------------------------------------------
69
70 GrB_Info info ;
71
72 ASSERT (R != NULL && R->static_header) ;
73
74 ASSERT_MATRIX_OK (M, "M for masker", GB0) ;
75 ASSERT (!GB_PENDING (M)) ;
76 ASSERT (!GB_JUMBLED (M)) ;
77 ASSERT (!GB_ZOMBIES (M)) ;
78
79 ASSERT_MATRIX_OK (C, "C for masker", GB0) ;
80 ASSERT (!GB_PENDING (C)) ;
81 ASSERT (!GB_JUMBLED (C)) ;
82 ASSERT (!GB_ZOMBIES (C)) ;
83
84 ASSERT_MATRIX_OK (Z, "Z for masker", GB0) ;
85 ASSERT (!GB_PENDING (Z)) ;
86 ASSERT (!GB_JUMBLED (Z)) ;
87 ASSERT (!GB_ZOMBIES (Z)) ;
88
89 ASSERT (!GB_IS_BITMAP (C)) ; // GB_masker not used if C is bitmap
90 ASSERT (!GB_IS_FULL (C)) ; // GB_masker not used if C is full
91
92 ASSERT (C->vdim == Z->vdim && C->vlen == Z->vlen) ;
93 ASSERT (C->vdim == M->vdim && C->vlen == M->vlen) ;
94
95 //--------------------------------------------------------------------------
96 // determine the sparsity of R
97 //--------------------------------------------------------------------------
98
99 int R_sparsity = GB_masker_sparsity (C, M, Mask_comp, Z) ;
100
101 //--------------------------------------------------------------------------
102 // initializations
103 //--------------------------------------------------------------------------
104
105 int64_t Rnvec, Rnvec_nonempty ;
106 int64_t *Rp = NULL ; size_t Rp_size = 0 ;
107 int64_t *Rh = NULL ; size_t Rh_size = 0 ;
108 int64_t *R_to_M = NULL ; size_t R_to_M_size = 0 ;
109 int64_t *R_to_C = NULL ; size_t R_to_C_size = 0 ;
110 int64_t *R_to_Z = NULL ; size_t R_to_Z_size = 0 ;
111 int R_ntasks = 0, R_nthreads ;
112 size_t TaskList_size = 0 ;
113 GB_task_struct *TaskList = NULL ;
114
115 //--------------------------------------------------------------------------
116 // phase0: finalize the sparsity structure of R and the vectors of R
117 //--------------------------------------------------------------------------
118
119 // This phase is identical to phase0 of GB_add, except that Ch is never a
120 // deep or shallow copy of Mh.
121
122 info = GB_add_phase0 (
123 // computed by by phase0:
124 &Rnvec, &Rh, &Rh_size,
125 &R_to_M, &R_to_M_size,
126 &R_to_C, &R_to_C_size,
127 &R_to_Z, &R_to_Z_size, NULL, &R_sparsity,
128 // original input:
129 M, C, Z, Context) ;
130 if (info != GrB_SUCCESS)
131 {
132 // out of memory
133 return (info) ;
134 }
135
136 GBURBLE ("masker:(%s:%s%s%s%s%s=%s) ",
137 GB_sparsity_char (R_sparsity),
138 GB_sparsity_char_matrix (C),
139 Mask_struct ? "{" : "<",
140 Mask_comp ? "!" : "",
141 GB_sparsity_char_matrix (M),
142 Mask_struct ? "}" : ">",
143 GB_sparsity_char_matrix (Z)) ;
144
145 //--------------------------------------------------------------------------
146 // phase1: split R into tasks, and count entries in each vector of R
147 //--------------------------------------------------------------------------
148
149 if (R_sparsity == GxB_SPARSE || R_sparsity == GxB_HYPERSPARSE)
150 {
151
152 //----------------------------------------------------------------------
153 // R is sparse or hypersparse: slice and analyze the R matrix
154 //----------------------------------------------------------------------
155
156 // phase1a: split R into tasks
157 info = GB_ewise_slice (
158 // computed by phase1a
159 &TaskList, &TaskList_size, &R_ntasks, &R_nthreads,
160 // computed by phase0:
161 Rnvec, Rh, R_to_M, R_to_C, R_to_Z, false,
162 // original input:
163 M, C, Z, Context) ;
164 if (info != GrB_SUCCESS)
165 {
166 // out of memory; free everything allocated by GB_add_phase0
167 GB_FREE (&Rh, Rh_size) ;
168 GB_FREE_WERK (&R_to_M, R_to_M_size) ;
169 GB_FREE_WERK (&R_to_C, R_to_C_size) ;
170 GB_FREE_WERK (&R_to_Z, R_to_Z_size) ;
171 return (info) ;
172 }
173
174 // count the number of entries in each vector of R
175 info = GB_masker_phase1 (
176 // computed or used by phase1:
177 &Rp, &Rp_size, &Rnvec_nonempty,
178 // from phase1a:
179 TaskList, R_ntasks, R_nthreads,
180 // from phase0:
181 Rnvec, Rh, R_to_M, R_to_C, R_to_Z,
182 // original input:
183 M, Mask_comp, Mask_struct, C, Z, Context) ;
184 if (info != GrB_SUCCESS)
185 {
186 // out of memory; free everything allocated by GB_add_phase0
187 GB_FREE_WERK (&TaskList, TaskList_size) ;
188 GB_FREE (&Rh, Rh_size) ;
189 GB_FREE_WERK (&R_to_M, R_to_M_size) ;
190 GB_FREE_WERK (&R_to_C, R_to_C_size) ;
191 GB_FREE_WERK (&R_to_Z, R_to_Z_size) ;
192 return (info) ;
193 }
194
195 }
196 else
197 {
198
199 //----------------------------------------------------------------------
200 // R is bitmap or full: only determine how many threads to use
201 //----------------------------------------------------------------------
202
203 GB_GET_NTHREADS_MAX (nthreads_max, chunk, Context) ;
204 R_nthreads = GB_nthreads (M->vlen * M->vdim, chunk, nthreads_max) ;
205 }
206
207 //--------------------------------------------------------------------------
208 // phase2: compute the entries (indices and values) in each vector of R
209 //--------------------------------------------------------------------------
210
211 // Rp and Rh are either freed by phase2, or transplanted into R.
212 // Either way, they are not freed here.
213
214 info = GB_masker_phase2 (
215 // computed or used by phase2:
216 R, R_is_csc,
217 // from phase1:
218 &Rp, Rp_size, Rnvec_nonempty,
219 // from phase1a:
220 TaskList, R_ntasks, R_nthreads,
221 // from phase0:
222 Rnvec, &Rh, Rh_size, R_to_M, R_to_C, R_to_Z, R_sparsity,
223 // original input:
224 M, Mask_comp, Mask_struct, C, Z, Context) ;
225
226 // if successful, Rh and Rp must not be freed; they are now R->h and R->p
227
228 // free workspace
229 GB_FREE_WERK (&TaskList, TaskList_size) ;
230 GB_FREE_WERK (&R_to_M, R_to_M_size) ;
231 GB_FREE_WERK (&R_to_C, R_to_C_size) ;
232 GB_FREE_WERK (&R_to_Z, R_to_Z_size) ;
233
234 if (info != GrB_SUCCESS)
235 {
236 // out of memory; note that Rp and Rh are already freed
237 return (info) ;
238 }
239
240 //--------------------------------------------------------------------------
241 // return result
242 //--------------------------------------------------------------------------
243
244 ASSERT_MATRIX_OK (R, "R output for masker", GB0) ;
245 return (GrB_SUCCESS) ;
246 }
247
248