Skip to content

Commit

Permalink
Merge pull request #596 from mrava87/patch_numpyv2
Browse files Browse the repository at this point in the history
Feature: migrate to numpy v2.0.0
  • Loading branch information
mrava87 authored Aug 9, 2024
2 parents a9c7f0f + 6510207 commit 32bad36
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ sphinx:
# Declare the Python requirements required to build your docs
python:
install:
- requirements: requirements-dev.txt
- requirements: requirements-doc.txt
- method: pip
path: .
11 changes: 11 additions & 0 deletions docs/source/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ In alphabetic order:

dtcwt
-----

.. warning::

``dtcwt`` is not yet supported with Numpy 2.

`dtcwt <https://dtcwt.readthedocs.io/en/0.12.0/>`_ is a library used to implement the DT-CWT operators.

Install it via ``pip`` with:
Expand All @@ -330,6 +335,7 @@ Install it via ``pip`` with:
>> pip install dtcwt
Devito
------
`Devito <https://github.com/devitocodes/devito>`_ is a library used to solve PDEs via
Expand Down Expand Up @@ -468,6 +474,11 @@ or with ``pip`` via
SPGL1
-----

.. warning::

``SPGL1`` is not yet supported with Numpy 2.

`SPGL1 <https://spgl1.readthedocs.io/en/latest/>`_ is used to solve sparsity-promoting
basis pursuit, basis pursuit denoise, and Lasso problems
in :py:func:`pylops.optimization.sparsity.SPGL1` solver.
Expand Down
2 changes: 1 addition & 1 deletion environment-dev-arm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ channels:
dependencies:
- python>=3.6.4
- pip
- numpy>=1.21.0,<2.0.0
- numpy>=1.21.0
- scipy>=1.11.0
- pytorch>=1.2.0
- cpuonly
Expand Down
2 changes: 1 addition & 1 deletion environment-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ channels:
dependencies:
- python>=3.6.4
- pip
- numpy>=1.21.0,<2.0.0
- numpy>=1.21.0
- scipy>=1.11.0
- pytorch>=1.2.0
- cpuonly
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ channels:
- defaults
dependencies:
- python>=3.6.4
- numpy>=1.21.0,<2.0.0
- numpy>=1.21.0
- scipy>=1.14.0
19 changes: 15 additions & 4 deletions pylops/basicoperators/restriction.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
__all__ = ["Restriction"]

import logging

from typing import Sequence, Union

import numpy as np
import numpy.ma as np_ma
from numpy.core.multiarray import normalize_axis_index

# need to check numpy version since normalize_axis_index will be
# soon moved from numpy.core.multiarray to from numpy.lib.array_utils
np_version = np.__version__.split(".")
if int(np_version[0]) < 2:
from numpy.core.multiarray import normalize_axis_index
else:
from numpy.lib.array_utils import normalize_axis_index

from pylops import LinearOperator
from pylops.utils._internal import _value_or_sized_to_tuple
Expand Down Expand Up @@ -128,8 +134,13 @@ def __init__(
)
forceflat = None

super().__init__(dtype=np.dtype(dtype), dims=dims, dimsd=dimsd,
forceflat=forceflat, name=name)
super().__init__(
dtype=np.dtype(dtype),
dims=dims,
dimsd=dimsd,
forceflat=forceflat,
name=name,
)

iavareshape = np.ones(len(self.dims), dtype=int)
iavareshape[axis] = len(iava)
Expand Down
27 changes: 13 additions & 14 deletions pylops/linearoperator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1242,23 +1242,14 @@ def _get_dtype(
) -> DTypeLike:
if dtypes is None:
dtypes = []
opdtypes = []
for obj in operators:
if obj is not None and hasattr(obj, "dtype"):
opdtypes.append(obj.dtype)
return np.find_common_type(opdtypes, dtypes)
dtypes.append(obj.dtype)
return np.result_type(*dtypes)


class _ScaledLinearOperator(LinearOperator):
"""
Sum Linear Operator
Modified version of scipy _ScaledLinearOperator which uses a modified
_get_dtype where the scalar and operator types are passed separately to
np.find_common_type. Passing them together does lead to problems when using
np.float32 operators which are cast to np.float64
"""
"""Scaled Linear Operator"""

