From 64eea79c3b2c59e60455db1091185f067242eebf Mon Sep 17 00:00:00 2001 From: manfredss Date: Wed, 22 Jul 2026 11:59:28 +0000 Subject: [PATCH] Full implementation of PaddlePaddle__Paddle-78823 --- .../PaddlePaddle__Paddle-78823/README.md | 44 ++ .../environment/README.md | 52 +++ .../PaddlePaddle__Paddle-78823/instruction.md | 45 ++ .../solution/code.patch | 424 ++++++++++++++++++ .../tests/test.patch | 295 ++++++++++++ .../PaddlePaddle__Paddle-78823/tests/test.sh | 10 + 6 files changed, 870 insertions(+) create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-78823/README.md create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-78823/environment/README.md create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-78823/instruction.md create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-78823/solution/code.patch create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-78823/tests/test.patch create mode 100755 swe-paddle/tasks/PaddlePaddle__Paddle-78823/tests/test.sh diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78823/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/README.md new file mode 100644 index 000000000..a4f81504a --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/README.md @@ -0,0 +1,44 @@ +# PaddlePaddle__Paddle-78823 + +This directory converts merged Paddle PR #78823 into a SWE-Paddle community task package. + +## Source + +| Field | Value | +| --- | --- | +| Repository | `PaddlePaddle/Paddle` | +| PR | [78823](https://github.com/PaddlePaddle/Paddle/pull/78823) | +| PR title | `[API Compatibility] add pin_memory for randint` | +| Base commit | `c3a5e799eb9e390f830c9e6c3fbea2e9370afa7f` | +| Gold commit | `ddb483237539d4d23c7dbbd44e3a360439c780ed` | +| Merged at | `2026-05-12` | +| Task type | `bug_fix` | +| Track | Python API compatibility | +| Resource | CUDA or XPU for complete verification; CPU-only for limited error-path coverage | + +## Summary + +Make `pin_memory=True` use consistent place-conversion semantics across tensor creation, random generation (including `randint`), and audio window creation. CPU placement must use the available CUDA/XPU pinned allocator, explicit accelerator and already-pinned places must resolve consistently, and builds without a pinned allocator must report an explicit unsupported error. + +## Scope + +The gold change is Python-only and covers place handling used by tensor creation and random APIs. The upstream regression tests exercise CUDA, XPU, already-pinned, CPU fallback, and unsupported CPU-only behavior; hardware-dependent cases remain conditional. + +## Artifacts + +- `proposal.md`: original candidate proposal and source rationale. +- `instruction.md`: self-contained task requirements for the coding agent. +- `environment/README.md`: checkout, build, device, and reproduction notes. +- `solution/code.patch`: exact production-file diff from base to gold commit. +- `tests/test.patch`: exact test-file diff from base to gold commit. +- `tests/test.sh`: strict wrapper for the five target test files. + +## Verification + +From a built Paddle checkout with the patches applied in the documented order: + +```bash +bash tests/test.sh +``` + +A CPU-only run is not complete verification because accelerator allocator branches are unavailable or skipped. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78823/environment/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/environment/README.md new file mode 100644 index 000000000..e492e6f54 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/environment/README.md @@ -0,0 +1,52 @@ +# Environment Notes + +## Expected environment + +- Repository: `PaddlePaddle/Paddle` +- Base commit: `c3a5e799eb9e390f830c9e6c3fbea2e9370afa7f` +- Gold commit: `ddb483237539d4d23c7dbbd44e3a360439c780ed` +- Patch type: Python production and test files +- Python dependency: `pytest` +- Complete resource requirement: one CUDA or XPU device with a matching Paddle source build + +## Checkout and build + +Start from a clean checkout of the exact base commit. The tests import Paddle's compiled runtime and instantiate device places, so use a Paddle installation built from this checkout (or an equivalent build whose Python source is overlaid by this checkout). A source build is recommended to keep the Python API and compiled extension aligned with the historical revision. + +Follow Paddle's build prerequisites for the selected backend. CUDA validation requires a CUDA-enabled build and compatible CUDA/cuDNN runtime; XPU validation requires an XPU-enabled build and runtime. No dataset, network service, multi-device topology, or distributed setup is required. + +## Device limitations + +- CUDA builds can validate CUDA-place conversion, CUDA pinned allocation, CPU-to-CUDA-pinned behavior, and CUDA-conditional API tests. +- XPU builds can validate XPU-place conversion, XPU pinned allocation, and CPU-to-XPU-pinned behavior. +- CPU-only builds have no pinned allocator. They can validate the explicit `Pinning memory is not supported` path and some mocked branch coverage, but accelerator-dependent tests are skipped or cannot allocate pinned memory. A green CPU-only run is therefore not complete task verification. + +## Patch application order + +From the Paddle repository root, assuming this task directory is available as `$TASK_DIR`: + +```bash +git checkout c3a5e799eb9e390f830c9e6c3fbea2e9370afa7f +git apply "$TASK_DIR/tests/test.patch" +bash "$TASK_DIR/tests/test.sh" # expected to expose failures before the fix +git apply "$TASK_DIR/solution/code.patch" +bash "$TASK_DIR/tests/test.sh" # expected to pass on a compatible build/device +``` + +Apply the test patch before the solution patch. Both patches are rooted at the Paddle repository root. + +## Exact test command + +`tests/test.sh` runs: + +```bash +python -m pytest \ + test/legacy_test/test_to_pinned_place.py \ + test/legacy_test/test_eager_tensor.py \ + test/legacy_test/test_rand.py \ + test/legacy_test/test_randint_op.py \ + test/legacy_test/test_randperm_op.py \ + -q +``` + +Run it from the root of the built Paddle checkout. Before applying the solution, new tests requiring the missing behavior should fail on a compatible accelerator build. After applying the solution, all applicable tests should pass; backend-conditional skips are expected when that backend is unavailable. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78823/instruction.md b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/instruction.md new file mode 100644 index 000000000..8b2e27ce5 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/instruction.md @@ -0,0 +1,45 @@ +# Unify pinned-memory place behavior for creation APIs + +## Problem + +Paddle's dynamic tensor-creation and random-generation APIs do not handle `pin_memory=True` consistently. In particular, calls such as `randint(..., device="cpu", pin_memory=True)` can reject CPU placement even when the build provides a CUDA or XPU pinned-host allocator, while related APIs contain differing conversion behavior. + +Implement consistent pinned-memory place semantics across the affected creation paths without changing normal behavior when pinned memory is not requested. + +## Required behavior + +When `pin_memory=True` is active on a supported dynamic execution path: + +- An already CUDA-pinned or XPU-pinned place remains unchanged. +- A CUDA place resolves to CUDA pinned host memory. +- An XPU place resolves to XPU pinned host memory. +- A CPU place resolves to an available pinned host allocator: + - use XPU pinned memory in an XPU-enabled build; + - otherwise use CUDA pinned memory in a CUDA-enabled build; + - if neither allocator is compiled in, raise `RuntimeError` with a clear message containing `Pinning memory is not supported`. +- Unsupported place kinds raise the same explicit `RuntimeError` rather than silently falling back. + +The CPU behavior applies whether the device is supplied as a string or as a Paddle place object. Preserve existing device defaults, shapes, dtypes, gradient flags, output semantics, and static/PIR behavior outside the dynamic pinned-memory path. + +## Affected API families + +Apply these semantics consistently to: + +- tensor conversion/creation, including `tensor`; +- shape- and value-based creators such as `full`, `full_like`, `eye`, `arange`, `empty`, and `empty_like`; +- random creators, including `rand`, `randn`, `randint`, and `randperm`; +- audio window creation paths that expose the same `device` and `pin_memory` behavior. + +`randint` must support CPU placement with `pin_memory=True` on CUDA- or XPU-enabled builds and return a tensor with the requested shape on the corresponding pinned place. + +## Verification + +The implementation must pass the target tests in: + +- `test/legacy_test/test_to_pinned_place.py` +- `test/legacy_test/test_eager_tensor.py` +- `test/legacy_test/test_rand.py` +- `test/legacy_test/test_randint_op.py` +- `test/legacy_test/test_randperm_op.py` + +Verify both successful allocator-backed branches and explicit unsupported behavior. Accelerator tests may remain conditional on compile-time support, but a CPU-only run must not be represented as complete CUDA/XPU validation. Do not delete tests, weaken assertions, or bypass unsupported-device checks. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78823/solution/code.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/solution/code.patch new file mode 100644 index 000000000..ccbd79552 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/solution/code.patch @@ -0,0 +1,424 @@ +diff --git a/python/paddle/audio/functional/window.py b/python/paddle/audio/functional/window.py +index c62216af84..e243fb8da3 100644 +--- a/python/paddle/audio/functional/window.py ++++ b/python/paddle/audio/functional/window.py +@@ -29,7 +29,7 @@ if TYPE_CHECKING: + from paddle.base.framework import ( + _current_expected_place, + _get_paddle_place, +- core, ++ _to_pinned_place, + in_dynamic_or_pir_mode, + ) + +@@ -473,26 +473,8 @@ def _apply_window_postprocess( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and paddle.in_dynamic_mode() +- and device is not None +- and not isinstance( +- device, (core.CUDAPinnedPlace, core.XPUPinnedPlace) +- ) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError( +- f"Pinning memory is not supported for {device}" +- ) ++ if pin_memory and paddle.in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + w = w.to(device=device) + if pin_memory and paddle.in_dynamic_mode(): + w = w.pin_memory() +diff --git a/python/paddle/base/framework.py b/python/paddle/base/framework.py +index 28168e98c6..2ce8aeeb98 100644 +--- a/python/paddle/base/framework.py ++++ b/python/paddle/base/framework.py +@@ -8449,6 +8449,49 @@ def _get_paddle_place(place): + ) + + ++def _to_pinned_place(place): ++ """Convert a place to its pinned counterpart for ``pin_memory=True``. ++ ++ PyTorch's contract is that ``pin_memory=True`` is requested against a ++ CPU placement and produces page-locked host memory. Paddle's pinned ++ allocators are tied to the GPU/XPU runtime, so we map: ++ ++ - already-pinned (``CUDAPinnedPlace`` / ``XPUPinnedPlace``): unchanged; ++ - ``CUDAPlace`` (or any ``Place`` reporting ``is_gpu_place``): ++ ``CUDAPinnedPlace``; ++ - ``XPUPlace`` (or any ``Place`` reporting ``is_xpu_place``): ++ ``XPUPinnedPlace``; ++ - ``CPUPlace`` (or any ``Place`` reporting ``is_cpu_place``): the XPU ++ pinned allocator when Paddle is compiled with XPU, the CUDA pinned ++ allocator when Paddle is compiled with CUDA, otherwise this branch ++ raises (no pinned allocator is available on a pure CPU build). This ++ relaxation lets PyTorch-style code such as ++ ``randint(..., device='cpu', pin_memory=True)`` work unchanged on ++ GPU/XPU builds. ++ ++ Any other place raises :class:`RuntimeError`. ++ """ ++ if isinstance(place, (core.CUDAPinnedPlace, core.XPUPinnedPlace)): ++ return place ++ if isinstance(place, core.CUDAPlace) or ( ++ isinstance(place, core.Place) and place.is_gpu_place() ++ ): ++ return core.CUDAPinnedPlace() ++ if isinstance(place, core.XPUPlace) or ( ++ isinstance(place, core.Place) and place.is_xpu_place() ++ ): ++ return core.XPUPinnedPlace() ++ if isinstance(place, core.CPUPlace) or ( ++ isinstance(place, core.Place) and place.is_cpu_place() ++ ): ++ if core.is_compiled_with_xpu(): ++ return core.XPUPinnedPlace() ++ if core.is_compiled_with_cuda(): ++ return core.CUDAPinnedPlace() ++ raise RuntimeError(f"Pinning memory is not supported for {place}") ++ raise RuntimeError(f"Pinning memory is not supported for {place}") ++ ++ + def _get_paddle_place_list(places): + if not isinstance(places, (list, tuple)): + raise TypeError("places must to be List or Tuple") +diff --git a/python/paddle/framework/__init__.py b/python/paddle/framework/__init__.py +index ef6c920698..ab75940272 100755 +--- a/python/paddle/framework/__init__.py ++++ b/python/paddle/framework/__init__.py +@@ -46,6 +46,7 @@ from ..base.framework import ( # noqa: F401 + _global_flags, + _set_expected_place, + _stride_in_no_check_dy2st_diff as _no_check_dy2st_diff, ++ _to_pinned_place, + convert_np_dtype_to_dtype_, + deprecate_stat_dict, + disable_signal_handler, +diff --git a/python/paddle/tensor/creation.py b/python/paddle/tensor/creation.py +index a60e88debc..13dc6fde10 100644 +--- a/python/paddle/tensor/creation.py ++++ b/python/paddle/tensor/creation.py +@@ -48,6 +48,7 @@ from ..framework import ( + _current_expected_place, + _current_expected_place_, + _get_paddle_place, ++ _to_pinned_place, + convert_np_dtype_to_dtype_, + core, + dygraph_only, +@@ -1058,15 +1059,8 @@ def tensor( + place = _get_paddle_place(device) + if place is None: + place = _current_expected_place_() +- if pin_memory and not isinstance( +- place, (core.CUDAPinnedPlace, core.XPUPinnedPlace) +- ): +- if isinstance(place, core.CUDAPlace): +- place = core.CUDAPinnedPlace() +- elif isinstance(place, core.XPUPlace): +- place = core.XPUPinnedPlace() +- else: +- raise RuntimeError(f"Pinning memory is not supported for {place}.") ++ if pin_memory: ++ place = _to_pinned_place(place) + + if in_dynamic_mode(): + is_tensor = paddle.is_tensor(data) +@@ -1425,26 +1419,8 @@ def full_like( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance( +- device, (core.CUDAPinnedPlace, core.XPUPinnedPlace) +- ) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError( +- f"Pinning memory is not supported for {device}" +- ) ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + + tensor = _C_ops.full_like(x, fill_value, dtype, device) + if requires_grad is True: +@@ -2029,26 +2005,8 @@ def eye( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance( +- device, (core.CUDAPinnedPlace, core.XPUPinnedPlace) +- ) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError( +- f"Pinning memory is not supported for {device}" +- ) ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + tensor = _C_ops.eye( + num_rows, + num_columns, +@@ -2197,26 +2155,8 @@ def full( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance( +- device, (core.CUDAPinnedPlace, core.XPUPinnedPlace) +- ) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError( +- f"Pinning memory is not supported for {device}" +- ) ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + + tensor = fill_constant( + shape=shape, +@@ -2347,26 +2287,8 @@ def arange( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance( +- device, (core.CUDAPinnedPlace, core.XPUPinnedPlace) +- ) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError( +- f"Pinning memory is not supported for {device}" +- ) ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + + if is_value_input and in_pir_mode(): + tensor = _C_ops.arange( +@@ -3246,26 +3168,8 @@ def empty( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance( +- device, (core.CUDAPinnedPlace, core.XPUPinnedPlace) +- ) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError( +- f"Pinning memory is not supported for {device}" +- ) ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + tensor = _C_ops.empty( + shape, + convert_np_dtype_to_dtype_(dtype), +@@ -3384,26 +3288,8 @@ def empty_like( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance( +- device, (core.CUDAPinnedPlace, core.XPUPinnedPlace) +- ) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError( +- f"Pinning memory is not supported for {device}" +- ) ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + + if in_dynamic_mode(): + x_shape = x.shape +diff --git a/python/paddle/tensor/random.py b/python/paddle/tensor/random.py +index f5853fed47..66f5f476ef 100644 +--- a/python/paddle/tensor/random.py ++++ b/python/paddle/tensor/random.py +@@ -21,7 +21,7 @@ from typing import TYPE_CHECKING, overload + import paddle + from paddle import _C_ops + from paddle._C_ops import poisson # noqa: F401 +-from paddle.base.framework import _current_expected_place ++from paddle.base.framework import _current_expected_place, _to_pinned_place + from paddle.base.libpaddle import DataType + from paddle.common_ops_import import Variable + from paddle.framework import ( +@@ -1059,22 +1059,8 @@ def randn( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance(device, (core.CUDAPinnedPlace, core.XPUPinnedPlace)) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError(f"Pinning memory is not supported for {device}") ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + tensor = standard_normal( + shape, + dtype, +@@ -1897,21 +1883,8 @@ def randint( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and not isinstance(place, (core.CUDAPinnedPlace, core.XPUPinnedPlace)) +- ): +- if isinstance(place, core.CUDAPlace) or ( +- isinstance(place, core.Place) and place.is_gpu_place() +- ): +- place = core.CUDAPinnedPlace() +- elif isinstance(place, core.XPUPlace) or ( +- isinstance(place, core.Place) and place.is_xpu_place() +- ): +- place = core.XPUPinnedPlace() +- else: +- raise RuntimeError(f"Pinning memory is not supported for {place}") ++ if pin_memory and in_dynamic_mode(): ++ place = _to_pinned_place(place) + + if in_dynamic_mode(): + shape = paddle.utils.convert_shape_to_list(shape) +@@ -2290,22 +2263,8 @@ def randperm( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance(device, (core.CUDAPinnedPlace, core.XPUPinnedPlace)) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError(f"Pinning memory is not supported for {device}") ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + + if not isinstance(dtype, (core.VarDesc.VarType, paddle.pir.core.DataType)): + dtype = convert_np_dtype_to_dtype_(dtype) +@@ -2449,22 +2408,8 @@ def rand( + if device is not None + else _current_expected_place() + ) +- if ( +- pin_memory +- and in_dynamic_mode() +- and device is not None +- and not isinstance(device, (core.CUDAPinnedPlace, core.XPUPinnedPlace)) +- ): +- if isinstance(device, core.CUDAPlace) or ( +- isinstance(device, core.Place) and device.is_gpu_place() +- ): +- device = core.CUDAPinnedPlace() +- elif isinstance(device, core.XPUPlace) or ( +- isinstance(device, core.Place) and device.is_xpu_place() +- ): +- device = core.XPUPinnedPlace() +- else: +- raise RuntimeError(f"Pinning memory is not supported for {device}") ++ if pin_memory and in_dynamic_mode() and device is not None: ++ device = _to_pinned_place(device) + tensor = uniform( + shape=shape, + dtype=dtype, diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78823/tests/test.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/tests/test.patch new file mode 100644 index 000000000..ee42def19 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/tests/test.patch @@ -0,0 +1,295 @@ +diff --git a/test/legacy_test/test_eager_tensor.py b/test/legacy_test/test_eager_tensor.py +index f27c697a76..e21b19433b 100644 +--- a/test/legacy_test/test_eager_tensor.py ++++ b/test/legacy_test/test_eager_tensor.py +@@ -422,16 +422,33 @@ class TestEagerTensor(unittest.TestCase): + tensor_pin = paddle.tensor(self.array, device="xpu_pinned") + self.assertEqual(tensor_pin.place, core.XPUPinnedPlace()) + +- with self.assertRaises(RuntimeError) as context: +- paddle.tensor( +- self.array, +- device="cpu", +- pin_memory=True, # no support ++ # ``device="cpu", pin_memory=True`` is relaxed to map onto the ++ # available pinned allocator (matching torch's pin_memory contract). ++ # On a pure-CPU build there is no pinned allocator at all, so the ++ # call must raise with the legacy "Pinning memory is not supported" ++ # message; on GPU/XPU builds it succeeds and produces a pinned ++ # tensor. ++ if core.is_compiled_with_xpu(): ++ tensor_res = paddle.tensor( ++ self.array, device="cpu", pin_memory=True ++ ) ++ self.assertEqual(tensor_res.place, core.XPUPinnedPlace()) ++ elif core.is_compiled_with_cuda(): ++ tensor_res = paddle.tensor( ++ self.array, device="cpu", pin_memory=True ++ ) ++ self.assertEqual(tensor_res.place, core.CUDAPinnedPlace()) ++ else: ++ with self.assertRaises(RuntimeError) as context: ++ paddle.tensor( ++ self.array, ++ device="cpu", ++ pin_memory=True, ++ ) ++ self.assertIn( ++ "Pinning memory is not supported", ++ str(context.exception), + ) +- self.assertIn( +- "Pinning memory is not supported", +- str(context.exception), +- ) + + def test_tensor_and_to_tensor(self): + """ +diff --git a/test/legacy_test/test_rand.py b/test/legacy_test/test_rand.py +index 4b1509e49c..b97f8e8077 100644 +--- a/test/legacy_test/test_rand.py ++++ b/test/legacy_test/test_rand.py +@@ -135,14 +135,15 @@ class TestTensorCreation(unittest.TestCase): + ) + self.assertEqual(x.data_ptr(), y.data_ptr()) + +- def test_pin_memory_error_cases(self): +- """Test pin_memory error cases""" ++ def test_pin_memory_cpu_device(self): ++ """``device='cpu', pin_memory=True`` is relaxed to the available ++ pinned allocator (matches torch's pin_memory contract).""" + if not paddle.device.is_compiled_with_cuda(): + return + +- with dygraph_guard(), self.assertRaises(RuntimeError): +- # Test unsupported device with pin_memory=True +- paddle.rand([2, 3], device=paddle.CPUPlace(), pin_memory=True) ++ with dygraph_guard(): ++ x = paddle.rand([2, 3], device=paddle.CPUPlace(), pin_memory=True) ++ self.assertIn("pinned", str(x.place).lower()) + + + class TestCreationOut(unittest.TestCase): +diff --git a/test/legacy_test/test_randint_op.py b/test/legacy_test/test_randint_op.py +index 2090e504b3..d67b4e20e2 100644 +--- a/test/legacy_test/test_randint_op.py ++++ b/test/legacy_test/test_randint_op.py +@@ -13,11 +13,13 @@ + # limitations under the License. + + import unittest ++from unittest import mock + + import numpy as np + from op_test import OpTest, get_device, get_device_place, is_custom_device + + import paddle ++import paddle.tensor.random as paddle_tensor_random + + paddle.enable_static() + +@@ -415,10 +417,16 @@ class TestRandintDeviceRequiresGradPinMemory(unittest.TestCase): + self.assertFalse(x.stop_gradient) + paddle.enable_static() + +- def test_pin_memory_unsupported_device_raises(self): ++ def test_pin_memory_cpu(self): ++ if not ( ++ paddle.device.is_compiled_with_cuda() ++ or paddle.device.is_compiled_with_xpu() ++ ): ++ return + paddle.disable_static() +- with self.assertRaises(RuntimeError): +- paddle.randint(high=10, shape=[2, 3], device='cpu', pin_memory=True) ++ x = paddle.randint(high=10, shape=[2, 3], device='cpu', pin_memory=True) ++ self.assertEqual(x.shape, [2, 3]) ++ self.assertTrue("pinned" in str(x.place)) + paddle.enable_static() + + def test_pin_memory_cuda(self): +@@ -429,6 +437,30 @@ class TestRandintDeviceRequiresGradPinMemory(unittest.TestCase): + self.assertTrue("pinned" in str(x.place)) + paddle.enable_static() + ++ def test_pin_memory_cpu_xpu_branch(self): ++ # Cover the cpu+pin_memory XPU branch (line where ++ # ``place = core.XPUPinnedPlace()`` runs when ++ # ``is_compiled_with_xpu()`` is True). XPUPinnedPlace can't be ++ # instantiated on a CUDA-only build, so route it to CUDAPinnedPlace. ++ if not paddle.device.is_compiled_with_cuda(): ++ return ++ paddle.disable_static() ++ with ( ++ mock.patch.object( ++ paddle.device, 'is_compiled_with_xpu', return_value=True ++ ), ++ mock.patch.object( ++ paddle_tensor_random.core, ++ 'XPUPinnedPlace', ++ paddle_tensor_random.core.CUDAPinnedPlace, ++ ), ++ ): ++ x = paddle.randint( ++ high=10, shape=[2, 3], device='cpu', pin_memory=True ++ ) ++ self.assertTrue("pinned" in str(x.place)) ++ paddle.enable_static() ++ + + if __name__ == "__main__": + unittest.main() +diff --git a/test/legacy_test/test_randperm_op.py b/test/legacy_test/test_randperm_op.py +index a4aacebad4..8c7c969760 100644 +--- a/test/legacy_test/test_randperm_op.py ++++ b/test/legacy_test/test_randperm_op.py +@@ -525,14 +525,17 @@ class TestRandpermNewParams(unittest.TestCase): + self.assertEqual(result.dtype, out_tensor.dtype) + self.assertTrue(check_randperm_out(self.n, result.numpy())) + +- def test_pin_memory_error_cases(self): +- """Test pin_memory error cases""" ++ def test_pin_memory_cpu_device(self): ++ """``device='cpu', pin_memory=True`` is relaxed to the available ++ pinned allocator (matches torch's pin_memory contract).""" + if not paddle.device.is_compiled_with_cuda(): + return + +- with dygraph_guard(), self.assertRaises(RuntimeError): +- # Test unsupported device with pin_memory=True +- paddle.randperm([2, 3], device=paddle.CPUPlace(), pin_memory=True) ++ with dygraph_guard(): ++ x = paddle.randperm( ++ self.n, device=paddle.CPUPlace(), pin_memory=True ++ ) ++ self.assertIn("pinned", str(x.place).lower()) + + + class TestRandperm_compatible(unittest.TestCase): +diff --git a/test/legacy_test/test_to_pinned_place.py b/test/legacy_test/test_to_pinned_place.py +new file mode 100644 +index 0000000000..41f6ecbfff +--- /dev/null ++++ b/test/legacy_test/test_to_pinned_place.py +@@ -0,0 +1,121 @@ ++# Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved. ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++""" ++Direct branch coverage for ``paddle.base.framework._to_pinned_place``. ++ ++A natural CI run hits only the branch matching the host's compile flags ++(typically just the CUDA path). These tests use ``unittest.mock.patch`` ++over ``core.is_compiled_with_*`` and a small fake place class to drive ++every branch on any build. ++""" ++ ++import contextlib ++import unittest ++from unittest import mock ++ ++from paddle.base import core ++from paddle.base.framework import _to_pinned_place ++ ++ ++class TestToPinnedPlace(unittest.TestCase): ++ def test_already_cuda_pinned_returns_same_object(self): ++ # Branch 1: already-pinned passes through unchanged. Constructing ++ # ``CUDAPinnedPlace()`` itself requires a CUDA-built wheel, so skip ++ # on a pure-CPU build (where the branch is unreachable in practice). ++ if not core.is_compiled_with_cuda(): ++ self.skipTest("CUDA not compiled in this build") ++ p = core.CUDAPinnedPlace() ++ self.assertIs(_to_pinned_place(p), p) ++ ++ def test_cuda_place_to_cuda_pinned(self): ++ # Branch 2: explicit GPU place -> CUDAPinnedPlace. ++ if not core.is_compiled_with_cuda(): ++ self.skipTest("CUDA not compiled in this build") ++ result = _to_pinned_place(core.CUDAPlace(0)) ++ self.assertIsInstance(result, core.CUDAPinnedPlace) ++ ++ def test_cpu_place_on_cuda_build(self): ++ # Branch 5: CPU placement, CUDA-compiled build -> CUDAPinnedPlace. ++ if not core.is_compiled_with_cuda(): ++ self.skipTest("CUDA not compiled in this build") ++ with mock.patch.object( ++ core, 'is_compiled_with_xpu', return_value=False ++ ): ++ result = _to_pinned_place(core.CPUPlace()) ++ self.assertIsInstance(result, core.CUDAPinnedPlace) ++ ++ def test_cpu_place_on_xpu_compiled_branch(self): ++ # Branch 4: drives the XPU sub-branch on the CPU path. On a ++ # non-XPU host, ``core.XPUPinnedPlace()`` itself raises, but the ++ # branch line still executes (which is what the coverage tool ++ # records); on an XPU host the constructor succeeds. ++ with ( ++ mock.patch.object(core, 'is_compiled_with_xpu', return_value=True), ++ contextlib.suppress(Exception), ++ ): ++ _to_pinned_place(core.CPUPlace()) ++ ++ def test_xpu_place_branch(self): ++ # Branch 3: explicit XPUPlace -> XPUPinnedPlace. Hard to drive on ++ # a non-XPU host because ``core.XPUPlace(0)`` itself fails to ++ # construct, so we substitute ``core.XPUPlace`` with a stand-in ++ # class long enough for the ``isinstance`` check to match. The ++ # subsequent ``XPUPinnedPlace()`` constructor may raise on a ++ # non-XPU host, which we tolerate -- coverage cares only that ++ # the branch executed. ++ class FakeXPUPlace: ++ def is_xpu_place(self): ++ return True ++ ++ def is_gpu_place(self): ++ return False ++ ++ def is_cpu_place(self): ++ return False ++ ++ with ( ++ mock.patch.object(core, 'XPUPlace', new=FakeXPUPlace), ++ contextlib.suppress(Exception), ++ ): ++ _to_pinned_place(FakeXPUPlace()) ++ ++ def test_cpu_place_on_pure_cpu_build_raises(self): ++ # Branch 6: CPU placement on a build with neither CUDA nor XPU ++ # compiled in -> RuntimeError with the legacy message. ++ with ( ++ mock.patch.object(core, 'is_compiled_with_xpu', return_value=False), ++ mock.patch.object( ++ core, 'is_compiled_with_cuda', return_value=False ++ ), ++ self.assertRaises(RuntimeError) as ctx, ++ ): ++ _to_pinned_place(core.CPUPlace()) ++ self.assertIn("Pinning memory is not supported", str(ctx.exception)) ++ ++ def test_unsupported_place_raises(self): ++ # Branch 7 (final raise): any object that isn't a CUDA/XPU/CPU ++ # place falls through to the catch-all RuntimeError. A plain ++ # Python object naturally fails every ``isinstance`` check inside ++ # the helper without needing to fake ``core.Place``. ++ class _OtherPlace: ++ pass ++ ++ with self.assertRaises(RuntimeError) as ctx: ++ _to_pinned_place(_OtherPlace()) ++ self.assertIn("Pinning memory is not supported", str(ctx.exception)) ++ ++ ++if __name__ == '__main__': ++ unittest.main() diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-78823/tests/test.sh b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/tests/test.sh new file mode 100755 index 000000000..d3aaf7de1 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-78823/tests/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +python -m pytest \ + test/legacy_test/test_to_pinned_place.py \ + test/legacy_test/test_eager_tensor.py \ + test/legacy_test/test_rand.py \ + test/legacy_test/test_randint_op.py \ + test/legacy_test/test_randperm_op.py \ + -q