1 //------------------------------------------------------------------------------
2 // GB_mx_object_to_mxArray
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 GraphBLAS sparse or full matrix to a MATLAB struct C containing
11 // C.matrix and a string C.class. The GraphBLAS matrix is destroyed.
12
13 // This could be done using only user-callable GraphBLAS functions, by
14 // extracting the tuples and converting them into a MATLAB sparse matrix. But
15 // that would be much slower and take more memory. Instead, most of the work
16 // can be done by pointers, and directly accessing the internal contents of C.
17 // If C has type GB_BOOL_code or GB_FP64_code, then C can be converted to a
18 // MATLAB matrix in constant time with essentially no extra memory allocated.
19 // This is faster, but it means that this MATLAB interface will only work with
20 // this specific implementation of GraphBLAS.
21
22 // Note that the GraphBLAS matrix may contain explicit zeros. These entries
23 // should not appear in a MATLAB matrix but MATLAB handles them without
24 // difficulty. They are returned to MATLAB in C.matrix. If any work is done
25 // in MATLAB on the matrix, these entries will get dropped. If they are to be
26 // preserved, do C.pattern = GB_spones_mex (C.matrix) in MATLAB before
27 // modifying C.matrix.
28
29 #include "GB_mex.h"
30
31 static const char *MatrixFields [ ] = { "matrix", "class", "values" } ;
32
GB_mx_object_to_mxArray(GrB_Matrix * handle,const char * name,const bool create_struct)33 mxArray *GB_mx_object_to_mxArray // returns the MATLAB mxArray
34 (
35 GrB_Matrix *handle, // handle of GraphBLAS matrix to convert
36 const char *name,
37 const bool create_struct // if true, then return a struct
38 )
39 {
40 GB_CONTEXT ("GB_mx_object_to_mxArray") ;
41
42 // get the inputs
43 mxArray *A, *Astruct, *X = NULL ;
44 GrB_Matrix C = *handle ;
45 GrB_Type ctype = C->type ;
46
47 // may have pending tuples
48 ASSERT_MATRIX_OK (C, name, GB0) ;
49
50 // C must not be shallow
51 ASSERT (!C->p_shallow) ;
52 ASSERT (!C->h_shallow) ;
53 ASSERT (!C->b_shallow) ;
54 ASSERT (!C->i_shallow) ;
55 ASSERT (!C->x_shallow) ;
56
57 // make sure there are no pending computations
58 if (GB_IS_FULL (C) || GB_IS_BITMAP (C))
59 {
60 ASSERT (!GB_JUMBLED (C)) ;
61 ASSERT (!GB_ZOMBIES (C)) ;
62 ASSERT (!GB_PENDING (C)) ;
63 }
64 else
65 {
66 // this may convert C to full
67 GrB_Matrix_wait (&C) ;
68 C = (*handle) ;
69 }
70
71 // must be done after GrB_Matrix_wait:
72 int64_t cnz = GB_NNZ (C) ;
73
74 ASSERT_MATRIX_OK (C, "TO MATLAB after assembling pending tuples", GB0) ;
75
76 // ensure C is sparse or full, not hypersparse or bitmap
77 GxB_Matrix_Option_set_(C, GxB_SPARSITY_CONTROL, GxB_FULL + GxB_SPARSE) ;
78 ASSERT_MATRIX_OK (C, "TO MATLAB, sparse or full", GB0) ;
79 ASSERT (!GB_IS_HYPERSPARSE (C)) ;
80 ASSERT (!GB_IS_BITMAP (C)) ;
81
82 // get the current sparsity
83 int sparsity ;
84 GxB_Matrix_Option_get_(C, GxB_SPARSITY_STATUS, &sparsity) ;
85 ASSERT (sparsity == GxB_FULL || sparsity == GxB_SPARSE) ;
86
87 // make sure it's CSC
88 if (!C->is_csc)
89 {
90 GxB_Matrix_Option_set_(C, GxB_FORMAT, GxB_BY_COL) ;
91 }
92
93 // setting to CSC may have transposed the matrix
94 ASSERT (GB_JUMBLED_OK (C)) ;
95 GrB_Matrix_wait (&C) ;
96 ASSERT (!GB_JUMBLED (C)) ;
97 cnz = GB_NNZ (C) ;
98
99 ASSERT_MATRIX_OK (C, "TO MATLAB, non-hyper CSC", GB0) ;
100 ASSERT (!GB_JUMBLED (C)) ;
101 ASSERT (!GB_IS_HYPERSPARSE (C)) ;
102 ASSERT (!GB_IS_BITMAP (C)) ;
103 ASSERT (GB_IS_SPARSE (C) || GB_IS_FULL (C)) ;
104 ASSERT (C->is_csc) ;
105
106 // MATLAB doesn't want NULL pointers in its empty matrices
107 if (C->x == NULL)
108 {
109 ASSERT (C->nzmax == 0 && cnz == 0) ;
110 C->x = (GB_void *) GB_malloc_memory (2 * sizeof (double),
111 sizeof (GB_void), &(C->x_size)) ;
112 memset (C->x, 0, 2 * sizeof (double)) ;
113 C->x_shallow = false ;
114 }
115
116 bool C_is_full = (sparsity == GxB_FULL) ;
117 if (!C_is_full)
118 {
119 // MATLAB doesn't want NULL pointers in its empty sparse matrices
120 if (C->i == NULL)
121 {
122 ASSERT (C->nzmax == 0 && cnz == 0) ;
123 C->i = (int64_t *) GB_malloc_memory (1, sizeof (int64_t),
124 &(C->i_size)) ;
125 C->i [0] = 0 ;
126 C->i_shallow = false ;
127 }
128 if (C->p == NULL)
129 {
130 ASSERT (C->nzmax == 0 && cnz == 0) ;
131 C->p = (int64_t *) GB_malloc_memory (C->vdim + 1,
132 sizeof (int64_t), &(C->p_size)) ;
133 memset (C->p, 0, (C->vdim + 1) * sizeof (int64_t)) ;
134 C->p_shallow = false ;
135 }
136 }
137
138 C->nzmax = GB_IMAX (C->nzmax, 1) ;
139
140 //--------------------------------------------------------------------------
141 // create the MATLAB matrix A and link in the numerical values of C
142 //--------------------------------------------------------------------------
143
144 if (C_is_full)
145 {
146 // C is full.
147 // allocate an empty dense matrix of the right type, then set content
148
149 void *Cx = (void *) C->x ;
150
151 if (ctype == GrB_BOOL)
152 {
153 A = mxCreateLogicalMatrix (0, 0) ;
154 mxSetData (A, Cx) ;
155 }
156 else if (ctype == GrB_FP32)
157 {
158 A = mxCreateNumericMatrix (0, 0, mxSINGLE_CLASS, mxREAL) ;
159 mxSetSingles (A, Cx) ;
160 }
161 else if (ctype == GrB_FP64)
162 {
163 A = mxCreateNumericMatrix (0, 0, mxDOUBLE_CLASS, mxREAL) ;
164 mxSetDoubles (A, Cx) ;
165 }
166 else if (ctype == GrB_INT8)
167 {
168 A = mxCreateNumericMatrix (0, 0, mxINT8_CLASS, mxREAL) ;
169 mxSetInt8s (A, Cx) ;
170 }
171 else if (ctype == GrB_INT16)
172 {
173 A = mxCreateNumericMatrix (0, 0, mxINT16_CLASS, mxREAL) ;
174 mxSetInt16s (A, Cx) ;
175 }
176 else if (ctype == GrB_INT32)
177 {
178 A = mxCreateNumericMatrix (0, 0, mxINT32_CLASS, mxREAL) ;
179 mxSetInt32s (A, Cx) ;
180 }
181 else if (ctype == GrB_INT64)
182 {
183 A = mxCreateNumericMatrix (0, 0, mxINT64_CLASS, mxREAL) ;
184 mxSetInt64s (A, Cx) ;
185 }
186 else if (ctype == GrB_UINT8)
187 {
188 A = mxCreateNumericMatrix (0, 0, mxUINT8_CLASS, mxREAL) ;
189 mxSetUint8s (A, Cx) ;
190 }
191 else if (ctype == GrB_UINT16)
192 {
193 A = mxCreateNumericMatrix (0, 0, mxUINT16_CLASS, mxREAL) ;
194 mxSetUint16s (A, Cx) ;
195 }
196 else if (ctype == GrB_UINT32)
197 {
198 A = mxCreateNumericMatrix (0, 0, mxUINT32_CLASS, mxREAL) ;
199 mxSetUint32s (A, Cx) ;
200 }
201 else if (ctype == GrB_UINT64)
202 {
203 A = mxCreateNumericMatrix (0, 0, mxUINT64_CLASS, mxREAL) ;
204 mxSetUint64s (A, Cx) ;
205 }
206 else if (ctype == GxB_FC32)
207 {
208 A = mxCreateNumericMatrix (0, 0, mxSINGLE_CLASS, mxCOMPLEX) ;
209 mxSetComplexSingles (A, Cx) ;
210 }
211 else if (ctype == Complex || ctype == GxB_FC64)
212 {
213 A = mxCreateNumericMatrix (0, 0, mxDOUBLE_CLASS, mxCOMPLEX) ;
214 mxSetComplexDoubles (A, Cx) ;
215 }
216 else
217 {
218 mexErrMsgTxt ("... unsupported type") ;
219 }
220
221 mexMakeMemoryPersistent (C->x) ;
222 C->x_shallow = false ;
223 GB_AS_IF_FREE (C->x) ; // unlink C->x from C; now in MATLAB C
224
225 }
226 else if (C->type == GrB_BOOL)
227 {
228 // C is boolean, which is the same as a MATLAB logical sparse matrix
229 A = mxCreateSparseLogicalMatrix (0, 0, 0) ;
230 mexMakeMemoryPersistent (C->x) ;
231 mxSetData (A, (bool *) C->x) ;
232 C->x_shallow = false ;
233
234 // C->x is treated as if it was freed
235 GB_AS_IF_FREE (C->x) ; // unlink C->x from C; now in MATLAB C
236
237 }
238 else if (C->type == GrB_FP64)
239 {
240 // C is double, which is the same as a MATLAB double sparse matrix
241 A = mxCreateSparse (0, 0, 0, mxREAL) ;
242 mexMakeMemoryPersistent (C->x) ;
243 mxSetData (A, C->x) ;
244 C->x_shallow = false ;
245
246 // C->x is treated as if it was freed
247 GB_AS_IF_FREE (C->x) ; // unlink C->x from C; in MATLAB C
248
249 }
250 else if (C->type == Complex || C->type == GxB_FC64)
251 {
252
253 // user-defined Complex type, or GraphBLAS GxB_FC64
254 A = mxCreateSparse (C->vlen, C->vdim, C->nzmax, mxCOMPLEX) ;
255 memcpy (mxGetComplexDoubles (A), C->x, cnz * sizeof (GxB_FC64_t)) ;
256
257 }
258 else if (C->type == GxB_FC32)
259 {
260
261 // C is single complex, typecast to sparse double complex
262 A = mxCreateSparse (C->vlen, C->vdim, C->nzmax, mxCOMPLEX) ;
263 GB_cast_array (mxGetComplexDoubles (A), GB_FC64_code,
264 C->x, C->type->code, NULL, C->type->size, cnz, 1) ;
265
266 }
267 else
268 {
269
270 // otherwise C is cast into a MATLAB double sparse matrix
271 A = mxCreateSparse (0, 0, 0, mxREAL) ;
272 size_t Sx_size ;
273 double *Sx = (double *) GB_malloc_memory (cnz+1, sizeof (double),
274 &Sx_size) ;
275 GB_cast_array (Sx, GB_FP64_code,
276 C->x, C->type->code, NULL, C->type->size, cnz, 1) ;
277 mexMakeMemoryPersistent (Sx) ;
278 mxSetPr (A, Sx) ;
279
280 // Sx was just malloc'd, and given to MATLAB. Treat it as if
281 // GraphBLAS has freed it
282 GB_AS_IF_FREE (Sx) ;
283
284 if (create_struct)
285 {
286 // If C is int64 or uint64, then typecasting can lose information,
287 // so keep an uncasted copy of C->x as well.
288 X = GB_mx_create_full (0, 0, C->type) ;
289 mxSetM (X, cnz) ;
290 mxSetN (X, 1) ;
291 mxSetData (X, C->x) ;
292 mexMakeMemoryPersistent (C->x) ;
293 C->x_shallow = false ;
294 // treat C->x as if it were freed
295 GB_AS_IF_FREE (C->x) ;
296 }
297 }
298
299 // set nrows, ncols, nzmax, and the pattern of A
300 mxSetM (A, C->vlen) ;
301 mxSetN (A, C->vdim) ;
302 mxSetNzmax (A, C->nzmax) ;
303
304 if (!C_is_full)
305 {
306 mxFree (mxGetJc (A)) ;
307 mxFree (mxGetIr (A)) ;
308 mexMakeMemoryPersistent (C->p) ;
309 mexMakeMemoryPersistent (C->i) ;
310 mxSetJc (A, (size_t *) C->p) ;
311 mxSetIr (A, (size_t *) C->i) ;
312
313 // treat C->p as if freed
314 GB_AS_IF_FREE (C->p) ;
315
316 // treat C->i as if freed
317 C->i_shallow = false ;
318 GB_AS_IF_FREE (C->i) ;
319 }
320
321 // free C, but leave any shallow components untouched
322 // since these have been transplanted into the MATLAB matrix.
323 GrB_Matrix_free_(handle) ;
324
325 if (create_struct)
326 {
327 // create the type
328 mxArray *atype = GB_mx_Type_to_mxstring (ctype) ;
329 // create the output struct
330 Astruct = mxCreateStructMatrix (1, 1,
331 (X == NULL) ? 2 : 3, MatrixFields) ;
332 mxSetFieldByNumber (Astruct, 0, 0, A) ;
333 mxSetFieldByNumber (Astruct, 0, 1, atype) ;
334 if (X != NULL)
335 {
336 mxSetFieldByNumber (Astruct, 0, 2, X) ;
337 }
338 return (Astruct) ;
339 }
340 else
341 {
342 return (A) ;
343 }
344 }
345
346