Pack¶
Pack - 29¶
Version¶
name: Pack (GitHub)
domain:
mainsince_version:
29function:
Truesupport_level:
SupportType.COMMONshape inference:
True
This version of the operator has been available since version 29.
Summary¶
Packs UINT8 codes into UINT8 (default) or UINT32 words along axis (default -1).
Input and output have the same rank, at least 1; axis must be in [-rank, rank-1].
The output_type selects storage words of word_bits = 8 or 32 bits.
Specify either bits in [1, 8], or both radix and group_size;
the two spellings are mutually exclusive. With bits, each element of X
must be in [0, 2^bits - 1].
When using bits, an input axis dimension N becomes B = ceil(N * bits / word_bits).
Each fixed combination of indices in the other dimensions defines an
independent sequence, called a row below; a rank-1 tensor has one row.
Within each row, codes are packed in increasing axis index order, with
the least significant bit of each code first. Number the bits of each code
and storage word starting at 0 for the least significant bit. For code index
0 <= i < N and bit index 0 <= k < bits, bit k of input code i is stored
at bit (i * bits + k) % word_bits of output word floor((i * bits + k) / word_bits).
When a code spans a word boundary, its remaining higher-order bits occupy
the low bits of the next word. Each row begins at bit 0 of a new word.
Unused high bits in the final word of each row are zero. Bit positions
are defined numerically, independently of host endianness; tensor raw data
uses ONNX’s little-endian serialization.
All non-axis dimensions are preserved, including zero dimensions, and the
same output shape rule applies when any non-axis dimension is zero.
An input with N = 0 produces an output with B = 0.
With default UINT8 storage, bits = 3 packs [0, 1, 2, 3, 4, 5, 6, 7] into
[136, 198, 250]. With bits = 5, the rows [[1, 2, 3], [31, 0, 16]]
are packed into [[65, 12], [31, 64]].
With UINT32 storage, bits = 5 packs [1, 2, 3] into [3137].
Grouped packing uses radix R in [2, 256] and positive group size G, with R^G <= 2^63 - 1. Each input value must be in [0, R - 1]. Divide each row into consecutive groups of G values; zero-pad the last group if incomplete. Group [x0, …, x(G-1)] becomes the integer sum(xj * R^j): the first digit is least significant. Each group index occupies W = ceil(log2(R^G)) bits. Pack these indices contiguously, least-significant bit first, with no word alignment between groups. The output axis dimension is ceil(ceil(N/G) * W / word_bits). Unused final word bits are zero. R^G determines the precision of the integer arithmetic needed to form and decode group indices: indices range from 0 to R^G - 1 and require W value bits. Implementations must also accommodate intermediate values, including R^G if computed explicitly. Prefer (R, G) pairs that can be implemented using standard 32-bit or 64-bit integer arithmetic rather than requiring arbitrary-precision arithmetic. This specification’s R^G bound permits exact signed 64-bit arithmetic for the base-radix group calculations. One commonly used choice is (R, G) = (3, 5), packing five ternary codes into one byte because 3^5 = 243. The original bits spelling is exactly (R, G) = (2^bits, 1). With UINT8 storage and R=3, G=5, [0, 1, 2, 0, 1] becomes [102], and [2, 2, 2, 2, 2] becomes [242]. Partial group [1, 2] becomes [7]. Results for invalid inputs are unspecified; function decompositions need not diagnose out-of-range codes.
Attributes¶
axis - INT (default is
-1):Axis along which independent sequences are packed. Negative axes are supported.
bits - INT :
Number of bits per unsigned code. Mutually exclusive with radix/group_size.
group_size - INT :
Codes per group. Positive; radix^group_size must fit in positive INT64.
output_type - INT (default is
2):Packed storage type: TensorProto UINT8 (2) or UINT32 (12). Logical codes remain UINT8.
radix - INT :
Base of grouped codes, in [2, 256]. Requires group_size; excludes bits.
Inputs¶
X (heterogeneous) - T:
UINT8 tensor of rank at least 1, with codes below the selected radix.
Outputs¶
Y (heterogeneous) - TStorage:
Packed tensor preserving every dimension except axis, which becomes ceil(ceil(N / group_size) * group_width / word_bits); bits implies group_size=1.
Type Constraints¶
T in (
tensor(uint8)):Unsigned byte tensors.
TStorage in (
tensor(uint32),tensor(uint8)):Unsigned storage word tensors.
Examples¶
_pack_uint32_axis¶
import numpy as np
import onnx
node = helper.make_node(
"Pack", ["X"], ["Y"], bits=5, axis=0, output_type=TensorProto.UINT32
)
x = np.full((7, 2), 31, dtype=np.uint8)
y = np.array([[0xFFFFFFFF, 0xFFFFFFFF], [7, 7]], dtype=np.uint32)
expect(node, inputs=[x], outputs=[y], name="test_pack_uint32_axis")
_pack_ternary¶
import numpy as np
import onnx
node = helper.make_node("Pack", ["X"], ["Y"], radix=3, group_size=5)
x = np.array([[0, 1, 2, 0, 1], [2, 2, 2, 2, 2]], dtype=np.uint8)
y = np.array([[102], [242]], dtype=np.uint8)
expect(node, inputs=[x], outputs=[y], name="test_pack_ternary")
_pack_3bit¶
import numpy as np
import onnx
node = helper.make_node("Pack", ["X"], ["Y"], bits=3)
x = np.arange(8, dtype=np.uint8)
y = np.array([0x88, 0xC6, 0xFA], dtype=np.uint8)
expect(node, inputs=[x], outputs=[y], name="test_pack_3bit")
_pack_rows_5bit¶
import numpy as np
import onnx
node = helper.make_node("Pack", ["X"], ["Y"], bits=5)
x = np.array([[1, 2, 3], [31, 0, 16]], dtype=np.uint8)
y = np.array([[0x41, 0x0C], [0x1F, 0x40]], dtype=np.uint8)
expect(node, inputs=[x], outputs=[y], name="test_pack_rows_5bit")
_pack_empty¶
import numpy as np
import onnx
node = helper.make_node("Pack", ["X"], ["Y"], bits=3)
x = np.empty((2, 0), dtype=np.uint8)
expect(node, inputs=[x], outputs=[x], name="test_pack_empty")
_pack_empty_rows¶
import numpy as np
import onnx
node = helper.make_node("Pack", ["X"], ["Y"], bits=5)
x = np.empty((2, 0, 9), dtype=np.uint8)
y = np.empty((2, 0, 6), dtype=np.uint8)
expect(node, inputs=[x], outputs=[y], name="test_pack_empty_rows")