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