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