(self, func, types, args=(), kwargs=None)
| 623 | self.decomposed = set() |
| 624 | |
| 625 | def __torch_dispatch__(self, func, types, args=(), kwargs=None): |
| 626 | self.test_case.precision = self.saved_precision |
| 627 | self.test_case.rel_tol = self.saved_rel_tol |
| 628 | |
| 629 | self.called.add(func) |
| 630 | all_called[func] += 1 |
| 631 | |
| 632 | # Stuff we shouldn't bother testing |
| 633 | # (TODO: remove detach from the decomp table?) |
| 634 | # N.b. Testing in-place ops would need dedicated logic |
| 635 | in_place = func.name()[-1] == '_' |
| 636 | ignored_ops = [ |
| 637 | torch.ops.aten.detach.default, |
| 638 | # non-deterministic ops |
| 639 | torch.ops.aten.empty.memory_format, |
| 640 | torch.ops.aten.empty_like.default, |
| 641 | torch.ops.aten.new_empty.default, |
| 642 | torch.ops.aten.empty_strided.default, |
| 643 | torch.ops.aten.new_empty_strided.default, |
| 644 | torch.ops.aten.randn.default, |
| 645 | torch.ops.aten.native_dropout.default, |
| 646 | ] |
| 647 | if ( |
| 648 | func not in decomposition_table or |
| 649 | func in ignored_ops or |
| 650 | torch.Tag.nondeterministic_seeded in func.tags or |
| 651 | any_unsupported(args, kwargs) or |
| 652 | in_place |
| 653 | ): |
| 654 | return func(*args, **kwargs) |
| 655 | |
| 656 | self.decomposed.add(func) |
| 657 | all_decomposed.add(func) |
| 658 | |
| 659 | # We take 2 main strategies for verifying correctness/numerical stability of decompositions |
| 660 | # The first one is simply tolerance checking between decomp_out and pytorch_out |
| 661 | # However, for fp16/bf16 and reductions, this becomes very |
| 662 | # finicky, as there are not many guarantees we can make. |
| 663 | # So, for fp16/bf16, we instead compare the difference of |
| 664 | # {decomp_out, pytorch_out_64} and {pytorch_out, |
| 665 | # pytorch_out_64}. In other words, we compare how far the |
| 666 | # decomposition and pytorch are from the "ground truth" (i.e. |
| 667 | # fp64). If the decomposition results in more error, we error |
| 668 | |
| 669 | # We also decompose the decomposition recursively for |
| 670 | # further coverage, as some paths not be exercised directly by |
| 671 | # OpInfos (sadly) but just by other ops |
| 672 | |
| 673 | decomposition = decomposition_table[func] |
| 674 | |
| 675 | do_relative_check = self.test_dtype in [torch.float16, torch.bfloat16] |
| 676 | if self.run_all: |
| 677 | # Execute recursively via DFS, to find the root of a possible error first |
| 678 | with self: |
| 679 | decomp_out = pytree.tree_leaves(decomposition(*args, **kwargs)) |
| 680 | else: |
| 681 | decomp_out = pytree.tree_leaves(decomposition(*args, **kwargs)) |
| 682 |
nothing calls this directly
no test coverage detected