moble / spherical_functions

Python/numba package for evaluating Wigner D matrices and spin-weighted spherical harmonics
MIT License
52 stars 11 forks source link

Allow installation on python 3.12 via pip #27

Closed SimonMaenaut closed 6 months ago

SimonMaenaut commented 6 months ago

It is not possible to install this package on python 3.12 (released 2023-10-02) due to the limitation in pyproject.toml :

https://github.com/moble/spherical_functions/blob/96ecc3ee1c50d3c8f0d1da7116cdc3285367cb87/pyproject.toml#L13

While I understand this package is no longer in active development, many other packages (indirectly) depend on it.

With very minor tweaks I installed it in a virtual environment and ran all the tests.

Output of running the tests ``` pytest tests --run_slow_tests ========================================== test session starts =========================================== platform linux -- Python 3.12.3, pytest-8.1.2, pluggy-1.5.0 -- python cachedir: .pytest_cache rootdir: spherical_functions configfile: pyproject.toml plugins: anyio-4.3.0 collected 71 items tests/test_SWSHs.py::test_SWSH_NINJA_values PASSED [ 1%] tests/test_SWSHs.py::test_SWSH_values PASSED [ 2%] tests/test_SWSHs.py::test_SWSH_WignerD_expression PASSED [ 4%] tests/test_SWSHs.py::test_SWSH_spin_behavior PASSED [ 5%] tests/test_SWSHs.py::test_SWSH_conjugation PASSED [ 7%] tests/test_SWSHs.py::test_SWSH_grid PASSED [ 8%] tests/test_SWSHs.py::test_SWSH_signatures PASSED [ 9%] tests/test_Wigner3j.py::test_Wigner3j_properties PASSED [ 11%] tests/test_Wigner3j.py::test_Wigner3j_values PASSED [ 12%] tests/test_WignerD.py::test_Wigner_D_linear_indices PASSED [ 14%] tests/test_WignerD.py::test_Wigner_D_matrices_negative_argument PASSED [ 15%] tests/test_WignerD.py::test_Wigner_D_elements_representation_property PASSED [ 16%] tests/test_WignerD.py::test_Wigner_D_matrices_representation_property PASSED [ 18%] tests/test_WignerD.py::test_Wigner_D_matrix_inverse PASSED [ 19%] tests/test_WignerD.py::test_Wigner_D_element_symmetries PASSED [ 21%] tests/test_WignerD.py::test_Wigner_D_element_roundoff PASSED [ 22%] tests/test_WignerD.py::test_Wigner_D_element_underflow PASSED [ 23%] tests/test_WignerD.py::test_Wigner_D_element_overflow PASSED [ 25%] tests/test_WignerD.py::test_Wigner_D_element_values PASSED [ 26%] tests/test_WignerD.py::test_Wigner_D_matrix PASSED [ 28%] tests/test_WignerD.py::test_Wigner_D_input_types PASSED [ 29%] tests/test_WignerD.py::test_Wigner_D_signatures PASSED [ 30%] tests/test_mode_conversions.py::test_eth_derivation[eth_NP-1] PASSED [ 32%] tests/test_mode_conversions.py::test_eth_derivation[eth_GHP-1] PASSED [ 33%] tests/test_mode_conversions.py::test_eth_derivation[ethbar_NP--1] PASSED [ 35%] tests/test_mode_conversions.py::test_eth_derivation[ethbar_GHP--1] PASSED [ 36%] tests/test_mode_conversions.py::test_ethbar_inverse_NP PASSED [ 38%] tests/test_modes.py::test_modes_creation PASSED [ 39%] tests/test_modes.py::test_modes_copying_and_pickling[np_copy] XFAIL (Unexpected numpy defaults) [ 40%] tests/test_modes.py::test_modes_copying_and_pickling[np_array_copy] XFAIL (Unexpected numpy de...) [ 42%] tests/test_modes.py::test_modes_copying_and_pickling[np_array_copy_subok] PASSED [ 43%] tests/test_modes.py::test_modes_copying_and_pickling[ndarray_copy] PASSED [ 45%] tests/test_modes.py::test_modes_copying_and_pickling[pickle_roundtrip] PASSED [ 46%] tests/test_modes.py::test_modes_copying_and_pickling[copy_copy] PASSED [ 47%] tests/test_modes.py::test_modes_copying_and_pickling[copy_deepcopy] PASSED [ 49%] tests/test_modes.py::test_modes_grid PASSED [ 50%] tests/test_modes.py::test_modes_addition PASSED [ 52%] tests/test_modes.py::test_modes_subtraction PASSED [ 53%] tests/test_modes.py::test_modes_multiplication PASSED [ 54%] tests/test_modes.py::test_modes_conjugate PASSED [ 56%] tests/test_modes.py::test_modes_real PASSED [ 57%] tests/test_modes.py::test_modes_imag PASSED [ 59%] tests/test_modes.py::test_modes_squared_angular_momenta PASSED [ 60%] tests/test_modes.py::test_modes_derivative_commutators PASSED [ 61%] tests/test_modes.py::test_modes_derivatives_on_grids PASSED [ 63%] tests/test_modes.py::test_modes_norm PASSED [ 64%] tests/test_modes.py::test_modes_ufuncs PASSED [ 66%] tests/test_multiplication.py::test_trivial_multiplication[multiply] PASSED [ 67%] tests/test_multiplication.py::test_trivial_multiplication[spinsfast_multiply] PASSED [ 69%] tests/test_multiplication.py::test_SWSH_multiplication_formula[multiply] PASSED [ 70%] tests/test_multiplication.py::test_SWSH_multiplication_formula[spinsfast_multiply] PASSED [ 71%] tests/test_multiplication.py::test_first_nontrivial_multiplication[multiply] PASSED [ 73%] tests/test_multiplication.py::test_first_nontrivial_multiplication[spinsfast_multiply] PASSED [ 74%] tests/test_recursion.py::test_WignerDRecursion_accuracy PASSED [ 76%] tests/test_recursion.py::test_WignerDRecursion_timing PASSED [ 77%] tests/test_recursion.py::test_WignerDRecursion_lineprofiling PASSED [ 78%] tests/test_recursions.py::test_complex_powers PASSED [ 80%] tests/test_spherical_functions.py::test_constant_as_ell_0_mode PASSED [ 81%] tests/test_spherical_functions.py::test_vector_as_ell_1_modes PASSED [ 83%] tests/test_spherical_functions.py::test_finite_constant_arrays PASSED [ 84%] tests/test_spherical_functions.py::test_factorials PASSED [ 85%] tests/test_spherical_functions.py::test_binomial_coefficients PASSED [ 87%] tests/test_spherical_functions.py::test_ladder_operator_coefficient PASSED [ 88%] tests/test_spherical_functions.py::test_Wigner_coefficient PASSED [ 90%] tests/test_spherical_functions.py::test_LM_range PASSED [ 91%] tests/test_spherical_functions.py::test_LM_index PASSED [ 92%] tests/test_spherical_functions.py::test_LM_total_size PASSED [ 94%] tests/test_spherical_functions.py::test_LMpM_range PASSED [ 95%] tests/test_spherical_functions.py::test_LMpM_range_half_integer PASSED [ 97%] tests/test_spherical_functions.py::test_LMpM_index PASSED [ 98%] tests/test_spherical_functions.py::test_LMpM_total_size PASSED [100%] ============================================ warnings summary ============================================ tests/test_recursion.py: 165 warnings spherical_functions/tests/test_recursion.py:39: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.) error = float(min(abs(sympyd+myd), abs(sympyd-myd))) -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html ======================== 69 passed, 2 xfailed, 165 warnings in 1197.11s (0:19:57) ======================== pytest tests --run_slow_tests 1208.92s user 150.47s system 113% cpu 19:58.43 total ```
Changes made compared to current master ```diff diff --git a/pyproject.toml b/pyproject.toml index 00902cb..09e537f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ packages = [{include = "spherical_functions" }] include = ["spherical_functions/*.npy"] [tool.poetry.dependencies] -python = ">=3.8,<3.12" +python = ">=3.8" numpy = ">=1.20" scipy = "^1.0" numba = ">=0.55" diff --git a/tests/test_WignerD.py b/tests/test_WignerD.py index 346fbcd..2e55f44 100644 --- a/tests/test_WignerD.py +++ b/tests/test_WignerD.py @@ -235,7 +235,7 @@ def test_Wigner_D_element_overflow(Rs, ell_max): def slow_Wignerd(beta, ell, mp, m): # https://en.wikipedia.org/wiki/Wigner_D-matrix#Wigner_.28small.29_d-matrix Prefactor = math.sqrt( - math.factorial(ell + mp) * math.factorial(ell - mp) * math.factorial(ell + m) * math.factorial(ell - m)) + math.factorial(int(ell + mp)) * math.factorial(int(ell - mp)) * math.factorial(int(ell + m)) * math.factorial(int(ell - m))) s_min = int(round(max(0, round(m - mp)))) s_max = int(round(min(round(ell + m), round(ell - mp)))) assert isinstance(s_max, int), type(s_max) @@ -243,8 +243,8 @@ def slow_Wignerd(beta, ell, mp, m): return Prefactor * sum([((-1.) ** (mp - m + s) * math.cos(beta / 2.) ** (2 * ell + m - mp - 2 * s) * math.sin(beta / 2.) ** (mp - m + 2 * s) - / float(math.factorial(ell + m - s) * math.factorial(s) * math.factorial(mp - m + s) - * math.factorial(ell - mp - s))) + / float(math.factorial(int(ell + m - s)) * math.factorial(int(s)) * math.factorial(int(mp - m + s)) + * math.factorial(int(ell - mp - s)))) for s in range(s_min, s_max + 1)]) ```

