Skip to content

Commit a1974a5

Browse files
committed
Numpy 2.0 compatibility
1 parent bfb90b9 commit a1974a5

File tree

3 files changed

+78
-36
lines changed

3 files changed

+78
-36
lines changed

src/sage/matrix/matrix1.pyx

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import sage.modules.free_module
2525
from sage.structure.coerce cimport coercion_model
2626

2727

28+
_MISSING = object()
29+
30+
2831
cdef class Matrix(Matrix0):
2932
###################################################
3033
# Coercion to Various Systems
@@ -670,7 +673,7 @@ cdef class Matrix(Matrix0):
670673
entries = [[sib(v, 2) for v in row] for row in self.rows()]
671674
return sib.name('matrix')(self.base_ring(), entries)
672675

673-
def numpy(self, dtype=None, copy=True):
676+
def numpy(self, dtype=None, copy=_MISSING):
674677
"""
675678
Return the Numpy matrix associated to this matrix.
676679
@@ -680,14 +683,6 @@ cdef class Matrix(Matrix0):
680683
then the type will be determined as the minimum type required
681684
to hold the objects in the sequence.
682685
683-
- ``copy`` -- if `self` is already an `ndarray`, then this flag
684-
determines whether the data is copied (the default), or whether
685-
the internal array is returned. Note that this is incompatible
686-
with the behavior of ``copy`` argument to ``__array__`` method
687-
in numpy 2.0, see `Adapting to changes in the copy keyword
688-
<https://numpy.org/devdocs/numpy_2_0_migration_guide.html#adapting-to-changes-in-the-copy-keyword>`_.
689-
Currently SageMath is using numpy 1.26.
690-
691686
EXAMPLES::
692687
693688
sage: # needs numpy
@@ -713,7 +708,7 @@ cdef class Matrix(Matrix0):
713708
typecodes::
714709
715710
sage: import numpy # needs numpy
716-
sage: numpy.typecodes.items() # needs numpy
711+
sage: numpy.typecodes.items() # needs numpy # random
717712
[('All', '?bhilqpBHILQPefdgFDGSUVOMm'), ('AllFloat', 'efdgFDG'),
718713
...
719714
@@ -738,40 +733,69 @@ cdef class Matrix(Matrix0):
738733
sage: b.shape
739734
(3, 4)
740735
741-
TESTS:
742-
743-
This ensures the docstring above is correct. It needs to be changed
744-
when numpy version in SageMath is updated to 2.0.0::
736+
TESTS::
745737
746738
sage: # needs numpy
747-
sage: import numpy as np
748-
sage: np.__version__
749-
'1.26.4'
739+
sage: matrix(3, range(12)).numpy(copy=False)
740+
doctest:warning...
741+
DeprecationWarning: passing copy argument to numpy() is deprecated
742+
See https://github.com/sagemath/sage/issues/39152 for details.
743+
array([[ 0, 1, 2, 3],
744+
[ 4, 5, 6, 7],
745+
[ 8, 9, 10, 11]])
750746
"""
747+
if copy is not _MISSING:
748+
from sage.misc.superseded import deprecation
749+
deprecation(39152, "passing copy argument to numpy() is deprecated")
751750
import numpy
752751
return numpy.asarray(self.list(), dtype=dtype).reshape(self.nrows(), self.ncols())
753752

754-
def __array__(self, dtype=None):
753+
def __array__(self, dtype=None, copy=None):
755754
"""
756755
Define the magic ``__array__`` function so that ``numpy.array(m)`` can convert
757756
a matrix ``m`` to a numpy array. See
758757
`Interoperability with NumPy <https://numpy.org/doc/1.26/user/basics.interoperability.html>`_.
759758
760759
Note that subclasses should override :meth:`numpy`, but usually not this method.
761760
762-
SageMath is using Numpy 1.26, so there is no ``copy`` argument.
761+
INPUT:
763762
764-
TESTS:
763+
- ``dtype`` -- the desired data-type for the array. If not given,
764+
then the type will be determined automatically.
765765
766-
This ensures the docstring above is correct. It needs to be changed
767-
when numpy version in SageMath is updated to 2.0.0::
766+
- ``copy`` -- required for numpy 2.0 compatibility.
767+
See <https://numpy.org/devdocs/numpy_2_0_migration_guide.html#adapting-to-changes-in-the-copy-keyword>`_.
768+
Note that ``copy=False`` is not supported.
769+
770+
TESTS::
768771
769772
sage: # needs numpy
770773
sage: import numpy as np
771-
sage: np.__version__
772-
'1.26.4'
773-
"""
774-
return self.numpy(dtype, copy=False)
774+
sage: a = matrix(3, range(12))
775+
sage: if np.lib.NumpyVersion(np.__version__) >= '2.0.0':
776+
....: try:
777+
....: np.array(a, copy=False) # in numpy 2.0, this raises an error
778+
....: except ValueError:
779+
....: pass
780+
....: else:
781+
....: assert False
782+
....: else:
783+
....: b = np.array(a, copy=False) # in numpy 1.26, this means "avoid copy if possible"
784+
....: # https://numpy.org/doc/1.26/reference/generated/numpy.array.html#numpy.array
785+
....: # but no-copy is not supported so it will copy anyway
786+
....: a[0,0] = 1
787+
....: assert b[0,0] == 0
788+
....: b = np.asarray(a)
789+
....: a[0,0] = 2
790+
....: assert b[0,0] == 1
791+
"""
792+
import numpy as np
793+
if np.lib.NumpyVersion(np.__version__) >= '2.0.0':
794+
if copy is False:
795+
raise ValueError("Sage matrix cannot be converted to numpy array without copying")
796+
else:
797+
assert copy is None # numpy versions before 2.0 should not pass copy argument
798+
return self.numpy(dtype)
775799

776800
###################################################
777801
# Construction functions

src/sage/matrix/matrix_mod2_dense.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse
565565
return list(C)
566566
return C
567567

568-
def numpy(self, dtype=None, copy=True):
568+
def numpy(self, dtype=None):
569569
"""
570570
Return the Numpy matrix associated to this matrix.
571571
See :meth:`.matrix1.Matrix.numpy`.

