1 //------------------------------------------------------------------------------
2 // GB_mex.h: definitions for the MATLAB interface to GraphBLAS
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 // to turn on memory usage debug printing, uncomment this line:
11 // #define GB_PRINT_MALLOC 1
12 
13 #ifndef GB_MEXH
14 #define GB_MEXH
15 
16 #include "GB_mxm.h"
17 #include "GB_Pending.h"
18 #include "GB_add.h"
19 #include "GB_subref.h"
20 #include "GB_transpose.h"
21 #include "GB_sort.h"
22 #include "GB_apply.h"
23 #include "GB_mex_generic.h"
24 
25 #include "graphblas_demos.h"
26 
27 // demos.h use mxMalloc, etc, and so do the MATLAB Test/* mexFunctions,
28 // but the tests here need to distinguish between mxMalloc and malloc.
29 #undef malloc
30 #undef calloc
31 #undef realloc
32 #undef free
33 
34 #undef OK
35 #include "usercomplex.h"
36 #include "mex.h"
37 #include "matrix.h"
38 
39 #define PARGIN(k) ((nargin > (k)) ? pargin [k] : NULL)
40 
41 #define GET_SCALAR(arg,type,n,n_default)    \
42     n = n_default ;                    \
43     if (nargin > arg) n = (type) mxGetScalar (pargin [arg]) ;
44 
45 // MATCH(s,t) compares two strings and returns true if equal
46 #define MATCH(s,t) (strcmp(s,t) == 0)
47 
48 // timer functions, and result statistics
49 extern double grbtime, tic [2] ;
50 void GB_mx_put_time (void) ;
51 void GB_mx_clear_time (void) ;          // clear the time and start the tic
52 #define GB_MEX_TIC { GB_mx_clear_time ( ) ; }
53 #define GB_MEX_TOC { grbtime = simple_toc (tic) ; }
54 
55 void GB_mx_abort (void) ;               // assertion failure
56 
57 bool GB_mx_mxArray_to_BinaryOp          // true if successful, false otherwise
58 (
59     GrB_BinaryOp *op_handle,            // the binary op
60     const mxArray *op_matlab,           // MATLAB version of op
61     const char *name,                   // name of the argument
62     const GrB_Type default_optype,      // default operator type
63     const bool user_complex             // if true, use user-defined Complex
64 ) ;
65 
66 bool GB_mx_mxArray_to_UnaryOp           // true if successful
67 (
68     GrB_UnaryOp *op_handle,             // the unary op
69     const mxArray *op_matlab,           // MATLAB version of op
70     const char *name,                   // name of the argument
71     const GrB_Type default_optype,      // default operator type
72     const bool user_complex             // if true, use user-defined Complex
73 ) ;
74 
75 bool GB_mx_mxArray_to_SelectOp          // true if successful
76 (
77     GxB_SelectOp *handle,               // returns GraphBLAS version of op
78     const mxArray *op_matlab,           // MATLAB version of op
79     const char *name                    // name of the argument
80 ) ;
81 
82 bool GB_mx_string_to_BinaryOp       // true if successful, false otherwise
83 (
84     GrB_BinaryOp *op_handle,        // the binary op
85     const GrB_Type default_optype,  // default operator type
86     const mxArray *opname_mx,       // MATLAB string with operator name
87     const mxArray *optype_mx,       // MATLAB string with operator type
88     const bool user_complex         // if true, use user-defined Complex op
89 ) ;
90 
91 bool GB_mx_string_to_UnaryOp            // true if successful, false otherwise
92 (
93     GrB_UnaryOp *op_handle,             // the unary op
94     const GrB_Type default_optype,      // default operator type
95     const mxArray *opname_mx,           // MATLAB string with operator name
96     const mxArray *optype_mx,           // MATLAB string with operator type
97     const bool user_complex             // true if X is complex
98 ) ;
99 
100 mxArray *GB_mx_Vector_to_mxArray    // returns the MATLAB mxArray
101 (
102     GrB_Vector *handle,             // handle of GraphBLAS matrix to convert
103     const char *name,               // name for error reporting
104     const bool create_struct        // if true, then return a struct
105 ) ;
106 
107 mxArray *GB_mx_Matrix_to_mxArray    // returns the MATLAB mxArray
108 (
109     GrB_Matrix *handle,             // handle of GraphBLAS matrix to convert
110     const char *name,
111     const bool create_struct        // if true, then return a struct
112 ) ;
113 
114 mxArray *GB_mx_object_to_mxArray    // returns the MATLAB mxArray
115 (
116     GrB_Matrix *handle,             // handle of GraphBLAS matrix to convert
117     const char *name,
118     const bool create_struct        // if true, then return a struct
119 ) ;
120 
121 GrB_Matrix GB_mx_mxArray_to_Matrix     // returns GraphBLAS version of A
122 (
123     const mxArray *A_matlab,            // MATLAB version of A
124     const char *name,                   // name of the argument
125     bool deep_copy,                     // if true, return a deep copy
126     const bool empty    // if false, 0-by-0 matrices are returned as NULL.
127                         // if true, a 0-by-0 matrix is returned.
128 ) ;
129 
130 GrB_Vector GB_mx_mxArray_to_Vector     // returns GraphBLAS version of V
131 (
132     const mxArray *V_matlab,            // MATLAB version of V
133     const char *name,                   // name of the argument
134     const bool deep_copy,               // if true, return a deep copy
135     const bool empty    // if false, 0-by-0 matrices are returned as NULL.
136                         // if true, a 0-by-0 matrix is returned.
137 ) ;
138 
139 GrB_Type GB_mx_Type                    // returns a GraphBLAS type
140 (
141     const mxArray *X                   // MATLAB matrix to query
142 ) ;
143 
144 void GB_mx_mxArray_to_array    // convert mxArray to array
145 (
146     const mxArray *Xmatlab,     // input MATLAB array
147     // output:
148     GB_void **X,                // pointer to numerical values (shallow)
149     int64_t *nrows,             // number of rows of X
150     int64_t *ncols,             // number of columns of X
151     GrB_Type *xtype             // GraphBLAS type of X, NULL if error
152 ) ;
153 
154 int GB_mx_mxArray_to_string // returns length of string, or -1 if S not a string
155 (
156     char *string,           // size maxlen
157     const size_t maxlen,    // length of string
158     const mxArray *S        // MATLAB mxArray containing a string
159 ) ;
160 
161 bool GB_mx_mxArray_to_Descriptor    // true if successful, false otherwise
162 (
163     GrB_Descriptor *handle,         // descriptor to return
164     const mxArray *D_matlab,        // MATLAB struct
165     const char *name                // name of the descriptor
166 ) ;
167 
168 bool GB_mx_mxArray_to_Semiring         // true if successful
169 (
170     GrB_Semiring *handle,               // the semiring
171     const mxArray *semiring_matlab,     // MATLAB version of semiring
172     const char *name,                   // name of the argument
173     const GrB_Type default_optype,      // default operator type
174     const bool user_complex         // if true, use user-defined Complex op
175 ) ;
176 
177 GrB_Semiring GB_mx_semiring         // semiring, or NULL if error
178 (
179     const GrB_Monoid add_monoid,    // input monoid
180     const GrB_BinaryOp mult         // input multiply operator
181 ) ;
182 
183 GrB_Monoid GB_mx_BinaryOp_to_Monoid // monoid, or NULL if error
184 (
185     const GrB_BinaryOp add          // monoid operator
186 ) ;
187 
188 bool GB_mx_mxArray_to_indices       // true if successful, false otherwise
189 (
190     GrB_Index **handle,             // index array returned
191     const mxArray *I_matlab,        // MATLAB mxArray to get
192     GrB_Index *ni,                  // length of I, or special
193     GrB_Index Icolon [3],           // for all but GB_LIST
194     bool *I_is_list                 // true if I is an explicit list
195 ) ;
196 
197 bool GB_mx_Monoid               // true if successful, false otherwise
198 (
199     GrB_Monoid *handle,         // monoid to construct
200     const GrB_BinaryOp add,     // monoid operator
201     const bool malloc_debug     // true if malloc debug should be done
202 ) ;
203 
204 bool GB_mx_get_global       // true if doing malloc_debug
205 (
206     bool cover              // true if doing statement coverage
207 ) ;
208 
209 void GB_mx_put_global
210 (
211     bool cover
212 ) ;
213 
214 bool GB_mx_same     // true if arrays X and Y are the same
215 (
216     char *X,
217     char *Y,
218     int64_t len     // length of X and Y
219 ) ;
220 
221 bool GB_mx_xsame    // true if arrays X and Y are the same (ignoring zombies)
222 (
223     char *X,
224     char *Y,
225     int8_t *Xb,     // bitmap of X and Y (NULL if no bitmap)
226     int64_t len,    // length of X and Y
227     size_t s,       // size of each entry of X and Y
228     int64_t *I      // row indices (for zombies), same length as X and Y
229 ) ;
230 
231 bool GB_mx_xsame32  // true if arrays X and Y are the same (ignoring zombies)
232 (
233     float *X,
234     float *Y,
235     int8_t *Xb,     // bitmap of X and Y (NULL if no bitmap)
236     int64_t len,    // length of X and Y
237     int64_t *I,     // row indices (for zombies), same length as X and Y
238     float eps       // error tolerance allowed (eps > 0)
239 ) ;
240 
241 bool GB_mx_xsame64  // true if arrays X and Y are the same (ignoring zombies)
242 (
243     double *X,
244     double *Y,
245     int8_t *Xb,     // bitmap of X and Y (NULL if no bitmap)
246     int64_t len,    // length of X and Y
247     int64_t *I,     // row indices (for zombies), same length as X and Y
248     double eps      // error tolerance allowed (eps > 0)
249 ) ;
250 
251 bool GB_mx_isequal  // true if A and B are exactly the same
252 (
253     GrB_Matrix A,
254     GrB_Matrix B,
255     double eps      // if A and B are both FP32 or FP64, and if eps > 0,
256                     // then the values are considered equal if their relative
257                     // difference is less than or equal to eps.
258 ) ;
259 
260 GrB_Matrix GB_mx_alias      // output matrix (NULL if no match found)
261 (
262     char *arg_name,         // name of the output matrix
263     const mxArray *arg,     // string to select the alias
264     char *arg1_name,        // name of first possible alias
265     GrB_Matrix arg1,        // first possible alias
266     char *arg2_name,        // name of 2nd possible alias
267     GrB_Matrix arg2         // second possible alias
268 ) ;
269 
270 mxArray *GB_mx_create_full      // return new MATLAB full matrix
271 (
272     const GrB_Index nrows,
273     const GrB_Index ncols,
274     GrB_Type type               // type of the matrix to create
275 ) ;
276 
277 mxArray *GB_mx_Type_to_mxstring        // returns a MATLAB string
278 (
279     const GrB_Type type
280 ) ;
281 
282 GrB_Type GB_mx_string_to_Type       // GrB_Type from the string
283 (
284     const mxArray *type_mx,         // string with type name
285     const GrB_Type default_type     // default type if string empty
286 ) ;
287 
288 //------------------------------------------------------------------------------
289 
290 // remove a block that had been allocated from within GraphBLAS and then
291 // exported.
292 #define REMOVE(p)                                       \
293 {                                                       \
294     if ((p) != NULL)                                    \
295     {                                                   \
296         GB_Global_nmalloc_decrement ( ) ;               \
297         if (GB_Global_memtable_find (p))                \
298         {                                               \
299             GB_Global_memtable_remove (p) ;             \
300         }                                               \
301     }                                                   \
302 }
303 
304 #define GB_AS_IF_FREE(p)                \
305 {                                       \
306     GB_Global_nmalloc_decrement ( ) ;   \
307     GB_Global_memtable_remove (p) ;     \
308     (p) = NULL ;                        \
309 }
310 
311 #ifdef GB_PRINT_MALLOC
312 
313 #define METHOD_START(OP) \
314     printf ("\n================================================================================\n") ; \
315     printf ("method: [%s] start: "GBd" "GBd"\n", #OP, \
316         GB_Global_nmalloc_get ( ), GB_Global_free_pool_nblocks_total ( )) ; \
317     printf ("================================================================================\n") ;
318 
319 #define METHOD_TRY \
320     printf ("\n--------------------------------------------------------------------- try %d\n", tries) ;
321 
322 #define METHOD_FINAL(OP) \
323     printf ("\n================================================================================\n") ; \
324     printf ("method: [%s] # tries before success: %d\n", #OP, tries) ;  \
325     printf ("================================================================================\n") ;
326 
327 #else
328 
329 #define METHOD_START(OP) ;
330 #define METHOD_TRY ;
331 #define METHOD_FINAL(OP) ;
332 
333 #endif
334 
335 // test a GraphBLAS operation with malloc debuging
336 #define METHOD(GRAPHBLAS_OPERATION)                                         \
337     METHOD_START (GRAPHBLAS_OPERATION) ;                                    \
338     if (!malloc_debug)                                                      \
339     {                                                                       \
340         /* no malloc debugging; just call the method */                     \
341         GB_MEX_TIC ;                                                        \
342         GrB_Info info = GRAPHBLAS_OPERATION ;                               \
343         GB_MEX_TOC ;                                                        \
344         if (info == GrB_PANIC) mexErrMsgTxt ("panic!") ;                    \
345         if (! (info == GrB_SUCCESS || info == GrB_NO_VALUE))                \
346         {                                                                   \
347             FREE_ALL ;                                                      \
348             printf ("info: %d\n", info) ;                                   \
349             mexErrMsgTxt ("method failed") ;                                \
350         }                                                                   \
351     }                                                                       \
352     else                                                                    \
353     {                                                                       \
354         /* brutal malloc debug */                                           \
355         int nmalloc_start = (int) GB_Global_nmalloc_get ( ) ;               \
356         int nfree_pool_start = (int) GB_Global_free_pool_nblocks_total ( ) ;\
357         for (int tries = 0 ; ; tries++)                                     \
358         {                                                                   \
359             /* give GraphBLAS the ability to do a # of mallocs, */          \
360             /* callocs, and reallocs of larger size, equal to tries */      \
361             GB_Global_malloc_debug_count_set (tries) ;                      \
362             METHOD_TRY ;                                                    \
363             /* call the method with malloc debug enabled */                 \
364             GB_Global_malloc_debug_set (true) ;                             \
365             GB_MEX_TIC ;                                                    \
366             GrB_Info info = GRAPHBLAS_OPERATION ;                           \
367             /* do not finish the work */                                    \
368             GB_MEX_TOC ;                                                    \
369             GB_Global_malloc_debug_set (false) ;                            \
370             if (tries > 1000000) mexErrMsgTxt ("infinite loop!") ;          \
371             if (info == GrB_SUCCESS || info == GrB_NO_VALUE)                \
372             {                                                               \
373                 /* finally gave GraphBLAS enough malloc's to do the work */ \
374                 METHOD_FINAL (GRAPHBLAS_OPERATION) ;                        \
375                 break ;                                                     \
376             }                                                               \
377             else if (info == GrB_OUT_OF_MEMORY)                             \
378             {                                                               \
379                 /* out of memory; check for leaks */                        \
380                 /* output matrix may have changed; recopy for next test */  \
381                 /* but turn off malloc debugging to get the copy */         \
382                 FREE_DEEP_COPY ;                                            \
383                 GET_DEEP_COPY ;                                             \
384                 int nmalloc_end = (int) GB_Global_nmalloc_get ( ) ;         \
385                 int nfree_pool_end =                                        \
386                     (int) GB_Global_free_pool_nblocks_total ( ) ;           \
387                 int nleak = nmalloc_end - nmalloc_start ;                   \
388                 int nfree_delta = nfree_pool_end - nfree_pool_start ;       \
389                 if (nleak > nfree_delta)                                    \
390                 {                                                           \
391                     /* memory leak */                                       \
392                     printf ("Leak! tries %d : nleak %d\n"                   \
393                         "nmalloc_end:        %d\n"                          \
394                         "nmalloc_start:      %d\n"                          \
395                         "nfree_pool start:   %d\n"                          \
396                         "nfree_pool end:     %d\n"                          \
397                         "method [%s]\n",                                    \
398                         tries, nleak, nmalloc_end, nmalloc_start,           \
399                         nfree_pool_start, nfree_pool_end,                   \
400                         GB_STR (GRAPHBLAS_OPERATION)) ;                     \
401                     mexWarnMsgIdAndTxt ("GB:leak", "memory leak") ;         \
402                     FREE_ALL ;                                              \
403                     mexErrMsgTxt ("Leak!") ;                                \
404                 }                                                           \
405             }                                                               \
406             else                                                            \
407             {                                                               \
408                 /* another error has occurred */                            \
409                 FREE_ALL ;                                                  \
410                 printf ("info: %d\n", info) ; \
411                 mexErrMsgTxt ("unexpected error in mex brutal malloc debug") ; \
412             }                                                               \
413         }                                                                   \
414     }
415 
416 //------------------------------------------------------------------------------
417 // statement coverage
418 //------------------------------------------------------------------------------
419 
420 // GB_cover_get copies GraphBLAS_grbcov from the MATLAB global workspace into
421 // the internal GB_cov array.  The MATLAB array is created if it doesn't exist.
422 // Thus, to clear the counts simply clear GraphBLAS_grbcov from the MATLAB
423 // global workpace.
424 void GB_cover_get (void) ;
425 
426 // GB_cover_put copies the internal GB_cov array back into the MATLAB
427 // GraphBLAS_grbcov array, for analysis and for subsequent statement counting.
428 // This way, multiple tests in MATLAB can be accumulated into a single array
429 // of counters.
430 void GB_cover_put (void) ;
431 
432 #endif
433 
434