From c6e73c75c33b7519060e83aa9adc2a339b80d819 Mon Sep 17 00:00:00 2001 From: atalman Date: Thu, 12 Dec 2024 09:17:36 -0800 Subject: [PATCH] Remove validation framework scripts and check_binary.sh --- .github/scripts/validate_binaries.sh | 112 --- .github/scripts/validate_pipy.sh | 20 - .github/scripts/validate_poetry.sh | 31 - .github/scripts/validate_test_ops.sh | 38 - .github/scripts/xpu_env_helper.bat | 17 - .../test-validate-domain-library.yml | 19 - .../validate-aarch64-linux-binaries.yml | 162 ---- .github/workflows/validate-binaries.yml | 189 ----- .github/workflows/validate-docker-images.yml | 81 -- .github/workflows/validate-domain-library.yml | 145 ---- .github/workflows/validate-linux-binaries.yml | 179 ----- .../validate-macos-arm64-binaries.yml | 140 ---- .../workflows/validate-nightly-binaries.yml | 27 - ...alidate-nightly-pypi-wheel-binary-size.yml | 26 - .../workflows/validate-release-binaries.yml | 33 - .../workflows/validate-windows-binaries.yml | 144 ---- check_binary.sh | 390 ---------- conda/.DS_Store | Bin 6148 -> 0 bytes conda/.gitignore | 1 - run_tests.sh | 731 ------------------ test/check_binary_symbols.py | 109 --- test/smoke_test/assets/dog2.jpg | Bin 90796 -> 0 bytes test/smoke_test/assets/rgb_pytorch.jpg | Bin 2126 -> 0 bytes test/smoke_test/assets/rgb_pytorch.png | Bin 575 -> 0 bytes test/smoke_test/smoke_test.py | 352 --------- test_example_code/CMakeLists.txt | 26 - test_example_code/check-torch-cuda.cpp | 15 - test_example_code/check-torch-mkl.cpp | 6 - test_example_code/check-torch-xnnpack.cpp | 7 - test_example_code/cnn_smoke.py | 36 - test_example_code/rnn_smoke.py | 13 - test_example_code/simple-torch-test.cpp | 6 - wheel/build_wheel.sh | 278 ------- 33 files changed, 3333 deletions(-) delete mode 100755 .github/scripts/validate_binaries.sh delete mode 100644 .github/scripts/validate_pipy.sh delete mode 100644 .github/scripts/validate_poetry.sh delete mode 100644 .github/scripts/validate_test_ops.sh delete mode 100644 .github/scripts/xpu_env_helper.bat delete mode 100644 .github/workflows/test-validate-domain-library.yml delete mode 100644 .github/workflows/validate-aarch64-linux-binaries.yml delete mode 100644 .github/workflows/validate-binaries.yml delete mode 100644 .github/workflows/validate-docker-images.yml delete mode 100644 .github/workflows/validate-domain-library.yml delete mode 100644 .github/workflows/validate-linux-binaries.yml delete mode 100644 .github/workflows/validate-macos-arm64-binaries.yml delete mode 100644 .github/workflows/validate-nightly-binaries.yml delete mode 100644 .github/workflows/validate-nightly-pypi-wheel-binary-size.yml delete mode 100644 .github/workflows/validate-release-binaries.yml delete mode 100644 .github/workflows/validate-windows-binaries.yml delete mode 100755 check_binary.sh delete mode 100644 conda/.DS_Store delete mode 100644 conda/.gitignore delete mode 100755 run_tests.sh delete mode 100755 test/check_binary_symbols.py delete mode 100644 test/smoke_test/assets/dog2.jpg delete mode 100644 test/smoke_test/assets/rgb_pytorch.jpg delete mode 100644 test/smoke_test/assets/rgb_pytorch.png delete mode 100644 test/smoke_test/smoke_test.py delete mode 100644 test_example_code/CMakeLists.txt delete mode 100644 test_example_code/check-torch-cuda.cpp delete mode 100644 test_example_code/check-torch-mkl.cpp delete mode 100644 test_example_code/check-torch-xnnpack.cpp delete mode 100644 test_example_code/cnn_smoke.py delete mode 100644 test_example_code/rnn_smoke.py delete mode 100644 test_example_code/simple-torch-test.cpp delete mode 100755 wheel/build_wheel.sh diff --git a/.github/scripts/validate_binaries.sh b/.github/scripts/validate_binaries.sh deleted file mode 100755 index 2e838613a..000000000 --- a/.github/scripts/validate_binaries.sh +++ /dev/null @@ -1,112 +0,0 @@ -if [[ ${MATRIX_PACKAGE_TYPE} == "libtorch" ]]; then - curl ${MATRIX_INSTALLATION} -o libtorch.zip - unzip libtorch.zip -else - - export PYTHON_RUN="python3" - if [[ ${TARGET_OS} == 'windows' ]]; then - export PYTHON_RUN="python" - # Currently xpu env need a helper script to activate - if [[ ${MATRIX_GPU_ARCH_TYPE} == "xpu" ]]; then - export PYTHON_RUN="${PWD}/.github/scripts/xpu_env_helper.bat python" - fi - fi - - if [[ ${TARGET_OS} == 'macos-arm64' ]]; then - conda update -y -n base -c defaults conda - elif [[ ${TARGET_OS} != 'linux-aarch64' ]]; then - # Conda pinned see issue: https://github.com/ContinuumIO/anaconda-issues/issues/13350 - conda install -y conda=23.11.0 - fi - # Please note ffmpeg is required for torchaudio, see https://github.com/pytorch/pytorch/issues/96159 - conda create -y -n ${ENV_NAME} python=${MATRIX_PYTHON_VERSION} numpy ffmpeg - conda activate ${ENV_NAME} - - # Remove when https://github.com/pytorch/builder/issues/1985 is fixed - if [[ ${MATRIX_GPU_ARCH_TYPE} == 'cuda-aarch64' ]]; then - pip3 install numpy --force-reinstall - fi - - INSTALLATION=${MATRIX_INSTALLATION/"conda install"/"conda install -y"} - TEST_SUFFIX="" - - # force-reinstall: latest version of packages are reinstalled - if [[ ${USE_FORCE_REINSTALL} == 'true' ]]; then - INSTALLATION=${INSTALLATION/"pip3 install"/"pip3 install --force-reinstall"} - fi - # extra-index-url: extra dependencies are downloaded from pypi - if [[ ${USE_EXTRA_INDEX_URL} == 'true' ]]; then - INSTALLATION=${INSTALLATION/"--index-url"/"--extra-index-url"} - fi - - # use-meta-cdn: use meta cdn for pypi download - if [[ ${USE_META_CDN} == 'true' ]]; then - INSTALLATION=${INSTALLATION/"download.pytorch.org"/"d3kup0pazkvub8.cloudfront.net"} - fi - - - if [[ ${TORCH_ONLY} == 'true' ]]; then - INSTALLATION=${INSTALLATION/"torchvision torchaudio"/""} - TEST_SUFFIX=" --package torchonly" - fi - - # if RELESE version is passed as parameter - install speific version - if [[ ! -z ${RELEASE_VERSION} ]]; then - INSTALLATION=${INSTALLATION/"torch "/"torch==${RELEASE_VERSION} "} - INSTALLATION=${INSTALLATION/"-y pytorch "/"-y pytorch==${RELEASE_VERSION} "} - INSTALLATION=${INSTALLATION/"::pytorch "/"::pytorch==${RELEASE_VERSION} "} - - if [[ ${USE_VERSION_SET} == 'true' ]]; then - INSTALLATION=${INSTALLATION/"torchvision "/"torchvision==${VISION_RELEASE_VERSION} "} - INSTALLATION=${INSTALLATION/"torchaudio "/"torchaudio==${AUDIO_RELEASE_VERSION} "} - fi - fi - - export OLD_PATH=${PATH} - # Workaround macos-arm64 runners. Issue: https://github.com/pytorch/test-infra/issues/4342 - if [[ ${TARGET_OS} == 'macos-arm64' ]]; then - export PATH="${CONDA_PREFIX}/bin:${PATH}" - fi - - # Make sure we remove previous installation if it exists - if [[ ${MATRIX_PACKAGE_TYPE} == 'wheel' ]]; then - pip3 uninstall -y torch torchaudio torchvision - fi - eval $INSTALLATION - - if [[ ${TARGET_OS} == 'linux' ]]; then - export CONDA_LIBRARY_PATH="$(dirname $(which python))/../lib" - export LD_LIBRARY_PATH=$CONDA_LIBRARY_PATH:$LD_LIBRARY_PATH - source ${PWD}/check_binary.sh - fi - - # We are only interested in CUDA tests and Python 3.8-3.11. Not all requirement libraries are available for 3.12 yet. - if [[ ${INCLUDE_TEST_OPS:-} == 'true' && ${MATRIX_GPU_ARCH_TYPE} == 'cuda' && ${MATRIX_PYTHON_VERSION} != "3.12" ]]; then - source ./.github/scripts/validate_test_ops.sh - fi - - # Regular smoke test - ${PYTHON_RUN} ./test/smoke_test/smoke_test.py ${TEST_SUFFIX} - # For pip install also test with numpy 2.0.0 for python 3.8 or above - if [[ ${MATRIX_PACKAGE_TYPE} == 'wheel' && ${MATRIX_PYTHON_VERSION} != "3.8" ]]; then - pip3 install numpy==2.0.0 --force-reinstall - ${PYTHON_RUN} ./test/smoke_test/smoke_test.py ${TEST_SUFFIX} - fi - - - if [[ ${TARGET_OS} == 'macos-arm64' ]]; then - export PATH=${OLD_PATH} - fi - - # Use case CUDA_VISIBLE_DEVICES: https://github.com/pytorch/pytorch/issues/128819 - if [[ ${MATRIX_GPU_ARCH_TYPE} == 'cuda' ]]; then - python -c "import torch;import os;print(torch.cuda.device_count(), torch.__version__);os.environ['CUDA_VISIBLE_DEVICES']='0';print(torch.empty(2, device='cuda'))" - fi - - # this is optional step - if [[ ${TARGET_OS} != linux* ]]; then - conda deactivate - conda env remove -n ${ENV_NAME} - fi - -fi diff --git a/.github/scripts/validate_pipy.sh b/.github/scripts/validate_pipy.sh deleted file mode 100644 index 5858e4c28..000000000 --- a/.github/scripts/validate_pipy.sh +++ /dev/null @@ -1,20 +0,0 @@ -conda create -yn ${ENV_NAME}_pypi python=${MATRIX_PYTHON_VERSION} numpy ffmpeg -conda activate ${ENV_NAME}_pypi - -TEST_SUFFIX="" -RELEASE_SUFFIX="" -# if RELESE version is passed as parameter - install speific version -if [[ ! -z ${RELEASE_VERSION} ]]; then - RELEASE_SUFFIX="==${RELEASE_VERSION}" -fi - -if [[ ${TORCH_ONLY} == 'true' ]]; then - TEST_SUFFIX=" --package torchonly" - pip3 install torch${RELEASE_SUFFIX} -else - pip3 install torch${RELEASE_SUFFIX} torchvision torchaudio -fi - -python ./test/smoke_test/smoke_test.py ${TEST_SUFFIX} --runtime-error-check disabled -conda deactivate -conda env remove -p ${ENV_NAME}_pypi diff --git a/.github/scripts/validate_poetry.sh b/.github/scripts/validate_poetry.sh deleted file mode 100644 index 6b7fe2412..000000000 --- a/.github/scripts/validate_poetry.sh +++ /dev/null @@ -1,31 +0,0 @@ - -conda create -y -n ${ENV_NAME}_poetry python=${MATRIX_PYTHON_VERSION} numpy ffmpeg -conda activate ${ENV_NAME}_poetry -curl -sSL https://install.python-poetry.org | python3 - --git https://github.com/python-poetry/poetry.git@master -export PATH="/root/.local/bin:$PATH" - -poetry --version -poetry new test_poetry -cd test_poetry - -TEST_SUFFIX="" -if [[ ${TORCH_ONLY} == 'true' ]]; then - TEST_SUFFIX=" --package torchonly" -fi - -RELEASE_SUFFIX="" -# if RELESE version is passed as parameter - install speific version -if [[ ! -z ${RELEASE_VERSION} ]]; then - RELEASE_SUFFIX="@${RELEASE_VERSION}" -fi - -if [[ ${TORCH_ONLY} == 'true' ]]; then - poetry --quiet add torch${RELEASE_SUFFIX} -else - poetry --quiet add torch${RELEASE_SUFFIX} torchaudio torchvision -fi - -python ../test/smoke_test/smoke_test.py ${TEST_SUFFIX} --runtime-error-check disabled -conda deactivate -conda env remove -p ${ENV_NAME}_poetry -cd .. diff --git a/.github/scripts/validate_test_ops.sh b/.github/scripts/validate_test_ops.sh deleted file mode 100644 index 5df646705..000000000 --- a/.github/scripts/validate_test_ops.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -set -eux -o pipefail - -retry () { - $* || (sleep 1 && $*) || (sleep 2 && $*) || (sleep 4 && $*) || (sleep 8 && $*) -} - -BRANCH="" -if [[ ${MATRIX_CHANNEL} == "test" || ${MATRIX_CHANNEL} == "release" ]]; then - SHORT_VERSION=${MATRIX_STABLE_VERSION%.*} - BRANCH="--branch release/${SHORT_VERSION}" -fi - - -# Clone the Pytorch branch -retry git clone ${BRANCH} --depth 1 https://github.com/pytorch/pytorch.git -retry git submodule update --init --recursive -pushd pytorch - -pip install expecttest numpy pyyaml jinja2 packaging hypothesis unittest-xml-reporting scipy - -# Run pytorch cuda wheels validation -# Detect ReduceLogicKernel (ReduceOp and kernel) IMA -python test/test_ops.py -k test_dtypes_all_cuda -# Detect BinaryMulKernel (elementwise binary functor internal mul) IMA -python test/test_torch.py -k test_index_reduce_reduce_prod_cuda_int32 -# Detect BinaryBitwiseOpsKernels (at::native::BitwiseAndFunctor) IMA -python test/test_binary_ufuncs.py -k test_contig_vs_every_other___rand___cuda_int32 -# Detect MaxMinElementwiseKernel (maximum) IMA -python test/test_schema_check.py -k test_schema_correctness_clamp_cuda_int8 - -pushd /tmp -# Detect StepKernel (nextafter) IMA -python -c "import torch; print(torch.nextafter(torch.tensor([-4.5149, -5.9053, -0.9516, -2.3615, 1.5591], device='cuda:0'), torch.tensor(3.8075, device='cuda:0')))" -# Detect BinaryGeometricKernels (atan2) IMA -python -c "import torch; x = (torch.randn((2,1,1), dtype=torch.float, device='cuda')*5).to(torch.float32); y=(torch.randn((), dtype=torch.float, device='cuda')*5).to(torch.float32); print(torch.atan2(x,y))" -popd diff --git a/.github/scripts/xpu_env_helper.bat b/.github/scripts/xpu_env_helper.bat deleted file mode 100644 index 7f331ebc2..000000000 --- a/.github/scripts/xpu_env_helper.bat +++ /dev/null @@ -1,17 +0,0 @@ -call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" - -set args=%1 -shift -:start -if [%1] == [] goto done -set args=%args% %1 -shift -goto start - -:done -if "%args%" == "" ( - echo Usage: xpu_env_helper.bat [command] [args] - echo e.g. xpu_env_helper.bat icpx --version -) - -%args% || exit /b 1 diff --git a/.github/workflows/test-validate-domain-library.yml b/.github/workflows/test-validate-domain-library.yml deleted file mode 100644 index 6c651e709..000000000 --- a/.github/workflows/test-validate-domain-library.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Test validate domain library - -on: - pull_request: - paths: - - .github/workflows/validate-domain-library.yml - - .github/workflows/test-validate-domain-library.yml - workflow_dispatch: - -jobs: - test-validate-domain-library: - uses: ./.github/workflows/validate-domain-library.yml - with: - package_type: "conda,wheel" - os: "all" - channel: "release" - repository: "pytorch/builder" - ref: main - smoke_test: "echo test" diff --git a/.github/workflows/validate-aarch64-linux-binaries.yml b/.github/workflows/validate-aarch64-linux-binaries.yml deleted file mode 100644 index bae4f9221..000000000 --- a/.github/workflows/validate-aarch64-linux-binaries.yml +++ /dev/null @@ -1,162 +0,0 @@ -name: Validate Aarch64 linux binaries - -on: - workflow_call: - inputs: - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: string - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate - optional' - default: "" - required: false - type: string - use-version-set: - description: 'Applies when version is used, use version for each domain' - default: false - required: false - type: boolean - release-matrix: - description: 'Release matrix - optional' - default: "" - required: false - type: string - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - workflow_dispatch: - inputs: - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: choice - options: - - release - - nightly - - test - - all - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate - optional' - default: "" - required: false - type: string - release-matrix: - description: 'Release matrix - optional' - default: "" - required: false - type: string - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - -jobs: - generate-aarch64-linux-matrix: - uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main - with: - package-type: wheel - os: linux-aarch64 - channel: ${{ inputs.channel }} - with-cuda: disable - use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }} - - linux-aarch64: - needs: generate-aarch64-linux-matrix - strategy: - matrix: ${{ fromJson(needs.generate-aarch64-linux-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/linux_job.yml@main - name: ${{ matrix.build_name }} - with: - runner: ${{ matrix.validation_runner }} - repository: "pytorch/builder" - ref: ${{ github.ref }} - job-name: ${{ matrix.build_name }} - docker-image: ${{ matrix.container_image }} - binary-matrix: ${{ toJSON(matrix) }} - no-sudo: true - script: | - set -ex - export DESIRED_PYTHON=${{ matrix.python_version }} - echo "/opt/conda/bin" >> $GITHUB_PATH - ############################################################################### - # Install conda - # disable SSL_verify due to getting "Could not find a suitable TLS CA certificate bundle, invalid path" - # when using Python version, less than the conda latest - ############################################################################### - echo 'Installing conda-forge' - curl -L -o /mambaforge.sh https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-aarch64.sh - chmod +x /mambaforge.sh - /mambaforge.sh -b -p /opt/conda - rm /mambaforge.sh - source /opt/conda/etc/profile.d/conda.sh - conda config --set ssl_verify False - - export ENV_NAME="conda-env-${{ github.run_id }}" - export TARGET_OS="linux-aarch64" - export TORCH_ONLY=${{ inputs.torchonly }} - export RELEASE_VERSION=${{ inputs.version }} - export USE_FORCE_REINSTALL=${{ inputs.use-force-reinstall }} - export USE_EXTRA_INDEX_URL=${{ inputs.use-extra-index-url }} - export USE_META_CDN=${{ inputs.use-meta-cdn }} - export USE_VERSION_SET=${{ inputs.use-version-set }} - if [[ ${USE_VERSION_SET} == 'true' ]]; then - export VISION_RELEASE_VERSION=${{ fromJson(inputs.release-matrix).torchvision }} - export AUDIO_RELEASE_VERSION=${{ fromJson(inputs.release-matrix).torchaudio }} - fi - - printf '%s\n' ${{ toJson(inputs.release-matrix) }} > release_matrix.json - eval "$(conda shell.bash hook)" - - # NB: The latest conda 23.11.0 pulls in some dependencies of conda-libmamba-solver that - # require GLIBC_2.25, which is not available in the current aarch64 image causing the - # subsequence git command to fail. Basically, they don't work with CentOS 7 which AML 2 - # is based on https://github.com/ContinuumIO/anaconda-issues/issues/12822 - unset LD_LIBRARY_PATH - - # Standard case: Validate binaries - source ./.github/scripts/validate_binaries.sh diff --git a/.github/workflows/validate-binaries.yml b/.github/workflows/validate-binaries.yml deleted file mode 100644 index b13b9168f..000000000 --- a/.github/workflows/validate-binaries.yml +++ /dev/null @@ -1,189 +0,0 @@ -name: Validate binaries - -# A reusable workflow that triggers a set of jobs that perform a smoke test / validation of pytorch binaries. -# Optionally restricts validation to the specified OS and channel. -# For the details about parameter values, see: -# pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main -# For an example of the `workflow_call` usage see: -# https://github.com/pytorch/builder/pull/1144 -on: - workflow_call: - inputs: - os: - description: "Operating system to generate for (linux, windows, macos, macos-arm64)" - required: true - type: string - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: string - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - include-test-ops: - description: 'Include Test Ops tests (only Linux)' - default: false - required: false - type: boolean - use-version-set: - description: 'Use version for each domain' - default: false - required: false - type: boolean - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use_split_build: - description: | - [Experimental] Use Split Build - required: false - type: boolean - default: false - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - workflow_dispatch: - inputs: - os: - description: "Operating system to generate for (linux, windows, macos, macos-arm64)" - required: true - type: choice - default: all - options: - - windows - - linux - - linux-aarch64 - - macos - - all - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: choice - default: test - options: - - release - - nightly - - test - - all - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate' - default: "" - required: false - type: string - include-test-ops: - description: 'Include Test Ops tests (only Linux)' - default: false - required: false - type: boolean - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use_split_build: - description: | - [Experimental] Use Split Build - required: false - type: boolean - default: false - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - - -jobs: - generate-release-matrix: - uses: pytorch/test-infra/.github/workflows/generate_release_matrix.yml@main - with: - version: ${{ inputs.version }} - - win: - if: inputs.os == 'windows' || inputs.os == 'all' - needs: generate-release-matrix - uses: ./.github/workflows/validate-windows-binaries.yml - with: - channel: ${{ inputs.channel }} - torchonly: ${{ inputs.torchonly }} - version: ${{ inputs.version }} - release-matrix: ${{ needs.generate-release-matrix.outputs.matrix }} - use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }} - use-force-reinstall: ${{ inputs.use-force-reinstall }} - use-extra-index-url: ${{ inputs.use-extra-index-url }} - use-meta-cdn: ${{ inputs.use-meta-cdn }} - - linux: - if: inputs.os == 'linux' || inputs.os == 'all' - needs: generate-release-matrix - uses: ./.github/workflows/validate-linux-binaries.yml - with: - channel: ${{ inputs.channel }} - torchonly: ${{ inputs.torchonly }} - version: ${{ inputs.version }} - release-matrix: ${{ needs.generate-release-matrix.outputs.matrix }} - include-test-ops: ${{ inputs.include-test-ops }} - use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }} - use-force-reinstall: ${{ inputs.use-force-reinstall }} - use_split_build: ${{ inputs.use_split_build }} - use-extra-index-url: ${{ inputs.use-extra-index-url }} - use-meta-cdn: ${{ inputs.use-meta-cdn }} - - linux-aarch64: - if: inputs.os == 'linux-aarch64' || inputs.os == 'all' - needs: generate-release-matrix - uses: ./.github/workflows/validate-aarch64-linux-binaries.yml - with: - channel: ${{ inputs.channel }} - torchonly: ${{ inputs.torchonly }} - version: ${{ inputs.version }} - release-matrix: ${{ needs.generate-release-matrix.outputs.matrix }} - use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }} - use-force-reinstall: ${{ inputs.use-force-reinstall }} - use-extra-index-url: ${{ inputs.use-extra-index-url }} - use-meta-cdn: ${{ inputs.use-meta-cdn }} - - mac-arm64: - if: inputs.os == 'macos' || inputs.os == 'all' - needs: generate-release-matrix - uses: ./.github/workflows/validate-macos-arm64-binaries.yml - with: - channel: ${{ inputs.channel }} - torchonly: ${{ inputs.torchonly }} - version: ${{ inputs.version }} - release-matrix: ${{ needs.generate-release-matrix.outputs.matrix }} - use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }} - use-force-reinstall: ${{ inputs.use-force-reinstall }} - use-extra-index-url: ${{ inputs.use-extra-index-url }} - use-meta-cdn: ${{ inputs.use-meta-cdn }} diff --git a/.github/workflows/validate-docker-images.yml b/.github/workflows/validate-docker-images.yml deleted file mode 100644 index 46ad0aa2a..000000000 --- a/.github/workflows/validate-docker-images.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Validate Nightly Docker Images -on: - pull_request: - paths: - - .github/workflows/validate-docker-images.yml - workflow_call: - inputs: - channel: - description: 'PyTorch channel to use (nightly, test, release, all)' - required: true - type: string - default: 'nightly' - generate_dockerhub_images: - description: 'Generate Docker Hub images (strip ghcr.io/ prefix for release)' - default: false - required: false - type: boolean - ref: - description: 'Reference to checkout, defaults to empty' - default: "" - required: false - type: string - workflow_dispatch: - inputs: - channel: - description: 'PyTorch channel to use (nightly, test, release, all)' - required: true - type: choice - default: 'nightly' - options: - - 'nightly' - - 'test' - - 'release' - generate_dockerhub_images: - description: 'Generate Docker Hub images (strip ghcr.io/ prefix for release)' - default: false - required: false - type: boolean - ref: - description: 'Reference to checkout, defaults to empty' - default: "" - required: false - type: string - -jobs: - generate-matrix: - uses: pytorch/test-infra/.github/workflows/generate_docker_release_matrix.yml@main - with: - channel: ${{ inputs.channel }} - generate_dockerhub_images : ${{ inputs.generate_dockerhub_images }} - secrets: inherit - run-gpu-tests: - needs: generate-matrix - name: cuda${{ matrix.cuda }}-cudnn${{ matrix.cudnn_version }}-${{ matrix.image_type }} - strategy: - matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/linux_job.yml@main - with: - runner: ${{ matrix.validation_runner }} - repository: "pytorch/builder" - ref: ${{ inputs.ref || github.ref }} - job-name: cuda${{ matrix.cuda }}-cudnn${{ matrix.cudnn_version }}-${{ matrix.image_type }} - binary-matrix: ${{ toJSON(matrix) }} - docker-image: ${{matrix.docker}} - timeout: 180 - script: | - set -ex - - export MATRIX_GPU_ARCH_VERSION="${{ matrix.cuda }}" - export MATRIX_IMAGE_TYPE="${{ matrix.image_type }}" - export TARGET_OS="linux" - TORCH_COMPILE_CHECK="--torch-compile-check enabled" - if [[ ${MATRIX_IMAGE_TYPE} == "runtime" ]]; then - TORCH_COMPILE_CHECK="--torch-compile-check disabled" - fi - export MATRIX_GPU_ARCH_TYPE="cuda" - if [[ ${MATRIX_GPU_ARCH_VERSION} == "cpu" ]]; then - export MATRIX_GPU_ARCH_TYPE="cpu" - fi - python test/smoke_test/smoke_test.py --package torchonly --runtime-error-check disabled ${TORCH_COMPILE_CHECK} diff --git a/.github/workflows/validate-domain-library.yml b/.github/workflows/validate-domain-library.yml deleted file mode 100644 index 6d117109a..000000000 --- a/.github/workflows/validate-domain-library.yml +++ /dev/null @@ -1,145 +0,0 @@ -name: Validate domain libary - -# A reusable workflow that triggers a set of jobs that perform a smoke test / validation of pytorch binaries. -# Optionally restricts validation to the specified OS and channel. -# For the details about parameter values, see: -# pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main -on: - workflow_call: - inputs: - os: - description: "Operating system to generate for (linux, windows, macos, macos-arm64)" - required: false - type: string - default: "all" - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: string - ref: - description: 'Reference to checkout, defaults to empty' - default: "" - required: false - type: string - package_type: - description: "Package type (conda, wheel, all)" - required: false - type: string - default: "all" - repository: - description: "Path to repository to checkout" - required: true - type: string - smoke_test: - description: "Path to a smoke test script" - required: true - type: string - with_cuda: - description: "With cuda enable/disable" - required: false - type: string - default: disable - -jobs: - generate-linux-matrix: - if: (inputs.os == 'linux' || inputs.os == 'all') - uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main - with: - package-type: ${{ inputs.package_type }} - os: linux - channel: ${{ inputs.channel }} - with-cuda: ${{ inputs.with_cuda }} - generate-windows-matrix: - if: (inputs.os == 'windows' || inputs.os == 'all') - uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main - with: - package-type: ${{ inputs.package_type }} - os: windows - channel: ${{ inputs.channel }} - with-cuda: ${{ inputs.with_cuda }} - generate-macos-arm64-matrix: - if: (inputs.os == 'macos-arm64' || inputs.os == 'all') - uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main - with: - package-type: ${{ inputs.package_type }} - os: macos-arm64 - channel: ${{ inputs.channel }} - with-cuda: ${{ inputs.with_cuda }} - validate-linux: - if: (inputs.os == 'linux' || inputs.os == 'all') - needs: generate-linux-matrix - strategy: - matrix: ${{ fromJson(needs.generate-linux-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/linux_job.yml@main - name: "linux-${{ matrix.package_type }}-${{ matrix.python_version }}-${{ matrix.desired_cuda }}" - with: - runner: ${{ matrix.validation_runner }} - repository: ${{ inputs.repository }} - ref: ${{ inputs.ref || github.ref }} - job-name: "linux-${{ matrix.package_type }}-${{ matrix.python_version }}-${{ matrix.desired_cuda }}" - binary-matrix: ${{ toJSON(matrix) }} - script: | - set -ex - export ENV_NAME="conda-env-${{ github.run_id }}" - export SMOKE_TEST="${{ inputs.smoke_test }}" - eval $SMOKE_TEST - validate-windows: - if: (inputs.os == 'windows' || inputs.os == 'all') - needs: generate-windows-matrix - strategy: - matrix: ${{ fromJson(needs.generate-windows-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/windows_job.yml@main - name: "windows-${{ matrix.package_type }}-${{ matrix.python_version }}-${{ matrix.desired_cuda }}" - with: - runner: ${{ matrix.validation_runner }} - repository: ${{ inputs.repository }} - ref: ${{ inputs.ref || github.ref }} - job-name: "windows-${{ matrix.package_type }}-${{ matrix.python_version }}-${{ matrix.desired_cuda }}" - binary-matrix: ${{ toJSON(matrix) }} - script: | - set -ex - export ENV_NAME="conda-env-${{ github.run_id }}" - export SMOKE_TEST="${{ inputs.smoke_test }}" - export TARGET_OS="windows" - eval $SMOKE_TEST - validate-macos: - if: (inputs.os == 'macos' || inputs.os == 'all') - needs: generate-macos-matrix - strategy: - matrix: ${{ fromJson(needs.generate-macos-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/macos_job.yml@main - name: "macos-${{ matrix.package_type }}-${{ matrix.python_version }}-${{ matrix.desired_cuda }}" - with: - runner: ${{ matrix.validation_runner }} - repository: ${{ inputs.repository }} - ref: ${{ inputs.ref || github.ref }} - job-name: "macos-${{ matrix.package_type }}-${{ matrix.python_version }}-${{ matrix.desired_cuda }}" - binary-matrix: ${{ toJSON(matrix) }} - script: | - set -ex - export ENV_NAME="conda-env-${{ github.run_id }}" - export TARGET_OS="macos" - export SMOKE_TEST="${{ inputs.smoke_test }}" - eval $SMOKE_TEST - validate-macos-arm64: - if: (inputs.os == 'macos-arm64' || inputs.os == 'all') - needs: generate-macos-matrix - strategy: - matrix: ${{ fromJson(needs.generate-macos-arm64-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/macos_job.yml@main - name: "macos-${{ matrix.package_type }}-${{ matrix.python_version }}-${{ matrix.desired_cuda }}" - with: - runner: ${{ matrix.validation_runner }} - repository: ${{ inputs.repository }} - ref: ${{ inputs.ref || github.ref }} - job-name: "macos-arm64-${{ matrix.package_type }}-${{ matrix.python_version }}-${{ matrix.desired_cuda }}" - binary-matrix: ${{ toJSON(matrix) }} - script: | - set -ex - export ENV_NAME="conda-env-${{ github.run_id }}" - export TARGET_OS="macos-arm64" - eval $SMOKE_TEST diff --git a/.github/workflows/validate-linux-binaries.yml b/.github/workflows/validate-linux-binaries.yml deleted file mode 100644 index dd40a9dad..000000000 --- a/.github/workflows/validate-linux-binaries.yml +++ /dev/null @@ -1,179 +0,0 @@ -name: Validate linux binaries - -on: - workflow_call: - inputs: - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: string - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate - optional' - default: "" - required: false - type: string - use-version-set: - description: 'Applies when version is used, use version for each domain' - default: false - required: false - type: boolean - release-matrix: - description: 'Release matrix - optional' - default: "" - required: false - type: string - include-test-ops: - description: 'Include Test Ops tests (only Linux)' - default: false - required: false - type: boolean - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use_split_build: - description: | - [Experimental] Use split build - required: false - type: boolean - default: false - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - workflow_dispatch: - inputs: - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: choice - options: - - release - - nightly - - test - - all - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate - optional' - default: "" - required: false - type: string - release-matrix: - description: 'Release matrix - optional' - default: "" - required: false - type: string - include-test-ops: - description: 'Include Test Ops tests (only Linux)' - default: false - required: false - type: boolean - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use_split_build: - description: | - [Experimental] Use split build - required: false - type: boolean - default: false - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - -jobs: - generate-linux-matrix: - uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main - with: - package-type: wheel,libtorch # We stopped producing conda nightlies - os: linux - channel: ${{ inputs.channel }} - use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }} - use_split_build: ${{ inputs.use_split_build }} - with-xpu: enable - - linux: - needs: generate-linux-matrix - strategy: - matrix: ${{ fromJson(needs.generate-linux-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/linux_job.yml@main - name: ${{ matrix.build_name }} - with: - runner: ${{ matrix.validation_runner }} - repository: "pytorch/builder" - ref: ${{ github.ref }} - job-name: ${{ matrix.build_name }} - docker-image: ${{ (matrix.gpu_arch_type == 'xpu' && matrix.container_image) || (matrix.gpu_arch_version == '12.6' && 'pytorch/almalinux-builder:cpu-main') || 'pytorch/conda-builder' }} - binary-matrix: ${{ toJSON(matrix) }} - timeout: 180 - script: | - set -ex - export ENV_NAME="conda-env-${{ github.run_id }}" - export TORCH_ONLY=${{ inputs.torchonly }} - export INCLUDE_TEST_OPS=${{ inputs.include-test-ops }} - export USE_ONLY_DL_PYTORCH_ORG=${{ inputs.use-only-dl-pytorch-org }} - export USE_EXTRA_INDEX_URL=${{ inputs.use-extra-index-url }} - export USE_META_CDN=${{ inputs.use-meta-cdn }} - export RELEASE_VERSION=${{ inputs.version }} - export USE_VERSION_SET=${{ inputs.use-version-set }} - if [[ ${USE_VERSION_SET} == 'true' ]]; then - export VISION_RELEASE_VERSION=${{ fromJson(inputs.release-matrix).torchvision }} - export AUDIO_RELEASE_VERSION=${{ fromJson(inputs.release-matrix).torchaudio }} - fi - - export USE_FORCE_REINSTALL=${{ inputs.use-force-reinstall }} - export TARGET_OS="linux" - # Due to xpu doesn't use pytorch/conda-builder image, need to install conda firstly - if [[ ${{ matrix.gpu_arch_type }} == 'xpu' ]]; then - source ./common/install_conda.sh - fi - eval "$(conda shell.bash hook)" - printf '%s\n' ${{ toJson(inputs.release-matrix) }} > release_matrix.json - - # Special case PyPi installation package. And Install of PyPi package via poetry - if [[ ${MATRIX_PACKAGE_TYPE} == "manywheel" && \ - ${MATRIX_GPU_ARCH_VERSION} == "12.4" && \ - ${MATRIX_CHANNEL} == "release" && \ - ${USE_ONLY_DL_PYTORCH_ORG} == "false" ]]; then - source ./.github/scripts/validate_pipy.sh - source ./.github/scripts/validate_poetry.sh - fi - - # Standart case: Validate binaries - source ./.github/scripts/validate_binaries.sh diff --git a/.github/workflows/validate-macos-arm64-binaries.yml b/.github/workflows/validate-macos-arm64-binaries.yml deleted file mode 100644 index be9a4c0c3..000000000 --- a/.github/workflows/validate-macos-arm64-binaries.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: Validate MacOS ARM64 Binaries - -on: - workflow_call: - inputs: - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: string - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate - optional' - default: "" - required: false - type: string - use-version-set: - description: 'Applies when version is used, use version for each domain' - default: false - required: false - type: boolean - release-matrix: - description: 'Release matrix - optional' - default: "" - required: false - type: string - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - workflow_dispatch: - inputs: - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: choice - options: - - release - - nightly - - test - - all - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate - optional' - default: "" - required: false - type: string - release-matrix: - description: 'Release matrix - optional' - default: "" - required: false - type: string - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - -jobs: - generate-macos-arm64-matrix: - uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main - with: - package-type: wheel,libtorch # We stopped producing conda nightlies - os: macos-arm64 - channel: ${{ inputs.channel }} - use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }} - - macos-arm64: - needs: generate-macos-arm64-matrix - strategy: - matrix: ${{ fromJson(needs.generate-macos-arm64-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/macos_job.yml@main - name: ${{ matrix.build_name }} - with: - runner: ${{ matrix.validation_runner }} - repository: "pytorch/builder" - ref: ${{ github.ref }} - job-name: ${{ matrix.build_name }} - binary-matrix: ${{ toJSON(matrix) }} - script: | - set -ex - export ENV_NAME="conda-env-${{ github.run_id }}" - export TARGET_OS="macos-arm64" - export TORCH_ONLY=${{ inputs.torchonly }} - export RELEASE_VERSION=${{ inputs.version }} - export USE_FORCE_REINSTALL=${{ inputs.use-force-reinstall }} - export USE_EXTRA_INDEX_URL=${{ inputs.use-extra-index-url }} - export USE_META_CDN=${{ inputs.use-meta-cdn }} - export USE_VERSION_SET=${{ inputs.use-version-set }} - - if [[ ${{ matrix.package_type }} == "conda" ]]; then - export IS_M1_CONDA_BUILD_JOB=1 - fi - - if [[ ${USE_VERSION_SET} == 'true' ]]; then - export VISION_RELEASE_VERSION=${{ fromJson(inputs.release-matrix).torchvision }} - export AUDIO_RELEASE_VERSION=${{ fromJson(inputs.release-matrix).torchaudio }} - fi - - printf '%s\n' ${{ toJson(inputs.release-matrix) }} > release_matrix.json - source ./.github/scripts/validate_binaries.sh diff --git a/.github/workflows/validate-nightly-binaries.yml b/.github/workflows/validate-nightly-binaries.yml deleted file mode 100644 index 13ad52b17..000000000 --- a/.github/workflows/validate-nightly-binaries.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Scheduled validation of the nightly binaries -name: cron - -on: - schedule: - # At 3:30 pm UTC (8:30 am PDT) - - cron: "30 15 * * *" - # Have the ability to trigger this job manually through the API - workflow_dispatch: - push: - branches: - - main - paths: - - .github/workflows/validate-nightly-binaries.yml - - .github/workflows/validate-linux-binaries.yml - - .github/workflows/validate-windows-binaries.yml - - .github/workflows/validate-macos-arm64-binaries.yml - - test/smoke_test/* - -jobs: - nightly: - uses: ./.github/workflows/validate-binaries.yml - with: - channel: nightly - os: all - use_split_build: true - use-meta-cdn: true diff --git a/.github/workflows/validate-nightly-pypi-wheel-binary-size.yml b/.github/workflows/validate-nightly-pypi-wheel-binary-size.yml deleted file mode 100644 index 24fffc16e..000000000 --- a/.github/workflows/validate-nightly-pypi-wheel-binary-size.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Validate Nightly PyPI Wheel Binary Size -on: - pull_request: - paths: - - .github/workflows/validate-nightly-pypi-wheel-binary-size.yml - workflow_dispatch: - schedule: - # At 2:30 pm UTC (7:30 am PDT) - - cron: "30 14 * * *" - -jobs: - nightly-pypi-binary-size-validation: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - repository: pytorch/test-infra - - name: Install requirements - run: | - pip3 install -r tools/binary_size_validation/requirements.txt - - name: Run validation - run: | - python tools/binary_size_validation/binary_size_validation.py \ - --url https://download.pytorch.org/whl/nightly/cu121/torch/ \ - --include "linux" --only-latest-version --threshold 750 diff --git a/.github/workflows/validate-release-binaries.yml b/.github/workflows/validate-release-binaries.yml deleted file mode 100644 index 1a8c51571..000000000 --- a/.github/workflows/validate-release-binaries.yml +++ /dev/null @@ -1,33 +0,0 @@ -# Scheduled validation of the release binaries -name: cron - -on: - schedule: - # At 3 am UTC (7 am PDT) - - cron: "0 3 * * *" - # Have the ability to trigger this job manually through the API - workflow_dispatch: - push: - branches: - - main - paths: - - .github/workflows/validate-release-binaries.yml - - .github/workflows/validate-linux-binaries.yml - - .github/workflows/validate-windows-binaries.yml - - .github/workflows/validate-macos-arm64-binaries.yml - - test/smoke_test/* - pull_request: - paths: - - .github/workflows/validate-nightly-binaries.yml - - .github/workflows/validate-linux-binaries.yml - - .github/workflows/validate-windows-binaries.yml - - .github/workflows/validate-macos-arm64-binaries.yml - - .github/scripts/validate_binaries.sh - - test/smoke_test/* - -jobs: - release: - uses: ./.github/workflows/validate-binaries.yml - with: - channel: release - os: all diff --git a/.github/workflows/validate-windows-binaries.yml b/.github/workflows/validate-windows-binaries.yml deleted file mode 100644 index 5b709b198..000000000 --- a/.github/workflows/validate-windows-binaries.yml +++ /dev/null @@ -1,144 +0,0 @@ -name: Validate Windows binary images - -on: - workflow_call: - inputs: - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: string - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate - optional' - default: "" - required: false - type: string - use-version-set: - description: 'Applies when version is used, use version for each domain' - default: false - required: false - type: boolean - release-matrix: - description: 'Release matrix - optional' - default: "" - required: false - type: string - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - workflow_dispatch: - inputs: - channel: - description: "Channel to use (nightly, test, release, all)" - required: true - type: choice - options: - - release - - nightly - - test - - all - torchonly: - description: 'Validate torchonly' - default: false - required: false - type: boolean - version: - description: 'Version to validate - optional' - default: "" - required: false - type: string - release-matrix: - description: 'Release matrix - optional' - default: "" - required: false - type: string - use-only-dl-pytorch-org: - description: 'Use only download.pytorch.org when generating wheel install command' - default: false - required: false - type: boolean - use-force-reinstall: - description: 'Use force-reinstall for pip binaries' - default: false - required: false - type: boolean - use-meta-cdn: - description: 'Use meta cdn for installing pip binaries' - default: false - required: false - type: boolean - use-extra-index-url: - description: 'Use extra-index url for pip tests' - default: false - required: false - type: boolean - -jobs: - generate-windows-matrix: - uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main - with: - package-type: wheel,libtorch # We stopped producing conda nightlies - os: windows - channel: ${{ inputs.channel }} - use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }} - with-xpu: enable - - win: - needs: generate-windows-matrix - strategy: - matrix: ${{ fromJson(needs.generate-windows-matrix.outputs.matrix) }} - fail-fast: false - uses: pytorch/test-infra/.github/workflows/windows_job.yml@main - name: ${{ matrix.build_name }} - with: - runner: ${{ matrix.package_type == 'libtorch' && 'windows.4xlarge' || matrix.validation_runner }} - repository: "pytorch/builder" - ref: ${{ github.ref }} - job-name: ${{ matrix.build_name }} - binary-matrix: ${{ toJSON(matrix) }} - timeout: 60 - script: | - set -ex - export ENV_NAME="conda-env-${{ github.run_id }}" - export TARGET_OS="windows" - export TORCH_ONLY=${{ inputs.torchonly }} - export RELEASE_VERSION=${{ inputs.version }} - export USE_FORCE_REINSTALL=${{ inputs.use-force-reinstall }} - export USE_EXTRA_INDEX_URL=${{ inputs.use-extra-index-url }} - export USE_META_CDN=${{ inputs.use-meta-cdn }} - export USE_VERSION_SET=${{ inputs.use-version-set }} - if [[ ${USE_VERSION_SET} == 'true' ]]; then - export VISION_RELEASE_VERSION=${{ fromJson(inputs.release-matrix).torchvision }} - export AUDIO_RELEASE_VERSION=${{ fromJson(inputs.release-matrix).torchaudio }} - fi - - printf '%s\n' ${{ toJson(inputs.release-matrix) }} > release_matrix.json - source /c/Jenkins/Miniconda3/etc/profile.d/conda.sh - if [[ ${MATRIX_GPU_ARCH_TYPE} == "cuda" ]]; then - ./windows/internal/driver_update.bat - elif [[ ${MATRIX_GPU_ARCH_TYPE} == "xpu" ]]; then - export CUDA_VERSION=xpu - ./windows/internal/xpu_install.bat - fi - source ./.github/scripts/validate_binaries.sh diff --git a/check_binary.sh b/check_binary.sh deleted file mode 100755 index 952ee5459..000000000 --- a/check_binary.sh +++ /dev/null @@ -1,390 +0,0 @@ -#!/bin/bash -set -eux -o pipefail - -# This script checks the following things on binaries -# 1. The gcc abi matches DESIRED_DEVTOOLSET -# 2. MacOS binaries do not link against OpenBLAS -# 3. There are no protobuf symbols of any sort anywhere (turned off, because -# this is currently not true) -# 4. Standard Python imports work -# 5. MKL is available everywhere except for MacOS wheels -# 6. XNNPACK is available everywhere except for MacOS wheels -# 7. CUDA is setup correctly and does not hang -# 8. Magma is available for CUDA builds -# 9. CuDNN is available for CUDA builds -# -# This script needs the env variables DESIRED_PYTHON, DESIRED_CUDA, -# DESIRED_DEVTOOLSET and PACKAGE_TYPE -# -# This script expects PyTorch to be installed into the active Python (the -# Python returned by `which python`). Or, if this is testing a libtorch -# Pythonless binary, then it expects to be in the root folder of the unzipped -# libtorch package. - - -if [[ -z ${DESIRED_PYTHON:-} ]]; then - export DESIRED_PYTHON=${MATRIX_PYTHON_VERSION:-} -fi -if [[ -z ${DESIRED_CUDA:-} ]]; then - export DESIRED_CUDA=${MATRIX_DESIRED_CUDA:-} -fi -if [[ -z ${DESIRED_DEVTOOLSET:-} ]]; then - export DESIRED_DEVTOOLSET=${MATRIX_DESIRED_DEVTOOLSET:-} -fi -if [[ -z ${PACKAGE_TYPE:-} ]]; then - export PACKAGE_TYPE=${MATRIX_PACKAGE_TYPE:-} -fi - -# The install root depends on both the package type and the os -# All MacOS packages use conda, even for the wheel packages. -if [[ "$PACKAGE_TYPE" == libtorch ]]; then - # NOTE: Only $PWD works on both CentOS and Ubuntu - export install_root="$PWD" -else - - if [[ $DESIRED_PYTHON =~ ([0-9].[0-9]+)t ]]; then - # For python that is maj.mint keep original version - py_dot="$DESIRED_PYTHON" - elif [[ $DESIRED_PYTHON =~ ([0-9].[0-9]+) ]]; then - # Strip everything but major.minor from DESIRED_PYTHON version - py_dot="${BASH_REMATCH[0]}" - else - echo "Unexpected ${DESIRED_PYTHON} format" - exit 1 - fi - export install_root="$(dirname $(which python))/../lib/python${py_dot}/site-packages/torch/" -fi - -############################################################################### -# Setup XPU ENV -############################################################################### -if [[ "$DESIRED_CUDA" == 'xpu' ]]; then - set +u - # Refer https://www.intel.com/content/www/us/en/developer/articles/tool/pytorch-prerequisites-for-intel-gpus.html - source /opt/intel/oneapi/compiler/latest/env/vars.sh - source /opt/intel/oneapi/pti/latest/env/vars.sh -fi - -############################################################################### -# Check GCC ABI -############################################################################### - -# NOTE [ Building libtorch with old vs. new gcc ABI ] -# -# Packages built with one version of ABI could not be linked against by client -# C++ libraries that were compiled using the other version of ABI. Since both -# gcc ABIs are still common in the wild, we need to support both ABIs. Currently: -# -# - All the nightlies built on CentOS 7 + devtoolset7 use the old gcc ABI. -# - All the nightlies built on Ubuntu 16.04 + gcc 5.4 use the new gcc ABI. - -echo "Checking that the gcc ABI is what we expect" -if [[ "$(uname)" != 'Darwin' ]]; then - function is_expected() { - if [[ "$DESIRED_DEVTOOLSET" == *"cxx11-abi"* || "$DESIRED_CUDA" == *"rocm"* ]]; then - if [[ "$1" -gt 0 || "$1" == "ON " ]]; then - echo 1 - fi - else - if [[ -z "$1" || "$1" == 0 || "$1" == "OFF" ]]; then - echo 1 - fi - fi - } - - # First we check that the env var in TorchConfig.cmake is correct - - # We search for D_GLIBCXX_USE_CXX11_ABI=1 in torch/TorchConfig.cmake - torch_config="${install_root}/share/cmake/Torch/TorchConfig.cmake" - if [[ ! -f "$torch_config" ]]; then - echo "No TorchConfig.cmake found!" - ls -lah "$install_root/share/cmake/Torch" - exit 1 - fi - echo "Checking the TorchConfig.cmake" - cat "$torch_config" - - # The sed call below is - # don't print lines by default (only print the line we want) - # -n - # execute the following expression - # e - # replace lines that match with the first capture group and print - # s/.*D_GLIBCXX_USE_CXX11_ABI=\(.\)".*/\1/p - # any characters, D_GLIBCXX_USE_CXX11_ABI=, exactly one any character, a - # quote, any characters - # Note the exactly one single character after the '='. In the case that the - # variable is not set the '=' will be followed by a '"' immediately and the - # line will fail the match and nothing will be printed; this is what we - # want. Otherwise it will capture the 0 or 1 after the '='. - # /.*D_GLIBCXX_USE_CXX11_ABI=\(.\)".*/ - # replace the matched line with the capture group and print - # /\1/p - actual_gcc_abi="$(sed -ne 's/.*D_GLIBCXX_USE_CXX11_ABI=\(.\)".*/\1/p' < "$torch_config")" - if [[ "$(is_expected "$actual_gcc_abi")" != 1 ]]; then - echo "gcc ABI $actual_gcc_abi not as expected." - exit 1 - fi - - # We also check that there are [not] cxx11 symbols in libtorch - # - echo "Checking that symbols in libtorch.so have the right gcc abi" - python3 "$(dirname ${BASH_SOURCE[0]})/test/check_binary_symbols.py" - - echo "cxx11 symbols seem to be in order" -fi # if on Darwin - -############################################################################### -# Check for no OpenBLAS -# TODO Check for no Protobuf symbols (not finished) -# Print *all* runtime dependencies -############################################################################### -# We have to loop through all shared libraries for this -if [[ "$(uname)" == 'Darwin' ]]; then - all_dylibs=($(find "$install_root" -name '*.dylib')) - for dylib in "${all_dylibs[@]}"; do - echo "All dependencies of $dylib are $(otool -L $dylib) with rpath $(otool -l $dylib | grep LC_RPATH -A2)" - - # Check that OpenBlas is not linked to on Macs - echo "Checking the OpenBLAS is not linked to" - if [[ -n "$(otool -L $dylib | grep -i openblas)" ]]; then - echo "ERROR: Found openblas as a dependency of $dylib" - echo "Full dependencies is: $(otool -L $dylib)" - exit 1 - fi - - # Check for protobuf symbols - #proto_symbols="$(nm $dylib | grep protobuf)" || true - #if [[ -n "$proto_symbols" ]]; then - # echo "ERROR: Detected protobuf symbols in $dylib" - # echo "Symbols are $proto_symbols" - # exit 1 - #fi - done -else - all_libs=($(find "$install_root" -name '*.so')) - for lib in "${all_libs[@]}"; do - echo "All dependencies of $lib are $(ldd $lib) with runpath $(objdump -p $lib | grep RUNPATH)" - - # Check for protobuf symbols - #proto_symbols=$(nm $lib | grep protobuf) || true - #if [[ -n "$proto_symbols" ]]; then - # echo "ERROR: Detected protobuf symbols in $lib" - # echo "Symbols are $proto_symbols" - # exit 1 - #fi - done -fi - -setup_link_flags () { - REF_LIB="-Wl,-R${install_root}/lib" - if [[ "$(uname)" == 'Darwin' ]]; then - REF_LIB="-Wl,-rpath ${install_root}/lib" - fi - ADDITIONAL_LINKER_FLAGS="" - if [[ "$(uname)" == 'Linux' ]]; then - ADDITIONAL_LINKER_FLAGS="-Wl,--no-as-needed" - fi - C10_LINK_FLAGS="" - if [ -f "${install_root}/lib/libc10.so" ] || [ -f "${install_root}/lib/libc10.dylib" ]; then - C10_LINK_FLAGS="-lc10" - fi - TORCH_CPU_LINK_FLAGS="" - if [ -f "${install_root}/lib/libtorch_cpu.so" ] || [ -f "${install_root}/lib/libtorch_cpu.dylib" ]; then - TORCH_CPU_LINK_FLAGS="-ltorch_cpu" - fi - TORCH_CUDA_LINK_FLAGS="" - if [ -f "${install_root}/lib/libtorch_cuda.so" ] || [ -f "${install_root}/lib/libtorch_cuda.dylib" ]; then - TORCH_CUDA_LINK_FLAGS="-ltorch_cuda" - elif [ -f "${install_root}/lib/libtorch_cuda_cpp.so" ] && [ -f "${install_root}/lib/libtorch_cuda_cpp.so" ] || \ - [ -f "${install_root}/lib/libtorch_cuda_cu.dylib" ] && [ -f "${install_root}/lib/libtorch_cuda_cu.dylib" ]; then - TORCH_CUDA_LINK_FLAGS="-ltorch_cuda_cpp -ltorch_cuda_cu" - fi -} - -TEST_CODE_DIR="$(dirname $(realpath ${BASH_SOURCE[0]}))/test_example_code" -build_and_run_example_cpp () { - if [[ "$DESIRED_DEVTOOLSET" == *"cxx11-abi"* ]]; then - GLIBCXX_USE_CXX11_ABI=1 - else - GLIBCXX_USE_CXX11_ABI=0 - fi - setup_link_flags - g++ ${TEST_CODE_DIR}/$1.cpp -I${install_root}/include -I${install_root}/include/torch/csrc/api/include -D_GLIBCXX_USE_CXX11_ABI=$GLIBCXX_USE_CXX11_ABI -std=gnu++17 -L${install_root}/lib ${REF_LIB} ${ADDITIONAL_LINKER_FLAGS} -ltorch $TORCH_CPU_LINK_FLAGS $TORCH_CUDA_LINK_FLAGS $C10_LINK_FLAGS -o $1 - ./$1 -} - -build_example_cpp_with_incorrect_abi () { - if [[ "$DESIRED_DEVTOOLSET" == *"cxx11-abi"* ]]; then - GLIBCXX_USE_CXX11_ABI=0 - else - GLIBCXX_USE_CXX11_ABI=1 - fi - set +e - setup_link_flags - g++ ${TEST_CODE_DIR}/$1.cpp -I${install_root}/include -I${install_root}/include/torch/csrc/api/include -D_GLIBCXX_USE_CXX11_ABI=$GLIBCXX_USE_CXX11_ABI -std=gnu++17 -L${install_root}/lib ${REF_LIB} ${ADDITIONAL_LINKER_FLAGS} -ltorch $TORCH_CPU_LINK_FLAGS $TORCH_CUDA_LINK_FLAGS $C10_LINK_FLAGS -o $1 - ERRCODE=$? - set -e - if [ "$ERRCODE" -eq "0" ]; then - echo "Building example with incorrect ABI didn't throw error. Aborting." - exit 1 - else - echo "Building example with incorrect ABI throws expected error. Proceeding." - fi -} - -############################################################################### -# Check simple Python/C++ calls -############################################################################### -if [[ "$PACKAGE_TYPE" == 'libtorch' ]]; then - # NS: Set LD_LIBRARY_PATH for CUDA builds, but perhaps it should be removed - if [[ "$DESIRED_CUDA" == "cu"* ]]; then - export LD_LIBRARY_PATH=/usr/local/cuda/lib64 - fi - build_and_run_example_cpp simple-torch-test - # `_GLIBCXX_USE_CXX11_ABI` is always ignored by gcc in devtoolset7, so we test - # the expected failure case for Ubuntu 16.04 + gcc 5.4 only. - if [[ "$DESIRED_DEVTOOLSET" == *"cxx11-abi"* ]]; then - build_example_cpp_with_incorrect_abi simple-torch-test - fi -else - pushd /tmp - python -c 'import torch' - popd -fi - -############################################################################### -# Check torch.git_version -############################################################################### -if [[ "$PACKAGE_TYPE" != 'libtorch' ]]; then - pushd /tmp - python -c 'import torch; assert torch.version.git_version != "Unknown"' - python -c 'import torch; assert torch.version.git_version != None' - popd -fi - - -############################################################################### -# Check for MKL -############################################################################### - -if [[ "$PACKAGE_TYPE" == 'libtorch' ]]; then - echo "Checking that MKL is available" - build_and_run_example_cpp check-torch-mkl -elif [[ "$(uname -m)" != "arm64" && "$(uname -m)" != "s390x" ]]; then - if [[ "$(uname)" != 'Darwin' || "$PACKAGE_TYPE" != *wheel ]]; then - if [[ "$(uname -m)" == "aarch64" ]]; then - echo "Checking that MKLDNN is available on aarch64" - pushd /tmp - python -c 'import torch; exit(0 if torch.backends.mkldnn.is_available() else 1)' - popd - else - echo "Checking that MKL is available" - pushd /tmp - python -c 'import torch; exit(0 if torch.backends.mkl.is_available() else 1)' - popd - fi - fi -fi - -############################################################################### -# Check for XNNPACK -############################################################################### - -if [[ "$PACKAGE_TYPE" == 'libtorch' ]]; then - echo "Checking that XNNPACK is available" - build_and_run_example_cpp check-torch-xnnpack -else - if [[ "$(uname)" != 'Darwin' || "$PACKAGE_TYPE" != *wheel ]] && [[ "$(uname -m)" != "s390x" ]]; then - echo "Checking that XNNPACK is available" - pushd /tmp - python -c 'import torch.backends.xnnpack; exit(0 if torch.backends.xnnpack.enabled else 1)' - popd - fi -fi - -############################################################################### -# Check CUDA configured correctly -############################################################################### -# Skip these for Windows machines without GPUs -if [[ "$OSTYPE" == "msys" ]]; then - GPUS=$(wmic path win32_VideoController get name) - if [[ ! "$GPUS" == *NVIDIA* ]]; then - echo "Skip CUDA tests for machines without a Nvidia GPU card" - exit 0 - fi -fi - -# Test that CUDA builds are setup correctly -if [[ "$DESIRED_CUDA" != 'cpu' && "$DESIRED_CUDA" != 'xpu' && "$DESIRED_CUDA" != 'cpu-cxx11-abi' && "$DESIRED_CUDA" != *"rocm"* && "$(uname -m)" != "s390x" ]]; then - if [[ "$PACKAGE_TYPE" == 'libtorch' ]]; then - build_and_run_example_cpp check-torch-cuda - else - pushd /tmp - echo "Checking that CUDA archs are setup correctly" - timeout 20 python -c 'import torch; torch.randn([3,5]).cuda()' - - # These have to run after CUDA is initialized - - echo "Checking that magma is available" - python -c 'import torch; torch.rand(1).cuda(); exit(0 if torch.cuda.has_magma else 1)' - - echo "Checking that CuDNN is available" - python -c 'import torch; exit(0 if torch.backends.cudnn.is_available() else 1)' - - # Validates builds is free of linker regressions reported in https://github.com/pytorch/pytorch/issues/57744 - echo "Checking that exception handling works" - python -c "import torch; from unittest import TestCase;TestCase().assertRaises(RuntimeError, lambda:torch.eye(7, 7, device='cuda:7'))" - - echo "Checking that basic RNN works" - python ${TEST_CODE_DIR}/rnn_smoke.py - - echo "Checking that basic CNN works" - python ${TEST_CODE_DIR}/cnn_smoke.py - - echo "Test that linalg works" - python -c "import torch;x=torch.rand(3,3,device='cuda');print(torch.linalg.svd(torch.mm(x.t(), x)))" - - popd - fi # if libtorch -fi # if cuda - -########################## -# Run parts of smoke tests -########################## -if [[ "$PACKAGE_TYPE" != 'libtorch' ]]; then - pushd "$(dirname ${BASH_SOURCE[0]})/test/smoke_test" - python -c "from smoke_test import test_linalg; test_linalg()" - if [[ "$DESIRED_CUDA" == *cuda* ]]; then - python -c "from smoke_test import test_linalg; test_linalg('cuda')" - fi - popd -fi - -############################################################################### -# Check PyTorch supports TCP_TLS gloo transport -############################################################################### - -if [[ "$(uname)" == 'Linux' && "$PACKAGE_TYPE" != 'libtorch' ]]; then - GLOO_CHECK="import torch.distributed as dist -try: - dist.init_process_group('gloo', rank=0, world_size=1) -except RuntimeError as e: - print(e) -" - RESULT=`GLOO_DEVICE_TRANSPORT=TCP_TLS MASTER_ADDR=localhost MASTER_PORT=63945 python -c "$GLOO_CHECK"` - GLOO_TRANSPORT_IS_NOT_SUPPORTED='gloo transport is not supported' - if [[ "$RESULT" =~ "$GLOO_TRANSPORT_IS_NOT_SUPPORTED" ]]; then - echo "PyTorch doesn't support TLS_TCP transport, please build with USE_GLOO_WITH_OPENSSL=1" - exit 1 - fi -fi - -############################################################################### -# Check for C++ ABI compatibility between gcc7 and gcc9 compiled binaries -############################################################################### -if [[ "$(uname)" == 'Linux' && ("$PACKAGE_TYPE" == 'conda' || "$PACKAGE_TYPE" == 'manywheel')]]; then - pushd /tmp - python -c "import torch; exit(0 if torch.compiled_with_cxx11_abi() else (0 if torch._C._PYBIND11_BUILD_ABI == '_cxxabi1011' else 1))" - popd -fi diff --git a/conda/.DS_Store b/conda/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0&1)" in - *2* | *3.5* | *3.6*) - numpy_ver=1.11 - ;; - esac - retry pip install -q "numpy==${numpy_ver}" || true -fi - -echo "Testing with:" -pip freeze -conda list || true - -############################################################################## -# Smoke tests -############################################################################## -# TODO use check_binary.sh, which requires making sure it runs on Windows -pushd / -echo "Smoke testing imports" -python -c 'import torch' - -# Test that MKL is there -if [[ "$(uname)" == 'Darwin' && "$package_type" == *wheel ]]; then - echo 'Not checking for MKL on Darwin wheel packages' -else - echo "Checking that MKL is available" - python -c 'import torch; exit(0 if torch.backends.mkl.is_available() else 1)' -fi - -if [[ "$OSTYPE" == "msys" ]]; then - GPUS=$(wmic path win32_VideoController get name) - if [[ ! "$GPUS" == *NVIDIA* ]]; then - echo "Skip CUDA tests for machines without a Nvidia GPU card" - exit 0 - fi -fi - -# Test that the version number is consistent during building and testing -if [[ "$PYTORCH_BUILD_NUMBER" -gt 1 ]]; then - expected_version="${PYTORCH_BUILD_VERSION}.post${PYTORCH_BUILD_NUMBER}" -else - expected_version="${PYTORCH_BUILD_VERSION}" -fi -echo "Checking that we are testing the package that is just built" -python -c "import torch; exit(0 if torch.__version__ == '$expected_version' else 1)" - -# Test that CUDA builds are setup correctly -if [[ "$cuda_ver" != 'cpu' ]]; then - cuda_installed=1 - nvidia-smi || cuda_installed=0 - if [[ "$cuda_installed" == 0 ]]; then - echo "Skip CUDA tests for machines without a Nvidia GPU card" - else - # Test CUDA archs - echo "Checking that CUDA archs are setup correctly" - timeout 20 python -c 'import torch; torch.randn([3,5]).cuda()' - - # These have to run after CUDA is initialized - echo "Checking that magma is available" - python -c 'import torch; torch.rand(1).cuda(); exit(0 if torch.cuda.has_magma else 1)' - echo "Checking that CuDNN is available" - python -c 'import torch; exit(0 if torch.backends.cudnn.is_available() else 1)' - fi -fi - -# Check that OpenBlas is not linked to on MacOS -if [[ "$(uname)" == 'Darwin' ]]; then - echo "Checking the OpenBLAS is not linked to" - all_dylibs=($(find "$(python -c "import site; print(site.getsitepackages()[0])")"/torch -name '*.dylib')) - for dylib in "${all_dylibs[@]}"; do - if [[ -n "$(otool -L $dylib | grep -i openblas)" ]]; then - echo "Found openblas as a dependency of $dylib" - echo "Full dependencies is: $(otool -L $dylib)" - exit 1 - fi - done - - echo "Checking that OpenMP is available" - python -c "import torch; exit(0 if torch.backends.openmp.is_available() else 1)" -fi - -popd - -# TODO re-enable the other tests after the nightlies are moved to CI. This is -# because the binaries keep breaking, often from additional tests, that aren't -# real problems. Once these are on circleci and a smoke-binary-build is added -# to PRs then this should stop happening and these can be re-enabled. -echo "Not running unit tests. Hopefully these problems are caught by CI" -exit 0 - - -############################################################################## -# Running unit tests (except not right now) -############################################################################## -echo "$(date) :: Starting tests for $package_type package for python$py_ver and $cuda_ver" - -# We keep track of exact tests to skip, as otherwise we would be hardly running -# any tests. But b/c of issues working with pytest/normal-python-test/ and b/c -# of special snowflake tests in test/run_test.py we also take special care of -# those -tests_to_skip=() - -# -# Entire file exclusions -############################################################################## -entire_file_exclusions=("-x") - -# cpp_extensions doesn't work with pytest, so we exclude it from the pytest run -# here and then manually run it later. Note that this is only because this -# entire_fil_exclusions flag is only passed to the pytest run -entire_file_exclusions+=("cpp_extensions") - -# TODO temporary line to fix next days nightlies, but should be removed when -# issue is fixed -entire_file_exclusions+=('type_info') - -if [[ "$cuda_ver" == 'cpu' ]]; then - # test/test_cuda.py exits early if the installed torch is not built with - # CUDA, but the exit doesn't work when running with pytest, so pytest will - # still try to run all the CUDA tests and then fail - entire_file_exclusions+=("cuda") - entire_file_exclusions+=("nccl") -fi - -if [[ "$(uname)" == 'Darwin' || "$OSTYPE" == "msys" ]]; then - # pytest on Mac doesn't like the exits in these files - entire_file_exclusions+=('c10d') - entire_file_exclusions+=('distributed') - - # pytest doesn't mind the exit but fails the tests. On Mac we run this - # later without pytest - entire_file_exclusions+=('thd_distributed') -fi - - -# -# Universal flaky tests -############################################################################## - -# RendezvousEnvTest sometimes hangs forever -# Otherwise it will fail on CUDA with -# Traceback (most recent call last): -# File "test_c10d.py", line 179, in test_common_errors -# next(gen) -# AssertionError: ValueError not raised -tests_to_skip+=('RendezvousEnvTest and test_common_errors') - -# This hung forever once on conda_3.5_cu92 -tests_to_skip+=('TestTorch and test_sum_dim') - -# test_trace_warn isn't actually flaky, but it doesn't work with pytest so we -# just skip it -tests_to_skip+=('TestJit and test_trace_warn') -# -# Python specific flaky tests -############################################################################## - -# test_dataloader.py:721: AssertionError -# looks like a timeout, but interestingly only appears on python 3 -if [[ "$py_ver" == 3* ]]; then - tests_to_skip+=('TestDataLoader and test_proper_exit') -fi - -# -# CUDA flaky tests, all package types -############################################################################## -if [[ "$cuda_ver" != 'cpu' ]]; then - - # - # DistributedDataParallelTest - # All of these seem to fail - tests_to_skip+=('DistributedDataParallelTest') - - # - # RendezvousEnvTest - # Traceback (most recent call last): - # File "test_c10d.py", line 201, in test_nominal - # store0, rank0, size0 = next(gen0) - # File "/opt/python/cp36-cp36m/lib/python3.6/site-packages/torch/distributed/rendezvous.py", line 131, in _env_rendezvous_handler - # store = TCPStore(master_addr, master_port, start_daemon) - # RuntimeError: Address already in use - tests_to_skip+=('RendezvousEnvTest and test_nominal') - - # - # TestCppExtension - # - # Traceback (most recent call last): - # File "test_cpp_extensions.py", line 134, in test_jit_cudnn_extension - # with_cuda=True) - # File "/opt/python/cp35-cp35m/lib/python3.5/site-packages/torch/utils/cpp_extension.py", line 552, in load - # with_cuda) - # File "/opt/python/cp35-cp35m/lib/python3.5/site-packages/torch/utils/cpp_extension.py", line 729, in _jit_compile - # return _import_module_from_library(name, build_directory) - # File "/opt/python/cp35-cp35m/lib/python3.5/site-packages/torch/utils/cpp_extension.py", line 867, in _import_module_from_library - # return imp.load_module(module_name, file, path, description) - # File "/opt/python/cp35-cp35m/lib/python3.5/imp.py", line 243, in load_module - # return load_dynamic(name, filename, file) - # File "/opt/python/cp35-cp35m/lib/python3.5/imp.py", line 343, in load_dynamic - # return _load(spec) - # File "", line 693, in _load - # File "", line 666, in _load_unlocked - # File "", line 577, in module_from_spec - # File "", line 938, in create_module - # File "", line 222, in _call_with_frames_removed - # ImportError: libcudnn.so.7: cannot open shared object file: No such file or directory - tests_to_skip+=('TestCppExtension and test_jit_cudnn_extension') - - # - # TestCuda - # - - # 3.7_cu80 - # RuntimeError: CUDA error: out of memory - tests_to_skip+=('TestCuda and test_arithmetic_large_tensor') - - # 3.7_cu80 - # RuntimeError: cuda runtime error (2) : out of memory at /opt/conda/conda-bld/pytorch-nightly_1538097262541/work/aten/src/THC/THCTensorCopy.cu:205 - tests_to_skip+=('TestCuda and test_autogpu') - - # - # TestDistBackend - # - - # Traceback (most recent call last): - # File "test_thd_distributed.py", line 1046, in wrapper - # self._join_and_reduce(fn) - # File "test_thd_distributed.py", line 1108, in _join_and_reduce - # self.assertEqual(p.exitcode, first_process.exitcode) - # File "/pytorch/test/common.py", line 399, in assertEqual - # super(TestCase, self).assertEqual(x, y, message) - # AssertionError: None != 77 : - tests_to_skip+=('TestDistBackend and test_all_gather_group') - tests_to_skip+=('TestDistBackend and test_all_reduce_group_max') - tests_to_skip+=('TestDistBackend and test_all_reduce_group_min') - tests_to_skip+=('TestDistBackend and test_all_reduce_group_sum') - tests_to_skip+=('TestDistBackend and test_all_reduce_group_product') - tests_to_skip+=('TestDistBackend and test_barrier_group') - tests_to_skip+=('TestDistBackend and test_broadcast_group') - - # Traceback (most recent call last): - # File "test_thd_distributed.py", line 1046, in wrapper - # self._join_and_reduce(fn) - # File "test_thd_distributed.py", line 1108, in _join_and_reduce - # self.assertEqual(p.exitcode, first_process.exitcode) - # File "/pytorch/test/common.py", line 397, in assertEqual - # super(TestCase, self).assertLessEqual(abs(x - y), prec, message) - # AssertionError: 12 not less than or equal to 1e-05 - tests_to_skip+=('TestDistBackend and test_barrier') - - # Traceback (most recent call last): - # File "test_distributed.py", line 1267, in wrapper - # self._join_and_reduce(fn) - # File "test_distributed.py", line 1350, in _join_and_reduce - # self.assertEqual(p.exitcode, first_process.exitcode) - # File "/pytorch/test/common.py", line 399, in assertEqual - # super(TestCase, self).assertEqual(x, y, message) - # AssertionError: None != 1 - tests_to_skip+=('TestDistBackend and test_broadcast') - - # Memory leak very similar to all the conda ones below, but appears on manywheel - # 3.6m_cu80 - # AssertionError: 1605632 not less than or equal to 1e-05 : __main__.TestEndToEndHybridFrontendModels.test_vae_cuda leaked 1605632 bytes CUDA memory on device 0 - tests_to_skip+=('TestEndToEndHybridFrontendModels and test_vae_cuda') - - # ________________________ TestNN.test_embedding_bag_cuda ________________________ - # - # self = - # dtype = torch.float32 - # - # @unittest.skipIf(not TEST_CUDA, "CUDA unavailable") - # @repeat_test_for_types(ALL_TENSORTYPES) - # @skipIfRocm - # def test_embedding_bag_cuda(self, dtype=torch.float): - # self._test_EmbeddingBag(True, 'sum', False, dtype) - # self._test_EmbeddingBag(True, 'mean', False, dtype) - # self._test_EmbeddingBag(True, 'max', False, dtype) - # if dtype != torch.half: - # # torch.cuda.sparse.HalfTensor is not enabled. - # self._test_EmbeddingBag(True, 'sum', True, dtype) - # > self._test_EmbeddingBag(True, 'mean', True, dtype) - # - # test_nn.py:2144: - # _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - # test_nn.py:2062: in _test_EmbeddingBag - # _test_vs_Embedding(N, D, B, L) - # test_nn.py:2059: in _test_vs_Embedding - # self.assertEqual(es_weight_grad, e.weight.grad, needed_prec) - # common.py:373: in assertEqual - # assertTensorsEqual(x, y) - # common.py:365: in assertTensorsEqual - # self.assertLessEqual(max_err, prec, message) - # E AssertionError: tensor(0.0000, device='cuda:0', dtype=torch.float32) not less than or equal to 2e-05 : - # 1 failed, 1202 passed, 19 skipped, 2 xfailed, 796 warnings in 1166.73 seconds = - # Traceback (most recent call last): - # File "test/run_test.py", line 391, in - # main() - # File "test/run_test.py", line 383, in main - # raise RuntimeError(message) - tests_to_skip+=('TestNN and test_embedding_bag_cuda') -fi - - -########################################################################## -# Conda specific flaky tests -########################################################################## - -# Only on Anaconda's python 2.7 -# So, this doesn't really make sense. All the mac jobs are run on the same -# machine, so the wheel jobs still use conda to silo their python -# installations. The wheel job for Python 2.7 should use the exact same Python -# from conda as the conda job for Python 2.7. Yet, this only appears on the -# conda jobs. -if [[ "$package_type" == 'conda' && "$py_ver" == '2.7' ]]; then - # Traceback (most recent call last): - # File "test_jit.py", line 6281, in test_wrong_return_type - # @torch.jit.script - # File "/Users/administrator/nightlies/2018_09_30/wheel_build_dirs/conda_2.7/conda/envs/env_py2.7_0_20180930/lib/python2.7/site-packages/torch/jit/__init__.py", line 639, in script - # graph = _jit_script_compile(ast, rcb) - # File "/Users/administrator/nightlies/2018_09_30/wheel_build_dirs/conda_2.7/conda/envs/env_py2.7_0_20180930/lib/python2.7/site-packages/torch/jit/annotations.py", line 80, in get_signature - # return parse_type_line(type_line) - # File "/Users/administrator/nightlies/2018_09_30/wheel_build_dirs/conda_2.7/conda/envs/env_py2.7_0_20180930/lib/python2.7/site-packages/torch/jit/annotations.py", line 131, in parse_type_line - # return arg_types, ann_to_type(ret_ann) - # File "/Users/administrator/nightlies/2018_09_30/wheel_build_dirs/conda_2.7/conda/envs/env_py2.7_0_20180930/lib/python2.7/site-packages/torch/jit/annotations.py", line 192, in ann_to_type - # return TupleType([ann_to_type(a) for a in ann.__args__]) - # TypeError: 'TupleInstance' object is not iterable - tests_to_skip+=('TestScript and test_wrong_return_type') -fi - -# Lots of memory leaks on CUDA -if [[ "$package_type" == 'conda' && "$cuda_ver" != 'cpu' ]]; then - - # 3.7_cu92 - # AssertionError: 63488 not less than or equal to 1e-05 : __main__.TestEndToEndHybridFrontendModels.test_mnist_cuda leaked 63488 bytes CUDA memory on device 0 - tests_to_skip+=('TestEndToEndHybridFrontendModels and test_mnist_cuda') - - # 2.7_cu92 - # AssertionError: __main__.TestNN.test_BatchNorm3d_momentum_eval_cuda leaked -1024 bytes CUDA memory on device 0 - tests_to_skip+=('TestNN and test_BatchNorm3d_momentum_eval_cuda') - - # - # All of test_BCE is flaky - tests_to_skip+=('TestNN and test_BCE') - - # 3.5_cu80 - # AssertionError: 3584 not less than or equal to 1e-05 : test_nn.TestNN.test_BCEWithLogitsLoss_cuda_double leaked 3584 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_BCEWithLogitsLoss_cuda_double') - - # 2.7_cu92 - # AssertionError: __main__.TestNN.test_ConvTranspose2d_cuda leaked -1024 bytes CUDA memory on device 0 - tests_to_skip+=('TestNN and test_ConvTranspose2d_cuda') - - # 3.7_cu90 - # AssertionError: 1024 not less than or equal to 1e-05 : __main__.TestNN.test_ConvTranspose3d_cuda leaked -1024 bytes CUDA memory on device 0 - tests_to_skip+=('TestNN and test_ConvTranspose3d_cuda') - - # - # - # CTCLoss - # These are all flaky - tests_to_skip+=('TestNN and test_CTCLoss') - - # 2.7_cu90 - # 2.7_cu92 - # 3.5_cu90 x2 - # 3.6_cu90 - # 3.7_cu80 x3 - # 3.7_cu90 - # AssertionError: 37376 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_1d_target_cuda_double leaked 37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_1d_target_cuda_double') - - # 2.7_cu80 --18944 - # 2.7_cu92 - # 3.5_cu90 --18944 x2 - # 3.5_cu92 --18944 x2 - # 3.6_cu90 --18944 - # 3.6_cu92 --18944 - # 3.7_cu80 - # AssertionError: 37376 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_1d_target_cuda_float leaked -37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_1d_target_cuda_float') - - # 3.5_cu90 - # 3.7_cu92 - # AssertionError: 37376 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_1d_target_sum_reduction_cuda_double leaked 37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_1d_target_sum_reduction_cuda_double') - - # 3.7_cu92 - # AssertionError: 18432 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_1d_target_sum_reduction_cuda_float leaked -18432 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_1d_target_sum_reduction_cuda_float') - - # 3.5_cu92 x2 - # 3.6_cu80 - # 3.7_cu90 - # AssertionError: AssertionError: 37376 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_2d_int_target_cuda_double leaked 37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_2d_int_target_cuda_double') - - # 3.5_cu92 - # 3.6_cu80 --37376 - # 3.6_cu92 - # AssertionError: 18944 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_2d_int_target_cuda_float leaked 18944 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_2d_int_target_cuda_float') - - # 2.7_cu90 - # 3.5_cu80 - # 3.7_cu80 x2 - # AssertionError: 37376 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_2d_int_target_sum_reduction_cuda_double leaked 37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_2d_int_target_sum_reduction_cuda_double') - - # 2.7_cu90 - # 2.7_cu92 --18944 - # AssertionError: __main__.TestNN.test_CTCLoss_2d_int_target_sum_reduction_cuda_float leaked -37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_2d_int_target_sum_reduction_cuda_float') - - # 2.7_cu92 - # AssertionError: __main__.TestNN.test_CTCLoss_cuda_double leaked 37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_cuda_double') - - # 2.7_cu92 - # AssertionError: __main__.TestNN.test_CTCLoss_cuda_float leaked 18944 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_cuda_float') - - # 2.7_cu92 - # 3.5_cu90 x2 - # 3.5_cu92 - # 3.5_cu92 - # 3.6_cu80 x2 - # AssertionError: 37376 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_sum_reduction_cuda_double leaked 37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_sum_reduction_cuda_double') - - # 2.7_cu92 --18944 - # 3.6_cu80 - # AssertionError: 37376 not less than or equal to 1e-05 : __main__.TestNN.test_CTCLoss_sum_reduction_cuda_float leaked -37376 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_CTCLoss_sum_reduction_cuda_float') - - # - # - # NLLLoss - # These are all flaky - tests_to_skip+=('TestNN and NLLLoss') - - # 3.5_cu90 x2 - # AssertionError: 3584 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_2d_cuda_double leaked 3584 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_cuda_double') - - # 2.7_cu80 - # AssertionError: __main__.TestNN.test_NLLLoss_2d_cuda_float leaked 2560 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_cuda_float') - - # 2.7_cu80 - # 2.7_cu92 - # 3.6_cu80 x2 - # AssertionError: 1536 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_2d_cuda_half leaked 1536 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_cuda_half') - - # 2.7_cu90 - # 3.6_cu80 x2 - # 3.6_cu90 - # 3.6_cu92 - # AssertionError: 3584 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_2d_ignore_index_cuda_double leaked 3584 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_ignore_index_cuda_double') - - # 3.6_cu80 x2 - # 3.6_cu90 - # AssertionError: 3584 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_2d_ignore_index_cuda_float leaked -3584 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_ignore_index_cuda_float') - - # 3.6_cu90 - # AssertionError: 3584 not less than or equal to 1e-05 : test_nn.TestNN.test_NLLLoss_2d_weights_cuda_double leaked 3584 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_ignore_index_cuda_half') - - # 3.6_cu80 - # AssertionError: 3584 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_2d_sum_reduction_cuda_double leaked 3584 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_sum_reduction_cuda_double') - - # 3.6_cu80 - # AssertionError: 2560 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_2d_sum_reduction_cuda_float leaked 2560 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_sum_reduction_cuda_float') - - # 3.7_cu92 - # AssertionError: 1536 not less than or equal to 1e-05 : test_nn.TestNN.test_NLLLoss_2d_weights_cuda_half leaked 1536 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_weights_cuda_half') - - # 3.6_cu80 - # AssertionError: 1536 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_2d_sum_reduction_cuda_half leaked 1536 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_sum_reduction_cuda_half') - - # 2.7_cu92 - # AssertionError: __main__.TestNN.test_NLLLoss_2d_weights_cuda_float leaked 2560 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_2d_weights_cuda_float') - - # 3.5_cu80 x2 - # 3.6_cu90 - # AssertionError: 1536 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_dim_is_3_cuda_double leaked 1536 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_dim_is_3_cuda_double') - - # 3.6_cu80 - # AssertionError: 1536 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_dim_is_3_sum_reduction_cuda_float leaked 1536 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_dim_is_3_sum_reduction_cuda_float') - - # 3.6_cu80 - # 3.7_cu80 x2 - # AssertionError: 1536 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_dim_is_3_sum_reduction_cuda_half leaked 1536 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_dim_is_3_sum_reduction_cuda_half') - - # 3.5_cu80 - # 3.7_cu80 x2 - # AssertionError: 10752 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_higher_dim_cuda_double leaked 10752 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_higher_dim_cuda_double') - - # 3.5_cu80 - # 3.7_cu80 --10752 x2 - # AssertionError: 5120 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_higher_dim_cuda_float leaked -5120 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_higher_dim_cuda_float') - - # 3.5_cu80 - # 3.5 cu90 - # AssertionError: 3584 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_higher_dim_cuda_half leaked 3584 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_higher_dim_cuda_half') - - # 3.5_cu90 - # AssertionError: 10752 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_higher_dim_sum_reduction_cuda_double leaked 10752 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_higher_dim_sum_reduction_cuda_double') - - # 3.5_cu90 - # AssertionError: 5120 not less than or equal to 1e-05 : __main__.TestNN.test_NLLLoss_higher_dim_sum_reduction_cuda_float leaked -5120 bytes CUDA memory on device 0 - #tests_to_skip+=('TestNN and test_NLLLoss_higher_dim_sum_reduction_cuda_float') - - # ______________________ TestNN.test_variable_sequence_cuda ______________________ - # common_utils.py:277: in wrapper - # method(*args, **kwargs) - # common_utils.py:241: in __exit__ - # self.name, after - before, i)) - # common_utils.py:399: in assertEqual - # super(TestCase, self).assertLessEqual(abs(x - y), prec, message) - # E AssertionError: 1024 not less than or equal to 1e-05 : test_nn.TestNN.test_variable_sequence_cuda leaked 1024 bytes CUDA memory on device 0 - tests_to_skip+=('TestNN and test_variable_sequence_cuda') - - # 3.7_cu90 - # AssertionError: 1024 not less than or equal to 1e-05 : __main__.TestJit.test_fuse_last_device_cuda leaked 1024 bytes CUDA memory on device 1 - tests_to_skip+=('TestJit and test_fuse_last_device_cuda') - - # 3.7_cu92 x2 - # AssertionError: 1024 not less than or equal to 1e-05 : __main__.TestJit.test_ge_cuda leaked 1024 bytes CUDA memory on device 0 - tests_to_skip+=('TestJit and test_ge_cuda') - - # 3.5_cu90 - # AssertionError: 1024 not less than or equal to 1e-05 : test_jit.TestJit.test_comparison_ge_le_cuda leaked -1024 bytes CUDA memory on device 0 - tests_to_skip+=('TestJit and test_comparison_ge_le_cuda') - - # 3.6_cu92 - # 3.7_cu92 - # AssertionError: 1024 not less than or equal to 1e-05 : __main__.TestJit.test_relu_cuda leaked 1024 bytes CUDA memory on device 0 - tests_to_skip+=('TestJit and test_relu_cuda') - - # 3.7_cu92 x3 - # AssertionError: 1024 not less than or equal to 1e-05 : __main__.TestScript.test_milstm_fusion_cuda leaked 1024 bytes CUDA memory on device 1 - tests_to_skip+=('TestScript and test_milstm_fusion_cuda') -fi - - -############################################################################## -# MacOS specific flaky tests -############################################################################## - -if [[ "$(uname)" == 'Darwin' ]]; then - # TestCppExtensions by default uses a temp folder in /tmp. This doesn't - # work for this Mac machine cause there is only one machine and /tmp is - # shared. (All the linux builds are on docker so have their own /tmp). - tests_to_skip+=('TestCppExtension') -fi - -if [[ "$(uname)" == 'Darwin' && "$package_type" == 'conda' ]]; then - - # - # TestDistBackend - # Seems like either most of the Mac builds get this error or none of them - # do - # - - # Traceback (most recent call last): - # File "test_thd_distributed.py", line 1046, in wrapper - # self._join_and_reduce(fn) - # File "test_thd_distributed.py", line 1120, in _join_and_reduce - # first_process.exitcode == SKIP_IF_SMALL_WORLDSIZE_EXIT_CODE - # AssertionError - tests_to_skip+=('TestDistBackend and test_reduce_group_max') - - # Traceback (most recent call last): - # File "test_thd_distributed.py", line 1046, in wrapper - # self._join_and_reduce(fn) - # File "test_thd_distributed.py", line 1132, in _join_and_reduce - # self.assertEqual(first_process.exitcode, 0) - # File "/Users/administrator/nightlies/2018_10_01/wheel_build_dirs/conda_2.7/pytorch/test/common.py", line 397, in assertEqual - # super(TestCase, self).assertLessEqual(abs(x - y), prec, message) - # AssertionError: 1 not less than or equal to 1e-05 - tests_to_skip+=('TestDistBackend and test_isend') - tests_to_skip+=('TestDistBackend and test_reduce_group_min') - tests_to_skip+=('TestDistBackend and test_reduce_max') - tests_to_skip+=('TestDistBackend and test_reduce_min') - tests_to_skip+=('TestDistBackend and test_reduce_group_max') - tests_to_skip+=('TestDistBackend and test_reduce_group_min') - tests_to_skip+=('TestDistBackend and test_reduce_max') - tests_to_skip+=('TestDistBackend and test_reduce_min') - tests_to_skip+=('TestDistBackend and test_reduce_product') - tests_to_skip+=('TestDistBackend and test_reduce_sum') - tests_to_skip+=('TestDistBackend and test_scatter') - tests_to_skip+=('TestDistBackend and test_send_recv') - tests_to_skip+=('TestDistBackend and test_send_recv_any_source') -fi - - -# Turn the set of tests to skip into an invocation that pytest understands -excluded_tests_logic='' -for exclusion in "${tests_to_skip[@]}"; do - if [[ -z "$excluded_tests_logic" ]]; then - # Only true for i==0 - excluded_tests_logic="not ($exclusion)" - else - excluded_tests_logic="$excluded_tests_logic and not ($exclusion)" - fi -done - - -############################################################################## -# Run the tests -############################################################################## -echo -echo "$(date) :: Calling 'python test/run_test.py -v -p pytest ${entire_file_exclusions[@]} -- --disable-pytest-warnings -k '$excluded_tests_logic'" - -python test/run_test.py -v -p pytest ${entire_file_exclusions[@]} -- --disable-pytest-warnings -k "'" "$excluded_tests_logic" "'" - -echo -echo "$(date) :: Finished 'python test/run_test.py -v -p pytest ${entire_file_exclusions[@]} -- --disable-pytest-warnings -k '$excluded_tests_logic'" - -# cpp_extensions don't work with pytest, so we run them without pytest here, -# except there's a failure on CUDA builds (documented above), and -# cpp_extensions doesn't work on a shared mac machine (also documented above) -if [[ "$cuda_ver" == 'cpu' && "$(uname)" != 'Darwin' ]]; then - echo - echo "$(date) :: Calling 'python test/run_test.py -v -i cpp_extensions'" - python test/run_test.py -v -i cpp_extensions - echo - echo "$(date) :: Finished 'python test/run_test.py -v -i cpp_extensions'" -fi - -# thd_distributed can run on Mac but not in pytest -if [[ "$(uname)" == 'Darwin' ]]; then - echo - echo "$(date) :: Calling 'python test/run_test.py -v -i thd_distributed'" - python test/run_test.py -v -i thd_distributed - echo - echo "$(date) :: Finished 'python test/run_test.py -v -i thd_distributed'" -fi diff --git a/test/check_binary_symbols.py b/test/check_binary_symbols.py deleted file mode 100755 index dba11fbe9..000000000 --- a/test/check_binary_symbols.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python3 -import concurrent.futures -import distutils.sysconfig -import itertools -import functools -import os -import re -from pathlib import Path -from typing import Any, List, Tuple - -# We also check that there are [not] cxx11 symbols in libtorch -# -# To check whether it is using cxx11 ABI, check non-existence of symbol: -PRE_CXX11_SYMBOLS=( - "std::basic_string<", - "std::list", -) -# To check whether it is using pre-cxx11 ABI, check non-existence of symbol: -CXX11_SYMBOLS=( - "std::__cxx11::basic_string", - "std::__cxx11::list", -) -# NOTE: Checking the above symbols in all namespaces doesn't work, because -# devtoolset7 always produces some cxx11 symbols even if we build with old ABI, -# and CuDNN always has pre-cxx11 symbols even if we build with new ABI using gcc 5.4. -# Instead, we *only* check the above symbols in the following namespaces: -LIBTORCH_NAMESPACE_LIST=( - "c10::", - "at::", - "caffe2::", - "torch::", -) - - -def _apply_libtorch_symbols(symbols): - return [re.compile(f"{x}.*{y}") for (x,y) in itertools.product(LIBTORCH_NAMESPACE_LIST, symbols)] - - -LIBTORCH_CXX11_PATTERNS = _apply_libtorch_symbols(CXX11_SYMBOLS) - -LIBTORCH_PRE_CXX11_PATTERNS = _apply_libtorch_symbols(PRE_CXX11_SYMBOLS) - -@functools.lru_cache(100) -def get_symbols(lib :str ) -> List[Tuple[str, str, str]]: - from subprocess import check_output - lines = check_output(f'nm "{lib}"|c++filt', shell=True) - return [x.split(' ', 2) for x in lines.decode('latin1').split('\n')[:-1]] - - -def grep_symbols(lib: str, patterns: List[Any]) -> List[str]: - def _grep_symbols(symbols: List[Tuple[str, str, str]], patterns: List[Any]) -> List[str]: - rc = [] - for _s_addr, _s_type, s_name in symbols: - for pattern in patterns: - if pattern.match(s_name): - rc.append(s_name) - continue - return rc - all_symbols = get_symbols(lib) - num_workers= 32 - chunk_size = (len(all_symbols) + num_workers - 1 ) // num_workers - def _get_symbols_chunk(i): - return all_symbols[i * chunk_size : (i + 1) * chunk_size] - - with concurrent.futures.ThreadPoolExecutor(max_workers=32) as executor: - tasks = [executor.submit(_grep_symbols, _get_symbols_chunk(i), patterns) for i in range(num_workers)] - return functools.reduce(list.__add__, (x.result() for x in tasks), []) - - -def check_lib_symbols_for_abi_correctness(lib: str, pre_cxx11_abi: bool = True) -> None: - print(f"lib: {lib}") - cxx11_symbols = grep_symbols(lib, LIBTORCH_CXX11_PATTERNS) - pre_cxx11_symbols = grep_symbols(lib, LIBTORCH_PRE_CXX11_PATTERNS) - num_cxx11_symbols = len(cxx11_symbols) - num_pre_cxx11_symbols = len(pre_cxx11_symbols) - print(f"num_cxx11_symbols: {num_cxx11_symbols}") - print(f"num_pre_cxx11_symbols: {num_pre_cxx11_symbols}") - if pre_cxx11_abi: - if num_cxx11_symbols > 0: - raise RuntimeError(f"Found cxx11 symbols, but there shouldn't be any, see: {cxx11_symbols[:100]}") - if num_pre_cxx11_symbols < 1000: - raise RuntimeError("Didn't find enough pre-cxx11 symbols.") - # Check for no recursive iterators, regression test for https://github.com/pytorch/pytorch/issues/133437 - rec_iter_symbols = grep_symbols(lib, [re.compile("std::filesystem::recursive_directory_iterator.*")]) - if len(rec_iter_symbols) > 0: - raise RuntimeError(f"recursive_directory_iterator in used pre-CXX11 binaries, see; {rec_iter_symbols}") - else: - if num_pre_cxx11_symbols > 0: - raise RuntimeError(f"Found pre-cxx11 symbols, but there shouldn't be any, see: {pre_cxx11_symbols[:100]}") - if num_cxx11_symbols < 100: - raise RuntimeError("Didn't find enought cxx11 symbols") - - -def main() -> None: - if "install_root" in os.environ: - install_root = Path(os.getenv("install_root")) # noqa: SIM112 - else: - if os.getenv("PACKAGE_TYPE") == "libtorch": - install_root = Path(os.getcwd()) - else: - install_root = Path(distutils.sysconfig.get_python_lib()) / "torch" - - libtorch_cpu_path = install_root / "lib" / "libtorch_cpu.so" - pre_cxx11_abi = "cxx11-abi" not in os.getenv("DESIRED_DEVTOOLSET", "") - check_lib_symbols_for_abi_correctness(libtorch_cpu_path, pre_cxx11_abi) - - -if __name__ == "__main__": - main() diff --git a/test/smoke_test/assets/dog2.jpg b/test/smoke_test/assets/dog2.jpg deleted file mode 100644 index 528dfec7209cd3bbb415cf610a57b8955149d028..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90796 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 diff --git a/test/smoke_test/assets/rgb_pytorch.jpg b/test/smoke_test/assets/rgb_pytorch.jpg deleted file mode 100644 index d49e658b94ffb204533dc228d8a2a2f6b100637d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2126 zcmbW1dpy+X9>;%kVJ@a5qh>H}BPK-|w^3-eO_P5u!IZj zu(zSw03Z+mfTRPEi~`31xU4KpRt64(!4L>I5{2G|l9NMWcPh%GamwmyIAv8;4Q&Hm z4XyonRaJu7-u(v+jg5`fbA~dKnMf^C5OUn+lC=(s%jGdXGtCd3UI(1 z@P>f20I&iGq5zUS1=OYYlmY!F;NJp)Ay64v7#x9=lLl&c0ALUV0)|3lWS~%KI!d|^ zKow+mY8qO}D$+b*T9?tp*wg|z-nyzy$+=gkZR8aYhd}PyjlnAK(a|OB)iX9RH6xiH zI(D35V{1pXclq|@Df;Q}T+e%d|977Y7a4&;!6Bhx;Sv9cPe^24yPkCOR$6*S=1;e? z3hxx%y;pqyK}mJZ!`er6^$m?rI8UEFZ|``~+4rV@U~p*o?a27Vuai^LA7^HH3yVvi zMawH+#H(9eAOP|^mUR9d>@QpjQZ6tQ3W375xIo}gsUZqb8BIgkomMoM=Ve7LVk{hO zomx=UhQJ#+3zfVAdXc-djmP&aY@z)|_TPcU{a<8%fc=MS1VBMR(&9lB05TxnOd{li z|8PxY@%!06Tim3++zU+V$!pzja=b%k!j23usP`&Zg!(4xcL`|P2`dWv8_=KaaeZ7J zzLDA0UrUex(JKY9tku@#?2SsRQGSn*9e=fJiszaZ-MZHEn6q(q&0%vWe@-;p>zg%I z!SePu*gmVGMX+l7Q36=;BClFEzjWO5YE93!Z>quXLOcI-xqgXLarVY;-lee}5@7Ni zYqNze0jwF5CCjCyXZU7$7n_59AdG}>3h3G~WTPRS7G-(xOx19i5ksPQS5z%WoJj_^lhg5$vNKeEo*p`lI`sfBX`;lmGUqAS~zG2uI7I;GW zmnJ5I_gr;86G(j+VAXy?Xf*Hf%(%A=b*ppKEBS9t=W+;G-ING=UP{G8OHRbko7A}Y z(Wl>z9FCZe&Ju$=@(}tm5i&fgfU%KshvOliO($PySz-!e z#CDjA2vugs?EIJQU!DybH#}t8yO~#qEV6IFTzc3`Lt0H^V1DI&SJUjHW2SwEQ~!kN zt2;(sUSwZkTQj0@7vol}*PKhS5FbgFGXo4?+BVk0k#|>0k-S+Rv!3tow@M5Ds~h_C9lFae0?UMiTWAxdtDCe(`>M6~wwVHJ=lsn}8_G(CgnPZ}I_O%_<11zQ9fGYQW4X>@@T3;oE zpCpedt+Puw5}>2dJ$vMAo>Txlf~@LB->5C+il}au!Sn&EWA|HOw5jz(imc zG8^t=QIml%W~wOhKp0Y`novM=4`VypWd_Q{2N4pSihx$F=IV=_((J|M&<|J82H@qIZ9f&HnAhE<8iQnv%Z=#VAE3P(l=+~Yg*fU zji}zpnb#D#3=b(r3a7c$Y?YZ@#1ThiIxf*BP>d`g%}`w!VEo6VbuKW#>lxoARRl8Bv#BoxMc^&yAd&$Ci)DZ6#<;tj|S zBzI$NNuXAP2JShp#xAn@g%!+haOA1c41?^cXP+c| RJo$4w`r4NMvO*+p{{<4)%02)9 diff --git a/test/smoke_test/assets/rgb_pytorch.png b/test/smoke_test/assets/rgb_pytorch.png deleted file mode 100644 index c9d08e6c7da91991a780ded69d966fbd0c18eb5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 575 zcmV-F0>J%=P)mlEGZGj7-Nhv#u#IaF~&Ub z`}JAfg#UT3ZorwgrlOmy&ZeT3tmdYokF5TtqKT|6OhpG-yO@e{SsR&(T3LIUib7f2 znTje|JDQ3TSx-%UeE*C;Ugi2tTyNdf{F~uOlZ7=kb3iOQS&ODRAd+PmaMjNnZUjJ9 zlUR+5Lc--_C1A-ayjS3rUX``c4B&cG-3=31RsxEw^1%(M0Zvu|%SvEb2`nptWhJnz z1eTScX5Ns^HuHuz#(W(4^W5BYWUP<8T=~wzvHVw=eSveE- z@vmU*u$W9x_LNA6ouqO*$_do4iT5CcyX{v$JfuD{;{F;oStf}>w4t0GG0jzx?!IRz zQ-)W1Qwl#ZaW_~0ufxGg%Bmjxm&rPqx3c N002ovPDHLkV1i6h2Xz1d diff --git a/test/smoke_test/smoke_test.py b/test/smoke_test/smoke_test.py deleted file mode 100644 index d8ba7f8d4..000000000 --- a/test/smoke_test/smoke_test.py +++ /dev/null @@ -1,352 +0,0 @@ -import os -import re -import sys -import argparse -import torch -import json -import importlib -import subprocess -import torch._dynamo -import torch.nn as nn -import torch.nn.functional as F -from pathlib import Path - -if "MATRIX_GPU_ARCH_VERSION" in os.environ: - gpu_arch_ver = os.getenv("MATRIX_GPU_ARCH_VERSION") -else: - gpu_arch_ver = os.getenv("GPU_ARCH_VERSION") # Use fallback if available -gpu_arch_type = os.getenv("MATRIX_GPU_ARCH_TYPE") -channel = os.getenv("MATRIX_CHANNEL") -package_type = os.getenv("MATRIX_PACKAGE_TYPE") -target_os = os.getenv("TARGET_OS", sys.platform) -BASE_DIR = Path(__file__).parent.parent.parent - -is_cuda_system = gpu_arch_type == "cuda" -NIGHTLY_ALLOWED_DELTA = 3 - -MODULES = [ - { - "name": "torchvision", - "repo": "https://github.com/pytorch/vision.git", - "smoke_test": "./vision/test/smoke_test.py", - "extension": "extension", - "repo_name": "vision", - }, - { - "name": "torchaudio", - "repo": "https://github.com/pytorch/audio.git", - "smoke_test": "./audio/test/smoke_test/smoke_test.py --no-ffmpeg", - "extension": "_extension", - "repo_name": "audio", - }, -] - - -class Net(nn.Module): - def __init__(self): - super().__init__() - self.conv1 = nn.Conv2d(1, 32, 3, 1) - self.conv2 = nn.Conv2d(32, 64, 3, 1) - self.fc1 = nn.Linear(9216, 1) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - x = F.max_pool2d(x, 2) - x = torch.flatten(x, 1) - output = self.fc1(x) - return output - -def load_json_from_basedir(filename: str): - try: - with open(BASE_DIR / filename) as fptr: - return json.load(fptr) - except FileNotFoundError as exc: - raise ImportError(f"File {filename} not found error: {exc.strerror}") from exc - except json.JSONDecodeError as exc: - raise ImportError(f"Invalid JSON {filename}") from exc - -def read_release_matrix(): - return load_json_from_basedir("release_matrix.json") - -def test_numpy(): - import numpy as np - x = np.arange(5) - torch.tensor(x) - -def check_version(package: str) -> None: - release_version = os.getenv("RELEASE_VERSION") - # if release_version is specified, use it to validate the packages - if(release_version): - release_matrix = read_release_matrix() - stable_version = release_matrix["torch"] - else: - stable_version = os.getenv("MATRIX_STABLE_VERSION") - - # only makes sense to check nightly package where dates are known - if channel == "nightly": - check_nightly_binaries_date(package) - elif stable_version is not None: - if not torch.__version__.startswith(stable_version): - raise RuntimeError( - f"Torch version mismatch, expected {stable_version} for channel {channel}. But its {torch.__version__}" - ) - - if release_version and package == "all": - for module in MODULES: - imported_module = importlib.import_module(module["name"]) - module_version = imported_module.__version__ - if not module_version.startswith(release_matrix[module["name"]]): - raise RuntimeError( - f"{module['name']} version mismatch, expected: \ - {release_matrix[module['name']]} for channel {channel}. But its {module_version}" - ) - else: - print(f"{module['name']} version actual: {module_version} expected: \ - {release_matrix[module['name']]} for channel {channel}.") - - else: - print(f"Skip version check for channel {channel} as stable version is None") - - -def check_nightly_binaries_date(package: str) -> None: - from datetime import datetime - format_dt = '%Y%m%d' - - date_t_str = re.findall("dev\\d+", torch.__version__) - date_t_delta = datetime.now() - datetime.strptime(date_t_str[0][3:], format_dt) - if date_t_delta.days >= NIGHTLY_ALLOWED_DELTA: - raise RuntimeError( - f"the binaries are from {date_t_str} and are more than {NIGHTLY_ALLOWED_DELTA} days old!" - ) - - if package == "all": - for module in MODULES: - imported_module = importlib.import_module(module["name"]) - module_version = imported_module.__version__ - date_m_str = re.findall("dev\\d+", module_version) - date_m_delta = datetime.now() - datetime.strptime(date_m_str[0][3:], format_dt) - print(f"Nightly date check for {module['name']} version {module_version}") - if date_m_delta.days > NIGHTLY_ALLOWED_DELTA: - raise RuntimeError( - f"Expected {module['name']} to be less then {NIGHTLY_ALLOWED_DELTA} days. But its {date_m_delta}" - ) - - -def test_cuda_runtime_errors_captured() -> None: - cuda_exception_missed = True - try: - print("Testing test_cuda_runtime_errors_captured") - torch._assert_async(torch.tensor(0, device="cuda")) - torch._assert_async(torch.tensor(0 + 0j, device="cuda")) - except RuntimeError as e: - if re.search("CUDA", f"{e}"): - print(f"Caught CUDA exception with success: {e}") - cuda_exception_missed = False - else: - raise e - if cuda_exception_missed: - raise RuntimeError("Expected CUDA RuntimeError but have not received!") - - -def smoke_test_cuda(package: str, runtime_error_check: str, torch_compile_check: str) -> None: - if not torch.cuda.is_available() and is_cuda_system: - raise RuntimeError(f"Expected CUDA {gpu_arch_ver}. However CUDA is not loaded.") - - if package == 'all' and is_cuda_system: - for module in MODULES: - imported_module = importlib.import_module(module["name"]) - # TBD for vision move extension module to private so it will - # be _extention. - version = "N/A" - if module["extension"] == "extension": - version = imported_module.extension._check_cuda_version() - else: - version = imported_module._extension._check_cuda_version() - print(f"{module['name']} CUDA: {version}") - - # torch.compile is available on macos-arm64 and Linux for python 3.8-3.13 - if (torch_compile_check == "enabled" and sys.version_info < (3, 13, 0) - and target_os in ["linux", "linux-aarch64", "macos-arm64", "darwin"]): - smoke_test_compile("cuda" if torch.cuda.is_available() else "cpu") - - if torch.cuda.is_available(): - if torch.version.cuda != gpu_arch_ver: - raise RuntimeError( - f"Wrong CUDA version. Loaded: {torch.version.cuda} Expected: {gpu_arch_ver}" - ) - print(f"torch cuda: {torch.version.cuda}") - # todo add cudnn version validation - print(f"torch cudnn: {torch.backends.cudnn.version()}") - print(f"cuDNN enabled? {torch.backends.cudnn.enabled}") - - torch.cuda.init() - print("CUDA initialized successfully") - print(f"Number of CUDA devices: {torch.cuda.device_count()}") - for i in range(torch.cuda.device_count()): - print(f"Device {i}: {torch.cuda.get_device_name(i)}") - - # nccl is availbale only on Linux - if (sys.platform in ["linux", "linux2"]): - print(f"torch nccl version: {torch.cuda.nccl.version()}") - - if runtime_error_check == "enabled": - test_cuda_runtime_errors_captured() - - -def smoke_test_conv2d() -> None: - import torch.nn as nn - - print("Testing smoke_test_conv2d") - # With square kernels and equal stride - m = nn.Conv2d(16, 33, 3, stride=2) - # non-square kernels and unequal stride and with padding - m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2)) - assert m is not None - # non-square kernels and unequal stride and with padding and dilation - basic_conv = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1)) - input = torch.randn(20, 16, 50, 100) - output = basic_conv(input) - - if is_cuda_system: - print("Testing smoke_test_conv2d with cuda") - conv = nn.Conv2d(3, 3, 3).cuda() - x = torch.randn(1, 3, 24, 24, device="cuda") - with torch.cuda.amp.autocast(): - out = conv(x) - assert out is not None - - supported_dtypes = [torch.float16, torch.float32, torch.float64] - for dtype in supported_dtypes: - print(f"Testing smoke_test_conv2d with cuda for {dtype}") - conv = basic_conv.to(dtype).cuda() - input = torch.randn(20, 16, 50, 100, device="cuda").type(dtype) - output = conv(input) - assert output is not None - - -def test_linalg(device="cpu") -> None: - print(f"Testing smoke_test_linalg on {device}") - A = torch.randn(5, 3, device=device) - U, S, Vh = torch.linalg.svd(A, full_matrices=False) - assert U.shape == A.shape and S.shape == torch.Size([3]) and Vh.shape == torch.Size([3, 3]) - torch.dist(A, U @ torch.diag(S) @ Vh) - - U, S, Vh = torch.linalg.svd(A) - assert U.shape == torch.Size([5, 5]) and S.shape == torch.Size([3]) and Vh.shape == torch.Size([3, 3]) - torch.dist(A, U[:, :3] @ torch.diag(S) @ Vh) - - A = torch.randn(7, 5, 3, device=device) - U, S, Vh = torch.linalg.svd(A, full_matrices=False) - torch.dist(A, U @ torch.diag_embed(S) @ Vh) - - if device == "cuda": - supported_dtypes = [torch.float32, torch.float64] - for dtype in supported_dtypes: - print(f"Testing smoke_test_linalg with cuda for {dtype}") - A = torch.randn(20, 16, 50, 100, device=device, dtype=dtype) - torch.linalg.svd(A) - - -def smoke_test_compile(device: str = "cpu") -> None: - supported_dtypes = [torch.float16, torch.float32, torch.float64] - - def foo(x: torch.Tensor) -> torch.Tensor: - return torch.sin(x) + torch.cos(x) - - for dtype in supported_dtypes: - print(f"Testing smoke_test_compile for {device} and {dtype}") - x = torch.rand(3, 3, device=device).type(dtype) - x_eager = foo(x) - x_pt2 = torch.compile(foo)(x) - torch.testing.assert_close(x_eager, x_pt2) - - # Check that SIMD were detected for the architecture - if device == "cpu": - from torch._inductor.codecache import pick_vec_isa - isa = pick_vec_isa() - if not isa: - raise RuntimeError("Can't detect vectorized ISA for CPU") - print(f"Picked CPU ISA {type(isa).__name__} bit width {isa.bit_width()}") - - # Reset torch dynamo since we are changing mode - torch._dynamo.reset() - dtype = torch.float32 - torch.set_float32_matmul_precision('high') - print(f"Testing smoke_test_compile with mode 'max-autotune' for {dtype}") - x = torch.rand(64, 1, 28, 28, device=device).type(torch.float32) - model = Net().to(device=device) - x_pt2 = torch.compile(model, mode="max-autotune")(x) - - -def smoke_test_modules(): - cwd = os.getcwd() - for module in MODULES: - if module["repo"]: - if not os.path.exists(f"{cwd}/{module['repo_name']}"): - print(f"Path does not exist: {cwd}/{module['repo_name']}") - try: - subprocess.check_output( - f"git clone --depth 1 {module['repo']}", - stderr=subprocess.STDOUT, - shell=True, - ) - except subprocess.CalledProcessError as exc: - raise RuntimeError( - f"Cloning {module['repo']} FAIL: {exc.returncode} Output: {exc.output}" - ) from exc - try: - smoke_test_command = f"python3 {module['smoke_test']}" - if target_os == 'windows': - smoke_test_command = f"python {module['smoke_test']}" - output = subprocess.check_output( - smoke_test_command, stderr=subprocess.STDOUT, shell=True, - universal_newlines=True) - except subprocess.CalledProcessError as exc: - raise RuntimeError(f"Module {module['name']} FAIL: {exc.returncode} Output: {exc.output}") from exc - else: - print(f"Output: \n{output}\n") - - -def main() -> None: - parser = argparse.ArgumentParser() - parser.add_argument( - "--package", - help="Package to include in smoke testing", - type=str, - choices=["all", "torchonly"], - default="all", - ) - parser.add_argument( - "--runtime-error-check", - help="No Runtime Error check", - type=str, - choices=["enabled", "disabled"], - default="enabled", - ) - parser.add_argument( - "--torch-compile-check", - help="Check torch compile", - type=str, - choices=["enabled", "disabled"], - default="enabled", - ) - options = parser.parse_args() - print(f"torch: {torch.__version__}") - print(torch.__config__.parallel_info()) - - check_version(options.package) - smoke_test_conv2d() - test_linalg() - test_numpy() - if is_cuda_system: - test_linalg("cuda") - - if options.package == "all": - smoke_test_modules() - - smoke_test_cuda(options.package, options.runtime_error_check, options.torch_compile_check) - - -if __name__ == "__main__": - main() diff --git a/test_example_code/CMakeLists.txt b/test_example_code/CMakeLists.txt deleted file mode 100644 index 1724a6ed0..000000000 --- a/test_example_code/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required(VERSION 3.0 FATAL_ERROR) -project(simple-torch-test) - -find_package(Torch REQUIRED) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}") - - -add_executable(simple-torch-test simple-torch-test.cpp) -target_include_directories(simple-torch-test PRIVATE ${TORCH_INCLUDE_DIRS}) -target_link_libraries(simple-torch-test "${TORCH_LIBRARIES}") -set_property(TARGET simple-torch-test PROPERTY CXX_STANDARD 17) - -find_package(CUDAToolkit 11.8) - -target_link_libraries(simple-torch-test CUDA::cudart CUDA::cufft CUDA::cusparse CUDA::cublas CUDA::cusolver) -find_library(CUDNN_LIBRARY NAMES cudnn) -target_link_libraries(simple-torch-test ${CUDNN_LIBRARY} ) -if (MSVC) - file(GLOB TORCH_DLLS "$ENV{CUDA_PATH}/bin/cudnn64_8.dll" "$ENV{NVTOOLSEXT_PATH}/bin/x64/*.dll") - message("dlls to copy " ${TORCH_DLLS}) - add_custom_command(TARGET simple-torch-test - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${TORCH_DLLS} - $) -endif (MSVC) diff --git a/test_example_code/check-torch-cuda.cpp b/test_example_code/check-torch-cuda.cpp deleted file mode 100644 index 35e9f8ebc..000000000 --- a/test_example_code/check-torch-cuda.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -int main(int argc, const char* argv[]) { - std::cout << "Checking that CUDA archs are setup correctly" << std::endl; - TORCH_CHECK(torch::rand({ 3, 5 }, torch::Device(torch::kCUDA)).defined(), "CUDA archs are not setup correctly"); - - // These have to run after CUDA is initialized - - std::cout << "Checking that magma is available" << std::endl; - TORCH_CHECK(torch::hasMAGMA(), "MAGMA is not available"); - - std::cout << "Checking that CuDNN is available" << std::endl; - TORCH_CHECK(torch::cuda::cudnn_is_available(), "CuDNN is not available"); - return 0; -} diff --git a/test_example_code/check-torch-mkl.cpp b/test_example_code/check-torch-mkl.cpp deleted file mode 100644 index 419898d79..000000000 --- a/test_example_code/check-torch-mkl.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(int argc, const char* argv[]) { - TORCH_CHECK(torch::hasMKL(), "MKL is not available"); - return 0; -} diff --git a/test_example_code/check-torch-xnnpack.cpp b/test_example_code/check-torch-xnnpack.cpp deleted file mode 100644 index b0e901954..000000000 --- a/test_example_code/check-torch-xnnpack.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -int main(int argc, const char* argv[]) { - TORCH_CHECK(at::globalContext().isXNNPACKAvailable(), "XNNPACK is not available"); - return 0; -} diff --git a/test_example_code/cnn_smoke.py b/test_example_code/cnn_smoke.py deleted file mode 100644 index 3269a2b74..000000000 --- a/test_example_code/cnn_smoke.py +++ /dev/null @@ -1,36 +0,0 @@ -r""" -It's used to check basic rnn features with cuda. -For example, it would throw exception if some components are missing -""" - -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.optim as optim - -class SimpleCNN(nn.Module): - def __init__(self): - super().__init__() - self.conv = nn.Conv2d(1, 1, 3) - self.pool = nn.MaxPool2d(2, 2) - - def forward(self, inputs): - output = self.pool(F.relu(self.conv(inputs))) - output = output.view(1) - return output - -# Mock one infer -device = torch.device("cuda:0") -net = SimpleCNN().to(device) -net_inputs = torch.rand((1, 1, 5, 5), device=device) -outputs = net(net_inputs) -print(outputs) - -criterion = nn.MSELoss() -optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.1) - -# Mock one step training -label = torch.full((1,), 1.0, dtype=torch.float, device=device) -loss = criterion(outputs, label) -loss.backward() -optimizer.step() diff --git a/test_example_code/rnn_smoke.py b/test_example_code/rnn_smoke.py deleted file mode 100644 index 5965b30ea..000000000 --- a/test_example_code/rnn_smoke.py +++ /dev/null @@ -1,13 +0,0 @@ -r""" -It's used to check basic rnn features with cuda. -For example, it would throw exception if missing some components are missing -""" - -import torch -import torch.nn as nn - -device = torch.device("cuda:0") -rnn = nn.RNN(10, 20, 2).to(device) -inputs = torch.randn(5, 3, 10).to(device) -h0 = torch.randn(2, 3, 20).to(device) -output, hn = rnn(inputs, h0) diff --git a/test_example_code/simple-torch-test.cpp b/test_example_code/simple-torch-test.cpp deleted file mode 100644 index 1b9453fe6..000000000 --- a/test_example_code/simple-torch-test.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(int argc, const char* argv[]) { - TORCH_WARN("Simple test passed!"); - return 0; -} diff --git a/wheel/build_wheel.sh b/wheel/build_wheel.sh deleted file mode 100755 index d91b29be0..000000000 --- a/wheel/build_wheel.sh +++ /dev/null @@ -1,278 +0,0 @@ -#!/usr/bin/env bash -set -ex -SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" - -# Env variables that should be set: -# DESIRED_PYTHON -# Which Python version to build for in format 'Maj.min' e.g. '2.7' or '3.6' -# -# PYTORCH_FINAL_PACKAGE_DIR -# **absolute** path to folder where final whl packages will be stored. The -# default should not be used when calling this from a script. The default -# is 'whl', and corresponds to the default in the wheel/upload.sh script. -# -# MAC_PACKAGE_WORK_DIR -# absolute path to a workdir in which to clone an isolated conda -# installation and pytorch checkout. If the pytorch checkout already exists -# then it will not be overwritten. - -# Function to retry functions that sometimes timeout or have flaky failures -retry () { - $* || (sleep 1 && $*) || (sleep 2 && $*) || (sleep 4 && $*) || (sleep 8 && $*) -} - -# Parameters -if [[ -n "$DESIRED_PYTHON" && -n "$PYTORCH_BUILD_VERSION" && -n "$PYTORCH_BUILD_NUMBER" ]]; then - desired_python="$DESIRED_PYTHON" - build_version="$PYTORCH_BUILD_VERSION" - build_number="$PYTORCH_BUILD_NUMBER" -else - if [ "$#" -ne 3 ]; then - echo "illegal number of parameters. Need PY_VERSION BUILD_VERSION BUILD_NUMBER" - echo "for example: build_wheel.sh 2.7 0.1.6 20" - echo "Python version should be in format 'M.m'" - exit 1 - fi - desired_python=$1 - build_version=$2 - build_number=$3 -fi - -echo "Building for Python: $desired_python Version: $build_version Build: $build_number" -python_nodot="$(echo $desired_python | tr -d m.u)" - -# Version: setup.py uses $PYTORCH_BUILD_VERSION.post$PYTORCH_BUILD_NUMBER if -# PYTORCH_BUILD_NUMBER > 1 -if [[ -n "$OVERRIDE_PACKAGE_VERSION" ]]; then - # This will be the *exact* version, since build_number<1 - build_version="$OVERRIDE_PACKAGE_VERSION" - build_number=0 - build_number_prefix='' -else - if [[ $build_number -eq 1 ]]; then - build_number_prefix="" - else - build_number_prefix=".post$build_number" - fi -fi -export PYTORCH_BUILD_VERSION=$build_version -export PYTORCH_BUILD_NUMBER=$build_number - -package_type="${PACKAGE_TYPE:-wheel}" -# Fill in empty parameters with defaults -if [[ -z "$TORCH_PACKAGE_NAME" ]]; then - TORCH_PACKAGE_NAME='torch' -fi -TORCH_PACKAGE_NAME="$(echo $TORCH_PACKAGE_NAME | tr '-' '_')" -if [[ -z "$PYTORCH_REPO" ]]; then - PYTORCH_REPO='pytorch' -fi -if [[ -z "$PYTORCH_BRANCH" ]]; then - PYTORCH_BRANCH="v${build_version}" -fi -if [[ -z "$RUN_TEST_PARAMS" ]]; then - RUN_TEST_PARAMS=() -fi -if [[ -z "$PYTORCH_FINAL_PACKAGE_DIR" ]]; then - if [[ -n "$BUILD_PYTHONLESS" ]]; then - PYTORCH_FINAL_PACKAGE_DIR='libtorch' - else - PYTORCH_FINAL_PACKAGE_DIR='whl' - fi -fi -mkdir -p "$PYTORCH_FINAL_PACKAGE_DIR" || true - -# Create an isolated directory to store this builds pytorch checkout and conda -# installation -if [[ -z "$MAC_PACKAGE_WORK_DIR" ]]; then - MAC_PACKAGE_WORK_DIR="$(pwd)/tmp_wheel_conda_${DESIRED_PYTHON}_$(date +%H%M%S)" -fi -mkdir -p "$MAC_PACKAGE_WORK_DIR" || true -if [[ -n ${GITHUB_ACTIONS} ]]; then - pytorch_rootdir="${PYTORCH_ROOT:-${MAC_PACKAGE_WORK_DIR}/pytorch}" -else - pytorch_rootdir="${MAC_PACKAGE_WORK_DIR}/pytorch" -fi -whl_tmp_dir="${MAC_PACKAGE_WORK_DIR}/dist" -mkdir -p "$whl_tmp_dir" - -mac_version='macosx_11_0_arm64' -libtorch_arch='arm64' - -# Create a consistent wheel package name to rename the wheel to -wheel_filename_new="${TORCH_PACKAGE_NAME}-${build_version}${build_number_prefix}-cp${python_nodot}-none-${mac_version}.whl" - -########################################################### - -# Have a separate Pytorch repo clone -if [[ ! -d "$pytorch_rootdir" ]]; then - git clone "https://github.com/${PYTORCH_REPO}/pytorch" "$pytorch_rootdir" - pushd "$pytorch_rootdir" - if ! git checkout "$PYTORCH_BRANCH" ; then - echo "Could not checkout $PYTORCH_BRANCH, so trying tags/v${build_version}" - git checkout tags/v${build_version} - fi - popd -fi -pushd "$pytorch_rootdir" -git submodule update --init --recursive -popd - -########################## -# now build the binary - - -export TH_BINARY_BUILD=1 -export INSTALL_TEST=0 # dont install test binaries into site-packages -export MACOSX_DEPLOYMENT_TARGET=10.15 -export CMAKE_PREFIX_PATH=${CONDA_PREFIX:-"$(dirname $(which conda))/../"} - -SETUPTOOLS_PINNED_VERSION="=46.0.0" -PYYAML_PINNED_VERSION="=5.3" -EXTRA_CONDA_INSTALL_FLAGS="" -case $desired_python in - 3.13) - echo "Using 3.13 deps" - SETUPTOOLS_PINNED_VERSION=">=68.0.0" - PYYAML_PINNED_VERSION=">=6.0.1" - NUMPY_PINNED_VERSION="=2.1.0" - ;; - 3.12) - echo "Using 3.12 deps" - SETUPTOOLS_PINNED_VERSION=">=68.0.0" - PYYAML_PINNED_VERSION=">=6.0.1" - NUMPY_PINNED_VERSION="=2.0.2" - ;; - 3.11) - echo "Using 3.11 deps" - SETUPTOOLS_PINNED_VERSION=">=46.0.0" - PYYAML_PINNED_VERSION=">=5.3" - NUMPY_PINNED_VERSION="=2.0.2" - ;; - 3.10) - echo "Using 3.10 deps" - SETUPTOOLS_PINNED_VERSION=">=46.0.0" - PYYAML_PINNED_VERSION=">=5.3" - NUMPY_PINNED_VERSION="=2.0.2" - ;; - 3.9) - echo "Using 3.9 deps" - SETUPTOOLS_PINNED_VERSION=">=46.0.0" - PYYAML_PINNED_VERSION=">=5.3" - NUMPY_PINNED_VERSION="=2.0.2" - ;; - *) - echo "Using default deps" - NUMPY_PINNED_VERSION="=1.11.3" - ;; -esac - -# Install into a fresh env -tmp_env_name="wheel_py$python_nodot" -conda create ${EXTRA_CONDA_INSTALL_FLAGS} -yn "$tmp_env_name" python="$desired_python" -source activate "$tmp_env_name" - -pip install -q "numpy=${NUMPY_PINNED_VERSION}" "pyyaml${PYYAML_PINNED_VERSION}" requests -retry conda install ${EXTRA_CONDA_INSTALL_FLAGS} -yq llvm-openmp=14.0.6 cmake ninja "setuptools${SETUPTOOLS_PINNED_VERSION}" typing_extensions -retry pip install -qr "${pytorch_rootdir}/requirements.txt" || true - -# For USE_DISTRIBUTED=1 on macOS, need libuv and pkg-config to find libuv. -export USE_DISTRIBUTED=1 -retry conda install ${EXTRA_CONDA_INSTALL_FLAGS} -yq libuv pkg-config - -if [[ -n "$CROSS_COMPILE_ARM64" ]]; then - export CMAKE_OSX_ARCHITECTURES=arm64 -fi -export USE_MKLDNN=OFF -export USE_QNNPACK=OFF -export BUILD_TEST=OFF - -pushd "$pytorch_rootdir" -echo "Calling setup.py bdist_wheel at $(date)" - -if [[ "$USE_SPLIT_BUILD" == "true" ]]; then - echo "Calling setup.py bdist_wheel for split build (BUILD_LIBTORCH_WHL)" - BUILD_LIBTORCH_WHL=1 BUILD_PYTHON_ONLY=0 python setup.py bdist_wheel -d "$whl_tmp_dir" - echo "Finished setup.py bdist_wheel for split build (BUILD_LIBTORCH_WHL)" - echo "Calling setup.py bdist_wheel for split build (BUILD_PYTHON_ONLY)" - BUILD_PYTHON_ONLY=1 BUILD_LIBTORCH_WHL=0 python setup.py bdist_wheel -d "$whl_tmp_dir" --cmake - echo "Finished setup.py bdist_wheel for split build (BUILD_PYTHON_ONLY)" -else - python setup.py bdist_wheel -d "$whl_tmp_dir" -fi - -echo "Finished setup.py bdist_wheel at $(date)" - -if [[ $package_type != 'libtorch' ]]; then - echo "delocating wheel dependencies" - retry pip install https://github.com/matthew-brett/delocate/archive/refs/tags/0.10.4.zip - echo "found the following wheels:" - find $whl_tmp_dir -name "*.whl" - echo "running delocate" - find $whl_tmp_dir -name "*.whl" | xargs -I {} delocate-wheel -v {} - find $whl_tmp_dir -name "*.whl" - find $whl_tmp_dir -name "*.whl" | xargs -I {} delocate-listdeps {} - echo "Finished delocating wheels at $(date)" -fi - -echo "The wheel is in $(find $whl_tmp_dir -name '*.whl')" - -wheel_filename_gen=$(find $whl_tmp_dir -name '*.whl' | head -n1 | xargs -I {} basename {}) -popd - -if [[ -z "$BUILD_PYTHONLESS" ]]; then - # Copy the whl to a final destination before tests are run - echo "Renaming Wheel file: $wheel_filename_gen to $wheel_filename_new" - cp "$whl_tmp_dir/$wheel_filename_gen" "$PYTORCH_FINAL_PACKAGE_DIR/$wheel_filename_new" - - ########################## - # now test the binary, unless it's cross compiled arm64 - if [[ -z "$CROSS_COMPILE_ARM64" ]]; then - pip uninstall -y "$TORCH_PACKAGE_NAME" || true - pip uninstall -y "$TORCH_PACKAGE_NAME" || true - - # Create new "clean" conda environment for testing - conda create ${EXTRA_CONDA_INSTALL_FLAGS} -yn "test_conda_env" python="$desired_python" - conda activate test_conda_env - - pip install "$PYTORCH_FINAL_PACKAGE_DIR/$wheel_filename_new" -v - - echo "$(date) :: Running tests" - pushd "$pytorch_rootdir" - "${SOURCE_DIR}/../run_tests.sh" 'wheel' "$desired_python" 'cpu' - popd - echo "$(date) :: Finished tests" - fi -else - pushd "$pytorch_rootdir" - - mkdir -p libtorch/{lib,bin,include,share} - cp -r "$(pwd)/build/lib" "$(pwd)/libtorch/" - - # for now, the headers for the libtorch package will just be - # copied in from the wheel - unzip -d any_wheel "$whl_tmp_dir/$wheel_filename_gen" - if [[ -d $(pwd)/any_wheel/torch/include ]]; then - cp -r "$(pwd)/any_wheel/torch/include" "$(pwd)/libtorch/" - else - cp -r "$(pwd)/any_wheel/torch/lib/include" "$(pwd)/libtorch/" - fi - cp -r "$(pwd)/any_wheel/torch/share/cmake" "$(pwd)/libtorch/share/" - if [[ "${libtorch_arch}" == "x86_64" ]]; then - if [[ -x "$(pwd)/any_wheel/torch/.dylibs/libiomp5.dylib" ]]; then - cp -r "$(pwd)/any_wheel/torch/.dylibs/libiomp5.dylib" "$(pwd)/libtorch/lib/" - else - cp -r "$(pwd)/any_wheel/torch/lib/libiomp5.dylib" "$(pwd)/libtorch/lib/" - fi - else - cp -r "$(pwd)/any_wheel/torch/lib/libomp.dylib" "$(pwd)/libtorch/lib/" - fi - rm -rf "$(pwd)/any_wheel" - - echo $PYTORCH_BUILD_VERSION > libtorch/build-version - echo "$(pushd $pytorch_rootdir && git rev-parse HEAD)" > libtorch/build-hash - - zip -rq "$PYTORCH_FINAL_PACKAGE_DIR/libtorch-macos-${libtorch_arch}-$PYTORCH_BUILD_VERSION.zip" libtorch - cp "$PYTORCH_FINAL_PACKAGE_DIR/libtorch-macos-${libtorch_arch}-$PYTORCH_BUILD_VERSION.zip" \ - "$PYTORCH_FINAL_PACKAGE_DIR/libtorch-macos-${libtorch_arch}-latest.zip" -fi