1 //------------------------------------------------------------------------------
2 // gb_get_shallow: create a shallow copy of a MATLAB sparse matrix
3 //------------------------------------------------------------------------------
4 
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: GPL-3.0-or-later
7 
8 //------------------------------------------------------------------------------
9 
10 // A = gb_get_shallow (X) constructs a shallow GrB_Matrix from a MATLAB
11 // mxArray, which can either be a MATLAB sparse matrix (double, complex, or
12 // logical) or a MATLAB struct that contains a GraphBLAS matrix.
13 
14 // X must not be NULL, but it can be an empty matrix, as X = [ ] or even X = ''
15 // (the empty string).  In this case, A is returned as NULL.  This is not an
16 // error here, since the caller might be getting an optional input matrix, such
17 // as Cin or the Mask.
18 
19 // For v4, is_uniform is false, and the s component has length 9.
20 // For v5, is_uniform may be true, and the s component has length 10.
21 
22 #include "gb_matlab.h"
23 
24 #define IF(error,message) \
25     CHECK_ERROR (error, "invalid GraphBLAS struct (" message ")" ) ;
26 
gb_get_shallow(const mxArray * X)27 GrB_Matrix gb_get_shallow   // return a shallow copy of MATLAB sparse matrix
28 (
29     const mxArray *X
30 )
31 {
32 
33     //--------------------------------------------------------------------------
34     // check inputs
35     //--------------------------------------------------------------------------
36 
37     CHECK_ERROR (X == NULL, "matrix missing") ;
38 
39     //--------------------------------------------------------------------------
40     // construct the shallow GrB_Matrix
41     //--------------------------------------------------------------------------
42 
43     GrB_Matrix A = NULL ;
44 
45     if (gb_mxarray_is_empty (X))
46     {
47 
48         //----------------------------------------------------------------------
49         // matrix is empty
50         //----------------------------------------------------------------------
51 
52         // X is a 0-by-0 MATLAB matrix.  Create a new 0-by-0 matrix of the same
53         // type as X, with the default format.
54         OK (GrB_Matrix_new (&A, gb_mxarray_type (X), 0, 0)) ;
55 
56     }
57     else if (mxIsStruct (X))
58     {
59 
60         //----------------------------------------------------------------------
61         // construct a shallow GrB_Matrix copy from a MATLAB struct
62         //----------------------------------------------------------------------
63 
64         bool GraphBLASv4 = false ;
65         bool GraphBLASv3 = false ;
66 
67         // get the type
68         mxArray *mx_type = mxGetField (X, 0, "GraphBLASv5") ;
69         if (mx_type == NULL)
70         {
71             // check if it is a GraphBLASv4 struct
72             mx_type = mxGetField (X, 0, "GraphBLASv4") ;
73             GraphBLASv4 = true ;
74         }
75         if (mx_type == NULL)
76         {
77             // check if it is a GraphBLASv3 struct
78             mx_type = mxGetField (X, 0, "GraphBLAS") ;
79             GraphBLASv3 = true ;
80         }
81         CHECK_ERROR (mx_type == NULL, "not a GraphBLAS struct") ;
82 
83         GrB_Type type = gb_mxstring_to_type (mx_type) ;
84         size_t type_size ;
85         OK (GxB_Type_size (&type_size, type)) ;
86 
87         // get the scalar info
88         mxArray *opaque = mxGetField (X, 0, "s") ;
89         IF (opaque == NULL, ".s missing") ;
90         IF (mxGetM (opaque) != 1, ".s wrong size") ;
91         size_t s_size = mxGetN (opaque) ;
92         if (GraphBLASv3)
93         {
94             IF (s_size != 8, ".s wrong size") ;
95         }
96         else if (GraphBLASv4)
97         {
98             IF (s_size != 9, ".s wrong size") ;
99         }
100         else
101         {
102             IF (s_size != 10, ".s wrong size") ;
103         }
104         int64_t *s = mxGetInt64s (opaque) ;
105         int64_t plen          = s [0] ;
106         int64_t vlen          = s [1] ;
107         int64_t vdim          = s [2] ;
108         int64_t nvec          = s [3] ;
109         int64_t nvec_nonempty = s [4] ;
110         bool    by_col        = (bool) (s [6]) ;
111         int64_t nzmax         = s [7] ;
112 
113         int sparsity_status, sparsity_control ;
114         int64_t nvals ;
115         bool is_uniform ;
116         if (GraphBLASv3)
117         {
118             // GraphBLASv3 struct: sparse or hypersparse only
119             sparsity_control = GxB_AUTO_SPARSITY ;
120             nvals            = 0 ;
121             is_uniform       = false ;
122         }
123         else
124         {
125             // GraphBLASv4 or v5 struct: sparse, hypersparse, bitmap, or full
126             sparsity_control = (int) (s [5]) ;
127             nvals            = s [8] ;
128             if (GraphBLASv4)
129             {
130                 // GraphBLASv4: is_uniform is always false
131                 is_uniform = false ;
132             }
133             else
134             {
135                 // GraphBLASv5: is_uniform is present as s [9]
136                 is_uniform = (bool) s [9] ;
137             }
138         }
139 
140         int nfields = mxGetNumberOfFields (X) ;
141         switch (nfields)
142         {
143             case 3 :
144                 // A is full, with 3 fields: GraphBLAS*, s, x
145                 sparsity_status = GxB_FULL ;
146                 break ;
147 
148             case 5 :
149                 // A is sparse, with 5 fields: GraphBLAS*, s, x, p, i
150                 sparsity_status = GxB_SPARSE ;
151                 break ;
152 
153             case 6 :
154                 // A is hypersparse, with 6 fields: GraphBLAS*, s, x, p, i, h
155                 sparsity_status = GxB_HYPERSPARSE ;
156                 break ;
157 
158             case 4 :
159                 // A is bitmap, with 4 fields: GraphBLAS*, s, x, b
160                 sparsity_status = GxB_BITMAP ;
161                 break ;
162 
163             default : ERROR ("invalid GraphBLAS struct") ;
164         }
165 
166         // each component
167         int64_t *Ap = NULL, *Ai = NULL, *Ah = NULL ;
168         int8_t *Ab = NULL ;
169         void *Ax = NULL ;
170 
171         // size of each component
172         size_t Ap_size = 0 ;
173         size_t Ah_size = 0 ;
174         size_t Ab_size = 0 ;
175         size_t Ai_size = 0 ;
176         size_t Ax_size = 0 ;
177 
178         if (sparsity_status == GxB_HYPERSPARSE || sparsity_status == GxB_SPARSE)
179         {
180             // A is hypersparse or sparse
181 
182             // get Ap
183             mxArray *Ap_mx = mxGetField (X, 0, "p") ;
184             IF (Ap_mx == NULL, ".p missing") ;
185             IF (mxGetM (Ap_mx) != 1, ".p wrong size") ;
186             Ap = mxGetInt64s (Ap_mx) ;
187             IF (Ap == NULL, ".p wrong type") ;
188             Ap_size = mxGetN (Ap_mx) * sizeof (int64_t) ;
189 
190             // get Ai
191             mxArray *Ai_mx = mxGetField (X, 0, "i") ;
192             IF (Ai_mx == NULL, ".i missing") ;
193             IF (mxGetM (Ai_mx) != 1, ".i wrong size") ;
194             Ai_size = mxGetN (Ai_mx) * sizeof (int64_t) ;
195             Ai = (Ai_size == 0) ? NULL : mxGetInt64s (Ai_mx) ;
196             IF (Ai == NULL && Ai_size > 0, ".i wrong type") ;
197         }
198 
199         // get the values
200         mxArray *Ax_mx = mxGetField (X, 0, "x") ;
201         IF (Ax_mx == NULL, ".x missing") ;
202         IF (mxGetM (Ax_mx) != 1, ".x wrong size") ;
203         Ax_size = mxGetN (Ax_mx) ;
204         Ax = (Ax_size == 0) ? NULL : ((void *) mxGetUint8s (Ax_mx)) ;
205         IF (Ax == NULL && Ax_size > 0, ".x wrong type") ;
206 
207         if (sparsity_status == GxB_HYPERSPARSE)
208         {
209             // A is hypersparse
210             // get the hyperlist
211             mxArray *Ah_mx = mxGetField (X, 0, "h") ;
212             IF (Ah_mx == NULL, ".h missing") ;
213             IF (mxGetM (Ah_mx) != 1, ".h wrong size") ;
214             Ah_size = mxGetN (Ah_mx) * sizeof (int64_t) ;
215             Ah = (Ah_size == 0) ? NULL : ((int64_t *) mxGetInt64s (Ah_mx)) ;
216             IF (Ah == NULL && Ah_size > 0, ".h wrong type") ;
217         }
218 
219         if (sparsity_status == GxB_BITMAP)
220         {
221             // A is bitmap
222             // get the bitmap
223             mxArray *Ab_mx = mxGetField (X, 0, "b") ;
224             IF (Ab_mx == NULL, ".b missing") ;
225             IF (mxGetM (Ab_mx) != 1, ".b wrong size") ;
226             Ab_size = mxGetN (Ab_mx) ;
227             Ab = (Ab_size == 0) ? NULL : ((int8_t *) mxGetInt8s (Ab_mx)) ;
228             IF (Ab == NULL && Ab_size > 0, ".b wrong type") ;
229         }
230 
231         //----------------------------------------------------------------------
232         // import the matrix
233         //----------------------------------------------------------------------
234 
235         int64_t nrows = (by_col) ? vlen : vdim ;
236         int64_t ncols = (by_col) ? vdim : vlen ;
237 
238         switch (sparsity_status)
239         {
240             case GxB_FULL :
241                 if (by_col)
242                 {
243                     OK (GxB_Matrix_import_FullC (&A, type, nrows, ncols,
244                         &Ax, Ax_size, is_uniform, NULL)) ;
245                 }
246                 else
247                 {
248                     OK (GxB_Matrix_import_FullR (&A, type, nrows, ncols,
249                         &Ax, Ax_size, is_uniform, NULL)) ;
250                 }
251                 break ;
252 
253             case GxB_SPARSE :
254                 if (by_col)
255                 {
256                     OK (GxB_Matrix_import_CSC (&A, type, nrows, ncols,
257                         &Ap, &Ai, &Ax, Ap_size, Ai_size, Ax_size, is_uniform,
258                         false, NULL)) ;
259                 }
260                 else
261                 {
262                     OK (GxB_Matrix_import_CSR (&A, type, nrows, ncols,
263                         &Ap, &Ai, &Ax, Ap_size, Ai_size, Ax_size, is_uniform,
264                         false, NULL)) ;
265                 }
266                 break ;
267 
268             case GxB_HYPERSPARSE :
269                 if (by_col)
270                 {
271                     OK (GxB_Matrix_import_HyperCSC (&A, type, nrows, ncols,
272                         &Ap, &Ah, &Ai, &Ax,
273                         Ap_size, Ah_size, Ai_size, Ax_size, is_uniform,
274                         nvec, false, NULL)) ;
275                 }
276                 else
277                 {
278                     OK (GxB_Matrix_import_HyperCSR (&A, type, nrows, ncols,
279                         &Ap, &Ah, &Ai, &Ax,
280                         Ap_size, Ah_size, Ai_size, Ax_size, is_uniform,
281                         nvec, false, NULL)) ;
282                 }
283                 break ;
284 
285             case GxB_BITMAP :
286                 if (by_col)
287                 {
288                     OK (GxB_Matrix_import_BitmapC (&A, type, nrows, ncols,
289                         &Ab, &Ax, Ab_size, Ax_size, is_uniform, nvals, NULL)) ;
290                 }
291                 else
292                 {
293                     OK (GxB_Matrix_import_BitmapR (&A, type, nrows, ncols,
294                         &Ab, &Ax, Ab_size, Ax_size, is_uniform, nvals, NULL)) ;
295                 }
296                 break ;
297 
298             default: ;
299         }
300 
301     }
302     else
303     {
304 
305         //----------------------------------------------------------------------
306         // construct a shallow GrB_Matrix copy of a MATLAB matrix
307         //----------------------------------------------------------------------
308 
309         // get the type and dimensions
310         bool X_is_sparse = mxIsSparse (X) ;
311 
312         GrB_Type type = gb_mxarray_type (X) ;
313         GrB_Index nrows = (GrB_Index) mxGetM (X) ;
314         GrB_Index ncols = (GrB_Index) mxGetN (X) ;
315 
316         // get Xp, Xi, nzmax, or create them
317         GrB_Index *Xp, *Xi, nzmax ;
318         if (X_is_sparse)
319         {
320             // get the nzmax, Xp, and Xi from the MATLAB sparse matrix X
321             nzmax = (GrB_Index) mxGetNzmax (X) ;
322             Xp = (GrB_Index *) mxGetJc (X) ;
323             Xi = (GrB_Index *) mxGetIr (X) ;
324         }
325         else
326         {
327             // X is a MATLAB full matrix; so is the GrB_Matrix
328             nzmax = nrows * ncols ;
329             Xp = NULL ;
330             Xi = NULL ;
331         }
332 
333         // get the numeric data
334         void *Xx = NULL ;
335         size_t type_size = 0 ;
336         if (type == GrB_FP64)
337         {
338             // MATLAB sparse or full double matrix
339             Xx = mxGetDoubles (X) ;
340             type_size = sizeof (double) ;
341         }
342         else if (type == GxB_FC64)
343         {
344             // MATLAB sparse or full double complex matrix
345             Xx = mxGetComplexDoubles (X) ;
346             type_size = 2 * sizeof (double) ;
347         }
348         else if (type == GrB_BOOL)
349         {
350             // MATLAB sparse or full logical matrix
351             Xx = mxGetData (X) ;
352             type_size = sizeof (bool) ;
353         }
354         else if (X_is_sparse)
355         {
356             // MATLAB does not support any other kinds of sparse matrices
357             ERROR ("unsupported type") ;
358         }
359         else if (type == GrB_INT8)
360         {
361             // full int8 matrix
362             Xx = mxGetInt8s (X) ;
363             type_size = sizeof (int8_t) ;
364         }
365         else if (type == GrB_INT16)
366         {
367             // full int16 matrix
368             Xx = mxGetInt16s (X) ;
369             type_size = sizeof (int16_t) ;
370         }
371         else if (type == GrB_INT32)
372         {
373             // full int32 matrix
374             Xx = mxGetInt32s (X) ;
375             type_size = sizeof (int32_t) ;
376         }
377         else if (type == GrB_INT64)
378         {
379             // full int64 matrix
380             Xx = mxGetInt64s (X) ;
381             type_size = sizeof (int64_t) ;
382         }
383         else if (type == GrB_UINT8)
384         {
385             // full uint8 matrix
386             Xx = mxGetUint8s (X) ;
387             type_size = sizeof (uint8_t) ;
388         }
389         else if (type == GrB_UINT16)
390         {
391             // full uint16 matrix
392             Xx = mxGetUint16s (X) ;
393             type_size = sizeof (uint16_t) ;
394         }
395         else if (type == GrB_UINT32)
396         {
397             // full uint32 matrix
398             Xx = mxGetUint32s (X) ;
399             type_size = sizeof (uint32_t) ;
400         }
401         else if (type == GrB_UINT64)
402         {
403             // full uint64 matrix
404             Xx = mxGetUint64s (X) ;
405             type_size = sizeof (uint64_t) ;
406         }
407         else if (type == GrB_FP32)
408         {
409             // full single matrix
410             Xx = mxGetSingles (X) ;
411             type_size = sizeof (float) ;
412         }
413         else if (type == GxB_FC32)
414         {
415             // full single complex matrix
416             Xx = mxGetComplexSingles (X) ;
417             type_size = 2 * sizeof (float) ;
418         }
419         else
420         {
421             ERROR ("unsupported type") ;
422         }
423 
424         if (X_is_sparse)
425         {
426             // import the matrix in CSC format.  This sets Xp, Xi, and Xx to
427             // NULL, but it does not change the MATLAB matrix they came from.
428             OK (GxB_Matrix_import_CSC (&A, type, nrows, ncols, &Xp, &Xi, &Xx,
429                 (ncols+1) * sizeof (int64_t),
430                 nzmax * sizeof (int64_t),
431                 nzmax * type_size, false, false, NULL)) ;
432         }
433         else
434         {
435             // import a full matrix
436             OK (GxB_Matrix_import_FullC (&A, type, nrows, ncols, &Xx,
437                 nzmax * type_size, false, NULL)) ;
438         }
439     }
440 
441     //-------------------------------------------------------------------------
442     // tell GraphBLAS the matrix is shallow
443     //-------------------------------------------------------------------------
444 
445     // TODO need a shallow import
446     A->p_shallow = (A->p != NULL) ;
447     A->h_shallow = (A->h != NULL) ;
448     A->b_shallow = (A->b != NULL) ;
449     A->i_shallow = (A->i != NULL) ;
450     A->x_shallow = (A->x != NULL) ;
451     #ifdef GB_DEBUG
452     if (A->p != NULL) GB_Global_memtable_remove (A->p) ;
453     if (A->h != NULL) GB_Global_memtable_remove (A->h) ;
454     if (A->b != NULL) GB_Global_memtable_remove (A->b) ;
455     if (A->i != NULL) GB_Global_memtable_remove (A->i) ;
456     if (A->x != NULL) GB_Global_memtable_remove (A->x) ;
457     #endif
458 
459     //--------------------------------------------------------------------------
460     // return the result
461     //--------------------------------------------------------------------------
462 
463     return (A) ;
464 }
465 
466