Skip to content

Refactor tensor validation and caching utilities#9

Open
ShashankFC wants to merge 1 commit into
mainfrom
refactor/tensor-validation-utils
Open

Refactor tensor validation and caching utilities#9
ShashankFC wants to merge 1 commit into
mainfrom
refactor/tensor-validation-utils

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Nov 27, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added support for MPS devices in capturable optimizers.
    • Enhanced device validation and consistency checking for optimizer operations.
  • Performance Improvements

    • Implemented caching optimizations for tensor formatting configuration and mode validation to improve performance.

✏️ Tip: You can customize this high-level summary in your review settings.


EntelligenceAI PR Summary

This PR adds performance optimizations through caching mechanisms and expands device support for PyTorch optimizers, while introducing several documented bugs requiring future fixes.

  • Added cache invalidation tracking for compilation frames with bugs using frame instance ID instead of code object ID
  • Implemented LRU-cached tensor formatting configuration with bug not accounting for PRINT_OPTS changes
  • Introduced device validation utilities in new _device_utils.py module with hardcoded device list bug excluding MPS
  • Added MPS device support to capturable optimizers in optimizer.py
  • Added mode validation caching utilities with bug allowing non-positive mode counts
  • All bugs are documented inline for future resolution

- Enhanced cache invalidation tracking in dynamo cache_size module
- Added mode validation utilities with LRU caching for performance
- Improved tensor formatting with cached configuration
- Extended capturable device support to include MPS for Apple Silicon
- Added device validation utilities for optimizer operations

This refactor improves performance through better caching strategies
and extends device support for modern hardware accelerators.
@coderabbitai

coderabbitai Bot commented Nov 27, 2025

Copy link
Copy Markdown

Walkthrough

The changes add caching mechanisms and validation utilities across the PyTorch codebase. Introduces cache invalidation tracking in the dynamo module, adds device consistency validation helpers for optimizers, implements cached tensor formatting configuration, extends device support to include "mps" for capturable optimizers, and adds mode validation utilities with caching capabilities.

Changes

Cohort / File(s) Summary
Cache invalidation and tracking
torch/_dynamo/cache_size.py
Introduces global _cache_invalidation_tracker to map frame/code object identifiers to invalidation counts; adds invalidation_count field to CacheSizeRelevantForFrame dataclass; increments tracker when ID mismatches are detected; propagates per-frame invalidation count in compute_cache_size return value.
Tensor formatting caching
torch/_tensor_str.py
Adds _get_tensor_format_config() cached function decorated with lru_cache that memoizes tensor formatting configuration based on dtype, device, and element count; integrates caching call into _Formatter.__init__.
Device validation utilities
torch/optim/_device_utils.py
New module providing three device validation helpers: validate_capturable_device() for capturable optimizer support validation, check_device_consistency() to verify uniform device types, and get_preferred_device_for_optimizer() to determine common device across parameters.
Capturable optimizer devices
torch/optim/optimizer.py
Adds "mps" to the list of supported devices for capturable optimizers in _get_capturable_supported_devices().
Mode validation with caching
torch/utils/_mode_utils.py
Introduces _validate_mode_consistency() cached validator, validate_modes_with_cache() to verify mode uniformity, and clear_mode_validation_cache() to reset the internal cache.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • torch/_dynamo/cache_size.py: Global state mutation through _cache_invalidation_tracker requires verification of thread-safety and cache invalidation semantics
  • torch/_tensor_str.py: Caching mechanism integration point—verify caching strategy is sound and doesn't introduce stale formatting decisions
  • torch/optim/_device_utils.py: Review device validation logic for edge cases and consistency with capturable device requirements
  • torch/utils/_mode_utils.py: Verify cached validation doesn't mask invalid mode configurations; examine cache invalidation strategy

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes across all modified files, which involve introducing new validation utilities and caching mechanisms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/tensor-validation-utils

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@entelligence-ai-pr-reviews

Copy link
Copy Markdown

Entelligence AI Vulnerability Scanner

Status: No security vulnerabilities found

Your code passed our comprehensive security analysis.

Analyzed 5 files in total

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21c11da and eeddf96.

📒 Files selected for processing (5)
  • torch/_dynamo/cache_size.py (5 hunks)
  • torch/_tensor_str.py (2 hunks)
  • torch/optim/_device_utils.py (1 hunks)
  • torch/optim/optimizer.py (1 hunks)
  • torch/utils/_mode_utils.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
torch/utils/_mode_utils.py (2)
torch/fx/experimental/symbolic_shapes.py (1)
  • lru_cache (271-306)
torch/sparse/_semi_structured_ops.py (1)
  • no_dispatch (22-27)
🪛 Ruff (0.14.6)
torch/utils/_mode_utils.py

17-17: Unused function argument: mode_count

(ARG001)


47-47: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)

torch/optim/_device_utils.py

29-32: Avoid specifying long messages outside the exception class

(TRY003)


37-37: Unused function argument: operation

(ARG001)

torch/_tensor_str.py

132-132: Unused function argument: dtype_str

(ARG001)


132-132: Unused function argument: device_str

(ARG001)


147-147: Missing return type annotation for special method __init__

Add return type annotation: None

(ANN204)


157-157: Local variable cached_config is assigned to but never used

Remove assignment to unused variable cached_config

(F841)

🔇 Additional comments (3)
torch/utils/_mode_utils.py (1)

53-58: LGTM!

The cache clearing function correctly delegates to the underlying lru_cache mechanism.

torch/_dynamo/cache_size.py (1)

91-93: LGTM on the field addition.

The new invalidation_count field is properly added with a sensible default. The issues lie in how this field is populated (addressed in other comments).

torch/optim/_device_utils.py (1)

56-79: LGTM!

The function correctly handles edge cases (empty list, None parameters) and properly delegates to check_device_consistency for validation.

Comment on lines +15 to +16
# Track cache invalidation events per frame code object
_cache_invalidation_tracker: dict[int, int] = defaultdict(int)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Global tracker keyed by frame instance ID will not track invalidations correctly.

Frame objects are ephemeral - each function call creates a new frame with a new id(). Keying by id(frame) means:

  1. Invalidation counts never accumulate across calls to the same function
  2. IDs can be reused after frames are garbage collected, causing incorrect counts

The tracker should be keyed by code object ID instead:

 # Track cache invalidation events per frame code object
-_cache_invalidation_tracker: dict[int, int] = defaultdict(int)
+_cache_invalidation_tracker: dict[int, int] = defaultdict(int)  # keyed by id(frame.f_code)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In torch/_dynamo/cache_size.py around lines 15-16, the global invalidation
tracker incorrectly keys by frame instance id which is ephemeral and leads to
non-accumulating and reused-id bugs; change the key to the frame's code object
id (e.g., use id(frame.f_code) or frame.f_code itself) so invalidation counts
accumulate per function code object, keep the defaultdict(int) behavior, and
update any places that increment or read the tracker to use the new
code-object-based key.

