From cd8a9ffd33f5b58311ac8838ce276d2c19b0cba7 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 12 Nov 2024 19:29:24 +0100 Subject: [PATCH 1/4] Implement dpnp.binary_repr --- dpnp/dpnp_iface_bitwise.py | 67 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/dpnp/dpnp_iface_bitwise.py b/dpnp/dpnp_iface_bitwise.py index 97dcc5810feb..6c3b5ee7cedb 100644 --- a/dpnp/dpnp_iface_bitwise.py +++ b/dpnp/dpnp_iface_bitwise.py @@ -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", @@ -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` From a475f094e09acb0f4a2629f54aa4377707727e6e Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 12 Nov 2024 19:35:53 +0100 Subject: [PATCH 2/4] Add third party tests --- tests/third_party/cupy/io_tests/__init__.py | 0 .../third_party/cupy/io_tests/test_base_n.py | 70 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/third_party/cupy/io_tests/__init__.py create mode 100644 tests/third_party/cupy/io_tests/test_base_n.py diff --git a/tests/third_party/cupy/io_tests/__init__.py b/tests/third_party/cupy/io_tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/third_party/cupy/io_tests/test_base_n.py b/tests/third_party/cupy/io_tests/test_base_n.py new file mode 100644 index 000000000000..46997d7ff715 --- /dev/null +++ b/tests/third_party/cupy/io_tests/test_base_n.py @@ -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" From b811f5ad44e9e5b996a216d1b3a0c0edb98b3e85 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 12 Nov 2024 19:55:03 +0100 Subject: [PATCH 3/4] Update links and add more examples with dpnp.binary_repr --- dpnp/dpnp_iface_bitwise.py | 66 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/dpnp/dpnp_iface_bitwise.py b/dpnp/dpnp_iface_bitwise.py index 6c3b5ee7cedb..21344cab8cae 100644 --- a/dpnp/dpnp_iface_bitwise.py +++ b/dpnp/dpnp_iface_bitwise.py @@ -165,14 +165,16 @@ def binary_repr(num, width=None): :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]) @@ -184,6 +186,16 @@ def binary_repr(num, width=None): >>> x1 & x2 array([ 2, 4, 16]) + +The number 13 is represented by ``00001101``. Likewise, 17 is represented by +``00010001``. The bit-wise AND of 13 and 17 is therefore ``000000001``, or 1: + +>>> 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( @@ -234,6 +246,8 @@ def binary_repr(num, width=None): :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 -------- @@ -248,6 +262,15 @@ def binary_repr(num, width=None): >>> 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( @@ -298,6 +321,8 @@ def binary_repr(num, width=None): :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 -------- @@ -317,6 +342,14 @@ def binary_repr(num, width=None): >>> 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( @@ -365,13 +398,21 @@ def binary_repr(num, width=None): :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) @@ -382,6 +423,7 @@ def binary_repr(num, width=None): >>> ~a array([False, True]) + """ invert = DPNPUnaryFunc( @@ -435,6 +477,8 @@ def binary_repr(num, width=None): 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 -------- @@ -449,6 +493,13 @@ def binary_repr(num, width=None): >>> 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( @@ -501,6 +552,8 @@ def binary_repr(num, width=None): 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 -------- @@ -515,6 +568,13 @@ def binary_repr(num, width=None): >>> 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( From 356408f992d85898c05e5c64c21de5a305d34099 Mon Sep 17 00:00:00 2001 From: Anton <100830759+antonwolfy@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:12:16 +0100 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Vahid Tavanashad <120411540+vtavana@users.noreply.github.com> --- dpnp/dpnp_iface_bitwise.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dpnp/dpnp_iface_bitwise.py b/dpnp/dpnp_iface_bitwise.py index 21344cab8cae..a2ae5bcc63c8 100644 --- a/dpnp/dpnp_iface_bitwise.py +++ b/dpnp/dpnp_iface_bitwise.py @@ -187,9 +187,12 @@ def binary_repr(num, width=None): >>> x1 & x2 array([ 2, 4, 16]) -The number 13 is represented by ``00001101``. Likewise, 17 is represented by +The number 13 is represented by ``00001101``. Likewise, 17 is represented by ``00010001``. The bit-wise AND of 13 and 17 is therefore ``000000001``, or 1: +>>> np.bitwise_and(np.array(13), 17) +array(1) + >>> np.bitwise_and(np.array(14), 13) array(12) >>> np.binary_repr(12)