def __init__(
self,
Expand All @@ -1269,7 +1260,15 @@ def __init__(
raise ValueError("LinearOperator expected as A")
if not np.isscalar(alpha):
raise ValueError("scalar expected as alpha")
dtype = _get_dtype([A], [type(alpha)])
if isinstance(alpha, complex) and not np.iscomplexobj(
np.ones(1, dtype=A.dtype)
):
# if the scalar is of complex type but not the operator, find out type
dtype = _get_dtype([A], [type(alpha)])
else:
# if both the scalar and operator are of real or complex type, use type
# of the operator
dtype = A.dtype
super(_ScaledLinearOperator, self).__init__(dtype=dtype, shape=A.shape)
self.args = (A, alpha)

Expand Down Expand Up @@ -1465,7 +1464,7 @@ def __init__(self, A: LinearOperator, p: int) -> None:
if not isintlike(p) or p < 0:
raise ValueError("non-negative integer expected as p")

super(_PowerLinearOperator, self).__init__(dtype=_get_dtype([A]), shape=A.shape)
super(_PowerLinearOperator, self).__init__(dtype=A.dtype, shape=A.shape)
self.args = (A, p)

def _power(self, fun: Callable, x: NDArray) -> NDArray:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ classifiers = [
"Topic :: Scientific/Engineering :: Mathematics",
]
dependencies = [
"numpy >= 1.21.0 , < 2.0.0",
"numpy >= 1.21.0",
"scipy >= 1.11.0",
]
dynamic = ["version"]
Expand Down
12 changes: 12 additions & 0 deletions pytests/test_dtcwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

from pylops.signalprocessing import DTCWT

# currently test only if numpy<2.0.0 is installed...
np_version = np.__version__.split(".")

par1 = {"ny": 10, "nx": 10, "dtype": "float64"}
par2 = {"ny": 50, "nx": 50, "dtype": "float64"}

Expand All @@ -17,6 +20,8 @@ def sequential_array(shape):
@pytest.mark.parametrize("par", [(par1), (par2)])
def test_dtcwt1D_input1D(par):
"""Test for DTCWT with 1D input"""
if int(np_version[0]) >= 2:
return

t = sequential_array((par["ny"],))

Expand All @@ -31,6 +36,8 @@ def test_dtcwt1D_input1D(par):
@pytest.mark.parametrize("par", [(par1), (par2)])
def test_dtcwt1D_input2D(par):
"""Test for DTCWT with 2D input (forward-inverse pair)"""
if int(np_version[0]) >= 2:
return

t = sequential_array(
(
Expand All @@ -50,6 +57,8 @@ def test_dtcwt1D_input2D(par):
@pytest.mark.parametrize("par", [(par1), (par2)])
def test_dtcwt1D_input3D(par):
"""Test for DTCWT with 3D input (forward-inverse pair)"""
if int(np_version[0]) >= 2:
return

t = sequential_array((par["ny"], par["ny"], par["ny"]))

Expand All @@ -64,6 +73,9 @@ def test_dtcwt1D_input3D(par):
@pytest.mark.parametrize("par", [(par1), (par2)])
def test_dtcwt1D_birot(par):
"""Test for DTCWT birot (forward-inverse pair)"""
if int(np_version[0]) >= 2:
return

birots = ["antonini", "legall", "near_sym_a", "near_sym_b"]

t = sequential_array(
Expand Down
8 changes: 7 additions & 1 deletion pytests/test_sparsity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from pylops.basicoperators import FirstDerivative, Identity, MatrixMult
from pylops.optimization.sparsity import fista, irls, ista, omp, spgl1, splitbregman

# currently test spgl1 only if numpy<2.0.0 is installed...
np_version = np.__version__.split(".")

par1 = {
"ny": 11,
"nx": 11,
Expand Down Expand Up @@ -359,6 +362,9 @@ def test_ISTA_FISTA_multiplerhs(par):
)
def test_SPGL1(par):
"""Invert problem with SPGL1"""
if int(np_version[0]) >= 2:
return

np.random.seed(42)
Aop = MatrixMult(np.random.randn(par["ny"], par["nx"]))

Expand Down Expand Up @@ -412,6 +418,6 @@ def test_SplitBregman(par):
x0=x0 if par["x0"] else None,
restart=False,
show=False,
**dict(iter_lim=5, damp=1e-3)
**dict(iter_lim=5, damp=1e-3),
)
assert (np.linalg.norm(x - xinv) / np.linalg.norm(x)) < 1e-1
17 changes: 17 additions & 0 deletions pytests/test_torchoperator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import platform

import numpy as np
import pytest
import torch
Expand All @@ -17,6 +19,11 @@ def test_TorchOperator(par):
must equal the adjoint of operator applied to the same vector, the two
results are also checked to be the same.
"""
# temporarily, skip tests on mac as torch seems not to recognized
# numpy when v2 is installed
if platform.system() == "Darwin":
return

Dop = MatrixMult(np.random.normal(0.0, 1.0, (par["ny"], par["nx"])))
Top = TorchOperator(Dop, batch=False)

Expand All @@ -40,6 +47,11 @@ def test_TorchOperator(par):
@pytest.mark.parametrize("par", [(par1)])
def test_TorchOperator_batch(par):
"""Apply forward for input with multiple samples (= batch) and flattened arrays"""
# temporarily, skip tests on mac as torch seems not to recognized
# numpy when v2 is installed
if platform.system() == "Darwin":
return

Dop = MatrixMult(np.random.normal(0.0, 1.0, (par["ny"], par["nx"])))
Top = TorchOperator(Dop, batch=True)

Expand All @@ -56,6 +68,11 @@ def test_TorchOperator_batch(par):
@pytest.mark.parametrize("par", [(par1)])
def test_TorchOperator_batch_nd(par):
"""Apply forward for input with multiple samples (= batch) and nd-arrays"""
# temporarily, skip tests on mac as torch seems not to recognized
# numpy when v2 is installed
if platform.system() == "Darwin":
return

Dop = MatrixMult(np.random.normal(0.0, 1.0, (par["ny"], par["nx"])), otherdims=(2,))
Top = TorchOperator(Dop, batch=True, flatten=False)

Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
numpy>=1.21.0,<2.0.0
numpy>=1.21.0
scipy>=1.11.0
--extra-index-url https://download.pytorch.org/whl/cpu
torch>=1.2.0
Expand Down
32 changes: 32 additions & 0 deletions requirements-doc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Currently we force rdt to use numpy<2.0.0 to build the documentation
# since the dtcwt and spgl1 are not yet compatible with numpy=2.0.0
numpy>=1.21.0,<2.0.0
scipy>=1.11.0
--extra-index-url https://download.pytorch.org/whl/cpu
torch>=1.2.0
numba
pyfftw
PyWavelets
spgl1
scikit-fmm
sympy
devito
dtcwt
matplotlib
ipython
pytest
pytest-runner
setuptools_scm
docutils<0.18
Sphinx
pydata-sphinx-theme
sphinx-gallery
numpydoc
nbsphinx
image
pre-commit
autopep8
isort
black
flake8
mypy

0 comments on commit 32bad36

Please sign in to comment.