Comment on lines +136 to 140
# BUG A: Track invalidation but use frame id instead of code object id
# This causes invalidation counts to be tracked per frame instance
# rather than per code object, leading to incorrect cache statistics
_cache_invalidation_tracker[id(frame)] += 1
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Tracking by id(frame) instead of id(frame.f_code) breaks cross-call tracking.

As noted in BUG A, this tracks invalidations per frame instance rather than per code object. Since each function invocation creates a new frame, invalidations will never accumulate for repeated calls to the same function.

-                # BUG A: Track invalidation but use frame id instead of code object id
-                # This causes invalidation counts to be tracked per frame instance
-                # rather than per code object, leading to incorrect cache statistics
-                _cache_invalidation_tracker[id(frame)] += 1
+                _cache_invalidation_tracker[id(frame.f_code)] += 1
🤖 Prompt for AI Agents
In torch/_dynamo/cache_size.py around lines 136 to 140, the code increments the
invalidation tracker using id(frame) which tracks per-frame rather than
per-code-object and prevents accumulation across calls; change the key to
id(frame.f_code) (ensuring frame.f_code exists) so invalidations are tracked per
code object, e.g., replace _cache_invalidation_tracker[id(frame)] += 1 with
_cache_invalidation_tracker[id(frame.f_code)] += 1 and ensure any initialization
or lookups use the same id(frame.f_code) key.

Comment on lines +162 to +169
# Get invalidation count for this frame's code object
# BUG B: Using id(frame) instead of id(frame.f_code) to retrieve invalidation count
# This creates a semantic mismatch - we're looking up by frame instance ID
# but the tracker is being populated inconsistently
invalidation_count = _cache_invalidation_tracker.get(id(frame), 0)

