1 //------------------------------------------------------------------------------
2 // GB_mx_mxArray_to_Matrix
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 // Convert a MATLAB sparse or full matrix, or a struct to a GraphBLAS sparse
11 // matrix. The mxArray is either a struct containing two terms: a sparse or
12 // full matrix or vector, and type (a string, "logical", "double", etc), or it
13 // is just a plain sparse or full matrix. If A.class is present, it is used to
14 // typecast the MATLAB matrix into the corresponding type in GraphBLAS.
15
16 // That is:
17 // A = sparse (...) ; % a sparse double or logical GraphBLAS matrix
18
19 // A.matrix = A ; A.class = 'int8' ; Represents a MATLAB sparse or full matrix
20 // that represents a GraphBLAS int8 matrix. On input, the MATLAB sparse or
21 // full matrix is typecasted.
22
23 // The MATLAB matrix or struct is not modified. If deep_copy is true, the
24 // GraphBLAS matrix is always a deep copy and can be modified by GraphBLAS.
25 // Otherwise, its pattern (A->p, A->h, and A->i) may be a shallow copy, and
26 // A->x is a shallow copy if the MATLAB matrix is 'logical' or 'double'.
27
28 // If the MATLAB matrix is double complex, it becomes a GraphBLAS
29 // Complex or GxB_FC64 matrix.
30
31 // A->x is always a deep copy for other types, since it must be typecasted from
32 // MATLAB to GraphBLAS.
33
34 // Like GB_mx_Matrix_to_mxArray, this could be done using only user-callable
35 // GraphBLAS functions, but the method used here is faster.
36
37 // A.sparsity sets the GxB_SPARSITY_CONTROL option: 0 to 15 (see GB_conform.c),
38 // which is any sum of these 4 flags:
39 //
40 // // GxB_SPARSITY_CONTROL can be any sum or bitwise OR of these 4 values:
41 // #define GxB_HYPERSPARSE 1 // hypersparse form
42 // #define GxB_SPARSE 2 // sparse form
43 // #define GxB_BITMAP 4 // a bitmap
44 // #define GxB_FULL 8 // full (all entries must be present)
45
46 #include "GB_mex.h"
47
48 #define FREE_ALL \
49 { \
50 GrB_Matrix_free_(&A) ; \
51 }
52
GB_mx_mxArray_to_Matrix(const mxArray * A_matlab,const char * name,bool deep_copy,const bool empty)53 GrB_Matrix GB_mx_mxArray_to_Matrix // returns GraphBLAS version of A
54 (
55 const mxArray *A_matlab, // MATLAB version of A
56 const char *name, // name of the argument
57 bool deep_copy, // if true, return a deep copy
58 const bool empty // if false, 0-by-0 matrices are returned as NULL.
59 // if true, a 0-by-0 matrix is returned.
60 )
61 {
62
63 //--------------------------------------------------------------------------
64 // check for empty matrix
65 //--------------------------------------------------------------------------
66
67 GB_CONTEXT ("mxArray_to_Matrix") ;
68
69 GrB_Matrix A = NULL ;
70
71 if (A_matlab == NULL)
72 {
73 // input is not present; this is not an error if A is an
74 // optional input
75 return (NULL) ;
76 }
77
78 if ((mxGetM (A_matlab) == 0) && (mxGetN (A_matlab) == 0))
79 {
80 // input is "[ ]", zero-by-zero.
81 if (empty)
82 {
83 // treat as a sparse 0-by-0 matrix, not NULL
84 GrB_Matrix_new (&A, GrB_FP64, 0, 0) ;
85 ASSERT_MATRIX_OK (A, "got A = [ ] from MATLAB", GB0) ;
86 return (A) ;
87 }
88 else
89 {
90 // Treat as NULL in GraphBLAS. Useful for mask matrices
91 return (NULL) ;
92 }
93 }
94
95 //--------------------------------------------------------------------------
96 // get the matrix
97 //--------------------------------------------------------------------------
98
99 const mxArray *Amatrix = NULL ;
100 GrB_Type atype_in, atype_out ;
101 GB_Type_code atype_in_code, atype_out_code ;
102
103 if (mxIsStruct (A_matlab))
104 {
105 // look for A.matrix
106 int fieldnumber = mxGetFieldNumber (A_matlab, "matrix") ;
107 if (fieldnumber >= 0)
108 {
109 Amatrix = mxGetFieldByNumber (A_matlab, 0, fieldnumber) ;
110 }
111 else
112 {
113 // A.matrix not present, try A.vector
114 fieldnumber = mxGetFieldNumber (A_matlab, "vector") ;
115 if (fieldnumber < 0)
116 {
117 FREE_ALL ;
118 mexWarnMsgIdAndTxt ("GB:warn", "invalid matrix/vector struct") ;
119 return (NULL) ;
120 }
121 Amatrix = mxGetFieldByNumber (A_matlab, 0, fieldnumber) ;
122 if (mxGetN (Amatrix) != 1)
123 {
124 FREE_ALL ;
125 mexWarnMsgIdAndTxt ("GB:warn", "vector must be n-by-1") ;
126 return (NULL) ;
127 }
128 }
129
130 // get the type
131 ASSERT (Amatrix != NULL) ;
132
133 atype_in = GB_mx_Type (Amatrix) ;
134 atype_out = atype_in ;
135 fieldnumber = mxGetFieldNumber (A_matlab, "class") ;
136 if (fieldnumber >= 0)
137 {
138 mxArray *s = mxGetFieldByNumber (A_matlab, 0, fieldnumber) ;
139 atype_out = GB_mx_string_to_Type (s, atype_in) ;
140 }
141 }
142 else
143 {
144 // just a matrix
145 Amatrix = A_matlab ;
146 atype_in = GB_mx_Type (Amatrix) ;
147 atype_out = atype_in ;
148 }
149
150 bool A_is_sparse = mxIsSparse (Amatrix) ;
151
152 //--------------------------------------------------------------------------
153 // get the matrix type
154 //--------------------------------------------------------------------------
155
156 atype_in_code = atype_in->code ;
157 atype_out_code = atype_out->code ;
158
159 //--------------------------------------------------------------------------
160 // get the size and content of the MATLAB matrix
161 //--------------------------------------------------------------------------
162
163 int64_t nrows = mxGetM (Amatrix) ;
164 int64_t ncols = mxGetN (Amatrix) ;
165 int64_t *Mp, *Mi, anz, anzmax ;
166
167 if (A_is_sparse)
168 {
169 Mp = (int64_t *) mxGetJc (Amatrix) ;
170 Mi = (int64_t *) mxGetIr (Amatrix) ;
171 anz = Mp [ncols] ;
172 anzmax = mxGetNzmax (Amatrix) ;
173 }
174 else
175 {
176 Mp = NULL ;
177 Mi = NULL ;
178 anz = nrows * ncols ;
179 anzmax = anz ;
180 }
181
182 GB_void *Mx = mxGetData (Amatrix) ;
183
184 //--------------------------------------------------------------------------
185 // look for A.values
186 //--------------------------------------------------------------------------
187
188 if (mxIsStruct (A_matlab))
189 {
190 // this is used for int64 and uint64 only
191 int fieldnumber = mxGetFieldNumber (A_matlab, "values") ;
192 if (fieldnumber >= 0)
193 {
194 mxArray *values = mxGetFieldByNumber (A_matlab, 0, fieldnumber) ;
195 if (mxIsComplex (values))
196 {
197 mexErrMsgTxt ("A.values must be real") ;
198 }
199 if (mxGetNumberOfElements (values) >= anz)
200 {
201 Mx = mxGetData (values) ;
202 atype_in = GB_mx_Type (values) ;
203 atype_in_code = atype_in->code ;
204 anzmax = mxGetNumberOfElements (values) ;
205 }
206 }
207 }
208
209 ASSERT_TYPE_OK (atype_in, "A type in", GB0) ;
210 ASSERT_TYPE_OK (atype_out, "A type out", GB0) ;
211
212 if (atype_in == NULL || atype_out == NULL)
213 {
214 FREE_ALL ;
215 mexWarnMsgIdAndTxt ("GB:warn", "types must be numeric") ;
216 return (NULL) ;
217 }
218
219 GrB_Info info ;
220
221 // MATLAB matrices are sparse or full CSC, not hypersparse or bitmap
222 bool is_csc = true ;
223 int sparsity = (A_is_sparse) ? GxB_SPARSE : GxB_FULL ;
224
225 //--------------------------------------------------------------------------
226 // get the pattern of A
227 //--------------------------------------------------------------------------
228
229 if (deep_copy)
230 {
231
232 // create the GraphBLAS matrix
233 info = GB_new (&A, false, // sparse or full, new mx header
234 atype_out, (GrB_Index) nrows, (GrB_Index) ncols,
235 GB_Ap_calloc, is_csc, sparsity, GxB_HYPER_DEFAULT, 0, Context) ;
236 if (info != GrB_SUCCESS)
237 {
238 FREE_ALL ;
239 mexWarnMsgIdAndTxt ("GB:warn", "new deep matrix failed") ;
240 return (NULL) ;
241 }
242
243 // A is a deep copy and can be modified by GraphBLAS
244 info = GB_bix_alloc (A, anz, false, false, sparsity != GxB_FULL, true,
245 Context) ;
246 if (info != GrB_SUCCESS)
247 {
248 FREE_ALL ;
249 mexWarnMsgIdAndTxt ("GB:warn", "out of memory") ;
250 return (NULL) ;
251 }
252
253 if (sparsity != GxB_FULL)
254 {
255 memcpy (A->p, Mp, (ncols+1) * sizeof (int64_t)) ;
256 memcpy (A->i, Mi, anz * sizeof (int64_t)) ;
257 }
258 A->magic = GB_MAGIC ;
259
260 }
261 else
262 {
263
264 // the GraphBLAS pattern (A->p and A->i) are pointers into the
265 // MATLAB matrix and must not be modified.
266
267 // [ create the GraphBLAS matrix, do not allocate A->p
268 info = GB_new (&A, false, // sparse or full, new mx header
269 atype_out, (GrB_Index) nrows, (GrB_Index) ncols,
270 GB_Ap_null, is_csc, sparsity, GxB_HYPER_DEFAULT, 0, Context) ;
271 if (info != GrB_SUCCESS)
272 {
273 FREE_ALL ;
274 mexWarnMsgIdAndTxt ("GB:warn", "new shallow matrix failed") ;
275 return (NULL) ;
276 }
277
278 A->p_size = 0 ;
279 A->i_size = 0 ;
280
281 if (sparsity != GxB_FULL)
282 {
283 A->p = Mp ;
284 A->i = Mi ;
285 A->p_shallow = true ;
286 A->i_shallow = true ;
287 }
288 else
289 {
290 A->p = NULL ;
291 A->i = NULL ;
292 A->p_shallow = false ;
293 A->i_shallow = false ;
294 }
295
296 A->h_shallow = false ; // A->h is NULL
297 A->magic = GB_MAGIC ; // A->p now initialized ]
298 }
299
300 //--------------------------------------------------------------------------
301 // copy the numerical values from MATLAB to the GraphBLAS matrix
302 //--------------------------------------------------------------------------
303
304 if (sparsity == GxB_FULL)
305 {
306 A->x_shallow = (!deep_copy && (atype_out_code == atype_in_code)) ;
307 }
308 else
309 {
310 A->x_shallow = (!deep_copy &&
311 ((atype_out_code == GB_BOOL_code ||
312 atype_out_code == GB_FP64_code ||
313 atype_out_code == GB_FC64_code)
314 && (atype_out_code == atype_in_code))) ;
315 }
316
317 if (A->x_shallow)
318 {
319 // the MATLAB matrix and GraphBLAS matrix have the same type; (logical,
320 // double, or double complex), and a deep copy is not requested. Just
321 // make a shallow copy.
322 A->nzmax = anzmax ;
323 A->x = Mx ;
324 A->x_size = 0 ; // A->x is shallow
325 }
326 else
327 {
328 if (!deep_copy)
329 {
330 // allocate new space for the GraphBLAS values
331 A->nzmax = GB_IMAX (anz, 1) ;
332 A->x = (GB_void *) GB_malloc_memory (A->nzmax * atype_out->size,
333 sizeof (GB_void), &(A->x_size)) ;
334 if (A->x == NULL)
335 {
336 FREE_ALL ;
337 mexWarnMsgIdAndTxt ("GB:warn", "out of memory") ;
338 return (NULL) ;
339 }
340 }
341
342 GB_cast_array (
343 A->x,
344 (atype_out_code == GB_UDT_code) ? GB_FC64_code : atype_out_code,
345 Mx,
346 (atype_in_code == GB_UDT_code) ? GB_FC64_code : atype_in_code,
347 NULL,
348 (atype_in_code == GB_UDT_code) ? sizeof(GxB_FC64_t) :atype_in->size,
349 anz, 1) ;
350 }
351
352 //--------------------------------------------------------------------------
353 // look for CSR/CSC and hyper/non-hyper format
354 //--------------------------------------------------------------------------
355
356 bool A_is_hyper = false ;
357 bool has_hyper_switch = false ;
358 bool has_sparsity_control = false ;
359 int sparsity_control = GxB_AUTO_SPARSITY ;
360 double hyper_switch = GxB_HYPER_DEFAULT ;
361
362 if (mxIsStruct (A_matlab))
363 {
364 // look for A.is_csc
365 int fieldnumber = mxGetFieldNumber (A_matlab, "is_csc") ;
366 if (fieldnumber >= 0)
367 {
368 is_csc = mxGetScalar (mxGetFieldByNumber (A_matlab,
369 0, fieldnumber)) ;
370 }
371
372 // look for A.is_hyper (ignored if hyper_switch present
373 // or if A is full)
374 fieldnumber = mxGetFieldNumber (A_matlab, "is_hyper") ;
375 if (fieldnumber >= 0)
376 {
377 A_is_hyper = mxGetScalar (mxGetFieldByNumber (A_matlab,
378 0, fieldnumber)) ;
379 }
380
381 // look for A.hyper_switch (ignored if A is full)
382 fieldnumber = mxGetFieldNumber (A_matlab, "hyper_switch") ;
383 if (fieldnumber >= 0)
384 {
385 has_hyper_switch = true ;
386 hyper_switch = mxGetScalar (mxGetFieldByNumber (A_matlab,
387 0, fieldnumber)) ;
388 }
389
390 // look for A.sparsity
391 fieldnumber = mxGetFieldNumber (A_matlab, "sparsity") ;
392 if (fieldnumber >= 0)
393 {
394 has_sparsity_control = true ;
395 sparsity_control = mxGetScalar (mxGetFieldByNumber (A_matlab,
396 0, fieldnumber)) ;
397 }
398 }
399
400 //--------------------------------------------------------------------------
401 // compute the # of non-empty vectors in A only when needed
402 //--------------------------------------------------------------------------
403
404 if (sparsity != GxB_FULL)
405 {
406 A->nvec_nonempty = -1 ;
407 }
408
409 ASSERT_MATRIX_OK (A, "got natural A from MATLAB", GB0) ;
410 ASSERT (A->h == NULL) ;
411
412 //--------------------------------------------------------------------------
413 // convert to CSR if requested
414 //--------------------------------------------------------------------------
415
416 int64_t nrows_old = GB_NROWS (A) ;
417 int64_t ncols_old = GB_NCOLS (A) ;
418
419 if (!is_csc)
420 {
421 // this might convert A to hypersparse
422 GxB_Matrix_Option_set_(A, GxB_FORMAT, GxB_BY_ROW) ;
423 // so convert it back; hypersparsity is defined below
424 if (sparsity != GxB_FULL)
425 {
426 GB_convert_hyper_to_sparse (A, Context) ;
427 }
428 ASSERT (!A->is_csc) ;
429 }
430
431 ASSERT_MATRIX_OK (A, "conformed from MATLAB", GB0) ;
432 ASSERT (A->h == NULL) ;
433 ASSERT (A->is_csc == is_csc) ;
434
435 //--------------------------------------------------------------------------
436 // convert to hypersparse or set hypersparse ratio, if requested
437 //--------------------------------------------------------------------------
438
439 if (sparsity == GxB_FULL)
440 {
441 // leave as-is
442 ;
443 }
444 else if (has_hyper_switch)
445 {
446 // this sets the hyper_switch and then conforms the matrix to its
447 // desired hypersparsity. It may stay non-hypersparse.
448 GxB_Matrix_Option_set_(A, GxB_HYPER_SWITCH, hyper_switch) ;
449 }
450 else if (A_is_hyper)
451 {
452 // this forces the matrix to be always hypersparse
453 ASSERT_MATRIX_OK (A, "to always hyper", GB0) ;
454 GxB_Matrix_Option_set_(A, GxB_SPARSITY_CONTROL, GxB_HYPERSPARSE) ;
455 ASSERT_MATRIX_OK (A, "always hyper", GB0) ;
456 }
457
458 //--------------------------------------------------------------------------
459 // set the sparsity control and conform the matrix
460 //--------------------------------------------------------------------------
461
462 if (has_sparsity_control)
463 {
464 ASSERT_MATRIX_OK (A, "setting sparsity", GB0) ;
465 GxB_Matrix_Option_set_(A, GxB_SPARSITY_CONTROL, sparsity_control) ;
466 ASSERT_MATRIX_OK (A, "set sparsity", GB0) ;
467 }
468
469 ASSERT (A->is_csc == is_csc) ;
470 ASSERT (nrows_old == GB_NROWS (A)) ;
471 ASSERT (ncols_old == GB_NCOLS (A)) ;
472
473 //--------------------------------------------------------------------------
474 // return the GraphBLAS matrix
475 //--------------------------------------------------------------------------
476
477 info = GrB_Matrix_wait (&A) ;
478 if (info != GrB_SUCCESS)
479 {
480 FREE_ALL ;
481 mexWarnMsgIdAndTxt ("GB:warn", "matrix wait failed") ;
482 return (NULL) ;
483 }
484
485 ASSERT_MATRIX_OK (A, "got A from MATLAB", GB0) ;
486 return (A) ;
487 }
488
489