File: GB_reshape.c

package info (click to toggle)
suitesparse-graphblas 7.4.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 67,112 kB
  • sloc: ansic: 1,072,243; cpp: 8,081; sh: 512; makefile: 506; asm: 369; python: 125; awk: 10
file content (397 lines) | stat: -rw-r--r-- 14,318 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
//------------------------------------------------------------------------------
// GB_reshape:  reshape a matrix into another matrix
//------------------------------------------------------------------------------

// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2022, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//------------------------------------------------------------------------------

// If the input matrix is nrows-by-ncols, and the size of the newly-created
// matrix C is nrows_new-by-ncols_new, then nrows*ncols must equal
// nrows_new*ncols_new.

#include "GB.h"
#include "GB_reshape.h"
#include "GB_transpose.h"
#include "GB_ek_slice.h"
#include "GB_build.h"

#define GB_FREE_WORKSPACE                       \
{                                               \
    GB_WERK_POP (T_ek_slicing, int64_t) ;       \
    GB_FREE (&I_work, I_work_size) ;            \
    GB_FREE (&J_work, J_work_size) ;            \
    GB_FREE (&S_work, S_work_size) ;            \
    if (T != A && T != C)                       \
    {                                           \
        GB_Matrix_free (&T) ;                   \
    }                                           \
}

#define GB_FREE_ALL                             \
{                                               \
    GB_FREE_WORKSPACE ;                         \
    if (Chandle == NULL)                        \
    {                                           \
        GB_phybix_free (A) ;                    \
    }                                           \
    else                                        \
    {                                           \
        GB_Matrix_free (&C) ;                   \
    }                                           \
}