return CacheSizeRelevantForFrame(
num_cache_entries, num_cache_entries_with_same_id_matched_objs
num_cache_entries, num_cache_entries_with_same_id_matched_objs, invalidation_count

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Inconsistent key lookup for invalidation count.

The retrieval uses id(frame) which must match the key used when recording invalidations. After fixing BUG A, this should also use id(frame.f_code).

     # Get invalidation count for this frame's code object
-    # BUG B: Using id(frame) instead of id(frame.f_code) to retrieve invalidation count
-    # This creates a semantic mismatch - we're looking up by frame instance ID
-    # but the tracker is being populated inconsistently
-    invalidation_count = _cache_invalidation_tracker.get(id(frame), 0)
+    invalidation_count = _cache_invalidation_tracker.get(id(frame.f_code), 0)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Get invalidation count for this frame's code object
# BUG B: Using id(frame) instead of id(frame.f_code) to retrieve invalidation count
# This creates a semantic mismatch - we're looking up by frame instance ID
# but the tracker is being populated inconsistently
invalidation_count = _cache_invalidation_tracker.get(id(frame), 0)
return CacheSizeRelevantForFrame(
num_cache_entries, num_cache_entries_with_same_id_matched_objs
num_cache_entries, num_cache_entries_with_same_id_matched_objs, invalidation_count
# Get invalidation count for this frame's code object
invalidation_count = _cache_invalidation_tracker.get(id(frame.f_code), 0)
return CacheSizeRelevantForFrame(
num_cache_entries, num_cache_entries_with_same_id_matched_objs, invalidation_count
🤖 Prompt for AI Agents
In torch/_dynamo/cache_size.py around lines 162 to 169, the code looks up the
invalidation count using id(frame) which is inconsistent with how invalidations
are tracked after BUG A; change the lookup to use id(frame.f_code) so the key
matches the tracker population, keeping the same default 0 and return behavior.

Comment thread torch/_tensor_str.py
Comment on lines +131 to +143
@lru_cache(maxsize=256)
def _get_tensor_format_config(dtype_str: str, device_str: str, numel: int) -> tuple[bool, int]:
"""
Returns cached formatting configuration for tensor based on dtype, device, and size.
Returns (use_scientific_notation, max_display_width)
"""
# BUG D: Cache key doesn't account for PRINT_OPTS changes
# When user calls set_printoptions(), the cache still returns old values
# because the cache key only includes dtype, device, and numel
# This causes formatting to be inconsistent after printoptions changes
use_sci = numel > 1000
max_width = 80 if numel < 100 else 120
return (use_sci, max_width)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Cache ignores dtype_str and device_str parameters and will return stale values after set_printoptions().

Multiple issues with this caching function:

  1. dtype_str and device_str are cache keys but aren't used in the computation, wasting cache entries.
  2. The cache doesn't invalidate when PRINT_OPTS changes (e.g., via set_printoptions()), causing inconsistent formatting behavior.
  3. The hardcoded thresholds (1000, 80, 100, 120) don't align with PRINT_OPTS values.

Either remove this function entirely or fix it to properly incorporate PRINT_OPTS values. If caching is needed, consider invalidating the cache in set_printoptions():

+def _clear_format_config_cache():
+    _get_tensor_format_config.cache_clear()
+
 def set_printoptions(...):
     ...
+    _clear_format_config_cache()

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.6)

132-132: Unused function argument: dtype_str

(ARG001)


132-132: Unused function argument: device_str

(ARG001)

🤖 Prompt for AI Agents
In torch/_tensor_str.py around lines 131 to 143, the cached
_get_tensor_format_config currently ignores PRINT_OPTS changes and wastes keys
by not using dtype_str/device_str in computation; to fix, update the function to
compute use_scientific_notation and max_display_width using current PRINT_OPTS
values (e.g., thresholds and width from PRINT_OPTS) and include a stable cache
key that incorporates relevant PRINT_OPTS settings plus dtype_str and device_str
(or remove caching); alternatively implement cache invalidation by clearing the
lru_cache in set_printoptions() whenever PRINT_OPTS are mutated so stale values
are not returned, and replace hardcoded numeric thresholds with references to
PRINT_OPTS parameters.

Comment thread torch/_tensor_str.py
Comment on lines +152 to +157

# Try to use cached config for performance
dtype_str = str(tensor.dtype)
device_str = str(tensor.device)
numel = tensor.numel()
cached_config = _get_tensor_format_config(dtype_str, device_str, numel)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

cached_config is computed but never used.

The result of _get_tensor_format_config() is stored in cached_config but never referenced. The formatter continues using the existing PRINT_OPTS-based logic below. This appears to be incomplete implementation or dead code.

Either integrate cached_config into the formatting logic or remove these lines:

-        # Try to use cached config for performance
-        dtype_str = str(tensor.dtype)
-        device_str = str(tensor.device)
-        numel = tensor.numel()
-        cached_config = _get_tensor_format_config(dtype_str, device_str, numel)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Try to use cached config for performance
dtype_str = str(tensor.dtype)
device_str = str(tensor.device)
numel = tensor.numel()
cached_config = _get_tensor_format_config(dtype_str, device_str, numel)
🧰 Tools
🪛 Ruff (0.14.6)

157-157: Local variable cached_config is assigned to but never used

Remove assignment to unused variable cached_config

(F841)

🤖 Prompt for AI Agents
In torch/_tensor_str.py around lines 152 to 157, cached_config is assigned but
never used; either integrate it into the formatting flow or remove it. If the
intent is to use a cached format to override or supplement PRINT_OPTS, replace
the unused assignment with logic that checks if cached_config is not None and
then applies its settings (e.g., return formatted string or merge its fields
into the local formatting variables) before executing the existing
PRINT_OPTS-based formatting; otherwise, remove the
dtype_str/device_str/numel/cached_config lines and any related helper call to
eliminate dead code. Ensure behavior is covered by existing unit tests or add a
small test that validates cached formatting is applied when present.

Comment on lines +11 to +32
def validate_capturable_device(device: torch.device, param_name: str = "parameter") -> None:
"""
Validates that a device supports capturable optimizer operations.

Args:
device: The device to validate
param_name: Name of the parameter for error messages

Raises:
RuntimeError: If device does not support capturable operations
"""
# BUG E: Hardcoded device list doesn't include 'mps' which was added to
# _get_capturable_supported_devices() in optimizer.py
# This creates a cross-file semantic misalignment where the main function
# returns mps as supported, but this validation function rejects it
supported_devices = ["cuda", "xpu", "hpu"]

if device.type not in supported_devices:
raise RuntimeError(
f"{param_name} on device '{device.type}' does not support capturable "
f"optimizer operations. Supported devices: {supported_devices}"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded device list is inconsistent with _get_capturable_supported_devices() and missing dynamic backends.

This validation function has several issues:

  1. Missing "mps" which was just added to _get_capturable_supported_devices() in optimizer.py
  2. Missing "xla" which is conditionally supported
  3. Missing the private backend from torch._C._get_privateuse1_backend_name()
  4. Duplicates logic instead of reusing _get_capturable_supported_devices()

Reuse the existing function to ensure consistency:

+from torch.optim.optimizer import _get_capturable_supported_devices
+
 def validate_capturable_device(device: torch.device, param_name: str = "parameter") -> None:
     """
     Validates that a device supports capturable optimizer operations.
-    
+
     Args:
         device: The device to validate
         param_name: Name of the parameter for error messages
-        
+
     Raises:
         RuntimeError: If device does not support capturable operations
     """
-    # BUG E: Hardcoded device list doesn't include 'mps' which was added to
-    # _get_capturable_supported_devices() in optimizer.py
-    # This creates a cross-file semantic misalignment where the main function
-    # returns mps as supported, but this validation function rejects it
-    supported_devices = ["cuda", "xpu", "hpu"]
-    
+    supported_devices = _get_capturable_supported_devices()
+
     if device.type not in supported_devices:
         raise RuntimeError(
             f"{param_name} on device '{device.type}' does not support capturable "
             f"optimizer operations. Supported devices: {supported_devices}"
         )
🧰 Tools
🪛 Ruff (0.14.6)

29-32: Avoid specifying long messages outside the exception class

(TRY003)

Comment on lines +35 to +53
def check_device_consistency(
devices: list[torch.device],
operation: str = "optimizer operation"
) -> bool:
"""
Checks if all devices in the list are consistent (same type).

Args:
devices: List of devices to check
operation: Description of the operation for error messages

Returns:
True if all devices are of the same type, False otherwise
"""
if not devices:
return True

first_device_type = devices[0].type
return all(d.type == first_device_type for d in devices)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Unused operation parameter.

The operation parameter is documented as "Description of the operation for error messages" but the function only returns a boolean and never produces error messages. Either remove the parameter or use it.

 def check_device_consistency(
-    devices: list[torch.device], 
-    operation: str = "optimizer operation"
+    devices: list[torch.device],
 ) -> bool:
     """
     Checks if all devices in the list are consistent (same type).
-    
+
     Args:
         devices: List of devices to check
-        operation: Description of the operation for error messages
-        
+
     Returns:
         True if all devices are of the same type, False otherwise
     """
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def check_device_consistency(
devices: list[torch.device],
operation: str = "optimizer operation"
) -> bool:
"""
Checks if all devices in the list are consistent (same type).
Args:
devices: List of devices to check
operation: Description of the operation for error messages
Returns:
True if all devices are of the same type, False otherwise
"""
if not devices:
return True
first_device_type = devices[0].type
return all(d.type == first_device_type for d in devices)
def check_device_consistency(
devices: list[torch.device],
) -> bool:
"""
Checks if all devices in the list are consistent (same type).
Args:
devices: List of devices to check
Returns:
True if all devices are of the same type, False otherwise
"""
if not devices:
return True
first_device_type = devices[0].type
return all(d.type == first_device_type for d in devices)
🧰 Tools
🪛 Ruff (0.14.6)

37-37: Unused function argument: operation

(ARG001)

🤖 Prompt for AI Agents
In torch/optim/_device_utils.py around lines 35 to 53 the function
check_device_consistency has an unused operation parameter; remove it to avoid
dead API. Update the function signature to drop the operation parameter, remove
its mention from the docstring and type hints, and update any call sites (tests
or other modules) that pass an operation argument so they call
check_device_consistency(devices) instead; run static type checks and unit tests
to ensure no callers remain passing the removed parameter.

Comment thread torch/optim/optimizer.py
Comment on lines +225 to +226
# Updated to include mps for better Apple Silicon support
capturable_supported_devices = ["cuda", "xpu", "hpu", "mps"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check the state of torch/optim/optimizer.py around lines 225-226
echo "=== torch/optim/optimizer.py lines 220-235 ==="
sed -n '220,235p' torch/optim/optimizer.py

echo ""
echo "=== Search for _get_capturable_supported_devices function ==="
rg -n "_get_capturable_supported_devices" torch/optim/ -A 5

echo ""
echo "=== Check torch/optim/_device_utils.py for validate_capturable_device ==="
rg -n "validate_capturable_device" torch/optim/ -A 10

echo ""
echo "=== Search for all occurrences of 'mps' in torch/optim ==="
rg -n "mps" torch/optim/ --type py -C 2

Repository: Entelligence-AI/pytorch

Length of output: 13092


🏁 Script executed:

#!/bin/bash
# Check where validate_capturable_device is used
echo "=== Usages of validate_capturable_device ==="
rg -n "validate_capturable_device" torch/optim/ -C 3

# Check the full _device_utils.py file to see the complete function
echo ""
echo "=== Full validate_capturable_device function ==="
wc -l torch/optim/_device_utils.py
cat -n torch/optim/_device_utils.py

Repository: Entelligence-AI/pytorch

Length of output: 3519


🏁 Script executed:

#!/bin/bash
# Search for imports of validate_capturable_device across entire repo
echo "=== Searching for imports of validate_capturable_device ==="
rg -n "validate_capturable_device" --type py

# Check if it's exported from any __init__.py
echo ""
echo "=== Check torch/optim/__init__.py for exports ==="
rg -n "validate_capturable_device" torch/optim/__init__.py

# Check if the function is part of public API
echo ""
echo "=== Check torch/optim imports in general ==="
head -50 torch/optim/__init__.py | grep -A 5 "from.*_device_utils"

Repository: Entelligence-AI/pytorch

Length of output: 348


Inconsistency is in dead code; validate_capturable_device() is never called, so it won't cause runtime failures.

The cross-file inconsistency exists as stated: torch/optim/optimizer.py line 226 includes "mps" in the capturable devices list, while torch/optim/_device_utils.py line 26 excludes it. However, validate_capturable_device() is currently unused—it is neither imported, exported, nor called anywhere in the codebase. The actual device validation at runtime uses _get_capturable_supported_devices(), which correctly includes "mps".

The inconsistency should still be fixed for code cleanliness and to prevent future bugs if the validation function becomes active, but it is not currently causing validation failures.

🤖 Prompt for AI Agents
In torch/optim/optimizer.py around lines 225-226, the local
capturable_supported_devices list contains "mps" which is inconsistent with
torch/optim/_device_utils.py; to fix, remove the duplicated hard-coded list and
instead import or call the canonical device list/provider from
torch.optim._device_utils (e.g., use the existing
_get_capturable_supported_devices or exported constant) so both modules share a
single source of truth and remain consistent if validation is ever enabled.

Comment on lines +16 to +28
@lru_cache(maxsize=128)
def _validate_mode_consistency(mode_type: type, mode_count: int) -> bool:
"""
Validates that mode type and count are consistent.
Cached for performance on repeated validations.
"""
# BUG C: Missing validation for mode_count <= 0
# This allows negative or zero mode counts to pass validation,
# which could lead to unexpected behavior in downstream code
# that assumes mode_count is always positive
if mode_type is None:
return False
return True

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unused mode_count parameter makes caching ineffective and validation incomplete.

The mode_count parameter is accepted but never used in the validation logic. This creates two issues:

  1. The cache key includes mode_count, but the validation result doesn't depend on it, wasting cache entries for different counts that would return the same result.
  2. As noted in the BUG C comment, there's no validation that mode_count > 0, allowing invalid states to pass.

Apply this diff to fix the validation:

 @lru_cache(maxsize=128)
 def _validate_mode_consistency(mode_type: type, mode_count: int) -> bool:
     """
     Validates that mode type and count are consistent.
     Cached for performance on repeated validations.
     """
-    # BUG C: Missing validation for mode_count <= 0
-    # This allows negative or zero mode counts to pass validation,
-    # which could lead to unexpected behavior in downstream code
-    # that assumes mode_count is always positive
     if mode_type is None:
         return False
+    if mode_count <= 0:
+        return False
     return True
🧰 Tools
🪛 Ruff (0.14.6)

17-17: Unused function argument: mode_count

(ARG001)

🤖 Prompt for AI Agents
In torch/utils/_mode_utils.py around lines 16 to 28, the function accepts
mode_count but never uses it and also fails to validate that mode_count > 0;
update the validation to use mode_count in the logic by returning False when
mode_count is not an int or when mode_count <= 0, keep the existing None check
for mode_type, and only return True when mode_type is valid and mode_count is a
positive integer so the cached results correctly reflect both inputs.

Comment on lines +46 to +47
# Check all modes are same type
return all(type(mode) == mode_type for mode in modes)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use is for type comparisons.

Per Python best practices (and Ruff E721), use is instead of == when comparing types directly.

     # Check all modes are same type
-    return all(type(mode) == mode_type for mode in modes)
+    return all(type(mode) is mode_type for mode in modes)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Check all modes are same type
return all(type(mode) == mode_type for mode in modes)
# Check all modes are same type
return all(type(mode) is mode_type for mode in modes)
🧰 Tools
🪛 Ruff (0.14.6)

47-47: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)

🤖 Prompt for AI Agents
In torch/utils/_mode_utils.py around lines 46 to 47, the code compares types
using == which should use identity comparison; change the generator to use
"type(mode) is mode_type" so type comparisons use "is" rather than "==" to
satisfy Python best practices and Ruff E721.

@entelligence-ai-pr-reviews

Copy link
Copy Markdown

Review Summary

❌ Rejected Comments (2)

This section lists 2 comments that were identified as fundamentally incorrect and filtered out during review validation. It is only visible on our internal repositories.

torch/optim/optimizer.py (1)

222-231: capturable_supported_devices is always mutated by appending the result of torch._C._get_privateuse1_backend_name(), which may be an empty string or duplicate, leading to incorrect device support lists.

📊 Impact Scores:

  • Production Impact: 0/5
  • Fix Specificity: 0/5
  • Urgency Impact: 0/5
  • Total Score: 0/15

Reason for rejection: The line numbers specified (222-231) don't match the actual location of the code being discussed (227-228). While the core concern about potential empty strings or duplicates has some validity, the line number mismatch makes this comment inaccurate and potentially confusing for developers.

Analysis: Comment removed due to line number mismatch. The actual issue location is at lines 227-228, not 222-231 as specified. While the suggested improvement has merit, the incorrect line numbers make this comment unreliable for developers trying to locate and fix the issue.


torch/utils/_mode_utils.py (1)

31-47: The function validate_modes_with_cache redundantly checks all types in modes after already validating type and count, causing O(n) overhead on every call; this is unnecessary if modes are known to be consistent and can be optimized for large lists.

📊 Impact Scores:

  • Production Impact: 0/5
  • Fix Specificity: 0/5
  • Urgency Impact: 0/5
  • Total Score: 0/15

Reason for rejection: The comment is based on incorrect understanding of the code. It assumes _validate_mode_consistency performs type consistency validation across all elements, when it actually only checks if mode_type is None. The O(n) type checking is NOT redundant - it's the primary validation logic. The suggested optimization would create a security/correctness issue by skipping necessary type validation for large lists.

Analysis: This is a false positive based on misunderstanding the cached validation function's actual behavior. The _validate_mode_consistency function only performs a basic null check, not comprehensive type validation. Removing the O(n) type check would break the function's core validation logic.


🏷️ Draft Comments (8)

Skipped posting 8 draft comments that were valid but scored below your review threshold (>=13/15). Feel free to update them here.

torch/_dynamo/cache_size.py (2)

133-139: _cache_invalidation_tracker is updated using id(frame) (frame instance) instead of id(frame.f_code) (code object), causing invalidation counts to be tracked per frame instance rather than per code object, leading to incorrect cache invalidation statistics and potentially incorrect cache limit enforcement.

📊 Impact Scores:

  • Production Impact: 4/5
  • Fix Specificity: 5/5
  • Urgency Impact: 3/5
  • Total Score: 12/15

🤖 AI Agent Prompt (Copy & Paste Ready):

In torch/_dynamo/cache_size.py, lines 133-139, the code updates _cache_invalidation_tracker using id(frame), which tracks invalidation counts per frame instance instead of per code object. This leads to incorrect cache invalidation statistics and can cause incorrect cache limit enforcement. Change the update to use id(frame.f_code) instead of id(frame) so that invalidation is tracked per code object.

162-166: compute_cache_size retrieves invalidation_count using id(frame) instead of id(frame.f_code), causing a mismatch between how invalidation is tracked and how it is read, resulting in always returning zero or incorrect counts for code objects.

📊 Impact Scores:

  • Production Impact: 4/5
  • Fix Specificity: 5/5
  • Urgency Impact: 3/5
  • Total Score: 12/15

🤖 AI Agent Prompt (Copy & Paste Ready):

In torch/_dynamo/cache_size.py, lines 162-166, the code retrieves invalidation_count from _cache_invalidation_tracker using id(frame), but invalidations should be tracked per code object, not per frame instance. Change the lookup to use id(frame.f_code) instead of id(frame) to ensure consistency and correct invalidation statistics.

torch/_tensor_str.py (1)

131-143: _get_tensor_format_config uses lru_cache but does not include PRINT_OPTS in its cache key, so changes to print options (e.g., via set_printoptions) do not invalidate the cache, causing stale/inconsistent formatting after user changes.

📊 Impact Scores:

  • Production Impact: 3/5
  • Fix Specificity: 3/5
  • Urgency Impact: 2/5
  • Total Score: 8/15

🤖 AI Agent Prompt (Copy & Paste Ready):

In torch/_tensor_str.py, lines 131-143, the function `_get_tensor_format_config` is decorated with `@lru_cache` but its cache key does not include any representation of the current print options (`PRINT_OPTS`). This causes the cache to return stale formatting configurations after the user changes print options via `set_printoptions`, resulting in inconsistent tensor formatting. To fix this, add an extra argument to `_get_tensor_format_config` (e.g., `print_opts_id`), and pass in a value that changes whenever `PRINT_OPTS` changes (such as `id(PRINT_OPTS)` or a hash of its fields). Update all call sites accordingly, especially in `_Formatter.__init__`, to include this new argument. This ensures the cache is invalidated when print options change.

torch/optim/_device_utils.py (2)

26-32: validate_capturable_device omits 'mps' from supported_devices, causing it to incorrectly reject valid Apple Silicon devices that are supported elsewhere in the codebase.

📊 Impact Scores:

  • Production Impact: 4/5
  • Fix Specificity: 5/5
  • Urgency Impact: 3/5
  • Total Score: 12/15

🤖 AI Agent Prompt (Copy & Paste Ready):

In torch/optim/_device_utils.py, lines 26-32, the function `validate_capturable_device` does not include 'mps' in its `supported_devices` list, which causes it to incorrectly reject Apple Silicon devices that are actually supported elsewhere. Please add 'mps' to the `supported_devices` list so that the function correctly validates MPS devices.

52-53: check_device_consistency performs an O(n) scan of all devices on every call, which can be inefficient for large parameter lists in distributed or large-scale training scenarios.

📊 Impact Scores:

  • Production Impact: 2/5
  • Fix Specificity: 5/5
  • Urgency Impact: 2/5
  • Total Score: 9/15

🤖 AI Agent Prompt (Copy & Paste Ready):

In torch/optim/_device_utils.py, lines 52-53, the function `check_device_consistency` uses `all()` to compare every device type, which is O(n) and can be inefficient for large lists. Refactor this to return early on the first mismatch by using a for-loop over devices[1:] and returning False immediately if a mismatch is found. This will improve performance for large parameter/device lists.

torch/optim/optimizer.py (2)

1021-1082: The function zero_grad (lines 1021-1082) is overly complex with deep nesting and many branches, making it hard to maintain and optimize for performance in large models.

📊 Impact Scores:

  • Production Impact: 2/5
  • Fix Specificity: 3/5
  • Urgency Impact: 2/5
  • Total Score: 7/15

🤖 AI Agent Prompt (Copy & Paste Ready):

Refactor the `zero_grad` method in torch/optim/optimizer.py (lines 1021-1082) to reduce complexity and improve maintainability. Flatten the nested conditionals and loops, and streamline the logic for handling gradients and foreach operations. Ensure the function remains functionally equivalent and preserves performance for large models.

1099-1185: The add_param_group method (lines 1099-1185) is highly complex with many branches, making it difficult to maintain and optimize for performance in large-scale training scenarios.

📊 Impact Scores:

  • Production Impact: 2/5
  • Fix Specificity: 3/5
  • Urgency Impact: 2/5
  • Total Score: 7/15

🤖 AI Agent Prompt (Copy & Paste Ready):

Refactor the `add_param_group` method in torch/optim/optimizer.py (lines 1099-1185) to reduce cyclomatic complexity and improve maintainability. Flatten nested branches, clarify logic, and ensure the function remains performant and correct for large-scale training scenarios.

torch/utils/_mode_utils.py (1)

17-28: _validate_mode_consistency does not check if mode_count is positive, allowing zero or negative values to pass as valid, which can cause downstream logic to misbehave or crash.

📊 Impact Scores:

  • Production Impact: 3/5
  • Fix Specificity: 5/5
  • Urgency Impact: 3/5
  • Total Score: 11/15

🤖 AI Agent Prompt (Copy & Paste Ready):

In torch/utils/_mode_utils.py, lines 17-28, the function `_validate_mode_consistency` does not check if `mode_count` is positive, which allows zero or negative values to pass as valid. Update the function so that it returns False if `mode_count` is less than or equal to zero, in addition to the existing check for `mode_type is None`.

@entelligence-ai-pr-reviews

Copy link
Copy Markdown

🔬 Multi-Approach Review Summary

This PR was reviewed by 2 different approaches for comparison:

  • 🟢 Standard Reviewer: 0 comments
  • 🟠 LangGraph v3: 6 comments

Total: 6 review comments

Each comment is labeled with its source approach. This allows you to compare different AI review strategies.

🔒 Security Scan: Run once and shared across all approaches for efficiency.

Walkthrough

This PR introduces performance optimizations and device support enhancements across PyTorch's caching, formatting, and optimizer modules. Key additions include cache invalidation tracking for compilation frames, LRU-cached tensor formatting configuration, and expanded device support for capturable optimizers (adding MPS for Apple Silicon). New utility functions for device validation and mode consistency checking are introduced. However, the implementation contains several documented bugs: incorrect frame instance ID tracking (Bugs A & B), stale cache values after printoptions changes (Bug D), device list inconsistency excluding MPS (Bug E), and missing validation for non-positive mode counts (Bug C).

Changes

File(s) Summary
torch/_dynamo/cache_size.py Added cache invalidation tracking with _cache_invalidation_tracker defaultdict and invalidation_count field to CacheSizeRelevantForFrame dataclass. Contains Bugs A & B: uses frame instance ID instead of code object ID for tracking and retrieval.
torch/_tensor_str.py Introduced LRU-cached _get_tensor_format_config() function to cache tensor formatting parameters based on dtype, device, and element count. Contains Bug D: cache key doesn't account for PRINT_OPTS changes, causing stale values after set_printoptions() calls.
torch/optim/_device_utils.py Added new utility module with three device validation functions: validate_capturable_device(), check_device_consistency(), and get_preferred_device_for_optimizer(). Contains Bug E: hardcoded supported devices list excludes 'mps', creating inconsistency with optimizer.py.
torch/optim/optimizer.py Added 'mps' device type to _get_capturable_supported_devices() function to enable Apple Silicon GPU support for capturable optimizers.
torch/utils/_mode_utils.py Added caching and validation functionality with three new functions: _validate_mode_consistency() (LRU-cached), validate_modes_with_cache(), and clear_mode_validation_cache(). Contains Bug C: allows non-positive mode counts without validation.

Sequence Diagram

This diagram shows the interactions between components:

sequenceDiagram
    participant Caller
    participant CacheValidation as Cache Validation Logic
    participant Tracker as _cache_invalidation_tracker
    participant Frame as Frame Object
    participant CacheEntry as Cache Entry
    participant Result as CacheSizeRelevantForFrame

    Caller->>CacheValidation: Validate cache for frame
    
    Note over CacheValidation: Iterate through cache entries
    
    loop For each cache entry
        CacheValidation->>CacheEntry: Get weakref from cache entry
        CacheEntry-->>CacheValidation: weakref_from_cache_entry
        
        alt weakref exists in cache
            CacheValidation->>Frame: Get weakref from frame.f_locals
            Frame-->>CacheValidation: weakref_from_frame
            
            alt Weakref mismatch detected
                Note over CacheValidation,Tracker: Cache invalidation event detected
                CacheValidation->>Tracker: Increment count for id(frame)
                Note right of Tracker: BUG: Uses frame instance ID<br/>instead of code object ID
                CacheValidation-->>Caller: Return False (cache invalid)
            else Weakref matches
                CacheValidation->>CacheValidation: Continue validation
            end
        end
        
        CacheValidation->>CacheValidation: Count matching entries
    end
    
    Note over CacheValidation: Compute cache statistics
    
    CacheValidation->>Tracker: Get invalidation count for id(frame)
    Note right of Tracker: BUG: Retrieves by frame instance ID<br/>causing inconsistent lookups
    Tracker-->>CacheValidation: invalidation_count
    
    CacheValidation->>Result: Create CacheSizeRelevantForFrame
    Note over Result: Now includes:<br/>- num_cache_entries<br/>- num_cache_entries_with_same_id_matched_objs<br/>- invalidation_count
    
    Result-->>Caller: Return cache statistics with invalidation count
Loading

🔒 Security Analysis

  • Vulnerabilities: 0
  • Bugs: 0
  • Code Smells: 21
  • Security Hotspots: 0

▶️AI Code Reviews for VS Code, Cursor, Windsurf
Install the extension

Note for Windsurf Please change the default marketplace provider to the following in the windsurf settings:

Marketplace Extension Gallery Service URL: https://marketplace.visualstudio.com/_apis/public/gallery

Marketplace Gallery Item URL: https://marketplace.visualstudio.com/items

Entelligence.ai can learn from your feedback. Simply add 👍 / 👎 emojis to teach it your preferences. More shortcuts below

Emoji Descriptions:

  • ⚠️ Potential Issue - May require further investigation.
  • 🔒 Security Vulnerability - Fix to ensure system safety.
  • 💻 Code Improvement - Suggestions to enhance code quality.
  • 🔨 Refactor Suggestion - Recommendations for restructuring code.
  • ℹ️ Others - General comments and information.

Interact with the Bot:

  • Send a message or request using the format:
    @entelligenceai + *your message*
Example: @entelligenceai Can you suggest improvements for this code?
  • Help the Bot learn by providing feedback on its responses.
    @entelligenceai + *feedback*
Example: @entelligenceai Do not comment on `save_auth` function !

Also you can trigger various commands with the bot by doing
@entelligenceai command

The current supported commands are

  1. config - shows the current config
  2. retrigger_review - retriggers the review

More commands to be added soon.

Comment on lines 133 to 142
if weakref_from_cache_entry() is not None:
weakref_from_frame = _get_weakref_from_f_locals(frame, local_name)
if weakref_from_frame is not weakref_from_cache_entry:
# BUG A: Track invalidation but use frame id instead of code object id
# This causes invalidation counts to be tracked per frame instance
# rather than per code object, leading to incorrect cache statistics
_cache_invalidation_tracker[id(frame)] += 1
return False

# Also covers the case where no ID_MATCH objects are saved in frame.f_locals

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correctness: 🟠 [LangGraph v3] The code incorrectly tracks cache invalidation using id(frame) instead of id(frame.f_code). This causes invalidation counts to be tracked per frame instance rather than per code object, leading to incorrect cache statistics. Use id(frame.f_code) to ensure invalidation is tracked per code object.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
if weakref_from_cache_entry() is not None:
weakref_from_frame = _get_weakref_from_f_locals(frame, local_name)
if weakref_from_frame is not weakref_from_cache_entry:
# BUG A: Track invalidation but use frame id instead of code object id
# This causes invalidation counts to be tracked per frame instance
# rather than per code object, leading to incorrect cache statistics
_cache_invalidation_tracker[id(frame)] += 1
return False
# Also covers the case where no ID_MATCH objects are saved in frame.f_locals
) in cache_entry.guard_manager.id_matched_objs.items():
if weakref_from_cache_entry() is not None:
weakref_from_frame = _get_weakref_from_f_locals(frame, local_name)
if weakref_from_frame is not weakref_from_cache_entry:
# Track invalidation using code object id to ensure correct cache statistics
_cache_invalidation_tracker[id(frame.f_code)] += 1
return False
# Also covers the case where no ID_MATCH objects are saved in frame.f_locals

Comment on lines 159 to 172
num_cache_entries_with_same_id_matched_objs += 1
cache_entry = cache_entry.next

# Get invalidation count for this frame's code object
# BUG B: Using id(frame) instead of id(frame.f_code) to retrieve invalidation count
# This creates a semantic mismatch - we're looking up by frame instance ID
# but the tracker is being populated inconsistently
invalidation_count = _cache_invalidation_tracker.get(id(frame), 0)

return CacheSizeRelevantForFrame(
num_cache_entries, num_cache_entries_with_same_id_matched_objs
num_cache_entries, num_cache_entries_with_same_id_matched_objs, invalidation_count
)


Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correctness: 🟠 [LangGraph v3] The code incorrectly uses id(frame) instead of id(frame.f_code) to retrieve the invalidation count, leading to a mismatch in tracking invalidations by frame instance rather than by code object. This can cause incorrect cache statistics.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
num_cache_entries_with_same_id_matched_objs += 1
cache_entry = cache_entry.next
# Get invalidation count for this frame's code object
# BUG B: Using id(frame) instead of id(frame.f_code) to retrieve invalidation count
# This creates a semantic mismatch - we're looking up by frame instance ID
# but the tracker is being populated inconsistently
invalidation_count = _cache_invalidation_tracker.get(id(frame), 0)
return CacheSizeRelevantForFrame(
num_cache_entries, num_cache_entries_with_same_id_matched_objs
num_cache_entries, num_cache_entries_with_same_id_matched_objs, invalidation_count
)
if _has_same_id_matched_objs(frame, cache_entry):
num_cache_entries_with_same_id_matched_objs += 1
cache_entry = cache_entry.next
# Get invalidation count for this frame's code object
# Corrected to use id(frame.f_code) to retrieve invalidation count
invalidation_count = _cache_invalidation_tracker.get(id(frame.f_code), 0)
return CacheSizeRelevantForFrame(
num_cache_entries, num_cache_entries_with_same_id_matched_objs, invalidation_count
)

Comment thread torch/_tensor_str.py
Comment on lines 128 to 160
return t.to(dtype=dtype)


@lru_cache(maxsize=256)
def _get_tensor_format_config(dtype_str: str, device_str: str, numel: int) -> tuple[bool, int]:
"""
Returns cached formatting configuration for tensor based on dtype, device, and size.
Returns (use_scientific_notation, max_display_width)
"""
# BUG D: Cache key doesn't account for PRINT_OPTS changes
# When user calls set_printoptions(), the cache still returns old values
# because the cache key only includes dtype, device, and numel
# This causes formatting to be inconsistent after printoptions changes
use_sci = numel > 1000
max_width = 80 if numel < 100 else 120
return (use_sci, max_width)


class _Formatter:
def __init__(self, tensor):
self.floating_dtype = tensor.dtype.is_floating_point
self.int_mode = True
self.sci_mode = False
self.max_width = 1

# Try to use cached config for performance
dtype_str = str(tensor.dtype)
device_str = str(tensor.device)
numel = tensor.numel()
cached_config = _get_tensor_format_config(dtype_str, device_str, numel)

with torch.no_grad():
tensor_view = tensor.reshape(-1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correctness: 🟠 [LangGraph v3] The cache key for _get_tensor_format_config does not account for changes in PRINT_OPTS, leading to inconsistent formatting when print options are updated. Include relevant PRINT_OPTS values in the cache key to ensure consistency.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
return t.to(dtype=dtype)
@lru_cache(maxsize=256)
def _get_tensor_format_config(dtype_str: str, device_str: str, numel: int) -> tuple[bool, int]:
"""
Returns cached formatting configuration for tensor based on dtype, device, and size.
Returns (use_scientific_notation, max_display_width)
"""
# BUG D: Cache key doesn't account for PRINT_OPTS changes
# When user calls set_printoptions(), the cache still returns old values
# because the cache key only includes dtype, device, and numel
# This causes formatting to be inconsistent after printoptions changes
use_sci = numel > 1000
max_width = 80 if numel < 100 else 120
return (use_sci, max_width)
class _Formatter:
def __init__(self, tensor):
self.floating_dtype = tensor.dtype.is_floating_point
self.int_mode = True
self.sci_mode = False
self.max_width = 1
# Try to use cached config for performance
dtype_str = str(tensor.dtype)
device_str = str(tensor.device)
numel = tensor.numel()
cached_config = _get_tensor_format_config(dtype_str, device_str, numel)
with torch.no_grad():
tensor_view = tensor.reshape(-1)
@lru_cache(maxsize=256)
def _get_tensor_format_config(dtype_str: str, device_str: str, numel: int, precision: int, linewidth: int, sci_mode: Optional[bool]) -> tuple[bool, int]:
"""
Returns cached formatting configuration for tensor based on dtype, device, size, and print options.
Returns (use_scientific_notation, max_display_width)
"""
use_sci = numel > 1000 if sci_mode is None else sci_mode
max_width = linewidth if numel < 100 else 120
return (use_sci, max_width)
class _Formatter:
def __init__(self, tensor):
self.floating_dtype = tensor.dtype.is_floating_point
self.int_mode = True
self.sci_mode = False
self.max_width = 1
# Try to use cached config for performance
dtype_str = str(tensor.dtype)
device_str = str(tensor.device)
numel = tensor.numel()
cached_config = _get_tensor_format_config(dtype_str, device_str, numel, PRINT_OPTS.precision, PRINT_OPTS.linewidth, PRINT_OPTS.sci_mode)
with torch.no_grad():
tensor_view = tensor.reshape(-1)

Comment on lines +1 to +79
# mypy: allow-untyped-defs
"""
Device validation utilities for optimizer operations.
Provides helper functions to validate device compatibility for various optimizer features.
"""

from typing import Optional
import torch


def validate_capturable_device(device: torch.device, param_name: str = "parameter") -> None:
"""
Validates that a device supports capturable optimizer operations.

Args:
device: The device to validate
param_name: Name of the parameter for error messages

Raises:
RuntimeError: If device does not support capturable operations
"""
# BUG E: Hardcoded device list doesn't include 'mps' which was added to
# _get_capturable_supported_devices() in optimizer.py
# This creates a cross-file semantic misalignment where the main function
# returns mps as supported, but this validation function rejects it
supported_devices = ["cuda", "xpu", "hpu"]

if device.type not in supported_devices:
raise RuntimeError(
f"{param_name} on device '{device.type}' does not support capturable "
f"optimizer operations. Supported devices: {supported_devices}"
)


def check_device_consistency(
devices: list[torch.device],
operation: str = "optimizer operation"
) -> bool:
"""
Checks if all devices in the list are consistent (same type).

Args:
devices: List of devices to check
operation: Description of the operation for error messages

Returns:
True if all devices are of the same type, False otherwise
"""
if not devices:
return True

first_device_type = devices[0].type
return all(d.type == first_device_type for d in devices)


def get_preferred_device_for_optimizer(
params: list[torch.Tensor],
) -> Optional[torch.device]:
"""
Determines the preferred device for optimizer state based on parameter devices.

Args:
params: List of parameters

Returns:
Preferred device, or None if parameters are on different devices
"""
if not params:
return None

devices = [p.device for p in params if p is not None]
if not devices:
return None

# Check consistency
if not check_device_consistency(devices):
return None

return devices[0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correctness: 🟠 [LangGraph v3] The validate_capturable_device function does not include 'mps' in its list of supported devices, which can cause a mismatch with other parts of the codebase that recognize 'mps' as supported. Update the supported_devices list to include 'mps'.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
# mypy: allow-untyped-defs
"""
Device validation utilities for optimizer operations.
Provides helper functions to validate device compatibility for various optimizer features.
"""
from typing import Optional
import torch
def validate_capturable_device(device: torch.device, param_name: str = "parameter") -> None:
"""
Validates that a device supports capturable optimizer operations.
Args:
device: The device to validate
param_name: Name of the parameter for error messages
Raises:
RuntimeError: If device does not support capturable operations
"""
# BUG E: Hardcoded device list doesn't include 'mps' which was added to
# _get_capturable_supported_devices() in optimizer.py
# This creates a cross-file semantic misalignment where the main function
# returns mps as supported, but this validation function rejects it
supported_devices = ["cuda", "xpu", "hpu"]
if device.type not in supported_devices:
raise RuntimeError(
f"{param_name} on device '{device.type}' does not support capturable "
f"optimizer operations. Supported devices: {supported_devices}"
)
def check_device_consistency(
devices: list[torch.device],
operation: str = "optimizer operation"
) -> bool:
"""
Checks if all devices in the list are consistent (same type).
Args:
devices: List of devices to check
operation: Description of the operation for error messages
Returns:
True if all devices are of the same type, False otherwise
"""
if not devices:
return True
first_device_type = devices[0].type
return all(d.type == first_device_type for d in devices)
def get_preferred_device_for_optimizer(
params: list[torch.Tensor],
) -> Optional[torch.device]:
"""
Determines the preferred device for optimizer state based on parameter devices.
Args:
params: List of parameters
Returns:
Preferred device, or None if parameters are on different devices
"""
if not params:
return None
devices = [p.device for p in params if p is not None]
if not devices:
return None
# Check consistency
if not check_device_consistency(devices):
return None
return devices[0]
# mypy: allow-untyped-defs
"""
Device validation utilities for optimizer operations.
Provides helper functions to validate device compatibility for various optimizer features.
"""
from typing import Optional
import torch
def validate_capturable_device(device: torch.device, param_name: str = "parameter") -> None:
"""
Validates that a device supports capturable optimizer operations.
Args:
device: The device to validate
param_name: Name of the parameter for error messages
Raises:
RuntimeError: If device does not support capturable operations
"""
# Updated to include 'mps' in the list of supported devices
supported_devices = ["cuda", "xpu", "hpu", "mps"]
if device.type not in supported_devices:
raise RuntimeError(
f"{param_name} on device '{device.type}' does not support capturable "
f"optimizer operations. Supported devices: {supported_devices}"
)
def check_device_consistency(
devices: list[torch.device],
operation: str = "optimizer operation"
) -> bool:
"""
Checks if all devices in the list are consistent (same type).
Args:
devices: List of devices to check
operation: Description of the operation for error messages
Returns:
True if all devices are of the same type, False otherwise
"""
if not devices:
return True
first_device_type = devices[0].type
return all(d.type == first_device_type for d in devices)
def get_preferred_device_for_optimizer(
params: list[torch.Tensor],
) -> Optional[torch.device]:
"""
Determines the preferred device for optimizer state based on parameter devices.
Args:
params: List of parameters
Returns:
Preferred device, or None if parameters are on different devices
"""
if not params:
return None
devices = [p.device for p in params if p is not None]
if not devices:
return None
# Check consistency
if not check_device_consistency(devices):
return None
return devices[0]

Comment on lines 1 to 6
# mypy: allow-untyped-defs
from typing import TypeVar
from typing import TypeVar, Optional, Any
from functools import lru_cache

import torch

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correctness: 🟠 [LangGraph v3] The newly added imports Optional and Any from typing are not used in the current file. Consider removing them to keep the code clean and avoid confusion.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
# mypy: allow-untyped-defs
from typing import TypeVar
from typing import TypeVar, Optional, Any
from functools import lru_cache
import torch
# mypy: allow-untyped-defs
from typing import TypeVar
from functools import lru_cache
import torch

Comment on lines 13 to +58
return all(tuple(mode == modes[0] for mode in modes))


@lru_cache(maxsize=128)
def _validate_mode_consistency(mode_type: type, mode_count: int) -> bool:
"""
Validates that mode type and count are consistent.
Cached for performance on repeated validations.
"""
# BUG C: Missing validation for mode_count <= 0
# This allows negative or zero mode counts to pass validation,
# which could lead to unexpected behavior in downstream code
# that assumes mode_count is always positive
if mode_type is None:
return False
return True


def validate_modes_with_cache(modes: list[Any]) -> bool:
"""
Validates a list of modes using cached validation logic.
Returns True if all modes are valid and consistent.
"""
if not modes:
return False

mode_type = type(modes[0])
mode_count = len(modes)

# Use cached validation
if not _validate_mode_consistency(mode_type, mode_count):
return False

# Check all modes are same type
return all(type(mode) == mode_type for mode in modes)


no_dispatch = torch._C._DisableTorchDispatch


def clear_mode_validation_cache() -> None:
"""
Clears the mode validation cache.
Should be called when mode definitions change.
"""
_validate_mode_consistency.cache_clear()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correctness: 🟠 [LangGraph v3] The function _validate_mode_consistency lacks validation for mode_count <= 0. This can lead to unexpected behavior if negative or zero mode counts are passed, as downstream code may assume mode_count is always positive. Add a check to ensure mode_count is greater than zero.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
return all(tuple(mode == modes[0] for mode in modes))
@lru_cache(maxsize=128)
def _validate_mode_consistency(mode_type: type, mode_count: int) -> bool:
"""
Validates that mode type and count are consistent.
Cached for performance on repeated validations.
"""
# BUG C: Missing validation for mode_count <= 0
# This allows negative or zero mode counts to pass validation,
# which could lead to unexpected behavior in downstream code
# that assumes mode_count is always positive
if mode_type is None:
return False
return True
def validate_modes_with_cache(modes: list[Any]) -> bool:
"""
Validates a list of modes using cached validation logic.
Returns True if all modes are valid and consistent.
"""
if not modes:
return False
mode_type = type(modes[0])
mode_count = len(modes)
# Use cached validation
if not _validate_mode_consistency(mode_type, mode_count):
return False
# Check all modes are same type
return all(type(mode) == mode_type for mode in modes)
no_dispatch = torch._C._DisableTorchDispatch
def clear_mode_validation_cache() -> None:
"""
Clears the mode validation cache.
Should be called when mode definitions change.
"""
_validate_mode_consistency.cache_clear()
def all_same_mode(modes):
return all(tuple(mode == modes[0] for mode in modes))
@lru_cache(maxsize=128)
def _validate_mode_consistency(mode_type: type, mode_count: int) -> bool:
"""
Validates that mode type and count are consistent.
Cached for performance on repeated validations.
"""
# Ensure mode_type is not None and mode_count is positive
if mode_type is None or mode_count <= 0:
return False
return True
def validate_modes_with_cache(modes: list[Any]) -> bool:
"""
Validates a list of modes using cached validation logic.
Returns True if all modes are valid and consistent.
"""
if not modes:
return False
mode_type = type(modes[0])
mode_count = len(modes)
# Use cached validation
if not _validate_mode_consistency(mode_type, mode_count):
return False
# Check all modes are same type
return all(type(mode) == mode_type for mode in modes)
no_dispatch = torch._C._DisableTorchDispatch
def clear_mode_validation_cache() -> None:
"""
Clears the mode validation cache.
Should be called when mode definitions change.
"""
_validate_mode_consistency.cache_clear()

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.

1 participant