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 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
|
//------------------------------------------------------------------------------
// GB_setElement: C(row,col) = scalar or += scalar
//------------------------------------------------------------------------------
// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2022, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// Sets the value of single scalar, C(row,col) = scalar, or C(row,col)+=scalar,
// typecasting from the type of scalar to the type of C, as needed. Not
// user-callable; does the work for all GrB_*_setElement* functions, and for
// GrB_*assign when a single entry is modified.
// If C(row,col) is already present in the matrix, its value is overwritten
// with the scalar. Otherwise, if the mode determined by GrB_init is
// non-blocking, the tuple (i,j,scalar) is appended to a list of pending tuples
// to C. GB_wait assembles these pending tuples.
// GB_setElement when accum is NULL is used by GrB_*_setElement. It is the
// same as GrB_*assign with an implied SECOND accum operator whose ztype,
// xtype, and ytype are the same as C, with I=i, J=j, a 1-by-1 dense matrix A
// (where nnz (A) == 1), no mask, mask not complemented, C_replace effectively
// false (its value is ignored), and A transpose effectively false (since
// transposing a scalar has no effect).
// GB_setElement when accum is not NULL uses the accum operator instead of the
// implied SECOND operator. It is used by GrB_*_assign, as a special case.
// Compare this function with GrB_*_extractElement_*
#include "GB_Pending.h"
#define GB_FREE_ALL ;
GrB_Info GB_setElement // set a single entry, C(row,col) = scalar
(
GrB_Matrix C, // matrix to modify
const GrB_BinaryOp accum, // if NULL: C(row,col) = scalar
// else: C(row,col) += scalar
const void *scalar, // scalar to set
const GrB_Index row, // row index
const GrB_Index col, // column index
const GB_Type_code scalar_code, // type of the scalar
GB_Context Context
)
{
//--------------------------------------------------------------------------
// check inputs
//--------------------------------------------------------------------------
GrB_Info info ;
ASSERT (C != NULL) ;
GB_RETURN_IF_NULL (scalar) ;
if (row >= GB_NROWS (C))
{
GB_ERROR (GrB_INVALID_INDEX,
"Row index " GBu " out of range; must be < " GBd,
row, GB_NROWS (C)) ;
}
if (col >= GB_NCOLS (C))
{
GB_ERROR (GrB_INVALID_INDEX,
"Column index " GBu " out of range; must be < " GBd,
col, GB_NCOLS (C)) ;
}
ASSERT (scalar_code <= GB_UDT_code) ;
GrB_Type ctype = C->type ;
GB_Type_code ccode = ctype->code ;
// scalar_code and C must be compatible
if (!GB_code_compatible (scalar_code, ccode))
{
GB_ERROR (GrB_DOMAIN_MISMATCH,
"Input scalar of type [%s]\n"
"cannot be typecast to entry of type [%s]",
GB_code_string (scalar_code), ctype->name) ;
}
if (accum != NULL)
{
// C and scalar must be compatible with the accum operator
GB_RETURN_IF_FAULTY_OR_POSITIONAL (accum) ;
GB_OK (GB_BinaryOp_compatible (accum, ctype, ctype, NULL, scalar_code,
Context)) ;
}
// pending tuples and zombies are expected, and C might be jumbled too
ASSERT (GB_JUMBLED_OK (C)) ;
ASSERT (GB_PENDING_OK (C)) ;
ASSERT (GB_ZOMBIES_OK (C)) ;
//--------------------------------------------------------------------------
// sort C if needed; do not assemble pending tuples or kill zombies yet
//--------------------------------------------------------------------------
if (C->jumbled)
{
GB_OK (GB_wait (C, "C (setElement:jumbled)", Context)) ;
}
// zombies and pending tuples are still OK, but C is no longer jumbled
ASSERT (!GB_JUMBLED (C)) ;
ASSERT (GB_PENDING_OK (C)) ;
ASSERT (GB_ZOMBIES_OK (C)) ;
bool C_is_full = GB_IS_FULL (C) ;
//--------------------------------------------------------------------------
// check if C needs to convert to non-iso, or if C is a new iso matrix
//--------------------------------------------------------------------------
// stype is the type of this scalar
GrB_Type stype = GB_code_type (scalar_code, ctype) ;
size_t csize = ctype->size ;
if (C->iso)
{
//----------------------------------------------------------------------
// typecast the scalar and compare with the iso value of C
//----------------------------------------------------------------------
bool convert_to_non_iso ;
if (accum != NULL)
{
// C(i,j) += scalar always converts C to non-iso
convert_to_non_iso = true ;
}
else if (ctype != stype)
{
// s = (ctype) scalar
GB_void s [GB_VLA(csize)] ;
GB_cast_scalar (s, ccode, scalar, scalar_code, csize) ;
// compare s with the iso value of C
convert_to_non_iso = (memcmp (C->x, s, csize) != 0) ;
}
else
{
// compare the scalar with the iso value of C
convert_to_non_iso = (memcmp (C->x, scalar, csize) != 0) ;
}
if (convert_to_non_iso)
{
// The new entry differs from the iso value of C. Assemble all
// pending tuples and convert C to non-iso. Zombies are OK.
if (C->Pending != NULL)
{
GB_OK (GB_wait (C, "C (setElement:to non-iso)", Context)) ;
}
GB_OK (GB_convert_any_to_non_iso (C, true, Context)) ;
}
}
else if (GB_nnz (C) == 0 && !C_is_full && C->Pending == NULL
&& accum == NULL)
{
//----------------------------------------------------------------------
// C is empty: this is the first setElement, convert C to iso
//----------------------------------------------------------------------
if (ctype != stype)
{
// s = (ctype) scalar
GB_void s [GB_VLA(csize)] ;
GB_cast_scalar (s, ccode, scalar, scalar_code, csize) ;
GB_OK (GB_convert_any_to_iso (C, s, Context)) ;
}
else
{
GB_OK (GB_convert_any_to_iso (C, (GB_void *) scalar, Context)) ;
}
}
//--------------------------------------------------------------------------
// handle the CSR/CSC format
//--------------------------------------------------------------------------
int64_t i, j ;
if (C->is_csc)
{
// set entry with index i in vector j
i = row ;
j = col ;
}
else
{
// set entry with index j in vector i
i = col ;
j = row ;
}
int64_t pleft ;
bool found = false ;
bool is_zombie ;
bool C_is_bitmap = GB_IS_BITMAP (C) ;
C_is_full = GB_IS_FULL (C) ;
if (C_is_full || C_is_bitmap)
{
//----------------------------------------------------------------------
// C is bitmap or full
//----------------------------------------------------------------------
pleft = i + j * C->vlen ;
found = true ;
is_zombie = false ;
}
else
{
//----------------------------------------------------------------------
// C is sparse or hypersparse
//----------------------------------------------------------------------
int64_t pC_start, pC_end ;
const int64_t *restrict Ch = C->h ;
if (C->nvals == 0)
{
// C is empty
found = false ;
}
else if (Ch != NULL)
{
// C is hypersparse, with at least one entry
int64_t k ;
if (C->Y == NULL)
{
// C is hypersparse but does not yet have a hyper_hash
k = 0 ;
found = GB_lookup (true, Ch, C->p, C->vlen, &k,
C->nvec-1, j, &pC_start, &pC_end) ;
}
else
{
// C is hypersparse, with a hyper_hash that is already built
k = GB_hyper_hash_lookup (C->p, C->Y->p, C->Y->i, C->Y->x,
C->Y->vdim-1, j, &pC_start, &pC_end) ;
found = (k >= 0) ;
}
ASSERT (GB_IMPLIES (found, j == Ch [k])) ;
}
else
{
// C is sparse
pC_start = C->p [j] ;
pC_end = C->p [j+1] ;
found = true ;
}
//----------------------------------------------------------------------
// binary search in kth vector for index i
//----------------------------------------------------------------------
if (found)
{
// vector j has been found; now look for index i
pleft = pC_start ;
int64_t pright = pC_end - 1 ;
// Time taken for this step is at most O(log(nnz(C(:,j))).
const int64_t *restrict Ci = C->i ;
GB_BINARY_SEARCH_ZOMBIE (i, Ci, pleft, pright, found,
C->nzombies, is_zombie) ;
}
}
//--------------------------------------------------------------------------
// set the element
//--------------------------------------------------------------------------
if (found)
{
//----------------------------------------------------------------------
// C (i,j) found
//----------------------------------------------------------------------
// if not zombie:
// no accum: action: ( =A ): copy A into C
// with accum: action: ( C+=A ): accumulate A into C
// else action: ( undelete ): bring a zombie back to life
int8_t cb = (C_is_bitmap) ? C->b [pleft] : 0 ;
if (!C->iso)
{
void *cx = ((GB_void *) C->x) + (pleft*csize) ;
if (accum == NULL || is_zombie || (C_is_bitmap && cb == 0))
{
// C(i,j) = (ctype) scalar
GB_cast_scalar (cx, ccode, scalar, scalar_code, csize) ;
}
else
{
// C(i,j) += scalar
GxB_binary_function faccum = accum->binop_function ;
GB_cast_function cast_C_to_X, cast_Z_to_Y, cast_Z_to_C ;
cast_C_to_X = GB_cast_factory (accum->xtype->code, ctype->code);
cast_Z_to_Y = GB_cast_factory (accum->ytype->code, scalar_code);
cast_Z_to_C = GB_cast_factory (ctype->code, accum->ztype->code);
// scalar workspace
GB_void xaccum [GB_VLA(accum->xtype->size)] ;
GB_void yaccum [GB_VLA(accum->ytype->size)] ;
GB_void zaccum [GB_VLA(accum->ztype->size)] ;
// xaccum = (accum->xtype) cx
cast_C_to_X (xaccum, cx, ctype->size) ;
// yaccum = (accum->ytype) scalar
cast_Z_to_Y (yaccum, scalar, accum->ytype->size) ;
// zaccum = xaccum "+" yaccum
faccum (zaccum, xaccum, yaccum) ;
// cx = (ctype) zaccum
cast_Z_to_C (cx, zaccum, ctype->size) ;
}
}
if (is_zombie)
{
// bring the zombie back to life
C->i [pleft] = i ;
C->nzombies-- ;
}
else if (C_is_bitmap)
{
// set the entry in the C bitmap
C->nvals += (cb == 0) ;
C->b [pleft] = 1 ;
}
return (GrB_SUCCESS) ;
}
else
{
//----------------------------------------------------------------------
// C (i,j) not found: add a pending tuple
//----------------------------------------------------------------------
// action: ( insert )
// No typecasting can be done. The new pending tuple must either be
// the first pending tuple, or its type must match the prior pending
// tuples. See GB_subassign_methods.h for a complete description.
//----------------------------------------------------------------------
// check for wait
//----------------------------------------------------------------------
bool wait = false ;
if (C->Pending == NULL)
{
// the new pending tuple is the first one, so it will define
// C->type-pending = stype. No need to wait.
wait = false ;
}
else
{
if (stype != C->Pending->type)
{
// the scalar type (stype) must match the type of the
// prior pending tuples. If the type is different, prior
// pending tuples must be assembled first.
wait = true ;
}
else if
(
// the types match, now check the pending operator
! (
// the operators are the same
(accum == C->Pending->op)
// or both operators are SECOND_Ctype, implicit or explicit
|| (GB_op_is_second (accum, ctype) &&
GB_op_is_second (C->Pending->op, ctype))
)
)
{
wait = true ;
}
}
if (wait)
{
//------------------------------------------------------------------
// incompatible pending tuples: wait is required
//------------------------------------------------------------------
// Pending tuples exist. Either the pending operator is not
// SECOND_ctype (implicit or explicit), or the type of prior
// pending tuples is not the same as the type of the scalar. This
// new tuple requires both conditions to hold. All prior tuples
// must be assembled before this new one can be added.
GB_OK (GB_wait (C, "C (setElement:incompatible pending tuples)",
Context)) ;
// repeat the search since the C(i,j) entry may have been in
// the list of pending tuples. There are no longer any pending
// tuples, so this recursion will only happen once. The
// pending operator will become the implicit SECOND_ctype, or
// accum, and the type of the pending tuples will become stype.
return (GB_setElement (C, accum, scalar, row, col, scalar_code,
Context)) ;
}
else
{
//------------------------------------------------------------------
// the new tuple is now compatible with prior tuples, if any
//------------------------------------------------------------------
ASSERT (GB_PENDING_OK (C)) ;
ASSERT (GB_ZOMBIES_OK (C)) ;
// C (i,j) must be added to the list of pending tuples.
// If this is the first pending tuple, then the type of pending
// tuples becomes the type of this scalar, and the pending operator
// becomes NULL, which is the implicit SECOND_ctype operator,
// or non-NULL if accum is present.
if (!GB_Pending_add (&(C->Pending), C->iso, (GB_void *) scalar,
stype, accum, i, j, C->vdim > 1, Context))
{
// out of memory
GB_phybix_free (C) ;
return (GrB_OUT_OF_MEMORY) ;
}
ASSERT (GB_PENDING (C)) ;
// if this was the first tuple, then the pending operator and
// pending type have been defined
if (accum == NULL)
{
ASSERT (GB_op_is_second (C->Pending->op, ctype)) ;
}
else
{
ASSERT (C->Pending->op == accum) ;
}
ASSERT (C->Pending->type == stype) ;
ASSERT (C->Pending->size == stype->size) ;
// one more pending tuple; block if too many of them
return (GB_block (C, Context)) ;
}
}
}
|