Skip to content

Fix #122054: raise on out-of-bounds axis in tf.experimental.numpy.swapaxes for XLA consistency - #122544

Merged
copybara-service[bot] merged 3 commits into
tensorflow:masterfrom
Mattral:fix-122054-swapaxes-negative-axis-bounds
Jul 30, 2026
Merged

Fix #122054: raise on out-of-bounds axis in tf.experimental.numpy.swapaxes for XLA consistency#122544
copybara-service[bot] merged 3 commits into
tensorflow:masterfrom
Mattral:fix-122054-swapaxes-negative-axis-bounds

Conversation

@Mattral

@Mattral Mattral commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes inconsistent behavior between eager and jit_compile=True for out-of-bounds negative axes in tf.experimental.numpy.swapaxes.

Root Cause

When an axis outside [-rank, rank) was passed (e.g. -10 on a rank-5 tensor), the old adjust_axes only partially normalized it (-10 + 5 = -5). The resulting perm still contained negative values.

  • Eager mode: Transpose kernel silently re-normalized → appeared to work.
  • jit_compile=True / tf2xla: XLA bridge does not re-normalize → opaque error "-3 is out of range [0 .. 5)".

Changes

  • Added explicit bounds check in adjust_axes (before normalization) so swapaxes now raises a clear ValueError early, matching NumPy's intent.
  • Cleaned up adjust_axes to consistently use the rank parameter (removed a_rank scoping bug).
  • Added regression tests for both the error case and the original failing negative-axis + jit_compile=True scenario.

Testing

  • Added testSwapaxesOutOfBoundsAxisRaises and testSwapaxesNegativeAxisWithJitCompile.
  • Both pass in eager and under jit_compile=True.

Fixes #122054

@google-ml-butler google-ml-butler Bot added the size:M CL Change Size: Medium label Jul 2, 2026
@google-cla

google-cla Bot commented Jul 2, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@google-ml-butler
google-ml-butler Bot requested a review from cantonios July 2, 2026 17:04
@google-ml-butler google-ml-butler Bot added the awaiting review Pull request awaiting review label Jul 2, 2026
@Mattral
Mattral force-pushed the fix-122054-swapaxes-negative-axis-bounds branch from ef4ac57 to d61d431 Compare July 2, 2026 17:19
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@Mattral
Mattral force-pushed the fix-122054-swapaxes-negative-axis-bounds branch from d61d431 to 607deba Compare July 2, 2026 17:29
@Mattral

Mattral commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@cantonios
Hi, this is my first PR. I've addressed #122054 by adding bounds checking in swapaxes for better XLA consistency + added tests. CLA is now green.

Would appreciate a review when you have time. Thanks!

@keerthanakadiri
keerthanakadiri requested a review from a team July 3, 2026 04:53
@keerthanakadiri keerthanakadiri added the comp:ops OPs related issues label Jul 3, 2026
@github-project-automation github-project-automation Bot moved this to Assigned Reviewer in PR Queue Jul 3, 2026
@keerthanakadiri keerthanakadiri added the prtype:bugfix PR to fix a bug label Jul 3, 2026
…paxes for XLA consistency

Added explicit bounds check before negative axis normalization in
tf.experimental.numpy.swapaxes. This prevents partial normalization
(-10 becoming -5) that caused opaque "out of range" errors under
jit_compile=True while eager mode silently succeeded.

Also cleaned up the adjust_axes helper and added regression tests
for both the error case and the jit_compile path, matching NumPy behavior.

Fixes tensorflow#122054
@Mattral
Mattral force-pushed the fix-122054-swapaxes-negative-axis-bounds branch from 607deba to 5afa9ba Compare July 7, 2026 10:29
@Mattral

Mattral commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Force-pushed an amended commit to fix pylint indentation issues (W0311) flagged by CI. No changes to the fix's logic, same bounds-check approach as before, just corrected to match the repo's 2-space style. Should be green now.

@dmiltr3 dmiltr3 left a comment

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.

Review of PR #122544

Thank you for addressing this issue! Fixing the inconsistent behavior between eager and XLA modes is very important.

Findings

Here are a few items to consider before merging this PR:

1. Missing tf Import in Tests

