Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Split-20 - change uneven split behavior to be more torch-like #5321

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
280 changes: 280 additions & 0 deletions onnx/backend/test/case/node/split.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
inputs=[node_input],
outputs=expected_outputs,
name="test_split_equal_parts_1d_opset18",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

split = np.array([2, 4]).astype(np.int64)
Expand All @@ -206,6 +207,7 @@
inputs=[node_input, split],
outputs=expected_outputs,
name="test_split_variable_parts_1d_opset18",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

@staticmethod
Expand All @@ -232,6 +234,7 @@
inputs=[node_input],
outputs=expected_outputs,
name="test_split_equal_parts_2d",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

split = np.array([2, 4]).astype(np.int64)
Expand All @@ -254,6 +257,7 @@
inputs=[node_input, split],
outputs=expected_outputs,
name="test_split_variable_parts_2d_opset18",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

@staticmethod
Expand All @@ -278,6 +282,7 @@
inputs=[node_input],
outputs=expected_outputs,
name="test_split_equal_parts_default_axis_opset18",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

split = np.array([2, 4]).astype(np.int64)
Expand All @@ -294,6 +299,7 @@
inputs=[node_input, split],
outputs=expected_outputs,
name="test_split_variable_parts_default_axis_opset18",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

@staticmethod
Expand All @@ -319,6 +325,7 @@
inputs=[node_input, split],
outputs=expected_outputs,
name="test_split_zero_size_splits_opset18",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

@staticmethod
Expand All @@ -344,6 +351,7 @@
inputs=[node_input],
outputs=expected_outputs,
name="test_split_1d_uneven_split_opset18",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

@staticmethod
Expand Down Expand Up @@ -374,4 +382,276 @@
inputs=[node_input],
outputs=expected_outputs,
name="test_split_2d_uneven_split_opset18",
opset_imports=[onnx.helper.make_opsetid("", 18)],
)

@staticmethod
def export_1d_opset20() -> None:
node_input = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).astype(np.float32)

node = onnx.helper.make_node(
"Split",
inputs=["input"],
outputs=["output_1", "output_2", "output_3"],
axis=0,
num_outputs=3,
)

expected_outputs = [
np.array([1.0, 2.0]).astype(np.float32),
np.array([3.0, 4.0]).astype(np.float32),
np.array([5.0, 6.0]).astype(np.float32),
]
expect(
node,
inputs=[node_input],
outputs=expected_outputs,
name="test_split_equal_parts_1d_opset20",
)

split = np.array([2, 4]).astype(np.int64)
node = onnx.helper.make_node(
"Split",
inputs=["input", "split"],
outputs=["output_1", "output_2"],
axis=0,
)

expected_outputs = [
np.array([1.0, 2.0]).astype(np.float32),
np.array([3.0, 4.0, 5.0, 6.0]).astype(np.float32),
]
expect(
node,
inputs=[node_input, split],
outputs=expected_outputs,
name="test_split_variable_parts_1d_opset20",
)

@staticmethod
def export_2d_opset20() -> None:
node_input = np.array(
[[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [7.0, 8.0, 9.0, 10.0, 11.0, 12.0]]
).astype(np.float32)

node = onnx.helper.make_node(
"Split",
inputs=["input"],
outputs=["output_1", "output_2"],
axis=1,
num_outputs=2,
)

expected_outputs = [
np.array([[1.0, 2.0, 3.0], [7.0, 8.0, 9.0]]).astype(np.float32),
np.array([[4.0, 5.0, 6.0], [10.0, 11.0, 12.0]]).astype(np.float32),
]

expect(
node,
inputs=[node_input],
outputs=expected_outputs,
name="test_split_equal_parts_2d_opset20",
)

split = np.array([2, 4]).astype(np.int64)
node = onnx.helper.make_node(
"Split",
inputs=["input", "split"],
outputs=["output_1", "output_2"],
axis=1,
)

expected_outputs = [
np.array([[1.0, 2.0], [7.0, 8.0]]).astype(np.float32),
np.array([[3.0, 4.0, 5.0, 6.0], [9.0, 10.0, 11.0, 12.0]]).astype(
np.float32
),
]

expect(
node,
inputs=[node_input, split],
outputs=expected_outputs,
name="test_split_variable_parts_2d_opset20",
)

@staticmethod
def export_default_values_opset20() -> None:
node_input = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).astype(np.float32)

# If axis is not specified, split is applied on default axis 0
node = onnx.helper.make_node(
"Split",
inputs=["input"],
outputs=["output_1", "output_2", "output_3"],
num_outputs=3,
)

expected_outputs = [
np.array([1.0, 2.0]).astype(np.float32),
np.array([3.0, 4.0]).astype(np.float32),
np.array([5.0, 6.0]).astype(np.float32),
]
expect(
node,
inputs=[node_input],
outputs=expected_outputs,
name="test_split_equal_parts_default_axis_opset20",
)

split = np.array([2, 4]).astype(np.int64)
node = onnx.helper.make_node(
"Split", inputs=["input", "split"], outputs=["output_1", "output_2"]
)

expected_outputs = [
np.array([1.0, 2.0]).astype(np.float32),
np.array([3.0, 4.0, 5.0, 6.0]).astype(np.float32),
]
expect(
node,
inputs=[node_input, split],
outputs=expected_outputs,
name="test_split_variable_parts_default_axis_opset20",
)