src/sage/matrix/matrix_numpy_dense.pyx

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ cdef class Matrix_numpy_dense(Matrix_dense):
364364
return False
365365
return True
366366

367-
def numpy(self, dtype=None, copy=True):
367+
def numpy(self, dtype=None):
368368
"""
369369
Return the Numpy matrix associated to this matrix.
370370
@@ -373,9 +373,6 @@ cdef class Matrix_numpy_dense(Matrix_dense):
373373
- ``dtype`` -- the desired data-type for the array. If not given,
374374
then the type will be determined automatically.
375375
376-
- ``copy`` -- boolean (default: ``True``); determines whether the data is copied
377-
(the default), or whether the internal numpy array is returned.
378-
379376
EXAMPLES::
380377
381378
sage: m = matrix(RDF,[[1,2],[3,4]])
@@ -425,8 +422,9 @@ cdef class Matrix_numpy_dense(Matrix_dense):
425422
sage: m.numpy()
426423
array([], shape=(5, 0), dtype=float64)
427424
428-
Test for ``copy``::
425+
Test that a copy is always made::
429426
427+
sage: import numpy as np
430428
sage: m = matrix(RDF,2); m
431429
[0.0 0.0]
432430
[0.0 0.0]
@@ -441,17 +439,37 @@ cdef class Matrix_numpy_dense(Matrix_dense):
441439
sage: n
442440
array([[2., 0.],
443441
[0., 0.]])
444-
sage: n=numpy.asarray(m) # should not copy
442+
sage: n=numpy.asarray(m) # should still copy
445443
sage: m[0,0]=4
446444
sage: n
447-
array([[4., 0.],
445+
array([[3., 0.],
448446
[0., 0.]])
449447
sage: n=numpy.asarray(m, dtype=numpy.int64) # should copy
450448
sage: m[0,0]=5
451449
sage: n
452450
array([[4, 0],
453451
[0, 0]])
454-
sage: n=numpy.array(m, dtype=numpy.int64, copy=False)
452+
453+
::
454+
455+
sage: import numpy as np
456+
sage: a = matrix(RDF, 3, range(12))
457+
sage: if np.lib.NumpyVersion(np.__version__) >= '2.0.0':
458+
....: try:
459+
....: np.array(a, copy=False) # in numpy 2.0, this raises an error
460+
....: except ValueError:
461+
....: pass
462+
....: else:
463+
....: assert False
464+
....: else:
465+
....: b = np.array(a, copy=False) # in numpy 1.26, this means "avoid copy if possible"
466+
....: # https://numpy.org/doc/1.26/reference/generated/numpy.array.html#numpy.array
467+
....: # but no-copy is not supported so it will copy anyway
468+
....: a[0,0] = 1
469+
....: assert b[0,0] == 0
470+
....: b = np.asarray(a)
471+
....: a[0,0] = 2
472+
....: assert b[0,0] == 1
455473
456474
Make sure it's reasonably fast (the temporary numpy array is immediately
457475
destroyed otherwise it consumes 200MB memory)::
@@ -467,7 +485,7 @@ cdef class Matrix_numpy_dense(Matrix_dense):
467485
(3000+0j)
468486
"""
469487
import numpy as np
470-
return np.array(self._matrix_numpy, dtype=dtype, copy=copy)
488+
return np.array(self._matrix_numpy, dtype=dtype)
471489

472490
def _replace_self_with_numpy(self, numpy_matrix):
473491
"""

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy