Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
warnings as errors. Docstring style: `docs/source/contributing.md`.

### Fixed
- Splines and other objects of the fake `cupyx.scipy` can be copied with
`copy.copy` and `copy.deepcopy` (also inside a copied container), as real
CuPy splines can.
- `xp.algorithms.segment_sum` and `SegmentPlan.sum` work on the fake CuPy: it
cannot compile the CUDA kernel, so the sum runs on the host buffers of the
fake arrays and the CuPy path of a program runs without a GPU.
Expand Down
9 changes: 9 additions & 0 deletions src/cunumpy/_fake_cupyx.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from __future__ import annotations

import copy
import importlib
import sys
import types
Expand Down Expand Up @@ -263,6 +264,14 @@ def __call__(self, *args: Any, **kwargs: Any) -> Any:
def __repr__(self) -> str:
return f"<fake cupyx {self._obj!r}>"

# copies wrap a copy of the SciPy object and share the fake CuPy module (a
# module cannot be copied), as real CuPy splines can be copied
def __copy__(self) -> _Proxy:
return _Proxy(copy.copy(self._obj), self._cupy)

def __deepcopy__(self, memo: dict) -> _Proxy:
return _Proxy(copy.deepcopy(self._obj, memo), self._cupy)


class _ProxyClass:
"""A SciPy class seen as a CuPy class: instances are :class:`_Proxy`."""
Expand Down
20 changes: 20 additions & 0 deletions tests/unit/test_fake_cupyx.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ def test_splines_fit_and_evaluate_on_the_device():
assert "ok" in run(code)


def test_splines_can_be_copied():
code = """
import copy
import numpy as np
import cunumpy as xp
xp.set_backend("cupy")
x = xp.linspace(0.0, 1.0, 11)
spl = xp.scipy.interpolate.UnivariateSpline(x, xp.sin(3 * x), k=3, s=0.0, ext=3)
for c in (copy.copy(spl), copy.deepcopy(spl), copy.deepcopy({"spline": spl})["spline"]):
assert isinstance(c, xp.scipy.interpolate.UnivariateSpline)
assert xp.is_gpu(c(x))
assert np.array_equal(xp.to_numpy(c(x)), xp.to_numpy(spl(x)))
# a deep copy is independent of the original
c = copy.deepcopy(spl)
assert c._obj is not spl._obj
print("ok")
"""
assert "ok" in run(code)


def test_host_arrays_are_rejected_like_cupy():
code = """
import numpy as np
Expand Down
Loading