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

Add dpnp.binary_repr function #2168

Merged
merged 7 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 130 additions & 3 deletions dpnp/dpnp_iface_bitwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@


import dpctl.tensor._tensor_elementwise_impl as ti
import numpy

from dpnp.dpnp_algo.dpnp_elementwise_common import DPNPBinaryFunc, DPNPUnaryFunc

__all__ = [
"binary_repr",
"bitwise_and",
"bitwise_invert",
"bitwise_left_shift",
Expand All @@ -58,6 +60,71 @@
]


def binary_repr(num, width=None):
"""
Return the binary representation of the input number as a string.

For negative numbers, if `width` is not given, a minus sign is added to the
front. If `width` is given, the two's complement of the number is returned,
with respect to that width.

In a two's-complement system negative numbers are represented by the two's
complement of the absolute value. A N-bit two's-complement system can
represent every integer in the range :math:`-2^{N-1}` to :math:`+2^{N-1}-1`.

For full documentation refer to :obj:`numpy.binary_repr`.

Parameters
----------
num : int
Only an integer decimal number can be used.
width : {None, int}, optional
The length of the returned string if `num` is positive, or the length
of the two's complement if `num` is negative, provided that `width` is
at least a sufficient number of bits for `num` to be represented in the
designated form. If the `width` value is insufficient, an error is
raised.
Default: ``None``.

Returns
-------
bin : str
Binary representation of `num` or two's complement of `num`.

See Also
--------
:obj:`dpnp.base_repr` : Return a string representation of a number in the
given base system.
bin : Python's built-in binary representation generator of an integer.

Notes
-----
:obj:`dpnp.binary_repr` is equivalent to using :obj:`dpnp.base_repr` with
base 2, but significantly faster.

Examples
--------
>>> import numpy as np
>>> np.binary_repr(3)
'11'
>>> np.binary_repr(-3)
'-11'
>>> np.binary_repr(3, width=4)
'0011'

The two's complement is returned when the input number is negative and
`width` is specified:

>>> np.binary_repr(-3, width=3)
'101'
>>> np.binary_repr(-3, width=5)
'11101'

"""

return numpy.binary_repr(num, width)


_BITWISE_AND_DOCSTRING = """
Computes the bitwise AND of the underlying binary representation of each
element `x1_i` of the input array `x1` with the respective element `x2_i`
Expand Down Expand Up @@ -98,14 +165,16 @@
:obj:`dpnp.logical_and` : Compute the truth value of ``x1`` AND ``x2`` element-wise.
:obj:`dpnp.bitwise_or`: Compute the bit-wise OR of two arrays element-wise.
:obj:`dpnp.bitwise_xor` : Compute the bit-wise XOR of two arrays element-wise.
:obj:`dpnp.binary_repr` : Return the binary representation of the input number
as a string.

Examples
--------
>>> import dpnp as np
>>> x1 = np.array([2, 5, 255])
>>> x2 = np.array([3,14,16])
>>> x2 = np.array([3, 14, 16])
>>> np.bitwise_and(x1, x2)
[2, 4, 16]
array([ 2, 4, 16])

>>> a = np.array([True, True])
>>> b = np.array([False, True])
Expand All @@ -117,6 +186,16 @@

>>> x1 & x2
array([ 2, 4, 16])

The number 13 is represented by ``00001101``. Likewise, 17 is represented by
antonwolfy marked this conversation as resolved.
Show resolved Hide resolved
``00010001``. The bit-wise AND of 13 and 17 is therefore ``000000001``, or 1:

antonwolfy marked this conversation as resolved.
Show resolved Hide resolved
>>> np.bitwise_and(np.array(14), 13)
array(12)
>>> np.binary_repr(12)
'1100'
>>> np.bitwise_and(np.array([14, 3]), 13)
array([12, 1])
"""

