|
9 | 9 |
|
10 | 10 | import pytest |
11 | 11 | import torch |
| 12 | +from executorch.backends.arm._passes import ComputeConstantOpsAOTPass |
12 | 13 | from executorch.backends.cortex_m.passes.cortex_m_pass_manager import ( |
13 | 14 | CortexMPassManager, |
14 | 15 | LiftConstantTensorsPass, |
@@ -552,14 +553,100 @@ def test_convs_around_a_reshape_behind_lut_activation_lower_to_cmsis_nn(): |
552 | 553 | tester.run_method_and_compare_outputs(inputs=inputs, qtol=1) |
553 | 554 |
|
554 | 555 |
|
555 | | -def test_explicit_layout_rejects_float_input_to_int8_conv(): |
| 556 | +def test_explicit_layout_lowers_conv_on_a_quantized_constant(): |
| 557 | + inputs = (torch.randn(1, 8, 16),) |
556 | 558 | model = ThenConv1d(lambda x: torch.full_like(x, 0.5, dtype=torch.float32)) |
557 | | - tester = CortexMTester(model.eval(), (torch.randn(1, 8, 16),)) |
| 559 | + tester = _run_explicit_layout_passes(CortexMTester(model.eval(), inputs)) |
| 560 | + program = tester.get_artifact(StageType.RUN_PASSES).exported_program() |
| 561 | + |
| 562 | + assert _count(program, exir_ops.edge.aten.full_like.default) == 0 |
| 563 | + assert _count(program, exir_ops.edge.aten.full.default) == 1 |
| 564 | + assert _count(program, exir_ops.edge.cortex_m.quantized_conv2d_nhwc.default) == 1 |
| 565 | + tester.run_method_and_compare_outputs(inputs=inputs, qtol=1) |
| 566 | + |
| 567 | + |
| 568 | +@pytest.mark.parametrize("use_explicit_layout", [False, True]) |
| 569 | +@pytest.mark.parametrize( |
| 570 | + "constant,constant_op,shape", |
| 571 | + [ |
| 572 | + ( |
| 573 | + lambda: torch.arange(-1.0, 1.0, 0.125), |
| 574 | + exir_ops.edge.aten.arange.start_step, |
| 575 | + (16,), |
| 576 | + ), |
| 577 | + ( |
| 578 | + lambda: torch.scalar_tensor(0.5), |
| 579 | + exir_ops.edge.aten.scalar_tensor.default, |
| 580 | + (), |
| 581 | + ), |
| 582 | + ], |
| 583 | + ids=["arange", "scalar_tensor"], |
| 584 | +) |
| 585 | +def test_quantized_constant_is_computed_before_quantizing( |
| 586 | + use_explicit_layout, constant, constant_op, shape |
| 587 | +): |
| 588 | + torch.manual_seed(0) |
| 589 | + inputs = (torch.randn(shape),) |
| 590 | + tester = CortexMTester(Apply(lambda x: x + constant()).eval(), inputs) |
| 591 | + tester.quantize(Quantize(CortexMQuantizer(use_explicit_layout=use_explicit_layout))) |
| 592 | + tester.export().to_edge() |
| 593 | + program = _run_passes(tester, use_explicit_layout) |
| 594 | + |
| 595 | + assert _count(program, constant_op) == 0 |
| 596 | + assert _count(program, exir_ops.edge.cortex_m.quantized_add.default) == 1 |
| 597 | + tester.run_method_and_compare_outputs(inputs=inputs, qtol=1) |
| 598 | + |
| 599 | + |
| 600 | +@pytest.mark.parametrize("use_explicit_layout", [False, True]) |
| 601 | +@pytest.mark.parametrize( |
| 602 | + "fill", |
| 603 | + [lambda x: torch.full((1, 8, 16), 0.5), lambda x: torch.full_like(x, 0.5)], |
| 604 | + ids=["full", "full_like"], |
| 605 | +) |
| 606 | +def test_quantized_fill_stays_a_runtime_int8_full(use_explicit_layout, fill): |
| 607 | + torch.manual_seed(0) |
| 608 | + inputs = (torch.randn(1, 8, 16),) |
| 609 | + tester = CortexMTester(Apply(lambda x: x + fill(x)).eval(), inputs) |
| 610 | + tester.quantize(Quantize(CortexMQuantizer(use_explicit_layout=use_explicit_layout))) |
| 611 | + tester.export().to_edge() |
| 612 | + program = _run_passes(tester, use_explicit_layout) |
| 613 | + |
| 614 | + (full,) = program.graph.find_nodes( |
| 615 | + op="call_function", target=exir_ops.edge.aten.full.default |
| 616 | + ) |
| 617 | + assert full.meta["val"].dtype == torch.int8 |
| 618 | + assert _count(program, exir_ops.edge.aten.full_like.default) == 0 |
| 619 | + assert _count(program, exir_ops.edge.cortex_m.quantized_add.default) == 1 |
| 620 | + tester.run_method_and_compare_outputs(inputs=inputs, qtol=1) |
| 621 | + |
| 622 | + |
| 623 | +def test_failed_constant_precompute_is_an_error(monkeypatch): |
| 624 | + def fail(self, node): |
| 625 | + raise ValueError("cannot evaluate") |
| 626 | + |
| 627 | + monkeypatch.setattr(ComputeConstantOpsAOTPass, "compute_node_aot", fail) |
| 628 | + tester = CortexMTester( |
| 629 | + Apply(lambda x: x + torch.arange(-1.0, 1.0, 0.125)).eval(), |
| 630 | + (torch.randn(16),), |
| 631 | + ) |
558 | 632 |
|
559 | 633 | with pytest.raises(Exception) as caught: |
560 | 634 | _run_explicit_layout_passes(tester) |
561 | 635 |
|
562 | 636 | error = caught.value |
563 | 637 | while error.__cause__ is not None: |
564 | 638 | error = error.__cause__ |
565 | | - assert "must move an int8 tensor" in str(error) |
| 639 | + assert "Could not precompute quantized constant ops" in str(error) |
| 640 | + |
| 641 | + |
| 642 | +@pytest.mark.parametrize("use_explicit_layout", [False, True]) |
| 643 | +def test_unquantized_constant_is_not_precomputed(use_explicit_layout): |
| 644 | + inputs = (torch.randn(1, 8, 16),) |
| 645 | + tester = CortexMTester( |
| 646 | + Apply(lambda x: x + torch.full((1, 8, 16), 0.5)).eval(), inputs |
| 647 | + ) |
| 648 | + tester.export().to_edge() |
| 649 | + program = _run_passes(tester, use_explicit_layout) |
| 650 | + |
| 651 | + assert _count(program, exir_ops.edge.aten.full.default) == 1 |
| 652 | + tester.run_method_and_compare_outputs(inputs=inputs) |
0 commit comments