GrB_Info GB_reshape         // reshape a GrB_Matrix into another GrB_Matrix
(
    // output, if not in-place:
    GrB_Matrix *Chandle,    // output matrix, in place if Chandle == NULL
    // input, or input/output:
    GrB_Matrix A,           // input matrix, or input/output if in-place
    // input:
    bool by_col,            // true if reshape by column, false if by row
    int64_t nrows_new,      // number of rows of C
    int64_t ncols_new,      // number of columns of C
    GB_Context Context
)
{

    //--------------------------------------------------------------------------
    // check inputs
    //--------------------------------------------------------------------------

    GrB_Info info ;
    ASSERT_MATRIX_OK (A, "A for reshape", GB0) ;

    int64_t *I_work = NULL, *J_work = NULL ;
    GB_void *S_work = NULL, *S_input = NULL ;
    size_t I_work_size = 0, J_work_size = 0, S_work_size = 0 ;
    GB_WERK_DECLARE (T_ek_slicing, int64_t) ;
    GrB_Matrix C = NULL, T = NULL ;

    bool in_place = (Chandle == NULL) ;
    if (!in_place)
    { 
        (*Chandle) = NULL ;
    }

    GrB_Index matrix_size, s ;
    int64_t nrows_old = GB_NROWS (A) ;
    int64_t ncols_old = GB_NCOLS (A) ;
    bool ok = GB_int64_multiply (&matrix_size, nrows_old, ncols_old) ;
    if (!ok)
    { 
        // problem too large
        return (GrB_OUT_OF_MEMORY) ;
    }

    ok = GB_int64_multiply (&s, nrows_new, ncols_new) ;
    if (!ok || s != matrix_size)
    { 
        // dimensions are invalid
        return (GrB_DIMENSION_MISMATCH) ;
    }

    //--------------------------------------------------------------------------
    // finish any pending work, and transpose the input matrix if needed
    //--------------------------------------------------------------------------

    GB_MATRIX_WAIT (A) ;

    GrB_Type type = A->type ;
    bool A_is_csc = A->is_csc ;
    if (A_is_csc != by_col)
    {
        // transpose the input matrix
        if (in_place)
        { 
            // transpose A in-place
            GB_OK (GB_transpose_in_place (A, by_col, Context)) ;
            T = A ;
        }
        else
        { 
            // T = A'
            GB_OK (GB_new (&T,  // new header
                type, A->vdim, A->vlen, GB_Ap_null, by_col, GxB_AUTO_SPARSITY,
                GB_Global_hyper_switch_get ( ), 0, Context)) ;
            GB_OK (GB_transpose_cast (T, type, by_col, A, false, Context)) ;
            // now T can be reshaped in-place to construct C
            in_place = true ;
        }
    }
    else
    { 
        // use T = A as-is, and reshape it either in-place or not in-place
        T = A ;
    }

    // T is now in the format required for the reshape
    ASSERT_MATRIX_OK (T, "T for reshape", GB0) ;
    ASSERT (T->is_csc == by_col) ;

    //--------------------------------------------------------------------------
    // determine the dimensions of C
    //--------------------------------------------------------------------------

    int64_t vlen_new, vdim_new ;
    bool T_is_csc = T->is_csc ;
    if (T_is_csc)
    { 
        vlen_new = nrows_new ;
        vdim_new = ncols_new ;
    }
    else
    { 
        vlen_new = ncols_new ;
        vdim_new = nrows_new ;
    }

    //--------------------------------------------------------------------------
    // C = reshape (T), keeping the same format (by_col)
    //--------------------------------------------------------------------------

    if (GB_IS_FULL (T) || GB_IS_BITMAP (T))
    {

        //----------------------------------------------------------------------
        // T and C are both full or both bitmap
        //----------------------------------------------------------------------

        if (in_place)
        { 
            // move T into C
            C = T ;
            T = NULL ;
        }
        else
        { 
            // copy T into C
            GB_OK (GB_dup (&C, T, Context)) ;
        }
        // change the size of C
        C->vlen = vlen_new ;
        C->vdim = vdim_new ;
        C->nvec = vdim_new ;
        C->nvec_nonempty = (vlen_new == 0) ? 0 : vdim_new ;

    }
    else
    {

        //----------------------------------------------------------------------
        // sparse/hypersparse case
        //----------------------------------------------------------------------

        int64_t nvals = GB_nnz (T) ;
        int64_t *Tp = T->p ;
        int64_t *Th = T->h ;
        int64_t *Ti = T->i ;
        bool T_iso = T->iso ;
        int64_t tvlen = T->vlen ;
        bool T_jumbled = T->jumbled ;

        GB_GET_NTHREADS_MAX (nthreads_max, chunk, Context) ;
        int T_nthreads, T_ntasks ;
        GB_SLICE_MATRIX (T, 1, chunk) ;

        //----------------------------------------------------------------------
        // allocate output and workspace
        //----------------------------------------------------------------------

        if (in_place)
        { 

            //------------------------------------------------------------------
            // Remove T->i and T->x from T; these become I_work and S_work
            //------------------------------------------------------------------

            // remove T->i from T; it becomes I_work
            I_work = T->i ; I_work_size = T->i_size ;
            T->i = NULL   ; T->i_size = 0 ;
            // remove T->x from T; it becomes S_work
            S_work = T->x ; S_work_size = T->x_size ;
            T->x = NULL   ; T->x_size = 0 ;
            S_input = NULL ;

            // move T into C
            C = T ;
            T = NULL ;

        }
        else
        {

            //------------------------------------------------------------------
            // create a new matrix C for GB_builder and allocate I_work
            //------------------------------------------------------------------

            // create the output matrix (just the header; no content)
            GB_OK (GB_new (&C, // new header
                type, vlen_new, vdim_new, GB_Ap_null, T_is_csc,
                GxB_AUTO_SPARSITY, GB_Global_hyper_switch_get ( ), 0,
                Context)) ;
            // allocate new space for the future C->i
            I_work = GB_MALLOC (nvals, int64_t, &I_work_size) ;
            if (I_work == NULL)
            { 
                // out of memory
                GB_FREE_ALL ;
                return (GrB_OUT_OF_MEMORY) ;
            }
            // use T->x as S_input to GB_builder, which is not modified
            S_input = T->x ;
        }

        if (vdim_new > 1)
        {
            // J_work is not needed if vdim_new == 1
            J_work = GB_MALLOC (nvals, int64_t, &J_work_size) ;
            if (J_work == NULL)
            { 
                // out of memory
                GB_FREE_ALL ;
                return (GrB_OUT_OF_MEMORY) ;
            }
        }

        //----------------------------------------------------------------------
        // construct the new indices
        //----------------------------------------------------------------------

        int tid ;

        if (vdim_new == 1)
        { 

            //------------------------------------------------------------------
            // C is a single vector: no J_work is needed, and new index is 1D
            //------------------------------------------------------------------

            #pragma omp parallel for num_threads(T_nthreads) schedule(static)
            for (tid = 0 ; tid < T_ntasks ; tid++)
            {
                int64_t kfirst = kfirst_Tslice [tid] ;
                int64_t klast  = klast_Tslice  [tid] ;
                for (int64_t k = kfirst ; k <= klast ; k++)
                {
                    int64_t jold = GBH (Th, k) ;
                    int64_t pT_start, pT_end ;
                    GB_get_pA (&pT_start, &pT_end, tid, k,
                        kfirst, klast, pstart_Tslice, Tp, tvlen) ;
                    for (int64_t p = pT_start ; p < pT_end ; p++)
                    {
                        int64_t iold = Ti [p] ;
                        // convert (iold,jold) to a 1D index
                        int64_t index_1d = iold + jold * tvlen ;
                        // save the new 1D index
                        I_work [p] = index_1d ;
                    }
                }
            }

        }
        else
        { 

            //------------------------------------------------------------------
            // C is a matrix
            //------------------------------------------------------------------

            #pragma omp parallel for num_threads(T_nthreads) schedule(static)
            for (tid = 0 ; tid < T_ntasks ; tid++)
            {
                int64_t kfirst = kfirst_Tslice [tid] ;
                int64_t klast  = klast_Tslice  [tid] ;
                for (int64_t k = kfirst ; k <= klast ; k++)
                {
                    int64_t jold = GBH (Th, k) ;
                    int64_t pT_start, pT_end ;
                    GB_get_pA (&pT_start, &pT_end, tid, k,
                        kfirst, klast, pstart_Tslice, Tp, tvlen) ;
                    for (int64_t p = pT_start ; p < pT_end ; p++)
                    {
                        int64_t iold = Ti [p] ;
                        // convert (iold,jold) to a 1D index
                        int64_t index_1d = iold + jold * tvlen ;
                        // convert the 1D index to the 2d index: (inew,jnew)
                        int64_t inew = index_1d % vlen_new ;
                        int64_t jnew = (index_1d - inew) / vlen_new ;
                        // save the new indices
                        I_work [p] = inew ;
                        J_work [p] = jnew ;
                    }
                }
            }
        }

        //----------------------------------------------------------------------
        // free the old C->p and C->h, if constructing C in place
        //----------------------------------------------------------------------

        if (in_place)
        { 
            GB_phybix_free (C) ;
        }

        //----------------------------------------------------------------------
        // build the output matrix C
        //----------------------------------------------------------------------

        GB_OK (GB_builder (
            C,              // output matrix
            type,           // same type as T
            vlen_new,       // new vlen
            vdim_new,       // new vdim
            T_is_csc,       // same format as T
            &I_work,        // transplanted into C->i
            &I_work_size,
            &J_work,        // freed when done
            &J_work_size,
            &S_work,        // array of values; transplanted into C->x in-place
            &S_work_size,
            !T_jumbled,     // indices may be jumbled on input
            true,           // no duplicates exist
            nvals,          // number of entries in T and C 
            true,           // C is a matrix
            NULL,           // I_input is not used
            NULL,           // J_input is not used
            S_input,        // S_input is used if not in-place; NULL if in-place
            T_iso,          // true if T and C are iso-valued
            nvals,          // number of entries in T and C 
            NULL,           // no dup operator
            type,           // type of S_work and S_input
            true,           // burble is allowed
            Context
        )) ;

        ASSERT (I_work == NULL) ;   // transplanted into C->i
        ASSERT (J_work == NULL) ;   // freed by GB_builder
        ASSERT (S_work == NULL) ;   // freed by GB_builder
    }

    //--------------------------------------------------------------------------
    // transpose C if needed, to change its format to match the format of A
    //--------------------------------------------------------------------------

    ASSERT_MATRIX_OK (C, "C for reshape before transpose", GB0) ;
    ASSERT (C->is_csc == T_is_csc) ;
    if (A_is_csc != T_is_csc)
    { 
        GB_OK (GB_transpose_in_place (C, A_is_csc, Context)) ;
    }

    //--------------------------------------------------------------------------
    // free workspace, conform C, and return results
    //--------------------------------------------------------------------------

    GB_FREE_WORKSPACE ;
    GB_OK (GB_conform (C, Context)) ;
    ASSERT_MATRIX_OK (C, "C result for reshape", GB0) ;
    if (Chandle != NULL)
    { 
        (*Chandle) = C ;
    }
    return (GrB_SUCCESS) ;
}