From e40ef5ae976c08957d3df991db1238455aa8f41c Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Mon, 9 Mar 2026 21:19:49 -0700 Subject: [PATCH 01/25] Update umath patching * Add safety checks to patching * Add warnings related to multi-threaded programs * Implement _GlobalPatch wrapper class which implements patching globally * Rename _patch module to _patch_numpy * Rename patching functions --- .github/workflows/conda-package.yml | 4 +- AGENTS.md | 8 +- CMakeLists.txt | 16 +- conda-recipe-cf/meta.yaml | 2 +- conda-recipe/meta.yaml | 2 +- mkl_umath/AGENTS.md | 8 +- mkl_umath/__init__.py | 9 +- mkl_umath/src/AGENTS.md | 9 +- .../src/{_patch.pyx => _patch_numpy.pyx} | 224 ++++++++++-------- mkl_umath/tests/AGENTS.md | 2 +- mkl_umath/tests/test_basic.py | 26 +- 11 files changed, 174 insertions(+), 136 deletions(-) rename mkl_umath/src/{_patch.pyx => _patch_numpy.pyx} (54%) diff --git a/.github/workflows/conda-package.yml b/.github/workflows/conda-package.yml index 20b6c3ce..914ba5d6 100644 --- a/.github/workflows/conda-package.yml +++ b/.github/workflows/conda-package.yml @@ -133,7 +133,7 @@ jobs: run: | source "$CONDA/etc/profile.d/conda.sh" conda activate test_mkl_umath - python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" + python -c "import mkl_umath, numpy as np; mkl_umath.patch_numpy_umath(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests run: | @@ -328,7 +328,7 @@ jobs: run: | @ECHO ON conda activate mkl_umath_test - python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" + python -c "import mkl_umath, numpy as np; mkl_umath.patch_numpy_umath(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests shell: cmd /C CALL {0} diff --git a/AGENTS.md b/AGENTS.md index edc0d0e8..0712729f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,13 +7,13 @@ Entry point for agent context in this repo. It provides: - `mkl_umath._ufuncs` — OneMKL-backed NumPy ufunc loops -- `mkl_umath._patch` — runtime patching interface (`use_in_numpy()`, `restore()`, `is_patched()`) +- `mkl_umath._patch_numpy` — runtime patching interface (`patch_numpy_umath` `restore_numpy_umath`, `is_patched()`) - Performance-optimized math operations (sin, cos, exp, log, etc.) using Intel MKL VM ## Key components - **Python interface:** `mkl_umath/__init__.py`, `_init_helper.py` - **Core C implementation:** `mkl_umath/src/` (ufuncsmodule.c, mkl_umath_loops.c.src) -- **Cython patch layer:** `mkl_umath/src/_patch.pyx` +- **Cython patch layer:** `mkl_umath/src/_patch_numpy.pyx` - **Code generation:** `generate_umath.py`, `generate_umath_doc.py` - **Build system:** CMake (CMakeLists.txt) + scikit-build @@ -50,9 +50,9 @@ CC=icx pip install --no-build-isolation --no-deps . ## Usage ```python import mkl_umath -mkl_umath.use_in_numpy() # Patch NumPy to use MKL loops +mkl_umath.patch_numpy_umath() # Patch NumPy to use MKL loops # ... perform NumPy operations (now accelerated) ... -mkl_umath.restore() # Restore original NumPy loops +mkl_umath.restore_numpy_umath() # Restore original NumPy loops ``` ## How to work in this repo diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a00e86f..38eb6fb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,13 +135,13 @@ if (UNIX) endif() install(TARGETS _ufuncs LIBRARY DESTINATION mkl_umath) -add_cython_target(_patch "mkl_umath/src/_patch.pyx" C OUTPUT_VAR _generated_src) -Python_add_library(_patch MODULE WITH_SOABI ${_generated_src}) -target_include_directories(_patch PRIVATE "mkl_umath/src/" ${Python_NumPy_INCLUDE_DIRS} ${Python_INCLUDE_DIRS}) -target_compile_definitions(_patch PUBLIC NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) -target_link_libraries(_patch PRIVATE mkl_umath_loops) -set_target_properties(_patch PROPERTIES C_STANDARD 99) +add_cython_target(_patch_numpy "mkl_umath/src/_patch_numpy.pyx" C OUTPUT_VAR _generated_src) +Python_add_library(_patch_numpy MODULE WITH_SOABI ${_generated_src}) +target_include_directories(_patch_numpy PRIVATE "mkl_umath/src/" ${Python_NumPy_INCLUDE_DIRS} ${Python_INCLUDE_DIRS}) +target_compile_definitions(_patch_numpy PUBLIC NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) +target_link_libraries(_patch_numpy PRIVATE mkl_umath_loops) +set_target_properties(_patch_numpy PROPERTIES C_STANDARD 99) if (UNIX) - set_target_properties(_patch PROPERTIES INSTALL_RPATH "$ORIGIN/../..;$ORIGIN/../../..;$ORIGIN") + set_target_properties(_patch_numpy PROPERTIES INSTALL_RPATH "$ORIGIN/../..;$ORIGIN/../../..;$ORIGIN") endif() -install(TARGETS _patch LIBRARY DESTINATION mkl_umath) +install(TARGETS _patch_numpy LIBRARY DESTINATION mkl_umath) diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index 92d5a5db..f678f9af 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -44,7 +44,7 @@ test: imports: - mkl_umath - mkl_umath._ufuncs - - mkl_umath._patch + - mkl_umath._patch_numpy about: home: http://github.com/IntelPython/mkl_umath diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index 38ea112f..4b15337c 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -48,7 +48,7 @@ test: imports: - mkl_umath - mkl_umath._ufuncs - - mkl_umath._patch + - mkl_umath._patch_numpy about: home: http://github.com/IntelPython/mkl_umath diff --git a/mkl_umath/AGENTS.md b/mkl_umath/AGENTS.md index 06b55865..f3c38bed 100644 --- a/mkl_umath/AGENTS.md +++ b/mkl_umath/AGENTS.md @@ -14,9 +14,9 @@ Core MKL-backed ufunc implementation: Python interface, Cython patching, and C/M ## Patching API ```python -mkl_umath.use_in_numpy() # Replace NumPy loops with MKL -mkl_umath.restore() # Restore original NumPy loops -mkl_umath.is_patched() # Check patch status +mkl_umath.patch_numpy_umath() # Replace NumPy loops with MKL +mkl_umath.restore_numpy_umath() # Restore original NumPy loops +mkl_umath.is_patched() # Check patch status ``` ## Development guardrails @@ -31,6 +31,6 @@ mkl_umath.is_patched() # Check patch status - Docstrings: dual NumPy 1.x/2.x support via separate docstring modules ## Notes -- `_patch.pyx` is Cython; changes require Cython rebuild +- `_patch_numpy.pyx` is Cython; changes require Cython rebuild - MKL VM loops in `src/mkl_umath_loops.c.src` - `src/ufuncsmodule.c` — NumPy ufunc registration and dispatch diff --git a/mkl_umath/__init__.py b/mkl_umath/__init__.py index 0477d5a2..148bbc5b 100644 --- a/mkl_umath/__init__.py +++ b/mkl_umath/__init__.py @@ -29,10 +29,13 @@ """ from . import _init_helper -from ._patch import is_patched, mkl_umath, restore, use_in_numpy +from ._patch_numpy import ( + is_patched, + mkl_umath, + patch_numpy_umath, + restore_numpy_umath, +) from ._ufuncs import * from ._version import __version__ -# TODO: add __all__ with public API and remove star imports - del _init_helper diff --git a/mkl_umath/src/AGENTS.md b/mkl_umath/src/AGENTS.md index 83d978e7..9ff52362 100644 --- a/mkl_umath/src/AGENTS.md +++ b/mkl_umath/src/AGENTS.md @@ -7,7 +7,7 @@ C/Cython implementation layer: MKL VM integration, ufunc loops, and NumPy patchi - **ufuncsmodule.h** — ufunc module public headers - **mkl_umath_loops.c.src** — MKL VM loop implementations (template, ~60k LOC) - **mkl_umath_loops.h.src** — loop function declarations (template) -- **_patch.pyx** — Cython patching layer (runtime NumPy loop replacement) +- **_patch_numpy.pyx** — Cython patching layer (runtime NumPy loop replacement) - **fast_loop_macros.h** — loop generation macros - **blocking_utils.h** — blocking/chunking utilities for large arrays @@ -21,15 +21,16 @@ C/Cython implementation layer: MKL VM integration, ufunc loops, and NumPy patchi - Blocking strategy: chunk large arrays for cache efficiency - Error handling: MKL VM status → NumPy error state -## Patching mechanism (_patch.pyx) -- Cython extension exposing `use_in_numpy()`, `restore()`, `is_patched()` +## Patching mechanism (_patch_numpy.pyx) +- Cython extension exposing `patch_numpy_umath()`, `restore_numpy_umath()`, + `is_patched()` - Replaces function pointers in NumPy's ufunc loop tables - Thread-safe: guards against concurrent patching - Reversible: stores original pointers for restoration ## Build output - `mkl_umath_loops.c` → shared library (libmkl_umath_loops.so/.dll) -- `_patch.pyx` → Python extension (_patch.*.so) +- `_patch_numpy.pyx` → Python extension (_patch.*.so) - `ufuncsmodule.c` + `__umath_generated.c` → `_ufuncs` extension ## Development notes diff --git a/mkl_umath/src/_patch.pyx b/mkl_umath/src/_patch_numpy.pyx similarity index 54% rename from mkl_umath/src/_patch.pyx rename to mkl_umath/src/_patch_numpy.pyx index 8d2d299d..6cd2990e 100644 --- a/mkl_umath/src/_patch.pyx +++ b/mkl_umath/src/_patch_numpy.pyx @@ -26,6 +26,9 @@ # distutils: language = c # cython: language_level=3 +from contextlib import ContextDecorator +from threading import Lock, local + import mkl_umath._ufuncs as mu cimport numpy as cnp @@ -36,30 +39,26 @@ from libc.stdlib cimport free, malloc cnp.import_umath() - ctypedef struct function_info: cnp.PyUFuncGenericFunction original_function cnp.PyUFuncGenericFunction patch_function int* signature -cdef class patch: +cdef class _patch_impl: cdef int functions_count cdef function_info* functions - cdef bint _is_patched functions_dict = dict() def __cinit__(self): cdef int pi, oi - self._is_patched = False - umaths = [i for i in dir(mu) if isinstance(getattr(mu, i), np.ufunc)] self.functions_count = 0 for umath in umaths: - mkl_umath = getattr(mu, umath) - self.functions_count += mkl_umath.ntypes + mkl_umath_func = getattr(mu, umath) + self.functions_count += mkl_umath_func.ntypes self.functions = malloc( self.functions_count * sizeof(function_info) @@ -115,7 +114,7 @@ cdef class patch: free(self.functions) def do_patch(self): - cdef int _res + cdef int res cdef cnp.PyUFuncGenericFunction temp cdef cnp.PyUFuncGenericFunction function cdef int* signature @@ -126,14 +125,16 @@ cdef class patch: function = self.functions[index].patch_function signature = self.functions[index].signature # TODO: check res, 0 means success, -1 means error - _res = cnp.PyUFunc_ReplaceLoopBySignature( + res = cnp.PyUFunc_ReplaceLoopBySignature( np_umath, function, signature, &temp ) - - self._is_patched = True + if res != 0: + raise RuntimeError( + f"Failed to patch {func[0]} with signature {func[1]}" + ) def do_unpatch(self): - cdef int _res + cdef int res cdef cnp.PyUFuncGenericFunction temp cdef cnp.PyUFuncGenericFunction function cdef int* signature @@ -143,103 +144,127 @@ cdef class patch: index = self.functions_dict[func] function = self.functions[index].original_function signature = self.functions[index].signature - # TODO: check res, 0 means success, -1 means error - _res = cnp.PyUFunc_ReplaceLoopBySignature( + res = cnp.PyUFunc_ReplaceLoopBySignature( np_umath, function, signature, &temp ) - - self._is_patched = False + if res != 0: + raise RuntimeError( + f"Failed to restore {func[0]} with signature {func[1]}" + ) + + +class _GlobalPatch: + def __init__(self): + self._lock = Lock() + self._patch_count = 0 + self._tls = local() + self._patcher = None + + def do_patch(self, verbose=False): + with self._lock: + local_count = getattr(self._tls, "local_count", 0) + if self._patch_count == 0: + if verbose: + print( + "Now patching NumPy FFT submodule with mkl_fft NumPy " + "interface." + ) + print( + "Please direct bug reports to " + "https://github.com/IntelPython/mkl_fft" + ) + if self._patcher is None: + # lazy initialization of the patcher to save memory + self._patcher = _patch_impl() + self._patcher.do_patch() + + self._patch_count += 1 + self._tls.local_count = local_count + 1 + + def do_restore(self, verbose=False): + with self._lock: + local_count = getattr(self._tls, "local_count", 0) + if local_count <= 0: + if verbose: + print( + "Warning: restore_numpy_umath called more times than " + "patch_numpy_fft in this thread." + ) + return + self._tls.local_count -= 1 + self._patch_count -= 1 + if self._patch_count == 0: + if verbose: + print("Now restoring original NumPy loops.") + self._patcher.do_unpatch() def is_patched(self): - return self._is_patched - -from threading import local as threading_local - -_tls = threading_local() + with self._lock: + return self._patch_count > 0 -def _is_tls_initialized(): - return getattr(_tls, "initialized", False) +_patch = _GlobalPatch() -def _initialize_tls(): - _tls.patch = patch() - _tls.initialized = True - - -def use_in_numpy(): +def patch_numpy_umath(verbose=False): """ - Enables using of mkl_umath in Numpy. - - Examples - -------- - >>> import mkl_umath - >>> mkl_umath.is_patched() - # False - - >>> mkl_umath.use_in_numpy() # Enable mkl_umath in Numpy - >>> mkl_umath.is_patched() - # True - - >>> mkl_umath.restore() # Disable mkl_umath in Numpy - >>> mkl_umath.is_patched() - # False - + Patch NumPy's ufuncs with mkl_umath's loops. + + Parameters + ---------- + verbose : bool, optional + print message when starting the patching process. + + Notes + ----- + This function uses reference-counted semantics. Each call increments a + global patch counter. Restoration requires a matching number of calls + between `patch_numpy_umath` and `restore_numpy_umath`. + + ⚠️ Warning + ------------------------- + If used in a multi-threaded program, ALL concurrent threads executing NumPy + operations must either have applied the patch prior to execution, or run + entirely within the `mkl_umath` context manager. Executing standard NumPy + calls in one thread while another thread is actively patching or unpatching + will lead to undefined behavior at best, and segmentation faults at worst. + For this reason, it is recommended to prefer the `mkl_umath` context + manager. """ - if not _is_tls_initialized(): - _initialize_tls() - _tls.patch.do_patch() + _patch.do_patch(verbose=verbose) -def restore(): +def restore_numpy_umath(verbose=False): """ - Disables using of mkl_umath in Numpy. - - Examples - -------- - >>> import mkl_umath - >>> mkl_umath.is_patched() - # False - - >>> mkl_umath.use_in_numpy() # Enable mkl_umath in Numpy - >>> mkl_umath.is_patched() - # True - - >>> mkl_umath.restore() # Disable mkl_umath in Numpy - >>> mkl_umath.is_patched() - # False - + Restore NumPy's ufuncs to the original loops. + + Parameters + ---------- + verbose : bool, optional + print message when starting restoration process. + + Notes + ----- + This function uses reference-counted semantics. Each call decrements a + global patch counter. Restoration requires a matching number of calls + between `patch_numpy_umath` and `restore_numpy_umath`. + + ⚠️ Warning + ------------------------- + If used in a multi-threaded program, ALL concurrent threads executing NumPy + operations must either have applied the patch prior to execution, or run + entirely within the `mkl_umath` context manager. Executing standard NumPy + calls in one thread while another thread is actively patching or unpatching + will lead to undefined behavior at best, and segmentation faults at worst. + For this reason, it is recommended to prefer the `mkl_umath` context + manager. """ - if not _is_tls_initialized(): - _initialize_tls() - _tls.patch.do_unpatch() + _patch.do_restore(verbose=verbose) def is_patched(): - """ - Returns whether Numpy has been patched with mkl_umath. - - Examples - -------- - >>> import mkl_umath - >>> mkl_umath.is_patched() - # False - - >>> mkl_umath.use_in_numpy() # Enable mkl_umath in Numpy - >>> mkl_umath.is_patched() - # True - - >>> mkl_umath.restore() # Disable mkl_umath in Numpy - >>> mkl_umath.is_patched() - # False - - """ - if not _is_tls_initialized(): - _initialize_tls() - return _tls.patch.is_patched() - - -from contextlib import ContextDecorator + """Return True if NumPy umath loops have been patched by mkl_umath.""" + return _patch.is_patched() class mkl_umath(ContextDecorator): @@ -247,6 +272,16 @@ class mkl_umath(ContextDecorator): Context manager and decorator to temporarily patch NumPy ufuncs with MKL-based implementations. + ⚠️ Warning + ------------------------- + If used in a multi-threaded program, ALL concurrent threads executing NumPy + operations must either have applied the patch prior to execution, or run + entirely within the `mkl_umath` context manager. Executing standard NumPy + calls in one thread while another thread is actively patching or unpatching + will lead to undefined behavior at best, and segmentation faults at worst. + For this reason, it is recommended to prefer the `mkl_umath` context + manager. + Examples -------- >>> import mkl_umath @@ -259,12 +294,11 @@ class mkl_umath(ContextDecorator): >>> mkl_umath.is_patched() # False - """ def __enter__(self): - use_in_numpy() + patch_numpy_umath() return self def __exit__(self, *exc): - restore() + restore_numpy_umath() return False diff --git a/mkl_umath/tests/AGENTS.md b/mkl_umath/tests/AGENTS.md index 74571825..f962fafc 100644 --- a/mkl_umath/tests/AGENTS.md +++ b/mkl_umath/tests/AGENTS.md @@ -7,7 +7,7 @@ Unit tests for MKL-backed ufuncs and NumPy patching. ## Test coverage - Ufunc correctness: compare MKL loops vs NumPy reference -- Patching: `use_in_numpy()`, `restore()`, `is_patched()` state transitions +- Patching: `patch_numpy_umath()`, `restore_numpy_umath()`, `is_patched()` state transitions - Edge cases: NaN, Inf, empty arrays, large arrays - Dtype coverage: float32, float64, complex64, complex128 diff --git a/mkl_umath/tests/test_basic.py b/mkl_umath/tests/test_basic.py index 3fd5a35c..ff53c146 100644 --- a/mkl_umath/tests/test_basic.py +++ b/mkl_umath/tests/test_basic.py @@ -26,7 +26,7 @@ import numpy as np import pytest -import mkl_umath._patch as mp # pylint: disable=no-name-in-module +import mkl_umath import mkl_umath._ufuncs as mu # pylint: disable=no-name-in-module np.random.seed(42) @@ -63,8 +63,8 @@ def get_args(args_str, size, low, high): mkl_cases = {} fall_back_cases = {} for umath in umaths: - mkl_umath = getattr(mu, umath) - types = mkl_umath.types + _mkl_umath = getattr(mu, umath) + types = _mkl_umath.types size_mkl = 8192 + 1 for type_ in types: args_str = type_[: type_.find("->")] @@ -102,10 +102,10 @@ def get_id(val): def test_mkl_umath(case): umath, _ = case args = test_mkl[case] - mkl_umath = getattr(mu, umath) + _mkl_umath = getattr(mu, umath) np_umath = getattr(np, umath) - mkl_res = mkl_umath(*args) + mkl_res = _mkl_umath(*args) np_res = np_umath(*args) assert np.allclose(mkl_res, np_res), f"Results for '{umath}' do not match" @@ -115,10 +115,10 @@ def test_mkl_umath(case): def test_fall_back_umath(case): umath, _ = case args = test_fall_back[case] - mkl_umath = getattr(mu, umath) + _mkl_umath = getattr(mu, umath) np_umath = getattr(np, umath) - mkl_res = mkl_umath(*args) + mkl_res = _mkl_umath(*args) np_res = np_umath(*args) assert np.allclose(mkl_res, np_res), f"Results for '{umath}' do not match" @@ -193,11 +193,11 @@ def test_reduce_complex(func, dtype): def test_patch(): - mp.restore() - assert not mp.is_patched() + mkl_umath.restore_numpy_umath() + assert not mkl_umath.is_patched() - mp.use_in_numpy() # Enable mkl_umath in Numpy - assert mp.is_patched() + mkl_umath.patch_numpy_umath() # Enable mkl_umath in Numpy + assert mkl_umath.is_patched() - mp.restore() # Disable mkl_umath in Numpy - assert not mp.is_patched() + mkl_umath.restore_numpy_umath() # Disable mkl_umath in Numpy + assert not mkl_umath.is_patched() From 4d0d6f0b101ae8a67df1f02f1342f7fb5fc63a4d Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Sat, 28 Feb 2026 23:31:58 -0800 Subject: [PATCH 02/25] move mkl_umath build system to meson * removes cmake and setup.py from the project * vendors process_src_template.py from numpy and updates vendored conv_template.py * updates build scripts * updates meta.yaml * updates pyproject.toml * adds meson.build and meson.options --- CMakeLists.txt | 147 ----------------------- _vendored/README.md | 4 +- _vendored/conv_template.py | 58 +++++---- _vendored/process_src_template.py | 66 ++++++++++ conda-recipe-cf/build.sh | 36 +++--- conda-recipe-cf/meta.yaml | 6 +- conda-recipe/build.sh | 36 +++--- conda-recipe/meta.yaml | 6 +- meson.build | 172 +++++++++++++++++++++++++++ meson.options | 2 + mkl_umath/generate_umath.py | 2 +- mkl_umath/ufunc_docstrings_numpy1.py | 4 +- pyproject.toml | 13 +- setup.py | 107 ----------------- 14 files changed, 328 insertions(+), 331 deletions(-) delete mode 100644 CMakeLists.txt create mode 100644 _vendored/process_src_template.py create mode 100644 meson.build create mode 100644 meson.options delete mode 100644 setup.py diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 38eb6fb7..00000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,147 +0,0 @@ -cmake_minimum_required(VERSION 3.27...3.28 FATAL_ERROR) - -cmake_policy(SET CMP0135 NEW) - -project(mkl_umath - LANGUAGES C - DESCRIPTION "mkl_umath module" -) - -option(OPTIMIZATION_REPORT - "Whether to generate optimization vectorization report" - OFF -) - -find_package(Python COMPONENTS Interpreter Development NumPy REQUIRED) - -# Print out the discovered paths -include(CMakePrintHelpers) -cmake_print_variables(Python_INCLUDE_DIRS) -cmake_print_variables(Python_LIBRARIES) -cmake_print_variables(Python_NumPy_INCLUDE_DIRS) - -set(CYTHON_FLAGS "-t -w \"${CMAKE_SOURCE_DIR}\"") -find_package(Cython REQUIRED) - -find_package(TBB REQUIRED) -set(MKL_ARCH "intel64") -set(MKL_LINK "dynamic") -set(MKL_THREADING "tbb_thread") -set(MKL_INTERFACE "lp64") -find_package(MKL REQUIRED) - -if(WIN32) - string(CONCAT WARNING_FLAGS - "-Wall " - "-Wextra " - "-Winit-self " - "-Wunused-function " - "-Wuninitialized " - "-Wmissing-declarations " - "-Wstrict-prototypes " - "-Wno-unused-parameter " - "-Wno-implicit-function-declaration " - ) - string(CONCAT SDL_FLAGS - "/GS " - "/DynamicBase " - ) - string(CONCAT PRECISION_FLAGS - "/fp:precise " - "/Qimf-precision=high " - "/Qprotect-parens " - ) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox ${WARNING_FLAGS} ${SDL_FLAGS} ${PRECISION_FLAGS}") - set(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} ${WARNING_FLAGS} ${SDL_FLAGS} /fp:precise -O0 -g1 -DDEBUG" - ) - set(MKL_UMATH_LINKER_OPTIONS "LINKER:/NXCompat;LINKER:/DynamicBase") -elseif(UNIX) - string(CONCAT WARNING_FLAGS - "-Wall " - "-Wextra " - "-Winit-self " - "-Wunused-function " - "-Wuninitialized " - "-Wmissing-declarations " - "-Wstrict-prototypes " - "-Wno-unused-parameter " - "-fdiagnostics-color=auto " - ) - string(CONCAT SDL_FLAGS - "-fstack-protector " - "-fstack-protector-all " - "-fpic " - "-fPIC " - "-D_FORTIFY_SOURCE=2 " - "-Wformat " - "-Wformat-security " -# "-fno-strict-overflow " # no-strict-overflow is implied by -fwrapv - "-fno-delete-null-pointer-checks " - "-fwrapv " - ) - string(CONCAT CFLAGS - "${WARNING_FLAGS}" - "${SDL_FLAGS}" - ) - string(CONCAT PRECISION_FLAGS - "-fprotect-parens " - "-fimf-precision=high " - "-fno-fast-math " - ) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 ${CFLAGS} ${PRECISION_FLAGS}") - set(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} ${CFLAGS} -fno-fast-math -O0 -g1 -DDEBUG" - ) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-function-pointer-types ${CFLAGS}") - set(MKL_UMATH_LINKER_OPTIONS "LINKER:-z,noexecstack,-z,relro,-z,now") -else() - message(FATAL_ERROR "Unsupported system.") -endif() - -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) -# set_property(GLOBAL PROPERTY GLOBAL_DEPENDS_DEBUG_MODE 1) -set(_linker_options ${MKL_UMATH_LINKER_OPTIONS}) - -set(_trgt mkl_umath_loops) -add_library(${_trgt} SHARED mkl_umath/src/mkl_umath_loops.c) -set_target_properties(${_trgt} PROPERTIES - CMAKE_POSITION_INDEPENDENT_CODE ON - C_STANDARD 99 -) -target_include_directories(${_trgt} PUBLIC mkl_umath/src/ ${Python_NumPy_INCLUDE_DIRS} ${Python_INCLUDE_DIRS}) -target_link_libraries(${_trgt} PUBLIC MKL::MKL ${Python_LIBRARIES}) -target_link_options(${_trgt} PUBLIC ${_linker_options}) -target_compile_options(${_trgt} PUBLIC -fveclib=SVML) -target_compile_options(${_trgt} PUBLIC -fvectorize) -if(OPTIMIZATION_REPORT) - target_compile_options(${_trgt} PRIVATE -qopt-report=3) -endif() -install(TARGETS ${_trgt} - LIBRARY DESTINATION mkl_umath - ARCHIVE DESTINATION mkl_umath - RUNTIME DESTINATION mkl_umath -) - -python_add_library(_ufuncs MODULE WITH_SOABI "mkl_umath/src/ufuncsmodule.c" "mkl_umath/src/__umath_generated.c") -target_include_directories(_ufuncs PRIVATE "mkl_umath/src" ${Python_NumPy_INCLUDE_DIRS} ${MKL_INCLUDE_DIR}) -target_compile_definitions(_ufuncs PUBLIC NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) -target_link_options(_ufuncs PRIVATE ${_linker_options}) -target_link_libraries(_ufuncs PRIVATE mkl_umath_loops) -set_target_properties(_ufuncs PROPERTIES C_STANDARD 99) -if (UNIX) - set_target_properties(_ufuncs PROPERTIES INSTALL_RPATH "$ORIGIN/../..;$ORIGIN/../../..;$ORIGIN") -endif() -install(TARGETS _ufuncs LIBRARY DESTINATION mkl_umath) - -add_cython_target(_patch_numpy "mkl_umath/src/_patch_numpy.pyx" C OUTPUT_VAR _generated_src) -Python_add_library(_patch_numpy MODULE WITH_SOABI ${_generated_src}) -target_include_directories(_patch_numpy PRIVATE "mkl_umath/src/" ${Python_NumPy_INCLUDE_DIRS} ${Python_INCLUDE_DIRS}) -target_compile_definitions(_patch_numpy PUBLIC NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) -target_link_libraries(_patch_numpy PRIVATE mkl_umath_loops) -set_target_properties(_patch_numpy PROPERTIES C_STANDARD 99) -if (UNIX) - set_target_properties(_patch_numpy PROPERTIES INSTALL_RPATH "$ORIGIN/../..;$ORIGIN/../../..;$ORIGIN") -endif() -install(TARGETS _patch_numpy LIBRARY DESTINATION mkl_umath) diff --git a/_vendored/README.md b/_vendored/README.md index b3f3a919..42ec7946 100644 --- a/_vendored/README.md +++ b/_vendored/README.md @@ -1,5 +1,3 @@ ## Vendored files -File `conv_template.py` is copied from NumPy's numpy/distutils folder, since -`numpy.distutils` is absent from the installation layout starting with -Python 3.12 +Files `conv_template.py` and `process_src_template.py` are copied from NumPy's numpy/numpy/_build_utils folder diff --git a/_vendored/conv_template.py b/_vendored/conv_template.py index c8933d1d..3f634737 100644 --- a/_vendored/conv_template.py +++ b/_vendored/conv_template.py @@ -82,8 +82,8 @@ __all__ = ['process_str', 'process_file'] import os -import sys import re +import sys # names for replacement that are already global. global_names = {} @@ -106,12 +106,12 @@ def parse_structure(astr, level): at zero. Returns an empty list if no loops found. """ - if level == 0 : + if level == 0: loopbeg = "/**begin repeat" loopend = "/**end repeat**/" - else : - loopbeg = "/**begin repeat%d" % level - loopend = "/**end repeat%d**/" % level + else: + loopbeg = f"/**begin repeat{level}" + loopend = f"/**end repeat{level}**/" ind = 0 line = 0 @@ -124,9 +124,9 @@ def parse_structure(astr, level): start2 = astr.find("\n", start2) fini1 = astr.find(loopend, start2) fini2 = astr.find("\n", fini1) - line += astr.count("\n", ind, start2+1) - spanlist.append((start, start2+1, fini1, fini2+1, line)) - line += astr.count("\n", start2+1, fini2) + line += astr.count("\n", ind, start2 + 1) + spanlist.append((start, start2 + 1, fini1, fini2 + 1, line)) + line += astr.count("\n", start2 + 1, fini2) ind = fini2 spanlist.sort() return spanlist @@ -135,10 +135,13 @@ def parse_structure(astr, level): def paren_repl(obj): torep = obj.group(1) numrep = obj.group(2) - return ','.join([torep]*int(numrep)) + return ','.join([torep] * int(numrep)) + parenrep = re.compile(r"\(([^)]*)\)\*(\d+)") plainrep = re.compile(r"([^*]+)\*(\d+)") + + def parse_values(astr): # replaces all occurrences of '(a,b,c)*4' in astr # with 'a,b,c,a,b,c,a,b,c,a,b,c'. Empty braces generate @@ -155,7 +158,7 @@ def parse_values(astr): named_re = re.compile(r"#\s*(\w*)\s*=([^#]*)#") exclude_vars_re = re.compile(r"(\w*)=(\w*)") exclude_re = re.compile(":exclude:") -def parse_loop_header(loophead) : +def parse_loop_header(loophead): """Find all named replacements in the header Returns a list of dictionaries, one for each loop iteration, @@ -179,14 +182,13 @@ def parse_loop_header(loophead) : name = rep[0] vals = parse_values(rep[1]) size = len(vals) - if nsub is None : + if nsub is None: nsub = size - elif nsub != size : + elif nsub != size: msg = "Mismatch in number of values, %d != %d\n%s = %s" raise ValueError(msg % (nsub, size, name, vals)) names.append((name, vals)) - # Find any exclude variables excludes = [] @@ -200,30 +202,33 @@ def parse_loop_header(loophead) : # generate list of dictionaries, one for each template iteration dlist = [] - if nsub is None : + if nsub is None: raise ValueError("No substitution variables found") for i in range(nsub): tmp = {name: vals[i] for name, vals in names} dlist.append(tmp) return dlist + replace_re = re.compile(r"@(\w+)@") -def parse_string(astr, env, level, line) : - lineno = "#line %d\n" % line + + +def parse_string(astr, env, level, line): + lineno = f"#line {line}\n" # local function for string replacement, uses env def replace(match): name = match.group(1) - try : + try: val = env[name] except KeyError: - msg = 'line %d: no definition of key "%s"'%(line, name) + msg = f'line {line}: no definition of key "{name}"' raise ValueError(msg) from None return val code = [lineno] struct = parse_structure(astr, level) - if struct : + if struct: # recurse over inner loops oldend = 0 newlevel = level + 1 @@ -234,18 +239,18 @@ def replace(match): oldend = sub[3] newline = line + sub[4] code.append(replace_re.sub(replace, pref)) - try : + try: envlist = parse_loop_header(head) except ValueError as e: - msg = "line %d: %s" % (newline, e) + msg = f"line {newline}: {e}" raise ValueError(msg) - for newenv in envlist : + for newenv in envlist: newenv.update(env) newcode = parse_string(text, newenv, newlevel, newline) code.extend(newcode) suff = astr[oldend:] code.append(replace_re.sub(replace, suff)) - else : + else: # replace keys code.append(replace_re.sub(replace, astr)) code.append('\n') @@ -284,8 +289,8 @@ def process_file(source): try: code = process_str(''.join(lines)) except ValueError as e: - raise ValueError('In "%s" loop at %s' % (sourcefile, e)) from None - return '#line 1 "%s"\n%s' % (sourcefile, code) + raise ValueError(f'In "{sourcefile}" loop at {e}') from None + return f'#line 1 "{sourcefile}"\n{code}' def unique_key(adict): @@ -321,9 +326,10 @@ def main(): try: writestr = process_str(allstr) except ValueError as e: - raise ValueError("In %s loop at %s" % (file, e)) from None + raise ValueError(f"In {file} loop at {e}") from None outfile.write(writestr) + if __name__ == "__main__": main() diff --git a/_vendored/process_src_template.py b/_vendored/process_src_template.py new file mode 100644 index 00000000..f934c222 --- /dev/null +++ b/_vendored/process_src_template.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import argparse +import importlib.util +import os + + +def get_processor(): + # Convoluted because we can't import from numpy + # (numpy is not yet built) + conv_template_path = os.path.join( + os.path.dirname(__file__), + 'conv_template.py' + ) + spec = importlib.util.spec_from_file_location( + 'conv_template', conv_template_path + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod.process_file + + +def process_and_write_file(fromfile, outfile): + """Process tempita templated file and write out the result. + + The template file is expected to end in `.src` + (e.g., `.c.src` or `.h.src`). + Processing `npy_somefile.c.src` generates `npy_somefile.c`. + + """ + process_file = get_processor() + content = process_file(fromfile) + with open(outfile, 'w') as f: + f.write(content) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "infile", + type=str, + help="Path to the input file" + ) + parser.add_argument( + "-o", + "--outfile", + type=str, + help="Path to the output file" + ) + parser.add_argument( + "-i", + "--ignore", + type=str, + help="An ignored input - may be useful to add a " + "dependency between custom targets", + ) + args = parser.parse_args() + + if not args.infile.endswith('.src'): + raise ValueError(f"Unexpected extension: {args.infile}") + + outfile_abs = os.path.join(os.getcwd(), args.outfile) + process_and_write_file(args.infile, outfile_abs) + + +if __name__ == "__main__": + main() diff --git a/conda-recipe-cf/build.sh b/conda-recipe-cf/build.sh index 00414a1c..23ab8731 100644 --- a/conda-recipe-cf/build.sh +++ b/conda-recipe-cf/build.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # This is necessary to help DPC++ find Intel libraries such as SVML, IRNG, etc in build prefix export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${BUILD_PREFIX}/lib" @@ -11,21 +12,28 @@ export ICXCFG read -r GLIBC_MAJOR GLIBC_MINOR <<< "$(conda list '^sysroot_linux-64$' \ | tail -n 1 | awk '{print $2}' | grep -oP '\d+' | head -n 2 | tr '\n' ' ')" -export CMAKE_GENERATOR="Ninja" -SKBUILD_ARGS=( - "--" - "-DCMAKE_C_COMPILER:PATH=icx" - "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" -) +if [-d "build"]; then + rm -rf build +fi + +export CC=icx +export CXX=icpx + +${PYTHON} -m build -w -n -x + +${PYTHON} -m wheel tags --remove \ + --platform "manylinux_${GLIBC_MAJOR}_${GLIBC_MINOR}_x86_64" \ + dist/mkl_umath*.whl + +${PYTHON} -m pip install dist/mkl_umath*.whl \ + --no-build-isolation \ + --no-deps \ + --only-binary :all: \ + --no-index \ + --prefix "${PREFIX}" + -vv if [ -n "${WHEELS_OUTPUT_FOLDER}" ]; then - # Install packages and assemble wheel package from built bits - WHEELS_BUILD_ARGS=( - "-p" "manylinux_${GLIBC_MAJOR}_${GLIBC_MINOR}_x86_64" - ) - ${PYTHON} setup.py install bdist_wheel "${WHEELS_BUILD_ARGS[@]}" "${SKBUILD_ARGS[@]}" + mkdir -p "${WHEELS_OUTPUT_FOLDER}" cp dist/mkl_umath*.whl "${WHEELS_OUTPUT_FOLDER}" -else - # Perform regular install - ${PYTHON} setup.py install "${SKBUILD_ARGS[@]}" fi diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index f678f9af..1d8d5177 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -20,12 +20,12 @@ requirements: - {{ compiler('dpcpp') }} >=2024.2 # [not osx] - sysroot_linux-64 >=2.28 # [linux] host: - - setuptools >=77 - - cmake + - meson-python >=0.13.0 + - meson + - pkg-config - ninja - git - cython - - scikit-build - python - mkl-devel - tbb-devel diff --git a/conda-recipe/build.sh b/conda-recipe/build.sh index 00414a1c..23ab8731 100644 --- a/conda-recipe/build.sh +++ b/conda-recipe/build.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # This is necessary to help DPC++ find Intel libraries such as SVML, IRNG, etc in build prefix export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${BUILD_PREFIX}/lib" @@ -11,21 +12,28 @@ export ICXCFG read -r GLIBC_MAJOR GLIBC_MINOR <<< "$(conda list '^sysroot_linux-64$' \ | tail -n 1 | awk '{print $2}' | grep -oP '\d+' | head -n 2 | tr '\n' ' ')" -export CMAKE_GENERATOR="Ninja" -SKBUILD_ARGS=( - "--" - "-DCMAKE_C_COMPILER:PATH=icx" - "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" -) +if [-d "build"]; then + rm -rf build +fi + +export CC=icx +export CXX=icpx + +${PYTHON} -m build -w -n -x + +${PYTHON} -m wheel tags --remove \ + --platform "manylinux_${GLIBC_MAJOR}_${GLIBC_MINOR}_x86_64" \ + dist/mkl_umath*.whl + +${PYTHON} -m pip install dist/mkl_umath*.whl \ + --no-build-isolation \ + --no-deps \ + --only-binary :all: \ + --no-index \ + --prefix "${PREFIX}" + -vv if [ -n "${WHEELS_OUTPUT_FOLDER}" ]; then - # Install packages and assemble wheel package from built bits - WHEELS_BUILD_ARGS=( - "-p" "manylinux_${GLIBC_MAJOR}_${GLIBC_MINOR}_x86_64" - ) - ${PYTHON} setup.py install bdist_wheel "${WHEELS_BUILD_ARGS[@]}" "${SKBUILD_ARGS[@]}" + mkdir -p "${WHEELS_OUTPUT_FOLDER}" cp dist/mkl_umath*.whl "${WHEELS_OUTPUT_FOLDER}" -else - # Perform regular install - ${PYTHON} setup.py install "${SKBUILD_ARGS[@]}" fi diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index 4b15337c..579a6888 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -19,12 +19,12 @@ requirements: - {{ stdlib('c') }} - {{ compiler('dpcpp') }} >=2024.2 # [not osx] host: - - setuptools >=77 - - cmake + - meson-python >=0.13.0 + - meson + - pkg-config - ninja - git - cython - - scikit-build - python - python-gil # [py>=314] - mkl-devel diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..63d41a9e --- /dev/null +++ b/meson.build @@ -0,0 +1,172 @@ +project( + 'mkl_umath', + ['c', 'cython'], + version: run_command( + 'python', '-c', + 'import os; exec(open("mkl_umath/_version.py").read()); print(__version__)', + check: true + ).stdout().strip(), + default_options: [ + 'c_std=c99', + 'buildtype=release', + 'b_ndebug=if-release' + ] +) + +py = import('python').find_installation(pure: false) + +# get includes +# numpy includes +np_dir = run_command(py, + ['-c', 'import numpy; print(numpy.get_include())'], + check: true +).stdout().strip() + +inc_np = include_directories(np_dir, 'mkl_umath/src') + +# Python headers needed for loops +py_dep = py.dependency() + +# compiler/linker +cc = meson.get_compiler('c') +c_args = [] +link_args = [] + +if get_option('opt_report') + if cc.get_id().startswith('intel') + c_args += 'qopt-report=3' + else + warning('Optimization report requested with non-Intel compiler') + endif +endif + +if cc.get_argument_syntax() == 'msvc' + c_args += [ + '/Ox', '/GS', '/DynamicBase', + '/fp:precise', 'Qimf-precision=high', '/Qprotect-parens' + ] + link_args = ['/NXCompat', '/DynamicBase'] +elif cc.get_argument_syntax() == 'gcc' + c_args += [ + '-O3', '-Wall', '-Wextra', '-Winit-self', '-Wunused-function', + '-Wuninitialized', '-Wmissing-declarations', '-Wstrict-prototypes', + '-Wno-unused-parameter', '-fdiagnostics-color=auto', + '-fstack-protector', '-fstack-protector-all', '-fpic', '-fPIC', + '-D_FORTIFY_SOURCE=2', '-Wformat', '-Wformat-security', + '-fno-delete-null-pointer-checks', '-fwrapv', + '-fprotect-parens', '-fimf-precision=high', '-fno-fast-math', + '-Wno-incompatible-function-pointer-types' + ] + link_args += ['-Wl,-z,noexecstack', '-Wl,-z,relro', '-Wl,-z,now'] +else + error('Unsupported system.') +endif + +# vectorization flags +c_args += ['-fveclib=SVML', '-fvectorize'] + +# dependencies +tbb_dep = dependency('tbb', required: true) + +# manually waterfall the meson deps: pkg-config to cmake to find_library +mkl_dep = dependency('mkl-dynamic-lp64-tbb', required: false) + +if not mkl_dep.found() + mkl_dep = dependency('MKL', method: 'cmake', + modules: ['MKL::MKL'], + cmake_args: [ + '-DMKL_ARCH=intel64', + '-DMKL_LINK=dynamic', + '-DMKL_THREADING=tbb_thread', + '-DMKL_INTERFACE=lp64' + ], + required: false + ) +endif + +if not mkl_dep.found() + # use static: false to emulate -DMKL_LINK=dynamic + # static: false docs are wrong, this will only consider shared libs + # see: https://github.com/mesonbuild/meson/issues/14163 + mkl_core = cc.find_library('mkl_core', required: true, static: false) + mkl_intel_lp64 = cc.find_library('mkl_intel_lp64', required: true, static: false) + mkl_tbb_thread = cc.find_library('mkl_tbb_thread', required: true, static: false) + mkl_dep = declare_dependency(dependencies: [mkl_core, mkl_intel_lp64, mkl_tbb_thread]) +endif + +# generate loops, similar to numpy +src_file_cli = find_program('_vendored/process_src_template.py') + +src_file = generator( + src_file_cli, + arguments: ['@INPUT@', '-o', '@OUTPUT@'], + output: '@BASENAME@' +) + +gen_loops_c = src_file.process('mkl_umath/src/mkl_umath_loops.c.src') +gen_loops_h = src_file.process('mkl_umath/src/mkl_umath_loops.h.src') + +gen_umath_c = custom_target( + '__umath_generated', + output: '__umath_generated.c', + input: 'mkl_umath/generate_umath.py', + command: [py, '@INPUT@', '-o', '@OUTPUT@'] +) + +mkl_umath_loops = shared_library( + 'mkl_umath_loops', + sources: [gen_loops_c, gen_loops_h], + include_directories: inc_np, + dependencies: [mkl_dep, tbb_dep, py_dep], + c_args: c_args, + link_args: link_args, + install: true, + install_dir: py.get_install_dir() / 'mkl_umath' +) + +loops_dep = declare_dependency(link_with: mkl_umath_loops) + +# extension modules + +ext_rpath = '' +if host_machine.system() != 'windows' + ext_rpath = '$ORIGIN/../..:$ORIGIN/../../..:$ORIGIN' +endif + +# gen_loops_h is generated by custom target so must be included in sources +py.extension_module( + '_ufuncs', + sources: ['mkl_umath/src/ufuncsmodule.c', gen_umath_c, gen_loops_h], + include_directories: inc_np, + dependencies: [mkl_dep, tbb_dep, loops_dep], + c_args: c_args + ['-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION'], + link_args: link_args, + install_rpath: ext_rpath, + install: true, + subdir: 'mkl_umath' +) + +py.extension_module( + '_patch_numpy', + sources: ['mkl_umath/src/_patch_numpy.pyx'], + include_directories: inc_np, + dependencies: [loops_dep], + c_args: c_args + ['-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION'], + cython_args: ['-3', '--fast-fail'], + link_args: link_args, + install_rpath: ext_rpath, + install: true, + subdir: 'mkl_umath' +) + +# install python sources + +py.install_sources( + [ + 'mkl_umath/__init__.py', + 'mkl_umath/_init_helper.py', + 'mkl_umath/_version.py', + 'mkl_umath/generate_umath.py' + ], + subdir: 'mkl_umath' +) diff --git a/meson.options b/meson.options new file mode 100644 index 00000000..4117cabc --- /dev/null +++ b/meson.options @@ -0,0 +1,2 @@ +option('opt_report', type: 'boolean', value: false, + description: 'Whether to generate optimization vectorization report') diff --git a/mkl_umath/generate_umath.py b/mkl_umath/generate_umath.py index 31be813e..c1325157 100644 --- a/mkl_umath/generate_umath.py +++ b/mkl_umath/generate_umath.py @@ -1152,7 +1152,7 @@ def make_ufuncs(funcdict): if uf.signature is None: sig = "NULL" else: - sig = '"{}"'.format(uf.signature) + sig = f'"{uf.signature}"' fmt = textwrap.dedent("""\ identity = {identity_expr}; if ({has_identity} && identity == NULL) {{ diff --git a/mkl_umath/ufunc_docstrings_numpy1.py b/mkl_umath/ufunc_docstrings_numpy1.py index 39efaee7..8e7aeb96 100644 --- a/mkl_umath/ufunc_docstrings_numpy1.py +++ b/mkl_umath/ufunc_docstrings_numpy1.py @@ -82,9 +82,9 @@ def add_newdoc(place, name, doc): ) if name[0] != "_" and name not in skip: if "\nx :" in doc: - assert "$OUT_SCALAR_1" in doc, "in {}".format(name) + assert "$OUT_SCALAR_1" in doc, f"in {name}" elif "\nx2 :" in doc or "\nx1, x2 :" in doc: - assert "$OUT_SCALAR_2" in doc, "in {}".format(name) + assert "$OUT_SCALAR_2" in doc, f"in {name}" else: raise AssertionError(f"Could not detect number of inputs in {name}") diff --git a/pyproject.toml b/pyproject.toml index af289e94..e88fa59a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,12 +24,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [build-system] -build-backend = "setuptools.build_meta" +build-backend = "mesonpy" requires = [ - "cmake", + "meson-python>=0.13.0", "ninja", - "scikit-build", - "setuptools>=77", "Cython", "numpy" ] @@ -89,10 +87,3 @@ line_length = 80 multi_line_output = 3 skip = ["_vendored/conv_template.py"] use_parentheses = true - -[tool.setuptools] -include-package-data = true -packages = ["mkl_umath"] - -[tool.setuptools.dynamic] -version = {attr = "mkl_umath._version.__version__"} diff --git a/setup.py b/setup.py deleted file mode 100644 index 2eef2e14..00000000 --- a/setup.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) 2019, Intel Corporation -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Intel Corporation nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import importlib.machinery -import sys -from os import makedirs -from os.path import dirname, exists, join - -import skbuild -from setuptools.modified import newer - -sys.path.insert(0, dirname(__file__)) # Ensures local imports work -from _vendored.conv_template import process_file as process_c_file # noqa: E402 - - -# TODO: rewrite generation in CMake, see NumPy meson implementation -# https://github.com/numpy/numpy/blob/c6fb3357541fd8cf6e4faeaeda3b1a9065da0520/numpy/_core/meson.build#L623 -def load_module(name, fn): - """Credit: numpy.compat.npy_load_module""" - return importlib.machinery.SourceFileLoader(name, fn).load_module(name) - - -def separator_join(sep, strs): - """ - Joins non-empty arguments strings with dot. - - Credit: numpy.distutils.misc_util.dot_join - """ - assert isinstance(strs, (list, tuple)) - assert isinstance(sep, str) - return sep.join([si for si in strs if si]) - - -pdir = join(dirname(__file__), "mkl_umath") -wdir = join(pdir, "src") - -generate_umath_py = join(pdir, "generate_umath.py") -n = separator_join("_", ("mkl_umath", "generate_umath")) -generate_umath = load_module(n, generate_umath_py) -del n - - -def generate_umath_c(build_dir): - target_dir = join(build_dir, "src") - target = join(target_dir, "__umath_generated.c") - if not exists(target_dir): - print( - "Folder {} was expected to exist, but creating".format(target_dir) - ) - makedirs(target_dir) - script = generate_umath_py - if newer(script, target): - with open(target, "w") as f: - f.write( - generate_umath.make_code( - generate_umath.defdict, generate_umath.__file__ - ) - ) - return [] - - -generate_umath_c(pdir) - -loops_header_templ = join(wdir, "mkl_umath_loops.h.src") -processed_loops_h_fn = join(wdir, "mkl_umath_loops.h") -loops_header_processed = process_c_file(loops_header_templ) - -with open(processed_loops_h_fn, "w") as fid: - fid.write(loops_header_processed) - -loops_src_templ = join(wdir, "mkl_umath_loops.c.src") -processed_loops_src_fn = join(wdir, "mkl_umath_loops.c") -loops_src_processed = process_c_file(loops_src_templ) - -with open(processed_loops_src_fn, "w") as fid: - fid.write(loops_src_processed) - - -skbuild.setup( - packages=[ - "mkl_umath", - ], - package_data={"mkl_umath": ["tests/*.*"]}, - include_package_data=True, -) From 8bd269978056aa1d0c22f77d2eaee5cb5e3e6255 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Tue, 3 Mar 2026 22:51:22 -0800 Subject: [PATCH 03/25] add tests to module --- meson.build | 5 +++++ mkl_umath/tests/__init__.py | 1 + 2 files changed, 6 insertions(+) create mode 100644 mkl_umath/tests/__init__.py diff --git a/meson.build b/meson.build index 63d41a9e..b6e6ccb3 100644 --- a/meson.build +++ b/meson.build @@ -170,3 +170,8 @@ py.install_sources( ], subdir: 'mkl_umath' ) + +install_subdir( + 'mkl_umath/tests', + install_dir: py.get_install_dir() / 'mkl_umath' +) diff --git a/mkl_umath/tests/__init__.py b/mkl_umath/tests/__init__.py new file mode 100644 index 00000000..fa81adaf --- /dev/null +++ b/mkl_umath/tests/__init__.py @@ -0,0 +1 @@ +# empty file From 70d3ce55bdc2b093e02e2dad6b7e0182c2b5b498 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Tue, 3 Mar 2026 23:08:35 -0800 Subject: [PATCH 04/25] exclude process_src_template from linting --- .flake8 | 4 +++- pyproject.toml | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 36cf71be..4c55551e 100644 --- a/.flake8 +++ b/.flake8 @@ -26,7 +26,9 @@ per-file-ignores = mkl_umath/ufunc_docstrings_numpy1.py: E501 mkl_umath/ufunc_docstrings_numpy2.py: E501 -exclude = _vendored/conv_template.py +exclude = + _vendored/conv_template.py + _vendored/process_src_template.py filename = *.py, *.pyx, *.pxi, *.pxd max_line_length = 80 diff --git a/pyproject.toml b/pyproject.toml index e88fa59a..0c080c56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,7 @@ Download = "http://github.com/IntelPython/mkl_umath" Homepage = "http://github.com/IntelPython/mkl_umath" [tool.black] -exclude = "_vendored/conv_template.py" +extend-exclude = "_vendored/" line-length = 80 [tool.cython-lint] @@ -85,5 +85,5 @@ force_grid_wrap = 0 include_trailing_comma = true line_length = 80 multi_line_output = 3 -skip = ["_vendored/conv_template.py"] +skip = ["_vendored/conv_template.py", "_vendored/process_src_template.py"] use_parentheses = true From 6a8786f88795c62f62d73c10450470ed8e1496e4 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Tue, 3 Mar 2026 23:24:35 -0800 Subject: [PATCH 05/25] add python-build to meta.yamls also fix license in pyproject.toml --- conda-recipe-cf/meta.yaml | 2 ++ conda-recipe/meta.yaml | 1 + pyproject.toml | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index 1d8d5177..fe8272f8 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -27,6 +27,8 @@ requirements: - git - cython - python + - python-gil # [py>=314] + - python-build - mkl-devel - tbb-devel - numpy diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index 579a6888..3f535615 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -27,6 +27,7 @@ requirements: - cython - python - python-gil # [py>=314] + - python-build - mkl-devel - tbb-devel - numpy-base diff --git a/pyproject.toml b/pyproject.toml index 0c080c56..82a1e8fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ dependencies = ["numpy >=1.26.4", "mkl-service"] description = "Intel (R) MKL-based universal functions for NumPy arrays" dynamic = ["version"] keywords = ["mkl_umath"] -license = "BSD-3-Clause" +license = {text = "BSD-3-Clause"} name = "mkl_umath" readme = {file = "README.md", content-type = "text/markdown"} requires-python = ">=3.10,<3.15" From c81ae5c455a52fb25ae5761cb175527aec66a03e Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 14:48:18 -0800 Subject: [PATCH 06/25] fix syntax in build shell scripts --- conda-recipe-cf/build.sh | 4 ++-- conda-recipe/build.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conda-recipe-cf/build.sh b/conda-recipe-cf/build.sh index 23ab8731..3b7f73d6 100644 --- a/conda-recipe-cf/build.sh +++ b/conda-recipe-cf/build.sh @@ -12,7 +12,7 @@ export ICXCFG read -r GLIBC_MAJOR GLIBC_MINOR <<< "$(conda list '^sysroot_linux-64$' \ | tail -n 1 | awk '{print $2}' | grep -oP '\d+' | head -n 2 | tr '\n' ' ')" -if [-d "build"]; then +if [ -d "build" ]; then rm -rf build fi @@ -30,7 +30,7 @@ ${PYTHON} -m pip install dist/mkl_umath*.whl \ --no-deps \ --only-binary :all: \ --no-index \ - --prefix "${PREFIX}" + --prefix "${PREFIX}" \ -vv if [ -n "${WHEELS_OUTPUT_FOLDER}" ]; then diff --git a/conda-recipe/build.sh b/conda-recipe/build.sh index 23ab8731..3b7f73d6 100644 --- a/conda-recipe/build.sh +++ b/conda-recipe/build.sh @@ -12,7 +12,7 @@ export ICXCFG read -r GLIBC_MAJOR GLIBC_MINOR <<< "$(conda list '^sysroot_linux-64$' \ | tail -n 1 | awk '{print $2}' | grep -oP '\d+' | head -n 2 | tr '\n' ' ')" -if [-d "build"]; then +if [ -d "build" ]; then rm -rf build fi @@ -30,7 +30,7 @@ ${PYTHON} -m pip install dist/mkl_umath*.whl \ --no-deps \ --only-binary :all: \ --no-index \ - --prefix "${PREFIX}" + --prefix "${PREFIX}" \ -vv if [ -n "${WHEELS_OUTPUT_FOLDER}" ]; then From 5dd80ee01bd8f6d614b64bfcfd44e53a7ab5f4bd Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 14:50:15 -0800 Subject: [PATCH 07/25] update build_pip workflow --- .github/workflows/build_pip.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/build_pip.yaml b/.github/workflows/build_pip.yaml index 511a54cf..db5192e0 100644 --- a/.github/workflows/build_pip.yaml +++ b/.github/workflows/build_pip.yaml @@ -48,10 +48,8 @@ jobs: - name: Build conda package run: | - pip install --no-cache-dir scikit-build cmake ninja cython + pip install --no-cache-dir meson-python ninja cython pip install --no-cache-dir numpy ${{ matrix.use_pre }} - echo "CONDA_PREFFIX is '${CONDA_PREFIX}'" - export MKLROOT=${CONDA_PREFIX} CC=icx pip install . --no-build-isolation --no-deps --verbose pip install --no-cache-dir pytest pip list From 2fe637318aefa113039cfa8362db4cc6e0f03c3c Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 17:24:11 -0800 Subject: [PATCH 08/25] update clang workflow add workflow using standard clang --- .github/workflows/build-with-clang.yml | 5 +- .../workflows/build-with-standard-clang.yml | 64 +++++++++++++++++++ meson.build | 15 +++-- mkl_umath/src/mkl_umath_loops.c.src | 2 +- 4 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/build-with-standard-clang.yml diff --git a/.github/workflows/build-with-clang.yml b/.github/workflows/build-with-clang.yml index 251747da..434a6dce 100644 --- a/.github/workflows/build-with-clang.yml +++ b/.github/workflows/build-with-clang.yml @@ -8,7 +8,7 @@ on: permissions: read-all jobs: - build-with-clang: + build-with-intel-clang: runs-on: ubuntu-latest strategy: @@ -56,7 +56,7 @@ jobs: - name: Install mkl_umath dependencies run: | - pip install scikit-build cmake ninja cython setuptools">=77" + pip install meson-python ninja cython mkl-service pip install ${{ matrix.numpy_version }} - name: List oneAPI folder content @@ -67,7 +67,6 @@ jobs: source ${{ env.ONEAPI_ROOT }}/setvars.sh echo "$CMPLR_ROOT" export CC="$CMPLR_ROOT/bin/icx" - export CFLAGS="${CFLAGS} -fno-fast-math -O2" pip install . --no-build-isolation --no-deps --verbose - name: Run mkl_umath tests diff --git a/.github/workflows/build-with-standard-clang.yml b/.github/workflows/build-with-standard-clang.yml new file mode 100644 index 00000000..40503067 --- /dev/null +++ b/.github/workflows/build-with-standard-clang.yml @@ -0,0 +1,64 @@ +name: Build project with standard clang compiler + +on: + pull_request: + push: + branches: [master] + +permissions: read-all + +jobs: + build-with-standard-clang: + runs-on: ubuntu-latest + + strategy: + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + numpy_version: ["numpy'>=2'"] + + env: + COMPILER_ROOT: /usr/bin + + defaults: + run: + shell: bash -el {0} + + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@3155a141048f8f89c06b4cdae32e7853e97536bc # 0.13.0 + with: + access_token: ${{ github.token }} + + - name: Install Dependencies + run: | + sudo apt-get update && sudo apt-get install -y clang + + - name: Setup Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: ${{ matrix.python }} + architecture: x64 + + - name: Checkout repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Install mkl_umath dependencies + run: | + pip install meson-python ninja cython mkl-service + pip install mkl-devel tbb-devel + pip install ${{ matrix.numpy_version }} + + - name: Build mkl_umath + run: | + export CC=${{ env.COMPILER_ROOT }}/clang + pip install . --no-build-isolation --no-deps --verbose + + - name: Run mkl_umath tests + run: | + pip install pytest + # mkl_umath cannot be installed in editable mode, we need + # to change directory before importing it and running tests + cd .. + python -m pytest -sv --pyargs mkl_umath/mkl_umath/tests diff --git a/meson.build b/meson.build index b6e6ccb3..d1d1c4b1 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'mkl_umath', - ['c', 'cython'], + ['c', 'cpp', 'cython'], version: run_command( 'python', '-c', 'import os; exec(open("mkl_umath/_version.py").read()); print(__version__)', @@ -42,10 +42,13 @@ endif if cc.get_argument_syntax() == 'msvc' c_args += [ - '/Ox', '/GS', '/DynamicBase', - '/fp:precise', 'Qimf-precision=high', '/Qprotect-parens' + '/Ox', '/GS', '/DynamicBase', '/fp:precise' ] link_args = ['/NXCompat', '/DynamicBase'] + + if cc.get_id().startswith('intel') + c_args += ['Qimf-precision=high', '/Qprotect-parens'] + endif elif cc.get_argument_syntax() == 'gcc' c_args += [ '-O3', '-Wall', '-Wextra', '-Winit-self', '-Wunused-function', @@ -53,11 +56,13 @@ elif cc.get_argument_syntax() == 'gcc' '-Wno-unused-parameter', '-fdiagnostics-color=auto', '-fstack-protector', '-fstack-protector-all', '-fpic', '-fPIC', '-D_FORTIFY_SOURCE=2', '-Wformat', '-Wformat-security', - '-fno-delete-null-pointer-checks', '-fwrapv', - '-fprotect-parens', '-fimf-precision=high', '-fno-fast-math', + '-fno-delete-null-pointer-checks', '-fwrapv', '-fno-fast-math', '-Wno-incompatible-function-pointer-types' ] link_args += ['-Wl,-z,noexecstack', '-Wl,-z,relro', '-Wl,-z,now'] + if cc.get_id().startswith('intel') + c_args += ['-fimf-precision=high', '-fprotect-parens'] + endif else error('Unsupported system.') endif diff --git a/mkl_umath/src/mkl_umath_loops.c.src b/mkl_umath/src/mkl_umath_loops.c.src index b588ca6f..5131d620 100644 --- a/mkl_umath/src/mkl_umath_loops.c.src +++ b/mkl_umath/src/mkl_umath_loops.c.src @@ -151,7 +151,7 @@ static inline npy_double spacing(npy_double x) { } static inline npy_float spacingf(npy_float x) { - if (isinff(x)) + if (isinf(x)) return ((npy_float) NAN); return copysignf(nextafterf(fabsf(x), INFINITY), x) - x; From 084953a049bca233e69b5a36489fe3fce9882163 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 19:28:41 -0800 Subject: [PATCH 09/25] fix import error in builds --- .github/workflows/build-with-clang.yml | 2 +- .github/workflows/build-with-standard-clang.yml | 2 +- .github/workflows/build_pip.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-with-clang.yml b/.github/workflows/build-with-clang.yml index 434a6dce..06f9af8e 100644 --- a/.github/workflows/build-with-clang.yml +++ b/.github/workflows/build-with-clang.yml @@ -76,4 +76,4 @@ jobs: # mkl_umath cannot be installed in editable mode, we need # to change directory before importing it and running tests cd .. - python -m pytest -sv --pyargs mkl_umath/mkl_umath/tests + python -m pytest -sv --pyargs mkl_umath diff --git a/.github/workflows/build-with-standard-clang.yml b/.github/workflows/build-with-standard-clang.yml index 40503067..72998c0e 100644 --- a/.github/workflows/build-with-standard-clang.yml +++ b/.github/workflows/build-with-standard-clang.yml @@ -61,4 +61,4 @@ jobs: # mkl_umath cannot be installed in editable mode, we need # to change directory before importing it and running tests cd .. - python -m pytest -sv --pyargs mkl_umath/mkl_umath/tests + python -m pytest -sv --pyargs mkl_umath diff --git a/.github/workflows/build_pip.yaml b/.github/workflows/build_pip.yaml index db5192e0..9e170066 100644 --- a/.github/workflows/build_pip.yaml +++ b/.github/workflows/build_pip.yaml @@ -56,4 +56,4 @@ jobs: # mkl_umath cannot be installed in editable mode, we need # to change directory before importing it and running tests cd .. - python -m pytest -v mkl_umath/mkl_umath/tests + python -m pytest -sv --pyargs mkl_umath From e7bba53c0a1a36cd04d82d03bf36d5d293b07d34 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 19:54:55 -0800 Subject: [PATCH 10/25] update Windows build scripts --- conda-recipe-cf/bld.bat | 30 +++++++++++++++--------------- conda-recipe/bld.bat | 27 +++++++++++++++++---------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/conda-recipe-cf/bld.bat b/conda-recipe-cf/bld.bat index e27318da..d2a5956e 100644 --- a/conda-recipe-cf/bld.bat +++ b/conda-recipe-cf/bld.bat @@ -2,24 +2,24 @@ REM A workaround for activate-dpcpp.bat issue to be addressed in 2021.4 set "LIB=%BUILD_PREFIX%\Library\lib;%BUILD_PREFIX%\compiler\lib;%LIB%" set "INCLUDE=%BUILD_PREFIX%\include;%INCLUDE%" -"%PYTHON%" setup.py clean --all -set "SKBUILD_ARGS=-G Ninja -- -DCMAKE_C_COMPILER:PATH=icx -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" +set "CC=icx" +set "CXX=icx" -FOR %%V IN (14.0.0 14 15.0.0 15 16.0.0 16 17.0.0 17) DO @( - REM set DIR_HINT if directory exists - IF EXIST "%BUILD_PREFIX%\Library\lib\clang\%%V\" ( - SET "SYCL_INCLUDE_DIR_HINT=%BUILD_PREFIX%\Library\lib\clang\%%V" - ) +%PYTHON% -m build -w -n -x +if %ERRORLEVEL% neq "0" exit 1 + +for /f %%f in ('dir /b /S .\dist') do ( + %PYTHON% -m pip install %%f ^ + --no-build-isolation ^ + --no-deps ^ + --only-binary :all: ^ + --no-index ^ + --prefix %PREFIX% ^ + -vv + if %ERRORLEVEL% neq 0 exit 1 ) if NOT "%WHEELS_OUTPUT_FOLDER%"=="" ( - rem Install and assemble wheel package from the build bits - "%PYTHON%" setup.py install bdist_wheel %SKBUILD_ARGS% - if errorlevel 1 exit 1 copy dist\mkl_umath*.whl %WHEELS_OUTPUT_FOLDER% - if errorlevel 1 exit 1 -) ELSE ( - rem Only install - "%PYTHON%" setup.py install %SKBUILD_ARGS% - if errorlevel 1 exit 1 + if %ERRORLEVEL% neq 0 exit 1 ) diff --git a/conda-recipe/bld.bat b/conda-recipe/bld.bat index 0616542f..d2a5956e 100644 --- a/conda-recipe/bld.bat +++ b/conda-recipe/bld.bat @@ -2,17 +2,24 @@ REM A workaround for activate-dpcpp.bat issue to be addressed in 2021.4 set "LIB=%BUILD_PREFIX%\Library\lib;%BUILD_PREFIX%\compiler\lib;%LIB%" set "INCLUDE=%BUILD_PREFIX%\include;%INCLUDE%" -"%PYTHON%" setup.py clean --all -set "SKBUILD_ARGS=-G Ninja -- -DCMAKE_C_COMPILER:PATH=icx -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" +set "CC=icx" +set "CXX=icx" + +%PYTHON% -m build -w -n -x +if %ERRORLEVEL% neq "0" exit 1 + +for /f %%f in ('dir /b /S .\dist') do ( + %PYTHON% -m pip install %%f ^ + --no-build-isolation ^ + --no-deps ^ + --only-binary :all: ^ + --no-index ^ + --prefix %PREFIX% ^ + -vv + if %ERRORLEVEL% neq 0 exit 1 +) if NOT "%WHEELS_OUTPUT_FOLDER%"=="" ( - rem Install and assemble wheel package from the build bits - "%PYTHON%" setup.py install bdist_wheel %SKBUILD_ARGS% - if errorlevel 1 exit 1 copy dist\mkl_umath*.whl %WHEELS_OUTPUT_FOLDER% - if errorlevel 1 exit 1 -) ELSE ( - rem Only install - "%PYTHON%" setup.py install %SKBUILD_ARGS% - if errorlevel 1 exit 1 + if %ERRORLEVEL% neq 0 exit 1 ) From a0cdf835b3a4f763137c619699d58f4ecc4e0c0e Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 20:22:10 -0800 Subject: [PATCH 11/25] fix linting --- .flake8 | 2 ++ pyproject.toml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.flake8 b/.flake8 index 4c55551e..84cfe007 100644 --- a/.flake8 +++ b/.flake8 @@ -12,6 +12,8 @@ extend-ignore = D102, # missing docstring in public function: D103, + # missing docstring in public package: + D104, # missing docstring in __init__: D107, # 1 blank line required between summary line and description: diff --git a/pyproject.toml b/pyproject.toml index 82a1e8fd..01c6ea23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,8 @@ Download = "http://github.com/IntelPython/mkl_umath" Homepage = "http://github.com/IntelPython/mkl_umath" [tool.black] -extend-exclude = "_vendored/" +extend-exclude = "(^|/)_vendored/" +force-exclude = "(^|/)_vendored/" line-length = 80 [tool.cython-lint] From adde61831cee974604890a8f29e0ea3ee0e2a808 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 20:22:14 -0800 Subject: [PATCH 12/25] add conda-package-cf workflow tests against conda-forge packages --- .github/workflows/conda-package-cf.yml | 337 +++++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 .github/workflows/conda-package-cf.yml diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml new file mode 100644 index 00000000..0a473122 --- /dev/null +++ b/.github/workflows/conda-package-cf.yml @@ -0,0 +1,337 @@ +name: Conda package with conda-forge channel only + +on: + push: + branches: + - master + pull_request: + +permissions: read-all + +env: + PACKAGE_NAME: mkl_umath + VER_SCRIPT1: "import json; f = open('ver.json', 'r'); j = json.load(f); f.close(); d = j['mkl_umath'][0];" + VER_SCRIPT2: "print('='.join((d[s] for s in ('version', 'build'))))" + +jobs: + build_linux: + runs-on: ubuntu-latest + strategy: + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@3155a141048f8f89c06b4cdae32e7853e97536bc # 0.13.0 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Set pkgs_dirs + run: | + echo "pkgs_dirs: [~/.conda/pkgs]" >> ~/.condarc + + - name: Cache conda packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + env: + CACHE_NUMBER: 0 # Increase to reset cache + with: + path: ~/.conda/pkgs + key: + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}-${{hashFiles('**/meta.yaml') }} + restore-keys: | + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- + + - name: Add conda to system path + run: echo $CONDA/bin >> $GITHUB_PATH + + - name: Install conda-build + run: conda install conda-build + + - name: Build conda package + run: | + CHANNELS="-c conda-forge --override-channels" + VERSIONS="--python ${{ matrix.python }}" + TEST="--no-test" + echo "CONDA_BLD=${CONDA}/conda-bld/linux-64" >> $GITHUB_ENV + + conda build \ + $TEST \ + $VERSIONS \ + $CHANNELS \ + conda-recipe-cf + + - name: Upload artifact + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + path: ${{ env.CONDA_BLD }}/${{ env.PACKAGE_NAME }}-*.conda + + test_linux: + needs: build_linux + runs-on: ${{ matrix.runner }} + + strategy: + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + experimental: [false] + runner: [ubuntu-latest] + continue-on-error: ${{ matrix.experimental }} + env: + CHANNELS: -c conda-forge --override-channels + + steps: + - name: Download artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + - name: Add conda to system path + run: echo $CONDA/bin >> $GITHUB_PATH + - name: Install conda-build + run: conda install conda-build + - name: Create conda channel + run: | + mkdir -p $GITHUB_WORKSPACE/channel/linux-64 + mv ${PACKAGE_NAME}-*.conda $GITHUB_WORKSPACE/channel/linux-64 + conda index $GITHUB_WORKSPACE/channel + # Test channel + conda search $PACKAGE_NAME -c $GITHUB_WORKSPACE/channel --override-channels + + - name: Collect dependencies + run: | + CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}" + conda create -n test_mkl_umath $PACKAGE_NAME python=${{ matrix.python }} $CHANNELS --only-deps --dry-run > lockfile + - name: Display lockfile + run: cat lockfile + + - name: Set pkgs_dirs + run: | + echo "pkgs_dirs: [~/.conda/pkgs]" >> ~/.condarc + + - name: Cache conda packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + env: + CACHE_NUMBER: 0 # Increase to reset cache + with: + path: ~/.conda/pkgs + key: + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}-${{hashFiles('lockfile') }} + restore-keys: | + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- + + - name: Install mkl_umath + run: | + CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}" + conda create -n test_mkl_umath python=${{ matrix.python }} $PACKAGE_NAME pytest $CHANNELS + # Test installed packages + conda list -n test_mkl_umath + + - name: Smoke test + run: | + source $CONDA/etc/profile.d/conda.sh + conda activate test_mkl_umath + python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" + + - name: Run tests + run: | + source $CONDA/etc/profile.d/conda.sh + conda activate test_mkl_umath + pytest -v --pyargs ${{ env.PACKAGE_NAME }} + + build_windows: + runs-on: windows-latest + + strategy: + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + env: + conda-bld: C:\Miniconda\conda-bld\win-64\ + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@3155a141048f8f89c06b4cdae32e7853e97536bc # 0.13.0 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 + with: + miniforge-variant: Miniforge3 + miniforge-version: latest + activate-environment: build + channels: conda-forge + conda-remove-defaults: "true" + python-version: ${{ matrix.python }} + + - name: Cache conda packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + env: + CACHE_NUMBER: 3 # Increase to reset cache + with: + path: /home/runner/conda_pkgs_dir + key: + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}-${{hashFiles('**/meta.yaml') }} + restore-keys: | + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- + + - name: Store conda paths as envs + shell: bash -l {0} + run: | + echo "CONDA_BLD=$CONDA/conda-bld/win-64/" | tr "\\\\" '/' >> $GITHUB_ENV + + - name: Install conda build + run: | + conda activate + conda install -y conda-build + conda list -n base + + - name: Build conda package + run: | + conda activate + conda build --no-test --python ${{ matrix.python }} -c conda-forge --override-channels conda-recipe-cf + + - name: Upload artifact + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + path: ${{ env.CONDA_BLD }}${{ env.PACKAGE_NAME }}-*.conda + + test_windows: + needs: build_windows + runs-on: ${{ matrix.runner }} + defaults: + run: + shell: cmd /C CALL {0} + strategy: + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + experimental: [false] + runner: [windows-latest] + continue-on-error: ${{ matrix.experimental }} + env: + workdir: '${{ github.workspace }}' + CHANNELS: -c conda-forge --override-channels + + steps: + - name: Download artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + + - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 + with: + miniforge-variant: Miniforge3 + miniforge-version: latest + activate-environment: mkl_umath_test + channels: conda-forge + conda-remove-defaults: "true" + python-version: ${{ matrix.python }} + + - name: Install conda-index + run: | + conda install -n base conda-index + + - name: Create conda channel with the artifact bit + shell: cmd /C CALL {0} + run: | + echo ${{ env.workdir }} + mkdir ${{ env.workdir }}\channel + mkdir ${{ env.workdir }}\channel\win-64 + move ${{ env.PACKAGE_NAME }}-*.conda ${{ env.workdir }}\channel\win-64 + dir ${{ env.workdir }}\channel\win-64 + + - name: Index the channel + shell: cmd /C CALL {0} + run: | + conda index ${{ env.workdir }}\channel + + - name: Dump mkl_umath version info from created channel to STDOUT + shell: cmd /C CALL {0} + run: | + conda search ${{ env.PACKAGE_NAME }} -c ${{ env.workdir }}/channel --override-channels --info --json + + - name: Dump mkl_umath version info from created channel into ver.json + shell: cmd /C CALL {0} + run: | + conda search ${{ env.PACKAGE_NAME }} -c ${{ env.workdir }}/channel --override-channels --info --json > ${{ env.workdir }}\ver.json + + - name: Output content of workdir + shell: pwsh + run: Get-ChildItem -Path ${{ env.workdir }} + + - name: Output content of produced ver.json + shell: pwsh + run: Get-Content -Path ${{ env.workdir }}\ver.json + + - name: Collect dependencies + shell: cmd /C CALL {0} + run: | + @ECHO ON + IF NOT EXIST ver.json ( + copy /Y ${{ env.workdir }}\ver.json . + ) + SET "SCRIPT=%VER_SCRIPT1% %VER_SCRIPT2%" + FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( + SET PACKAGE_VERSION=%%F + ) + conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} --only-deps --dry-run > lockfile + + - name: Display lockfile content + shell: pwsh + run: Get-Content -Path .\lockfile + + - name: Cache conda packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + env: + CACHE_NUMBER: 0 # Increase to reset cache + with: + path: /home/runner/conda_pkgs_dir + key: + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}-${{hashFiles('lockfile') }} + restore-keys: | + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- + ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- + + # add intel-openmp as an explicit dependency + # to avoid it being missed when package version is specified exactly + - name: Install mkl_umath + shell: cmd /C CALL {0} + run: | + @ECHO ON + IF NOT EXIST ver.json ( + copy /Y ${{ env.workdir }}\ver.json . + ) + set "SCRIPT=%VER_SCRIPT1% %VER_SCRIPT2%" + FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( + SET PACKAGE_VERSION=%%F + ) + SET "TEST_DEPENDENCIES=pytest pytest-cov" + SET "WORKAROUND_DEPENDENCIES=intel-openmp" + SET "DEPENDENCIES=%TEST_DEPENDENCIES% %WORKAROUND_DEPENDENCIES%" + conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} + + - name: Report content of test environment + shell: cmd /C CALL {0} + run: | + conda activate + echo "Value of CONDA environment variable was: " %CONDA% + echo "Value of CONDA_PREFIX environment variable was: " %CONDA_PREFIX% + conda info && conda list -n mkl_umath_test + + - name: Smoke test + shell: cmd /C CALL {0} + run: | + @ECHO ON + conda activate mkl_umath_test + python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" + + - name: Run tests + shell: cmd /C CALL {0} + run: | + conda activate mkl_umath_test + python -m pytest -v -s --pyargs ${{ env.PACKAGE_NAME }} From 32372a4e1d720f56932ff19e14abc8d93dd2a279 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 20:36:18 -0800 Subject: [PATCH 13/25] fix flags on windows builds --- meson.build | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index d1d1c4b1..b4a79aa5 100644 --- a/meson.build +++ b/meson.build @@ -47,7 +47,9 @@ if cc.get_argument_syntax() == 'msvc' link_args = ['/NXCompat', '/DynamicBase'] if cc.get_id().startswith('intel') - c_args += ['Qimf-precision=high', '/Qprotect-parens'] + c_args += [ + '/Qimf-precision=high', '/Qprotect-parens', '/clang:-fveclib=SVML' + ] endif elif cc.get_argument_syntax() == 'gcc' c_args += [ @@ -61,15 +63,15 @@ elif cc.get_argument_syntax() == 'gcc' ] link_args += ['-Wl,-z,noexecstack', '-Wl,-z,relro', '-Wl,-z,now'] if cc.get_id().startswith('intel') - c_args += ['-fimf-precision=high', '-fprotect-parens'] + c_args += [ + '-fimf-precision=high', '-fprotect-parens', '-fveclib=SVML', + '-fvectorize' + ] endif else error('Unsupported system.') endif -# vectorization flags -c_args += ['-fveclib=SVML', '-fvectorize'] - # dependencies tbb_dep = dependency('tbb', required: true) From 28758eefb52e7495c6b0c2fcb94341fe3a8c4fb4 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 20:53:11 -0800 Subject: [PATCH 14/25] add python-gil to run requirements when building on conda-forge --- conda-recipe-cf/meta.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index fe8272f8..b80b4003 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -34,6 +34,8 @@ requirements: - numpy run: - python + - python-gil # [py>=314] + - mkl-service - {{ pin_compatible('intel-cmplr-lib-rt') }} test: From 3540484b0f6045337df3e14a79462e20169542e9 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 22:30:23 -0800 Subject: [PATCH 15/25] fix conda-forge workflow build matrices --- .github/workflows/conda-package-cf.yml | 44 ++++++++++++++++++++------ conda-recipe-cf/meta.yaml | 2 ++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index 0a473122..ea27592d 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -18,7 +18,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + include: + - python: "3.10" + numpy: "2.2" + - python: "3.11" + numpy: "2.3" + - python: "3.12" + numpy: "2.3" + - python: "3.13" + numpy: "2.3" + - python: "3.14" + numpy: "2.3" + steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@3155a141048f8f89c06b4cdae32e7853e97536bc # 0.13.0 @@ -50,17 +61,17 @@ jobs: - name: Install conda-build run: conda install conda-build - - name: Build conda package + - name: Build conda package with NumPy 2.x run: | - CHANNELS="-c conda-forge --override-channels" - VERSIONS="--python ${{ matrix.python }}" - TEST="--no-test" + CHANNELS=(-c conda-forge --override-channels) + VERSIONS=(--python "${{ matrix.python }}" --numpy "${{ matrix.numpy }}") + TEST=(--no-test) echo "CONDA_BLD=${CONDA}/conda-bld/linux-64" >> $GITHUB_ENV conda build \ - $TEST \ - $VERSIONS \ - $CHANNELS \ + "${TEST[@]}" \ + "${VERSIONS[@]}" \ + "${CHANNELS[@]}" \ conda-recipe-cf - name: Upload artifact @@ -76,6 +87,7 @@ jobs: strategy: matrix: python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + numpy: ['numpy">2"'] experimental: [false] runner: [ubuntu-latest] continue-on-error: ${{ matrix.experimental }} @@ -146,7 +158,18 @@ jobs: strategy: matrix: - python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + include: + - python: "3.10" + numpy: "2.2" + - python: "3.11" + numpy: "2.3" + - python: "3.12" + numpy: "2.3" + - python: "3.13" + numpy: "2.3" + - python: "3.14" + numpy: "2.3" + env: conda-bld: C:\Miniconda\conda-bld\win-64\ steps: @@ -193,7 +216,7 @@ jobs: - name: Build conda package run: | conda activate - conda build --no-test --python ${{ matrix.python }} -c conda-forge --override-channels conda-recipe-cf + conda build --no-test --python ${{ matrix.python }} --numpy ${{ matrix.numpy }} -c conda-forge --override-channels conda-recipe-cf - name: Upload artifact uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 @@ -210,6 +233,7 @@ jobs: strategy: matrix: python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + numpy: ['numpy">2"'] experimental: [false] runner: [windows-latest] continue-on-error: ${{ matrix.experimental }} diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index b80b4003..98d49a7d 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -32,10 +32,12 @@ requirements: - mkl-devel - tbb-devel - numpy + - wheel >=0.41.3 run: - python - python-gil # [py>=314] - mkl-service + - {{ pin_compatible('numpy') }} - {{ pin_compatible('intel-cmplr-lib-rt') }} test: From ddc5574fcb875602c79481ec6eac35933224fffb Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 23:22:34 -0800 Subject: [PATCH 16/25] use stdlib('c') in conda-forge meta.yaml --- conda-recipe-cf/conda_build_config.yaml | 24 ++++++++++++++++++++++++ conda-recipe-cf/meta.yaml | 11 +++++------ 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 conda-recipe-cf/conda_build_config.yaml diff --git a/conda-recipe-cf/conda_build_config.yaml b/conda-recipe-cf/conda_build_config.yaml new file mode 100644 index 00000000..6f302ab0 --- /dev/null +++ b/conda-recipe-cf/conda_build_config.yaml @@ -0,0 +1,24 @@ +numpy: + - '1.26.4' +c_compiler: # [linux] + - gcc # [linux] +cxx_compiler: # [linux] + - gxx # [linux] +cxx_compiler_version: # [linux] + - '14' # [linux] +c_stdlib: # [linux] + - sysroot # [linux] +c_stdlib_version: # [linux] + - '2.28' # [linux] +c_stdlib: # [win] + - vs # [win] +cxx_compiler: # [win] + - vs2022 # [win] +c_compiler: # [win] + - vs2022 # [win] +CFLAGS: + - -fno-fast-math # [linux] +CXXFLAGS: + - -fno-fast-math # [linux] +CL: + - /fp:precise # [win] diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index 98d49a7d..e2a3241c 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -1,15 +1,14 @@ -{% set version = "0.4.0dev1" %} -{% set buildnumber = 0 %} - package: name: mkl_umath - version: {{ version }} + version: {{ GIT_DESCRIBE_TAG }} source: path: ../ build: - number: {{ buildnumber }} + number: {{ GIT_DESCRIBE_NUMBER }} + script_env: + - WHEELS_OUTPUT_FOLDER ignore_run_exports: - blas @@ -17,8 +16,8 @@ requirements: build: - {{ compiler('c') }} - {{ compiler('cxx') }} + - {{ stdlib('c') }} - {{ compiler('dpcpp') }} >=2024.2 # [not osx] - - sysroot_linux-64 >=2.28 # [linux] host: - meson-python >=0.13.0 - meson From abe631b1cc1e7b3e0a3d083ff2bab78b8704e5b2 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 4 Mar 2026 23:22:39 -0800 Subject: [PATCH 17/25] fix typo in bld.bat --- conda-recipe-cf/bld.bat | 2 +- conda-recipe/bld.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conda-recipe-cf/bld.bat b/conda-recipe-cf/bld.bat index d2a5956e..58178a84 100644 --- a/conda-recipe-cf/bld.bat +++ b/conda-recipe-cf/bld.bat @@ -6,7 +6,7 @@ set "CC=icx" set "CXX=icx" %PYTHON% -m build -w -n -x -if %ERRORLEVEL% neq "0" exit 1 +if %ERRORLEVEL% neq 0 exit 1 for /f %%f in ('dir /b /S .\dist') do ( %PYTHON% -m pip install %%f ^ diff --git a/conda-recipe/bld.bat b/conda-recipe/bld.bat index d2a5956e..58178a84 100644 --- a/conda-recipe/bld.bat +++ b/conda-recipe/bld.bat @@ -6,7 +6,7 @@ set "CC=icx" set "CXX=icx" %PYTHON% -m build -w -n -x -if %ERRORLEVEL% neq "0" exit 1 +if %ERRORLEVEL% neq 0 exit 1 for /f %%f in ('dir /b /S .\dist') do ( %PYTHON% -m pip install %%f ^ From cce6f53508a488426fc1b24bd17f56694ac808fe Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 5 Mar 2026 00:09:56 -0800 Subject: [PATCH 18/25] remove intel-openmp dependency on conda-forge --- .github/workflows/conda-package-cf.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index ea27592d..488a5426 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -321,8 +321,6 @@ jobs: ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-python-${{ matrix.python }}- ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- - # add intel-openmp as an explicit dependency - # to avoid it being missed when package version is specified exactly - name: Install mkl_umath shell: cmd /C CALL {0} run: | @@ -334,10 +332,7 @@ jobs: FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( SET PACKAGE_VERSION=%%F ) - SET "TEST_DEPENDENCIES=pytest pytest-cov" - SET "WORKAROUND_DEPENDENCIES=intel-openmp" - SET "DEPENDENCIES=%TEST_DEPENDENCIES% %WORKAROUND_DEPENDENCIES%" - conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} + conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% pytest python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} - name: Report content of test environment shell: cmd /C CALL {0} From 7076a2b70b92da5aab1f9999705760ddf9b89c1e Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 5 Mar 2026 00:10:05 -0800 Subject: [PATCH 19/25] add pip to meta.yamls --- conda-recipe-cf/meta.yaml | 1 + conda-recipe/meta.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index e2a3241c..5c1a5bd2 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -32,6 +32,7 @@ requirements: - tbb-devel - numpy - wheel >=0.41.3 + - pip run: - python - python-gil # [py>=314] diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index 3f535615..eb3f6422 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -32,6 +32,7 @@ requirements: - tbb-devel - numpy-base - wheel >=0.41.3 + - pip run: - python - python-gil # [py>=314] From 8c0e9683e4045e1e0e7c99f689432c3ec8865efa Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 5 Mar 2026 01:21:40 -0800 Subject: [PATCH 20/25] apply actions linting to conda-package-cf --- .github/workflows/conda-package-cf.yml | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index 488a5426..6bc88849 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -56,7 +56,7 @@ jobs: ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}- - name: Add conda to system path - run: echo $CONDA/bin >> $GITHUB_PATH + run: echo "$CONDA/bin" >> "$GITHUB_PATH" - name: Install conda-build run: conda install conda-build @@ -66,7 +66,7 @@ jobs: CHANNELS=(-c conda-forge --override-channels) VERSIONS=(--python "${{ matrix.python }}" --numpy "${{ matrix.numpy }}") TEST=(--no-test) - echo "CONDA_BLD=${CONDA}/conda-bld/linux-64" >> $GITHUB_ENV + echo "CONDA_BLD=${CONDA}/conda-bld/linux-64" >> "$GITHUB_ENV" conda build \ "${TEST[@]}" \ @@ -100,21 +100,21 @@ jobs: with: name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} - name: Add conda to system path - run: echo $CONDA/bin >> $GITHUB_PATH + run: echo "$CONDA/bin" >> "$GITHUB_PATH" - name: Install conda-build run: conda install conda-build - name: Create conda channel run: | - mkdir -p $GITHUB_WORKSPACE/channel/linux-64 - mv ${PACKAGE_NAME}-*.conda $GITHUB_WORKSPACE/channel/linux-64 - conda index $GITHUB_WORKSPACE/channel + mkdir -p "$GITHUB_WORKSPACE/channel/linux-64" + mv "${PACKAGE_NAME}"-*.conda "$GITHUB_WORKSPACE/channel/linux-64" + conda index "$GITHUB_WORKSPACE/channel" # Test channel - conda search $PACKAGE_NAME -c $GITHUB_WORKSPACE/channel --override-channels + conda search "$PACKAGE_NAME" -c "$GITHUB_WORKSPACE/channel" --override-channels - name: Collect dependencies run: | - CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}" - conda create -n test_mkl_umath $PACKAGE_NAME python=${{ matrix.python }} $CHANNELS --only-deps --dry-run > lockfile + CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "conda-forge" --override-channels) + conda create -n test_mkl_umath "$PACKAGE_NAME" "python=${{ matrix.python }}" "${CHANNELS[@]}" --only-deps --dry-run > lockfile - name: Display lockfile run: cat lockfile @@ -136,20 +136,20 @@ jobs: - name: Install mkl_umath run: | - CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}" - conda create -n test_mkl_umath python=${{ matrix.python }} $PACKAGE_NAME pytest $CHANNELS + CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "conda-forge" --override-channels) + conda create -n test_mkl_umath "python=${{ matrix.python }}" "$PACKAGE_NAME" pytest "${CHANNELS[@]}" # Test installed packages conda list -n test_mkl_umath - name: Smoke test run: | - source $CONDA/etc/profile.d/conda.sh + source "$CONDA/etc/profile.d/conda.sh" conda activate test_mkl_umath python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests run: | - source $CONDA/etc/profile.d/conda.sh + source "$CONDA/etc/profile.d/conda.sh" conda activate test_mkl_umath pytest -v --pyargs ${{ env.PACKAGE_NAME }} @@ -205,7 +205,7 @@ jobs: - name: Store conda paths as envs shell: bash -l {0} run: | - echo "CONDA_BLD=$CONDA/conda-bld/win-64/" | tr "\\\\" '/' >> $GITHUB_ENV + echo "CONDA_BLD=$CONDA/conda-bld/win-64/" | tr "\\\\" '/' >> "$GITHUB_ENV" - name: Install conda build run: | From 10a4cd07efe006b075fa89bd51b29f72022a11c5 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 5 Mar 2026 01:22:29 -0800 Subject: [PATCH 21/25] ignore git blame of previous commit fixing linting --- .git-blame-ignore-revs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index f58872f6..a5ec6fc8 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -5,3 +5,6 @@ # added more linters 3a3df3fae1574558973e3829368dc8a7b9c76ad9 + +# fix linting in conda-package-cf +f94f488ce98e06e5ffab1ce02ecd525b1680726c From 4a18e33f94d56844b650178a6ad1c619ca3751a6 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 5 Mar 2026 18:33:18 -0800 Subject: [PATCH 22/25] fix race condition in meson build --- meson.build | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index b4a79aa5..2279aa2e 100644 --- a/meson.build +++ b/meson.build @@ -111,7 +111,6 @@ src_file = generator( ) gen_loops_c = src_file.process('mkl_umath/src/mkl_umath_loops.c.src') -gen_loops_h = src_file.process('mkl_umath/src/mkl_umath_loops.h.src') gen_umath_c = custom_target( '__umath_generated', @@ -120,6 +119,14 @@ gen_umath_c = custom_target( command: [py, '@INPUT@', '-o', '@OUTPUT@'] ) +gen_loops_h = custom_target( + 'mkl_umath_loops_h', + output: ['mkl_umath_loops.h'], + input: 'mkl_umath/src/mkl_umath_loops.h.src', + command: [src_file_cli, '@INPUT@', '-o', '@OUTPUT@'], + depends: gen_umath_c +) + mkl_umath_loops = shared_library( 'mkl_umath_loops', sources: [gen_loops_c, gen_loops_h], @@ -143,7 +150,7 @@ endif # gen_loops_h is generated by custom target so must be included in sources py.extension_module( '_ufuncs', - sources: ['mkl_umath/src/ufuncsmodule.c', gen_umath_c, gen_loops_h], + sources: ['mkl_umath/src/ufuncsmodule.c', gen_loops_h], include_directories: inc_np, dependencies: [mkl_dep, tbb_dep, loops_dep], c_args: c_args + ['-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION'], From 89a3354a24758b037e78186c3b23f3da96fe5e50 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Mon, 9 Mar 2026 14:37:05 -0700 Subject: [PATCH 23/25] clean up windows build workflows --- .github/workflows/conda-package-cf.yml | 45 ++++++++++++-------------- .github/workflows/conda-package.yml | 38 +++++++++++----------- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index 6bc88849..cab2fbc5 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -10,6 +10,8 @@ permissions: read-all env: PACKAGE_NAME: mkl_umath + MODULE_NAME: mkl_umath + TEST_ENV_NAME: test_mkl_umath VER_SCRIPT1: "import json; f = open('ver.json', 'r'); j = json.load(f); f.close(); d = j['mkl_umath'][0];" VER_SCRIPT2: "print('='.join((d[s] for s in ('version', 'build'))))" @@ -114,7 +116,7 @@ jobs: - name: Collect dependencies run: | CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "conda-forge" --override-channels) - conda create -n test_mkl_umath "$PACKAGE_NAME" "python=${{ matrix.python }}" "${CHANNELS[@]}" --only-deps --dry-run > lockfile + conda create -n "${{ env.TEST_ENV_NAME }}" "$PACKAGE_NAME" "python=${{ matrix.python }}" "${CHANNELS[@]}" --only-deps --dry-run > lockfile - name: Display lockfile run: cat lockfile @@ -137,25 +139,27 @@ jobs: - name: Install mkl_umath run: | CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "conda-forge" --override-channels) - conda create -n test_mkl_umath "python=${{ matrix.python }}" "$PACKAGE_NAME" pytest "${CHANNELS[@]}" + conda create -n "${{ env.TEST_ENV_NAME }}" "python=${{ matrix.python }}" "$PACKAGE_NAME" pytest "${CHANNELS[@]}" # Test installed packages - conda list -n test_mkl_umath + conda list -n "${{ env.TEST_ENV_NAME }}" - name: Smoke test run: | source "$CONDA/etc/profile.d/conda.sh" - conda activate test_mkl_umath - python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" + conda activate "${{ env.TEST_ENV_NAME }}" + python -c "import mkl_umath, numpy as np; mkl_umath.patch_numpy_umath(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests run: | source "$CONDA/etc/profile.d/conda.sh" - conda activate test_mkl_umath - pytest -v --pyargs ${{ env.PACKAGE_NAME }} + conda activate "${{ env.TEST_ENV_NAME }}" + pytest -v --pyargs ${{ env.MODULE_NAME }} build_windows: runs-on: windows-latest - + defaults: + run: + shell: cmd /C CALL {0} strategy: matrix: include: @@ -183,11 +187,9 @@ jobs: - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 with: - miniforge-variant: Miniforge3 miniforge-version: latest activate-environment: build channels: conda-forge - conda-remove-defaults: "true" python-version: ${{ matrix.python }} - name: Cache conda packages @@ -209,13 +211,11 @@ jobs: - name: Install conda build run: | - conda activate - conda install -y conda-build + conda install -n base -y conda-build conda list -n base - name: Build conda package run: | - conda activate conda build --no-test --python ${{ matrix.python }} --numpy ${{ matrix.numpy }} -c conda-forge --override-channels conda-recipe-cf - name: Upload artifact @@ -249,11 +249,9 @@ jobs: - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 with: - miniforge-variant: Miniforge3 miniforge-version: latest - activate-environment: mkl_umath_test channels: conda-forge - conda-remove-defaults: "true" + activate-environment: ${{ env.TEST_ENV_NAME }} python-version: ${{ matrix.python }} - name: Install conda-index @@ -303,7 +301,7 @@ jobs: FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( SET PACKAGE_VERSION=%%F ) - conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} --only-deps --dry-run > lockfile + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} --only-deps --dry-run > lockfile - name: Display lockfile content shell: pwsh @@ -332,25 +330,24 @@ jobs: FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( SET PACKAGE_VERSION=%%F ) - conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% pytest python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% pytest python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} - name: Report content of test environment shell: cmd /C CALL {0} run: | - conda activate echo "Value of CONDA environment variable was: " %CONDA% echo "Value of CONDA_PREFIX environment variable was: " %CONDA_PREFIX% - conda info && conda list -n mkl_umath_test + conda info && conda list -n ${{ env.TEST_ENV_NAME }} - name: Smoke test shell: cmd /C CALL {0} run: | @ECHO ON - conda activate mkl_umath_test - python -c "import mkl_umath, numpy as np; mkl_umath.use_in_numpy(); np.sin(np.linspace(0, 1, num=10**6));" + conda activate ${{ env.TEST_ENV_NAME }} + python -c "import mkl_umath, numpy as np; mkl_umath.patch_numpy_umath(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests shell: cmd /C CALL {0} run: | - conda activate mkl_umath_test - python -m pytest -v -s --pyargs ${{ env.PACKAGE_NAME }} + conda activate ${{ env.TEST_ENV_NAME }} + python -m pytest -v -s --pyargs ${{ env.MODULE_NAME }} diff --git a/.github/workflows/conda-package.yml b/.github/workflows/conda-package.yml index 914ba5d6..45e1f5c6 100644 --- a/.github/workflows/conda-package.yml +++ b/.github/workflows/conda-package.yml @@ -10,6 +10,8 @@ permissions: read-all env: PACKAGE_NAME: mkl_umath + MODULE_NAME: mkl_umath + TEST_ENV_NAME: test_mkl_umath VER_SCRIPT1: "import json; f = open('ver.json', 'r'); j = json.load(f); f.close(); d = j['mkl_umath'][0];" VER_SCRIPT2: "print('='.join((d[s] for s in ('version', 'build'))))" @@ -102,7 +104,7 @@ jobs: - name: Collect dependencies run: | CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "https://software.repos.intel.com/python/conda" -c "conda-forge" --override-channels) - conda create -n test_mkl_umath "$PACKAGE_NAME" "python=${{ matrix.python }}" "${CHANNELS[@]}" --only-deps --dry-run > lockfile + conda create -n "${{ env.TEST_ENV_NAME }}" "$PACKAGE_NAME" "python=${{ matrix.python }}" "${CHANNELS[@]}" --only-deps --dry-run > lockfile - name: Display lockfile run: cat lockfile @@ -125,25 +127,27 @@ jobs: - name: Install mkl_umath run: | CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "https://software.repos.intel.com/python/conda" -c "conda-forge" --override-channels) - conda create -n test_mkl_umath "python=${{ matrix.python }}" "$PACKAGE_NAME" pytest "${CHANNELS[@]}" + conda create -n "${{ env.TEST_ENV_NAME }}" "python=${{ matrix.python }}" "$PACKAGE_NAME" pytest "${CHANNELS[@]}" # Test installed packages - conda list -n test_mkl_umath + conda list -n "${{ env.TEST_ENV_NAME }}" - name: Smoke test run: | source "$CONDA/etc/profile.d/conda.sh" - conda activate test_mkl_umath + conda activate "${{ env.TEST_ENV_NAME }}" python -c "import mkl_umath, numpy as np; mkl_umath.patch_numpy_umath(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests run: | source "$CONDA/etc/profile.d/conda.sh" - conda activate test_mkl_umath + conda activate "${{ env.TEST_ENV_NAME }}" pytest -v --pyargs ${{ env.PACKAGE_NAME }} build_windows: runs-on: windows-latest - + defaults: + run: + shell: cmd /C CALL {0} strategy: matrix: python: ["3.10", "3.11", "3.12", "3.13", "3.14"] @@ -160,11 +164,9 @@ jobs: - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 with: - miniforge-variant: Miniforge3 miniforge-version: latest activate-environment: build channels: conda-forge - conda-remove-defaults: "true" python-version: ${{ matrix.python }} - name: Cache conda packages @@ -186,13 +188,11 @@ jobs: - name: Install conda build run: | - conda activate - conda install -y conda-build + conda install -n base -y conda-build conda list -n base - name: Build conda package run: | - conda activate conda build --no-test --python ${{ matrix.python }} -c https://software.repos.intel.com/python/conda -c conda-forge --override-channels conda-recipe - name: Upload artifact @@ -225,11 +225,9 @@ jobs: - uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0 with: - miniforge-variant: Miniforge3 miniforge-version: latest - activate-environment: mkl_umath_test channels: conda-forge - conda-remove-defaults: "true" + activate-environment: ${{ env.TEST_ENV_NAME }} python-version: ${{ matrix.python }} - name: Install conda-index @@ -279,7 +277,7 @@ jobs: FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( SET PACKAGE_VERSION=%%F ) - conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} --only-deps --dry-run > lockfile + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} --only-deps --dry-run > lockfile - name: Display lockfile content shell: pwsh @@ -313,7 +311,7 @@ jobs: SET "TEST_DEPENDENCIES=pytest pytest-cov" SET "WORKAROUND_DEPENDENCIES=intel-openmp" SET "DEPENDENCIES=%TEST_DEPENDENCIES% %WORKAROUND_DEPENDENCIES%" - conda install -n mkl_umath_test ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} - name: Report content of test environment shell: cmd /C CALL {0} @@ -321,17 +319,17 @@ jobs: conda activate echo "Value of CONDA environment variable was: " %CONDA% echo "Value of CONDA_PREFIX environment variable was: " %CONDA_PREFIX% - conda info && conda list -n mkl_umath_test + conda info && conda list -n ${{ env.TEST_ENV_NAME }} - name: Smoke test shell: cmd /C CALL {0} run: | @ECHO ON - conda activate mkl_umath_test + conda activate ${{ env.TEST_ENV_NAME }} python -c "import mkl_umath, numpy as np; mkl_umath.patch_numpy_umath(); np.sin(np.linspace(0, 1, num=10**6));" - name: Run tests shell: cmd /C CALL {0} run: | - conda activate mkl_umath_test - python -m pytest -v -s --pyargs ${{ env.PACKAGE_NAME }} + conda activate ${{ env.TEST_ENV_NAME }} + python -m pytest -v -s --pyargs ${{ env.MODULE_NAME }} From 9bb40655e6bb100c309143577a63a3f9a4b0d41f Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Tue, 10 Mar 2026 17:31:12 -0700 Subject: [PATCH 24/25] use openmp threading instead of tbb --- .github/workflows/build-with-standard-clang.yml | 6 ++++-- .github/workflows/build_pip.yaml | 2 +- conda-recipe-cf/meta.yaml | 1 - conda-recipe/meta.yaml | 1 - meson.build | 14 +++++++------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-with-standard-clang.yml b/.github/workflows/build-with-standard-clang.yml index 72998c0e..c94310e0 100644 --- a/.github/workflows/build-with-standard-clang.yml +++ b/.github/workflows/build-with-standard-clang.yml @@ -31,7 +31,9 @@ jobs: - name: Install Dependencies run: | - sudo apt-get update && sudo apt-get install -y clang + sudo apt-get update + sudo apt-get install -y clang + sudo apt-get install -y libomp-dev - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -47,7 +49,7 @@ jobs: - name: Install mkl_umath dependencies run: | pip install meson-python ninja cython mkl-service - pip install mkl-devel tbb-devel + pip install mkl-devel pip install ${{ matrix.numpy_version }} - name: Build mkl_umath diff --git a/.github/workflows/build_pip.yaml b/.github/workflows/build_pip.yaml index 9e170066..ca9863fc 100644 --- a/.github/workflows/build_pip.yaml +++ b/.github/workflows/build_pip.yaml @@ -42,7 +42,7 @@ jobs: - name: Install Compiler and MKL run: | - conda install mkl-devel tbb-devel dpcpp_linux-64 + conda install mkl-devel dpcpp_linux-64 python -c "import sys; print(sys.executable)" which python diff --git a/conda-recipe-cf/meta.yaml b/conda-recipe-cf/meta.yaml index 5c1a5bd2..8833cde1 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -29,7 +29,6 @@ requirements: - python-gil # [py>=314] - python-build - mkl-devel - - tbb-devel - numpy - wheel >=0.41.3 - pip diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index eb3f6422..235121b3 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -29,7 +29,6 @@ requirements: - python-gil # [py>=314] - python-build - mkl-devel - - tbb-devel - numpy-base - wheel >=0.41.3 - pip diff --git a/meson.build b/meson.build index 2279aa2e..918506c2 100644 --- a/meson.build +++ b/meson.build @@ -73,10 +73,10 @@ else endif # dependencies -tbb_dep = dependency('tbb', required: true) +omp_dep = dependency('openmp', required: true) # manually waterfall the meson deps: pkg-config to cmake to find_library -mkl_dep = dependency('mkl-dynamic-lp64-tbb', required: false) +mkl_dep = dependency('mkl-dynamic-lp64-iomp', required: false) if not mkl_dep.found() mkl_dep = dependency('MKL', method: 'cmake', @@ -84,7 +84,7 @@ if not mkl_dep.found() cmake_args: [ '-DMKL_ARCH=intel64', '-DMKL_LINK=dynamic', - '-DMKL_THREADING=tbb_thread', + '-DMKL_THREADING=intel_thread', '-DMKL_INTERFACE=lp64' ], required: false @@ -97,8 +97,8 @@ if not mkl_dep.found() # see: https://github.com/mesonbuild/meson/issues/14163 mkl_core = cc.find_library('mkl_core', required: true, static: false) mkl_intel_lp64 = cc.find_library('mkl_intel_lp64', required: true, static: false) - mkl_tbb_thread = cc.find_library('mkl_tbb_thread', required: true, static: false) - mkl_dep = declare_dependency(dependencies: [mkl_core, mkl_intel_lp64, mkl_tbb_thread]) + mkl_intel_thread = cc.find_library('mkl_intel_thread', required: true, static: false) + mkl_dep = declare_dependency(dependencies: [mkl_core, mkl_intel_lp64, mkl_intel_thread]) endif # generate loops, similar to numpy @@ -131,7 +131,7 @@ mkl_umath_loops = shared_library( 'mkl_umath_loops', sources: [gen_loops_c, gen_loops_h], include_directories: inc_np, - dependencies: [mkl_dep, tbb_dep, py_dep], + dependencies: [mkl_dep, py_dep], c_args: c_args, link_args: link_args, install: true, @@ -152,7 +152,7 @@ py.extension_module( '_ufuncs', sources: ['mkl_umath/src/ufuncsmodule.c', gen_loops_h], include_directories: inc_np, - dependencies: [mkl_dep, tbb_dep, loops_dep], + dependencies: [mkl_dep, loops_dep], c_args: c_args + ['-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION'], link_args: link_args, install_rpath: ext_rpath, From e8e0af9f94e7dc216635156aee616bc70d2cb10b Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 11 Mar 2026 03:43:44 -0700 Subject: [PATCH 25/25] Fix build on Windows * drop dependency search for OpenMP (unnecessary) and drop libomp-dev from standard clang builds * use CMake to find MKL --- .../workflows/build-with-standard-clang.yml | 1 - meson.build | 38 +++++-------------- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build-with-standard-clang.yml b/.github/workflows/build-with-standard-clang.yml index c94310e0..c34f3e2e 100644 --- a/.github/workflows/build-with-standard-clang.yml +++ b/.github/workflows/build-with-standard-clang.yml @@ -33,7 +33,6 @@ jobs: run: | sudo apt-get update sudo apt-get install -y clang - sudo apt-get install -y libomp-dev - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/meson.build b/meson.build index 918506c2..2cd8f2cc 100644 --- a/meson.build +++ b/meson.build @@ -72,34 +72,16 @@ else error('Unsupported system.') endif -# dependencies -omp_dep = dependency('openmp', required: true) - -# manually waterfall the meson deps: pkg-config to cmake to find_library -mkl_dep = dependency('mkl-dynamic-lp64-iomp', required: false) - -if not mkl_dep.found() - mkl_dep = dependency('MKL', method: 'cmake', - modules: ['MKL::MKL'], - cmake_args: [ - '-DMKL_ARCH=intel64', - '-DMKL_LINK=dynamic', - '-DMKL_THREADING=intel_thread', - '-DMKL_INTERFACE=lp64' - ], - required: false - ) -endif - -if not mkl_dep.found() - # use static: false to emulate -DMKL_LINK=dynamic - # static: false docs are wrong, this will only consider shared libs - # see: https://github.com/mesonbuild/meson/issues/14163 - mkl_core = cc.find_library('mkl_core', required: true, static: false) - mkl_intel_lp64 = cc.find_library('mkl_intel_lp64', required: true, static: false) - mkl_intel_thread = cc.find_library('mkl_intel_thread', required: true, static: false) - mkl_dep = declare_dependency(dependencies: [mkl_core, mkl_intel_lp64, mkl_intel_thread]) -endif +mkl_dep = dependency('MKL', method: 'cmake', + modules: ['MKL::MKL'], + cmake_args: [ + '-DMKL_ARCH=intel64', + '-DMKL_LINK=dynamic', + '-DMKL_THREADING=intel_thread', + '-DMKL_INTERFACE=lp64' + ], + required: false +) # generate loops, similar to numpy src_file_cli = find_program('_vendored/process_src_template.py')