-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Warn when lazy resampling upcasts non-float input to float32 (#6713) #9044
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |||||||||||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import unittest | ||||||||||||||||||||||||||||||
| import warnings | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import torch | ||||||||||||||||||||||||||||||
| from parameterized import parameterized | ||||||||||||||||||||||||||||||
|
|
@@ -45,6 +46,21 @@ def test_resample_function_impl(self, img, matrix, expected): | |||||||||||||||||||||||||||||
| out_1 = resample(img, matrix, {"lazy_resample_mode": "other value", "lazy_dtype": torch.float}) | ||||||||||||||||||||||||||||||
| self.assertIs(out.dtype, out_1.dtype) # testing dtype in different lazy_resample_mode | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_resample_warns_on_non_float_dtype(self): | ||||||||||||||||||||||||||||||
| """Lazy resampling upcasts non-floating-point inputs to float32; the user should be warned (see issue #6713).""" | ||||||||||||||||||||||||||||||
| img = convert_to_tensor(get_arange_img((3, 3)), dtype=torch.uint8) | ||||||||||||||||||||||||||||||
| with self.assertWarns(Warning): | ||||||||||||||||||||||||||||||
| out = resample(img, torch.eye(3), {"lazy_resample_mode": "auto"}) | ||||||||||||||||||||||||||||||
| self.assertIs(out.dtype, torch.float32) | ||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the test file and inspect relevant test/source behavior.
if [ -f tests/transforms/functional/test_resample.py ]; then
echo "== test file snippet =="
cat -n tests/transforms/functional/test_resample.py | sed -n '1,100p'
else
echo "tests/transforms/functional/test_resample.py not found"
fd -i 'test_resample\.py'
fi
echo "== resample definitions/usages matching warning contract =="
rg -n "resample|lazy_resample_mode|converts the input|UserWarning|Warning" tests/transforms/functional/test_resample.py -S || true
echo "== likely implementation files =="
rg -n "def resample|class .*Resample|lazy_resample_mode|converts the input|UserWarning" -S --glob '*.py' | head -n 120Repository: Project-MONAI/MONAI Length of output: 4348 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== candidate files =="
fd -i 'functional\.py$' monai/transforms/lazy || true
fd -i 'lazy.*\.py$' monai/transforms || true
rg -n "def resample\b" --glob '*.py' | head -n 80
echo "== warning strings =="
rg -n "converts the input|dtype torch\.(uint8|float)|dtype .*float32|lazy_dtype|lazy_resample_mode" --glob '*.py' | head -n 200
echo "== inspect candidate implementation =="
while IFS= read -r f; do
echo "--- $f ---"
ast-grep outline "$f" --match 'resample' --view expanded || true
cat -n "$f" | sed -n '1,220p'
done < <(fd -i 'functional\.py$' monai/transforms/lazy)Repository: Project-MONAI/MONAI Length of output: 215 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== resample definitions/usages =="
rg -n "def resample\b|class .*Resample|resample\b" --glob '*.py' | head -n 200 || true
echo "== warning strings and lazy options =="
rg -n "converts the input|lazy_dtype|lazy_resample_mode|warn\(|warnings\.warn|UserWarning|Warning" --glob '*.py' | head -n 250 || true
echo "== inspect monai/transforms/lazy/functional.py =="
wc -l monai/transforms/lazy/functional.py
cat -n monai/transforms/lazy/functional.py | sed -n '1,260p'
echo "== Python unittest assertWarns semantics probe =="
python3 - <<'PY'
import warnings, unittest
class Bad(unittest.TestCase):
def pass_warning(self):
with warnings.catch_warnings():
warnings.simplefilter("error")
with self.assertWarns(Warning):
warnings.warn("hello", DeprecationWarning)
with self.assertWarnsRegex(Warning, "hello"):
warnings.warn("hello", DeprecationWarning)
try:
Bad().pass_warning()
print("both accept unrelated warning subtype under current semantics")
except Exception as e:
print(type(e).__name__, e)
PYRepository: Project-MONAI/MONAI Length of output: 191 🏁 Script executed: #!/bin/bash
set -u
echo "== locate functional.py =="
fd -a 'functional.py$' . | sed 's#^\./##' | grep 'monai/transforms/lazy/functional.py' || true
echo "== resample references =="
rg -n "def resample\b|lazy_dtype|lazy_resample_mode|warnings\.warn|UserWarning|Warning" -S --glob '*.py' . | head -n 300
echo "== inspect monai/transforms/lazy/functional.py =="
if [ -f monai/transforms/lazy/functional.py ]; then
wc -l monai/transforms/lazy/functional.py
cat -n monai/transforms/lazy/functional.py | sed -n '1,260p'
else
echo "monai/transforms/lazy/functional.py missing"
fiRepository: Project-MONAI/MONAI Length of output: 47659 🏁 Script executed: #!/bin/bash
set -u
python3 - <<'PY'
import ast
path = "monai/transforms/lazy/utils.py"
with open(path) as f:
tree = ast.parse(f.read())
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and node.name == "resample":
file = node.lineno; col = node.col_offset
print(f"resample starts line {file} col {col}")
print(ast.get_source_segment(open(path).read(), node))
break
else:
print("resample function not found")
with open(path) as f:
for i, line in enumerate(f, start=1):
if 140 <= i <= 220:
print(f"{i}: {line.rstrip()}")
PYRepository: Project-MONAI/MONAI Length of output: 9441 Assert the warning category and message.
Proposed test change- with self.assertWarns(Warning):
+ with self.assertWarnsRegex(
+ UserWarning, r"converts the input of dtype torch\.uint8 to float32"
+ ):
out = resample(img, torch.eye(3), {"lazy_resample_mode": "auto"})📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Path instructions |
||||||||||||||||||||||||||||||
| self.assertIs(img.dtype, torch.uint8) # the input tensor itself is not mutated | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_resample_no_warning_for_float_dtype(self): | ||||||||||||||||||||||||||||||
| """Float32 inputs do not trigger the lazy resampling dtype warning.""" | ||||||||||||||||||||||||||||||
| img = convert_to_tensor(get_arange_img((3, 3)), dtype=torch.float32) | ||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||
| with warnings.catch_warnings(): | ||||||||||||||||||||||||||||||
| warnings.simplefilter("error") # turn any warning into an error | ||||||||||||||||||||||||||||||
| resample(img, torch.eye(3), {"lazy_resample_mode": "auto"}) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if __name__ == "__main__": | ||||||||||||||||||||||||||||||
| unittest.main() | ||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: Project-MONAI/MONAI
Length of output: 11556
🏁 Script executed:
Repository: Project-MONAI/MONAI
Length of output: 6928
Set an explicit caller-facing warning category and stack level.
warnings.warndefaults toUserWarning, but the dtype-conversion warning should pass it explicitly. Addstacklevel=2so the message points to the caller instead of this helper.Proposed fix
warnings.warn( f"Lazy resampling computes in floating point and converts the input of dtype {img.dtype} to " - "float32; the original data type is not preserved. For integer data such as label maps, set " - "`lazy=False` for the affected transforms (or cast back afterwards) if the data type must be preserved." + "float32; the original data type is not preserved. For integer data such as label maps, set " + "`lazy=False` for the affected transforms (or cast back afterwards) if the data type must be preserved.", + category=UserWarning, + stacklevel=2, )📝 Committable suggestion
🧰 Tools
🪛 Ruff (0.16.0)
[warning] 196-196: No explicit
stacklevelkeyword argument foundSet
stacklevel=2(B028)
🤖 Prompt for AI Agents
Source: Linters/SAST tools