Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# LICENSE file in the root directory of this source tree.

import numpy as np
import torch

from executorch.backends.nxp.backend import edge_helper
from executorch.backends.nxp.backend.ir.converter.node_converter import (
Expand Down Expand Up @@ -42,6 +43,12 @@ def _is_supported_in_IR(
case _:
return False # Everything else is unexpected.

supported_types = [torch.int8, torch.uint8]
if not NodeConverter.uses_quantization_type_for_io(
node, supported_types, [0], [0]
):
return False

return True

def convert(self, node: Node):
Expand Down
148 changes: 66 additions & 82 deletions backends/nxp/tests/ir/converter/node_converter/test_neg_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
# LICENSE file in the root directory of this source tree.

import numpy as np

# noinspection PyUnusedImports
import pytest
import torch

from executorch.backends.nxp.backend.edge_program_converter import (
EdgeProgramToIRConverter,
)
from executorch.backends.nxp.tests.executorch_pipeline import to_quantized_edge_program
from executorch.backends.nxp.tests.executors import (
convert_run_compare,
graph_contains_any_of_ops,
ToChannelFirstPreprocess,
ToChannelLastPreprocess,
)
from executorch.exir.dialects._ops import ops as exir_ops
from executorch.backends.nxp.tests.dataset_creator import LinearRampDatasetCreator
from executorch.backends.nxp.tests.executors import graph_contains_any_of_ops
from executorch.backends.nxp.tests.graph_verifier import DetailedGraphVerifier
from executorch.backends.nxp.tests.nsys_testing import lower_run_compare
from executorch.backends.nxp.tests.ops_aliases import Convolution, Neg
from executorch.backends.nxp.tests.use_qat import * # noqa F403


@pytest.fixture(autouse=True)
Expand All @@ -26,11 +26,6 @@ def reseed_model_per_test_run():
np.random.seed(23)


# noinspection PyProtectedMember
ExecutorchDelegateCall = torch.ops.higher_order.executorch_call_delegate
Neg = exir_ops.edge.aten.neg.default


class NegModule(torch.nn.Module):

def __init__(self):
Expand All @@ -45,79 +40,68 @@ class ConvNegModule(torch.nn.Module):

def __init__(self):
super().__init__()
self.conv = torch.nn.Conv2d(3, 3, 1)
self.conv = torch.nn.Conv2d(4, 4, 1)

# noinspection PyMethodMayBeStatic
def forward(self, x):
x = self.conv(x)
return -x


@pytest.mark.parametrize(
"input_shape",
[
pytest.param((8,), id="1D"),
pytest.param((4, 2), id="2D"),
pytest.param((1, 2, 3), id="3D"),
pytest.param((1, 2, 3, 4), id="4D"),
],
)
def test_convert_neg(mocker, input_shape):
model = NegModule()

converter_spy = mocker.spy(EdgeProgramToIRConverter, "convert_program")
delegated_ep = to_quantized_edge_program(model, input_shape).exported_program()

# Make sure the `neg` was delegated.
assert graph_contains_any_of_ops(delegated_ep.graph, [ExecutorchDelegateCall])
assert not graph_contains_any_of_ops(delegated_ep.graph, [Neg])

# Verify correct behavior of the converted NeutronIR model.
intermediate_ep = converter_spy.call_args.args[1]
neutron_ir_model, *_ = converter_spy.spy_return

input_data = (
np.random.random(input_shape).astype(np.float32) * 256.0 - 128.0
).astype(np.int8)

# Make sure the tested program contains the `neg`.
assert graph_contains_any_of_ops(intermediate_ep.graph, [Neg])

convert_run_compare(
intermediate_ep,
tfl_model=neutron_ir_model,
input_data=input_data,
)


def test_convert_neg__channels_last(mocker):
model = ConvNegModule()
input_shape = (1, 3, 4, 5)

converter_spy = mocker.spy(EdgeProgramToIRConverter, "convert_program")
delegated_ep = to_quantized_edge_program(
model, input_shape, use_neutron_for_format_conversion=False
).exported_program()

# Make sure the `neg` was delegated.
assert graph_contains_any_of_ops(delegated_ep.graph, [ExecutorchDelegateCall])
assert not graph_contains_any_of_ops(delegated_ep.graph, [Neg])

# Verify correct behavior of the converted NeutronIR model.
intermediate_ep = converter_spy.call_args.args[1]
neutron_ir_model, *_ = converter_spy.spy_return

input_data = (
np.random.random(input_shape).astype(np.float32) * 256.0 - 128.0
).astype(np.int8)

# Make sure the tested program contains the `neg`.
assert graph_contains_any_of_ops(intermediate_ep.graph, [Neg])

convert_run_compare(
intermediate_ep,
tfl_model=neutron_ir_model,
input_data=input_data,
tflite_input_preprocess=ToChannelLastPreprocess(),
tflite_output_preprocess=ToChannelFirstPreprocess(),
)
class TestNeg:
def test__basic_nsys_inference(self, mocker, request, use_qat):
# Use 256 elements so that, after quantization to int8, the input can
# cover the full discrete range [-128, 127].
# The dataset is generated as a linear float ramp and later quantized,
# which effectively exercises all int8 values.
input_shape = (256,)
model = NegModule()

graph_verifier = DetailedGraphVerifier(
mocker, expected_delegated_ops={Neg: 1}, expected_non_delegated_ops={}
)

converter_spy = mocker.spy(EdgeProgramToIRConverter, "convert_program")

lower_run_compare(
model,
input_shape,
graph_verifier,
request,
dataset_creator=LinearRampDatasetCreator(low=-1.0, high=1.0),
use_qat=use_qat,
)

intermediate_ep = converter_spy.call_args.args[1]

# Make sure the tested program contains the `neg`.
assert graph_contains_any_of_ops(intermediate_ep.graph, [Neg])

def test__channels_first_input(self, mocker, request):
# Use 256 elements so that, after quantization to int8, the input can
# cover the full discrete range [-128, 127].
# The dataset is generated as a linear float ramp and later quantized,
# which effectively exercises all int8 values.
input_shape = (1, 4, 8, 8)
model = ConvNegModule()

graph_verifier = DetailedGraphVerifier(
mocker,
expected_delegated_ops={Neg: 1, Convolution: 1},
expected_non_delegated_ops={},
)

converter_spy = mocker.spy(EdgeProgramToIRConverter, "convert_program")

lower_run_compare(
model,
input_shape,
graph_verifier,
request,
dataset_creator=LinearRampDatasetCreator(low=-1.0, high=1.0),
)

intermediate_ep = converter_spy.call_args.args[1]

# Make sure the tested program contains the `neg`.
assert graph_contains_any_of_ops(intermediate_ep.graph, [Neg])
1 change: 1 addition & 0 deletions backends/nxp/tests/ops_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
MaxPool2DWithIndices = exir_ops.edge.aten.max_pool2d_with_indices.default
MeanDim = exir_ops.edge.aten.mean.dim
MulTensor = exir_ops.edge.aten.mul.Tensor
Neg = exir_ops.edge.aten.neg.default
PermuteCopy = exir_ops.edge.aten.permute_copy.default
QuantizePerChannel = exir_ops.edge.quantized_decomposed.quantize_per_channel.default
QuantizePerTensor = exir_ops.edge.quantized_decomposed.quantize_per_tensor.default
Expand Down
Loading