Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REVIEW] Bitwise binary ops AND, OR, XOR (&, |, ^) #1292

Merged
merged 12 commits into from
Mar 28, 2019
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
## New Features

- PR #1194 Implement overloads for CUDA atomic operations

...
- PR #1292 Implemented Bitwise binary ops AND, OR, XOR (&, |, ^)

## Improvements

Expand Down
31 changes: 17 additions & 14 deletions cpp/include/cudf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,20 +203,23 @@ typedef enum {
* @brief Types of binary operations that can be performed on data.
*/
typedef enum {
GDF_ADD, /**< operator + */
GDF_SUB, /**< operator - */
GDF_MUL, /**< operator * */
GDF_DIV, /**< operator / using common type of lhs and rhs */
GDF_TRUE_DIV, /**< operator / after promoting type to floating point*/
GDF_FLOOR_DIV, /**< operator / after promoting to float and then flooring the result */
GDF_MOD, /**< operator % */
GDF_POW, /**< lhs ^ rhs */
GDF_EQUAL, /**< operator == */
GDF_NOT_EQUAL, /**< operator != */
GDF_LESS, /**< operator < */
GDF_GREATER, /**< operator > */
GDF_LESS_EQUAL, /**< operator <= */
GDF_GREATER_EQUAL, /**< operator >= */
GDF_ADD, ///< operator +
GDF_SUB, ///< operator -
GDF_MUL, ///< operator *
GDF_DIV, ///< operator / using common type of lhs and rhs
GDF_TRUE_DIV, ///< operator / after promoting type to floating point
GDF_FLOOR_DIV, ///< operator / after promoting to float and then flooring the result
GDF_MOD, ///< operator %
GDF_POW, ///< lhs ^ rhs
GDF_EQUAL, ///< operator ==
GDF_NOT_EQUAL, ///< operator !=
GDF_LESS, ///< operator <
GDF_GREATER, ///< operator >
GDF_LESS_EQUAL, ///< operator <=
GDF_GREATER_EQUAL, ///< operator >=
GDF_BITWISE_AND, ///< operator &
GDF_BITWISE_OR, ///< operator |
GDF_BITWISE_XOR, ///< operator ^
GDF_COALESCE, ///< operator x,y x is null ? y : x
GDF_INVALID_BINARY ///< invalid operation
} gdf_binary_operator;
Expand Down
28 changes: 28 additions & 0 deletions cpp/src/binary/jit/code/operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,34 @@ R"***(
return (y >= x);
}
};

struct BitwiseAnd {
template <typename TypeOut, typename TypeLhs, typename TypeRhs>
static TypeOut operate(TypeLhs x, TypeRhs y) {
return (static_cast<TypeOut>(x) & static_cast<TypeOut>(y));
}
};

using RBitwiseAnd = BitwiseAnd;

struct BitwiseOr {
template <typename TypeOut, typename TypeLhs, typename TypeRhs>
static TypeOut operate(TypeLhs x, TypeRhs y) {
return (static_cast<TypeOut>(x) | static_cast<TypeOut>(y));
}
};

using RBitwiseOr = BitwiseOr;

struct BitwiseXor {
template <typename TypeOut, typename TypeLhs, typename TypeRhs>
static TypeOut operate(TypeLhs x, TypeRhs y) {
return (static_cast<TypeOut>(x) ^ static_cast<TypeOut>(y));
}
};

