Skip to content

Conversation

ricardoV94
Copy link
Member

@ricardoV94 ricardoV94 commented Sep 28, 2025

This should be better than a dense dot product, both memory and speed wise. It's also more elegant?

Also:

  1. Remove unused scalar repeat logic. The helper function pt.repeat uses broadcast_to + alloc in this case, so it's never triggered unless users create their Op manually. Well no more
  2. Remove special axis=None logic. This is a common motif as many operations in numpy have a special meaning for axis=None, which means, ravel the input, and then apply operation as if axis=0. I decided to do that out of the Op, and let the Op focus on just axis=int case. Same as in Handle axis=None symbolically instead of within CumOp #1574

This way we only have to support the code that is really specific to Repeat with vector repeats.


📚 Documentation preview 📚: https://pytensor--1621.org.readthedocs.build/en/1621/

@ricardoV94 ricardoV94 force-pushed the use_incsubtesor_in_gradient_of_repeat branch from 6935466 to 2230e91 Compare September 28, 2025 19:51
Copy link

codecov bot commented Sep 28, 2025

Codecov Report

❌ Patch coverage is 78.00000% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.62%. Comparing base (96122d1) to head (2230e91).

Files with missing lines Patch % Lines
pytensor/tensor/extra_ops.py 73.80% 7 Missing and 4 partials ⚠️

❌ Your patch check has failed because the patch coverage (78.00%) is below the target coverage (100.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1621      +/-   ##
==========================================
- Coverage   81.64%   81.62%   -0.02%     
==========================================
  Files         231      231              
  Lines       52997    52968      -29     
  Branches     9395     9384      -11     
==========================================
- Hits        43267    43237      -30     
- Misses       7282     7285       +3     
+ Partials     2448     2446       -2     
Files with missing lines Coverage Δ
pytensor/link/numba/dispatch/basic.py 77.86% <100.00%> (ø)
pytensor/link/numba/dispatch/extra_ops.py 95.21% <100.00%> (-0.29%) ⬇️
pytensor/tensor/extra_ops.py 88.15% <73.80%> (-0.41%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the Repeat operation to use IncSubtensor for gradient computation instead of dense dot products, removing support for scalar repeats and axis=None at the Op level. These cases are now handled by the helper function pt.repeat using broadcast_to and reshape operations.

  • Replace gradient computation with IncSubtensor approach for better memory/performance
  • Remove scalar repeat and axis=None logic from Repeat Op, delegating to helper function
  • Simplify Numba implementation by removing object mode for most cases

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pytensor/tensor/extra_ops.py Main refactoring of Repeat Op constructor, make_node, grad, and repeat helper function
tests/tensor/test_extra_ops.py Updated tests to reflect new Op constraints and added static shape inference tests
tests/link/numba/test_extra_ops.py Updated test parameters to match new Op requirements
pytensor/link/numba/dispatch/extra_ops.py Simplified Numba implementation with fallback for unsupported cases
pytensor/link/numba/dispatch/basic.py Made node parameter required in generate_fallback_impl
pytensor/utils.py Removed outdated comment about Python < 2.6

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.



def generate_fallback_impl(op, node=None, storage_map=None, **kwargs):
def generate_fallback_impl(op, node, storage_map=None, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

Why?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because it's not really optional, the code immediately assumes node is passed (and not None)

Comment on lines +610 to +612
if axis is None or axis < 0:
# Operator Repeat does not support None or negative axis
continue
Copy link
Member

Choose a reason for hiding this comment

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

Check that the relevant error is raised?

r = Repeat(axis=0)(x, 2)
assert r.broadcastable == (False, True, False)
def test_static_shape(self):
x = TensorType(config.floatX, shape=(None, 1, 3))()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
x = TensorType(config.floatX, shape=(None, 1, 3))()
x = pt.tensor(shape=(None, 1, 3))

Why is TensorType being used directly here?

Copy link
Member Author

Choose a reason for hiding this comment

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

someone wrote it like that at first

Comment on lines +666 to +668
# This case could be implemented in the future
r = repeat(x, [1, 2, 4], axis=2)
assert r.type.shape == (None, 1, None)
Copy link
Member

Choose a reason for hiding this comment

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

The static shape is this case is just the sum of the repeat values?

You want to do something like try to constant fold the sum of repeats? We could just check if its a constant and grab the data out?

Copy link
Member Author

Choose a reason for hiding this comment

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

We could... we can always COULD

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants