BitShift

BitShift - 28

Version

  • name: BitShift (GitHub)

  • 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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",
)

BitShift - 11

Version

  • name: BitShift (GitHub)

  • 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.

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.