(l-onnx-doc-BitShift)= # BitShift (l-onnx-op-bitshift-28)= ## BitShift - 28 ### Version - **name**: [BitShift (GitHub)](https://github.com/onnx/onnx/blob/main/docs/Operators.md#BitShift) - **domain**: `main` - **since_version**: `28` - **function**: `False` - **support_level**: `SupportType.COMMON` - **shape inference**: `True` This version of the operator has been available **since version 28**. ### Summary Bitwise shift operator performs element-wise operation. For each input element, if the attribute "direction" is "RIGHT", this operator moves its binary representation toward the right side. If the attribute "direction" is "LEFT", bits of binary representation move toward the left side. The input X is the tensor to be shifted and another input Y specifies the amounts of shifting. For example, if "direction" is "RIGHT", X is [1, 4], and Y is [1, 1], the corresponding output Z would be [0, 2]. If "direction" is "LEFT" with X=[1, 2] and Y=[1, 2], the corresponding output Z would be [2, 8]. For a signed T the right shift is an arithmetic shift (sign-extending). The vacated high bits are filled with copies of the sign bit, so a negative X stays negative. For a signed T a left shift can move bits into and past the sign bit, and bits shifted past the sign bit are discarded. If Y is negative, or is greater than or equal to the number of bits of T, then the result is whatever the sign bit extension alone produces: -1 for a right shift on a negative X, where the fill is a sign bit of 1, and 0 in every other case. This operator supports **multidirectional (i.e., Numpy-style) broadcasting**; for more details please check [Broadcasting in ONNX](https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md). ### Attributes * **direction - STRING** (required) : Direction of moving bits. It can be either "RIGHT" (for right shift) or "LEFT" (for left shift). ### Inputs - **X** (heterogeneous) - **T**: First operand, input to be shifted. - **Y** (heterogeneous) - **T**: Second operand, amounts of shift. ### Outputs - **Z** (heterogeneous) - **T**: Output tensor ### Type Constraints * **T** in ( `tensor(int16)`, `tensor(int32)`, `tensor(int64)`, `tensor(int8)`, `tensor(uint16)`, `tensor(uint32)`, `tensor(uint64)`, `tensor(uint8)` ): Constrain input and output types to integer tensors. ### Examples #### _right_unit8 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([16, 4, 1]).astype(np.uint8) y = np.array([1, 2, 3]).astype(np.uint8) z = x >> y # expected output [8, 1, 0] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_right_uint8") ``` #### _right_unit16 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([16, 4, 1]).astype(np.uint16) y = np.array([1, 2, 3]).astype(np.uint16) z = x >> y # expected output [8, 1, 0] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_right_uint16") ``` #### _right_unit32 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([16, 4, 1]).astype(np.uint32) y = np.array([1, 2, 3]).astype(np.uint32) z = x >> y # expected output [8, 1, 0] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_right_uint32") ``` #### _right_unit64 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([16, 4, 1]).astype(np.uint64) y = np.array([1, 2, 3]).astype(np.uint64) z = x >> y # expected output [8, 1, 0] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_right_uint64") ``` #### _left_unit8 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([16, 4, 1]).astype(np.uint8) y = np.array([1, 2, 3]).astype(np.uint8) z = x << y # expected output [32, 16, 8] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_left_uint8") ``` #### _left_unit16 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([16, 4, 1]).astype(np.uint16) y = np.array([1, 2, 3]).astype(np.uint16) z = x << y # expected output [32, 16, 8] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_left_uint16") ``` #### _left_unit32 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([16, 4, 1]).astype(np.uint32) y = np.array([1, 2, 3]).astype(np.uint32) z = x << y # expected output [32, 16, 8] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_left_uint32") ``` #### _left_unit64 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([16, 4, 1]).astype(np.uint64) y = np.array([1, 2, 3]).astype(np.uint64) z = x << y # expected output [32, 16, 8] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_left_uint64") ``` #### _right_int8 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([16, 4, 1]).astype(np.int8) y = np.array([1, 2, 3]).astype(np.int8) z = x >> y # expected output [8, 1, 0] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int8") ``` #### _right_int16 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([16, 4, 1]).astype(np.int16) y = np.array([1, 2, 3]).astype(np.int16) z = x >> y # expected output [8, 1, 0] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int16") ``` #### _right_int32 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([16, 4, 1]).astype(np.int32) y = np.array([1, 2, 3]).astype(np.int32) z = x >> y # expected output [8, 1, 0] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int32") ``` #### _right_int64 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([16, 4, 1]).astype(np.int64) y = np.array([1, 2, 3]).astype(np.int64) z = x >> y # expected output [8, 1, 0] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int64") ``` #### _left_int8 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([16, 4, 1]).astype(np.int8) y = np.array([1, 2, 3]).astype(np.int8) z = x << y # expected output [32, 16, 8] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int8") ``` #### _left_int16 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([16, 4, 1]).astype(np.int16) y = np.array([1, 2, 3]).astype(np.int16) z = x << y # expected output [32, 16, 8] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int16") ``` #### _left_int32 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([16, 4, 1]).astype(np.int32) y = np.array([1, 2, 3]).astype(np.int32) z = x << y # expected output [32, 16, 8] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int32") ``` #### _left_int64 ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([16, 4, 1]).astype(np.int64) y = np.array([1, 2, 3]).astype(np.int64) z = x << y # expected output [32, 16, 8] expect(node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int64") ``` #### _right_int8_negative_input ```python import numpy as np import onnx # Right shift of a signed value is an arithmetic shift: the sign bit is # replicated into the vacated high bits, so a negative input stays negative. node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([-8, -1, -128]).astype(np.int8) y = np.array([1, 1, 1]).astype(np.int8) z = x >> y # expected output [-4, -1, -64] expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int8_negative_input", ) ``` #### _right_int32_negative_input ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([-8, -1, -2147483648]).astype(np.int32) y = np.array([1, 1, 1]).astype(np.int32) z = x >> y # expected output [-4, -1, -1073741824] expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int32_negative_input", ) ``` #### _left_int8_overflow ```python import numpy as np import onnx # Bits shifted past the most significant bit are discarded, so the result # wraps within the width of the type rather than being undefined as in C. node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([64, 1, -64]).astype(np.int8) y = np.array([1, 7, 1]).astype(np.int8) z = x << y # expected output [-128, -128, -128] expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int8_overflow" ) ``` #### _left_int32_overflow ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([1073741824, 1, -1073741824]).astype(np.int32) y = np.array([1, 31, 1]).astype(np.int32) z = x << y # expected output [-2147483648, -2147483648, -2147483648] expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int32_overflow" ) ``` #### _right_int8_shift_ge_width ```python import numpy as np import onnx # NumPy saturates a shift by at least the bit width, giving 0, or -1 for a # right shift of a negative value where the sign bit fills the result. C # and most hardware mask the shift count instead, so this is easy to get # wrong (see pytorch/pytorch#70904). node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([-8, 4, -1]).astype(np.int8) y = np.array([8, 9, 127]).astype(np.int8) z = np.array([-1, 0, -1]).astype(np.int8) expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int8_shift_ge_width", ) ``` #### _left_int8_shift_ge_width ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([-8, 4, -1]).astype(np.int8) y = np.array([8, 9, 127]).astype(np.int8) z = np.array([0, 0, 0]).astype(np.int8) expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int8_shift_ge_width", ) ``` #### _right_int32_shift_ge_width ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([-8, 4, -1]).astype(np.int32) y = np.array([32, 33, 100]).astype(np.int32) z = np.array([-1, 0, -1]).astype(np.int32) expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int32_shift_ge_width", ) ``` #### _left_int32_shift_ge_width ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([-8, 4, -1]).astype(np.int32) y = np.array([32, 33, 100]).astype(np.int32) z = np.array([0, 0, 0]).astype(np.int32) expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int32_shift_ge_width", ) ``` #### _right_int8_negative_shift ```python import numpy as np import onnx # A negative shift amount is out of range just as one at or past the bit # width is, and gives the same full-width result: 0, or -1 where an # arithmetic right shift fills the result with the sign bit. Y only reaches # negative values for a signed type, since it shares the type of X. node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([-8, 4, -1]).astype(np.int8) y = np.array([-1, -8, -16]).astype(np.int8) z = np.array([-1, 0, -1]).astype(np.int8) expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int8_negative_shift", ) ``` #### _left_int8_negative_shift ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([-8, 4, -1]).astype(np.int8) y = np.array([-1, -8, -16]).astype(np.int8) z = np.array([0, 0, 0]).astype(np.int8) expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int8_negative_shift", ) ``` #### _right_int32_negative_shift ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="RIGHT" ) x = np.array([-8, 4, -1]).astype(np.int32) y = np.array([-1, -32, -64]).astype(np.int32) z = np.array([-1, 0, -1]).astype(np.int32) expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_right_int32_negative_shift", ) ``` #### _left_int32_negative_shift ```python import numpy as np import onnx node = onnx.helper.make_node( "BitShift", inputs=["x", "y"], outputs=["z"], direction="LEFT" ) x = np.array([-8, 4, -1]).astype(np.int32) y = np.array([-1, -32, -64]).astype(np.int32) z = np.array([0, 0, 0]).astype(np.int32) expect( node, inputs=[x, y], outputs=[z], name="test_bitshift_left_int32_negative_shift", ) ``` ```{toctree} text_diff_BitShift_11_28 ``` (l-onnx-op-bitshift-11)= ## BitShift - 11 ### Version - **name**: [BitShift (GitHub)](https://github.com/onnx/onnx/blob/main/docs/Operators.md#BitShift) - **domain**: `main` - **since_version**: `11` - **function**: `False` - **support_level**: `SupportType.COMMON` - **shape inference**: `True` This version of the operator has been available **since version 11**. ### Summary Bitwise shift operator performs element-wise operation. For each input element, if the attribute "direction" is "RIGHT", this operator moves its binary representation toward the right side so that the input value is effectively decreased. If the attribute "direction" is "LEFT", bits of binary representation moves toward the left side, which results the increase of its actual value. The input X is the tensor to be shifted and another input Y specifies the amounts of shifting. For example, if "direction" is "Right", X is [1, 4], and S is [1, 1], the corresponding output Z would be [0, 2]. If "direction" is "LEFT" with X=[1, 2] and S=[1, 2], the corresponding output Y would be [2, 8]. Because this operator supports Numpy-style broadcasting, X's and Y's shapes are not necessarily identical. This operator supports **multidirectional (i.e., Numpy-style) broadcasting**; for more details please check [Broadcasting in ONNX](https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md). ### Attributes * **direction - STRING** (required) : Direction of moving bits. It can be either "RIGHT" (for right shift) or "LEFT" (for left shift). ### Inputs - **X** (heterogeneous) - **T**: First operand, input to be shifted. - **Y** (heterogeneous) - **T**: Second operand, amounts of shift. ### Outputs - **Z** (heterogeneous) - **T**: Output tensor ### Type Constraints * **T** in ( `tensor(uint16)`, `tensor(uint32)`, `tensor(uint64)`, `tensor(uint8)` ): Constrain input and output types to integer tensors.