If there is anything else that needs to be done to support python 3.12, I would be happy to contribute.

moble commented 6 months ago

Closed by #28

In the past, poetry has errored in certain situations if there was no upper bound in python, but it appears to work now. (YYMV; open a new issue if you run into problems.)

Also note that the date of a python release is not usually relevant; what matters is when numba adds support. In this case, numba only added support for python 3.12 in February.

moble commented 6 months ago

Oh, and I can't get github actions to run with python 3.12 on either ubuntu or macos. It complains about missing llvm-config and setuptools.build_meta even though neither is used by this package. Any insight is appreciated.

SimonMaenaut commented 6 months ago

From what I can see it's not installing the latest version of llvmlite (0.42), which is needed to support python 3.12.

SimonMaenaut commented 6 months ago

Found it!

Since python 3.8 is not supported by llvmlite 0.42 poetry refuses to upgrade to this version. It doesn't automatically handle different module versions for different python versions. My advice would be to drop support for python 3.8 in the next release.

Changes needed to stop supporting python 3.8 in the future. ```diff diff --git a/pyproject.toml b/pyproject.toml index 0737118..52b0012 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ packages = [{include = "spherical_functions" }] include = ["spherical_functions/*.npy"] [tool.poetry.dependencies] -python = ">=3.8" +python = ">=3.9" numpy = ">=1.20" scipy = "^1.0" numba = ">=0.55" @@ -26,7 +26,7 @@ line-profiler = ">=3.5.0" [tool.black] line-length = 120 -target-version = ['py38','py39','py310'] +target-version = ['py39','py310','py311','py312'] [tool.pytest.ini_options] minversion = "6.0" ```

