Skip to content

Add MLX op handler for aten.isinf#18988

Open
Jah-yee wants to merge 3 commits into
pytorch:mainfrom
Jah-yee:add-isinf-mlx-op-handler
Open

Add MLX op handler for aten.isinf#18988
Jah-yee wants to merge 3 commits into
pytorch:mainfrom
Jah-yee:add-isinf-mlx-op-handler

Conversation

@Jah-yee

@Jah-yee Jah-yee commented Apr 18, 2026

Copy link
Copy Markdown

Good day

Summary

This PR adds a decomposed MLX op handler for aten.isinf to the pytorch/executorch project.

Motivation

The MLX delegate converts PyTorch aten ops into MLX graph nodes during export. When an aten op has no handler, it falls back to CPU execution, breaking the GPU acceleration pipeline. Adding a handler for aten.isinf enables it to run on Metal GPU via MLX.

Implementation

The handler uses a decomposed approach:

isinf(x) = abs(x) == inf

This uses existing AbsNode and EqualNode which are already supported, avoiding the need for a dedicated MLX isinf op.

Changes

  • backends/mlx/ops.py: Added _isinf_handler function registered for torch.ops.aten.isinf.default
  • backends/mlx/test/test_ops.py: Added isinf to _UNARY_OP_TESTS with standard test configuration

Testing

The handler can be tested with:

python -m executorch.backends.mlx.test.run_all_tests -k isinf

Thank you for your attention. If there are any issues or suggestions, please leave a comment and I will address them promptly.

Warmly,
RoomWithOutRoof

@pytorch-bot

pytorch-bot Bot commented Apr 18, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/18988

Note: Links to docs will display an error until the docs builds have been completed.

❗ 1 Active SEVs

There are 1 currently active SEVs. If your PR is affected, please view them below:

⚠️ 13 Awaiting Approval

As of commit f811554 with merge base 490ec5c (image):

AWAITING APPROVAL - The following workflows need approval before CI can run:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Apr 18, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

Implement isinf op handler for the MLX delegate backend.

isinf(x) is decomposed as abs(x) == inf, using existing AbsNode
and EqualNode which are already supported in the MLX graph schema.

The handler also includes a corresponding test case with _inf_input_fn
that generates inputs containing both positive and negative infinity.

Fixes: pytorch#18922
@Jah-yee Jah-yee force-pushed the add-isinf-mlx-op-handler branch from 441eca1 to 810bc80 Compare April 19, 2026 17:43
@nil-is-all

Copy link
Copy Markdown
Contributor

@claude review

@claude

claude Bot commented Apr 20, 2026

Copy link
Copy Markdown

Claude finished @nil-is-all's task in 1m 31s —— View job


Reviewing PR #18988: Add MLX op handler for aten.isinf

  • Gather context and read changed files
  • Review backends/mlx/ops.py changes
  • Review backends/mlx/test/test_ops.py changes
  • Post review feedback

Review Summary

The decomposition approach (isinf(x) = abs(x) == inf) is sound and follows the same pattern as the existing _isnan_handler. The implementation correctly uses AbsNode and EqualNode, and the test coverage is appropriate. A few items to address:


Issues

1. Formatting: # activations comment merged onto the isinf linebackends/mlx/test/test_ops.py:4130

The isinf entry was inserted before the # activations comment, but the comment ended up appended to the end of the isinf line instead of staying on its own line:

# Before (line 4129-4131 in original):
    {"op_name": "isnan", ...},
    # activations
    {"op_name": "relu", ...},

# After (current):
    {"op_name": "isnan", ...},
    {"op_name": "isinf", ...},    # activations
    {"op_name": "relu", ...},

The # activations section comment should remain on its own line to preserve the visual grouping:

    {"op_name": "isnan", ...},
    {"op_name": "isinf", ...},
    # activations
    {"op_name": "relu", ...},

Fix this →

2. Hardcoded torch.float32 for the inf constantbackends/mlx/ops.py:468

inf_slot = emit_lifted_constant(P, float('inf'), torch.float32)

