From 3176c842ebeea939f18d96a02d544186e104c9c6 Mon Sep 17 00:00:00 2001 From: feixi139 <66367509+feixi139@users.noreply.github.com> Date: Sat, 29 Aug 2026 22:28:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20as=5Fstrided=20=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E8=A7=84=E5=88=99=EF=BC=9A=E5=AD=97=E8=8A=82=20offset?= =?UTF-8?q?=20=E6=8D=A2=E7=AE=97=E4=B8=BA=E5=85=83=E7=B4=A0=20storage=5Fof?= =?UTF-8?q?fset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Paddle 的 offset 以字节计,Torch 的 storage_offset 以元素计,原映射直接 透传导致 offset 非 0 时两侧起点不同,比较结果无意义。新增 AsStridedRule 完成单位换算并拒绝负数或未按 itemsize 对齐的 offset,同时补上缺失的 paddle.Tensor.as_strided 条目。附带用于覆盖重叠视图反向的语料。 --- .../as_strided_unfold_overlap_grad.txt | 31 +++++++++++++++ tester/paddle_to_torch/mapping.json | 14 +++---- tester/paddle_to_torch/rules.py | 38 +++++++++++++++++++ 3 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 tester/api_config/11_fix_op/as_strided_unfold_overlap_grad.txt diff --git a/tester/api_config/11_fix_op/as_strided_unfold_overlap_grad.txt b/tester/api_config/11_fix_op/as_strided_unfold_overlap_grad.txt new file mode 100644 index 00000000..7b969f7d --- /dev/null +++ b/tester/api_config/11_fix_op/as_strided_unfold_overlap_grad.txt @@ -0,0 +1,31 @@ +paddle.as_strided(Tensor([4],"float32"), shape=tuple(2,2,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([4],"float64"), shape=tuple(2,2,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([5],"float32"), shape=tuple(3,3,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([5],"float64"), shape=tuple(3,3,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([8],"float32"), shape=tuple(3,3,), stride=tuple(1,1,), offset=4, ) +paddle.as_strided(Tensor([64],"float32"), shape=tuple(32,32,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([64],"float16"), shape=tuple(32,32,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([64],"bfloat16"), shape=tuple(32,32,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([64],"complex64"), shape=tuple(32,32,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([64],"complex128"), shape=tuple(32,32,), stride=tuple(1,1,), ) +paddle.as_strided(Tensor([1],"float32"), shape=tuple(3,4,), stride=tuple(0,0,), ) +paddle.as_strided(Tensor([1],"float32"), shape=tuple(64,64,), stride=tuple(0,0,), ) +paddle.as_strided(Tensor([3],"float32"), shape=tuple(3,4,), stride=tuple(1,0,), ) +paddle.as_strided(Tensor([32],"float32"), shape=tuple(32,16,), stride=tuple(1,0,), ) +paddle.as_strided(Tensor([8, 4],"float32"), shape=tuple(6,4,), stride=tuple(2,1,), ) +paddle.as_strided(Tensor([6],"float32"), shape=tuple(2,3,), stride=tuple(3,1,), ) +paddle.as_strided(Tensor([6],"float32"), shape=tuple(3,2,), stride=tuple(1,3,), ) +paddle.as_strided(Tensor([128, 8],"float32"), shape=tuple(64,8,), stride=tuple(8,1,), ) +paddle.Tensor.as_strided(Tensor([64],"float32"), shape=tuple(32,32,), stride=tuple(1,1,), ) +paddle.Tensor.as_strided(Tensor([6],"float32"), shape=tuple(2,3,), stride=tuple(3,1,), ) +paddle.unfold(Tensor([5],"float32"), 0, 3, 1, ) +paddle.unfold(Tensor([5],"float64"), 0, 3, 1, ) +paddle.unfold(Tensor([16],"float32"), 0, 4, 1, ) +paddle.unfold(Tensor([128],"float32"), 0, 64, 1, ) +paddle.unfold(Tensor([128],"float16"), 0, 64, 1, ) +paddle.unfold(Tensor([128],"bfloat16"), 0, 64, 1, ) +paddle.unfold(Tensor([128],"complex64"), 0, 64, 1, ) +paddle.unfold(Tensor([64, 8],"float32"), 0, 8, 1, ) +paddle.unfold(Tensor([8, 4],"float32"), 0, 4, 2, ) +paddle.unfold(Tensor([12],"float32"), -1, 2, 5, ) +paddle.unfold(Tensor([12],"float32"), 0, 4, 4, ) diff --git a/tester/paddle_to_torch/mapping.json b/tester/paddle_to_torch/mapping.json index f9aef13b..6b966413 100644 --- a/tester/paddle_to_torch/mapping.json +++ b/tester/paddle_to_torch/mapping.json @@ -229,14 +229,14 @@ } }, "paddle.as_strided": { + "Rule": "AsStridedRule", "torch_api": "torch.as_strided", - "paddle_torch_args_map": { - "x": "input", - "shape": "size", - "stride": "stride", - "offset": "storage_offset" - }, - "description": "result = torch.as_strided(input=x, size=shape, stride=stride, storage_offset=offset)" + "description": "Paddle byte offset is converted to Torch element offset" + }, + "paddle.Tensor.as_strided": { + "Rule": "AsStridedRule", + "torch_api": "torch.Tensor.as_strided", + "description": "Paddle byte offset is converted to Torch element offset" }, "paddle.asin": { "torch_api": "torch.arcsin", diff --git a/tester/paddle_to_torch/rules.py b/tester/paddle_to_torch/rules.py index 5c5f6adc..72520b8c 100644 --- a/tester/paddle_to_torch/rules.py +++ b/tester/paddle_to_torch/rules.py @@ -419,6 +419,44 @@ def apply(self, paddle_api: str) -> ConvertResult: ) +class AsStridedRule(BaseRule): + PADDLE_APIS = ("paddle.as_strided", "paddle.Tensor.as_strided") + + def apply(self, paddle_api: str) -> ConvertResult: + # Paddle 的 offset 是字节数,Torch 的 storage_offset 是元素序号,不能直接透传。 + preprocess = """ +_storage_offset = locals().get("offset", 0) +# 缺省 offset 由绑定器补成 0,独立转换调用也保持相同语义。 +if _storage_offset is None: + _storage_offset = 0 +_storage_offset = int(_storage_offset) +_item_size = int(x.element_size()) +# 非法偏移提前失败,避免 Torch 以不同单位产生静默错位。 +if _storage_offset < 0 or _storage_offset % _item_size: + raise ValueError( + f"Paddle as_strided offset must be a non-negative multiple of itemsize, " + f"got offset={_storage_offset}, itemsize={_item_size}" + ) +_storage_offset //= _item_size +""" + # 共享参数绑定器把 Tensor receiver 统一命名为 x,函数与方法只需切换调用入口。 + if paddle_api == "paddle.Tensor.as_strided": + core = ( + "result = x.as_strided(size=shape, stride=stride, storage_offset=_storage_offset)" + ) + else: + core = ( + "result = torch.as_strided(" + "input=x, size=shape, stride=stride, storage_offset=_storage_offset)" + ) + return self.build_result( + paddle_api, + kind=ConversionKind.DIRECT, + preprocess=preprocess, + core=core, + ) + + # a class AsComplexRule(BaseRule): PADDLE_APIS = ("paddle.as_complex",)