diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 000000000..a4e7594b9 --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,24 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Summary: config actionlint (https://github.com/rhysd/actionlint) for certain +# things we use. + +self-hosted-runner: + # We don't have self-hosted runners, but we do use some of the "partner" + # runner images from https://github.com/actions/partner-runner-images + # and some runners that actionlint doesn't currently recognize. + labels: + - ubuntu-24.04-arm + - ubuntu-slim diff --git a/.github/actions/set-up-bazel/action.yaml b/.github/actions/set-up-bazel/action.yaml new file mode 100644 index 000000000..acde37f61 --- /dev/null +++ b/.github/actions/set-up-bazel/action.yaml @@ -0,0 +1,107 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Set up Bazel with caching +description: Installs Bazel and sets up multiple caches + +# Summary: reusable workflow to install Bazelisk (for Bazel) and set up Bazel +# caches. It grew out of trouble getting the existing bazel-contrib/setup-bazel +# action (from the GitHub Marketplace) to work with Netkos' "act". + +inputs: + debug: + description: 'Run with debugging options' + type: boolean + required: false + default: true + bazel-version: + description: 'Version of Bazel to use:' + type: string + required: false + default: '' + +permissions: + actions: write + contents: read + +runs: + using: 'composite' + steps: + - name: Determine the version of Bazel to use + shell: bash + env: + SHELLOPTS: ${{(inputs.debug || runner.debug) && 'xtrace' || ''}} + run: | + if [[ -n "${{inputs.bazel-version}}" ]]; then + version="${{inputs.bazel-version}}" + elif [[ -f ".bazelversion" ]]; then + version="$(tr -d ' \n\r' <.bazelversion)" + else + echo "::error::Bazel version has not been specified." + exit 1 + fi + # This variable is read by Bazelisk. + echo "USE_BAZEL_VERSION=${version}" >> "$GITHUB_ENV" + + - name: Install Bazelisk + env: + BAZELISK_SHOW_PROGRESS: ${{inputs.debug || runner.debug}} + shell: bash + run: npm install @bazel/bazelisk + + # This cache stores copies of Bazel downloaded by Bazelisk. The copies are + # stored in subdirs coded by hash numbers, and thus this cache can be + # shared between workflows. + - name: Set up cache for Bazelisk + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.cache/bazelisk + key: bazelisk-cache + + # This cache stores downloaded files (like .zip, .tar.gz) specified by + # repository rules like http_archive. This can be shared between workflows. + - name: Set up Bazel repository cache + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.cache/bazel/repository_cache + key: bazel-repo-${{env.USE_BAZEL_VERSION}} + restore-keys: bazel-repo- + + # This cache stores the compiled outputs of build actions, such as object + # files (.o), linked libraries (.so), binaries, & test results. + - name: Set up Bazel disk cache + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.cache/bazel/disk_cache + # yamllint disable-line rule:line-length + key: bazel-disk-${{runner.os}}-${{runner.arch}}-${{github.ref}}-${{hashFiles('**/WORKSPACE', '**/BUILD', '**/BUILD.tpl', '**/*.bzl')}} + restore-keys: bazel-disk-${{runner.os}}-${{runner.arch}}-${{github.ref}}- + + - name: Add configuration settings to .bazelrc + shell: bash + env: + SHELLOPTS: ${{(inputs.debug || runner.debug) && 'xtrace' || ''}} + run: | + if [[ "${{startsWith(runner.os, 'win')}}" == "true" ]]; then + num_cpus=${NUMBER_OF_PROCESSORS} + else + num_cpus=$(getconf _NPROCESSORS_ONLN) + fi + { + echo "startup --output_base=~/.cache/bazel/output_base" + echo "build --disk_cache=~/.cache/bazel/disk_cache" + echo "build --repository_cache=~/.cache/bazel/repository_cache" + echo "build --jobs=${num_cpus}" + echo "test --cache_test_results=no" + } >> .bazelrc diff --git a/.github/problem-matchers/black.json b/.github/problem-matchers/black.json index a0a981e43..639f1b40b 100644 --- a/.github/problem-matchers/black.json +++ b/.github/problem-matchers/black.json @@ -2,12 +2,12 @@ "problemMatcher": [ { "owner": "black", - "severity": "error", + "severity": "warning", "pattern": [ { - "regexp": "^would reformat (.+)$", - "file": 1, - "message": "File needs reformatting." + "regexp": "^(would reformat\\s(.*))$", + "message": 1, + "file": 2 } ] } diff --git a/.github/problem-matchers/hadolint.json b/.github/problem-matchers/hadolint.json index e9d5333c2..ce93d8d01 100644 --- a/.github/problem-matchers/hadolint.json +++ b/.github/problem-matchers/hadolint.json @@ -4,10 +4,12 @@ "owner": "hadolint", "pattern": [ { - "regexp": "^(.+?):(\\d+)\\s+(.*)$", + "regexp": "^(.*?):(\\d+) (DL\\d+|SC\\d+) (warning|error|info|style): (.*)$", "file": 1, "line": 2, - "message": 3 + "code": 3, + "severity": 4, + "message": 5 } ] } diff --git a/.github/workflows/_build_wheels.yaml b/.github/workflows/_build_wheels.yaml deleted file mode 100644 index a33967d0a..000000000 --- a/.github/workflows/_build_wheels.yaml +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Reusable workflow called by other workflows. (It never triggers on its own.) -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# The tilde is only to make our reusable workflows be listed last in the UI. -name: '~ Build & test Python wheels' -run-name: Build Python wheels and test that they work - -on: - workflow_call: - inputs: - upload: - description: 'Upload wheels to GitHub' - type: boolean - default: false - debug: - description: 'Run with debugging options' - type: boolean - default: false - - workflow_dispatch: - inputs: - upload: - description: 'Upload wheels to GitHub' - type: boolean - default: false - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -jobs: - build-wheels: - name: Build on ${{matrix.conf.os}}/${{matrix.conf.arch}} - runs-on: ${{matrix.conf.os}} - timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - conf: [ - {os: ubuntu-24.04, arch: x86_64}, - {os: macos-14, arch: arm64}, - {os: macos-15, arch: arm64}, - {os: windows-2025, arch: AMD64}, - ] - steps: - - name: Check out a copy of the git repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 1 - submodules: recursive - - - name: Set up Python with caching of pip dependencies - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 - with: - python-version: '3.12' - cache: pip - cache-dependency-path: | - requirements.txt - dev-requirements.txt - - - name: Install Python requirements - run: | - pip install -r requirements.txt - pip install -r dev-requirements.txt - pip install cibuildwheel==2.23.3 - - - if: startsWith(matrix.conf.os, 'macos') - name: Set CMake MACOSX_DEPLOYMENT_TARGET value on MacOS - run: | - set -x - os=${{matrix.conf.os}} - echo MACOSX_DEPLOYMENT_TARGET=${os: -2} >> "$GITHUB_ENV" - - - if: startsWith(matrix.conf.os, 'ubuntu') - name: Determine the number of threads to use (Linux) - run: echo "num_threads=$(( $(nproc) - 1 ))" >> "$GITHUB_ENV" - - - if: startsWith(matrix.conf.os, 'macos') - name: Determine the number of threads to use (MacOS) - run: echo "num_threads=$(( $(sysctl -n hw.ncpu) - 1 ))" >> "$GITHUB_ENV" - - - if: startsWith(matrix.conf.os, 'win') - name: Determine the number of threads to use (Windows) - shell: bash - run: echo "num_threads=$(( NUMBER_OF_PROCESSORS - 1 ))" >> "$GITHUB_ENV" - - - name: Build and test wheels - env: - # Note: additional cibuildwheel settings are in pyproject.toml. - CIBW_BUILD: 'cp310* cp311* cp312* cp313*' - CIBW_ARCHS: ${{matrix.conf.arch}} - CIBW_BUILD_VERBOSITY: ${{inputs.debug && 1 || ''}} - # Color codes make the raw logs hard to read. (CMake uses CLICOLOR.) - CLICOLOR: ${{inputs.debug && 0 || ''}} - CMAKE_BUILD_PARALLEL_LEVEL: ${{env.num_threads}} - run: | - cibuildwheel --output-dir wheelhouse - - - if: inputs.upload != false - name: Upload wheels to GitHub - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - with: - name: python-wheels-${{matrix.conf.os}}-${{matrix.conf.arch}} - path: ./wheelhouse/*.whl diff --git a/.github/workflows/_find_changes.yaml b/.github/workflows/_find_changes.yaml deleted file mode 100644 index 177de8cdb..000000000 --- a/.github/workflows/_find_changes.yaml +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Explanation for why file changes are tested using this workflow. -# -# GitHub Actions workflows path filters (i.e., adding "paths:" keywords to -# event triggers) would be the natural way to trigger workflows only when -# relevant files are changed in a PR – except that the way GitHub branch -# protection rules work is: "If a workflow is skipped due to path filtering -# [...] the checks associated with that workflow will remain in a Pending -# state. A PR that requires those checks to be successful will be blocked from -# merging." This makes path filters unusable with merge queues. So, we forgo the -# use of path filters and instead check file changes using our own workflow. -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# The tilde is only to make our reusable workflows be listed last in the UI. -name: '~ Find changed files' -run-name: Determine which files have been changed - -on: - workflow_call: - # N.B.: GitHub Actions workflow_call output values are ALWAYS strings, even - # for (what you think are) Booleans. Consequently, calling workflows have to - # test values using, e.g., ${{foo = 'false'}}, and not ${{foo}} or similar. - outputs: - code: - description: 'True if any potential code file was changed' - value: ${{jobs.test.outputs.nondoc-file-changes == 'true'}} - python: - description: 'True if any Python file was changed' - value: ${{jobs.test.outputs.python-changes == 'true'}} - -permissions: read-all - -jobs: - test: - name: Inspect changes - runs-on: ubuntu-24.04 - timeout-minutes: 5 - outputs: - nondoc-file-changes: ${{steps.nondoc-files.outputs.matched}} - python-changes: ${{steps.python-files.outputs.matched}} - steps: - - name: Check out a copy of the git repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Test whether the changes involved one or more non-doc files - uses: tomi/paths-filter-action@32c62f5ca100c1110406e3477d5b3ecef4666fec # v3.0.2 - id: nondoc-files - with: - base: ${{github.ref_name}} - # Note: when editing the patterns below, always make sure to negate - # the condition (i.e., use a leading '!'). See the next comment. - filters: | - matched: - - '!.github/ISSUE_TEMPLATE/**' - - '!.github/dependabot.yaml' - - '!.github/problem-matchers/**' - - '!CITATION.cff' - - '!docs/**' - - '!**/*.md' - - '!**/*.png' - # The default paths-filter-action behavior is "match at least one" - # pattern. We change the behavior to be "match every" pattern. - # Combined with negating every pattern above, it means the final output - # will be non-empty only if there is at least one file that is NOT a - # documentation file, or in other words, is a potential code file. - predicate-quantifier: 'every' - - - name: Test whether Python files are among the changed files - uses: tomi/paths-filter-action@32c62f5ca100c1110406e3477d5b3ecef4666fec # v3.0.2 - id: python-files - with: - base: ${{github.ref_name}} - filters: | - matched: - - '**/*.py' - - - name: Summary of test results - run: | - set -x - echo steps.nondoc-files.outputs.matched = │${{steps.nondoc-files.outputs.matched}}│ - echo steps.python-files.outputs.matched = │${{steps.python-files.outputs.matched}}│ diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 000000000..0f9450e74 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,491 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# yamllint disable rule:line-length + +name: Continuous integration +run-name: >- + Run CI checks for ${{github.event_name}} on ${{github.ref_name}} + by @${{github.actor}} + +on: + push: + branches: + - main + + pull_request: + branches: + - main + + merge_group: + types: + - checks_requested + + workflow_dispatch: + inputs: + debug: + description: 'Run with debugging options' + type: boolean + default: true + soft-linting: + description: 'Do not quit for linting errors' + type: boolean + default: true + +env: + # Python version to use for actions/setup-python. + python-version: '3.13' + SHELLOPTS: ${{inputs.debug && 'xtrace'}} + PIP_PROGRESS_BAR: 'off' + # Global flag determining whether lint errors are fatal. + # TODO(mhucka): uncomment next line & delete 2nd after formatting PRs merged. + # soft-linting: ${{inputs.soft-linting}} + soft-linting: true + +concurrency: + cancel-in-progress: true + group: ${{github.workflow}}-${{github.event.pull_request.number||github.ref}} + +permissions: read-all + +jobs: + python-checks: + name: 'Python format & lint checks' + runs-on: ubuntu-slim + timeout-minutes: 20 + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 + with: + python-version: ${{env.python-version}} + cache: pip + cache-dependency-path: | + requirements.txt + dev-requirements.txt + + - name: Install dependencies + run: pip install black~=25.9.0 flynt~=1.0 pylint~=4.0.0 + + - name: Check format + continue-on-error: ${{env.soft-linting == 'true'}} + run: | + echo '::add-matcher::.github/problem-matchers/black.json' + check/format-incremental + + - name: Check lint + continue-on-error: ${{env.soft-linting == 'true'}} + run: | + echo '::add-matcher::.github/problem-matchers/pylint.json' + pylint ${{env.soft-linting && '--exit-zero'}} -j 0 . + + docker-lint: + name: Dockerfile lint checks + # ubuntu-slim runners don't have docker installed. + runs-on: ubuntu-24.04 + timeout-minutes: 15 + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + + - name: Run hadolint on Dockerfiles + continue-on-error: ${{env.soft-linting == 'true'}} + env: + hadolint_version: 'sha256:e9dbf5113239ef2bf696d20c8f28d3019a47c26a38c98b89344d3e2846c4d5f8' + run: | + echo '::add-matcher::.github/problem-matchers/hadolint.json' + find . -name Dockerfile -print0 | \ + xargs -0 -r docker run --rm -i -v "${PWD}:/app" -w /app \ + --entrypoint /bin/hadolint \ + ghcr.io/hadolint/hadolint@${{env.hadolint_version}} + + shell-lint: + name: Shell script lint checks + runs-on: ubuntu-slim + timeout-minutes: 15 + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + + - name: Install problem matcher + run: echo '::add-matcher::.github/problem-matchers/shellcheck.json' + + - name: Run ShellCheck + continue-on-error: ${{env.soft-linting == 'true'}} + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 + with: + severity: error + check_together: 'yes' + additional_files: >- + check/format-incremental + + yaml-lint: + name: YAML lint checks + runs-on: ubuntu-slim + timeout-minutes: 15 + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + with: + fetch-depth: 0 + + - name: Install yamllint + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends -y yamllint + + - name: Lint the YAML files + continue-on-error: ${{env.soft-linting == 'true'}} + run: | + echo "::add-matcher::.github/problem-matchers/yamllint.json" + find . -name '*.yaml' -o -name '*.yml' | \ + grep -vE '\\.github/workflows' | \ + xargs yamllint -f github + + bazel-lint: + name: Bazel build lint checks + runs-on: ubuntu-slim + timeout-minutes: 15 + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + with: + fetch-depth: 0 + + - name: Install Buildifier + uses: jbajic/setup-buildifier@c558ee05c6f74ab5753ff794516750b4aadac296 # v1 + with: + buildifier-version: '8.2.1' + + - name: Run Buildifier in lint mode + continue-on-error: ${{env.soft-linting == 'true'}} + run: | + echo '::add-matcher::.github/problem-matchers/buildifier.json' + # shellcheck disable=SC2038 + find . -name 'BUILD' -o -name '*.bzl' -o -name 'WORKSPACE' | \ + xargs buildifier -mode=diff -lint=warn + + action-lint: + name: GitHub Actions lint checks + runs-on: ubuntu-slim + timeout-minutes: 15 + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + with: + fetch-depth: 0 + + - name: Run actionlint + continue-on-error: ${{env.soft-linting == 'true'}} + uses: raven-actions/actionlint@3a24062651993d40fed1019b58ac6fbdfbf276cc # v2 + with: + flags: ${{inputs.debug && '-verbose'}} + files: '.github/workflows/*.{yaml,yml}' + pyflakes: false + + library-tests: + name: Library tests + needs: + - action-lint + - bazel-lint + - python-checks + - shell-lint + - yaml-lint + runs-on: ${{matrix.os}} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + # It's not worth testing every possible combination here, so this only + # tests the endpoints of the supported range. The wheel build process + # (in a separate workflow) *does* use all the versions. + os: + - ubuntu-24.04 + - macos-14 + - macos-15 + - windows-2025 + python_version: + - '3.10' + - '3.13' + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + submodules: recursive + + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 + id: setup + with: + python-version: ${{matrix.python_version}} + cache: pip + cache-dependency-path: | + requirements.txt + dev-requirements.txt + + - name: Install dependencies + run: pip install -r requirements.txt -r dev-requirements.txt + + - name: Set up Bazel + uses: './.github/actions/set-up-bazel' + with: + debug: ${{inputs.debug}} + + - if: matrix.os != 'windows-2025' + name: Build the qsim C++ library and run tests (non-Windows case) + run: | + alias bazel=bazelisk + dev_tools/test_libs.sh ${{inputs.debug && '--config=verbose'}} + + - if: matrix.os == 'windows-2025' + name: Build the qsim C++ library and run tests (Windows case) + # On GitHub Windows runners, Bazel ends up finding a different "python3" + # binary than what's installed by setup-python unless we tell Bazel what + # to use. Here we do that by setting PYTHON_BIN_PATH. + env: + root: 'C:\\hostedtoolcache\\windows\\Python' + exe: '${{steps.setup.outputs.python-version}}\\x64\\python3.exe' + shell: cmd + run: bash -x dev_tools/test_libs.sh ${{inputs.debug && '--config=verbose'}} --action_env PYTHON_BIN_PATH=${{env.root}}\\${{env.exe}} + + - name: Install LLVM and OpenMP on macOS + if: startsWith(matrix.os, 'macos') + run: | + brew install -q libomp llvm@19 + brew unlink libomp + brew unlink llvm@19 + brew link --force libomp + brew link --force llvm@19 + + brew_prefix="$(brew --prefix)" + xcode_prefix="$(xcrun --sdk macosx --show-sdk-path)" + + export PATH="${brew_prefix}/bin:$PATH" + echo "PATH=${PATH}" >> "$GITHUB_ENV" + + export LDFLAGS="-L${brew_prefix}/lib -Wl,-rpath,${brew_prefix}/lib" + echo "LDFLAGS=${LDFLAGS}" >> "$GITHUB_ENV" + + export CXXFLAGS="-I${xcode_prefix}/usr/include -I${brew_prefix}/include -O3 -std=c++17 -flto=auto -Xpreprocessor -fopenmp" + echo "CXXFLAGS=${CXXFLAGS}" >> "$GITHUB_ENV" + + - name: Build the Python bindings + shell: bash + run: | + mkdir build + cd build + cmake ${{inputs.debug && '--debug-output' || ''}} .. + cmake --build . -j ${{inputs.debug && '--verbose' || ''}} + + options-tests: + name: Options tests + needs: + - action-lint + - bazel-lint + - python-checks + - shell-lint + - yaml-lint + runs-on: ubuntu-24.04 + timeout-minutes: 60 + strategy: + matrix: + # Hardware optimizers. + hardware_opt: [avx, sse, basic] + # Optimizers for parallelism. + parallel_opt: [openmp, nopenmp] + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + submodules: recursive + + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 + with: + python-version: ${{env.python-version}} + cache: pip + cache-dependency-path: | + requirements.txt + dev-requirements.txt + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Set up Bazel + uses: './.github/actions/set-up-bazel' + with: + debug: ${{inputs.debug}} + + - name: Run C++ tests + run: | + bazel test \ + --config=${{matrix.hardware_opt}} \ + --config=${{matrix.parallel_opt}} \ + ${{inputs.debug && '--config=verbose'}} \ + tests:all + + - name: Run sample simulation + run: | + bazel run \ + --config=${{matrix.hardware_opt}} \ + --config=${{matrix.parallel_opt}} \ + ${{inputs.debug && '--config=verbose'}} \ + apps:qsim_base -- -c circuits/circuit_q24 + + memory-tests: + name: Malloc/asan/msan tests + needs: + - action-lint + - bazel-lint + - python-checks + - shell-lint + - yaml-lint + runs-on: ubuntu-24.04 + timeout-minutes: 60 + env: + common_args: >- + --config=avx + --config=openmp + ${{inputs.debug && '--config=verbose'}} + tests:all + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + submodules: recursive + + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 + with: + python-version: ${{env.python-version}} + cache: pip + cache-dependency-path: | + requirements.txt + dev-requirements.txt + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Set up Bazel + uses: './.github/actions/set-up-bazel' + with: + debug: ${{inputs.debug}} + + - name: Install google-perftools for tcmalloc + run: | + sudo apt-get update + sudo apt-get install -y libgoogle-perftools-dev + + - name: Run TCMalloc tests + env: + PERFTOOLS_VERBOSE: ${{inputs.debug && 1}} + run: bazel test --config=tcmalloc ${{env.common_args}} + + - name: Run memory sanitizer tests + run: bazel test --config=msan ${{env.common_args}} + + - name: Run address sanitizer tests + run: bazel test --config=asan ${{env.common_args}} + + docker-tests: + name: Docker build tests + needs: + - action-lint + - docker-lint + - python-checks + - shell-lint + - yaml-lint + runs-on: ubuntu-24.04 + timeout-minutes: 60 + env: + # The next environment variable is used by Docker. + BUILDKIT_PROGRESS: ${{inputs.debug && 'plain'}} + steps: + - name: Check out a copy of the git repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + submodules: recursive + + - name: Build Docker images + run: | + # Running locally, a plain "docker compose build" works as expected. + # On GitHub, buildx tries to build all 3 images in parallel even if + # you set COMPOSE_PARALLEL_LIMIT or use --parallel 1. That fails b/c + # the qsim-base image is not available to the other two build jobs. + docker compose build qsim-base-image + docker compose build --parallel qsim-cxx-tests-image qsim-py-tests-image + + - name: Run C++ tests + run: docker run --rm qsim-cxx-tests:latest + + - name: Run Python tests + run: docker run --rm qsim-py-tests:latest + + - name: Run a sample simulation + run: docker run --rm qsim-base:latest -c /qsim/circuits/circuit_q24 + + - name: Test installation process + run: | + cd install/tests + docker compose build + + report-results: + name: CI + if: always() + needs: + - action-lint + - bazel-lint + - docker-lint + - docker-tests + - library-tests + - memory-tests + - options-tests + - python-checks + - shell-lint + - yaml-lint + runs-on: ubuntu-slim + timeout-minutes: 5 + steps: + - name: Report failure + if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: | + { + echo ":x: CI checks failed" + echo "One or more CI jobs failed. Please check the logs for details." + } >> "$GITHUB_STEP_SUMMARY" + + - name: Report success + run: | + echo ":white_check_mark: All CI checks passed" >> "$GITHUB_STEP_SUMMARY" + + - name: Point out if soft-linting is in effect + if: env.soft-linting + run: | + { + echo "> [!CAUTION]:" + echo "> **Soft linting is in effect**." + echo "> Format and lint errors have been ignored." + } >> "$GITHUB_STEP_SUMMARY" + + - name: Exit with error (if appropriate) + if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: exit 1 diff --git a/.github/workflows/ci_build_library.yaml b/.github/workflows/ci_build_library.yaml deleted file mode 100644 index 53278c6cb..000000000 --- a/.github/workflows/ci_build_library.yaml +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: 'CI: build & test library and apps' -run-name: Build the library on different platforms and run tests - -on: - push: - branches: - - main - - pull_request: - types: [opened, synchronize] - - merge_group: - types: - - checks_requested - - workflow_dispatch: - inputs: - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.event.pull_request.number||github.ref}} - -jobs: - find-changes: - name: Find changed files - uses: ./.github/workflows/_find_changes.yaml - secrets: inherit - - run-tests: - if: ${{needs.find-changes.outputs.code == 'true' || inputs.debug}} - name: Run tests - needs: find-changes - runs-on: ${{matrix.conf.os}} - continue-on-error: true - timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - conf: [ - {os: ubuntu-24.04, pyarch: x64, py: 10}, - {os: ubuntu-24.04, pyarch: x64, py: 11}, - {os: ubuntu-24.04, pyarch: x64, py: 12}, - {os: ubuntu-24.04, pyarch: x64, py: 13}, - - {os: macos-14, pyarch: arm64, py: 10}, - {os: macos-14, pyarch: arm64, py: 11}, - {os: macos-14, pyarch: arm64, py: 12}, - {os: macos-14, pyarch: arm64, py: 13}, - - {os: macos-15, pyarch: arm64, py: 10}, - {os: macos-15, pyarch: arm64, py: 11}, - {os: macos-15, pyarch: arm64, py: 12}, - {os: macos-15, pyarch: arm64, py: 13}, - - {os: windows-2025, pyarch: x64, py: 10}, - {os: windows-2025, pyarch: x64, py: 11}, - {os: windows-2025, pyarch: x64, py: 12}, - {os: windows-2025, pyarch: x64, py: 13}, - ] - env: - use-verbose: ${{inputs.debug}} - steps: - - if: >- - ${{needs.find-changes.outputs.code == 'false' - && github.event_name != 'workflow_dispatch'}} - name: Exit early if there were no changes to code files - run: exit 0 - - - name: Check out a copy of the git repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 1 - submodules: recursive - - - name: Set up Python with caching of pip dependencies - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 - id: setup - with: - python-version: '3.${{matrix.conf.py}}' - architecture: ${{matrix.conf.pyarch}} - cache: pip - cache-dependency-path: | - requirements.txt - dev-requirements.txt - - - name: Set up Bazel with caching - uses: bazel-contrib/setup-bazel@4fd964a13a440a8aeb0be47350db2fc640f19ca8 # 0.15.0 - with: - disk-cache: ${{github.workflow}} - bazelisk-cache: true - external-cache: true - repository-cache: true - - - name: Install qsim development dependencies - run: | - pip install -r requirements.txt - pip install -r dev-requirements.txt - - - if: matrix.conf.os != 'windows-2025' - name: Run the build and test script (non-Windows case) - env: - # Add xtrace to SHELLOPTS for all Bash scripts when doing debug runs. - SHELLOPTS: ${{inputs.debug && 'xtrace' || '' }} - run: dev_tools/test_libs.sh ${{env.use-verbose && '--config=verbose'}} - - - if: matrix.conf.os == 'windows-2025' - name: Run the build and test script (Windows case) - # On GitHub Windows runners, Bazel ends up finding a different - # "python3" binary than what's installed by setup-python unless we tell - # Bazel what to use. Here we do that by setting PYTHON_BIN_PATH. - env: - pyroot: 'C:\\hostedtoolcache\\windows\\Python' - pyexe: '${{steps.setup.outputs.python-version}}\\${{matrix.conf.pyarch}}\\python3.exe' - SHELLOPTS: ${{env.use-verbose && 'xtrace' || '' }} - shell: cmd - run: bash -x dev_tools/test_libs.sh ${{env.use-verbose && '--config=verbose'}} --action_env PYTHON_BIN_PATH=${{env.pyroot}}\\${{env.pyexe}} - - report-results: - if: always() - name: ${{github.workflow}} - needs: run-tests - runs-on: ubuntu-24.04 - timeout-minutes: 2 - steps: - - name: Exit with an appropriate status code - run: | - result="${{needs.run-tests.result}}" - [[ "$result" == "success" || "$result" == "skipped" ]] diff --git a/.github/workflows/ci_build_wheels.yaml b/.github/workflows/ci_build_wheels.yaml deleted file mode 100644 index 4eb2aeed4..000000000 --- a/.github/workflows/ci_build_wheels.yaml +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: 'CI: build & verify Python wheels' -run-name: Build Python wheels and verify them - -on: - push: - branches: - - main - - pull_request: - types: [opened, synchronize] - - merge_group: - types: - - checks_requested - - workflow_dispatch: - inputs: - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.event.pull_request.number||github.ref}} - -jobs: - find-changes: - name: Find changed files - uses: ./.github/workflows/_find_changes.yaml - secrets: inherit - - run-tests: - if: ${{needs.find-changes.outputs.code == 'true' || inputs.debug}} - name: Run tests - needs: find-changes - uses: ./.github/workflows/_build_wheels.yaml - secrets: inherit - with: - debug: ${{inputs.debug == true}} - - report-results: - if: always() - name: ${{github.workflow}} - needs: run-tests - runs-on: ubuntu-24.04 - timeout-minutes: 2 - steps: - - name: Exit with an appropriate status code - run: | - result="${{needs.run-tests.result}}" - [[ "$result" == "success" || "$result" == "skipped" ]] diff --git a/.github/workflows/ci_docker_tests.yaml b/.github/workflows/ci_docker_tests.yaml deleted file mode 100644 index 6739f0a9a..000000000 --- a/.github/workflows/ci_docker_tests.yaml +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: 'CI: build & test Docker images' -run-name: Build Docker images and test them - -on: - push: - branches: - - main - - pull_request: - types: [opened, synchronize] - - merge_group: - types: - - checks_requested - - workflow_dispatch: - inputs: - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.event.pull_request.number||github.ref}} - -jobs: - find-changes: - name: Find changed files - uses: ./.github/workflows/_find_changes.yaml - secrets: inherit - - run-tests: - if: ${{needs.find-changes.outputs.code == 'true' || inputs.debug}} - name: Run tests - needs: find-changes - runs-on: ubuntu-24.04 - continue-on-error: true - timeout-minutes: 60 - env: - # The next environment variable is used by Docker. - BUILDKIT_PROGRESS: ${{inputs.debug && 'plain' || ''}} - steps: - - if: >- - ${{needs.find-changes.outputs.code == 'false' - && github.event_name != 'workflow_dispatch'}} - name: Exit early if there were no changes to code files - run: exit 0 - - - name: Check out a copy of the git repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 1 - submodules: recursive - - - name: Build Docker images - run: | - # Running locally, a plain "docker compose build" works as expected. - # On GitHub, buildx tries to build all 3 images in parallel even if - # you set COMPOSE_PARALLEL_LIMIT or use --parallel 1. That fails b/c - # the qsim-base image is not available to the other two build jobs. - docker compose build qsim-base-image - docker compose build qsim-cxx-tests-image qsim-py-tests-image - - - name: Run C++ tests - run: docker run --rm qsim-cxx-tests:latest - - - name: Run Python tests - run: docker run --rm qsim-py-tests:latest - - - name: Run a sample simulation - run: docker run --rm qsim-base:latest -c /qsim/circuits/circuit_q24 - - - name: Test installation process - run: | - cd install/tests - docker compose build - - report-results: - if: always() - name: ${{github.workflow}} - needs: run-tests - runs-on: ubuntu-24.04 - timeout-minutes: 2 - steps: - - name: Exit with an appropriate status code - run: | - result="${{needs.run-tests.result}}" - [[ "$result" == "success" || "$result" == "skipped" ]] diff --git a/.github/workflows/ci_format_checks.yml b/.github/workflows/ci_format_checks.yml deleted file mode 100644 index a308029c1..000000000 --- a/.github/workflows/ci_format_checks.yml +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: 'CI: run code format tests' -run-name: Check source code with linters and formatters - -on: - push: - branches: - - main - - pull_request: - types: [opened, synchronize] - - merge_group: - types: - - checks_requested - - workflow_dispatch: - inputs: - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.event.pull_request.number||github.ref}} - -jobs: - find-changes: - name: Find changed files - uses: ./.github/workflows/_find_changes.yaml - secrets: inherit - - run-tests: - if: ${{needs.find-changes.outputs.python == 'true' || inputs.debug}} - name: Run tests - needs: find-changes - runs-on: ubuntu-24.04 - continue-on-error: true - timeout-minutes: 30 - env: - # Add xtrace to SHELLOPTS for all Bash scripts when doing debug runs. - SHELLOPTS: ${{inputs.debug && 'xtrace' || '' }} - steps: - - if: >- - ${{needs.find-changes.outputs.python == 'false' - && github.event_name != 'workflow_dispatch'}} - name: Exit early if there were no changes to code files - run: exit 0 - - - name: Check out a copy of the git repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - # Need the full git history for format-incremental. - fetch-depth: 0 - - - name: Set up Python with caching of pip dependencies - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 - with: - python-version: '3.13' - cache: pip - cache-dependency-path: | - requirements.txt - dev-requirements.txt - - - name: Install qsim development dependencies - run: | - pip install -r requirements.txt - pip install -r dev-requirements.txt - - - name: Check Python file format - run: check/format-incremental - - report-results: - if: always() - name: ${{github.workflow}} - needs: run-tests - runs-on: ubuntu-24.04 - timeout-minutes: 2 - steps: - - name: Exit with an appropriate status code - run: | - result="${{needs.run-tests.result}}" - [[ "$result" == "success" || "$result" == "skipped" ]] diff --git a/.github/workflows/ci_hardware_options.yaml b/.github/workflows/ci_hardware_options.yaml deleted file mode 100644 index a38f320a6..000000000 --- a/.github/workflows/ci_hardware_options.yaml +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: 'CI: test hardware options' -run-name: Test with instruction set extensions and parallelism - -on: - push: - branches: - - main - - pull_request: - types: [opened, synchronize] - - merge_group: - types: - - checks_requested - - workflow_dispatch: - inputs: - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.event.pull_request.number||github.ref}} - -jobs: - find-changes: - name: Find changed files - uses: ./.github/workflows/_find_changes.yaml - secrets: inherit - - run-tests: - if: ${{needs.find-changes.outputs.code == 'true' || inputs.debug}} - name: Run tests - needs: find-changes - runs-on: ubuntu-24.04 - continue-on-error: true - timeout-minutes: 60 - strategy: - matrix: - # Hardware optimizers. - hardware_opt: [avx, sse, basic] - # Optimizers for parallelism. - parallel_opt: [openmp, nopenmp] - env: - use-verbose: ${{inputs.debug}} - steps: - - if: >- - ${{needs.find-changes.outputs.code == 'false' - && github.event_name != 'workflow_dispatch'}} - name: Exit early if there were no changes to code files - run: exit 0 - - - name: Check out a copy of the git repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 1 - submodules: recursive - - - name: Set up Bazel with caching - uses: bazel-contrib/setup-bazel@4fd964a13a440a8aeb0be47350db2fc640f19ca8 # 0.15.0 - with: - disk-cache: ${{github.workflow}} - bazelisk-cache: true - external-cache: true - repository-cache: true - - - name: Set up Python with caching of pip dependencies - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 - with: - python-version: '3.12' - cache: pip - cache-dependency-path: | - requirements.txt - dev-requirements.txt - - - name: Install qsim development dependencies - run: | - pip install -r requirements.txt - pip install -r dev-requirements.txt - - - name: Run C++ tests - run: | - bazel test \ - --config=${{matrix.hardware_opt}} \ - --config=${{matrix.parallel_opt}} \ - ${{env.use-verbose && '--config=verbose'}} \ - tests:all - - - name: Run sample simulation - run: | - bazel run \ - --config=${{matrix.hardware_opt}} \ - --config=${{matrix.parallel_opt}} \ - ${{env.use-verbose && '--config=verbose'}} \ - apps:qsim_base -- -c circuits/circuit_q24 - - report-results: - if: always() - name: ${{github.workflow}} - needs: run-tests - runs-on: ubuntu-24.04 - timeout-minutes: 2 - steps: - - name: Exit with an appropriate status code - run: | - result="${{needs.run-tests.result}}" - [[ "$result" == "success" || "$result" == "skipped" ]] diff --git a/.github/workflows/ci_sanitizer_tests.yaml b/.github/workflows/ci_sanitizer_tests.yaml deleted file mode 100644 index e3ec49496..000000000 --- a/.github/workflows/ci_sanitizer_tests.yaml +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: 'CI: run sanitizer tests' -run-name: Test with address and memory sanitizers - -on: - push: - branches: - - main - - pull_request: - types: [opened, synchronize] - - merge_group: - types: - - checks_requested - - workflow_dispatch: - inputs: - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.event.pull_request.number||github.ref}} - -jobs: - find-changes: - name: Find changed files - uses: ./.github/workflows/_find_changes.yaml - secrets: inherit - - run-tests: - if: ${{needs.find-changes.outputs.code == 'true' || inputs.debug}} - name: Run tests - needs: find-changes - runs-on: ubuntu-24.04 - continue-on-error: true - timeout-minutes: 30 - strategy: - matrix: - # Memory and address sanitizers. - sanitizer_opt: [msan, asan] - env: - use-verbose: ${{inputs.debug}} - steps: - - if: >- - ${{needs.find-changes.outputs.code == 'false' - && github.event_name != 'workflow_dispatch'}} - name: Exit early if there were no changes to code files - run: exit 0 - - - name: Check out a copy of the git repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 1 - submodules: recursive - - - uses: bazel-contrib/setup-bazel@4fd964a13a440a8aeb0be47350db2fc640f19ca8 # 0.15.0 - with: - disk-cache: ${{github.workflow}} - bazelisk-cache: true - external-cache: true - repository-cache: true - - - name: Set up Python with caching of pip dependencies - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 - id: setup - with: - python-version: '3.12' - cache: pip - cache-dependency-path: | - requirements.txt - dev-requirements.txt - - - name: Install qsim development dependencies - run: | - pip install -r requirements.txt - pip install -r dev-requirements.txt - - - name: Run C++ tests - run: | - bazel test \ - --config=avx \ - --config=openmp \ - --config=${{matrix.sanitizer_opt}} \ - ${{env.use-verbose && '--config=verbose'}} \ - tests:all - - report-results: - if: always() - name: ${{github.workflow}} - needs: run-tests - runs-on: ubuntu-24.04 - timeout-minutes: 2 - steps: - - name: Exit with an appropriate status code - run: | - result="${{needs.run-tests.result}}" - [[ "$result" == "success" || "$result" == "skipped" ]] diff --git a/.github/workflows/ci_tcmalloc_test.yaml b/.github/workflows/ci_tcmalloc_test.yaml deleted file mode 100644 index 082e00812..000000000 --- a/.github/workflows/ci_tcmalloc_test.yaml +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: 'CI: run TCMalloc tests' -run-name: Test with TCMalloc (thread-caching malloc) - -on: - push: - branches: - - main - - pull_request: - types: [opened, synchronize] - - merge_group: - types: - - checks_requested - - workflow_dispatch: - inputs: - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.event.pull_request.number||github.ref}} - -jobs: - find-changes: - name: Find changed files - uses: ./.github/workflows/_find_changes.yaml - secrets: inherit - - run-tests: - if: ${{needs.find-changes.outputs.code == 'true' || inputs.debug}} - name: Run tests - needs: find-changes - runs-on: ubuntu-24.04 - continue-on-error: true - timeout-minutes: 30 - env: - use-verbose: ${{inputs.debug}} - steps: - - if: >- - ${{needs.find-changes.outputs.code == 'false' - && github.event_name != 'workflow_dispatch'}} - name: Exit early if there were no changes to code files - run: exit 0 - - - name: Check out a copy of the git repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 1 - submodules: recursive - - - name: Set up Bazel with caching - uses: bazel-contrib/setup-bazel@4fd964a13a440a8aeb0be47350db2fc640f19ca8 # 0.15.0 - with: - disk-cache: ${{github.workflow}} - bazelisk-cache: true - external-cache: true - repository-cache: true - - - name: Set up Python with caching of pip dependencies - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v5 - with: - python-version: '3.12' - cache: pip - cache-dependency-path: | - requirements.txt - dev-requirements.txt - - - name: Install qsim development dependencies - run: | - pip install -r requirements.txt - pip install -r dev-requirements.txt - - - name: Install google-perftools for tcmalloc - run: | - sudo apt-get update - sudo apt-get install -y libgoogle-perftools-dev - - - name: Run C++ tests - env: - PERFTOOLS_VERBOSE: ${{env.use-verbose && 1 || ''}} - run: | - bazel test \ - --config=avx \ - --config=openmp \ - --config=tcmalloc \ - ${{env.use-verbose && '--config=verbose'}} \ - tests:all - - report-results: - if: always() - name: ${{github.workflow}} - needs: run-tests - runs-on: ubuntu-24.04 - timeout-minutes: 2 - steps: - - name: Exit with an appropriate status code - run: | - result="${{needs.run-tests.result}}" - [[ "$result" == "success" || "$result" == "skipped" ]] diff --git a/.github/workflows/release_wheels.yml b/.github/workflows/release_wheels.yml deleted file mode 100644 index 70573f4a9..000000000 --- a/.github/workflows/release_wheels.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: 'Build & release Python wheels' -run-name: 'Build Python wheels and release them on PyPI' - -on: - release: - types: [published] - - workflow_dispatch: - inputs: - debug: - description: 'Run with debugging options' - type: boolean - default: true - -permissions: read-all - -jobs: - build-wheels: - name: Build and save wheels - uses: ./.github/workflows/_build_wheels.yaml - secrets: inherit - with: - upload: true - debug: ${{inputs.debug}} - - release-wheels: - name: Publish wheels - needs: build-wheels - runs-on: ubuntu-24.04 - timeout-minutes: 60 - steps: - - name: Retrieve saved wheels - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 - with: - path: dist/ - pattern: python-wheels-* - merge-multiple: true - - - name: Publish wheels on PyPI - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1 - with: - user: __token__ - password: ${{secrets.PYPI_API_TOKEN}} - packages_dir: dist/ - skip_existing: true - verbose: true diff --git a/pybind_interface/avx2/CMakeLists.txt b/pybind_interface/avx2/CMakeLists.txt index 86a4e9a73..cbd6ea2d3 100644 --- a/pybind_interface/avx2/CMakeLists.txt +++ b/pybind_interface/avx2/CMakeLists.txt @@ -15,11 +15,14 @@ cmake_minimum_required(VERSION 3.28) project(qsim) -IF (WIN32) - set(CMAKE_CXX_FLAGS "/arch:AVX2 /O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-mavx2 -mfma -O3 -flto=auto") -ENDIF() +if(WIN32) + add_compile_options(/arch:AVX2 /openmp) + # Add /O2 to any configuration that is NOT Debug. + # This prevents a conflict with /RTC1 in DEBUG builds. + add_compile_options($<$>:/O2>) +else() + add_compile_options(-mavx2 -mfma -O3 -flto=auto) +endif() if(APPLE) include_directories( @@ -36,7 +39,7 @@ if(APPLE) ) endif() -INCLUDE(../GetPybind11.cmake) +include(../GetPybind11.cmake) pybind11_add_module(qsim_avx2 pybind_main_avx2.cpp) target_link_libraries(qsim_avx2 PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/avx512/CMakeLists.txt b/pybind_interface/avx512/CMakeLists.txt index 892d14c24..a718a99f3 100644 --- a/pybind_interface/avx512/CMakeLists.txt +++ b/pybind_interface/avx512/CMakeLists.txt @@ -15,11 +15,14 @@ cmake_minimum_required(VERSION 3.28) project(qsim) -IF (WIN32) - set(CMAKE_CXX_FLAGS "/arch:AVX512 /O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-mavx512f -mbmi2 -O3 -flto=auto") -ENDIF() +if(WIN32) + add_compile_options(/arch:AVX512 /openmp) + # Add /O2 to any configuration that is NOT Debug. + # This prevents a conflict with /RTC1 in DEBUG builds. + add_compile_options($<$>:/O2>) +else() + add_compile_options(-mavx512f -mbmi2 -O3 -flto=auto) +endif() if(APPLE) include_directories( @@ -36,7 +39,7 @@ if(APPLE) ) endif() -INCLUDE(../GetPybind11.cmake) +include(../GetPybind11.cmake) pybind11_add_module(qsim_avx512 pybind_main_avx512.cpp) target_link_libraries(qsim_avx512 PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/basic/CMakeLists.txt b/pybind_interface/basic/CMakeLists.txt index 3d5750887..c8731ec8a 100644 --- a/pybind_interface/basic/CMakeLists.txt +++ b/pybind_interface/basic/CMakeLists.txt @@ -16,9 +16,12 @@ cmake_minimum_required(VERSION 3.28) project(qsim) if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") + add_compile_options(/openmp) + # Add /O2 to any configuration that is NOT Debug. + # This prevents a conflict with /RTC1 in DEBUG builds. + add_compile_options($<$>:/O2>) else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") + add_compile_options(-O3 -flto=auto) endif() if(APPLE) @@ -36,7 +39,7 @@ if(APPLE) ) endif() -INCLUDE(../GetPybind11.cmake) +include(../GetPybind11.cmake) pybind11_add_module(qsim_basic pybind_main_basic.cpp) target_link_libraries(qsim_basic PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/cuda/CMakeLists.txt b/pybind_interface/cuda/CMakeLists.txt index 24ebbe8e5..6ef6be3cf 100644 --- a/pybind_interface/cuda/CMakeLists.txt +++ b/pybind_interface/cuda/CMakeLists.txt @@ -16,9 +16,13 @@ cmake_minimum_required(VERSION 3.28) project(qsim LANGUAGES CXX CUDA) if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") + # Always apply AVX2 and openmp on Windows + add_compile_options(/openmp) + # Add /O2 to any configuration that is NOT Debug. + # This prevents a conflict with /RTC1 in DEBUG builds. + add_compile_options($<$>:/O2>) else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") + add_compile_options(-O3 -flto=auto) endif() if(APPLE) diff --git a/pybind_interface/custatevec/CMakeLists.txt b/pybind_interface/custatevec/CMakeLists.txt index eac992c2d..2bdd34c12 100644 --- a/pybind_interface/custatevec/CMakeLists.txt +++ b/pybind_interface/custatevec/CMakeLists.txt @@ -16,9 +16,12 @@ cmake_minimum_required(VERSION 3.28) project(qsim LANGUAGES CXX CUDA) if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") + add_compile_options(/openmp) + # Add /O2 to any configuration that is NOT Debug. + # This prevents a conflict with /RTC1 in DEBUG builds. + add_compile_options($<$>:/O2>) else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") + add_compile_options(-O3 -flto=auto) endif() if(APPLE) @@ -36,7 +39,7 @@ if(APPLE) ) endif() -INCLUDE(../GetPybind11.cmake) +include(../GetPybind11.cmake) find_package(Python3 3.10 REQUIRED) include_directories(${pybind11_INCLUDE_DIRS}) diff --git a/pybind_interface/decide/CMakeLists.txt b/pybind_interface/decide/CMakeLists.txt index dce80e51d..0c8b8d941 100644 --- a/pybind_interface/decide/CMakeLists.txt +++ b/pybind_interface/decide/CMakeLists.txt @@ -19,9 +19,12 @@ include(CheckLanguage) check_language(CUDA) if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") + add_compile_options(/openmp) + # Add /O2 to any configuration that is NOT Debug. + # This prevents a conflict with /RTC1 in DEBUG builds. + add_compile_options($<$>:/O2>) else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") + add_compile_options(-O3 -flto=auto) endif() if(APPLE) @@ -75,4 +78,3 @@ else() pybind11_add_module(qsim_decide decide.cpp) target_link_libraries(qsim_decide PUBLIC OpenMP::OpenMP_CXX) endif() - diff --git a/pybind_interface/hip/CMakeLists.txt b/pybind_interface/hip/CMakeLists.txt index 4b3a7cd13..56f0cd0e6 100644 --- a/pybind_interface/hip/CMakeLists.txt +++ b/pybind_interface/hip/CMakeLists.txt @@ -16,12 +16,15 @@ cmake_minimum_required(VERSION 3.28) project(qsim LANGUAGES CXX HIP) if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") + add_compile_options(/openmp) + # Add /O2 to any configuration that is NOT Debug. + # This prevents a conflict with /RTC1 in DEBUG builds. + add_compile_options($<$>:/O2>) else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") + add_compile_options(-O3 -flto=auto) endif() -INCLUDE(../GetPybind11.cmake) +include(../GetPybind11.cmake) find_package(PythonLibs 3.10 REQUIRED) list(APPEND CMAKE_MODULE_PATH "/opt/rocm/lib/cmake/hip") diff --git a/pybind_interface/sse/CMakeLists.txt b/pybind_interface/sse/CMakeLists.txt index e31360a2e..fee561006 100644 --- a/pybind_interface/sse/CMakeLists.txt +++ b/pybind_interface/sse/CMakeLists.txt @@ -15,11 +15,14 @@ cmake_minimum_required(VERSION 3.28) project(qsim) -IF (WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-msse4.1 -O3 -flto=auto") -ENDIF() +if(WIN32) + add_compile_options(/openmp) + # Add /O2 to any configuration that is NOT Debug. + # This prevents a conflict with /RTC1 in DEBUG builds. + add_compile_options($<$>:/O2>) +else() + add_compile_options(-msse4.1 -O3 -flto=auto) +endif() if(APPLE) include_directories( @@ -36,7 +39,7 @@ if(APPLE) ) endif() -INCLUDE(../GetPybind11.cmake) +include(../GetPybind11.cmake) pybind11_add_module(qsim_sse pybind_main_sse.cpp) target_link_libraries(qsim_sse PUBLIC OpenMP::OpenMP_CXX) diff --git a/pyproject.toml b/pyproject.toml index bfd0e41a3..7f2d8b1c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,8 @@ [tool.cibuildwheel] test-extras = "dev" dependency-versions = "latest" +enable = ["cpython-prerelease"] +environment.PIP_PREFER_BINARY = "1" # Due to package & module name conflict, temporarily move it away to run tests: before-test = "mv {package}/qsimcirq /tmp" test-command = "pytest -s -v {package}/qsimcirq_tests/qsimcirq_test.py && mv /tmp/qsimcirq {package}"