diff --git a/.github/workflows/benchmark-comment.yml b/.github/workflows/benchmark-comment.yml new file mode 100644 index 0000000..e873dbf --- /dev/null +++ b/.github/workflows/benchmark-comment.yml @@ -0,0 +1,64 @@ +# To workaroud https://github.com/actions/first-interaction/issues/10 in a secure way, +# we take the following steps to generate and comment a performance benchmark result: +# 1. first "performance tracking" workflow will generate the benchmark results in an unprivileged environment triggered on `pull_request` event +# 2. then this "performance tracking (comment)" workflow will show the result to us as a PR comment in a privileged environment +# Note that this workflow can only be modifed by getting checked-in to the default branch +# and thus is secure even though this workflow is granted with write permissions, etc. +# xref: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Performance tracking (comment) + +on: + workflow_run: + workflows: + - performance tracking + types: + - completed + +jobs: + comment: + runs-on: ubuntu-latest + #runs-on: self-hosted + if: > + ${{ github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v4 + + # restore records from the artifacts + - uses: dawidd6/action-download-artifact@v3 + with: + workflow: benchmark.yml + name: performance-tracking + workflow_conclusion: success + - name: output benchmark result + id: output-result-markdown + run: | + echo ::set-output name=body::$(cat ./benchmark-result.artifact) + - name: output pull request number + id: output-pull-request-number + run: | + echo ::set-output name=body::$(cat ./pull-request-number.artifact) + + # check if the previous comment exists + - name: find comment + uses: peter-evans/find-comment@v3 + id: fc + with: + issue-number: ${{ steps.output-pull-request-number.outputs.body }} + comment-author: 'github-actions[bot]' + body-includes: Benchmark Result + + # create/update comment + - name: create comment + if: ${{ steps.fc.outputs.comment-id == 0 }} + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ steps.output-pull-request-number.outputs.body }} + body: ${{ steps.output-result-markdown.outputs.body }} + - name: update comment + if: ${{ steps.fc.outputs.comment-id != 0 }} + uses: peter-evans/create-or-update-comment@v4 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + body: ${{ steps.output-result-markdown.outputs.body }} diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000..34c65be --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,57 @@ +name: Performance tracking +on: + pull_request: + +env: + PYTHON: ~ + +jobs: + performance-tracking: + runs-on: ubuntu-latest + #runs-on: self-hosted + steps: + # setup + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@latest + with: + version: 'nightly' + - uses: julia-actions/julia-buildpkg@latest + - name: install dependencies + run: julia -e 'using Pkg; pkg"add PkgBenchmark BenchmarkCI@0.1"' + + # run the benchmark suite + - name: run benchmarks + run: | + julia -e ' + using BenchmarkCI + BenchmarkCI.judge() + BenchmarkCI.displayjudgement() + ' + + # generate and record the benchmark result as markdown + - name: generate benchmark result + run: | + body=$(julia -e ' + using BenchmarkCI + + let + judgement = BenchmarkCI._loadjudge(BenchmarkCI.DEFAULT_WORKSPACE) + title = "Benchmark Result" + ciresult = BenchmarkCI.CIResult(; judgement, title) + BenchmarkCI.printcommentmd(stdout::IO, ciresult) + end + ') + body="${body//'%'/'%25'}" + body="${body//$'\n'/'%0A'}" + body="${body//$'\r'/'%0D'}" + echo $body > ./benchmark-result.artifact + + # record the pull request number + - name: record pull request number + run: echo ${{ github.event.pull_request.number }} > ./pull-request-number.artifact + + # save as artifacts (performance tracking (comment) workflow will use it) + - uses: actions/upload-artifact@v4 + with: + name: performance-tracking + path: ./*.artifact diff --git a/.github/workflows/changelog-enforcer.yml b/.github/workflows/changelog-enforcer.yml new file mode 100644 index 0000000..b7a2038 --- /dev/null +++ b/.github/workflows/changelog-enforcer.yml @@ -0,0 +1,15 @@ +name: "Changelog Enforcer" +on: + pull_request: + # The specific activity types are listed here to include "labeled" and "unlabeled" + # (which are not included by default for the "pull_request" trigger). + # This is needed to allow skipping enforcement of the changelog in PRs with specific labels, + # as defined in the (optional) "skipLabels" property. + types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] + +jobs: + # Enforces the update of a changelog file on every pull request + changelog: + runs-on: ubuntu-latest + steps: + - uses: dangoslen/changelog-enforcer@v3 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac7a18b..ea5592a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,11 @@ on: branches: [master, main] tags: ["*"] pull_request: +env: + PYTHON: ~ jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + name: Julia ${{ matrix.version }} - t=${{ matrix.threads }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -15,12 +17,16 @@ jobs: - '1.6' - '1.7' - '1.8' + - '1.9' + - '1.10' - '1' - nightly os: - ubuntu-latest - windows-latest - macOS-latest + threads: + - '1' arch: - x64 steps: @@ -29,22 +35,16 @@ jobs: with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - uses: actions/cache@v4 - env: - cache-name: cache-artifacts - with: - path: ~/.julia/artifacts - key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} - restore-keys: | - ${{ runner.os }}-test-${{ env.cache-name }}- - ${{ runner.os }}-test- - ${{ runner.os }}- + - uses: julia-actions/cache@v1 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 + env: + JULIA_NUM_THREADS: ${{ matrix.threads }} - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: file: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} docs: name: Documentation runs-on: ubuntu-latest @@ -53,8 +53,10 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: '1' + - uses: julia-actions/cache@v1 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-docdeploy@v1 env: + GKSwstype: nul # Fix for Plots with GR backend. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} diff --git a/.github/workflows/downgrade.yml b/.github/workflows/downgrade.yml new file mode 100644 index 0000000..61b4889 --- /dev/null +++ b/.github/workflows/downgrade.yml @@ -0,0 +1,29 @@ +name: Downgrade +on: + pull_request: + branches: [master, main] + paths-ignore: + - 'docs/**' + push: + branches: [master, main] + paths-ignore: + - 'docs/**' +env: + PYTHON: ~ +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + version: ['1.9'] + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + - uses: cjdoris/julia-downgrade-compat-action@v1 + with: + skip: Pkg,TOML,InteractiveUtils,Random,LinearAlgebra,Dates + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 \ No newline at end of file diff --git a/.github/workflows/invalidations.yml b/.github/workflows/invalidations.yml new file mode 100644 index 0000000..e6aade0 --- /dev/null +++ b/.github/workflows/invalidations.yml @@ -0,0 +1,41 @@ +name: Invalidations + +on: + pull_request: + +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: always. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + evaluate: + # Only run on PRs to the default branch. + # In the PR trigger above branches can be specified only explicitly whereas this check should work for master, main, or any other default branch + if: github.base_ref == github.event.repository.default_branch + runs-on: ubuntu-latest + steps: + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - uses: actions/checkout@v4 + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-invalidations@v1 + id: invs_pr + + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-invalidations@v1 + id: invs_default + + - name: Report invalidation counts + run: | + echo "Invalidations on default branch: ${{ steps.invs_default.outputs.total }} (${{ steps.invs_default.outputs.deps }} via deps)" >> $GITHUB_STEP_SUMMARY + echo "This branch: ${{ steps.invs_pr.outputs.total }} (${{ steps.invs_pr.outputs.deps }} via deps)" >> $GITHUB_STEP_SUMMARY + - name: Check if the PR does increase number of invalidations + if: steps.invs_pr.outputs.total > steps.invs_default.outputs.total + run: exit 1 \ No newline at end of file diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml new file mode 100644 index 0000000..d9ee073 --- /dev/null +++ b/.github/workflows/spelling.yml @@ -0,0 +1,15 @@ +name: Spell Check + +on: [pull_request] + +jobs: + typos-check: + name: Spell Check with Typos + runs-on: ubuntu-latest + steps: + - name: Checkout Actions Repository + uses: actions/checkout@v4 + - name: Check spelling + uses: crate-ci/typos@master + with: + config: .typos.toml \ No newline at end of file diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 0000000..f4ceaa3 --- /dev/null +++ b/.typos.toml @@ -0,0 +1,7 @@ +[default.extend-words] +ket = "ket" + +[type.ipynb] +# It detects false possitives in the base64 encoded images inside notebooks +extend-glob = ["*.ipynb", "*.tex"] +check-file = false diff --git a/README.md b/README.md index bb272a2..5a66a6f 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ And expanded for easier identification: The general purpose `MultiControl(controls, ocontrols, targets, targetXs)` can be used to create an arbitrary combination of muticontrol multiqubit gates. Each argument lists the indices of qubits that should get a certain symbol: `controls` is filled circles, `ocontrols` is empty circles, `targets` is the NOT symbol, and `targetXs` is the X symbols. -For named controled gates use `MultiControlU(str, controls, ocontrols, targets)`. +For named controlled gates use `MultiControlU(str, controls, ocontrols, targets)`. For noise events, you can use `Noise(targets)` or `NoiseAll()`.