In np_array_ops_test.py, the newly added tests testSwapaxesNegativeAxisWithJitCompile (and potentially others depending on implementation) use @tf.function. However, tf is not imported as tf in this file. You should use @def_function.function instead, which is already used throughout this module.

2. Incomplete Coverage for Dynamic Rank/Axes

The current fix in adjust_axes relies on static type checks:

        if isinstance(rank, int) and not (-rank <= x < rank):
          raise ValueError(
              f'axis {x} is out of bounds for array of dimension {rank}'
          )

While this correctly catches static errors, it bypasses validations in dynamic contexts:

  • Dynamic Rank: If rank is passed as a Tensor (common inside a @def_function.function with unknown input signature rank), isinstance(rank, int) evaluates to False. The static check is skipped entirely, meaning static out-of-bounds negative axes (e.g., -10) can still leak through to XLA causing the opaque range error.
  • Dynamic Axis: If the axis parameter itself is a runtime Tensor, it jumps straight to the else block skipping standard validation bounds tests.

Consider adding programmatic checks with control_flow_assert for dynamic tensors or clarifying that validations do not cover dynamically traced scenarios.

3. Cleanup of a_rank

Cleaning up local references to rely on standard parameterized inputs (rank instead of closure reference a_rank) is appreciated and solidifies graph safety.


Suggested Regression Tests

Consider integrating these test cases or derivatives under np_array_ops_test.py to assert edge bounds gracefully under def_function.function tracers:

  def testSwapaxesOutOfBoundsDynamicRank(self):
    x = np.zeros((1, 4, 32, 32, 8), dtype=np.float32)
    
    @def_function.function(jit_compile=True, input_signature=[tensor_spec.TensorSpec(dtype=dtypes.float32, shape=None)])
    def f(a):
      return np_array_ops.swapaxes(a, -10, 0)

    # Validate that bounds violations are caught with InvalidArgumentError or other structured exceptions.

@github-project-automation github-project-automation Bot moved this from Assigned Reviewer to Reviewer Requested Changes in PR Queue Jul 15, 2026
@Mattral

Mattral commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the detailed review and for highlighting these points!

I plan to work on the requested changes tomorrow (I have some other commitments today and want to give the updates proper attention). I'll update the tests to follow the module's existing import and decorator conventions, review the dynamic rank/axis validation path, and add appropriate regression coverage.

Once I push the updates, I'll request another review. Appreciate you taking the time to look at this.

@Mattral

Mattral commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review, this is really helpful.

  1. Missing tf import: confirmed, tf isn't imported in this file. Switching testSwapaxesNegativeAxisWithJitCompile to
    @def_function.function, which is already used elsewhere here.

  2. Dynamic rank/axis coverage: good catch! the current check only fires when both the axis and rank are static Python ints. I'm extending adjust_axes's dynamic branch with a runtime control_flow_assert.Assert bounds check, so eager execution andnon-XLA graph mode get the same clear error.

    One caveat before I push: under jit_compile=True, tf2xla lowers Assert to a no-op (tf2xla/kernels/assert_op.cc is registered as a dummy kernel, you can see it logging "Ignoring Assert operator ..." in other issues/models that hit
    this). So an Assert-based check won't raise inside a fully dynamic-rank function under jit_compile=True, that combination falls through to whatever the downstream transpose/scatter op does with an out-of-range index under XLA.

That's a separate, pre-existing XLA limitation, not something this PR can close. I'll document that in a comment and scope the new regression test to eager/graph mode accordingly, rather than asserting a guarantee jit_compile=True can't actually provide. Happy to file a separate tracking issue for the XLA-side gap if that's useful.

  1. a_rank cleanup: thanks noted, no further changes needed there.

Pushing an update shortly with these changes plus regression tests.

Mattral added a commit to Mattral/tensorflow that referenced this pull request Jul 16, 2026
…/axis

Extends the out-of-bounds axis check from the previous commit to the
dynamic branch of swapaxes's internal adjust_axes helper:

- Previously the bounds check only ran when both the axis and array
  rank were static Python ints, so a static axis combined with a
  dynamic (Tensor) rank -- e.g. inside a tf.function traced with an
  unspecified input signature -- skipped validation entirely.
- Adds a control_flow_assert.Assert runtime check on that branch so
  eager execution and non-XLA graph mode raise a clear
  InvalidArgumentError instead of silently normalizing an
  out-of-bounds negative axis into another out-of-bounds value.
