From 5028aafee80175c8f207a902b271779c53ee255a Mon Sep 17 00:00:00 2001 From: Laurent Farvacque Date: Fri, 21 Jun 2024 11:24:55 +0200 Subject: [PATCH] numpy 2.0 support (#785) * Switch to numpy 2.0, correct Segmentation fault * updated build action * upgrade upload-artifact * update matlab testing * full Matlab tests on linux --- .github/workflows/build-python-wheels.yml | 15 +++----- .github/workflows/matlab-tests.yml | 10 +++--- atmat/attests/githubrun.m | 11 +++--- atmat/attests/githubsetup.m | 7 +--- pyat/at.c | 18 +++++----- pyat/at/load/matfile.py | 2 +- pyat/at/load/reprfile.py | 2 +- pyat/at/load/utils.py | 43 ++++++++++++++++------- pyproject.toml | 9 +++-- 9 files changed, 65 insertions(+), 52 deletions(-) diff --git a/.github/workflows/build-python-wheels.yml b/.github/workflows/build-python-wheels.yml index f87e234c8..f7a37419e 100644 --- a/.github/workflows/build-python-wheels.yml +++ b/.github/workflows/build-python-wheels.yml @@ -13,7 +13,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04, macos-12, windows-2022] + os: [ubuntu-latest, windows-latest, macos-13, macos-14] steps: - uses: actions/checkout@v4 @@ -22,18 +22,13 @@ jobs: # see: https://github.com/pypa/setuptools_scm/issues/480 fetch-depth: 0 - - name: Install Python - uses: actions/setup-python@v5 - with: - python-version: '3.9' - - name: Build wheels - uses: pypa/cibuildwheel@v2.16.2 + uses: pypa/cibuildwheel@v2.19.1 - name: Upload wheels - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: wheels + name: wheels-${{ matrix.os }} path: ./wheelhouse/*.whl if-no-files-found: error @@ -59,7 +54,7 @@ jobs: run: python -m build --sdist - name: Upload sdist - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: tar.gz path: ./dist/*.tar.gz diff --git a/.github/workflows/matlab-tests.yml b/.github/workflows/matlab-tests.yml index ddd986b8b..3dc1b9b34 100644 --- a/.github/workflows/matlab-tests.yml +++ b/.github/workflows/matlab-tests.yml @@ -26,19 +26,19 @@ jobs: - uses: actions/checkout@v4 - - name: Set up python 3.9 + - name: Set up python uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' cache: pip - name: Set up MATLAB - uses: matlab-actions/setup-matlab@v1 + uses: matlab-actions/setup-matlab@v2 with: - release: R2022a + release: R2023a - name: Atmexall - uses: matlab-actions/run-command@v1 + uses: matlab-actions/run-command@v2 with: command: run('atmat/atpath');githubsetup(); diff --git a/atmat/attests/githubrun.m b/atmat/attests/githubrun.m index 09ac2bb7e..259c98a4c 100644 --- a/atmat/attests/githubrun.m +++ b/atmat/attests/githubrun.m @@ -2,9 +2,12 @@ % normally no reason to run it in a user workflow. tsuite=fullfile(atroot,'attests'); -if ispc || ismac - v=assertSuccess(run(testsuite(tsuite))); -else - v=assertSuccess(run(testsuite(tsuite,'Tag','GitHub'))); +if ~(ispc || ismac) + % Solution for + % "Intel MKL ERROR: Parameter 11 was incorrect on entry to DSBEVD." + % found there: + % https://fr.mathworks.com/matlabcentral/answers/1983899-intel-mkl-error-when-callying-scipy-from-matlab + py.sys.setdlopenflags(int32(bitor(int64(py.os.RTLD_LAZY), int64(py.os.RTLD_DEEPBIND)))); end +v=assertSuccess(run(testsuite(tsuite))); disp(table(v)); diff --git a/atmat/attests/githubsetup.m b/atmat/attests/githubsetup.m index 009611a72..1bee44b30 100644 --- a/atmat/attests/githubsetup.m +++ b/atmat/attests/githubsetup.m @@ -9,14 +9,9 @@ function githubsetup() atmexall -fail if ispc execfile=fullfile(getenv('pythonLocation'),'pythonw.exe'); - execmode='InProcess'; -elseif ismac - execfile=fullfile(getenv('pythonLocation'),'bin','python'); - execmode='InProcess'; else execfile=fullfile(getenv('pythonLocation'),'bin','python'); - execmode='OutOfProcess'; end -pyenv("Version", execfile,'ExecutionMode', execmode); +pyenv("Version", execfile,'ExecutionMode', 'InProcess'); disp(pyenv); end \ No newline at end of file diff --git a/pyat/at.c b/pyat/at.c index 50ca5867e..735d82f54 100644 --- a/pyat/at.c +++ b/pyat/at.c @@ -18,11 +18,13 @@ #include #include +#define atPrintf(...) PySys_WriteStdout(__VA_ARGS__) + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include - -#define NUMPY_IMPORT_ARRAY_RETVAL NULL -#define NUMPY_IMPORT_ARRAY_TYPE void * +#if NPY_ABI_VERSION < 0x02000000 + #define NPY_RAVEL_AXIS 32 +#endif typedef PyObject atElem; @@ -306,11 +308,11 @@ void set_energy_particle(PyObject *lattice, PyObject *energy, void set_current_fillpattern(PyArrayObject *bspos, PyArrayObject *bcurrents, struct parameters *param){ if(bcurrents != NULL){ - PyObject *bcurrentsum = PyArray_Sum(bcurrents, NPY_MAXDIMS, + PyObject *bcurrentsum = PyArray_Sum(bcurrents, NPY_RAVEL_AXIS, PyArray_DESCR(bcurrents)->type_num, - NULL); + NULL); param->beam_current = PyFloat_AsDouble(bcurrentsum); - Py_DECREF(bcurrentsum); + Py_DECREF(bcurrentsum); param->nbunch = PyArray_SIZE(bspos); param->bunch_spos = PyArray_DATA(bspos); param->bunch_currents = PyArray_DATA(bcurrents); @@ -319,7 +321,7 @@ void set_current_fillpattern(PyArrayObject *bspos, PyArrayObject *bcurrents, param->nbunch=1; param->bunch_spos = (double[1]){0.0}; param->bunch_currents = (double[1]){0.0}; - } + } } /* @@ -415,7 +417,7 @@ static PyObject *at_atpass(PyObject *self, PyObject *args, PyObject *kwargs) { else param.nturn = counter; - set_energy_particle(lattice, energy, particle, ¶m); + set_energy_particle(lattice, energy, particle, ¶m); set_current_fillpattern(bspos, bcurrents, ¶m); num_particles = (PyArray_SIZE(rin)/6); diff --git a/pyat/at/load/matfile.py b/pyat/at/load/matfile.py index 92d10666e..643dfdfe2 100644 --- a/pyat/at/load/matfile.py +++ b/pyat/at/load/matfile.py @@ -17,7 +17,7 @@ import scipy.io # imports necessary in 'globals()' for 'eval' -from numpy import array, uint8, NaN # noqa: F401 +from numpy import array, uint8, nan as NaN # noqa: F401 from .allfiles import register_format from .utils import split_ignoring_parentheses, RingParam, keep_elements diff --git a/pyat/at/load/reprfile.py b/pyat/at/load/reprfile.py index 8a1fe7cc6..9bf9d7b1f 100644 --- a/pyat/at/load/reprfile.py +++ b/pyat/at/load/reprfile.py @@ -13,7 +13,7 @@ import numpy as np # imports necessary in 'globals()' for 'eval' -from numpy import array, uint8, NaN # noqa: F401 +from numpy import array, uint8, nan as NaN # noqa: F401 from at.lattice import Lattice, Element diff --git a/pyat/at/load/utils.py b/pyat/at/load/utils.py index ef870af32..8d4872ab9 100644 --- a/pyat/at/load/utils.py +++ b/pyat/at/load/utils.py @@ -13,6 +13,8 @@ "keep_attributes", "split_ignoring_parentheses", "RingParam", + "protect", + "restore", ] import collections @@ -32,6 +34,7 @@ from at.lattice import idtable_element _ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") +_placeholder = "placeholder" def _no_encoder(v): @@ -379,25 +382,41 @@ def element_to_dict(elem: Element, encoder: Callable[[Any], Any] = _no_encoder) return dct -def split_ignoring_parentheses(string: str, delimiter: str = ",") -> list[str]: - """Split a string while keeping parenthesized expressions intact +def split_ignoring_parentheses( + string: str, + delimiter: str = ",", + fence: tuple[str, str] = ('\\(', '\\)'), + maxsplit: int = -1, +) -> list[str]: + """Split a string while keeping protected expressions intact Example: "l=0,hom(4,0.0,0)" -> ["l=0", "hom(4,0.0,0)"] """ - placeholder = "placeholder" + substituted, matches = protect(string, fence=fence) + parts = substituted.split(delimiter, maxsplit=maxsplit) + return restore(matches, *parts) + + +def protect(string: str, fence: tuple[str, str] = ('"', '"')): + inf, outf = fence + pattern = f"{inf}.*?{outf}" substituted = string[:] - matches = collections.deque(re.finditer("\\(.*?\\)", string)) + matches = collections.deque(re.finditer(pattern, string)) for match in matches: - substituted = substituted.replace(match.group(), placeholder, 1) - parts = substituted.split(delimiter) - replaced_parts = [] - for part in parts: - if placeholder in part: + substituted = substituted.replace(match.group(), _placeholder, 1) + return substituted, matches + + +def restore(matches, *parts): + + def rep(part): + while _placeholder in part: next_match = matches.popleft() - part = part.replace(placeholder, next_match.group(), 1) - replaced_parts.append(part) - assert not matches + part = part.replace(_placeholder, next_match.group(), 1) + return part + replaced_parts = [rep(part) for part in parts] + assert not matches return replaced_parts diff --git a/pyproject.toml b/pyproject.toml index dcce5d26b..f36f40069 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,8 @@ requires = [ "setuptools >= 64", "setuptools_scm >= 7", - "oldest-supported-numpy", + "oldest-supported-numpy; python_version <= '3.8'", + "numpy >= 2.0; python_version >= '3.9'", "wheel", ] # build-backend = "setuptools.build_meta" @@ -36,7 +37,8 @@ classifiers = [ requires-python = ">=3.7" dependencies = [ "importlib-resources;python_version<'3.9'", - "numpy >=1.16.6, <2.0", + "numpy >=1.16.6, <2.0; python_version <= '3.8'", + "numpy >= 1.23.5; python_version >= '3.9'", "scipy>=1.4.0" ] @@ -73,9 +75,6 @@ build-verbosity = "1" # "build" frontend fails on windows # build-frontend = "build" -[tool.cibuildwheel.macos] -archs = ["x86_64", "arm64"] - #[tool.cibuildwheel.linux] ## Pass the detected PyAT version to the linux docker containers #environment-pass = ["SETUPTOOLS_SCM_PRETEND_VERSION"]