Skip to content

Conversation

@locnd182644
Copy link
Contributor

Summary

Fixed incorrect output shape of Pool op when ceil_mode = true

Steps to Reproduce

Example: Create Pool Operator from PyTorch

class PoolModule(nn.Module):
    def forward(self, x):
        return torch.nn.AvgPool2d(2, 2, 1, True)(x)
class Module:
    def main(x: R.Tensor((1, 3, 17, 17), dtype="float32")) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")):
        with R.dataflow():
            lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.avg_pool2d(x, pool_size=[2, 2], strides=[2, 2], dilation=[1, 1], padding=[1, 1, 1, 1], ceil_mode=True, count_include_pad=True, layout="NCHW", out_layout="NCHW")
            gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,)
            R.output(gv)
        return gv

Expected

class Module:
    def main(x: R.Tensor((1, 3, 17, 17), dtype="float32")) -> R.Tuple(R.Tensor((1, 3, 9, 9), dtype="float32")):
        with R.dataflow():
            lv: R.Tensor((1, 3, 9, 9), dtype="float32") = R.nn.avg_pool2d(x, pool_size=[2, 2], strides=[2, 2], dilation=[1, 1], padding=[1, 1, 1, 1], ceil_mode=True, count_include_pad=True, layout="NCHW", out_layout="NCHW")
            gv: R.Tuple(R.Tensor((1, 3, 9, 9), dtype="float32")) = (lv,)
            R.output(gv)
        return gv

Resolve

PR1

…true

- Skip the last window as it would start in the bottom padded region
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @locnd182644, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical bug affecting the output shape calculation of pooling operations when ceil_mode is enabled. The fix introduces a precise mechanism to determine the correct output dimensions by verifying the validity of the last pooling window, ensuring that only valid windows contribute to the final output size. This enhancement improves the accuracy and reliability of pooling layers across various dimensions within the system.

Highlights

  • Corrected Pooling Output Shape: Fixed an issue where the output shape of pooling operations was incorrectly calculated when ceil_mode was set to true.
  • Invalid Last Window Check: Implemented a new logic to check if the last pooling window is invalid (i.e., starts in a bottom padded region) and adjusts the output dimension by subtracting one if it is.
  • Broad Application of Fix: The correction has been applied to the general N-D pooling implementation (pool_impl_nd) and specifically to 1D, 2D, and 3D pooling infer struct info functions (InferStructInfoPool1D, InferStructInfoPool2D, InferStructInfoPool3D).

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request fixes an issue with incorrect output shape calculation for pooling operations when ceil_mode=True. The change correctly implements the logic to handle edge cases for the last pooling window, aligning the behavior with frameworks like PyTorch. The fix is applied consistently across topi and relax pooling implementations. The logic appears sound and correctly addresses the reported bug. I have one suggestion to refactor the newly added logic in src/relax/op/nn/pooling.cc to reduce code duplication and improve maintainability.

Comment on lines +115 to +120
if (attrs->ceil_mode) {
PrimExpr invalid_last_w = (raw_out_w - 1) * attrs->strides[0] >= input_w + attrs->padding[0];
out_NCW_shape[2] = analyzer->Simplify(if_then_else(invalid_last_w, raw_out_w - 1, raw_out_w));
} else {
out_NCW_shape[2] = analyzer->Simplify(raw_out_w);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The logic to handle ceil_mode is duplicated across InferStructInfoPool1D, InferStructInfoPool2D, and InferStructInfoPool3D. To improve code maintainability and reduce redundancy, consider extracting this logic into a common helper function. For example:

// Placed within the relax namespace
inline PrimExpr InferPoolOutputSize(arith::Analyzer* analyzer, PrimExpr input_size,
                                    PrimExpr raw_out_size, PrimExpr stride,
                                    PrimExpr padding_before, bool ceil_mode) {
  if (ceil_mode) {
    PrimExpr invalid_last = (raw_out_size - 1) * stride >= input_size + padding_before;
    return analyzer->Simplify(if_then_else(invalid_last, raw_out_size - 1, raw_out_size));
  } else {
    return analyzer->Simplify(raw_out_size);
  }
}

Then, this block and similar blocks in InferStructInfoPool2D and InferStructInfoPool3D can be simplified to a single call, for instance:

out_NCW_shape[2] = InferPoolOutputSize(analyzer, input_w, raw_out_w, attrs->strides[0], attrs->padding[0], attrs->ceil_mode);

Copy link
Member

@tlopex tlopex left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks!

@tlopex tlopex merged commit 06a7cda into apache:main Jan 6, 2026
10 checks passed
@locnd182644 locnd182644 deleted the Relax/Op/Pool branch January 7, 2026 02:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] AveragePool operator produces wrong shape when ceil mode is set to 1

2 participants