1 //------------------------------------------------------------------------------
2 // GB_binop_builtin: determine if a binary operator is built-in
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 // Determine if the binary operator is built-in, for the multiplicative binary
11 // operator for A*B, or the binary operator for ewise operations (A+B, A.*B,
12 // and some uses of accum in GrB_assign)
13
14 // If so, determine the opcodes and type codes of the semiring.
15
16 // If the op is NULL, then it is the implicit GrB_SECOND_[A_type] operator.
17 // This is a built-in operator for built-in types. This feature is only used
18 // by GB_Matrix_wait.
19
20 // This function is not used by the CUDA jitified kernels, since they can
21 // typecast the entries in the matrices A and B to the types of x and y of the
22 // operator, as needed.
23
24 #include "GB.h"
25 #include "GB_binop.h"
26 #include "GB_unused.h"
27
GB_binop_builtin(const GrB_Type A_type,const bool A_is_pattern,const GrB_Type B_type,const bool B_is_pattern,const GrB_BinaryOp op,const bool flipxy,GB_Opcode * opcode,GB_Type_code * xcode,GB_Type_code * ycode,GB_Type_code * zcode)28 bool GB_binop_builtin // true if binary operator is builtin
29 (
30 // inputs:
31 const GrB_Type A_type,
32 const bool A_is_pattern, // true if only the pattern of A is used
33 const GrB_Type B_type,
34 const bool B_is_pattern, // true if only the pattern of B is used
35 const GrB_BinaryOp op, // binary operator; may be NULL
36 const bool flipxy, // true if z=op(y,x), flipping x and y
37 // outputs, unused by caller if this function returns false
38 GB_Opcode *opcode, // opcode for the binary operator
39 GB_Type_code *xcode, // type code for x input
40 GB_Type_code *ycode, // type code for y input
41 GB_Type_code *zcode // type code for z output
42 )
43 {
44
45 //--------------------------------------------------------------------------
46 // check if the operator is builtin, with no typecasting
47 //--------------------------------------------------------------------------
48
49 GrB_Type op_xtype, op_ytype, op_ztype ;
50 if (op == NULL)
51 {
52 // implicit GB_SECOND_[TYPE] operator
53 ASSERT (A_type == B_type) ;
54 (*opcode) = GB_SECOND_opcode ;
55 op_xtype = A_type ;
56 op_ytype = A_type ;
57 op_ztype = A_type ;
58 }
59 else
60 {
61 (*opcode) = op->opcode ;
62 op_xtype = op->xtype ;
63 op_ytype = op->ytype ;
64 op_ztype = op->ztype ;
65 }
66
67 if (*opcode >= GB_USER_opcode)
68 {
69 // the binary operator is user-defined
70 return (false) ;
71 }
72
73 bool op_is_positional = GB_OPCODE_IS_POSITIONAL (*opcode) ;
74
75 // check if A matches the input to the operator
76 if (!A_is_pattern && !op_is_positional)
77 {
78 if ((A_type != (flipxy ? op_ytype : op_xtype)) ||
79 (A_type->code >= GB_UDT_code))
80 {
81 // A is a user-defined type, or its type does not match the input
82 // to the operator
83 return (false) ;
84 }
85 }
86
87 // check if B matches the input to the operator
88 if (!B_is_pattern && !op_is_positional)
89 {
90 if ((B_type != (flipxy ? op_xtype : op_ytype)) ||
91 (B_type->code >= GB_UDT_code))
92 {
93 // B is a user-defined type, or its type does not match the input
94 // to the operator
95 return (false) ;
96 }
97 }
98
99 //--------------------------------------------------------------------------
100 // rename redundant boolean operators
101 //--------------------------------------------------------------------------
102
103 (*xcode) = op_xtype->code ;
104 (*ycode) = op_ytype->code ;
105 (*zcode) = op_ztype->code ;
106
107 ASSERT ((*xcode) < GB_UDT_code) ;
108 ASSERT ((*ycode) < GB_UDT_code) ;
109 ASSERT ((*zcode) < GB_UDT_code) ;
110
111 if ((*xcode) == GB_BOOL_code)
112 {
113 // z = op(x,y) where both x and y are boolean.
114 // DIV becomes FIRST
115 // RDIV becomes SECOND
116 // MIN and TIMES become LAND
117 // MAX and PLUS become LOR
118 // NE, ISNE, RMINUS, and MINUS become LXOR
119 // ISEQ becomes EQ
120 // ISGT becomes GT
121 // ISLT becomes LT
122 // ISGE and POW become GE
123 // ISLE becomes LE
124 (*opcode) = GB_boolean_rename (*opcode) ;
125 }
126
127 //--------------------------------------------------------------------------
128 // handle the flipxy (for a semiring only)
129 //--------------------------------------------------------------------------
130
131 // If flipxy is true, the matrices A and B have been flipped (A passed as B
132 // and B passed as A), so pass A as the 2nd argument to the operator, and B
133 // as the first. This can also be done by flipping operator opcodes
134 // instead of flipping the A and B inputs to the operator, thus simplifying
135 // the workers. The z=x-y and z=x/y operators are flipped using the GxB_*
136 // functions rminus (z=y-x)and rdiv (z=y/x).
137
138 bool handled = true ;
139 if (flipxy)
140 {
141 // All built-in semirings use either commutative multiplicative
142 // operators (PLUS, TIMES, ANY, ...), or operators that have flipped
143 // versions (DIV vs RDIV, ...). Flipping the operator does not handle
144 // ATAN2, BGET, and other built-in operators, but these do not
145 // correspond to built-in semirings.
146 (*opcode) = GB_binop_flip (*opcode, &handled) ; // for any opcode
147 }
148
149 return (handled) ;
150 }
151
152