-
Notifications
You must be signed in to change notification settings - Fork 851
Add sym_float and trunc support for dynamic float-scale interpolate #2843
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3310,6 +3310,129 @@ def forward(self, args): | |
| if layer.WhichOneof("layer") == "upsample": | ||
| assert len(layer.upsample.fractionalScalingFactor) == 0 | ||
|
|
||
| @staticmethod | ||
| def _xfail_if_torch_export_rejects_trunc(export_fn): | ||
| """ | ||
| ``F.interpolate(..., scale_factor=float, recompute_scale_factor=True)`` | ||
| over a dynamic shape decomposes into ``sym_float -> mul -> trunc`` nodes. | ||
| Some torch versions reject ``trunc`` in their export verifier | ||
| (SpecViolationError); the coremltools side of this lowering cannot be | ||
| exercised then, so skip instead of failing. | ||
| """ | ||
| try: | ||
| return export_fn() | ||
| except Exception as e: # noqa: BLE001 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need the |
||
| msg = str(e) | ||
| if "trunc" in msg or "SpecViolationError" in msg: | ||
| pytest.xfail( | ||
| "torch.export verifier rejects the trunc node on this torch version" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does it reject some of these models? |
||
| ) | ||
| raise | ||
|
|
||
| @pytest.mark.parametrize( | ||
| "compute_unit, backend, frontend", | ||
| itertools.product(compute_units, backends, TORCH_EXPORT_BASED_FRONTENDS), | ||
| ) | ||
| def test_interpolate_nearest2d_with_float_scale_dynamic( | ||
| self, compute_unit, backend, frontend | ||
| ): | ||
| if frontend == TorchFrontend.EXECUTORCH: | ||
| pytest.xfail("executorch incorrectly propagates dynamic shape") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is it incorrect? Is there a PyTorch issue for this? |
||
|
|
||
| input_shape = (1, 3, 10, 10) | ||
|
|
||
| class Model(nn.Module): | ||
| def __init__(self, scale_factor): | ||
| super().__init__() | ||
| self.scale_factor = scale_factor | ||
|
|
||
| def forward(self, args): | ||
| return nn.functional.interpolate( | ||
| args, | ||
| scale_factor=self.scale_factor, | ||
| mode="nearest", | ||
| recompute_scale_factor=True, | ||
| ) | ||
|
|
||
| model = Model((2.5, 1.5)) | ||
|
|
||
| upper_bound_coreml = 20 if backend[0] == "mlprogram" else -1 | ||
| upper_bound_torch = None if upper_bound_coreml == -1 else upper_bound_coreml | ||
| height = RangeDim(upper_bound=upper_bound_coreml) | ||
| width = RangeDim(upper_bound=upper_bound_coreml) | ||
| converter_input_type = [TensorType(shape=(1, 3, height, width), dtype=np.float32)] | ||
| torch_export_dynamic_shapes = { | ||
| "args": { | ||
| 2: torch.export.Dim(name="height", max=upper_bound_torch), | ||
| 3: torch.export.Dim(name="width", max=upper_bound_torch), | ||
| } | ||
| } | ||
|
|
||
| self._xfail_if_torch_export_rejects_trunc( | ||
| lambda: self.run_compare_torch( | ||
| input_shape, | ||
| model, | ||
| frontend=frontend, | ||
| backend=backend, | ||
| compute_unit=compute_unit, | ||
| converter_input_type=converter_input_type, | ||
| torch_export_dynamic_shapes=torch_export_dynamic_shapes, | ||
| ) | ||
| ) | ||
|
|
||
| @pytest.mark.parametrize( | ||
| "compute_unit, backend, frontend", | ||
| itertools.product(compute_units, backends, TORCH_EXPORT_BASED_FRONTENDS), | ||
| ) | ||
| def test_interpolate_bilinear2d_with_float_scale_dynamic( | ||
| self, compute_unit, backend, frontend | ||
| ): | ||
| if frontend == TorchFrontend.EXECUTORCH: | ||
| pytest.xfail("executorch incorrectly propagates dynamic shape") | ||
|
|
||
| input_shape = (1, 3, 9, 22) | ||
|
|
||
| class Model(nn.Module): | ||
| def __init__(self, scale_factor, align_corners): | ||
| super().__init__() | ||
| self.scale_factor = scale_factor | ||
| self.align_corners = align_corners | ||
|
|
||
| def forward(self, args): | ||
| return nn.functional.interpolate( | ||
| args, | ||
| scale_factor=self.scale_factor, | ||
| mode="bilinear", | ||
| align_corners=self.align_corners, | ||
| recompute_scale_factor=True, | ||
| ) | ||
|
|
||
| model = Model((2.5, 3.5), False) | ||
|
|
||
| upper_bound_coreml = 30 if backend[0] == "mlprogram" else -1 | ||
| upper_bound_torch = None if upper_bound_coreml == -1 else upper_bound_coreml | ||
| height = RangeDim(upper_bound=upper_bound_coreml) | ||
| width = RangeDim(upper_bound=upper_bound_coreml) | ||
| converter_input_type = [TensorType(shape=(1, 3, height, width), dtype=np.float32)] | ||
| torch_export_dynamic_shapes = { | ||
| "args": { | ||
| 2: torch.export.Dim(name="height", max=upper_bound_torch), | ||
| 3: torch.export.Dim(name="width", max=upper_bound_torch), | ||
| } | ||
| } | ||
|
|
||
| self._xfail_if_torch_export_rejects_trunc( | ||
| lambda: self.run_compare_torch( | ||
| input_shape, | ||
| model, | ||
| frontend=frontend, | ||
| backend=backend, | ||
| compute_unit=compute_unit, | ||
| converter_input_type=converter_input_type, | ||
| torch_export_dynamic_shapes=torch_export_dynamic_shapes, | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| class TestEmpty(TorchBaseTest): | ||
| @pytest.mark.parametrize( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be much cleaner to only test valid use cases and not use this helper method.