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