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