@staticmethod
def export_zero_size_splits_opset20() -> None:
# 1-dimensional tensor with dimension_size=0
node_input = np.array([]).astype(np.float32)

# Split emtpy tensor to tensors of size zero

Check warning on line 524 in onnx/backend/test/case/node/split.py

View workflow job for this annotation

GitHub Actions / Optional Lint

[misspell] reported by reviewdog 🐶 "emtpy" is a misspelling of "empty" Raw Output: ./onnx/backend/test/case/node/split.py:524:16: "emtpy" is a misspelling of "empty"
split = np.array([0, 0, 0]).astype(np.int64)
node = onnx.helper.make_node(
"Split",
inputs=["input", "split"],
outputs=["output_1", "output_2", "output_3"],
)

expected_outputs = [
np.array([]).astype(np.float32),
np.array([]).astype(np.float32),
np.array([]).astype(np.float32),
]
expect(
node,
inputs=[node_input, split],
outputs=expected_outputs,
name="test_split_zero_size_splits_opset20",
)

@staticmethod
def export_1d_uneven_split_opset20() -> None:
node_input = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]).astype(np.float32)

# If axis is not specified, split is applied on default axis 0
node = onnx.helper.make_node(
"Split",
inputs=["input"],
outputs=["output_1", "output_2", "output_3", "output_4"],
num_outputs=4,
)

expected_outputs = [
np.array([1.0, 2.0]).astype(np.float32),
np.array([3.0, 4.0]).astype(np.float32),
np.array([5.0, 6.0]).astype(np.float32),
np.array([7.0]).astype(np.float32),
]
expect(
node,
inputs=[node_input],
outputs=expected_outputs,
name="test_split_1d_uneven_split_opset20",
)

@staticmethod
def export_2d_uneven_split_1_opset20() -> None:
node_input = np.array(
[
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
[9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0],
]
).astype(np.float32)

node = onnx.helper.make_node(
"Split",
inputs=["input"],
outputs=["output_1", "output_2", "output_3"],
axis=1,
num_outputs=3,
)

expected_outputs = [
np.array([[1.0, 2.0, 3.0], [9.0, 10.0, 11.0]]).astype(np.float32),
np.array([[4.0, 5.0, 6.0], [12.0, 13.0, 14.0]]).astype(np.float32),
np.array([[7.0, 8.0], [15.0, 16.0]]).astype(np.float32),
]

expect(
node,
inputs=[node_input],
outputs=expected_outputs,
name="test_split_2d_uneven_split_opset20",
)

@staticmethod
def export_2d_uneven_split_2_opset20() -> None:
node_input = np.array(
[
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0],
[8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0],
]
).astype(np.float32)

node = onnx.helper.make_node(
"Split",
inputs=["input"],
outputs=["output_1", "output_2", "output_3"],
axis=1,
num_outputs=3,
)

expected_outputs = [
np.array([[1.0, 2.0, 3.0], [8.0, 9.0, 1.0]]).astype(np.float32),
np.array([[4.0, 5.0], [11.0, 12.0]]).astype(np.float32),
np.array([[6.0, 7.0], [13.0, 14.0]]).astype(np.float32),
]

expect(
node,
inputs=[node_input],
outputs=expected_outputs,
name="test_split_2d_uneven_split_2_opset20",
)

@staticmethod
def export_2d_uneven_split_3_opset20() -> None:
node_input = np.array(
[
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0],
[8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0],
]
).astype(np.float32)

node = onnx.helper.make_node(
"Split",
inputs=["input"],
outputs=["output_1", "output_2", "output_3"],
axis=0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of changing the axis, it would make sense to change the new attribute introduced minimize_diff or whatever it is called. At least, I am looking for some test-case to show the difference that attribute makes, whether that is this test or some other test.

num_outputs=3,
)

expected_outputs = [
np.array([[1.0, 2.0, 3.0], [8.0, 9.0, 1.0]]).astype(np.float32),
np.array([[4.0, 5.0], [11.0, 12.0]]).astype(np.float32),
np.array([[6.0, 7.0], [13.0, 14.0]]).astype(np.float32),
]

expect(
node,
inputs=[node_input],
outputs=expected_outputs,
name="test_split_2d_uneven_split_2_opset20",
)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion onnx/common/interned_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ namespace ONNX_NAMESPACE {
_(ScatterElements) \
_(Resize) \
_(ceil_mode) \
_(num_outputs)
_(num_outputs) \
_(minimize_diff)

enum BuiltinSymbol {
#define DEFINE_SYMBOL(s) k##s,
Expand Down
2 changes: 2 additions & 0 deletions onnx/defs/operator_sets.h
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,7 @@ class ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, StringSplit);
class ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, ImageDecoder);
class ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, IsInf);
class ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, IsNaN);
class ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, Split);

// Iterate over schema from ai.onnx version 20
class OpSet_Onnx_ver20 {
Expand All @@ -1131,6 +1132,7 @@ class OpSet_Onnx_ver20 {
fn(GetOpSchema<ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, ImageDecoder)>());
fn(GetOpSchema<ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, IsInf)>());
fn(GetOpSchema<ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, IsNaN)>());
fn(GetOpSchema<ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, Split)>());
}
};

Expand Down