1 //------------------------------------------------------------------------------
2 // GB_mex_assign: C<Mask>(I,J) = accum (C (I,J), A)
3 //------------------------------------------------------------------------------
4
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7
8 // This function is a wrapper for GrB_Matrix_assign, GrB_Matrix_assign_T
9 // GrB_Vector_assign, and GrB_Vector_assign_T (when kind=0 or by default). For
10 // these uses, the Mask must always be the same size as C.
11
12 // This mexFunction does calls GrB_Row_assign (when kind=2) or GrB_Col_assign
13 // (when kind=1). In these cases, the Mask is a single row or column,
14 // respectively. C is not modified outside that single row (for
15 // GrB_Row_assign) or column (for GrB_Col_assign).
16
17 // This function does the same thing as the MATLAB mimics GB_spec_assign.m
18 // (when kind=0), GB_spec_Col_assign (when kind=1), and GB_spec_Row_assign
19 // (when kind=2).
20
21 //------------------------------------------------------------------------------
22
23 #include "GB_mex.h"
24
25 #define USAGE "C = GB_mex_assign (C,Mask,accum,A,I,J,desc,kind) or (C, Work)"
26
27 #define FREE_ALL \
28 { \
29 GrB_Matrix_free_(&A) ; \
30 GrB_Matrix_free_(&Mask) ; \
31 GrB_Matrix_free_(&C) ; \
32 GrB_Descriptor_free_(&desc) ; \
33 GB_mx_put_global (true) ; \
34 }
35
36 #define GET_DEEP_COPY \
37 C = GB_mx_mxArray_to_Matrix (pargin [0], "C input", true, true) ; \
38 if (have_sparsity_control) \
39 { \
40 GxB_Matrix_Option_set (C, GxB_SPARSITY_CONTROL, C_sparsity_control) ; \
41 }
42
43 #define FREE_DEEP_COPY GrB_Matrix_free_(&C) ;
44
45 GrB_Matrix C = NULL ;
46 GrB_Matrix Mask = NULL ;
47 GrB_Matrix A = NULL ;
48 GrB_Descriptor desc = NULL ;
49 GrB_BinaryOp accum = NULL ;
50 GrB_Index *I = NULL, ni = 0, I_range [3] ;
51 GrB_Index *J = NULL, nj = 0, J_range [3] ;
52 bool ignore ;
53 bool malloc_debug = false ;
54 GrB_Info info = GrB_SUCCESS ;
55 int kind = 0 ;
56 GrB_Info assign (void) ;
57 int C_sparsity_control ;
58 int M_sparsity_control ;
59 bool have_sparsity_control = false ;
60
61 GrB_Info many_assign
62 (
63 int nwork,
64 int fA,
65 int fI,
66 int fJ,
67 int faccum,
68 int fMask,
69 int fdesc,
70 int fkind,
71 const mxArray *pargin [ ]
72 ) ;
73
74 //------------------------------------------------------------------------------
75 // assign: perform a single assignment
76 //------------------------------------------------------------------------------
77
78 #define OK(method) \
79 { \
80 info = method ; \
81 if (info != GrB_SUCCESS) \
82 { \
83 return (info) ; \
84 } \
85 }
86
assign()87 GrB_Info assign ( )
88 {
89 bool at = (desc != NULL && desc->in0 == GrB_TRAN) ;
90 GrB_Info info ;
91
92 int pr = 0 ;
93 bool ph = (pr > 0) ;
94
95 ASSERT_MATRIX_OK (C, "C for GB_mex_assign", pr) ;
96 ASSERT_MATRIX_OK_OR_NULL (Mask, "Mask for GB_mex_assign", pr) ;
97 ASSERT_MATRIX_OK (A, "A for GB_mex_assign", pr) ;
98 ASSERT_BINARYOP_OK_OR_NULL (accum, "accum for GB_mex_assign", pr) ;
99 ASSERT_DESCRIPTOR_OK_OR_NULL (desc, "desc for GB_mex_assign", pr) ;
100
101 if (kind == 1)
102 {
103 // test GrB_Col_assign
104 ASSERT (GB_VECTOR_OK (A)) ;
105 ASSERT (Mask == NULL || GB_VECTOR_OK (Mask)) ;
106 OK (GrB_Col_assign_(C, (GrB_Vector) Mask, accum, (GrB_Vector) A,
107 I, ni, J [0], desc)) ;
108 }
109 else if (kind == 2)
110 {
111 // test GrB_Row_assign
112 ASSERT (GB_VECTOR_OK (A)) ;
113 ASSERT (Mask == NULL || GB_VECTOR_OK (Mask)) ;
114 ASSERT_VECTOR_OK_OR_NULL ((GrB_Vector) Mask, "row mask", GB0) ;
115 ASSERT_VECTOR_OK ((GrB_Vector) A, "row u", GB0) ;
116
117 OK (GrB_Row_assign_(C, (GrB_Vector) Mask, accum, (GrB_Vector) A,
118 I [0], J, nj, desc)) ;
119 }
120 else if (GB_NROWS (A) == 1 && GB_NCOLS (A) == 1 && GB_NNZ (A) == 1)
121 {
122 // scalar expansion to matrix or vector
123 GB_void *Ax = A->x ;
124
125 if (ni == 1 && nj == 1 && Mask == NULL && I != GrB_ALL && J != GrB_ALL
126 && GB_op_is_second (accum, C->type) && A->type->code <= GB_FC64_code
127 && desc == NULL)
128 {
129 // test GrB_Matrix_setElement
130 #define ASSIGN(prefix,suffix, type) \
131 { \
132 type x = ((type *) Ax) [0] ; \
133 OK (prefix ## Matrix_setElement ## suffix \
134 (C, x, I [0], J [0])) ; \
135 } break ;
136
137 switch (A->type->code)
138 {
139 case GB_BOOL_code : ASSIGN (GrB_, _BOOL, bool) ;
140 case GB_INT8_code : ASSIGN (GrB_, _INT8, int8_t) ;
141 case GB_INT16_code : ASSIGN (GrB_, _INT16, int16_t) ;
142 case GB_INT32_code : ASSIGN (GrB_, _INT32, int32_t) ;
143 case GB_INT64_code : ASSIGN (GrB_, _INT64, int64_t) ;
144 case GB_UINT8_code : ASSIGN (GrB_, _UINT8, uint8_t) ;
145 case GB_UINT16_code : ASSIGN (GrB_, _UINT16, uint16_t) ;
146 case GB_UINT32_code : ASSIGN (GrB_, _UINT32, uint32_t) ;
147 case GB_UINT64_code : ASSIGN (GrB_, _UINT64, uint64_t) ;
148 case GB_FP32_code : ASSIGN (GrB_, _FP32, float) ;
149 case GB_FP64_code : ASSIGN (GrB_, _FP64, double) ;
150 case GB_FC32_code : ASSIGN (GxB_, _FC32, GxB_FC32_t) ;
151 case GB_FC64_code : ASSIGN (GxB_, _FC64, GxB_FC64_t) ;
152 case GB_UDT_code :
153 default:
154 FREE_ALL ;
155 mexErrMsgTxt ("GB_mex_assign: unknown type, setEl") ;
156 }
157
158 ASSERT_MATRIX_OK (C, "C after setElement", GB0) ;
159
160 }
161
162 if (GB_VECTOR_OK (C) && (Mask == NULL || GB_VECTOR_OK (Mask)))
163 {
164
165 // test GrB_Vector_assign_scalar functions
166 #undef ASSIGN
167 #define ASSIGN(prefix,suffix,type) \
168 { \
169 type x = ((type *) Ax) [0] ; \
170 OK (prefix ## Vector_assign ## suffix ((GrB_Vector) C, \
171 (GrB_Vector) Mask, accum, x, I, ni, desc)) ; \
172 } break ;
173
174 switch (A->type->code)
175 {
176 case GB_BOOL_code : ASSIGN (GrB_, _BOOL, bool) ;
177 case GB_INT8_code : ASSIGN (GrB_, _INT8, int8_t) ;
178 case GB_INT16_code : ASSIGN (GrB_, _INT16, int16_t) ;
179 case GB_INT32_code : ASSIGN (GrB_, _INT32, int32_t) ;
180 case GB_INT64_code : ASSIGN (GrB_, _INT64, int64_t) ;
181 case GB_UINT8_code : ASSIGN (GrB_, _UINT8, uint8_t) ;
182 case GB_UINT16_code : ASSIGN (GrB_, _UINT16, uint16_t) ;
183 case GB_UINT32_code : ASSIGN (GrB_, _UINT32, uint32_t) ;
184 case GB_UINT64_code : ASSIGN (GrB_, _UINT64, uint64_t) ;
185 case GB_FP32_code : ASSIGN (GrB_, _FP32, float) ;
186 case GB_FP64_code : ASSIGN (GrB_, _FP64, double) ;
187 case GB_FC32_code : ASSIGN (GxB_, _FC32, GxB_FC32_t) ;
188 case GB_FC64_code : ASSIGN (GxB_, _FC64, GxB_FC64_t) ;
189 case GB_UDT_code :
190 {
191 OK (GrB_Vector_assign_UDT ((GrB_Vector) C,
192 (GrB_Vector) Mask, accum, Ax, I, ni, desc)) ;
193 }
194 break ;
195 default:
196 FREE_ALL ;
197 mexErrMsgTxt ("GB_mex_assign: unknown type") ;
198 }
199
200 }
201 else
202 {
203
204 // test Matrix_assign_scalar functions
205 #undef ASSIGN
206 #define ASSIGN(prefix,suffix,type) \
207 { \
208 type x = ((type *) Ax) [0] ; \
209 OK (prefix ## Matrix_assign ## suffix \
210 (C, Mask, accum, x, I, ni, J, nj,desc)) ; \
211 } break ;
212
213 switch (A->type->code)
214 {
215 case GB_BOOL_code : ASSIGN (GrB_, _BOOL, bool) ;
216 case GB_INT8_code : ASSIGN (GrB_, _INT8, int8_t) ;
217 case GB_INT16_code : ASSIGN (GrB_, _INT16, int16_t) ;
218 case GB_INT32_code : ASSIGN (GrB_, _INT32, int32_t) ;
219 case GB_INT64_code : ASSIGN (GrB_, _INT64, int64_t) ;
220 case GB_UINT8_code : ASSIGN (GrB_, _UINT8, uint8_t) ;
221 case GB_UINT16_code : ASSIGN (GrB_, _UINT16, uint16_t) ;
222 case GB_UINT32_code : ASSIGN (GrB_, _UINT32, uint32_t) ;
223 case GB_UINT64_code : ASSIGN (GrB_, _UINT64, uint64_t) ;
224 case GB_FP32_code : ASSIGN (GrB_, _FP32, float) ;
225 case GB_FP64_code : ASSIGN (GrB_, _FP64, double) ;
226 case GB_FC32_code : ASSIGN (GxB_, _FC32, GxB_FC32_t) ;
227 case GB_FC64_code : ASSIGN (GxB_, _FC64, GxB_FC64_t) ;
228 case GB_UDT_code :
229 {
230 OK (GrB_Matrix_assign_UDT
231 (C, Mask, accum, Ax, I, ni, J, nj, desc)) ;
232 }
233 break ;
234
235 default:
236 FREE_ALL ;
237 mexErrMsgTxt ("unknown type: mtx assign") ;
238 }
239 }
240 }
241 else if (GB_VECTOR_OK (C) && GB_VECTOR_OK (A) &&
242 (Mask == NULL || GB_VECTOR_OK (Mask)) && !at)
243 {
244 // test GrB_Vector_assign
245 OK (GrB_Vector_assign_((GrB_Vector) C, (GrB_Vector) Mask, accum,
246 (GrB_Vector) A, I, ni, desc)) ;
247 }
248 else
249 {
250 // standard submatrix assignment
251 OK (GrB_Matrix_assign_(C, Mask, accum, A, I, ni, J, nj, desc)) ;
252 }
253
254 ASSERT_MATRIX_OK (C, "Final C before wait", GB0) ;
255 OK (GrB_Matrix_wait_(&C)) ;
256 return (info) ;
257 }
258
259 //------------------------------------------------------------------------------
260 // many_assign: do a sequence of assignments
261 //------------------------------------------------------------------------------
262
263 // The list of assignments is in a struct array
264
many_assign(int nwork,int fA,int fI,int fJ,int faccum,int fMask,int fdesc,int fkind,const mxArray * pargin[])265 GrB_Info many_assign
266 (
267 int nwork,
268 int fA,
269 int fI,
270 int fJ,
271 int faccum,
272 int fMask,
273 int fdesc,
274 int fkind,
275 const mxArray *pargin [ ]
276 )
277 {
278 GrB_Info info = GrB_SUCCESS ;
279
280 for (int64_t k = 0 ; k < nwork ; k++)
281 {
282
283 //----------------------------------------------------------------------
284 // get the kth work to do
285 //----------------------------------------------------------------------
286
287 // each struct has fields A, I, J, and optionally Mask, accum, and desc
288
289 mxArray *p ;
290
291 // [ turn off malloc debugging
292 bool save = GB_Global_malloc_debug_get ( ) ;
293 GB_Global_malloc_debug_set (false) ;
294
295 // get Mask (deep copy)
296 Mask = NULL ;
297 if (fMask >= 0)
298 {
299 p = mxGetFieldByNumber (pargin [1], k, fMask) ;
300 Mask = GB_mx_mxArray_to_Matrix (p, "Mask", true, false) ;
301 if (Mask == NULL && !mxIsEmpty (p))
302 {
303 FREE_ALL ;
304 mexErrMsgTxt ("Mask failed") ;
305 }
306 if (have_sparsity_control)
307 {
308 GxB_Matrix_Option_set (Mask, GxB_SPARSITY_CONTROL,
309 M_sparsity_control) ;
310 }
311 }
312
313 // get A (deep copy)
314 p = mxGetFieldByNumber (pargin [1], k, fA) ;
315 A = GB_mx_mxArray_to_Matrix (p, "A", true, true) ;
316 if (A == NULL)
317 {
318 FREE_ALL ;
319 mexErrMsgTxt ("A failed") ;
320 }
321
322 // get accum, if present
323 accum = NULL ;
324 if (faccum >= 0)
325 {
326 p = mxGetFieldByNumber (pargin [1], k, faccum) ;
327 bool user_complex = (Complex != GxB_FC64)
328 && (C->type == Complex || A->type == Complex) ;
329 if (!GB_mx_mxArray_to_BinaryOp (&accum, p, "accum",
330 C->type, user_complex))
331 {
332 FREE_ALL ;
333 mexErrMsgTxt ("accum failed") ;
334 }
335 }
336
337 // get I
338 p = mxGetFieldByNumber (pargin [1], k, fI) ;
339 if (!GB_mx_mxArray_to_indices (&I, p, &ni, I_range, &ignore))
340 {
341 FREE_ALL ;
342 mexErrMsgTxt ("I failed") ;
343 }
344
345 // get J
346 p = mxGetFieldByNumber (pargin [1], k, fJ) ;
347 if (!GB_mx_mxArray_to_indices (&J, p, &nj, J_range, &ignore))
348 {
349 FREE_ALL ;
350 mexErrMsgTxt ("J failed") ;
351 }
352
353 // get desc
354 desc = NULL ;
355 if (fdesc > 0)
356 {
357 p = mxGetFieldByNumber (pargin [1], k, fdesc) ;
358 if (!GB_mx_mxArray_to_Descriptor (&desc, p, "desc"))
359 {
360 FREE_ALL ;
361 mexErrMsgTxt ("desc failed") ;
362 }
363 }
364
365 // get kind
366 kind = 0 ;
367 if (fkind > 0)
368 {
369 p = mxGetFieldByNumber (pargin [1], k, fkind) ;
370 kind = (int) mxGetScalar (p) ;
371 }
372
373 // restore malloc debugging to test the method
374 GB_Global_malloc_debug_set (save) ; // ]
375
376 //----------------------------------------------------------------------
377 // C<Mask>(I,J) = A
378 //----------------------------------------------------------------------
379
380 info = assign ( ) ;
381
382 GrB_Matrix_free_(&A) ;
383 GrB_Matrix_free_(&Mask) ;
384 GrB_Descriptor_free_(&desc) ;
385
386 if (info != GrB_SUCCESS)
387 {
388 return (info) ;
389 }
390 }
391
392 ASSERT_MATRIX_OK (C, "Final C before wait", GB0) ;
393 OK (GrB_Matrix_wait_(&C)) ;
394 return (info) ;
395 }
396
397 //------------------------------------------------------------------------------
398 // GB_mex_assign mexFunction
399 //------------------------------------------------------------------------------
400
mexFunction(int nargout,mxArray * pargout[],int nargin,const mxArray * pargin[])401 void mexFunction
402 (
403 int nargout,
404 mxArray *pargout [ ],
405 int nargin,
406 const mxArray *pargin [ ]
407 )
408 {
409
410 C = NULL ;
411 Mask = NULL ;
412 A = NULL ;
413 desc = NULL ;
414 accum = NULL ;
415 I = NULL ; ni = 0 ;
416 J = NULL ; nj = 0 ;
417 malloc_debug = false ;
418 info = GrB_SUCCESS ;
419 kind = 0 ;
420 C_sparsity_control = GxB_AUTO_SPARSITY ;
421 M_sparsity_control = GxB_AUTO_SPARSITY ;
422 have_sparsity_control = false ;
423
424 //--------------------------------------------------------------------------
425 // check inputs
426 //--------------------------------------------------------------------------
427
428 malloc_debug = GB_mx_get_global (true) ;
429 A = NULL ;
430 C = NULL ;
431 Mask = NULL ;
432 desc = NULL ;
433
434 // check inputs
435 if (nargout > 1 || !
436 (nargin == 2 || nargin == 3 || nargin == 6 || nargin == 7 ||
437 nargin == 8))
438 {
439 mexErrMsgTxt ("Usage: " USAGE) ;
440 }
441
442 // get sparsity control if present
443 if (nargin == 3)
444 {
445 int n = mxGetNumberOfElements (pargin [2]) ;
446 if (n != 2) mexErrMsgTxt ("invalid sparsity control") ;
447 have_sparsity_control = true ;
448 double *p = mxGetDoubles (pargin [2]) ;
449 C_sparsity_control = (int) p [0] ;
450 M_sparsity_control = (int) p [1] ;
451 }
452
453 //--------------------------------------------------------------------------
454 // get C (make a deep copy)
455 //--------------------------------------------------------------------------
456
457 GET_DEEP_COPY ;
458 if (C == NULL)
459 {
460 FREE_ALL ;
461 mexErrMsgTxt ("C failed") ;
462 }
463
464 if (nargin == 2 || nargin == 3)
465 {
466
467 //----------------------------------------------------------------------
468 // get a list of work to do: a struct array of length nwork
469 //----------------------------------------------------------------------
470
471 // each entry is a struct with fields:
472 // Mask, accum, A, I, J, desc
473
474 if (!mxIsStruct (pargin [1]))
475 {
476 FREE_ALL ;
477 mexErrMsgTxt ("2nd argument must be a struct") ;
478 }
479
480 int nwork = mxGetNumberOfElements (pargin [1]) ;
481 int nf = mxGetNumberOfFields (pargin [1]) ;
482 for (int f = 0 ; f < nf ; f++)
483 {
484 mxArray *p ;
485 for (int k = 0 ; k < nwork ; k++)
486 {
487 p = mxGetFieldByNumber (pargin [1], k, f) ;
488 }
489 }
490
491 int fA = mxGetFieldNumber (pargin [1], "A") ;
492 int fI = mxGetFieldNumber (pargin [1], "I") ;
493 int fJ = mxGetFieldNumber (pargin [1], "J") ;
494 int faccum = mxGetFieldNumber (pargin [1], "accum") ;
495 int fMask = mxGetFieldNumber (pargin [1], "Mask") ;
496 int fdesc = mxGetFieldNumber (pargin [1], "desc") ;
497 int fkind = mxGetFieldNumber (pargin [1], "kind") ;
498
499 if (fA < 0 || fI < 0 || fJ < 0) mexErrMsgTxt ("A,I,J required") ;
500
501 METHOD (many_assign (nwork, fA, fI, fJ, faccum, fMask, fdesc, fkind,
502 pargin)) ;
503
504 }
505 else
506 {
507
508 //----------------------------------------------------------------------
509 // C<Mask>(I,J) = A, with a single assignment
510 //----------------------------------------------------------------------
511
512 // get Mask (deep copy)
513 Mask = GB_mx_mxArray_to_Matrix (pargin [1], "Mask", true, false) ;
514 if (Mask == NULL && !mxIsEmpty (pargin [1]))
515 {
516 FREE_ALL ;
517 mexErrMsgTxt ("Mask failed") ;
518 }
519
520 // get A (deep copy)
521 A = GB_mx_mxArray_to_Matrix (pargin [3], "A", true, true) ;
522 if (A == NULL)
523 {
524 FREE_ALL ;
525 mexErrMsgTxt ("A failed") ;
526 }
527
528 // get accum, if present
529 bool user_complex = (Complex != GxB_FC64)
530 && (C->type == Complex || A->type == Complex) ;
531 accum = NULL ;
532 if (!GB_mx_mxArray_to_BinaryOp (&accum, pargin [2], "accum",
533 C->type, user_complex))
534 {
535 FREE_ALL ;
536 mexErrMsgTxt ("accum failed") ;
537 }
538
539 // get I
540 if (!GB_mx_mxArray_to_indices (&I, pargin [4], &ni, I_range, &ignore))
541 {
542 FREE_ALL ;
543 mexErrMsgTxt ("I failed") ;
544 }
545
546 // get J
547 if (!GB_mx_mxArray_to_indices (&J, pargin [5], &nj, J_range, &ignore))
548 {
549 FREE_ALL ;
550 mexErrMsgTxt ("J failed") ;
551 }
552
553 // get desc
554 if (!GB_mx_mxArray_to_Descriptor (&desc, PARGIN (6), "desc"))
555 {
556 FREE_ALL ;
557 mexErrMsgTxt ("desc failed") ;
558 }
559
560 // get kind (0: matrix/vector, 1: col_assign, 2: row_assign)
561 kind = 0 ;
562 if (nargin > 7)
563 {
564 kind = (int) mxGetScalar (pargin [7]) ;
565 }
566
567 // C<Mask>(I,J) = A
568 METHOD (assign ( )) ;
569 }
570
571 //--------------------------------------------------------------------------
572 // return C to MATLAB as a struct
573 //--------------------------------------------------------------------------
574
575 pargout [0] = GB_mx_Matrix_to_mxArray (&C, "C assign result", true) ;
576 FREE_ALL ;
577 }
578
579