diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml new file mode 100644 index 00000000..f7706d0d --- /dev/null +++ b/.github/workflows/conda-package-cf.yml @@ -0,0 +1,393 @@ +name: Conda package with conda-forge channel only + +on: + push: + branches: + - master + pull_request: + +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'))))" + CONDA_BUILD_VERSION: 26.3.0 + +jobs: + build_linux: + runs-on: ubuntu-latest + strategy: + matrix: + 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@d07a454dad7609a92316b57b23c9ccfd4f59af66 # 0.13.1 + 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + 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: Update conda + run: | + conda update -n base --all + + - name: Install conda-build + run: conda install -n base conda-build=${{ env.CONDA_BUILD_VERSION }} -c conda-forge --override-channels + + - name: Show Conda info + run: | + conda info --all + + - name: List base environment packages + run: | + conda list -n base + + - name: Build conda package + run: | + 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[@]}" \ + conda-recipe-cf + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + 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"] + numpy: ['numpy">=2"'] + 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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + - name: Update conda + run: | + conda update -n base --all + - name: Install conda-index + run: | + conda install -n base conda-index -c conda-forge --override-channels + - name: Show Conda info + run: | + conda info --all + - name: List base environment packages + run: | + conda list -n base + - name: Add conda to system path + run: echo "$CONDA/bin" >> "$GITHUB_PATH" + - 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: Test conda channel + run: | + conda search "$PACKAGE_NAME" -c "$GITHUB_WORKSPACE"/channel --override-channels --info --json > "$GITHUB_WORKSPACE"/ver.json + cat "$GITHUB_WORKSPACE"/ver.json + - name: Collect dependencies + run: | + CHANNELS=(-c "$GITHUB_WORKSPACE/channel" -c "conda-forge" --override-channels) + PACKAGE_VERSION="$(python -c "${VER_SCRIPT1} ${VER_SCRIPT2}")" + conda create -n "${{ env.TEST_ENV_NAME }}" "${PACKAGE_NAME}=${PACKAGE_VERSION}" "python=${{ matrix.python }}" ${{ matrix.numpy }} "${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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + 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" -c "conda-forge" --override-channels) + PACKAGE_VERSION="$(python -c "${VER_SCRIPT1} ${VER_SCRIPT2}")" + conda create -n "${{ env.TEST_ENV_NAME }}" "python=${{ matrix.python }}" ${{ matrix.numpy }} "$PACKAGE_NAME=${PACKAGE_VERSION}" pytest "${CHANNELS[@]}" + # Test installed packages + conda list -n "${{ env.TEST_ENV_NAME }}" + + - name: Smoke test + run: | + source "$CONDA/etc/profile.d/conda.sh" + 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 "${{ env.TEST_ENV_NAME }}" + pytest -v --pyargs ${{ env.MODULE_NAME }} + + build_windows: + runs-on: windows-latest + + strategy: + matrix: + 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: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@d07a454dad7609a92316b57b23c9ccfd4f59af66 # 0.13.1 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + auto-update-conda: true + miniforge-version: latest + activate-environment: build + channels: conda-forge + python-version: ${{ matrix.python }} + conda-build-version: ${{ env.CONDA_BUILD_VERSION }} + + - name: Cache conda packages + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + 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 }} --numpy ${{ matrix.numpy }} -c conda-forge --override-channels conda-recipe-cf + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + 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"] + numpy: ['numpy">=2"'] + 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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }} + + - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1 + with: + auto-update-conda: true + miniforge-version: latest + activate-environment: ${{ env.TEST_ENV_NAME }} + channels: conda-forge + conda-remove-defaults: "true" + python-version: ${{ matrix.python }} + + - name: Install conda-index + run: | + conda install -n base conda-index + + - name: Show Conda info + run: | + conda info --all + + - name: List base environment packages + run: | + conda list -n base + + - 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 ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% python=${{ matrix.python }} ${{ matrix.numpy }} -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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + 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 }}- + + - 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" + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} ${{ matrix.numpy }} -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 ${{ env.TEST_ENV_NAME }} + + - name: Smoke test + shell: cmd /C CALL {0} + run: | + @ECHO ON + 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 ${{ env.TEST_ENV_NAME }} + python -m pytest -v -s --pyargs ${{ env.MODULE_NAME }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f08b5dd..4f36236b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,14 @@ option(OPTIMIZATION_REPORT OFF ) +set(MKL_THREADING_OPTIONS "intel_thread" "gnu_thread" "tbb_thread" "sequential") +set(MKL_THREADING "intel_thread" CACHE STRING "MKL threading layer") +set_property(CACHE MKL_THREADING PROPERTY STRINGS ${MKL_THREADING_OPTIONS}) + +if(NOT MKL_THREADING IN_LIST MKL_THREADING_OPTIONS) + message(FATAL_ERROR "Invalid MKL_THREADING value: ${MKL_THREADING}. Must be one of: ${MKL_THREADING_OPTIONS}") +endif() + find_package(Python COMPONENTS Interpreter Development NumPy REQUIRED) # Print out the discovered paths @@ -25,7 +33,6 @@ find_package(Cython REQUIRED) set(MKL_ARCH "intel64") set(MKL_LINK "dynamic") -set(MKL_THREADING "intel_thread") set(MKL_INTERFACE "lp64") find_package(MKL REQUIRED) diff --git a/conda-recipe-cf/build.sh b/conda-recipe-cf/build.sh index 00414a1c..c609fe3f 100644 --- a/conda-recipe-cf/build.sh +++ b/conda-recipe-cf/build.sh @@ -16,6 +16,7 @@ SKBUILD_ARGS=( "--" "-DCMAKE_C_COMPILER:PATH=icx" "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" + "-DMKL_THREADING=gnu_thread" ) if [ -n "${WHEELS_OUTPUT_FOLDER}" ]; then 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 810c1d15..59aa6d4b 100644 --- a/conda-recipe-cf/meta.yaml +++ b/conda-recipe-cf/meta.yaml @@ -17,18 +17,23 @@ requirements: build: - {{ compiler('c') }} - {{ compiler('cxx') }} + - {{ stdlib('c') }} - {{ compiler('dpcpp') }} >=2024.2 # [not osx] - - sysroot_linux-64 >=2.28 # [linux] host: - setuptools >=77 + - pip - cmake - ninja - git - cython - scikit-build - python + - python-gil # [py>=314] - mkl-devel - - numpy + - numpy # [not win] + - numpy <2.4 # [win] + # TODO: remove win numpy pin when numpy 2.5 is released, see https://github.com/numpy/numpy/issues/31337 + - llvm-openmp run: - python - python-gil # [py>=314]