- Documents that under jit_compile=True, tf2xla lowers Assert to a
  no-op, so a fully dynamic-rank + XLA-compiled call is not covered;
  that is a separate, pre-existing XLA limitation.
- Fixes testSwapaxesNegativeAxisWithJitCompile to use
  def_function.function instead of the unimported tf.function.
- Adds testSwapaxesOutOfBoundsDynamicRank regression test.

Addresses review feedback on PR tensorflow#122544.
@Mattral

Mattral commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

@dmiltr3 Pushed addressing all three points:

  • Switched to @def_function.function in the test.
  • Extended adjust_axes's dynamic branch with a runtime control_flow_assert.Assert bounds check.
  • Note: under jit_compile=True, tf2xla lowers Assert to a no-op, so a fully dynamic-rank + XLA-compiled call isn't covered by this check, that's a separate, pre-existing XLA limitation. Scoped the new regression test (testSwapaxesOutOfBoundsDynamicRank) to eager/graph mode accordingly rather than asserting something
    jit_compile=True can't actually guarantee.

Ran pylint (9.95/10, no new issues) and the full swapaxes test suite locally (4 passed) against tf-nightly.

Let me know if you'd like the XLA-mode gap tracked as a separate issue.

@Mattral
Mattral requested a review from dmiltr3 July 16, 2026 05:16
…/axis

Extends the out-of-bounds axis check from the previous commit to the
dynamic branch of swapaxes's internal adjust_axes helper:

- Previously the bounds check only ran when both the axis and array
  rank were static Python ints, so a static axis combined with a
  dynamic (Tensor) rank -- e.g. inside a tf.function traced with an
  unspecified input signature -- skipped validation entirely.
- Adds a control_flow_assert.Assert runtime check on that branch so
  eager execution and non-XLA graph mode raise a clear
  InvalidArgumentError instead of silently normalizing an
  out-of-bounds negative axis into another out-of-bounds value.
- Documents that under jit_compile=True, tf2xla lowers Assert to a
  no-op, so a fully dynamic-rank + XLA-compiled call is not covered;
  that is a separate, pre-existing XLA limitation.
- Fixes testSwapaxesNegativeAxisWithJitCompile to use
  def_function.function instead of the unimported tf.function.
- Adds testSwapaxesOutOfBoundsDynamicRank regression test.

Addresses review feedback on PR tensorflow#122544.
@Mattral
Mattral force-pushed the fix-122054-swapaxes-negative-axis-bounds branch from fd95907 to aba3139 Compare July 16, 2026 05:22

@dmiltr3 dmiltr3 left a comment

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.

Summary

The changes look good and address the issue #122054. However, some fixes were required to run the tests successfully in our internal environment.

Fixes Applied

1. Dependency for jit_compile=True

