| @@ -4294,6 +4294,9 @@ official: | |||
| 4294 | acl_op: v2.1 | 4294 | acl_op: v2.1 |
| 4295 | op_api: v2.1 | 4295 | op_api: v2.1 |
| 4296 | 4296 | ||
| 4297 | + - func: repeat_interleave.Tensor(Tensor repeats, *, SymInt? output_size=None) -> Tensor | ||
| 4298 | + op_api: [v2.2, newest] | ||
| 4299 | + | ||
| 4297 | - func: replication_pad1d(Tensor self, SymInt[2] padding) -> Tensor | 4300 | - func: replication_pad1d(Tensor self, SymInt[2] padding) -> Tensor |
| 4298 | acl_op: [v2.1, newest] | 4301 | acl_op: [v2.1, newest] |
| 4299 | op_api: [v2.1, newest] | 4302 | op_api: [v2.1, newest] |
| @@ -7700,6 +7703,9 @@ symint: | |||
| 7700 | acl_op: [v2.2, newest] | 7703 | acl_op: [v2.2, newest] |
| 7701 | op_api: [v2.2, newest] | 7704 | op_api: [v2.2, newest] |
| 7702 | 7705 | ||
| 7706 | + - func: repeat_interleave.Tensor(Tensor repeats, *, SymInt? output_size=None) -> Tensor | ||
| 7707 | + op_api: [v2.2, newest] | ||
| 7708 | + | ||
| 7703 | - func: repeat_interleave.self_int(Tensor self, SymInt repeats, int? dim=None, *, SymInt? output_size=None) -> Tensor | 7709 | - func: repeat_interleave.self_int(Tensor self, SymInt repeats, int? dim=None, *, SymInt? output_size=None) -> Tensor |
| 7704 | acl_op: [v2.2, newest] | 7710 | acl_op: [v2.2, newest] |
| 7705 | op_api: [v2.2, newest] | 7711 | op_api: [v2.2, newest] |
| @@ -215,5 +215,32 @@ at::Tensor repeat_interleave_symint( | |||
| 215 | } | 215 | } |
| 216 | return result; | 216 | return result; |
| 217 | } | 217 | } |
| 218 | + | ||
| 219 | + | ||
| 220 | +at::Tensor repeat_interleave_symint( | ||
| 221 | + const at::Tensor& repeats, | ||
| 222 | + c10::optional<c10::SymInt> output_size) | ||
| 223 | +{ | ||
| 224 | + TORCH_CHECK( | ||
| 225 | + repeats.dim() == 1, "repeat_interleave only accept 1D vector as repeat"); | ||
| 226 | + TORCH_CHECK( | ||
| 227 | + repeats.scalar_type() == at::kLong || repeats.scalar_type() == at::kInt, | ||
| 228 | + "repeats has to be Long or Int tensor"); | ||
| 229 | + if (repeats.size(0) == 0) { | ||
| 230 | + return at::empty_like(repeats, LEGACY_CONTIGUOUS_MEMORY_FORMAT); | ||
| 231 | + } | ||
| 232 | + at::Tensor cumsum = repeats.cumsum(0); | ||
| 233 | + int64_t total = 0; | ||
| 234 | + if (output_size.has_value()) { | ||
| 235 | + total = output_size.value().expect_int(); | ||
| 236 | + } else { | ||
| 237 | + total = cumsum[-1].item<int64_t>(); | ||
🟡 Medium Priority 变更行 224、229:当 该函数无任何对 触发条件: 建议:在函数开头增加 ![]() ![]() | |||
| 238 | + TORCH_CHECK((repeats >= 0).all().to(at::kBool).item<bool>(), "repeats cannot be negative."); | ||
| 239 | + } | ||
🟡 Medium Priority 变更行 226-231:新增的 对比同一文件中其他所有 触发条件:调用 建议:将 ![]() ![]() | |||
| 240 | + at::Tensor result = at::empty({total}, repeats.options()); | ||
| 241 | + EXEC_NPU_CMD(aclnnRepeatInterleaveTensor, repeats, total, result); | ||
| 242 | + return result; | ||
| 243 | +} | ||
| 244 | + | ||
| 218 | 245 | ||
| 219 | } | 246 | } |
| @@ -4823,6 +4823,9 @@ | |||
| 4823 | "func: repeat_interleave.self_Tensor(Tensor self, Tensor repeats, int? dim=None, *, SymInt? output_size=None) -> Tensor": { | 4823 | "func: repeat_interleave.self_Tensor(Tensor self, Tensor repeats, int? dim=None, *, SymInt? output_size=None) -> Tensor": { |
| 4824 | "version": ["v2.3", "newest"] | 4824 | "version": ["v2.3", "newest"] |
| 4825 | }, | 4825 | }, |
| 4826 | + "func: repeat_interleave.Tensor(Tensor repeats, *, SymInt? output_size=None) -> Tensor": { | ||
| 4827 | + "version": ["v2.2", "newest"] | ||
| 4828 | + }, | ||
| 4826 | "func: repeat_interleave.self_int(Tensor self, SymInt repeats, int? dim=None, *, SymInt? output_size=None) -> Tensor": { | 4829 | "func: repeat_interleave.self_int(Tensor self, SymInt repeats, int? dim=None, *, SymInt? output_size=None) -> Tensor": { |
| 4827 | "version": ["v2.3", "newest"] | 4830 | "version": ["v2.3", "newest"] |
| 4828 | }, | 4831 | }, |
| @@ -124,6 +124,65 @@ class TestRepeatInterleave(TestCase): | |||
| 124 | npu_output = self.npu_op_exec_without_dim(cpu_input1.npu(), input2) | 124 | npu_output = self.npu_op_exec_without_dim(cpu_input1.npu(), input2) |
| 125 | self.assertRtolEqual(cpu_output, npu_output) | 125 | self.assertRtolEqual(cpu_output, npu_output) |
| 126 | 126 | ||
| 127 | + def cpu_op_exec_repeats_only(self, repeats): | ||
| 128 | + output = torch.repeat_interleave(repeats) | ||
| 129 | + output = output.numpy() | ||
| 130 | + return output | ||
| 131 | + | ||
| 132 | + def npu_op_exec_repeats_only(self, repeats): | ||
| 133 | + output = torch.repeat_interleave(repeats) | ||
| 134 | + output = output.cpu() | ||
| 135 | + output = output.numpy() | ||
| 136 | + return output | ||
| 137 | + | ||
| 138 | + def cpu_op_exec_repeats_output_size(self, repeats, output_size): | ||
| 139 | + output = torch.repeat_interleave(repeats, output_size=output_size) | ||
| 140 | + output = output.numpy() | ||
| 141 | + return output | ||
| 142 | + | ||
| 143 | + def npu_op_exec_repeats_output_size(self, repeats, output_size): | ||
| 144 | + output = torch.repeat_interleave(repeats, output_size=output_size) | ||
| 145 | + output = output.cpu() | ||
| 146 | + output = output.numpy() | ||
| 147 | + return output | ||
| 148 | + | ||
| 149 | + def test_repeat_interleave_repeats_tensor_int64(self): | ||
| 150 | + repeats = torch.tensor([2, 3, 1, 4], dtype=torch.int64) | ||
| 151 | + cpu_output = self.cpu_op_exec_repeats_only(repeats) | ||
| 152 | + npu_output = self.npu_op_exec_repeats_only(repeats.npu()) | ||
| 153 | + self.assertRtolEqual(cpu_output, npu_output) | ||
| 154 | + | ||
| 155 | + def test_repeat_interleave_repeats_tensor_int32(self): | ||
| 156 | + repeats = torch.tensor([2, 3, 1, 4], dtype=torch.int32) | ||
| 157 | + cpu_output = self.cpu_op_exec_repeats_only(repeats) | ||
| 158 | + npu_output = self.npu_op_exec_repeats_only(repeats.npu()) | ||
| 159 | + self.assertRtolEqual(cpu_output, npu_output) | ||
| 160 | + | ||
| 161 | + def test_repeat_interleave_repeats_tensor_with_output_size(self): | ||
| 162 | + repeats = torch.tensor([2, 3, 1, 4], dtype=torch.int64) | ||
| 163 | + output_size = 10 | ||
| 164 | + cpu_output = self.cpu_op_exec_repeats_output_size(repeats, output_size) | ||
| 165 | + npu_output = self.npu_op_exec_repeats_output_size(repeats.npu(), output_size) | ||
| 166 | + self.assertRtolEqual(cpu_output, npu_output) | ||
| 167 | + | ||
| 168 | + def test_repeat_interleave_repeats_tensor_large(self): | ||
| 169 | + repeats = torch.randint(1, 5, (100,), dtype=torch.int64) | ||
| 170 | + cpu_output = self.cpu_op_exec_repeats_only(repeats) | ||
| 171 | + npu_output = self.npu_op_exec_repeats_only(repeats.npu()) | ||
| 172 | + self.assertRtolEqual(cpu_output, npu_output) | ||
| 173 | + | ||
| 174 | + def test_repeat_interleave_repeats_tensor_single_element(self): | ||
| 175 | + repeats = torch.tensor([5], dtype=torch.int64) | ||
| 176 | + cpu_output = self.cpu_op_exec_repeats_only(repeats) | ||
| 177 | + npu_output = self.npu_op_exec_repeats_only(repeats.npu()) | ||
| 178 | + self.assertRtolEqual(cpu_output, npu_output) | ||
| 179 | + | ||
| 180 | + def test_repeat_interleave_repeats_tensor_empty(self): | ||
| 181 | + repeats = torch.tensor([], dtype=torch.int64) | ||
| 182 | + cpu_output = self.cpu_op_exec_repeats_only(repeats) | ||
| 183 | + npu_output = self.npu_op_exec_repeats_only(repeats.npu()) | ||
| 184 | + self.assertRtolEqual(cpu_output, npu_output) | ||
| 185 | + | ||
| 127 | 186 | ||
| 128 | if __name__ == '__main__': | 187 | if __name__ == '__main__': |
| 129 | run_tests() | 188 | run_tests() |


🟡 Medium Priority
变更行 226-228:当
output_size.has_value()为 true 时,total被直接设为output_size.value().expect_int(),但未校验该值是否与repeats各元素之和一致。对比同文件中其他重载(如行 200-208),它们通过
apply_result_tensor中的TORCH_CHECK(output_size_expected == output_size, ...)进行此项校验。新增重载完全缺失此校验链。触发条件:
output_size与repeats.sum()不一致。例如repeat_interleave(tensor([2,3]), output_size=10)—— 实际 repeats 和为 5,但 output_size=10。 失败模式:创建的 result tensor 大小为output_size(10),但aclnnRepeatInterleaveTensor可能按实际 repeats 写入数据(5 个元素),导致 result 后半部分未初始化 / 越界写入,产生数据错乱。建议:在
output_size.has_value()分支内增加校验:用cumsum[-1].item<int64_t>()得到实际 repeats 总和,与output_size值做TORCH_CHECK比对,确保二者一致。或将 cumsum 计算移到 if 之前共享,并在 output_size 分支中校验。