The inf constant is always created as float32, even when the input tensor x may be float16 or bfloat16. This works because EqualNode likely handles type promotion internally, but it's worth verifying this is intentional and doesn't cause issues for half-precision inputs. The test does cover float16 and bfloat16, so if CI passes this is likely fine — but a comment noting the intentional choice or using the input's dtype would make this clearer.

3. Minor: _inf_input_fn overwrites some +inf with -infbackends/mlx/test/test_ops.py:4029-4032

mask_pos = torch.rand(shape) > 0.8   # ~20% set to +inf
mask_neg = torch.rand(shape) > 0.9   # ~10% set to -inf (may overwrite +inf)
x[mask_pos] = float('inf')
x[mask_neg] = float('-inf')

Since mask_neg is applied after mask_pos, some positions marked +inf will be overwritten to -inf. This isn't a bug per se (both infinities should be detected), but the effective distribution is slightly different from what the thresholds suggest. The existing _nan_input_fn uses a single mask, which is cleaner. Consider using non-overlapping masks if the intent is to have distinct proportions, or just note that overlap is acceptable.


Looks Good

  • The decomposition abs(x) == inf is mathematically correct and handles both +inf and -inf.
  • Properly uses make_tmp_slot() for the intermediate abs result, matching codebase conventions.
  • Correctly validates args/kwargs with require_args and require_kwargs.
  • Good placement under the "Numerical checks" section alongside _isnan_handler.
  • Test configuration with _SHAPES_3 and multiple float dtypes provides reasonable coverage.

Issue (1) is the only one I'd consider a must-fix before merge. The # activations comment being on its own line is part of the existing code organization pattern in that file, and the current diff breaks that grouping.
|

@nil-is-all nil-is-all requested review from metascroy May 4, 2026 16:56
Comment thread backends/mlx/test/test_ops.py Outdated
def fn(shape, dtype):
x = torch.randn(shape, dtype=dtype)
# Insert some inf values
mask_pos = torch.rand(shape) > 0.8

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add nan cases as well?

@metascroy

Copy link
Copy Markdown
Contributor

Looks great @Jah-yee! Left a small nit and running CI now.

@metascroy

Copy link
Copy Markdown
Contributor

Can you run the lintrunner?

pip install -r requirements-lintrunner.txt
lintrunner -a

Address @nil-is-all review comments:
1. Move '# activations' section comment to its own line (was merged onto isinf entry)
2. Add clarifying comment explaining float32 inf constant is intentionally
   typed for EqualNode type promotion
3. Use non-overlapping masks in _inf_input_fn to avoid +inf being
   overwritten by -inf
@pytorch-bot

pytorch-bot Bot commented May 4, 2026

Copy link
Copy Markdown

The following ciflow label(s) have been added but CI has not been triggered yet because the workflows are awaiting approval:

  • ciflow/mlx

Once a maintainer approves the workflows (scroll to the bottom of the PR page), the corresponding CI jobs will be triggered automatically. Please ping one of the reviewers if you do not have access to approve and run workflows.

@Jah-yee

Jah-yee commented May 4, 2026

Copy link
Copy Markdown
Author

Thanks for the thorough review, @nil-is-all!

思考路径

收到 3 条反馈,逐一分析:

  1. # activations 注释格式 — 确实违反代码风格。_UNARY_OP_TESTS 列表中 section comment 都是单独成行,isinf 被插入时意外合并了。这破坏了文件的视觉分组。

  2. float32 硬编码 dtype — MLX 的 EqualNode 内部做类型提升,所以 float32 的 inf 能正确匹配 float16/bfloat16 的输入。但原文没说明意图,reviewer 提的对,加个注释消除歧义。

  3. mask overlap — 这是我自己写 _inf_input_fn 时的疏忽,mask_negmask_pos 之后应用,会覆盖部分 +inf。但从功能角度不影响测试(两种 inf 都测到了),reviewer 也说 "not a bug per se"。不过非重叠 mask 更干净,改了。