bitwise_and = DPNPBinaryFunc(
Expand Down Expand Up @@ -167,6 +246,8 @@
:obj:`dpnp.logical_or` : Compute the truth value of ``x1`` OR ``x2`` element-wise.
:obj:`dpnp.bitwise_and`: Compute the bit-wise AND of two arrays element-wise.
:obj:`dpnp.bitwise_xor` : Compute the bit-wise XOR of two arrays element-wise.
:obj:`dpnp.binary_repr` : Return the binary representation of the input number
as a string.

Examples
--------
Expand All @@ -181,6 +262,15 @@

>>> x1 | x2
array([ 6, 5, 255])

The number 13 has the binary representation ``00001101``. Likewise, 16 is
represented by ``00010000``. The bit-wise OR of 13 and 16 is then ``00011101``,
or 29:

>>> np.bitwise_or(np.array(13), 16)
array(29)
>>> np.binary_repr(29)
'11101'
"""

bitwise_or = DPNPBinaryFunc(
Expand Down Expand Up @@ -231,6 +321,8 @@
:obj:`dpnp.logical_xor` : Compute the truth value of ``x1`` XOR `x2`, element-wise.
:obj:`dpnp.bitwise_and`: Compute the bit-wise AND of two arrays element-wise.
:obj:`dpnp.bitwise_or` : Compute the bit-wise OR of two arrays element-wise.
:obj:`dpnp.binary_repr` : Return the binary representation of the input number
as a string.

Examples
--------
Expand All @@ -250,6 +342,14 @@

>>> a ^ b
array([ True, False])

The number 13 is represented by ``00001101``. Likewise, 17 is represented by
``00010001``. The bit-wise XOR of 13 and 17 is therefore ``00011100``, or 28:

>>> np.bitwise_xor(np.array(13), 17)
array(28)
>>> np.binary_repr(28)
'11100'
"""

bitwise_xor = DPNPBinaryFunc(
Expand Down Expand Up @@ -298,13 +398,21 @@
:obj:`dpnp.bitwise_or` : Compute the bit-wise OR of two arrays element-wise.
:obj:`dpnp.bitwise_xor` : Compute the bit-wise XOR of two arrays element-wise.
:obj:`dpnp.logical_not` : Compute the truth value of NOT x element-wise.
:obj:`dpnp.binary_repr` : Return the binary representation of the input number
as a string.

Examples
--------
>>> import dpnp as np

The number 13 is represented by ``00001101``. The invert or bit-wise NOT of 13
is then:

>>> x = np.array([13])
>>> np.invert(x)
-14
array([-14])
>>> np.binary_repr(-14, width=8)
'11110010'

>>> a = np.array([True, False])
>>> np.invert(a)
Expand All @@ -315,6 +423,7 @@

>>> ~a
array([False, True])

"""

invert = DPNPUnaryFunc(
Expand Down Expand Up @@ -368,6 +477,8 @@
See Also
--------
:obj:`dpnp.right_shift` : Shift the bits of an integer to the right.
:obj:`dpnp.binary_repr` : Return the binary representation of the input number
as a string.

Examples
--------
Expand All @@ -382,6 +493,13 @@

>>> x1 << x2
array([10, 20, 40])

>>> np.binary_repr(5)
'101'
>>> np.left_shift(np.array(5), 2)
array(20)
>>> np.binary_repr(20)
'10100'
"""

left_shift = DPNPBinaryFunc(
Expand Down Expand Up @@ -434,6 +552,8 @@
See Also
--------
:obj:`dpnp.left_shift` : Shift the bits of an integer to the left.
:obj:`dpnp.binary_repr` : Return the binary representation of the input number
as a string.

Examples
--------
Expand All @@ -448,6 +568,13 @@

>>> x1 >> x2
array([5, 2, 1])

>>> np.binary_repr(10)
'1010'
>>> np.right_shift(np.array(10), 1)
array(5)
>>> np.binary_repr(5)
'101'
"""

right_shift = DPNPBinaryFunc(
Expand Down
Empty file.
70 changes: 70 additions & 0 deletions tests/third_party/cupy/io_tests/test_base_n.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import pytest

from tests.third_party.cupy import testing


class TestBinaryRepr(testing.NumpyAliasBasicTestBase):

func = "binary_repr"


@testing.parameterize(
*testing.product(
{
"args": [
(0,),
(3,),
(-3,),
(0, 0),
(3, 5),
(-3, 5),
# TODO(unno): Insuffisicent width is deprecated in numpy>=1.13.
# We need to check if it cause a warning, and maybe it causes an
# error in the future.
# (3, 0),
# (-3, 0),
]
}
)
)
class TestBinaryReprValues(testing.NumpyAliasValuesTestBase):

func = "binary_repr"


@pytest.mark.skip("base_repr() is not implemented")
class TestBaseRepr(testing.NumpyAliasBasicTestBase):

func = "base_repr"


@testing.parameterize(
*testing.product(
{
"args": [
(0,),
(5,),
(-5,),
(0, 2),
(0, 10),
(0, 36),
(5, 2),
(5, 10),
(5, 36),
(-5, 2),
(-5, 10),
(-5, 36),
(-5, 2, 0),
(-5, 2, 2),
(-5, 2, 10),
(5, 2, 0),
(5, 2, 2),
(5, 2, 10),
]
}
)
)
@pytest.mark.skip("base_repr() is not implemented")
class TestBaseReprValues(testing.NumpyAliasValuesTestBase):

func = "base_repr"
Loading