Describe your issue.
Test case for scipy interpolator deepcopy issue (scipy >= 1.18).
This test demonstrates that scipy >= 1.18 has issues with deepcopying
interpolator objects, particularly PchipInterpolator and Akima1DInterpolator.
The issue manifests as:
- Deepcopy fails
Reported in BLonD issue:
https://gitlab.cern.ch/blond/BLonD/-/work_items/339
To reproduce with scipy >= 1.18:
pip install 'scipy>=1.18'
python test_scipy_deepcopy_interpolator_bug.py
Expected behavior: All tests should pass.
Actual behavior with scipy >= 1.18: PchipInterpolator and Akima1DInterpolator
tests return NaN or raise exceptions after deepcopy.
Reproducing Code Example
from copy import deepcopy
import numpy as np
from scipy.interpolate import (
Akima1DInterpolator,
PchipInterpolator,
interp1d,
)
def test_deepcopy_interp1d():
"""interp1d deepcopy works fine."""
x = np.array([0.0, 1.0, 2.0, 3.0])
y = np.array([0.0, 1.0, 4.0, 9.0])
interp = interp1d(x, y, kind='cubic')
interp_copy = deepcopy(interp)
test_x = 0.5
original_result = interp(test_x)
copy_result = interp_copy(test_x)
assert not np.isnan(original_result), f"Original returned NaN: {original_result}"
assert not np.isnan(copy_result), f"Deepcopy returned NaN: {copy_result}"
assert np.isclose(original_result, copy_result), (
f"Results differ: original={original_result}, copy={copy_result}"
)
print("[PASS] interp1d deepcopy works correctly")
def test_deepcopy_pchip():
"""
PchipInterpolator deepcopy broken with scipy >= 1.18.
The deepcopied interpolator may:
- Return NaN on evaluation
- Raise exceptions
- Produce incorrect results
"""
x = np.array([0.0, 1.0, 2.0, 3.0])
y = np.array([0.0, 1.0, 4.0, 9.0])
interp = PchipInterpolator(x, y)
interp_copy = deepcopy(interp)
test_x = 0.5
original_result = interp(test_x)
copy_result = interp_copy(test_x)
print(f" Original result: {original_result}")
print(f" Deepcopy result: {copy_result}")
assert not np.isnan(original_result), (
f"Original returned NaN: {original_result} (scipy bug?)"
)
assert not np.isnan(copy_result), (
f"Deepcopy returned NaN: {copy_result} (scipy >= 1.18 bug)"
)
assert np.isclose(original_result, copy_result), (
f"Results differ: original={original_result}, copy={copy_result} "
f"(scipy >= 1.18 deepcopy bug)"
)
print("[PASS] PchipInterpolator deepcopy works correctly")
def test_deepcopy_akima():
"""
Akima1DInterpolator deepcopy broken with scipy >= 1.18.
Similar issues as PchipInterpolator.
"""
x = np.array([0.0, 1.0, 2.0, 3.0, 4.0])
y = np.array([0.0, 1.0, 4.0, 9.0, 16.0])
interp = Akima1DInterpolator(x, y)
interp_copy = deepcopy(interp)
test_x = 0.5
original_result = interp(test_x)
copy_result = interp_copy(test_x)
print(f" Original result: {original_result}")
print(f" Deepcopy result: {copy_result}")
assert not np.isnan(original_result), (
f"Original returned NaN: {original_result} (scipy bug?)"
)
assert not np.isnan(copy_result), (
f"Deepcopy returned NaN: {copy_result} (scipy >= 1.18 bug)"
)
assert np.isclose(original_result, copy_result), (
f"Results differ: original={original_result}, copy={copy_result} "
f"(scipy >= 1.18 deepcopy bug)"
)
print("[PASS] Akima1DInterpolator deepcopy works correctly")
def test_deepcopy_in_class():
"""
Deepcopy interpolator as part of a class instance.
This is the real-world scenario from BLonD's MagneticCycleByTime.
"""
class Container:
def __init__(self):
x = np.array([0.0, 1.0, 2.0, 3.0])
y = np.array([0.0, 1.0, 4.0, 9.0])
self.interp = PchipInterpolator(x, y)
original = Container()
copy = deepcopy(original)
test_x = 0.5
original_result = original.interp(test_x)
copy_result = copy.interp(test_x)
print(f" Original result: {original_result}")
print(f" Deepcopy result: {copy_result}")
assert not np.isnan(original_result), (
f"Original returned NaN: {original_result} (scipy bug?)"
)
assert not np.isnan(copy_result), (
f"Deepcopy returned NaN: {copy_result} (scipy >= 1.18 bug)"
)
assert np.isclose(original_result, copy_result), (
f"Results differ: original={original_result}, copy={copy_result} "
f"(scipy >= 1.18 deepcopy bug)"
)
print("[PASS] PchipInterpolator deepcopy in class works correctly")
if __name__ == "__main__":
import scipy
print(f"scipy version: {scipy.__version__}\n")
print("Testing deepcopy of scipy interpolators...")
print()
try:
test_deepcopy_interp1d()
except AssertionError as e:
print(f"[FAIL] interp1d deepcopy FAILED: {e}\n")
try:
test_deepcopy_pchip()
except AssertionError as e:
print(f"[FAIL] PchipInterpolator deepcopy FAILED: {e}\n")
try:
test_deepcopy_akima()
except AssertionError as e:
print(f"[FAIL] Akima1DInterpolator deepcopy FAILED: {e}\n")
try:
test_deepcopy_in_class()
except AssertionError as e:
print(f"[FAIL] PchipInterpolator deepcopy in class FAILED: {e}\n")
print("All tests passed!")
Error message
C:\workwork\python\xstuite_llm\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2025.1.1.1/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path C:\workwork\python\blond\test_scipy_deepcopy_interpolator_bug.py
Testing started at 9:59 AM ...
Launching pytest with arguments C:\workwork\python\blond\test_scipy_deepcopy_interpolator_bug.py --no-header --no-summary -q in C:\workwork\python\blond
============================= test session starts =============================
collecting ... collected 4 items
test_scipy_deepcopy_interpolator_bug.py::test_deepcopy_pchip Set special to `cpp`
FAILED [ 25%]
test_scipy_deepcopy_interpolator_bug.py:57 (test_deepcopy_pchip)
def test_deepcopy_pchip():
"""
PchipInterpolator deepcopy broken with scipy >= 1.18.
The deepcopied interpolator may:
- Return NaN on evaluation
- Raise exceptions
- Produce incorrect results
"""
x = np.array([0.0, 1.0, 2.0, 3.0])
y = np.array([0.0, 1.0, 4.0, 9.0])
interp = PchipInterpolator(x, y)
> interp_copy = deepcopy(interp)
^^^^^^^^^^^^^^^^
test_scipy_deepcopy_interpolator_bug.py:70:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:162: in deepcopy
y = _reconstruct(x, memo, *rv)
^^^^^^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:259: in _reconstruct
state = deepcopy(state, memo)
^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:136: in deepcopy
y = copier(x, memo)
^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:221: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
x = <module 'scipy._external.array_api_compat.numpy' from 'C:\\workwork\\python\\xstuite_llm\\venv\\Lib\\site-packages\\scipy\\_external\\array_api_compat\\numpy\\__init__.py'>
memo = {1836576690432: <scipy.interpolate._cubic.PchipInterpolator object at 0x000001AB99DB1160>, 1836504991872: {'_delegate_.... , 1.5 , 3.75],
[ 0. , 1. , 4. ]]), 'x': array([0., 1., 2., 3.]), 'extrapolate': True, 'axis': 0}, ...}
_nil = []
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
if memo is None:
memo = {}
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier is not None:
y = copier(x, memo)
else:
if issubclass(cls, type):
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier is not None:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
> rv = reductor(4)
^^^^^^^^^^^
E TypeError: cannot pickle 'module' object
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:151: TypeError
test_scipy_deepcopy_interpolator_bug.py::test_deepcopy_akima FAILED [ 50%]
test_scipy_deepcopy_interpolator_bug.py:92 (test_deepcopy_akima)
def test_deepcopy_akima():
"""
Akima1DInterpolator deepcopy broken with scipy >= 1.18.
Similar issues as PchipInterpolator.
"""
x = np.array([0.0, 1.0, 2.0, 3.0, 4.0])
y = np.array([0.0, 1.0, 4.0, 9.0, 16.0])
interp = Akima1DInterpolator(x, y)
> interp_copy = deepcopy(interp)
^^^^^^^^^^^^^^^^
test_scipy_deepcopy_interpolator_bug.py:102:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:162: in deepcopy
y = _reconstruct(x, memo, *rv)
^^^^^^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:259: in _reconstruct
state = deepcopy(state, memo)
^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:136: in deepcopy
y = copier(x, memo)
^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:221: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
x = <module 'scipy._external.array_api_compat.numpy' from 'C:\\workwork\\python\\xstuite_llm\\venv\\Lib\\site-packages\\scipy\\_external\\array_api_compat\\numpy\\__init__.py'>
memo = {1836532307040: <scipy.interpolate._cubic.Akima1DInterpolator object at 0x000001AB99DB1220>, 1836532470464: {'_delegat... [0., 2., 4., 6.],
[0., 1., 4., 9.]]), 'x': array([0., 1., 2., 3., 4.]), 'extrapolate': False, 'axis': 0}, ...}
_nil = []
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
if memo is None:
memo = {}
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier is not None:
y = copier(x, memo)
else:
if issubclass(cls, type):
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier is not None:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
> rv = reductor(4)
^^^^^^^^^^^
E TypeError: cannot pickle 'module' object
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:151: TypeError
test_scipy_deepcopy_interpolator_bug.py::test_deepcopy_interp1d PASSED [ 75%][PASS] interp1d deepcopy works correctly
test_scipy_deepcopy_interpolator_bug.py::test_deepcopy_in_class FAILED [100%]
test_scipy_deepcopy_interpolator_bug.py:124 (test_deepcopy_in_class)
def test_deepcopy_in_class():
"""
Deepcopy interpolator as part of a class instance.
This is the real-world scenario from BLonD's MagneticCycleByTime.
"""
class Container:
def __init__(self):
x = np.array([0.0, 1.0, 2.0, 3.0])
y = np.array([0.0, 1.0, 4.0, 9.0])
self.interp = PchipInterpolator(x, y)
original = Container()
> copy = deepcopy(original)
^^^^^^^^^^^^^^^^^^
test_scipy_deepcopy_interpolator_bug.py:137:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:162: in deepcopy
y = _reconstruct(x, memo, *rv)
^^^^^^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:259: in _reconstruct
state = deepcopy(state, memo)
^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:136: in deepcopy
y = copier(x, memo)
^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:221: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:162: in deepcopy
y = _reconstruct(x, memo, *rv)
^^^^^^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:259: in _reconstruct
state = deepcopy(state, memo)
^^^^^^^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:136: in deepcopy
y = copier(x, memo)
^^^^^^^^^^^^^^^
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:221: in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
x = <module 'scipy._external.array_api_compat.numpy' from 'C:\\workwork\\python\\xstuite_llm\\venv\\Lib\\site-packages\\scipy\\_external\\array_api_compat\\numpy\\__init__.py'>
memo = {1836549901872: <test_scipy_deepcopy_interpolator_bug.test_deepcopy_in_class.<locals>.Container object at 0x000001AB9A...B9AE78260>, 1836532472384: {'_delegate_to': <scipy.interpolate._interpolate._PPoly object at 0x000001AB9AEF3DE0>}, ...}
_nil = []
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
if memo is None:
memo = {}
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier is not None:
y = copier(x, memo)
else:
if issubclass(cls, type):
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier is not None:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
> rv = reductor(4)
^^^^^^^^^^^
E TypeError: cannot pickle 'module' object
C:\Users\slauber\AppData\Local\Programs\Python\Python312\Lib\copy.py:151: TypeError
=================== 3 failed, 1 passed, 1 warning in 0.97s ====================
Process finished with exit code 1
SciPy/NumPy/Python version and system information
1.18.0 2.4.6 sys.version_info(major=3, minor=12, micro=10, releaselevel='final', serial=0)
Build Dependencies:
blas:
cython blas ilp64: false
detection method: pkgconfig
found: true
has ilp64: false
include directory: C:/Users/runneradmin/AppData/Local/Temp/cibw-run-sm43q9rt/cp312-win_amd64/build/venv/Lib/site-packages/scipy_openblas32/include
lib directory: C:/Users/runneradmin/AppData/Local/Temp/cibw-run-sm43q9rt/cp312-win_amd64/build/venv/Lib/site-packages/scipy_openblas32/lib
name: scipy-openblas
openblas configuration: OpenBLAS 0.3.31.dev DYNAMIC_ARCH NO_AFFINITY SkylakeX
MAX_THREADS=24
pc file directory: D:/a/scipy-release/scipy-release/.openblas
version: 0.3.31.dev
lapack:
detection method: pkgconfig
found: true
has ilp64: false
include directory: C:/Users/runneradmin/AppData/Local/Temp/cibw-run-sm43q9rt/cp312-win_amd64/build/venv/Lib/site-packages/scipy_openblas32/include
lib directory: C:/Users/runneradmin/AppData/Local/Temp/cibw-run-sm43q9rt/cp312-win_amd64/build/venv/Lib/site-packages/scipy_openblas32/lib
name: scipy-openblas
openblas configuration: OpenBLAS 0.3.31.dev DYNAMIC_ARCH NO_AFFINITY SkylakeX
MAX_THREADS=24
pc file directory: D:/a/scipy-release/scipy-release/.openblas
version: 0.3.31.dev
pybind11:
detection method: config-tool
include directory: unknown
name: pybind11
version: 3.0.4
Compilers:
c:
commands: C:\Strawberry\c\bin\ccache.EXE, cc
linker: ld.bfd
name: gcc
version: 15.2.0
c++:
commands: C:\Strawberry\c\bin\ccache.EXE, c++
linker: ld.bfd
name: gcc
version: 15.2.0
cython:
commands: cython
linker: cython
name: cython
version: 3.2.5
fortran:
commands: gfortran
linker: ld.bfd
name: gcc
version: 15.2.0
pythran:
include directory: C:/Users/runneradmin/AppData/Local/Temp/build-env-i3hfaws3/Lib/site-packages/pythran
version: 0.18.1
Machine Information:
build:
cpu: x86_64
endian: little
family: x86_64
system: windows
cross-compiled: false
host:
cpu: x86_64
endian: little
family: x86_64
system: windows
Python Information:
path: C:/Users/runneradmin/AppData/Local/Temp/build-env-i3hfaws3/Scripts/python.exe
version: '3.12'
Describe your issue.
Test case for scipy interpolator deepcopy issue (scipy >= 1.18).
This test demonstrates that scipy >= 1.18 has issues with deepcopying
interpolator objects, particularly PchipInterpolator and Akima1DInterpolator.
The issue manifests as:
Reported in BLonD issue:
https://gitlab.cern.ch/blond/BLonD/-/work_items/339
To reproduce with scipy >= 1.18:
pip install 'scipy>=1.18'
python test_scipy_deepcopy_interpolator_bug.py
Expected behavior: All tests should pass.
Actual behavior with scipy >= 1.18: PchipInterpolator and Akima1DInterpolator
tests return NaN or raise exceptions after deepcopy.
Reproducing Code Example
Error message
SciPy/NumPy/Python version and system information