修改方案

Fix 1: 格式化

# Before
{"op_name": "isinf", ...},    # activations
{"op_name": "relu", ...

# After
{"op_name": "isinf", ...},
# activations
{"op_name": "relu", ...

Fix 2: dtype 注释

# Create inf constant (float32; EqualNode handles type promotion to match input dtype)
inf_slot = emit_lifted_constant(P, float('inf'), torch.float32)

Fix 3: 非重叠 mask

# Before: mask_neg 覆盖部分 mask_pos
mask_pos = torch.rand(shape) > 0.8
mask_neg = torch.rand(shape) > 0.9  # may overwrite +inf

# After: 完全独立的 mask
mask_pos = torch.rand(shape) > 0.8
mask_neg = (~mask_pos) & (torch.rand(shape) > 0.9)

Commit 已推送: 2cdde9d7f

Please re-review!

@metascroy

Copy link
Copy Markdown
Contributor

@Jah-yee it looks like lintrunner is still failing in CI, e.g.,

 Warning (UFMT) format
    Run `lintrunner -a` to apply this patch.

    You can run `lintrunner -a` to apply this patch.

    465  465 |     )
    466  466 | 
    467  467 |     # Create inf constant (float32; EqualNode handles type promotion to match input dtype)
    467      |-    inf_slot = emit_lifted_constant(P, float('inf'), torch.float32)
         468 |+    inf_slot = emit_lifted_constant(P, float("inf"), torch.float32)
    469  469 | 
    470  470 |     # Compare abs(x) == inf
    471  471 |     out = P.make_or_get_slot(n)

Make sure you run the linter locally and then resubmit the PR with the linted code.

@kirklandsign

Copy link
Copy Markdown
Contributor

@Jah-yee 请改一下这2个 linter

>>> Lint for backends/mlx/ops.py:

  Warning (UFMT) format
    Run `lintrunner -a` to apply this patch.

    You can run `lintrunner -a` to apply this patch.

    465  465 |     )
    466  466 | 
    467  467 |     # Create inf constant (float32; EqualNode handles type promotion to match input dtype)
    467      |-    inf_slot = emit_lifted_constant(P, float('inf'), torch.float32)
         468 |+    inf_slot = emit_lifted_constant(P, float("inf"), torch.float32)
    469  469 | 
    470  470 |     # Compare abs(x) == inf
    471  471 |     out = P.make_or_get_slot(n)



>>> Lint for backends/mlx/test/test_ops.py:

  Warning (UFMT) format
    Run `lintrunner -a` to apply this patch.

    You can run `lintrunner -a` to apply this patch.

    4028  4028 |         # Insert ~20% +inf and ~10% -inf using non-overlapping masks
    4029  4029 |         mask_pos = torch.rand(shape) > 0.8  # ~20% -> +inf
    4030  4030 |         mask_neg = (~mask_pos) & (torch.rand(shape) > 0.9)  # ~10% of remaining -> -inf
    4030       |-        x[mask_pos] = float('inf')
    4031       |-        x[mask_neg] = float('-inf')
          4031 |+        x[mask_pos] = float("inf")
          4032 |+        x[mask_neg] = float("-inf")
    4033  4033 |         return (x,)
    4034  4034 | 
    4035  4035 |     return fn

Fixes UFMT format warnings:
- backends/mlx/ops.py: float('inf') -> float("inf")
- backends/mlx/test/test_ops.py: float('inf')/float('-inf') -> double quotes
@linux-foundation-easycla

Copy link
Copy Markdown

CLA Missing ID

@Jah-yee

Jah-yee commented Jun 24, 2026

Copy link
Copy Markdown
Author

Thanks for the suggestion on adding nan cases! Supporting aten.isnan in the MLX backend would require a separate decomposed op handler (similar to the isinf handler in this PR) plus corresponding tests - best as a follow-up PR. For this PR (isinf), the lint fixes have been applied. Happy to open a follow-up issue if you'd like to track the isnan enhancement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/mlx CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants