From 4b0223bd2b57a187fb6f30587ad65c421cba1719 Mon Sep 17 00:00:00 2001 From: Daisuke Oyama Date: Wed, 14 Aug 2024 14:34:46 +0900 Subject: [PATCH 1/2] CI: Add workflow testing against NumPy v2 --- .github/workflows/ci_np2.yml | 32 ++++++++++++++++++++++++++++++++ environment_np2.yml | 18 ++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .github/workflows/ci_np2.yml create mode 100644 environment_np2.yml diff --git a/.github/workflows/ci_np2.yml b/.github/workflows/ci_np2.yml new file mode 100644 index 00000000..c3f7446c --- /dev/null +++ b/.github/workflows/ci_np2.yml @@ -0,0 +1,32 @@ +name: conda-build (NumPy v2) + +on: [push] + +jobs: + build-linux: + runs-on: ubuntu-latest + strategy: + max-parallel: 5 + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.12' + - name: Add conda to system path + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + echo $CONDA/bin >> $GITHUB_PATH + - name: Install dependencies + run: | + conda env update --file environment_np2.yml --name base + - name: Conda info + shell: bash -l {0} + run: | + conda info + conda list + - name: Test with pytest + run: | + conda install pytest + pytest diff --git a/environment_np2.yml b/environment_np2.yml new file mode 100644 index 00000000..bcb48273 --- /dev/null +++ b/environment_np2.yml @@ -0,0 +1,18 @@ +name: qe +channels: + - conda-forge + - defaults +dependencies: + - coverage + - numpy>=2 + - scipy + - pandas + - numba + - sympy + - ipython + - flake8 + - requests + - urllib3>=2 + - flit + - chardet # python>3.9,osx + - pytest From f48c50568bdae2ab9911c1aafa0acaaec15381c4 Mon Sep 17 00:00:00 2001 From: Daisuke Oyama Date: Wed, 14 Aug 2024 15:44:52 +0900 Subject: [PATCH 2/2] FIX: Fix NumPy v2 compatibility issue --- quantecon/markov/gth_solve.py | 7 ++++++- quantecon/markov/tests/test_gth_solve.py | 7 +++++++ quantecon/util/compat.py | 23 +++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 quantecon/util/compat.py diff --git a/quantecon/markov/gth_solve.py b/quantecon/markov/gth_solve.py index b3257c7b..76ca0438 100644 --- a/quantecon/markov/gth_solve.py +++ b/quantecon/markov/gth_solve.py @@ -6,6 +6,9 @@ import numpy as np from numba import jit +from ..util.compat import copy_if_needed + + def gth_solve(A, overwrite=False, use_jit=True): r""" This routine computes the stationary distribution of an irreducible @@ -52,7 +55,9 @@ def gth_solve(A, overwrite=False, use_jit=True): Simulation, Princeton University Press, 2009. """ - A1 = np.array(A, dtype=float, copy=not overwrite, order='C') + copy = copy_if_needed if overwrite else True + + A1 = np.array(A, dtype=float, copy=copy, order='C') # `order='C'` is for use with Numba <= 0.18.2 # See issue github.com/numba/numba/issues/1103 diff --git a/quantecon/markov/tests/test_gth_solve.py b/quantecon/markov/tests/test_gth_solve.py index f5275e86..16651f7e 100644 --- a/quantecon/markov/tests/test_gth_solve.py +++ b/quantecon/markov/tests/test_gth_solve.py @@ -196,6 +196,13 @@ def test_matrices_with_C_F_orders(): assert_array_equal(computed_F, stationary_dist) +def test_unable_to_avoid_copy(): + A = np.array([[0, 1], [0, 1]]) # dtype=int + stationary_dist = [0., 1.] + x = gth_solve(A, overwrite=True) + assert_array_equal(x, stationary_dist) + + def test_raises_value_error_non_2dim(): """Test with non 2dim input""" assert_raises(ValueError, gth_solve, np.array([0.4, 0.6])) diff --git a/quantecon/util/compat.py b/quantecon/util/compat.py new file mode 100644 index 00000000..5037eec8 --- /dev/null +++ b/quantecon/util/compat.py @@ -0,0 +1,23 @@ +""" +Utilities for compatibility + +""" +from typing import Optional +import numpy as np + + +# From scipy/_lib/_util.py + +copy_if_needed: Optional[bool] + +if np.lib.NumpyVersion(np.__version__) >= "2.0.0": + copy_if_needed = None +elif np.lib.NumpyVersion(np.__version__) < "1.28.0": + copy_if_needed = False +else: + # 2.0.0 dev versions, handle cases where copy may or may not exist + try: + np.array([1]).__array__(copy=None) # type: ignore[call-overload] + copy_if_needed = None + except TypeError: + copy_if_needed = False