There is documentation on how to support different module versions for different python versions, however it's non-trivial.

Changes needed to continue supporting python 3.8 in the future. ```diff git diff pyproject.toml diff --git a/pyproject.toml b/pyproject.toml index 0737118..a1526e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,9 +11,18 @@ include = ["spherical_functions/*.npy"] [tool.poetry.dependencies] python = ">=3.8" -numpy = ">=1.20" -scipy = "^1.0" -numba = ">=0.55" +numpy = [ + {version = "^1.24", python = ">=3.8,<3.9"}, + {version = "^1.26", python = ">=3.9"} +] +scipy = [ + {version = "^1.10", python = ">=3.8,<3.9"}, + {version = "^1.12", python = ">=3.9"} +] +numba = [ + {version = ">=0.55", python = ">=3.8,<3.9"}, + {version = ">=0.59", python = ">=3.9"} +] numpy-quaternion = ">=2022" spinsfast = ">=2022" ```
moble commented 6 months ago

Found it!

Ah, thanks for looking into this!

There is documentation on how to support different module versions for different python versions, however it's non-trivial.

Yeah, especially since I don't actually depend on llvmlite directly...

I think this is the end of the road for me and poetry. I've already switched my newer projects to hatch, which has been a much better experience, so when I get time, I'll do that for this project too.