1 //------------------------------------------------------------------------------
2 // GB_mex_setElement: MATLAB interface for A(i,j) = x
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 // x = A (i,j), where i and j are zero-based.  If i and j arrays, then
11 // x (k) = A (i (k), j (k)) is done for all k.
12 
13 // I and J and zero-based
14 
15 #include "GB_mex.h"
16 
17 #define USAGE "A = GB_mex_setElement (A, I, J, X, debug_wait)"
18 
19 bool debug_wait = false ;
20 
21 #define FREE_ALL                        \
22 {                                       \
23     GrB_Matrix_free_(&A) ;              \
24     GB_mx_put_global (true) ;           \
25 }
26 
27 #if defined ( __GNUC__ )
28 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
29 #endif
30 
31 // set all elements of a matrix and return if an error is encountered
32 #define setEl(prefix,name,type)                                             \
33 GrB_Info set_ ## name                                                       \
34 (GrB_Matrix A, type *X, GrB_Index *I, GrB_Index *J, GrB_Index ni)           \
35 {                                                                           \
36     for (int64_t k = 0 ; k < ni ; k++)                                      \
37     {                                                                       \
38         GrB_Info info = prefix ## Matrix_setElement_ ## name                \
39             (A, AMPERSAND (X [k]), I [k], J [k]) ;                          \
40         if (info != GrB_SUCCESS) return (info) ;                            \
41     }                                                                       \
42     if (debug_wait)                                                         \
43     {                                                                       \
44         return (GB_Matrix_wait (A, "A", NULL)) ;                            \
45     }                                                                       \
46     return (GrB_SUCCESS) ;                                                  \
47 }
48 
49 // create all the local set_TYPE functions
50 #define AMPERSAND(x) x
51 setEl (GrB_, BOOL   , bool          ) ;
52 setEl (GrB_, INT8   , int8_t        ) ;
53 setEl (GrB_, UINT8  , uint8_t       ) ;
54 setEl (GrB_, INT16  , int16_t       ) ;
55 setEl (GrB_, UINT16 , uint16_t      ) ;
56 setEl (GrB_, INT32  , int32_t       ) ;
57 setEl (GrB_, UINT32 , uint32_t      ) ;
58 setEl (GrB_, INT64  , int64_t       ) ;
59 setEl (GrB_, UINT64 , uint64_t      ) ;
60 setEl (GrB_, FP32   , float         ) ;
61 setEl (GrB_, FP64   , double        ) ;
62 setEl (GxB_, FC32   , GxB_FC32_t    ) ;
63 setEl (GxB_, FC64   , GxB_FC64_t    ) ;
64 #undef  AMPERSAND
65 #define AMPERSAND(x) &x
66 setEl (GrB_, UDT    , GxB_FC64_t) ;
67 #undef  AMPERSAND
68 
69 
70 // set all elements of a vector and return if an error is encountered
71 #define vsetEl(prefix,name,type)                                            \
72 GrB_Info vset_ ## name                                                      \
73 (GrB_Matrix A, type *X, GrB_Index *I, GrB_Index ni)                         \
74 {                                                                           \
75     GrB_Vector w = (GrB_Vector) A ;                                         \
76     for (int64_t k = 0 ; k < ni ; k++)                                      \
77     {                                                                       \
78         GrB_Info info = prefix ## Vector_setElement_ ## name                \
79             (w, AMPERSAND (X [k]), I [k]) ;                                 \
80         if (info != GrB_SUCCESS) return (info) ;                            \
81     }                                                                       \
82     if (debug_wait)                                                         \
83     {                                                                       \
84         return (GB_Matrix_wait (A, "A", NULL)) ;                            \
85     }                                                                       \
86     return (GrB_SUCCESS) ;                                                  \
87 }
88 
89 // create all the local set_TYPE functions
90 #define AMPERSAND(x) x
91 vsetEl (GrB_, BOOL   , bool          ) ;
92 vsetEl (GrB_, INT8   , int8_t        ) ;
93 vsetEl (GrB_, UINT8  , uint8_t       ) ;
94 vsetEl (GrB_, INT16  , int16_t       ) ;
95 vsetEl (GrB_, UINT16 , uint16_t      ) ;
96 vsetEl (GrB_, INT32  , int32_t       ) ;
97 vsetEl (GrB_, UINT32 , uint32_t      ) ;
98 vsetEl (GrB_, INT64  , int64_t       ) ;
99 vsetEl (GrB_, UINT64 , uint64_t      ) ;
100 vsetEl (GrB_, FP32   , float         ) ;
101 vsetEl (GrB_, FP64   , double        ) ;
102 vsetEl (GxB_, FC32   , GxB_FC32_t    ) ;
103 vsetEl (GxB_, FC64   , GxB_FC64_t    ) ;
104 #undef  AMPERSAND
105 #define AMPERSAND(x) &x
106 vsetEl (GrB_, UDT    , GxB_FC64_t) ;
107 #undef  AMPERSAND
108 
mexFunction(int nargout,mxArray * pargout[],int nargin,const mxArray * pargin[])109 void mexFunction
110 (
111     int nargout,
112     mxArray *pargout [ ],
113     int nargin,
114     const mxArray *pargin [ ]
115 )
116 {
117 
118     bool malloc_debug = GB_mx_get_global (true) ;
119 
120     GrB_Matrix A = NULL ;
121     GB_void *Y ;
122     GrB_Type xtype ;
123     GrB_Index *I = NULL, ni = 0, I_range [3] ;
124     GrB_Index *J = NULL, nj = 0, J_range [3] ;
125     bool is_list ;
126 
127     // check inputs
128     if (nargout > 1 || nargin < 4 || nargin > 5)
129     {
130         mexErrMsgTxt ("Usage: " USAGE) ;
131     }
132 
133     // get A (deep copy)
134     #define GET_DEEP_COPY \
135     A = GB_mx_mxArray_to_Matrix (pargin [0], "A input", true, true) ;
136     #define FREE_DEEP_COPY GrB_Matrix_free_(&A) ;
137     GET_DEEP_COPY ;
138     if (A == NULL)
139     {
140         FREE_ALL ;
141         mexErrMsgTxt ("A failed") ;
142     }
143 
144     // get I
145     if (!GB_mx_mxArray_to_indices (&I, pargin [1], &ni, I_range, &is_list))
146     {
147         FREE_ALL ;
148         mexErrMsgTxt ("I failed") ;
149     }
150     if (!is_list)
151     {
152         mexErrMsgTxt ("I is invalid; must be a list") ;
153     }
154 
155     // get J
156     if (!GB_mx_mxArray_to_indices (&J, pargin [2], &nj, J_range, &is_list))
157     {
158         FREE_ALL ;
159         mexErrMsgTxt ("J failed") ;
160     }
161     if (!is_list)
162     {
163         mexErrMsgTxt ("J is invalid; must be a list") ;
164     }
165 
166     if (ni != nj)
167     {
168         FREE_ALL ;
169         mexErrMsgTxt ("I and J must be the same size") ;
170     }
171 
172     // get X
173     if (ni != mxGetNumberOfElements (pargin [3]))
174     {
175         FREE_ALL ;
176         mexErrMsgTxt ("I and X must be the same size") ;
177     }
178     if (!(mxIsNumeric (pargin [3]) || mxIsLogical (pargin [3])))
179     {
180         FREE_ALL ;
181         mexErrMsgTxt ("X must be a numeric or logical array") ;
182     }
183     if (mxIsSparse (pargin [3]))
184     {
185         FREE_ALL ;
186         mexErrMsgTxt ("X cannot be sparse") ;
187     }
188 
189     // get debug_wait (if true, to GB_Matrix_wait after setElements)
190     GET_SCALAR (4, bool, debug_wait, false) ;
191 
192     if (mxIsComplex (pargin [3]))
193     {
194         xtype = Complex ;
195         Y = mxGetComplexDoubles (pargin [3]) ;
196     }
197     else
198     {
199         Y = mxGetData (pargin [3]) ;
200         xtype = GB_mx_Type (pargin [3]) ;
201         if (xtype == NULL)
202         {
203             FREE_ALL ;
204             mexErrMsgTxt ("X must be numeric") ;
205         }
206     }
207 
208     size_t s = 2 * sizeof (double) ;
209 
210     // A (i,j) = x, for a list of elements
211 
212     // the METHOD (...) macro is not used on each call to setElement, but
213     // to all of them.  Thus, if any failure occurs, the computation is rolled
214     // back to the very beginning, and another fresh, deep, copy of A is made,
215     // and the sequence of setElements is tried again.  If a setElement fails
216     // by running out of memory, it clears to whole matrix, so recovery cannot
217     // be made.
218 
219     if (A->vdim == 1)
220     {
221         // test GrB_Vector_setElement
222         switch (xtype->code)
223         {
224             case GB_BOOL_code   : METHOD (vset_BOOL   (A, (bool       *) Y, I, ni)) ; break ;
225             case GB_INT8_code   : METHOD (vset_INT8   (A, (int8_t     *) Y, I, ni)) ; break ;
226             case GB_INT16_code  : METHOD (vset_INT16  (A, (int16_t    *) Y, I, ni)) ; break ;
227             case GB_INT32_code  : METHOD (vset_INT32  (A, (int32_t    *) Y, I, ni)) ; break ;
228             case GB_INT64_code  : METHOD (vset_INT64  (A, (int64_t    *) Y, I, ni)) ; break ;
229             case GB_UINT8_code  : METHOD (vset_UINT8  (A, (uint8_t    *) Y, I, ni)) ; break ;
230             case GB_UINT16_code : METHOD (vset_UINT16 (A, (uint16_t   *) Y, I, ni)) ; break ;
231             case GB_UINT32_code : METHOD (vset_UINT32 (A, (uint32_t   *) Y, I, ni)) ; break ;
232             case GB_UINT64_code : METHOD (vset_UINT64 (A, (uint64_t   *) Y, I, ni)) ; break ;
233             case GB_FP32_code   : METHOD (vset_FP32   (A, (float      *) Y, I, ni)) ; break ;
234             case GB_FP64_code   : METHOD (vset_FP64   (A, (double     *) Y, I, ni)) ; break ;
235             case GB_FC32_code   : METHOD (vset_FC32   (A, (GxB_FC32_t *) Y, I, ni)) ; break ;
236             case GB_FC64_code   : METHOD (vset_FC64   (A, (GxB_FC64_t *) Y, I, ni)) ; break ;
237             case GB_UDT_code    : METHOD (vset_UDT    (A, (void       *) Y, I, ni)) ; break ;
238             default:
239                 FREE_ALL ;
240                 mexErrMsgTxt ("unsupported type") ;
241         }
242     }
243     else
244     {
245         // test GrB_Matrix_setElement
246         switch (xtype->code)
247         {
248             case GB_BOOL_code   : METHOD (set_BOOL   (A, (bool       *) Y, I, J, ni)) ; break ;
249             case GB_INT8_code   : METHOD (set_INT8   (A, (int8_t     *) Y, I, J, ni)) ; break ;
250             case GB_INT16_code  : METHOD (set_INT16  (A, (int16_t    *) Y, I, J, ni)) ; break ;
251             case GB_INT32_code  : METHOD (set_INT32  (A, (int32_t    *) Y, I, J, ni)) ; break ;
252             case GB_INT64_code  : METHOD (set_INT64  (A, (int64_t    *) Y, I, J, ni)) ; break ;
253             case GB_UINT8_code  : METHOD (set_UINT8  (A, (uint8_t    *) Y, I, J, ni)) ; break ;
254             case GB_UINT16_code : METHOD (set_UINT16 (A, (uint16_t   *) Y, I, J, ni)) ; break ;
255             case GB_UINT32_code : METHOD (set_UINT32 (A, (uint32_t   *) Y, I, J, ni)) ; break ;
256             case GB_UINT64_code : METHOD (set_UINT64 (A, (uint64_t   *) Y, I, J, ni)) ; break ;
257             case GB_FP32_code   : METHOD (set_FP32   (A, (float      *) Y, I, J, ni)) ; break ;
258             case GB_FP64_code   : METHOD (set_FP64   (A, (double     *) Y, I, J, ni)) ; break ;
259             case GB_FC32_code   : METHOD (set_FC32   (A, (GxB_FC32_t *) Y, I, J, ni)) ; break ;
260             case GB_FC64_code   : METHOD (set_FC64   (A, (GxB_FC64_t *) Y, I, J, ni)) ; break ;
261             case GB_UDT_code    : METHOD (set_UDT    (A, (void       *) Y, I, J, ni)) ; break ;
262             default:
263                 FREE_ALL ;
264                 mexErrMsgTxt ("unsupported type") ;
265         }
266     }
267 
268     // only do debug checks after adding lots of tuples
269     if (ni > 1000) { ASSERT_MATRIX_OK (A, "A added pending tuples", GB0) ; }
270 
271     // return A to MATLAB as a struct and free the GraphBLAS A
272     pargout [0] = GB_mx_Matrix_to_mxArray (&A, "A output", true) ;
273 
274     FREE_ALL ;
275 }
276 
277