5cbd3f29e3
Fuzz / Run fuzz harnesses (${{ github.event_name == 'schedule' && 'nightly' || 'smoke' }}) (push) Has been cancelled
Create Releases / call-mac (push) Has been cancelled
Create Releases / call-linux (push) Has been cancelled
Create Releases / call-sdist (push) Has been cancelled
Create Releases / call-win (push) Has been cancelled
Create Releases / call-pyodide (push) Has been cancelled
Windows_No_Exception_CI / build (x64, 3.10) (push) Has been cancelled
Check URLs / build (push) Has been cancelled
Create Releases / Attest CI build artifacts (push) Has been cancelled
Create Releases / Check for Publish release build to pypi (push) Has been cancelled
Create Releases / Check for Publish preview build to test.pypi-weekly (push) Has been cancelled
Create Releases / Publish preview build to test.pypi-weekly (push) Has been cancelled
Create Releases / Check for Publish release build to test.pypi (rc-candidates) (push) Has been cancelled
Create Releases / Publish release build to test.pypi (push) Has been cancelled
Create Releases / Check for Publish preview build to pypi-weekly (push) Has been cancelled
Create Releases / Publish preview build to pypi-weekly (push) Has been cancelled
Create Releases / Publish release build to pypi (push) Has been cancelled
Create Releases / test source distribution (push) Has been cancelled
clang-tidy / clang-tidy (push) Has been cancelled
Lint / Validate SBOM (push) Has been cancelled
Lint / Enforce style (push) Has been cancelled
CI / Test windows-2022, 3.14, External, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test windows-latest, 3.10, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test windows-latest, 3.14, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test windows-latest, 3.14t, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test ubuntu-24.04, 3.14, Internal, debug=1, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test ubuntu-24.04, 3.14, External, debug=0, unity_build=1, onnx_ml=1, autogen=1 (push) Has been cancelled
CI / Test ubuntu-24.04, 3.14, External, debug=0, unity_build=0, onnx_ml=0, autogen=0 (push) Has been cancelled
CI / Test macos-latest, 3.10, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test macos-latest, 3.14, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test macos-latest, 3.14t, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test ubuntu-24.04, 3.14, External, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test ubuntu-24.04, 3.10, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test ubuntu-24.04, 3.14, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
CI / Test ubuntu-24.04, 3.14t, Internal, debug=0, unity_build=0, onnx_ml=1, autogen=0 (push) Has been cancelled
Pixi CI / Install and lint (ubuntu-24.04-arm) (push) Has been cancelled
Pixi CI / Install and lint (windows-2022) (push) Has been cancelled
Pixi CI / Xcode generator build (push) Has been cancelled
Pixi CI / Install and test (macos-latest, default) (push) Has been cancelled
Pixi CI / Install and test (ubuntu-24.04-arm, default) (push) Has been cancelled
Pixi CI / Install and test (ubuntu-latest, default) (push) Has been cancelled
Pixi CI / Install and test (windows-2022, default) (push) Has been cancelled
Pixi CI / Install and test (macos-latest, oldies) (push) Has been cancelled
Pixi CI / Install and test (ubuntu-24.04-arm, oldies) (push) Has been cancelled
Pixi CI / Install and test (ubuntu-latest, oldies) (push) Has been cancelled
Pixi CI / Install and test (windows-2022, oldies) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled
Generate and publish ONNX docs / build (push) Has been cancelled
Generate and publish ONNX docs / deploy (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
500 lines
13 KiB
Python
500 lines
13 KiB
Python
# Copyright (c) ONNX Project Contributors
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
import onnx
|
|
from onnx import TensorProto
|
|
from onnx.backend.test.case.base import Base
|
|
from onnx.backend.test.case.node import expect
|
|
from onnx.helper import make_tensor
|
|
|
|
|
|
class QuantizeLinear(Base):
|
|
@staticmethod
|
|
def export() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
)
|
|
|
|
x = np.array([0, 2, 3, 1000, -254, -1000]).astype(np.float32)
|
|
y_scale = np.float32(2)
|
|
y_zero_point = np.uint8(128)
|
|
y = np.array([128, 129, 130, 255, 1, 0]).astype(np.uint8)
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_axis() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
[
|
|
[[-162, 10], [-100, 232], [-20, -50]],
|
|
[[-76, 0], [0, 252], [32, -44]],
|
|
[[245, -485], [-960, -270], [-375, -470]],
|
|
],
|
|
],
|
|
dtype=np.float32,
|
|
)
|
|
y_scale = np.array([2, 4, 5], dtype=np.float32)
|
|
y_zero_point = np.array([84, 24, 196], dtype=np.uint8)
|
|
y = (x / y_scale.reshape(1, 3, 1, 1) + y_zero_point.reshape(1, 3, 1, 1)).astype(
|
|
np.uint8
|
|
)
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_axis",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_e4m3fn() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
)
|
|
|
|
x = np.array([0.0, 1.0, 2.0, 100000.0, 200.0]).astype(np.float32)
|
|
y_scale = np.float32(2)
|
|
y_zero_point = make_tensor("y_zero_point", TensorProto.FLOAT8E4M3FN, [1], [0])
|
|
y = make_tensor("y", TensorProto.FLOAT8E4M3FN, [5], [0, 0.5, 1, 448, 96])
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_e4m3fn",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_e5m2() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
)
|
|
|
|
x = np.array([0.0, 1.0, 2.0, 100000.0, 200.0]).astype(np.float32)
|
|
y_scale = np.float32(2)
|
|
y_zero_point = make_tensor("y_zero_point", TensorProto.FLOAT8E5M2, [1], [0.0])
|
|
y = make_tensor("y", TensorProto.FLOAT8E5M2, [5], [0, 0.5, 1, 49152, 96])
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_e5m2",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_uint16() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
0.0,
|
|
-128.0,
|
|
3.0,
|
|
-3.0,
|
|
2.9,
|
|
-2.9,
|
|
3.1,
|
|
-3.1,
|
|
65536.0,
|
|
-65534.0,
|
|
70000.0,
|
|
-70000.0,
|
|
]
|
|
).astype(np.float32)
|
|
y_scale = np.float32(2.0)
|
|
y_zero_point = np.uint16(32767)
|
|
y = np.array(
|
|
[
|
|
32767,
|
|
32703,
|
|
32769,
|
|
32765,
|
|
32768,
|
|
32766,
|
|
32769,
|
|
32765,
|
|
65535,
|
|
0,
|
|
65535,
|
|
0,
|
|
]
|
|
).astype(np.uint16)
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_uint16",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_int16() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
0.0,
|
|
-514.0,
|
|
3.0,
|
|
-3.0,
|
|
2.9,
|
|
-2.9,
|
|
3.1,
|
|
-3.1,
|
|
65022.0,
|
|
-66046.0,
|
|
65023.0,
|
|
-66047.0,
|
|
65024.0,
|
|
-66048.0,
|
|
70000.0,
|
|
-70000.0,
|
|
]
|
|
).astype(np.float32)
|
|
y_scale = np.float32(2.0)
|
|
y_zero_point = np.int16(256)
|
|
y = np.array(
|
|
[
|
|
256,
|
|
-1,
|
|
258,
|
|
254,
|
|
257,
|
|
255,
|
|
258,
|
|
254,
|
|
32767,
|
|
-32767,
|
|
32767,
|
|
-32768,
|
|
32767,
|
|
-32768,
|
|
32767,
|
|
-32768,
|
|
]
|
|
).astype(np.int16)
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_int16",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_uint4() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
axis=0,
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
[0.0, 2.5, 4.8, 8.6],
|
|
[-30, -20, 6, 9],
|
|
[12, 15, 16, 40],
|
|
]
|
|
).astype(np.float32)
|
|
|
|
y_scale = np.asarray([2.0, 3.0, 4.0], dtype=np.float32)
|
|
y_zero_point = make_tensor(
|
|
"y_zero_point", TensorProto.UINT4, y_scale.shape, np.ones_like(y_scale)
|
|
)
|
|
y = make_tensor(
|
|
"y", TensorProto.UINT4, x.shape, [1, 2, 3, 5, 0, 0, 3, 4, 4, 5, 5, 11]
|
|
)
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_uint4",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_int4() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
axis=0,
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
[0.0, 2.5, 4.8, 8.6],
|
|
[-30, -20, 6, 9],
|
|
[12, 15, 16, 40],
|
|
]
|
|
).astype(np.float32)
|
|
|
|
y_scale = np.asarray([2.0, 3.0, 4.0], dtype=np.float32)
|
|
y_zero_point = make_tensor(
|
|
"y_zero_point", TensorProto.INT4, y_scale.shape, np.ones_like(y_scale)
|
|
)
|
|
y = make_tensor(
|
|
"y", TensorProto.INT4, x.shape, [1, 2, 3, 5, -8, -6, 3, 4, 4, 5, 5, 7]
|
|
)
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_int4",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_uint2() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
axis=0,
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
[0.0, 2.5, 4.8, 8.6],
|
|
[-2.0, -1.0, 1.0, 3.0],
|
|
[4.0, 5.0, 6.0, 7.0],
|
|
],
|
|
dtype=np.float32,
|
|
)
|
|
y_scale = np.asarray([2.0, 3.0, 4.0], dtype=np.float32)
|
|
y_zero_point = make_tensor(
|
|
"y_zero_point", TensorProto.UINT2, y_scale.shape, np.zeros_like(y_scale)
|
|
)
|
|
y = make_tensor(
|
|
"y", TensorProto.UINT2, x.shape, [0, 1, 2, 3, 0, 0, 0, 1, 1, 1, 2, 2]
|
|
)
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_uint2",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_int2() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
axis=0,
|
|
)
|
|
x = np.array(
|
|
[
|
|
[0.0, 2.5, 4.8, 8.6],
|
|
[-4.0, -3.0, 1.0, 2.0],
|
|
[-0.0, -2.5, -4.8, -8.6],
|
|
],
|
|
dtype=np.float32,
|
|
)
|
|
y_scale = np.asarray([2.0, 3.0, 4.0], dtype=np.float32)
|
|
y_zero_point = make_tensor(
|
|
"y_zero_point", TensorProto.INT2, y_scale.shape, np.zeros_like(y_scale)
|
|
)
|
|
y = make_tensor(
|
|
"y", TensorProto.INT2, x.shape, [0, 1, 1, 1, -1, -1, 0, 1, 0, -1, -1, -2]
|
|
)
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_int2",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_float4e2m1() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
axis=0,
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
[0.0, 2.5, 4.8, 8.6],
|
|
[-30, -20, 6, 9],
|
|
[-0.0, -2.5, -4.8, -8.6],
|
|
]
|
|
).astype(np.float32)
|
|
|
|
y_scale = np.asarray([2.0, 3.0, 4.0], dtype=np.float32)
|
|
y_zero_point = make_tensor(
|
|
"y_zero_point",
|
|
TensorProto.FLOAT4E2M1,
|
|
y_scale.shape,
|
|
np.zeros_like(y_scale),
|
|
)
|
|
y = make_tensor(
|
|
"y",
|
|
TensorProto.FLOAT4E2M1,
|
|
x.shape,
|
|
[0, 1, 2, 4, -6, -6, 2, 3, 0, -0.5, -1, -2],
|
|
)
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_float4e2m1",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_blocked_asymmetric() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale", "y_zero_point"],
|
|
outputs=["y"],
|
|
axis=1,
|
|
block_size=2,
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
[6.0, 12.0, 50.0, 5.0],
|
|
[1.0, 8.0, 4.0, 5.0],
|
|
[0.0, 20.0, 10.0, 4.0],
|
|
],
|
|
dtype=np.float32,
|
|
)
|
|
y_scale = np.array(
|
|
[
|
|
[1.5, 2.5],
|
|
[3.0, 4.9],
|
|
[5.1, 6.9],
|
|
],
|
|
dtype=np.float32,
|
|
)
|
|
y_zero_point = np.array(
|
|
[
|
|
[0, 1],
|
|
[1, 0],
|
|
[2, 3],
|
|
],
|
|
dtype=np.uint8,
|
|
)
|
|
# x.shape = (3, 4)
|
|
# y_scale.shape = (3, 2)
|
|
assert y_scale.shape == y_zero_point.shape
|
|
block_axis = 1
|
|
# The block shape is [x.shape[i] // y_scale.shape[i] for i in range(len(x.shape))] = (1, 2)
|
|
assert all(
|
|
x.shape[i] == y_scale.shape[i]
|
|
for i in range(len(x.shape))
|
|
if i != block_axis
|
|
)
|
|
assert x.shape[block_axis] % y_scale.shape[block_axis] == 0
|
|
repeats = x.shape[block_axis] // y_scale.shape[block_axis]
|
|
|
|
# Create element-wise scale and zero point
|
|
y_scale_elementwise = np.repeat(y_scale, repeats=repeats, axis=block_axis)
|
|
y_zero_point_elementwise = np.repeat(
|
|
y_zero_point, repeats=repeats, axis=block_axis
|
|
)
|
|
|
|
y = np.rint(x / y_scale_elementwise + y_zero_point_elementwise).astype(np.uint8)
|
|
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale, y_zero_point],
|
|
outputs=[y],
|
|
name="test_quantizelinear_blocked_asymmetric",
|
|
)
|
|
|
|
@staticmethod
|
|
def export_blocked_symmetric() -> None:
|
|
node = onnx.helper.make_node(
|
|
"QuantizeLinear",
|
|
inputs=["x", "y_scale"],
|
|
outputs=["y"],
|
|
axis=1,
|
|
block_size=2,
|
|
output_dtype=TensorProto.INT16,
|
|
)
|
|
|
|
x = np.array(
|
|
[
|
|
[6.0, -8, -10, 5.0],
|
|
[1.0, 8.0, 4.0, 5.0],
|
|
[0.0, 20.0, 10.0, 4.0],
|
|
],
|
|
dtype=np.float32,
|
|
)
|
|
|
|
y_scale = np.array(
|
|
[
|
|
[1.5, 2.5],
|
|
[3.0, 4.9],
|
|
[5.1, 6.9],
|
|
],
|
|
dtype=np.float32,
|
|
)
|
|
|
|
# x.shape = (3, 4)
|
|
# y_scale.shape = (3, 2)
|
|
|
|
block_axis = 1
|
|
# The block shape is [x.shape[i] // y_scale.shape[i] for i in range(len(x.shape))] = (1, 2)
|
|
assert all(
|
|
x.shape[i] == y_scale.shape[i]
|
|
for i in range(len(x.shape))
|
|
if i != block_axis
|
|
)
|
|
assert x.shape[block_axis] % y_scale.shape[block_axis] == 0
|
|
repeats = x.shape[block_axis] // y_scale.shape[block_axis]
|
|
|
|
# Create element-wise scale and zero point
|
|
y_scale_elementwise = np.repeat(y_scale, repeats=repeats, axis=block_axis)
|
|
|
|
y_val = np.clip(
|
|
np.rint(x / y_scale_elementwise), a_min=-32768, a_max=32767
|
|
).astype(np.int16)
|
|
y = make_tensor(
|
|
"y",
|
|
TensorProto.INT16,
|
|
x.shape,
|
|
y_val,
|
|
)
|
|
expect(
|
|
node,
|
|
inputs=[x, y_scale],
|
|
outputs=[y],
|
|
name="test_quantizelinear_blocked_symmetric",
|
|
)
|