The new test testSwapaxesNegativeAxisWithJitCompile uses @def_function.function(jit_compile=True). This requires the XLA JIT compiler to be linked/available.

  • Issue: Internal tests failed with UnimplementedError: Could not find compiler for platform Host.
  • Fix: Added explicit dependency to XLA CPU JIT compiler (//third_party/tensorflow/compiler/jit:xla_cpu_jit).
  • Action for Contributor: Ensure your test suite has access to the XLA compiler backend when running tests with jit_compile=True.

2. Dependency for errors_impl

The test testSwapaxesOutOfBoundsDynamicRank uses errors_impl.InvalidArgumentError.

  • Issue: Missing dependency in some configurations.
  • Fix: Added explicit dependency to errors_impl (//third_party/tensorflow/python/framework:errors).
  • Action for Contributor: Double check if explicit dependency/import updates are needed in your build/setup scripts (e.g., setup.py or Bazel files) for errors_impl.

Code Changes (BUILD updates examples)

If applicable in your environment, make sure your BUILD/Bazel files are updated accordingly:

# Example updates to test dependencies
deps = [
    ...
    "//third_party/tensorflow/compiler/jit:xla_cpu_jit", # Ensure JIT is available
    "//third_party/tensorflow/python/framework:errors",  # For errors_impl
    ...
]

Notes on Assert under XLA

Note that as documented in the code comments, control_flow_assert.Assert is lowered to a no-op under XLA (jit_compile=True). This means dynamic rank bounds violations may not be caught in fully dynamic-rank XLA-compiled functions. This is a known pre-existing limitation and not a blocker for this PR, but good to keep in mind.

@Mattral

Mattral commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the review and for the helpful notes from your internal testing!

I appreciate the dependency information for the XLA JIT compiler and errors_impl, as well as the clarification about control_flow_assert under XLA. I'll also check the failing CI run.

I'll take a careful look at everything tomorrow and push an update to the PR.

Thanks again for the detailed feedback.

@Mattral

Mattral commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, appreciate you running this against the internal config.

Both dependencies make sense; I've added them to the public BUILD file, mapped to the public path equivalents (your snippet uses the internal google3 //third_party/tensorflow/... prefix, which doesn't exist in this repo):

  • //tensorflow/compiler/jit:xla_cpu_jit (not
    //third_party/tensorflow/compiler/jit:xla_cpu_jit)
  • //tensorflow/python/framework:errors (not
    //third_party/tensorflow/python/framework:errors)

Both added to np_array_ops_test's deps in tensorflow/python/ops/numpy_ops/BUILD. My guess on why this only showed up internally: np_array_ops_test uses cuda_py_strict_test (strict deps), and the OSS build's "monolithic" config bundles XLA CPU JIT into the shared object regardless of per-target deps, so it passed here without the explicit
dep, but a more modular internal build graph needs it declared. Same idea for errors_impl.

On build-windows-x86: I checked a few other open, unrelated PRs and that check is failing on them too with what looks like the same linker error (undefined symbol: GetPythonAPIMaxIndex, in
python_api_parameter_converter_wrapper.obj),

so this appears to be a master-wide breakage in the pywrap C++ binding layer, not something this PR caused. np_array_ops_test is also already tagged no_windows (# TODO(b/215381493)) in the BUILD file, so this test target isn't even built on that job. Linux (cpu/cuda/cuda13) and arm64 all passed.

Happy to rebase/retrigger once master is green there.

Pushing the BUILD update now.

…ay_ops_test

Adds two deps to np_array_ops_test that were only being resolved
transitively in the OSS monolithic build:
- //tensorflow/compiler/jit:xla_cpu_jit, likely needed for
  testSwapaxesNegativeAxisWithJitCompile's jit_compile=True to find a
  registered compiler under a stricter/modular build graph.
- //tensorflow/python/framework:errors, needed for the errors_impl
  import used by testSwapaxesOutOfBoundsDynamicRank, since this
  target uses cuda_py_strict_test (strict deps).

Addresses review feedback on PR tensorflow#122544.
@Mattral

Mattral commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

@dmiltr3

Pushed in 5fbdf12. Local Python test suite is unaffected (still 4/4 passing, since only the BUILD file changed this round). Let me know if the internal presubmit is green now, and whether you'd like the Windows CI failure looked at separately or tracked as its own issue.

@dmiltr3
dmiltr3 self-requested a review July 17, 2026 10:01
@google-ml-butler google-ml-butler Bot added kokoro:force-run Tests on submitted change ready to pull PR ready for merge process labels Jul 17, 2026
@github-project-automation github-project-automation Bot moved this from Reviewer Requested Changes to Approved by Reviewer in PR Queue Jul 17, 2026
@kokoro-team kokoro-team removed the kokoro:force-run Tests on submitted change label Jul 17, 2026
@Mattral

Mattral commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the thorough reviews throughout this!

@nithyak0204 nithyak0204 removed the awaiting review Pull request awaiting review label Jul 20, 2026
@nithyak0204 nithyak0204 added ready to pull PR ready for merge process and removed ready to pull PR ready for merge process labels Jul 28, 2026
@copybara-service
copybara-service Bot merged commit d3acefc into tensorflow:master Jul 30, 2026
19 checks passed
@github-project-automation github-project-automation Bot moved this from Approved by Reviewer to Merged in PR Queue Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:ops OPs related issues prtype:bugfix PR to fix a bug ready to pull PR ready for merge process size:M CL Change Size: Medium

Projects

Status: Merged

Development

Successfully merging this pull request may close these issues.

tf2xla: -3 is out of range [0 .. 5) under jit_compile=True (eager succeeds)

6 participants