using RBitwiseXor = BitwiseXor;

)***";

} // namespace code
Expand Down
6 changes: 6 additions & 0 deletions cpp/src/binary/jit/util/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ namespace jit {
return "LessEqual";
case GDF_GREATER_EQUAL:
return "GreaterEqual";
case GDF_BITWISE_AND:
return "BitwiseAnd";
case GDF_BITWISE_OR:
return "BitwiseOr";
case GDF_BITWISE_XOR:
return "BitwiseXor";
default:
return "None";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,60 @@ TEST_F(BinaryOperationIntegrationTest, Pow_Vector_Vector_SI64) {
ASSERT_BINOP(out, lhs, rhs, POW());
}


TEST_F(BinaryOperationIntegrationTest, And_Vector_Vector_SI16_SI64_SI32) {
using AND = cudf::library::operation::BitwiseAnd<int16_t, int64_t, int32_t>;

auto lhs = cudf::test::column_wrapper<int16_t>{500,
[](gdf_size_type row) {return row;},
[](gdf_size_type row) {return (row % 6 > 0);}};
auto rhs = cudf::test::column_wrapper<int64_t>{500,
[](gdf_size_type row) {return 2;},
[](gdf_size_type row) {return (row % 4 > 0);}};
auto out = cudf::test::column_wrapper<int32_t>{lhs.get()->size, true};

auto result = gdf_binary_operation_v_v(out.get(), lhs.get(), rhs.get(), GDF_BITWISE_AND);
ASSERT_TRUE(result == GDF_SUCCESS);

ASSERT_BINOP(out, lhs, rhs, AND());
}


TEST_F(BinaryOperationIntegrationTest, Or_Vector_Vector_SI64_SI16_SI32) {
using OR = cudf::library::operation::BitwiseOr<int64_t, int16_t, int32_t>;

auto lhs = cudf::test::column_wrapper<int64_t>{500,
[](gdf_size_type row) {return row;},
[](gdf_size_type row) {return (row % 6 > 0);}};
auto rhs = cudf::test::column_wrapper<int16_t>{500,
[](gdf_size_type row) {return 2;},
[](gdf_size_type row) {return (row % 4 > 0);}};
auto out = cudf::test::column_wrapper<int32_t>{lhs.get()->size, true};

auto result = gdf_binary_operation_v_v(out.get(), lhs.get(), rhs.get(), GDF_BITWISE_OR);
ASSERT_TRUE(result == GDF_SUCCESS);

ASSERT_BINOP(out, lhs, rhs, OR());
}


TEST_F(BinaryOperationIntegrationTest, Xor_Vector_Vector_SI32_SI16_SI64) {
using XOR = cudf::library::operation::BitwiseXor<int32_t, int16_t, int64_t>;

auto lhs = cudf::test::column_wrapper<int32_t>{500,
[](gdf_size_type row) {return row;},
[](gdf_size_type row) {return (row % 6 > 0);}};
auto rhs = cudf::test::column_wrapper<int16_t>{500,
[](gdf_size_type row) {return 2;},
[](gdf_size_type row) {return (row % 4 > 0);}};
auto out = cudf::test::column_wrapper<int64_t>{lhs.get()->size, true};

auto result = gdf_binary_operation_v_v(out.get(), lhs.get(), rhs.get(), GDF_BITWISE_XOR);
ASSERT_TRUE(result == GDF_SUCCESS);

ASSERT_BINOP(out, lhs, rhs, XOR());
}

} // namespace binop
} // namespace test
} // namespace cudf
21 changes: 21 additions & 0 deletions cpp/tests/binary/util/operation.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,27 @@ namespace operation {
}
};

template <typename TypeOut, typename TypeLhs, typename TypeRhs>
struct BitwiseAnd {
TypeOut operator()(TypeLhs lhs, TypeRhs rhs) {
return (lhs & rhs);
}
};

template <typename TypeOut, typename TypeLhs, typename TypeRhs>
struct BitwiseOr {
TypeOut operator()(TypeLhs lhs, TypeRhs rhs) {
return (lhs | rhs);
}
};

template <typename TypeOut, typename TypeLhs, typename TypeRhs>
struct BitwiseXor {
TypeOut operator()(TypeLhs lhs, TypeRhs rhs) {
return (lhs ^ rhs);
}
};

} // namespace operation
} // namespace library
} // namespace cudf
Expand Down
3 changes: 3 additions & 0 deletions python/cudf/bindings/binops.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ _BINARY_OP['lt'] = GDF_LESS
_BINARY_OP['gt'] = GDF_GREATER
_BINARY_OP['le'] = GDF_LESS_EQUAL
_BINARY_OP['ge'] = GDF_GREATER_EQUAL
_BINARY_OP['and'] = GDF_BITWISE_AND
_BINARY_OP['or'] = GDF_BITWISE_OR
_BINARY_OP['xor'] = GDF_BITWISE_XOR


def apply_op(lhs, rhs, out, op):
Expand Down
5 changes: 4 additions & 1 deletion python/cudf/bindings/cudf_cpp.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ cdef extern from "cudf.h" nogil:
GDF_LESS,
GDF_GREATER,
GDF_LESS_EQUAL,
GDF_GREATER_EQUAL
GDF_GREATER_EQUAL,
GDF_BITWISE_AND,
GDF_BITWISE_OR,
GDF_BITWISE_XOR,


ctypedef struct gdf_scalar:
Expand Down
80 changes: 35 additions & 45 deletions python/cudf/dataframe/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,26 +421,18 @@ def __repr__(self):
len(self),
)

