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