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