1 //------------------------------------------------------------------------------
2 // GB_transpose_op: transpose, typecast, and apply an operator to a 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 // C = op (A')
11 
12 // The values of A are typecasted to op->xtype and then passed to the unary
13 // operator.  The output is assigned to C, which must be of type op->ztype; no
14 // output typecasting done with the output of the operator.
15 
16 // If the op is positional, it has been replaced with the unary op
17 // GxB_ONE_INT64, as a placeholder.  The true op (either op1 or op2) is applied
18 // later, in GB_transpose.
19 
20 // If A is sparse or hypersparse
21 //      The pattern of C is constructed.  C is sparse.
22 //      Workspaces and A_slice are non-NULL.
23 //      This method is parallel, but not highly scalable.  It uses only
24 //      nthreads = nnz(A)/(A->vlen) threads.
25 
26 // If A is full or packed:
27 //      The pattern of C is not constructed.  C is full.
28 //      Workspaces and A_slice are NULL.
29 //      This method is parallel and fully scalable.
30 
31 // If A is bitmap:
32 //      C->b is constructed.  C is bitmap.
33 //      Workspaces and A_slice are NULL.
34 //      This method is parallel and fully scalable.
35 
36 #include "GB_transpose.h"
37 #include "GB_binop.h"
38 #ifndef GBCOMPACT
39 #include "GB_unop__include.h"
40 #include "GB_binop__include.h"
41 #endif
42 
GB_transpose_op(GrB_Matrix C,const GrB_UnaryOp op1,const GrB_BinaryOp op2,const GxB_Scalar scalar,bool binop_bind1st,const GrB_Matrix A,int64_t * restrict * Workspaces,const int64_t * restrict A_slice,int nworkspaces,int nthreads)43 void GB_transpose_op    // transpose, typecast, and apply operator to a matrix
44 (
45     GrB_Matrix C,                       // output matrix
46         // no operator is applied if both op1 and op2 are NULL
47         const GrB_UnaryOp op1,          // unary operator to apply
48         const GrB_BinaryOp op2,         // binary operator to apply
49         const GxB_Scalar scalar,        // scalar to bind to binary operator
50         bool binop_bind1st,             // if true, binop(x,A) else binop(A,y)
51     const GrB_Matrix A,                 // input matrix
52     // for sparse or hypersparse case:
53     int64_t *restrict *Workspaces,   // Workspaces, size nworkspaces
54     const int64_t *restrict A_slice, // how A is sliced, size nthreads+1
55     int nworkspaces,                    // # of workspaces to use
56     // for all cases:
57     int nthreads                        // # of threads to use
58 )
59 {
60 
61     //--------------------------------------------------------------------------
62     // check inputs
63     //--------------------------------------------------------------------------
64 
65     ASSERT (!GB_ZOMBIES (A)) ;
66     ASSERT (GB_JUMBLED_OK (A)) ;
67     ASSERT (!GB_PENDING (A)) ;
68 
69     GrB_Info info ;
70     GrB_Type Atype = A->type ;
71     ASSERT (op1 != NULL || op2 != NULL) ;
72     GB_Opcode opcode = (op1 != NULL) ? op1->opcode : op2->opcode ;
73 
74     // positional operators are applied after the transpose
75     ASSERT (!GB_OPCODE_IS_POSITIONAL (opcode)) ;
76 
77     //--------------------------------------------------------------------------
78     // transpose the matrix and apply the operator
79     //--------------------------------------------------------------------------
80 
81     if (op1 != NULL)
82     {
83 
84         //----------------------------------------------------------------------
85         // unary operator
86         //----------------------------------------------------------------------
87 
88         ASSERT_UNARYOP_OK (op1, "op1 for transpose", GB0) ;
89         GrB_UnaryOp op = op1 ;
90 
91         #ifndef GBCOMPACT
92         if ((Atype == op->xtype)
93             || (opcode == GB_IDENTITY_opcode) || (opcode == GB_ONE_opcode))
94         {
95 
96             // The switch factory is used if the op is IDENTITY or ONE, or if
97             // no typecasting is being done.  The ONE operator ignores the type
98             // of its input and just produces a 1 of op->ztype == op->xtype.
99             // The IDENTITY operator can do arbitrary typecasting.
100 
101             //------------------------------------------------------------------
102             // define the worker for the switch factory
103             //------------------------------------------------------------------
104 
105             #define GB_unop_tran(opname,zname,aname) \
106                 GB (_unop_tran_ ## opname ## zname ## aname)
107 
108             #define GB_WORKER(opname,zname,ztype,aname,atype)               \
109             {                                                               \
110                 info = GB_unop_tran (opname,zname,aname)                    \
111                     (C, A, Workspaces, A_slice, nworkspaces, nthreads) ;    \
112                 if (info == GrB_SUCCESS) return ;                           \
113             }                                                               \
114             break ;
115 
116             //------------------------------------------------------------------
117             // launch the switch factory
118             //------------------------------------------------------------------
119 
120             #include "GB_unop_factory.c"
121         }
122 
123         #endif
124 
125         //----------------------------------------------------------------------
126         // generic worker: transpose, typecast, and apply unary operator
127         //----------------------------------------------------------------------
128 
129         GB_BURBLE_MATRIX (A, "(generic transpose: %s) ", op1->name) ;
130 
131         size_t asize = Atype->size ;
132         size_t zsize = op->ztype->size ;
133         size_t xsize = op->xtype->size ;
134         GB_cast_function
135             cast_A_to_X = GB_cast_factory (op->xtype->code, Atype->code) ;
136         GxB_unary_function fop = op->function ;
137 
138         ASSERT_TYPE_OK (op1->ztype, "op1 ztype", GB0) ;
139         ASSERT_TYPE_OK (op1->xtype, "op1 xtype", GB0) ;
140         ASSERT_TYPE_OK (C->type, "C type", GB0) ;
141         ASSERT (C->type->size == zsize) ;
142         ASSERT (C->type == op->ztype) ;
143 
144         // Cx [pC] = op (cast (Ax [pA]))
145         #define GB_CAST_OP(pC,pA)                                       \
146         {                                                               \
147             /* xwork = (xtype) Ax [pA] */                               \
148             GB_void xwork [GB_VLA(xsize)] ;                             \
149             cast_A_to_X (xwork, Ax +((pA)*asize), asize) ;              \
150             /* Cx [pC] = fop (xwork) ; Cx is of type op->ztype */       \
151             fop (Cx +((pC)*zsize), xwork) ;                             \
152         }
153 
154         #define GB_ATYPE GB_void
155         #define GB_CTYPE GB_void
156         #include "GB_unop_transpose.c"
157 
158     }
159     else
160     {
161 
162         //----------------------------------------------------------------------
163         // binary operator
164         //----------------------------------------------------------------------
165 
166         ASSERT_BINARYOP_OK (op2, "op2 for transpose", GB0) ;
167 
168         GB_Type_code xcode, ycode, zcode ;
169         bool op_is_first  = (opcode == GB_FIRST_opcode) ;
170         bool op_is_second = (opcode == GB_SECOND_opcode) ;
171         bool op_is_pair   = (opcode == GB_PAIR_opcode) ;
172 
173         size_t asize = Atype->size ;
174         size_t ssize = scalar->type->size ;
175         size_t zsize = op2->ztype->size ;
176         size_t xsize = op2->xtype->size ;
177         size_t ysize = op2->ytype->size ;
178 
179         GB_Type_code scode = scalar->type->code ;
180         xcode = op2->xtype->code ;
181         ycode = op2->ytype->code ;
182 
183         // typecast the scalar to the operator input
184         bool ignore_scalar = false ;
185         size_t ssize_cast ;
186         GB_Type_code scode_cast ;
187         if (binop_bind1st)
188         {
189             ssize_cast = xsize ;
190             scode_cast = xcode ;
191             ignore_scalar = op_is_second || op_is_pair ;
192         }
193         else
194         {
195             ssize_cast = ysize ;
196             scode_cast = ycode ;
197             ignore_scalar = op_is_first  || op_is_pair ;
198         }
199         GB_void swork [GB_VLA(ssize_cast)] ;
200         GB_void *scalarx = (GB_void *) scalar->x ;
201         if (scode_cast != scode && !ignore_scalar)
202         {
203             // typecast the scalar to the operator input, in swork
204             GB_cast_function cast_s = GB_cast_factory (scode_cast, scode) ;
205             cast_s (swork, scalar->x, ssize) ;
206             scalarx = swork ;
207         }
208 
209         #ifndef GBCOMPACT
210         if (binop_bind1st)
211         {
212 
213             //------------------------------------------------------------------
214             // C = op(scalar,A')
215             //------------------------------------------------------------------
216 
217             if (GB_binop_builtin (
218                 op2->xtype, ignore_scalar,
219                 Atype,      op_is_first  || op_is_pair,
220                 op2, false, &opcode, &xcode, &ycode, &zcode))
221             {
222 
223                 //--------------------------------------------------------------
224                 // define the worker for the switch factory
225                 //--------------------------------------------------------------
226 
227                 #define GB_bind1st_tran(op,xname) \
228                     GB (_bind1st_tran_ ## op ## xname)
229 
230                 #define GB_BINOP_WORKER(op,xname)                           \
231                 {                                                           \
232                     if (GB_bind1st_tran (op, xname) (C, scalarx, A,         \
233                         Workspaces, A_slice, nworkspaces, nthreads)         \
234                         == GrB_SUCCESS) return ;                            \
235                 }                                                           \
236                 break ;
237 
238                 //--------------------------------------------------------------
239                 // launch the switch factory
240                 //--------------------------------------------------------------
241 
242                 #define GB_NO_SECOND
243                 #define GB_NO_PAIR
244                 #include "GB_binop_factory.c"
245             }
246         }
247         else
248         {
249 
250             //------------------------------------------------------------------
251             // C = op(A',scalar)
252             //------------------------------------------------------------------
253 
254             if (GB_binop_builtin (
255                 Atype,      op_is_second || op_is_pair,
256                 op2->ytype, ignore_scalar,
257                 op2, false, &opcode, &xcode, &ycode, &zcode))
258             {
259 
260                 //--------------------------------------------------------------
261                 // define the worker for the switch factory
262                 //--------------------------------------------------------------
263 
264                 #define GB_bind2nd_tran(op,xname) \
265                     GB (_bind2nd_tran_ ## op ## xname)
266                 #undef  GB_BINOP_WORKER
267                 #define GB_BINOP_WORKER(op,xname)                           \
268                 {                                                           \
269                     if (GB_bind2nd_tran (op, xname) (C, A, scalarx,         \
270                         Workspaces, A_slice, nworkspaces, nthreads)         \
271                         == GrB_SUCCESS) return ;                            \
272                 }                                                           \
273                 break ;
274 
275                 //--------------------------------------------------------------
276                 // launch the switch factory
277                 //--------------------------------------------------------------
278 
279                 #define GB_NO_FIRST
280                 #define GB_NO_PAIR
281                 #include "GB_binop_factory.c"
282             }
283         }
284         #endif
285 
286         //----------------------------------------------------------------------
287         // generic worker: transpose, typecast and apply a binary operator
288         //----------------------------------------------------------------------
289 
290         GB_BURBLE_MATRIX (A, "(generic transpose: %s) ", op2->name) ;
291         GB_Type_code acode = Atype->code ;
292         GxB_binary_function fop = op2->function ;
293 
294         if (binop_bind1st)
295         {
296             // Cx = op (scalar,Ax)
297             GB_cast_function cast_A_to_Y = GB_cast_factory (ycode, acode) ;
298             // Cx [pC] = op (cast (scalar), cast (Ax [pA]))
299             #undef  GB_CAST_OP
300             #define GB_CAST_OP(pC,pA)                                       \
301             {                                                               \
302                 /* ywork = (ytype) Ax [pA] */                               \
303                 GB_void ywork [GB_VLA(ysize)] ;                             \
304                 cast_A_to_Y (ywork, Ax +((pA)*asize), asize) ;              \
305                 /* Cx [pC] = fop (xwork) ; Cx is of type op->ztype */       \
306                 fop (Cx +((pC)*zsize), scalarx, ywork) ;                    \
307             }
308             #include "GB_unop_transpose.c"
309         }
310         else
311         {
312             // Cx = op (Ax,scalar)
313             GB_cast_function cast_A_to_X = GB_cast_factory (xcode, acode) ;
314             // Cx [pC] = op (cast (Ax [pA]), cast (scalar))
315             #undef  GB_CAST_OP
316             #define GB_CAST_OP(pC,pA)                                       \
317             {                                                               \
318                 /* xwork = (xtype) Ax [pA] */                               \
319                 GB_void xwork [GB_VLA(xsize)] ;                             \
320                 cast_A_to_X (xwork, Ax +((pA)*asize), asize) ;              \
321                 /* Cx [pC] = fop (xwork) ; Cx is of type op->ztype */       \
322                 fop (Cx +(pC*zsize), xwork, scalarx) ;                      \
323             }
324             #include "GB_unop_transpose.c"
325         }
326     }
327 }
328 
329