# binary, rbinary, unary, orderedcompare, unorderedcompare
def _call_op(self, other, internal_fn, fn):
# binary, rbinary, orderedcompare, unorderedcompare
def _apply_op(self, fn, other):
harrism marked this conversation as resolved.
Show resolved Hide resolved
result = DataFrame()
result.set_index(self.index)
if internal_fn == '_unaryop':
for col in self._cols:
result[col] = self._cols[col]._unaryop(fn)
elif isinstance(other, Sequence):
if isinstance(other, Sequence):
for k, col in enumerate(self._cols):
result[col] = getattr(self._cols[col], internal_fn)(
other[k],
fn,
)
result[col] = getattr(self._cols[col], fn)(other[k])
elif isinstance(other, DataFrame):
for col in other._cols:
if col in self._cols:
result[col] = getattr(self._cols[col], internal_fn)(
other._cols[col],
fn,
)
result[col] = getattr(self._cols[col], fn)(
other._cols[col])
else:
result[col] = Series(cudautils.full(self.shape[0],
np.dtype('float64').type(np.nan),
Expand All @@ -457,42 +449,37 @@ def _call_op(self, other, internal_fn, fn):
" Series into a DataFrame first.")
elif isinstance(other, numbers.Number):
for col in self._cols:
result[col] = getattr(self._cols[col], internal_fn)(
other,
fn,
)
result[col] = getattr(self._cols[col], fn)(other)
else:
raise NotImplementedError(
"DataFrame operations with " + str(type(other)) + " not "
"supported at this time.")
return result

def _binaryop(self, other, fn):
return self._call_op(other, '_binaryop', fn)

def _rbinaryop(self, other, fn):
return self._call_op(other, '_rbinaryop', fn)

def _unaryop(self, fn):
return self._call_op(self, '_unaryop', fn)
result = DataFrame()
result.set_index(self.index)
for col in self._cols:
result[col] = self._cols[col]._unaryop(fn)
return result

def __add__(self, other):
return self._binaryop(other, 'add')
return self._apply_op('__add__', other)

def __radd__(self, other):
return self._rbinaryop(other, 'add')
return self._apply_op('__radd__', other)

def __sub__(self, other):
return self._binaryop(other, 'sub')
return self._apply_op('__sub__', other)

def __rsub__(self, other):
return self._rbinaryop(other, 'sub')
return self._apply_op('__rsub__', other)

def __mul__(self, other):
return self._binaryop(other, 'mul')
return self._apply_op('__mul__', other)

def __rmul__(self, other):
return self._rbinaryop(other, 'mul')
return self._apply_op('__rmul__', other)

def __pow__(self, other):
if other == 2:
Expand All @@ -501,42 +488,45 @@ def __pow__(self, other):
return NotImplemented

def __floordiv__(self, other):
return self._binaryop(other, 'floordiv')
return self._apply_op('__floordiv__', other)

def __rfloordiv__(self, other):
return self._rbinaryop(other, 'floordiv')
return self._apply_op('__rfloordiv__', other)

def __truediv__(self, other):
return self._binaryop(other, 'truediv')
return self._apply_op('__truediv__', other)

def __rtruediv__(self, other):
return self._rbinaryop(other, 'truediv')
return self._apply_op('__rtruediv__', other)

__div__ = __truediv__

def _unordered_compare(self, other, cmpops):
return self._call_op(other, '_unordered_compare', cmpops)
def __and__(self, other):
return self._apply_op('__and__', other)

def __or__(self, other):
return self._apply_op('__or__', other)

def _ordered_compare(self, other, cmpops):
return self._call_op(other, '_ordered_compare', cmpops)
def __xor__(self, other):
return self._apply_op('__xor__', other)

def __eq__(self, other):
return self._unordered_compare(other, 'eq')
return self._apply_op('__eq__', other)

def __ne__(self, other):
return self._unordered_compare(other, 'ne')
return self._apply_op('__ne__', other)

def __lt__(self, other):
return self._ordered_compare(other, 'lt')
return self._apply_op('__lt__', other)

def __le__(self, other):
return self._ordered_compare(other, 'le')
return self._apply_op('__le__', other)

def __gt__(self, other):
return self._ordered_compare(other, 'gt')
return self._apply_op('__gt__', other)

def __ge__(self, other):
return self._ordered_compare(other, 'ge')
return self._apply_op('__ge__', other)
devavret marked this conversation as resolved.
Show resolved Hide resolved

def __iter__(self):
return iter(self.columns)
Expand Down
9 changes: 9 additions & 0 deletions python/cudf/dataframe/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ def __rtruediv__(self, other):

__div__ = __truediv__

def __and__(self, other):
return self._apply_op('__and__', other)

def __or__(self, other):
return self._apply_op('__or__', other)

def __xor__(self, other):
return self._apply_op('__xor__', other)

def __eq__(self, other):
return self._apply_op('__eq__', other)

Expand Down
Loading