├── .coveragerc ├── .cz.toml ├── .flake8 ├── .github ├── .templatesyncignore ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug.yaml │ ├── documentation.yaml │ ├── enhancement.yaml │ └── feature.yaml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── citation.yml │ ├── coverage.yml │ ├── lint.yml │ ├── release.yml │ ├── template-sync.yaml │ ├── test-development.yml │ ├── test-minimum.yml │ └── test.yml ├── .gitignore ├── .gitlint ├── .isort.cfg ├── .mypy.ini ├── .pre-commit-config.yaml ├── .pylintrc ├── .travis.yml ├── CHANGELOG.md ├── CITATION.bib ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DEPRECATION.md ├── FILEMAP.md ├── INSTALL.md ├── LICENSE.txt ├── README.md ├── docs ├── explanations │ └── 01_noise_amplification_on_basis_gates │ │ ├── 01_noise_amplification_on_basis_gates.ipynb │ │ └── data │ │ ├── cx │ │ ├── cx_behavioral_a.json │ │ ├── cx_behavioral_b.json │ │ ├── cx_granular_a.json │ │ └── cx_granular_b.json │ │ ├── rz │ │ ├── rz_granular_a.json │ │ └── rz_granular_b.json │ │ ├── sx │ │ ├── sx_behavioral_a.json │ │ ├── sx_behavioral_b.json │ │ ├── sx_granular_a.json │ │ └── sx_granular_b.json │ │ └── x │ │ ├── x_behavioral_a.json │ │ ├── x_behavioral_b.json │ │ ├── x_granular_a.json │ │ └── x_granular_b.json ├── how_tos │ └── 01_simulating_H2_ground_state │ │ ├── 01_simulating_H2_ground_state.ipynb │ │ └── data.json ├── media │ ├── base_estimator.drawio │ ├── base_estimator.png │ ├── cover.png │ ├── extrapolation.png │ ├── noise_amplification-folding.png │ ├── noise_amplification.png │ ├── noise_profile.png │ ├── runtime.png │ └── zne.png ├── reference_guide.md └── tutorials │ ├── 0-estimator.ipynb │ ├── 1-zne.ipynb │ ├── 2-noise_amplification.ipynb │ ├── chsh.ipynb │ └── teaser.ipynb ├── pyproject.toml ├── pytest.ini ├── test ├── __init__.py ├── conftest.py ├── extrapolation │ ├── __init__.py │ ├── test_exponential_extrapolator.py │ ├── test_extrapolator.py │ └── test_polynomial_extrapolator.py ├── meta │ ├── __init__.py │ ├── test_call.py │ ├── test_cls.py │ ├── test_init.py │ ├── test_job.py │ └── test_run.py ├── noise_amplification │ ├── __init__.py │ ├── folding_amplifier │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_folding_amplifier.py │ │ ├── test_global_folding_amplifier.py │ │ └── test_local_folding_amplifier.py │ └── test_noise_amplifier.py ├── test_immutable_strategy.py ├── test_zne_strategy.py └── utils │ ├── __init__.py │ ├── test_classconstant.py │ ├── test_docstrings.py │ ├── test_grouping.py │ ├── test_serialization.py │ ├── test_strategy.py │ ├── test_typing.py │ ├── test_unset.py │ └── test_validation.py ├── tools ├── extremal_dependency_versions.py ├── symlink_submodule.py └── travis_before_script.bash ├── tox.ini └── zne ├── __init__.py ├── extrapolation ├── __init__.py ├── exponential_extrapolator.py ├── extrapolator.py └── polynomial_extrapolator.py ├── immutable_strategy.py ├── meta ├── __init__.py ├── call.py ├── cls.py ├── init.py ├── job.py └── run.py ├── noise_amplification ├── __init__.py ├── folding_amplifier │ ├── __init__.py │ ├── folding_amplifier.py │ ├── global_folding_amplifier.py │ └── local_folding_amplifier.py └── noise_amplifier.py ├── qiskit ├── __init__.py └── primitives │ └── __init__.py ├── types.py ├── utils ├── __init__.py ├── classconstant.py ├── docstrings.py ├── grouping.py ├── serialization.py ├── standard_gates.py ├── strategy.py ├── typing.py ├── unset.py └── validation.py └── zne_strategy.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = true 3 | data_file = .coverage 4 | omit = .venv/*,tools/*,test/*,docs/*,*/qiskit/* 5 | 6 | [paths] 7 | 8 | [report] 9 | fail_under = 100 10 | precision = 2 11 | sort = cover 12 | 13 | [html] 14 | directory = coverage-html 15 | 16 | [xml] 17 | 18 | [json] 19 | pretty_print = true 20 | -------------------------------------------------------------------------------- /.cz.toml: -------------------------------------------------------------------------------- 1 | [tool.commitizen] 2 | name = "cz_conventional_commits" 3 | bump_message = "bump: version $current_version → $new_version" 4 | version_format = "$version" 5 | version = "1.3.1" 6 | version_files = [ 7 | ".cz.toml:version", 8 | "zne/__init__.py:__version__", 9 | "test/__init__.py:__version__", 10 | "CITATION.bib:version" 11 | ] 12 | update_changelog_on_bump = true 13 | annotated_tag = true 14 | style = [ 15 | ["qmark", "fg:#ff9d00 bold"], 16 | ["question", "bold"], 17 | ["answer", "fg:#ff9d00 bold"], 18 | ["pointer", "fg:#ff9d00 bold"], 19 | ["highlighted", "fg:#ff9d00 bold"], 20 | ["selected", "fg:#cc5454"], 21 | ["separator", "fg:#cc5454"], 22 | ["instruction", ""], 23 | ["text", ""], 24 | ["disabled", "fg:#858585 italic"] 25 | ] 26 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 100 3 | max-complexity = 10 4 | select = B,C,E,F,W,T4,B9 5 | ignore = E203,E266,E731,W503 6 | exclude = tools/*,*/qiskit/* -------------------------------------------------------------------------------- /.github/.templatesyncignore: -------------------------------------------------------------------------------- 1 | # SOURCE 2 | docs/ 3 | pyproject_qiskit/ 4 | test/ 5 | # PACKAGE 6 | .cz.toml 7 | CITATION.bib 8 | pyproject.toml 9 | README.md 10 | # CI/CD/CT 11 | .github/CODEOWNERS 12 | .github/workflows 13 | tox.ini -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file defines the set of people responsible for different sections 2 | # of the code. 3 | # 4 | # Being listed as a code owner on a section here means that user is empowered 5 | # to approve and merge pull requests for that section of code. It also comes 6 | # with an expected responsibility to review and maintain those subsections of 7 | # the repository. 8 | # 9 | # The @qiskit-community/qiskit-prototypes team is the group who're responsible 10 | # for the entire project and are empowered to approve merge code in any 11 | # part of the repository. Any member of the terra-core group should not be 12 | # listed in this file as it's redudant. 13 | # 14 | # A PR can be merged when approved by at least one codeowner. For PRs that touch 15 | # more than one section with non-overlapping codeowners more than one codeowner 16 | # may be required to approve a PR. 17 | # 18 | # However, every contributor can (and should!) review open PRs. This 19 | # file is just about outlining responsiblity for maintainership and final 20 | # review/merging of PRs in different subsections of the repository. 21 | 22 | # Global rule, unless specialized by a later one 23 | * @qiskit-community/qiskit-prototypes 24 | 25 | # Folders (also their corresponding tests) 26 | 27 | # Override the directories to have _no_ code owners, so any review 28 | # from somebody with write access is acceptable. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yaml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug report 2 | description: Report defects to make amends 3 | labels: [bug, triage] 4 | assignees: 5 | - "" 6 | 7 | body: 8 | 9 | ################################################################################ 10 | ## SYSTEM INFO 11 | ################################################################################ 12 | - type: markdown 13 | attributes: 14 | value: | 15 | ### System information 16 | Please, provide some information about your system for debugging purposes. 17 | 18 | - type: input 19 | id: device 20 | attributes: 21 | label: Device 22 | placeholder: e.g. MacBook Pro (16-inch, 2019) 23 | validations: 24 | required: true 25 | - type: input 26 | id: os 27 | attributes: 28 | label: OS 29 | placeholder: e.g. macOS Monterey version 12.3.1 30 | validations: 31 | required: true 32 | - type: input 33 | id: python 34 | attributes: 35 | label: Python version 36 | placeholder: e.g. 3.12.0 37 | validations: 38 | required: true 39 | - type: input 40 | id: version 41 | attributes: 42 | label: Release version or branch/commit 43 | placeholder: e.g. 0.0.0 -OR- main/0f3f39f 44 | validations: 45 | required: true 46 | 47 | 48 | ################################################################################ 49 | ## REPORT 50 | ################################################################################ 51 | - type: markdown 52 | attributes: 53 | value: | 54 | ### Bug report 55 | 56 | - type: textarea 57 | id: current-behavior 58 | attributes: 59 | label: What is the current behavior? 60 | description: A clear and concise description of what the current behavior is. If applicable, add screenshots to help explain your problem. 61 | placeholder: Current behavior 62 | validations: 63 | required: true 64 | 65 | - type: textarea 66 | id: expected-behavior 67 | attributes: 68 | label: What is the expected behavior? 69 | description: A clear and concise description of what you expected to happen. 70 | placeholder: Expected behavior 71 | validations: 72 | required: true 73 | 74 | - type: textarea 75 | id: reproduce 76 | attributes: 77 | label: Steps to reproduce the problem 78 | description: Steps to reproduce the bug. A minimal, working, code example with output is best. If you are copying in code, please remember to enclose it in triple backticks so that it displays correctly. 79 | placeholder: | 80 | ```python 81 | from qiskit import QuantumCircuit 82 | circuit = QuantumCircuit(4) 83 | ``` 84 | validations: 85 | required: true 86 | 87 | 88 | ################################################################################ 89 | ## OPTIONAL 90 | ################################################################################ 91 | - type: markdown 92 | attributes: 93 | value: | 94 | ### Additional feedback 95 | 96 | - type: textarea 97 | id: context 98 | attributes: 99 | label: Context 100 | description: | 101 | Add any other context about the problem here if any. 102 | placeholder: Context 103 | validations: 104 | required: false 105 | 106 | - type: textarea 107 | id: suggestions 108 | attributes: 109 | label: Suggestions 110 | description: Not required, but if you have suggestions for how a contributor should fix this, or any problems we should be aware of, let us know. 111 | placeholder: Suggestions 112 | validations: 113 | required: false 114 | 115 | 116 | ################################################################################ 117 | ## AGREEMENT 118 | ################################################################################ 119 | - type: checkboxes 120 | id: terms 121 | attributes: 122 | label: Code of Conduct 123 | description: By submitting this issue, you agree to follow our Code of Conduct. 124 | options: 125 | - label: I agree to follow this project's Code of Conduct 126 | required: true 127 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yaml: -------------------------------------------------------------------------------- 1 | name: 📖 Documentation report 2 | description: Report suggestions for documentation upgrades 3 | labels: [documentation, triage] 4 | assignees: 5 | - "" 6 | 7 | body: 8 | 9 | ################################################################################ 10 | ## REPORT 11 | ################################################################################ 12 | - type: markdown 13 | attributes: 14 | value: | 15 | ### Documentation report 16 | 17 | - type: textarea 18 | id: current-behavior 19 | attributes: 20 | label: What is the current behavior? 21 | description: A clear and concise description of what the current status is. If applicable, add screenshots to help explain your problem. 22 | placeholder: Current behavior 23 | validations: 24 | required: true 25 | 26 | - type: textarea 27 | id: expected-behavior 28 | attributes: 29 | label: What is the expected behavior? 30 | description: A clear and concise description of what you expected. 31 | placeholder: Expected behavior 32 | validations: 33 | required: true 34 | 35 | 36 | ################################################################################ 37 | ## OPTIONAL 38 | ################################################################################ 39 | - type: markdown 40 | attributes: 41 | value: | 42 | ### Additional feedback 43 | 44 | - type: textarea 45 | id: context 46 | attributes: 47 | label: Context 48 | description: | 49 | Add any other context about the problem here if any. 50 | placeholder: Context 51 | validations: 52 | required: false 53 | 54 | - type: textarea 55 | id: suggestions 56 | attributes: 57 | label: Suggestions 58 | description: Not required, but if you have suggestions for how a contributor should fix this, or any problems we should be aware of, let us know. 59 | placeholder: Suggestions 60 | validations: 61 | required: false 62 | 63 | 64 | ################################################################################ 65 | ## AGREEMENT 66 | ################################################################################ 67 | - type: checkboxes 68 | id: terms 69 | attributes: 70 | label: Code of Conduct 71 | description: By submitting this issue, you agree to follow our Code of Conduct. 72 | options: 73 | - label: I agree to follow this project's Code of Conduct 74 | required: true 75 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.yaml: -------------------------------------------------------------------------------- 1 | name: ✨ Enhancement request 2 | description: Suggest a non-feature improvement for this project 3 | labels: [enhancement, triage] 4 | assignees: 5 | - "" 6 | 7 | body: 8 | 9 | ################################################################################ 10 | ## REPORT 11 | ################################################################################ 12 | - type: markdown 13 | attributes: 14 | value: | 15 | ### Enhancement request 16 | 17 | - type: textarea 18 | id: current-behavior 19 | attributes: 20 | label: What is the current behavior? 21 | description: A clear and concise description of what the current status is. If applicable, add screenshots to help explain your point. 22 | placeholder: Current behavior 23 | validations: 24 | required: true 25 | 26 | - type: textarea 27 | id: expected-behavior 28 | attributes: 29 | label: What is the expected enhancement? 30 | description: A clear and concise description of what you expect. 31 | placeholder: Expected behavior 32 | validations: 33 | required: true 34 | 35 | 36 | ################################################################################ 37 | ## OPTIONAL 38 | ################################################################################ 39 | - type: markdown 40 | attributes: 41 | value: | 42 | ### Additional feedback 43 | 44 | - type: textarea 45 | id: context 46 | attributes: 47 | label: Context 48 | description: | 49 | Add any other context about the problem here if any. 50 | placeholder: Context 51 | validations: 52 | required: false 53 | 54 | - type: textarea 55 | id: suggestions 56 | attributes: 57 | label: Suggestions 58 | description: Not required, but if you have suggestions for how a contributor should address this, or any problems we should be aware of, let us know. 59 | placeholder: Suggestions 60 | validations: 61 | required: false 62 | 63 | 64 | ################################################################################ 65 | ## AGREEMENT 66 | ################################################################################ 67 | - type: checkboxes 68 | id: terms 69 | attributes: 70 | label: Code of Conduct 71 | description: By submitting this issue, you agree to follow our Code of Conduct. 72 | options: 73 | - label: I agree to follow this project's Code of Conduct 74 | required: true 75 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yaml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature request 2 | description: Suggest an idea for this project 3 | labels: [feature, triage] 4 | assignees: 5 | - "" 6 | 7 | body: 8 | 9 | ################################################################################ 10 | ## REPORT 11 | ################################################################################ 12 | - type: markdown 13 | attributes: 14 | value: | 15 | ### Feature request 16 | 17 | - type: textarea 18 | id: current-behavior 19 | attributes: 20 | label: What is the current behavior? 21 | description: A clear and concise description of what the current status is. If applicable, add screenshots to help explain your point. 22 | placeholder: Current behavior 23 | validations: 24 | required: true 25 | 26 | - type: textarea 27 | id: expected-behavior 28 | attributes: 29 | label: What is the expected enhancement? 30 | description: A clear and concise description of what you expect. 31 | placeholder: Expected behavior 32 | validations: 33 | required: true 34 | 35 | 36 | ################################################################################ 37 | ## OPTIONAL 38 | ################################################################################ 39 | - type: markdown 40 | attributes: 41 | value: | 42 | ### Additional feedback 43 | 44 | - type: textarea 45 | id: context 46 | attributes: 47 | label: Context 48 | description: | 49 | Add any other context about the problem here if any. 50 | placeholder: Context 51 | validations: 52 | required: false 53 | 54 | - type: textarea 55 | id: suggestions 56 | attributes: 57 | label: Suggestions 58 | description: Not required, but if you have suggestions for how a contributor should address this, or any problems we should be aware of, let us know. 59 | placeholder: Suggestions 60 | validations: 61 | required: false 62 | 63 | 64 | ################################################################################ 65 | ## AGREEMENT 66 | ################################################################################ 67 | - type: checkboxes 68 | id: terms 69 | attributes: 70 | label: Code of Conduct 71 | description: By submitting this issue, you agree to follow our Code of Conduct. 72 | options: 73 | - label: I agree to follow this project's Code of Conduct 74 | required: true 75 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | ### Summary 14 | 15 | 16 | 17 | ### Details and comments 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/citation.yml: -------------------------------------------------------------------------------- 1 | name: Citation preview 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: ['CITATION.bib', '.github/workflows/citation.yml'] 7 | pull_request: 8 | branches: [ main ] 9 | paths: ['CITATION.bib', '.github/workflows/citation.yml'] 10 | 11 | jobs: 12 | build-preview: 13 | name: build-preview 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 30 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Check for non-ASCII characters 19 | run: | 20 | # Fail immediately if there are any non-ASCII characters in 21 | # the BibTeX source. We prefer "escaped codes" rather than 22 | # UTF-8 characters in order to ensure the bibliography will 23 | # display correctly even in documents that do not contain 24 | # \usepackage[utf8]{inputenc}. 25 | if [ -f "CITATION.bib" ]; then 26 | python3 -c 'open("CITATION.bib", encoding="ascii").read()' 27 | fi 28 | - name: Install LaTeX 29 | run: | 30 | if [ -f "CITATION.bib" ]; then 31 | sudo apt-get update 32 | sudo apt-get install -y texlive-latex-base texlive-publishers 33 | fi 34 | - name: Run LaTeX 35 | run: | 36 | if [ -f "CITATION.bib" ]; then 37 | arr=(${GITHUB_REPOSITORY//\// }) 38 | REPO=${arr[1]} 39 | export PROTOTYPE=${REPO#"prototype-"} 40 | cat <<- EOF > preview.tex 41 | \documentclass[preprint,aps,physrev,notitlepage]{revtex4-2} 42 | \usepackage{hyperref} 43 | \begin{document} 44 | \title{\texttt{$PROTOTYPE} BibTeX test} 45 | \maketitle 46 | \noindent 47 | \texttt{$PROTOTYPE} 48 | \cite{$PROTOTYPE} 49 | \bibliography{CITATION} 50 | \end{document} 51 | EOF 52 | pdflatex preview 53 | fi 54 | - name: Run BibTeX 55 | run: | 56 | if [ -f "CITATION.bib" ]; then 57 | bibtex preview 58 | fi 59 | - name: Re-run LaTeX 60 | run: | 61 | if [ -f "CITATION.bib" ]; then 62 | pdflatex preview 63 | pdflatex preview 64 | fi 65 | - name: Upload PDF 66 | if: always() 67 | uses: actions/upload-artifact@v2 68 | with: 69 | name: preview.pdf 70 | path: preview.pdf -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Code coverage 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'stable/**' 8 | pull_request: 9 | branches: 10 | - main 11 | - 'stable/**' 12 | 13 | jobs: 14 | coverage: 15 | name: coverage (${{ matrix.os }}, py${{ matrix.python-version }}) 16 | runs-on: ${{ matrix.os }} 17 | timeout-minutes: 30 18 | strategy: 19 | max-parallel: 4 20 | matrix: 21 | os: [ubuntu-latest] 22 | python-version: ['3.12'] 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python ${{ matrix.python-version }} 26 | uses: actions/setup-python@v2 27 | with: 28 | python-version: ${{ matrix.python-version }} 29 | - name: Install tox 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install tox coveragepy-lcov 'coverage<7' 33 | - name: Run coverage 34 | run: tox -e coverage 35 | - name: Convert to lcov 36 | if: always() 37 | run: coveragepy-lcov --output_file_path coveralls.info 38 | - name: Upload report to Coveralls 39 | if: success() 40 | uses: coverallsapp/github-action@master 41 | with: 42 | github-token: ${{ secrets.GITHUB_TOKEN }} 43 | path-to-lcov: coveralls.info 44 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'stable/**' 8 | pull_request: 9 | branches: 10 | - main 11 | - 'stable/**' 12 | 13 | jobs: 14 | lint: 15 | name: lint (${{ matrix.os }}, py${{ matrix.python-version }}) 16 | runs-on: ${{ matrix.os }} 17 | timeout-minutes: 30 18 | strategy: 19 | max-parallel: 4 20 | matrix: 21 | os: [ubuntu-latest] 22 | python-version: ['3.12'] 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python ${{ matrix.python-version }} 26 | uses: actions/setup-python@v2 27 | with: 28 | python-version: ${{ matrix.python-version }} 29 | - name: Install tox 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install tox 33 | - name: Run styles check 34 | shell: bash 35 | run: | 36 | # eval $(ssh-agent -s) 37 | # ssh-add - <<< '${{ secrets.PRIVATE_SSH_KEY }}' 38 | tox -e lint 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "[0-9]+.[0-9]+.[0-9]+*" 7 | 8 | jobs: 9 | 10 | github: 11 | name: github 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout tag 15 | uses: actions/checkout@v3 16 | with: 17 | ref: ${{ github.ref_name }} 18 | - name: Publish release 19 | uses: eloquent/github-release-action@v3 20 | if: github.ref_type == 'tag' 21 | with: 22 | token: ${{ secrets.GITHUB_TOKEN }} 23 | generateReleaseNotes: "true" 24 | 25 | pypi: 26 | name: pypi 27 | runs-on: ubuntu-latest 28 | needs: github 29 | steps: 30 | - name: Checkout tag 31 | uses: actions/checkout@v3 32 | with: 33 | ref: ${{ github.ref_name }} 34 | - name: Install flit 35 | run: | 36 | python -m pip install --upgrade pip 37 | pip install flit 38 | - name: Publish release 39 | env: 40 | FLIT_INDEX_URL: https://upload.pypi.org/legacy/ 41 | FLIT_USERNAME: __token__ 42 | FLIT_PASSWORD: ${{ secrets.PYPI_TOKEN }} 43 | run: flit publish 44 | -------------------------------------------------------------------------------- /.github/workflows/template-sync.yaml: -------------------------------------------------------------------------------- 1 | name: Template sync 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 1 * *" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | template-sync: 10 | name: Sync repo template 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | - name: Sync template 17 | uses: AndreasAugustin/actions-template-sync@v0.7.3 18 | with: 19 | github_token: ${{ secrets.GITHUB_TOKEN }} 20 | source_repo_path: pedrorrivero/pyproject-qiskit 21 | upstream_branch: main 22 | pr_title: "chore(template): merge template changes" 23 | pr_labels: template 24 | pr_commit_msg: "chore(template): merge template changes" 25 | -------------------------------------------------------------------------------- /.github/workflows/test-development.yml: -------------------------------------------------------------------------------- 1 | name: Development version tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'stable/**' 8 | pull_request: 9 | branches: 10 | - main 11 | - 'stable/**' 12 | schedule: 13 | - cron: '0 0 * * *' 14 | 15 | jobs: 16 | dev-tests: 17 | name: dev-tests (${{ matrix.os }}, py${{ matrix.python-version }}) 18 | runs-on: ${{ matrix.os }} 19 | timeout-minutes: 30 20 | strategy: 21 | max-parallel: 4 22 | matrix: 23 | os: [ubuntu-latest] 24 | python-version: ['3.12'] 25 | steps: 26 | - uses: actions/checkout@v3 27 | - name: Set up Python ${{ matrix.python-version }} 28 | uses: actions/setup-python@v1 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | - name: Install dependencies (development versions) 32 | shell: bash 33 | run: | 34 | python -m pip install --upgrade pip tox 35 | python -m pip install toml fire 36 | python tools/extremal_dependency_versions.py pin_dependencies dev --inplace 37 | - name: Modify tox.ini for more thorough check 38 | shell: bash 39 | run: | 40 | sed -i.bak -E '/#.*CI:[[:space:]]*skip-next-line/I{N;d;}' tox.ini 41 | cat tox.ini 42 | - name: Test using tox environment 43 | shell: bash 44 | run: | 45 | # eval $(ssh-agent -s) 46 | # ssh-add - <<< '${{ secrets.PRIVATE_SSH_KEY }}' 47 | pver=${{ matrix.python-version }} 48 | tox -e py${pver/./} -------------------------------------------------------------------------------- /.github/workflows/test-minimum.yml: -------------------------------------------------------------------------------- 1 | name: Minimum version tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'stable/**' 8 | pull_request: 9 | branches: 10 | - main 11 | - 'stable/**' 12 | 13 | jobs: 14 | min-tests: 15 | name: min-tests (${{ matrix.os }}, py${{ matrix.python-version }}) 16 | runs-on: ${{ matrix.os }} 17 | timeout-minutes: 30 18 | strategy: 19 | max-parallel: 4 20 | matrix: 21 | os: [ubuntu-latest] 22 | python-version: ['3.8'] 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python ${{ matrix.python-version }} 26 | uses: actions/setup-python@v1 27 | with: 28 | python-version: ${{ matrix.python-version }} 29 | - name: Install dependencies (minimum versions) 30 | shell: bash 31 | run: | 32 | python -m pip install --upgrade pip 33 | python -m pip install toml fire 34 | pip install "tox==$(./tools/extremal_dependency_versions.py get_tox_minversion)" 35 | python tools/extremal_dependency_versions.py pin_dependencies min --inplace 36 | - name: Modify tox.ini for more thorough check 37 | shell: bash 38 | run: | 39 | sed -i.bak -E '/#.*CI:[[:space:]]*skip-next-line/I{N;d;}' tox.ini 40 | cat tox.ini 41 | - name: Test using tox environment 42 | shell: bash 43 | run: | 44 | # eval $(ssh-agent -s) 45 | # ssh-add - <<< '${{ secrets.PRIVATE_SSH_KEY }}' 46 | pver=${{ matrix.python-version }} 47 | tox -e py${pver/./} -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'stable/**' 8 | pull_request: 9 | branches: 10 | - main 11 | - 'stable/**' 12 | schedule: 13 | - cron: '0 1 * * *' 14 | 15 | jobs: 16 | tests: 17 | name: tests (${{ matrix.os }}, py${{ matrix.python-version }}) 18 | runs-on: ${{ matrix.os }} 19 | timeout-minutes: 30 20 | strategy: 21 | max-parallel: 4 22 | matrix: 23 | os: [ubuntu-latest] 24 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] 25 | include: 26 | - os: macos-latest 27 | python-version: '3.8' 28 | - os: windows-latest 29 | python-version: '3.8' 30 | steps: 31 | - uses: actions/checkout@v3 32 | - name: Set up Python ${{ matrix.python-version }} 33 | uses: actions/setup-python@v1 34 | with: 35 | python-version: ${{ matrix.python-version }} 36 | - name: Install dependencies 37 | run: | 38 | python -m pip install --upgrade pip 39 | pip install tox 40 | - name: Modify tox.ini for more thorough check 41 | shell: bash 42 | run: | 43 | sed -i.bak -E '/#.*CI:[[:space:]]*skip-next-line/I{N;d;}' tox.ini 44 | cat tox.ini 45 | - name: Test using tox environment 46 | shell: bash 47 | run: | 48 | # eval $(ssh-agent -s) 49 | # ssh-add - <<< '${{ secrets.PRIVATE_SSH_KEY }}' 50 | pver=${{ matrix.python-version }} 51 | tox -e py${pver/./} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # MISC 2 | .ipynb_checkpoints 3 | __pycache__ 4 | .DS_Store 5 | 6 | 7 | # IDEs 8 | .idea/ 9 | .vscode/ 10 | 11 | 12 | # VIRTUAL ENVIRONMENTS 13 | venv/ 14 | .venv/ 15 | ## [pyenv] 16 | .python-version 17 | ## [dotenv] 18 | .env 19 | ## [virtualenv] 20 | ENV/ 21 | 22 | 23 | # UNIT TEST / COVERAGE REPORTS 24 | .tox/ 25 | .coverage 26 | .coverage.* 27 | htmlcov/ 28 | coverage-html/ 29 | coverage.xml 30 | *,cover 31 | .*cache 32 | nosetests.xml 33 | .hypothesis/ 34 | 35 | 36 | # SPHINX DOCUMENTATION 37 | docs/_build/ 38 | docs/stubs/ 39 | 40 | 41 | # DISTRIBUTION / PACKAGING 42 | .Python 43 | env/ 44 | build/ 45 | develop-eggs/ 46 | dist/ 47 | downloads/ 48 | eggs/ 49 | .eggs/ 50 | lib/ 51 | lib64/ 52 | parts/ 53 | sdist/ 54 | var/ 55 | *.egg-info/ 56 | .installed.cfg 57 | *.egg 58 | 59 | 60 | # INSTALLER LOGS 61 | pip-log.txt 62 | pip-delete-this-directory.txt 63 | -------------------------------------------------------------------------------- /.gitlint: -------------------------------------------------------------------------------- 1 | # Edit this file as you like. 2 | # 3 | # All these sections are optional. Each section with the exception of [general] represents 4 | # one rule and each key in it is an option for that specific rule. 5 | # 6 | # Rules and sections can be referenced by their full name or by id. For example 7 | # section "[body-max-line-length]" could be written as "[B1]". Full section names are 8 | # used in here for clarity. 9 | # 10 | [general] 11 | # Ignore certain rules, this example uses both full name and id 12 | ignore=B6 13 | contrib=CT1 14 | 15 | # verbosity should be a value between 1 and 3, the commandline -v flags take precedence over this 16 | verbosity = 3 17 | 18 | # By default gitlint will ignore merge, revert, fixup and squash commits. 19 | # ignore-merge-commits=true 20 | # ignore-revert-commits=true 21 | # ignore-fixup-commits=true 22 | # ignore-squash-commits=true 23 | 24 | # Ignore any data send to gitlint via stdin 25 | # ignore-stdin=true 26 | 27 | # Fetch additional meta-data from the local repository when manually passing a 28 | # commit message to gitlint via stdin or --commit-msg. Disabled by default. 29 | # staged=true 30 | 31 | # Enable debug mode (prints more output). Disabled by default. 32 | # debug=true 33 | 34 | # Enable community contributed rules 35 | # See http://jorisroovers.github.io/gitlint/contrib_rules for details 36 | # contrib=contrib-title-conventional-commits,CC1 37 | 38 | # Set the extra-path where gitlint will search for user defined rules 39 | # See http://jorisroovers.github.io/gitlint/user_defined_rules for details 40 | # extra-path=examples/ 41 | 42 | # This is an example of how to configure the "title-max-length" rule and 43 | # set the line-length it enforces to 80 44 | # [title-max-length] 45 | # line-length=50 46 | 47 | # [title-must-not-contain-word] 48 | # Comma-separated list of words that should not occur in the title. Matching is case 49 | # insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING" 50 | # will not cause a violation, but "WIP: my title" will. 51 | # words=wip 52 | 53 | # [title-match-regex] 54 | # python like regex (https://docs.python.org/2/library/re.html) that the 55 | # commit-msg title must be matched to. 56 | # Note that the regex can contradict with other rules if not used correctly 57 | # (e.g. title-must-not-contain-word). 58 | # regex=^US[0-9]+ 59 | 60 | # [body-max-line-length] 61 | # line-length=72 62 | 63 | # [body-min-length] 64 | # min-length=5 65 | 66 | # [body-is-missing] 67 | # Whether to ignore this rule on merge commits (which typically only have a title) 68 | # default = True 69 | # ignore-merge-commits=false 70 | 71 | # [body-changed-file-mention] 72 | # List of files that need to be explicitly mentioned in the body when they are changed 73 | # This is useful for when developers often erroneously edit certain files or git submodules. 74 | # By specifying this rule, developers can only change the file when they explicitly reference 75 | # it in the commit message. 76 | # files=gitlint/rules.py,README.md 77 | 78 | # [author-valid-email] 79 | # python like regex (https://docs.python.org/2/library/re.html) that the 80 | # commit author email address should be matched to 81 | # For example, use the following regex if you only want to allow email addresses from foo.com 82 | # regex=[^@]+@foo.com 83 | 84 | # [ignore-by-title] 85 | # Ignore certain rules for commits of which the title matches a regex 86 | # E.g. Match commit titles that start with "Release" 87 | # regex=^Release(.*) 88 | 89 | # Ignore certain rules, you can reference them by their id or by their full name 90 | # Use 'all' to ignore all rules 91 | # ignore=T1,body-min-length 92 | 93 | # [ignore-by-body] 94 | # Ignore certain rules for commits of which the body has a line that matches a regex 95 | # E.g. Match bodies that have a line that that contain "release" 96 | # regex=(.*)release(.*) 97 | # 98 | # Ignore certain rules, you can reference them by their id or by their full name 99 | # Use 'all' to ignore all rules 100 | # ignore=T1,body-min-length 101 | 102 | # This is a contrib rule - a community contributed rule. These are disabled by default. 103 | # You need to explicitly enable them one-by-one by adding them to the "contrib" option 104 | # under [general] section above. 105 | [contrib-title-conventional-commits] 106 | # Specify allowed commit types. For details see: https://www.conventionalcommits.org/ 107 | # Default: types = fix,feat,chore,docs,style,refactor,perf,test,revert 108 | types = build,bump,ci,docs,feat,fix,perf,refactor,style,test,chore,revert 109 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | skip_glob = tools/* 3 | extend_skip_glob = */qiskit/* 4 | profile = black 5 | ensure_newline_before_comments = true 6 | filter_files = true 7 | force_grid_wrap = 0 8 | include_trailing_comma = true 9 | multi_line_output = 3 10 | remove_redundant_aliases = true 11 | use_parentheses = true 12 | -------------------------------------------------------------------------------- /.mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | exclude = (qiskit|^tools|^test|^docs)/.* 3 | ignore_missing_imports = True 4 | no_implicit_optional = True 5 | scripts_are_modules = True 6 | strict_optional = False 7 | warn_redundant_casts = True 8 | warn_unused_configs = True 9 | warn_unused_ignores = True 10 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | minimum_pre_commit_version: '2.18.0' 2 | default_install_hook_types: [pre-commit, commit-msg] 3 | default_stages: [commit, manual] 4 | fail_fast: false 5 | 6 | 7 | repos: 8 | 9 | ################################################################################ 10 | ## pre-commit 11 | ################################################################################ 12 | - repo: https://github.com/PyCQA/flake8 13 | rev: 5.0.4 14 | hooks: 15 | - id: flake8 16 | name: flake8 17 | description: "PyFlakes lint + PyCodeStyle + McCabe cyclomatic complexity" 18 | stages: [commit] 19 | entry: flake8 20 | language: python 21 | files: .*\.py 22 | 23 | - repo: https://github.com/PyCQA/isort 24 | rev: 5.12.0 25 | hooks: 26 | - id: isort 27 | name: isort 28 | description: "Sort python imports" 29 | stages: [commit] 30 | entry: isort --check-only 31 | language: python 32 | files: .*\.py 33 | 34 | - repo: https://github.com/psf/black 35 | rev: 22.10.0 36 | hooks: 37 | - id: black 38 | name: black 39 | description: "Python code formatter" 40 | stages: [commit] 41 | entry: black --check 42 | language: python 43 | files: .*\.py 44 | 45 | - repo: https://github.com/PyCQA/pylint 46 | rev: v2.15.4 47 | hooks: 48 | - id: pylint 49 | name: pylint 50 | description: "Static code analysis" 51 | stages: [commit] 52 | entry: pylint --reports=no --disable=import-error 53 | language: python 54 | files: .*\.py 55 | exclude: (tools|test|docs)/.* 56 | 57 | - repo: https://github.com/pre-commit/mirrors-mypy 58 | rev: v0.982 59 | hooks: 60 | - id: mypy 61 | name: mypy 62 | description: "Static type checker" 63 | stages: [commit] 64 | entry: mypy 65 | language: python 66 | files: .*\.py 67 | exclude: (tools|test|docs)/.* 68 | 69 | 70 | ################################################################################ 71 | ## commit-msg 72 | ################################################################################ 73 | - repo: local 74 | hooks: 75 | - id: commitizen 76 | name: commitizen check 77 | description: "Enforce conventional commits" 78 | stages: [commit-msg] 79 | entry: cz check --commit-msg-file 80 | language: python 81 | 82 | - repo: https://github.com/jorisroovers/gitlint 83 | rev: v0.17.0 84 | hooks: 85 | - id: gitlint 86 | name: gitlint 87 | description: "Enforce extra committing rules" 88 | stages: [commit-msg] 89 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | install: 4 | - pip install -U pip 5 | - pip install -U tox 6 | - pip install toml fire 7 | language: python 8 | before_script: 9 | - bash tools/travis_before_script.bash 10 | - export PATH=${HOME}/.cargo/bin:$PATH 11 | script: 12 | - tox 13 | notifications: 14 | email: 15 | recipients: 16 | - pedro.rivero@ibm.com 17 | on_success: change 18 | on_failure: change 19 | 20 | jobs: 21 | include: 22 | - os: windows 23 | python: "3.8" 24 | env: TOXENV=py38 25 | - os: windows 26 | python: "3.9" 27 | env: TOXENV=py39 28 | - os: windows 29 | python: "3.10" 30 | env: TOXENV=py310 31 | - os: windows 32 | python: "3.11" 33 | env: TOXENV=py311 34 | - os: windows 35 | python: "3.12" 36 | env: TOXENV=py312 37 | 38 | - os: osx 39 | python: "3.8" 40 | env: TOXENV=py38 41 | - os: osx 42 | python: "3.9" 43 | env: TOXENV=py39 44 | - os: osx 45 | python: "3.10" 46 | env: TOXENV=py310 47 | - os: osx 48 | python: "3.11" 49 | env: TOXENV=py311 50 | - os: osx 51 | python: "3.12" 52 | env: TOXENV=py312 53 | 54 | - os: linux 55 | python: "3.8" 56 | env: TOXENV=py38 57 | - os: linux 58 | python: "3.9" 59 | env: TOXENV=py39 60 | - os: linux 61 | python: "3.10" 62 | env: TOXENV=py310 63 | - os: linux 64 | python: "3.11" 65 | env: TOXENV=py311 66 | - os: linux 67 | python: "3.12" 68 | env: TOXENV=py312 69 | 70 | - os: linux 71 | python: "3.12" 72 | env: TOXENV=lint 73 | - os: linux 74 | python: "3.12" 75 | env: TOXENV=coverage 76 | 77 | - name: "Minimum version tests" 78 | os: linux 79 | python: "3.8" 80 | env: 81 | - TOXENV=py38 82 | - STRATEGY=min 83 | - name: "Development version tests" 84 | os: linux 85 | python: "3.12" 86 | env: 87 | - TOXENV=py312 88 | - STRATEGY=dev 89 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.3.1 (2024-02-06) 2 | 3 | ### Refactor 4 | 5 | - drop BasePrimitiveResult.experiments and BasePrimitiveResult.num_experiments (#63) 6 | 7 | ## 1.3.0 (2024-02-05) 8 | 9 | ### Feat 10 | 11 | - **folding_amplifier**: add multi-qubit amplifier facade (#61) 12 | 13 | ### Fix 14 | 15 | - **zne_estrategy**: update default amplifier to multi-qubit (#62) 16 | - **extrapolation**: support zero variance inputs (#60) 17 | 18 | ## 1.2.2 (2023-12-22) 19 | 20 | ### Fix 21 | 22 | - **folding_amplifier**: avoid insertion of unnecessary barriers 23 | 24 | ### Refactor 25 | 26 | - **noise_amplifier**: stop using Circuit and DAG amplifier classes 27 | 28 | ## 1.2.1 (2023-12-02) 29 | 30 | ### Fix 31 | 32 | - **folding_amplifier**: add missing barriers setting to facades 33 | 34 | ## 1.2.0 (2023-12-02) 35 | 36 | ### Feat 37 | 38 | - **folding_amplifier**: add setting to control barrier insertion (#52) 39 | 40 | ## 1.1.0 (2023-05-25) 41 | 42 | ### Feat 43 | 44 | - **extrapolator**: add exponential extrapolators (#44) 45 | - **extrapolator**: add OLS extrapolator interface (#43) 46 | - **extrapolation**: new polynomial extrapolator with std errors (#41) 47 | - **utils**: add normalize_array typing util (#42) 48 | - **utils**: add strategy class decorator (#34) 49 | - **utils**: add classconstant descriptor (#32) 50 | - **unset**: add string representation (#31) 51 | - **unset**: add repr 52 | 53 | ### Refactor 54 | 55 | - discontinue using isint from typing utils module (#40) 56 | - **utils**: remove dict_library module (#39) 57 | - **cls**: update class through type call in zne function 58 | 59 | ### Perf 60 | 61 | - **folding_amplifier**: remove unnecessary barriers in folded circuits (#37) 62 | 63 | ## 1.0.0 (2022-11-08) 64 | 65 | ### Fix 66 | 67 | - **zne_strategy**: remove caching since mutable (#29) 68 | - **global_folding_amplifier**: add barriers between all gates (#28) 69 | 70 | ## 1.0.0rc0 (2022-11-04) 71 | 72 | ### Feat 73 | 74 | - **validation**: add quality descriptor (#21) 75 | - **utils**: add unset singleton (#22) 76 | 77 | ### Refactor 78 | 79 | - **zne_strategy**: discontinue frozen dataclass (#20) 80 | 81 | ## 1.0.0b2 (2022-10-27) 82 | 83 | ### Feat 84 | 85 | - **extrapolation**: add quadratic, cubic, and quartic extrapolators (#18) 86 | - **noise_amplification**: add two-qubit noise amplifier (#19) 87 | 88 | ## 1.0.0b1 (2022-10-20) 89 | 90 | ### Feat 91 | 92 | - **zne_strategy**: add is_noop check (#15) 93 | - **meta**: allow bypassing ZNE on run (#13) 94 | - **zne_strategy**: add noop constructor (#12) 95 | 96 | ## 1.0.0b0 (2022-10-17) 97 | -------------------------------------------------------------------------------- /CITATION.bib: -------------------------------------------------------------------------------- 1 | @misc{zne, 2 | author = {Pedro Rivero and Friederike Metz and Areeq Hasan and Agata M. Bra\'{n}czyk and Caleb Johnson}, 3 | title = {Zero Noise Extrapolation prototype}, 4 | year = {2023}, 5 | publisher = {Zenodo}, 6 | version = {1.3.1}, 7 | doi = {}, 8 | url = {https://github.com/qiskit-community/prototype-zne}, 9 | } 10 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Code of Conduct 4 | All members of this project agree to adhere to the Qiskit Code of Conduct listed at [https://github.com/Qiskit/qiskit/blob/master/CODE_OF_CONDUCT.md](https://github.com/Qiskit/qiskit/blob/master/CODE_OF_CONDUCT.md) 5 | 6 | ---- 7 | 8 | License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), 9 | Copyright Contributors to Qiskit. 10 | -------------------------------------------------------------------------------- /DEPRECATION.md: -------------------------------------------------------------------------------- 1 | # Deprecation Policy 2 | 3 | This software library is meant to evolve rapidly and, as such, does not follow [Qiskit's deprecation policy](https://github.com/Qiskit/qiskit/blob/main/DEPRECATION.md). Nonetheless, we will always try to give users ample time to adjust to changes without breaking code that they have already written. 4 | 5 | To avoid dependency issues, exact version specification is encouraged if no upcoming updates are needed (e.g. [version pinning](https://www.easypost.com/dependency-pinning-guide)). 6 | 7 | 8 | ## Supported API and pre-releases 9 | 10 | Following [Python's naming conventions](https://realpython.com/python-double-underscore/) as outlined by [PEP8](https://peps.python.org/pep-0008/), any module, variable, function, or class whose name starts with a leading underscore `_` will be considered _internal_ and not part of this library's supported _application programming interface_ (API). 11 | 12 | Some capabilities may be pre-released before reaching a stable state. These will not adhere to the deprecation policies in place and will actively warn users of their unstable, pre-release, condition until they are deemed otherwise. 13 | 14 | Every other piece of source code conforms the _public-facing_ API of this library and will therefore be subject to the rules outlined in this document. 15 | 16 | 17 | ## Migrations 18 | 19 | In order to avoid redundancy with [Qiskit](https://www.ibm.com/quantum/qiskit) and other IBM software products, once a specific capability from this library gets integrated into IBM's stable product stack we will proceed to its deprecation here. 20 | 21 | Said deprecation process will last for at least three months, and will not begin until a _migration guide_ explaining users how to transition to the new product feature is produced, approved, and published. 22 | 23 | 24 | ## Deprecations, breaking changes, and versioning 25 | 26 | This library follows [semantic versioning](https://semver.org/) (i.e. `MAJOR.MINOR.PATCH`). 27 | 28 | In most cases, functionality will not be changed or removed without displaying active warnings for a sufficiently long period of time. During this period, we will keep old interfaces and mark them as _deprecated_. Deprecations, changes and removals are considered API modifications, and can only occur in _minor_ releases, not _patch_ releases. 29 | 30 | We may occasionally introduce breaking changes (i.e. backwards incompatible) in order to bring new functionality to users more rapidly, simplify existing tooling, or facilitate maintenance. These changes will only be included in _major_ releases. 31 | 32 | Major version zero (i.e. `0.X.Y`) may include breaking changes in minor releases. 33 | 34 | 35 | ## Documenting changes 36 | 37 | Each substantial improvement, breaking change, or deprecation occurring for each release will be documented in [`CHANGELOG.md`](CHANGELOG.md). 38 | -------------------------------------------------------------------------------- /FILEMAP.md: -------------------------------------------------------------------------------- 1 | # Repository structure 2 | 3 | ## Guidelines 4 | 5 | - [`CITATION.bib`](CITATION.bib) -- 6 | BibTeX file including the bibliographic reference to cite the software. 7 | - [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) -- 8 | participation in the repository is subject to these conduct guidelines. 9 | - [`CONTRIBUTING.md`](CONTRIBUTING.md) -- 10 | guidelines to contribute to the repository. 11 | - [`DEPRECATION.md`](DEPRECATION.md) -- 12 | deprecation policy. 13 | - [`FILEMAP.md`](FILEMAP.md) -- 14 | a summary of the repository structure and explanations of the different files and folders. 15 | - [`INSTALL.md`](INSTALL.md) -- 16 | guidelines to install the software contained in this repo. 17 | - [`LICENSE.txt`](LICENSE.txt) -- 18 | one of the [standard legal requirements](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository) for an open source project. There are different types of [licenses for software](https://en.wikipedia.org/wiki/Software_license), some of the most popular being [open source](https://opensource.org/licenses). 19 | - [`README.md`](README.md) -- 20 | main readme for repository. 21 | 22 | 23 | ## Software package/library 24 | 25 | - `` -- 26 | it will have a different name for each repository using the [`pyproject-qiskit` template](https://github.com/pedrorrivero/pyproject-qiskit), and it holds the source code for the software. This name will determine how the software is imported after installation (e.g. `from import __version__`). 27 | - [`CHANGELOG.md`](CHANGELOG.md) -- 28 | file that logs all the changes made to the software on each new version release. 29 | - [`docs`](docs) -- 30 | documentation for the repository (e.g. tutorials, API docs, reference guide). 31 | - [`test`](test) -- 32 | folder where all project tests are located. It is a good practice to cover your project with tests to ensure correctness of implementation. 33 | - [`acceptance`](test/acceptance/) -- 34 | folder for [acceptance tests](https://en.wikipedia.org/wiki/Acceptance_testing). 35 | - [`integration`](test/integration/) -- 36 | folder for [integration tests](https://en.wikipedia.org/wiki/Integration_testing). 37 | - [`system`](test/system/) -- 38 | folder for [system tests](https://en.wikipedia.org/wiki/System_testing). 39 | - [`unit`](test/unit/) -- 40 | folder for [unit tests](https://en.wikipedia.org/wiki/Unit_testing). 41 | - [`pyproject.toml`](pyproject.toml) -- 42 | file containing the [build and package configurations](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/) for the python project. This file also contains configurations for: 43 | - [Black](https://black.readthedocs.io/) -- 44 | a tool for automatic code formatting. 45 | - [Autoflake](https://github.com/PyCQA/autoflake) -- 46 | a tool for removing unused imports and variables. 47 | 48 | 49 | ## CI/CD 50 | 51 | - [`.cz.toml`](.cz.toml) -- 52 | configuration file for [commitizen](https://commitizen-tools.github.io/commitizen/), a tool for software release management. 53 | - [`.github`](.github) -- 54 | folder for configuring anything related to GitHub. 55 | - [`ISSUE_TEMPLATE`](.github/ISSUE_TEMPLATE/) -- 56 | folder holding the [templates for issues](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/manually-creating-a-single-issue-template-for-your-repository) in the repository. 57 | - [`workflows`](.github/workflows/) -- 58 | configurations for [GitHub Actions](https://docs.github.com/en/actions) (e.g. CI/CD scripts). 59 | - [`.templatesyncignore`](.github/.templatesyncignore) -- 60 | file declaring paths to be ignored by the [template sync action](https://github.com/marketplace/actions/actions-template-sync). 61 | - [`CODEOWNERS`](.github/CODEOWNERS) -- 62 | file defining individuals and teams that are [responsible for code](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) in the repository. 63 | - [`PULL_REQUEST_TEMPLATE`](.github/PULL_REQUEST_TEMPLATE.md) -- 64 | file holding the [template for pull requests](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository) in the repository. 65 | - [`.gitignore`](.gitignore) -- 66 | git-specific file that tells which files to ignore when tracking/pushing/committing code (those files will not be tracked by git). 67 | - [`.pre-commit-config.yaml`](.pre-commit-config.yaml) -- 68 | configuration for [pre-commit](https://pre-commit.com/), a framework for managing and maintaining [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks). 69 | - [`.travis.yaml`](.travis.yaml) -- 70 | configuration file for [Travis-CI](https://www.travis-ci.com/); similar to [GitHub Actions](https://docs.github.com/en/actions) (e.g. CI/CD scripts). 71 | - [`tools`](tools) -- 72 | auxiliary tools and scripts for repository maintenance. 73 | - [`extremal_dependency_versions.py`](tools/extremal_dependency_versions.py) -- 74 | script for testing against minimum and development versions of specific dependencies. 75 | - [`symlink_submodule.py`](tools/symlink_submodule.py) -- 76 | auxiliary script to create symlinks to submodule files in base repo. 77 | - [`travis_before_script.bash`](tools/travis_before_script.bash) -- 78 | auxiliary script for extremal dependency testing using [Travis-CI](https://www.travis-ci.com/). 79 | 80 | 81 | ## Testing and style 82 | 83 | - [`.coveragerc`](.coveragerc) -- 84 | configuration for the [code coverage tool](https://coverage.readthedocs.io) used in testing. 85 | - `coverage-html` -- 86 | autogenerated folder holding html documents to explore code coverage after running the coverage tool. The entry point is `coverage-html/index.html`. 87 | - [`.flake8`](.flake8) -- 88 | configuration for [Flake8](https://flake8.pycqa.org/), a tool for code style enforcing. 89 | - [`.gitlint`](.gitlint) -- 90 | configuration for [Gitlint](https://jorisroovers.com/gitlint/latest/), a tool for checking commit message style. 91 | - [`.isort.cfg`](.isort.cfg) -- 92 | configuration for [isort](https://pycqa.github.io/isort/), a tool to sort and group python imports automatically. 93 | - [`.mypy.ini`](.mypy.ini) -- 94 | configuration for [mypy](https://www.mypy-lang.org/), a tool to for static type checking in python via [type annotations](https://docs.python.org/3/library/typing.html). 95 | - [`.pylintrc`](.pylintrc) -- 96 | configuration for [Pylint](https://pylint.readthedocs.io/), a tool for static code analysis (e.g. lint errors). 97 | - [`pytest.ini`](pytest.ini) -- 98 | configuration for the [PyTest framework](https://pytest.org) used for testing the software. 99 | - [`tox.ini`](tox.ini) -- 100 | configuration file for [tox](https://tox.readthedocs.io/en/latest/) framework that aims to automate and standardize testing in Python. Eases the packaging, testing and release process of Python software. 101 | -------------------------------------------------------------------------------- /docs/explanations/01_noise_amplification_on_basis_gates/data/cx/cx_behavioral_a.json: -------------------------------------------------------------------------------- 1 | {"noise_factors": [1, 21, 41, 61, 81, 101, 121, 141, 161, 181, 201, 221, 241, 261, 281, 301, 321, 341, 361, 381, 401, 421, 441, 461, 481, 501, 521, 541, 561, 581, 601, 621, 641, 661, 681, 701, 721, 741, 761, 781, 801, 821, 841, 861, 881, 901, 921, 941, 961, 981, 1001, 1021, 1041, 1061, 1081, 1101, 1121, 1141, 1161, 1181, 1201, 1221, 1241, 1261, 1281, 1301, 1321, 1341, 1361, 1381, 1401, 1421, 1441, 1461, 1481, 1501, 1521, 1541, 1561, 1581, 1601, 1621, 1641, 1661, 1681, 1701, 1721, 1741, 1761, 1781, 1801, 1821, 1841, 1861, 1881, 1901, 1921, 1941, 1961, 1981, 2001, 2021, 2041, 2061, 2081, 2101, 2121, 2141, 2161, 2181, 2201, 2221, 2241, 2261, 2281, 2301, 2321, 2341, 2361, 2381, 2401, 2421, 2441, 2461, 2481, 2501, 2521, 2541, 2561, 2581, 2601, 2621, 2641, 2661, 2681, 2701, 2721, 2741, 2761, 2781, 2801, 2821, 2841, 2861, 2881, 2901, 2921, 2941, 2961, 2981, 3001, 3021, 3041, 3061, 3081, 3101, 3121, 3141, 3161, 3181, 3201, 3221, 3241, 3261, 3281, 3301, 3321, 3341, 3361, 3381, 3401, 3421, 3441, 3461, 3481, 3501, 3521, 3541, 3561, 3581, 3601, 3621, 3641, 3661, 3681, 3701, 3721, 3741, 3761, 3781, 3801, 3821, 3841, 3861, 3881, 3901, 3921, 3941, 3961, 3981, 4001, 4021, 4041, 4061, 4081, 4101, 4121, 4141, 4161, 4181, 4201, 4221, 4241, 4261, 4281, 4301, 4321, 4341, 4361, 4381, 4401, 4421, 4441, 4461, 4481, 4501, 4521, 4541, 4561, 4581, 4601, 4621, 4641, 4661, 4681, 4701, 4721, 4741, 4761, 4781, 4801, 4821, 4841, 4861, 4881, 4901, 4921, 4941, 4961, 4981, 5001, 5021, 5041, 5061, 5081, 5101, 5121, 5141, 5161, 5181, 5201, 5221, 5241, 5261, 5281, 5301, 5321, 5341, 5361, 5381, 5401, 5421, 5441, 5461, 5481, 5501, 5521, 5541, 5561, 5581, 5601, 5621, 5641, 5661, 5681, 5701, 5721, 5741, 5761, 5781, 5801, 5821, 5841, 5861, 5881, 5901, 5921, 5941, 5961, 5981], "values": [-0.9115, -0.777, -0.657, -0.588, -0.4685, -0.4395, -0.3685, -0.309, -0.284, -0.2385, -0.1865, -0.157, -0.115, -0.108, -0.112, -0.0865, -0.1015, -0.068, -0.0885, -0.051, -0.024, -0.028, -0.0295, -0.026, 0.0105, -0.0155, 0.008, 0.009, 0.0045, -0.0425, 0.0095, -0.0125, -0.008, 0.0205, 0.005, 0.012, 0.008, 0.0155, 0.029, 0.012, 0.0305, 0.0425, 0.0175, 0.034, 0.0015, 0.0325, -0.0015, 0.041, 0.0405, 0.0755, 0.036, 0.0305, 0.033, 0.041, 0.0485, 0.0385, 0.031, 0.0445, 0.02, 0.0205, 0.021, 0.008, 0.05, 0.0255, 0.0525, 0.0325, 0.0395, 0.036, 0.0295, 0.042, 0.0175, 0.033, 0.025, 0.0605, 0.045, 0.0425, 0.0435, 0.0125, 0.05, 0.045, 0.04, 0.032, 0.041, 0.0435, 0.0375, 0.0265, 0.003, 0.044, 0.021, 0.005, 0.035, 0.0445, 0.043, 0.042, 0.017, 0.0495, 0.022, 0.0275, 0.043, 0.018, 0.022, 0.0615, 0.0635, 0.039, 0.019, 0.046, 0.045, 0.055, 0.0445, 0.045, 0.045, 0.0425, 0.0465, 0.0255, 0.04, 0.0325, 0.0285, 0.023, 0.0145, 0.0345, 0.039, 0.033, 0.033, 0.026, 0.022, 0.0235, 0.0465, 0.038, 0.042, 0.06, 0.016, 0.0465, 0.051, 0.032, 0.026, 0.014, 0.044, 0.011, 0.037, 0.043, 0.057, 0.023, 0.0285, 0.0245, 0.021, 0.0265, 0.0325, 0.0215, 0.033, 0.0235, 0.045, 0.0125, 0.0305, 0.0505, 0.0535, 0.02, 0.0315, 0.029, 0.008, 0.0175, 0.0245, 0.039, 0.0435, 0.044, 0.018, 0.019, 0.06, 0.014, 0.0195, 0.0235, 0.042, 0.006, 0.0275, 0.0215, 0.0045, 0.011, 0.0425, 0.044, 0.046, 0.0065, 0.035, 0.035, 0.0285, 0.03, 0.0275, 0.0295, 0.0245, 0.0325, 0.0595, 0.019, 0.0305, -0.0015, 0.024, 0.041, 0.0325, 0.0425, 0.006, 0.0615, 0.0155, 0.029, 0.0255, 0.0595, 0.049, 0.0235, 0.0335, 0.0345, 0.0455, 0.0365, 0.034, 0.0285, 0.0225, 0.0335, 0.058, 0.0255, 0.026, 0.0155, 0.02, 0.019, 0.049, 0.068, 0.052, 0.0055, 0.0155, 0.024, 0.0035, 0.025, 0.028, 0.0505, 0.0445, 0.031, 0.036, 0.037, 0.02, 0.029, 0.0625, 0.041, 0.0315, 0.023, 0.029, 0.05, 0.04, 0.0435, 0.041, 0.0305, 0.016, 0.0285, 0.0605, 0.0585, 0.016, 0.019, 0.03, 0.044, 0.027, 0.0325, 0.0515, 0.036, 0.026, 0.0195, 0.021, 0.044, 0.0345, 0.026, 0.0365, 0.035, 0.036, 0.0355, 0.014, 0.008, 0.0455, 0.055, 0.0365, 0.025, 0.018, 0.0265, 0.0335, 0.0245, 0.03, 0.008, 0.0255, 0.036, 0.0435, 0.0265, 0.0235, 0.035, 0.059, 0.031, 0.019, 0.0185, 0.0015, 0.036, 0.0405, 0.055, 0.0395, 0.038, 0.047, 0.059, 0.035, 0.034, 0.023, 0.018], "variances": [0.16916775000000006, 0.39627099999999993, 0.5683509999999999, 0.6542560000000001, 0.78050775, 0.80683975, 0.86420775, 0.904519, 0.919344, 0.94311775, 0.96521775, 0.975351, 0.986775, 0.988336, 0.987456, 0.99251775, 0.98969775, 0.995376, 0.99216775, 0.997399, 0.999424, 0.999216, 0.99912975, 0.999324, 0.99988975, 0.99975975, 0.999936, 0.999919, 0.99997975, 0.99819375, 0.99990975, 0.99984375, 0.999936, 0.99957975, 0.999975, 0.999856, 0.999936, 0.99975975, 0.999159, 0.999856, 0.99906975, 0.99819375, 0.99969375, 0.998844, 0.99999775, 0.99894375, 0.99999775, 0.998319, 0.99835975, 0.99429975, 0.998704, 0.99906975, 0.998911, 0.998319, 0.99764775, 0.99851775, 0.999039, 0.99801975, 0.9996, 0.99957975, 0.999559, 0.999936, 0.9975, 0.99934975, 0.99724375, 0.99894375, 0.99843975, 0.998704, 0.99912975, 0.998236, 0.99969375, 0.998911, 0.999375, 0.99633975, 0.997975, 0.99819375, 0.99810775, 0.99984375, 0.9975, 0.997975, 0.9984, 0.998976, 0.998319, 0.99810775, 0.99859375, 0.99929775, 0.999991, 0.998064, 0.999559, 0.999975, 0.998775, 0.99801975, 0.998151, 0.998236, 0.999711, 0.99754975, 0.999516, 0.99924375, 0.998151, 0.999676, 0.999516, 0.99621775, 0.99596775, 0.998479, 0.999639, 0.997884, 0.997975, 0.996975, 0.99801975, 0.997975, 0.997975, 0.99819375, 0.99783775, 0.99934975, 0.9984, 0.99894375, 0.99918775, 0.999471, 0.99978975, 0.99880975, 0.998479, 0.998911, 0.998911, 0.999324, 0.999516, 0.99944775, 0.99783775, 0.998556, 0.998236, 0.9964, 0.999744, 0.99783775, 0.997399, 0.998976, 0.999324, 0.999804, 0.998064, 0.999879, 0.998631, 0.998151, 0.996751, 0.999471, 0.99918775, 0.99939975, 0.999559, 0.99929775, 0.99894375, 0.99953775, 0.998911, 0.99944775, 0.997975, 0.99984375, 0.99906975, 0.99744975, 0.99713775, 0.9996, 0.99900775, 0.999159, 0.999936, 0.99969375, 0.99939975, 0.998479, 0.99810775, 0.998064, 0.999676, 0.999639, 0.9964, 0.999804, 0.99961975, 0.99944775, 0.998236, 0.999964, 0.99924375, 0.99953775, 0.99997975, 0.999879, 0.99819375, 0.998064, 0.997884, 0.99995775, 0.998775, 0.998775, 0.99918775, 0.9991, 0.99924375, 0.99912975, 0.99939975, 0.99894375, 0.99645975, 0.999639, 0.99906975, 0.99999775, 0.999424, 0.998319, 0.99894375, 0.99819375, 0.999964, 0.99621775, 0.99975975, 0.999159, 0.99934975, 0.99645975, 0.997599, 0.99944775, 0.99887775, 0.99880975, 0.99792975, 0.99866775, 0.998844, 0.99918775, 0.99949375, 0.99887775, 0.996636, 0.99934975, 0.999324, 0.99975975, 0.9996, 0.999639, 0.997599, 0.995376, 0.997296, 0.99996975, 0.99975975, 0.999424, 0.99998775, 0.999375, 0.999216, 0.99744975, 0.99801975, 0.999039, 0.998704, 0.998631, 0.9996, 0.999159, 0.99609375, 0.998319, 0.99900775, 0.999471, 0.999159, 0.9975, 0.9984, 0.99810775, 0.998319, 0.99906975, 0.999744, 0.99918775, 0.99633975, 0.99657775, 0.999744, 0.999639, 0.9991, 0.998064, 0.999271, 0.99894375, 0.99734775, 0.998704, 0.999324, 0.99961975, 0.999559, 0.998064, 0.99880975, 0.999324, 0.99866775, 0.998775, 0.998704, 0.99873975, 0.999804, 0.999936, 0.99792975, 0.996975, 0.99866775, 0.999375, 0.999676, 0.99929775, 0.99887775, 0.99939975, 0.9991, 0.999936, 0.99934975, 0.998704, 0.99810775, 0.99929775, 0.99944775, 0.998775, 0.996519, 0.999039, 0.999639, 0.99965775, 0.99999775, 0.998704, 0.99835975, 0.996975, 0.99843975, 0.998556, 0.997791, 0.996519, 0.998775, 0.998844, 0.999471, 0.999676]} -------------------------------------------------------------------------------- /docs/explanations/01_noise_amplification_on_basis_gates/data/cx/cx_behavioral_b.json: -------------------------------------------------------------------------------- 1 | {"noise_factors": [1, 21, 41, 61, 81, 101, 121, 141, 161, 181, 201, 221, 241, 261, 281, 301, 321, 341, 361, 381, 401, 421, 441, 461, 481, 501, 521, 541, 561, 581, 601, 621, 641, 661, 681, 701, 721, 741, 761, 781, 801, 821, 841, 861, 881, 901, 921, 941, 961, 981, 1001, 1021, 1041, 1061, 1081, 1101, 1121, 1141, 1161, 1181, 1201, 1221, 1241, 1261, 1281, 1301, 1321, 1341, 1361, 1381, 1401, 1421, 1441, 1461, 1481, 1501, 1521, 1541, 1561, 1581, 1601, 1621, 1641, 1661, 1681, 1701, 1721, 1741, 1761, 1781, 1801, 1821, 1841, 1861, 1881, 1901, 1921, 1941, 1961, 1981, 2001, 2021, 2041, 2061, 2081, 2101, 2121, 2141, 2161, 2181, 2201, 2221, 2241, 2261, 2281, 2301, 2321, 2341, 2361, 2381, 2401, 2421, 2441, 2461, 2481, 2501, 2521, 2541, 2561, 2581, 2601, 2621, 2641, 2661, 2681, 2701, 2721, 2741, 2761, 2781, 2801, 2821, 2841, 2861, 2881, 2901, 2921, 2941, 2961, 2981, 3001, 3021, 3041, 3061, 3081, 3101, 3121, 3141, 3161, 3181, 3201, 3221, 3241, 3261, 3281, 3301, 3321, 3341, 3361, 3381, 3401, 3421, 3441, 3461, 3481, 3501, 3521, 3541, 3561, 3581, 3601, 3621, 3641, 3661, 3681, 3701, 3721, 3741, 3761, 3781, 3801, 3821, 3841, 3861, 3881, 3901, 3921, 3941, 3961, 3981, 4001, 4021, 4041, 4061, 4081, 4101, 4121, 4141, 4161, 4181, 4201, 4221, 4241, 4261, 4281, 4301, 4321, 4341, 4361, 4381, 4401, 4421, 4441, 4461, 4481, 4501, 4521, 4541, 4561, 4581, 4601, 4621, 4641, 4661, 4681, 4701, 4721, 4741, 4761, 4781, 4801, 4821, 4841, 4861, 4881, 4901, 4921, 4941, 4961, 4981, 5001, 5021, 5041, 5061, 5081, 5101, 5121, 5141, 5161, 5181, 5201, 5221, 5241, 5261, 5281, 5301, 5321, 5341, 5361, 5381, 5401, 5421, 5441, 5461, 5481, 5501, 5521, 5541, 5561, 5581, 5601, 5621, 5641, 5661, 5681, 5701, 5721, 5741, 5761, 5781, 5801, 5821, 5841, 5861, 5881, 5901, 5921, 5941, 5961, 5981], "values": [-0.889, -0.763, -0.668, -0.584, -0.519, -0.436, -0.403, -0.3555, -0.283, -0.207, -0.2125, -0.1955, -0.173, -0.1725, -0.1595, -0.101, -0.113, -0.1035, -0.074, -0.062, -0.0405, -0.0445, -0.0795, -0.041, -0.0035, -0.0185, -0.041, 0.019, -0.017, -0.027, 0.008, 0.003, -0.0045, 0.0155, 0.007, -0.0245, 0.018, 0.015, 0.004, 0.024, 0.045, 0.019, 0.0235, 0.0525, 0.003, 0.0175, -0.0015, 0.032, 0.045, 0.0435, 0.0485, 0.0575, 0.0375, 0.012, 0.0295, 0.03, 0.039, 0.052, 0.048, 0.0165, 0.0615, 0.019, 0.059, 0.037, 0.0125, 0.0295, 0.0395, 0.0495, 0.0225, 0.0435, 0.0165, 0.0495, 0.0235, 0.004, 0.0455, 0.0135, 0.0115, 0.005, 0.031, 0.0365, 0.0255, 0.0585, 0.015, 0.0315, 0.0375, 0.051, 0.0415, 0.0335, 0.065, -0.0005, 0.0285, 0.046, 0.0045, 0.031, 0.08, 0.0045, 0.041, 0.0165, 0.0275, 0.051, 0.024, 0.0095, 0.0235, 0.0345, 0.012, 0.061, 0.0515, 0.036, 0.0215, 0.072, 0.0075, 0.0315, 0.045, 0.0265, 0.0375, -0.007, 0.041, 0.0325, 0.049, 0.041, 0.0515, 0.016, 0.071, 0.0315, 0.032, -0.0085, 0.035, 0.0465, 0.0875, 0.054, 0.0095, 0.0345, 0.0295, 0.0565, 0.0455, 0.0385, 0.0185, 0.0575, 0.0435, 0.064, 0.029, 0.0325, 0.026, 0.0325, 0.0425, 0.0305, 0.039, 0.058, 0.0255, 0.052, 0.0565, 0.015, 0.035, 0.023, 0.026, 0.031, 0.0275, 0.0465, 0.037, 0.001, 0.053, 0.044, 0.026, 0.027, 0.0015, 0.016, 0.0095, 0.0355, 0.0425, 0.016, 0.0645, 0.028, 0.0095, 0.057, 0.047, 0.023, 0.024, 0.043, 0.0485, 0.0285, 0.0225, 0.0345, 0.047, 0.043, 0.035, 0.021, 0.0135, 0.015, 0.0445, 0.056, 0.0295, 0.027, 0.022, 0.0465, 0.0305, 0.0105, 0.006, 0.0095, 0.013, 0.0035, 0.061, 0.0195, 0.036, 0.022, 0.0525, 0.022, 0.0385, 0.0285, 0.033, 0.0115, 0.0405, 0.0155, 0.0215, 0.0625, 0.0515, 0.0465, 0.0325, 0.037, 0.049, 0.0415, 0.0505, 0.019, 0.027, 0.0475, 0.055, 0.0105, 0.0265, 0.024, 0.047, 0.0565, 0.021, 0.0045, 0.0285, 0.0365, 0.036, 0.0245, 0.0435, 0.03, 0.0635, 0.043, 0.0125, 0.0285, 0.042, 0.057, 0.0275, 0.043, 0.045, 0.045, 0.0095, 0.0345, 0.04, 0.021, 0.0015, 0.012, 0.0775, 0.0305, 0.0265, 0.0285, 0.038, 0.012, 0.0235, 0.024, 0.0195, 0.052, 0.032, 0.0345, 0.0305, 0.016, 0.034, 0.058, 0.052, -0.004, 0.0435, 0.0105, 0.0275, 0.022, 0.041, 0.023, 0.0295, 0.026, 0.0065, 0.0225, 0.0295, 0.0145, 0.006, 0.023, 0.012, 0.0225, 0.0395, 0.04, 0.0185, 0.027, 0.035, 0.014, 0.028, 0.0245, 0.072, 0.023, 0.0285, 0.036], "variances": [0.20967899999999995, 0.41783099999999995, 0.5537759999999999, 0.658944, 0.730639, 0.809904, 0.837591, 0.87361975, 0.919911, 0.957151, 0.95484375, 0.96177975, 0.970071, 0.97024375, 0.97455975, 0.989799, 0.987231, 0.98928775, 0.994524, 0.996156, 0.99835975, 0.99801975, 0.99367975, 0.998319, 0.99998775, 0.99965775, 0.998319, 0.999639, 0.999711, 0.999271, 0.999936, 0.999991, 0.99997975, 0.99975975, 0.999951, 0.99939975, 0.999676, 0.999775, 0.999984, 0.999424, 0.997975, 0.999639, 0.99944775, 0.99724375, 0.999991, 0.99969375, 0.99999775, 0.998976, 0.997975, 0.99810775, 0.99764775, 0.99669375, 0.99859375, 0.999856, 0.99912975, 0.9991, 0.998479, 0.997296, 0.997696, 0.99972775, 0.99621775, 0.999639, 0.996519, 0.998631, 0.99984375, 0.99912975, 0.99843975, 0.99754975, 0.99949375, 0.99810775, 0.99972775, 0.99754975, 0.99944775, 0.999984, 0.99792975, 0.99981775, 0.99986775, 0.999975, 0.999039, 0.99866775, 0.99934975, 0.99657775, 0.999775, 0.99900775, 0.99859375, 0.997399, 0.99827775, 0.99887775, 0.995775, 0.99999975, 0.99918775, 0.997884, 0.99997975, 0.999039, 0.9936, 0.99997975, 0.998319, 0.99972775, 0.99924375, 0.997399, 0.999424, 0.99990975, 0.99944775, 0.99880975, 0.999856, 0.996279, 0.99734775, 0.998704, 0.99953775, 0.994816, 0.99994375, 0.99900775, 0.997975, 0.99929775, 0.99859375, 0.999951, 0.998319, 0.99894375, 0.997599, 0.998319, 0.99734775, 0.999744, 0.994959, 0.99900775, 0.998976, 0.99992775, 0.998775, 0.99783775, 0.99234375, 0.997084, 0.99990975, 0.99880975, 0.99912975, 0.99680775, 0.99792975, 0.99851775, 0.99965775, 0.99669375, 0.99810775, 0.995904, 0.999159, 0.99894375, 0.999324, 0.99894375, 0.99819375, 0.99906975, 0.998479, 0.996636, 0.99934975, 0.997296, 0.99680775, 0.999775, 0.998775, 0.999471, 0.999324, 0.999039, 0.99924375, 0.99783775, 0.998631, 0.999999, 0.997191, 0.998064, 0.999324, 0.999271, 0.99999775, 0.999744, 0.99990975, 0.99873975, 0.99819375, 0.999744, 0.99583975, 0.999216, 0.99990975, 0.996751, 0.997791, 0.999471, 0.999424, 0.998151, 0.99764775, 0.99918775, 0.99949375, 0.99880975, 0.997791, 0.998151, 0.998775, 0.999559, 0.99981775, 0.999775, 0.99801975, 0.996864, 0.99912975, 0.999271, 0.999516, 0.99783775, 0.99906975, 0.99988975, 0.999964, 0.99990975, 0.999831, 0.99998775, 0.996279, 0.99961975, 0.998704, 0.999516, 0.99724375, 0.999516, 0.99851775, 0.99918775, 0.998911, 0.99986775, 0.99835975, 0.99975975, 0.99953775, 0.99609375, 0.99734775, 0.99783775, 0.99894375, 0.998631, 0.997599, 0.99827775, 0.99744975, 0.999639, 0.999271, 0.99774375, 0.996975, 0.99988975, 0.99929775, 0.999424, 0.997791, 0.99680775, 0.999559, 0.99997975, 0.99918775, 0.99866775, 0.998704, 0.99939975, 0.99810775, 0.9991, 0.99596775, 0.998151, 0.99984375, 0.99918775, 0.998236, 0.996751, 0.99924375, 0.998151, 0.997975, 0.997975, 0.99990975, 0.99880975, 0.9984, 0.999559, 0.99999775, 0.999856, 0.99399375, 0.99906975, 0.99929775, 0.99918775, 0.998556, 0.999856, 0.99944775, 0.999424, 0.99961975, 0.997296, 0.998976, 0.99880975, 0.99906975, 0.999744, 0.998844, 0.996636, 0.997296, 0.999984, 0.99810775, 0.99988975, 0.99924375, 0.999516, 0.998319, 0.999471, 0.99912975, 0.999324, 0.99995775, 0.99949375, 0.99912975, 0.99978975, 0.999964, 0.999471, 0.999856, 0.99949375, 0.99843975, 0.9984, 0.99965775, 0.999271, 0.998775, 0.999804, 0.999216, 0.99939975, 0.994816, 0.999471, 0.99918775, 0.998704]} -------------------------------------------------------------------------------- /docs/explanations/01_noise_amplification_on_basis_gates/data/sx/sx_behavioral_a.json: -------------------------------------------------------------------------------- 1 | {"noise_factors": [1, 21, 41, 61, 81, 101, 121, 141, 161, 181, 201, 221, 241, 261, 281, 301, 321, 341, 361, 381, 401, 421, 441, 461, 481, 501, 521, 541, 561, 581, 601, 621, 641, 661, 681, 701, 721, 741, 761, 781, 801, 821, 841, 861, 881, 901, 921, 941, 961, 981, 1001, 1021, 1041, 1061, 1081, 1101, 1121, 1141, 1161, 1181, 1201, 1221, 1241, 1261, 1281, 1301, 1321, 1341, 1361, 1381, 1401, 1421, 1441, 1461, 1481, 1501, 1521, 1541, 1561, 1581, 1601, 1621, 1641, 1661, 1681, 1701, 1721, 1741, 1761, 1781, 1801, 1821, 1841, 1861, 1881, 1901, 1921, 1941, 1961, 1981, 2001, 2021, 2041, 2061, 2081, 2101, 2121, 2141, 2161, 2181, 2201, 2221, 2241, 2261, 2281, 2301, 2321, 2341, 2361, 2381, 2401, 2421, 2441, 2461, 2481, 2501, 2521, 2541, 2561, 2581, 2601, 2621, 2641, 2661, 2681, 2701, 2721, 2741, 2761, 2781, 2801, 2821, 2841, 2861, 2881, 2901, 2921, 2941, 2961, 2981, 3001, 3021, 3041, 3061, 3081, 3101, 3121, 3141, 3161, 3181, 3201, 3221, 3241, 3261, 3281, 3301, 3321, 3341, 3361, 3381, 3401, 3421, 3441, 3461, 3481, 3501, 3521, 3541, 3561, 3581, 3601, 3621, 3641, 3661, 3681, 3701, 3721, 3741, 3761, 3781, 3801, 3821, 3841, 3861, 3881, 3901, 3921, 3941, 3961, 3981, 4001, 4021, 4041, 4061, 4081, 4101, 4121, 4141, 4161, 4181, 4201, 4221, 4241, 4261, 4281, 4301, 4321, 4341, 4361, 4381, 4401, 4421, 4441, 4461, 4481, 4501, 4521, 4541, 4561, 4581, 4601, 4621, 4641, 4661, 4681, 4701, 4721, 4741, 4761, 4781, 4801, 4821, 4841, 4861, 4881, 4901, 4921, 4941, 4961, 4981], "values": [-0.9665, -0.959, -0.9405, -0.932, -0.9, -0.893, -0.855, -0.8485, -0.8085, -0.784, -0.7575, -0.7425, -0.692, -0.6685, -0.661, -0.6375, -0.6175, -0.594, -0.568, -0.565, -0.5465, -0.5245, -0.5235, -0.511, -0.5045, -0.481, -0.489, -0.4775, -0.465, -0.4735, -0.452, -0.443, -0.4375, -0.4605, -0.46, -0.434, -0.4645, -0.4255, -0.4485, -0.443, -0.4575, -0.457, -0.4565, -0.4835, -0.467, -0.4945, -0.475, -0.477, -0.501, -0.491, -0.489, -0.49, -0.5015, -0.484, -0.514, -0.5085, -0.5085, -0.5325, -0.5215, -0.5135, -0.514, -0.4995, -0.5055, -0.5145, -0.5125, -0.5215, -0.514, -0.5555, -0.532, -0.52, -0.5455, -0.5265, -0.5315, -0.517, -0.5125, -0.5085, -0.513, -0.507, -0.5205, -0.511, -0.5415, -0.5265, -0.5075, -0.4965, -0.524, -0.521, -0.534, -0.526, -0.527, -0.5355, -0.534, -0.5, -0.534, -0.536, -0.534, -0.5155, -0.5445, -0.505, -0.523, -0.539, -0.5405, -0.519, -0.525, -0.536, -0.527, -0.548, -0.5275, -0.539, -0.5325, -0.5395, -0.5435, -0.5105, -0.5455, -0.553, -0.547, -0.546, -0.5355, -0.5255, -0.534, -0.539, -0.519, -0.537, -0.5405, -0.537, -0.538, -0.5255, -0.555, -0.525, -0.5415, -0.5485, -0.527, -0.54, -0.5465, -0.5335, -0.5325, -0.5185, -0.56, -0.533, -0.5265, -0.555, -0.559, -0.545, -0.546, -0.5565, -0.5265, -0.544, -0.53, -0.536, -0.5335, -0.5645, -0.5595, -0.548, -0.4945, -0.5125, -0.544, -0.535, -0.572, -0.5375, -0.572, -0.5325, -0.5425, -0.559, -0.537, -0.53, -0.538, -0.5395, -0.557, -0.5345, -0.567, -0.544, -0.5545, -0.559, -0.5445, -0.552, -0.511, -0.583, -0.523, -0.543, -0.5445, -0.535, -0.5235, -0.541, -0.5625, -0.542, -0.543, -0.561, -0.542, -0.546, -0.541, -0.5645, -0.566, -0.5665, -0.559, -0.561, -0.5505, -0.538, -0.552, -0.5695, -0.555, -0.5475, -0.557, -0.5275, -0.5415, -0.55, -0.5195, -0.5405, -0.521, -0.54, -0.556, -0.5375, -0.5475, -0.536, -0.5415, -0.5405, -0.575, -0.5335, -0.5335, -0.5495, -0.57, -0.562, -0.5495, -0.5345, -0.541, -0.5155, -0.5645, -0.541, -0.5455, -0.5335, -0.532, -0.544, -0.54, -0.556, -0.5695, -0.521, -0.538, -0.534, -0.5335, -0.551, -0.5365, -0.5385, -0.5375, -0.555, -0.529, -0.556, -0.5735, -0.547, -0.5755, -0.557, -0.532, -0.5465], "variances": [0.06587774999999996, 0.08031900000000003, 0.11545974999999997, 0.13137599999999994, 0.18999999999999995, 0.20255099999999993, 0.2689750000000001, 0.28004774999999993, 0.34632775000000005, 0.3853439999999999, 0.42619375000000004, 0.4486937499999999, 0.521136, 0.55310775, 0.5630789999999999, 0.59359375, 0.6186937499999999, 0.6471640000000001, 0.677376, 0.6807750000000001, 0.70133775, 0.7248997500000001, 0.72594775, 0.7388790000000001, 0.7454797500000001, 0.7686390000000001, 0.760879, 0.77199375, 0.783775, 0.7757977500000001, 0.795696, 0.803751, 0.80859375, 0.78793975, 0.7884, 0.811644, 0.78423975, 0.81894975, 0.79884775, 0.803751, 0.79069375, 0.7911509999999999, 0.7916077499999999, 0.76622775, 0.781911, 0.75546975, 0.774375, 0.772471, 0.748999, 0.758919, 0.760879, 0.7599, 0.7484977500000001, 0.765744, 0.735804, 0.74142775, 0.74142775, 0.71644375, 0.72803775, 0.73631775, 0.735804, 0.75049975, 0.74446975, 0.73528975, 0.73734375, 0.72803775, 0.735804, 0.69141975, 0.716976, 0.7296, 0.7024297500000001, 0.72279775, 0.71750775, 0.732711, 0.73734375, 0.74142775, 0.736831, 0.7429509999999999, 0.72907975, 0.7388790000000001, 0.7067777500000001, 0.72279775, 0.7424437500000001, 0.75348775, 0.725424, 0.728559, 0.714844, 0.723324, 0.722271, 0.7132397500000001, 0.714844, 0.75, 0.714844, 0.712704, 0.714844, 0.7342597500000001, 0.70351975, 0.7449749999999999, 0.726471, 0.709479, 0.70785975, 0.730639, 0.724375, 0.712704, 0.722271, 0.6996959999999999, 0.72174375, 0.709479, 0.71644375, 0.7089397500000001, 0.7046077500000001, 0.7393897500000001, 0.7024297500000001, 0.694191, 0.7007909999999999, 0.701884, 0.7132397500000001, 0.7238497500000001, 0.714844, 0.709479, 0.730639, 0.7116309999999999, 0.70785975, 0.7116309999999999, 0.710556, 0.7238497500000001, 0.691975, 0.724375, 0.7067777500000001, 0.6991477500000001, 0.722271, 0.7083999999999999, 0.70133775, 0.71537775, 0.71644375, 0.73115775, 0.6863999999999999, 0.715911, 0.72279775, 0.691975, 0.687519, 0.7029749999999999, 0.701884, 0.69030775, 0.72279775, 0.704064, 0.7191, 0.712704, 0.71537775, 0.68133975, 0.68695975, 0.6996959999999999, 0.75546975, 0.73734375, 0.704064, 0.713775, 0.6728160000000001, 0.7110937500000001, 0.6728160000000001, 0.71644375, 0.70569375, 0.687519, 0.7116309999999999, 0.7191, 0.710556, 0.7089397500000001, 0.689751, 0.71430975, 0.6785110000000001, 0.704064, 0.69252975, 0.687519, 0.70351975, 0.6952959999999999, 0.7388790000000001, 0.6601110000000001, 0.726471, 0.705151, 0.70351975, 0.713775, 0.72594775, 0.707319, 0.68359375, 0.706236, 0.705151, 0.685279, 0.706236, 0.701884, 0.707319, 0.68133975, 0.6796440000000001, 0.67907775, 0.687519, 0.685279, 0.69694975, 0.710556, 0.6952959999999999, 0.67566975, 0.691975, 0.70024375, 0.689751, 0.72174375, 0.7067777500000001, 0.6975, 0.7301197500000001, 0.70785975, 0.728559, 0.7083999999999999, 0.6908639999999999, 0.7110937500000001, 0.70024375, 0.712704, 0.7067777500000001, 0.70785975, 0.669375, 0.71537775, 0.71537775, 0.69804975, 0.6751, 0.684156, 0.69804975, 0.71430975, 0.707319, 0.7342597500000001, 0.68133975, 0.707319, 0.7024297500000001, 0.71537775, 0.716976, 0.704064, 0.7083999999999999, 0.6908639999999999, 0.67566975, 0.728559, 0.710556, 0.714844, 0.71537775, 0.696399, 0.7121677500000001, 0.71001775, 0.7110937500000001, 0.691975, 0.720159, 0.6908639999999999, 0.6710977499999999, 0.7007909999999999, 0.66879975, 0.689751, 0.716976, 0.70133775]} -------------------------------------------------------------------------------- /docs/explanations/01_noise_amplification_on_basis_gates/data/sx/sx_behavioral_b.json: -------------------------------------------------------------------------------- 1 | {"noise_factors": [1, 21, 41, 61, 81, 101, 121, 141, 161, 181, 201, 221, 241, 261, 281, 301, 321, 341, 361, 381, 401, 421, 441, 461, 481, 501, 521, 541, 561, 581, 601, 621, 641, 661, 681, 701, 721, 741, 761, 781, 801, 821, 841, 861, 881, 901, 921, 941, 961, 981, 1001, 1021, 1041, 1061, 1081, 1101, 1121, 1141, 1161, 1181, 1201, 1221, 1241, 1261, 1281, 1301, 1321, 1341, 1361, 1381, 1401, 1421, 1441, 1461, 1481, 1501, 1521, 1541, 1561, 1581, 1601, 1621, 1641, 1661, 1681, 1701, 1721, 1741, 1761, 1781, 1801, 1821, 1841, 1861, 1881, 1901, 1921, 1941, 1961, 1981, 2001, 2021, 2041, 2061, 2081, 2101, 2121, 2141, 2161, 2181, 2201, 2221, 2241, 2261, 2281, 2301, 2321, 2341, 2361, 2381, 2401, 2421, 2441, 2461, 2481, 2501, 2521, 2541, 2561, 2581, 2601, 2621, 2641, 2661, 2681, 2701, 2721, 2741, 2761, 2781, 2801, 2821, 2841, 2861, 2881, 2901, 2921, 2941, 2961, 2981, 3001, 3021, 3041, 3061, 3081, 3101, 3121, 3141, 3161, 3181, 3201, 3221, 3241, 3261, 3281, 3301, 3321, 3341, 3361, 3381, 3401, 3421, 3441, 3461, 3481, 3501, 3521, 3541, 3561, 3581, 3601, 3621, 3641, 3661, 3681, 3701, 3721, 3741, 3761, 3781, 3801, 3821, 3841, 3861, 3881, 3901, 3921, 3941, 3961, 3981, 4001, 4021, 4041, 4061, 4081, 4101, 4121, 4141, 4161, 4181, 4201, 4221, 4241, 4261, 4281, 4301, 4321, 4341, 4361, 4381, 4401, 4421, 4441, 4461, 4481, 4501, 4521, 4541, 4561, 4581, 4601, 4621, 4641, 4661, 4681, 4701, 4721, 4741, 4761, 4781, 4801, 4821, 4841, 4861, 4881, 4901, 4921, 4941, 4961, 4981], "values": [-0.9745, -0.957, -0.943, -0.9265, -0.91, -0.8795, -0.8765, -0.8525, -0.811, -0.786, -0.7625, -0.737, -0.709, -0.698, -0.6815, -0.6555, -0.6545, -0.6195, -0.6045, -0.5855, -0.569, -0.5615, -0.5305, -0.5355, -0.526, -0.52, -0.5245, -0.4915, -0.493, -0.4565, -0.4795, -0.4465, -0.4735, -0.455, -0.4565, -0.461, -0.477, -0.469, -0.452, -0.447, -0.461, -0.4735, -0.4715, -0.4765, -0.481, -0.476, -0.4885, -0.4885, -0.4805, -0.487, -0.4975, -0.507, -0.485, -0.498, -0.52, -0.4915, -0.518, -0.4925, -0.517, -0.5105, -0.509, -0.5, -0.524, -0.499, -0.513, -0.5065, -0.5345, -0.519, -0.542, -0.5265, -0.5325, -0.5255, -0.526, -0.5345, -0.516, -0.554, -0.5175, -0.526, -0.5285, -0.507, -0.5095, -0.5005, -0.496, -0.5255, -0.5475, -0.543, -0.542, -0.5405, -0.53, -0.513, -0.538, -0.5175, -0.534, -0.526, -0.517, -0.5495, -0.5235, -0.54, -0.5585, -0.5385, -0.5355, -0.5255, -0.5365, -0.5155, -0.54, -0.544, -0.548, -0.559, -0.568, -0.536, -0.547, -0.5525, -0.5615, -0.5505, -0.5455, -0.5245, -0.5445, -0.5555, -0.5325, -0.5525, -0.5555, -0.5565, -0.537, -0.5615, -0.552, -0.5395, -0.544, -0.5465, -0.562, -0.5445, -0.542, -0.5425, -0.524, -0.5455, -0.552, -0.53, -0.552, -0.5645, -0.554, -0.5615, -0.5535, -0.559, -0.558, -0.531, -0.5635, -0.554, -0.531, -0.532, -0.5395, -0.537, -0.5365, -0.533, -0.5565, -0.544, -0.544, -0.546, -0.535, -0.554, -0.5615, -0.555, -0.5475, -0.551, -0.5605, -0.5545, -0.539, -0.5595, -0.537, -0.5445, -0.557, -0.5445, -0.5405, -0.52, -0.5655, -0.539, -0.5405, -0.565, -0.5605, -0.5715, -0.5585, -0.557, -0.5465, -0.5685, -0.574, -0.5595, -0.562, -0.5405, -0.549, -0.5535, -0.5775, -0.541, -0.561, -0.571, -0.559, -0.562, -0.5515, -0.5455, -0.557, -0.5685, -0.5445, -0.5585, -0.5435, -0.561, -0.582, -0.5765, -0.556, -0.5475, -0.5655, -0.5575, -0.533, -0.5565, -0.5545, -0.5505, -0.553, -0.544, -0.578, -0.5545, -0.565, -0.561, -0.55, -0.5545, -0.5535, -0.569, -0.549, -0.5575, -0.567, -0.57, -0.5445, -0.5435, -0.563, -0.561, -0.546, -0.571, -0.5675, -0.5605, -0.5505, -0.5625, -0.5445, -0.58, -0.572, -0.551, -0.562, -0.562, -0.557, -0.543, -0.5715, -0.558, -0.556, -0.583, -0.559, -0.578], "variances": [0.05034974999999997, 0.08415100000000009, 0.11075100000000004, 0.14159774999999997, 0.17189999999999994, 0.2264797500000001, 0.23174775000000014, 0.2732437499999999, 0.3422789999999999, 0.382204, 0.4185937500000001, 0.456831, 0.49731900000000007, 0.512796, 0.5355577499999999, 0.57031975, 0.57162975, 0.61621975, 0.6345797499999999, 0.6571897499999999, 0.676239, 0.68471775, 0.71856975, 0.7132397500000001, 0.723324, 0.7296, 0.7248997500000001, 0.7584277500000001, 0.756951, 0.7916077499999999, 0.7700797500000001, 0.8006377499999999, 0.7757977500000001, 0.792975, 0.7916077499999999, 0.787479, 0.772471, 0.780039, 0.795696, 0.800191, 0.787479, 0.7757977500000001, 0.77768775, 0.77294775, 0.7686390000000001, 0.773424, 0.76136775, 0.76136775, 0.76911975, 0.762831, 0.75249375, 0.7429509999999999, 0.764775, 0.751996, 0.7296, 0.7584277500000001, 0.731676, 0.75744375, 0.732711, 0.7393897500000001, 0.740919, 0.75, 0.725424, 0.750999, 0.736831, 0.74345775, 0.71430975, 0.730639, 0.706236, 0.72279775, 0.71644375, 0.7238497500000001, 0.723324, 0.71430975, 0.733744, 0.6930839999999999, 0.73219375, 0.723324, 0.72068775, 0.7429509999999999, 0.74040975, 0.74949975, 0.753984, 0.7238497500000001, 0.70024375, 0.705151, 0.706236, 0.70785975, 0.7191, 0.736831, 0.710556, 0.73219375, 0.714844, 0.723324, 0.732711, 0.69804975, 0.72594775, 0.7083999999999999, 0.6880777499999999, 0.71001775, 0.7132397500000001, 0.7238497500000001, 0.7121677500000001, 0.7342597500000001, 0.7083999999999999, 0.704064, 0.6996959999999999, 0.687519, 0.677376, 0.712704, 0.7007909999999999, 0.69474375, 0.68471775, 0.69694975, 0.7024297500000001, 0.7248997500000001, 0.70351975, 0.69141975, 0.71644375, 0.69474375, 0.69141975, 0.69030775, 0.7116309999999999, 0.68471775, 0.6952959999999999, 0.7089397500000001, 0.704064, 0.70133775, 0.684156, 0.70351975, 0.706236, 0.70569375, 0.725424, 0.7024297500000001, 0.6952959999999999, 0.7191, 0.6952959999999999, 0.68133975, 0.6930839999999999, 0.68471775, 0.6936377499999999, 0.687519, 0.6886359999999999, 0.718039, 0.68246775, 0.6930839999999999, 0.718039, 0.716976, 0.7089397500000001, 0.7116309999999999, 0.7121677500000001, 0.715911, 0.69030775, 0.704064, 0.704064, 0.701884, 0.713775, 0.6930839999999999, 0.68471775, 0.691975, 0.70024375, 0.696399, 0.68583975, 0.69252975, 0.709479, 0.68695975, 0.7116309999999999, 0.70351975, 0.689751, 0.70351975, 0.70785975, 0.7296, 0.68020975, 0.709479, 0.70785975, 0.6807750000000001, 0.68583975, 0.6733877500000001, 0.6880777499999999, 0.689751, 0.70133775, 0.67680775, 0.6705240000000001, 0.68695975, 0.684156, 0.70785975, 0.698599, 0.6936377499999999, 0.66649375, 0.707319, 0.685279, 0.673959, 0.687519, 0.684156, 0.69584775, 0.7024297500000001, 0.689751, 0.67680775, 0.70351975, 0.6880777499999999, 0.7046077500000001, 0.685279, 0.661276, 0.66764775, 0.6908639999999999, 0.70024375, 0.68020975, 0.68919375, 0.715911, 0.69030775, 0.69252975, 0.69694975, 0.694191, 0.704064, 0.6659160000000001, 0.69252975, 0.6807750000000001, 0.685279, 0.6975, 0.69252975, 0.6936377499999999, 0.676239, 0.698599, 0.68919375, 0.6785110000000001, 0.6751, 0.70351975, 0.7046077500000001, 0.683031, 0.685279, 0.701884, 0.673959, 0.6779437500000001, 0.68583975, 0.69694975, 0.68359375, 0.70351975, 0.6636, 0.6728160000000001, 0.696399, 0.684156, 0.684156, 0.689751, 0.705151, 0.6733877500000001, 0.6886359999999999, 0.6908639999999999, 0.6601110000000001, 0.687519, 0.6659160000000001]} -------------------------------------------------------------------------------- /docs/how_tos/01_simulating_H2_ground_state/data.json: -------------------------------------------------------------------------------- 1 | {"noise_factors": [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199], "values": [-1.8013159644397592, -1.7769705332195236, -1.741410142770287, -1.7078936000446099, -1.6613286758874612, -1.6438780527457584, -1.597947948202834, -1.5725442867790804, -1.5054031958083125, -1.4914389796059582, -1.4433313278356732, -1.4010988092817533, -1.344928311637361, -1.3186897978015717, -1.283029312976597, -1.251811603893277, -1.1996001666836997, -1.177462525331265, -1.1313174153546084, -1.1134766762818333, -1.0908624680823304, -1.0570128790349573, -1.0232244977144038, -1.0209339298880704, -0.9768172546227052, -0.9847838129309499, -0.9649785194355042, -0.9594374406729831, -0.9554650901872478, -0.9508830579982653, -0.9484848042776969, -0.9443409226562758, -0.9444340608179684, -0.9616434272046145, -0.9665414165909825, -0.9781701641063891, -0.9744941248289913, -0.9837683072688549, -0.9955195807255325, -0.9747780373132929, -1.0105863588173662, -1.0319750140261397, -1.0349895366268838, -1.022931464379255, -1.0326921529118744, -1.045780640944338, -1.0413872484342264, -1.0596427801349757, -1.0712213882078931, -1.0992001912938918, -1.0780479739522468, -1.089781370972403, -1.089539901133894, -1.0897349136427144, -1.1001838184414698, -1.1101055557365935, -1.0860259357982955, -1.1016136203149696, -1.1028696433943614, -1.0979168151652179, -1.0993911813823318, -1.079051588815305, -1.11127937776859, -1.0817766173599141, -1.0973930049397764, -1.0831235516249769, -1.1025371518825495, -1.082655697598564, -1.0683716121107492, -1.0696279677612355, -1.0633466338310562, -1.070001666338471, -1.0824291964500703, -1.0677939725935326, -1.0598024946671951, -1.075777510801983, -1.078079027425004, -1.0764190293442932, -1.0739001699792132, -1.0812849740175776, -1.0841688178757114, -1.0650386688883662, -1.0644755550024387, -1.063514824580373, -1.0811370970818777, -1.0694844472358631, -1.0572013237670888, -1.0585600994192501, -1.0643401833325004, -1.0800182204343824, -1.0601842290851804, -1.0451048300305217, -1.0778588874240702, -1.0614462832680898, -1.0640267882092842, -1.0737122150167324, -1.0674317684675323, -1.0836950418145597, -1.0739235110269787, -1.067234857763206], "variances": [0.08075915171537483, 0.0901609739325171, 0.10884846395672519, 0.12836039849226477, 0.15092772258357148, 0.16391911390781513, 0.1841108899021921, 0.19588822144382606, 0.22794748124782865, 0.2340552971845224, 0.25045527465664735, 0.2665106748437645, 0.28525141694317596, 0.292414912211869, 0.3050306132584429, 0.3135532748032851, 0.3229966951529358, 0.32740036440386716, 0.33580314039780035, 0.3384781982755462, 0.34133322647162023, 0.3431958405863889, 0.3441061592167546, 0.3440829902194321, 0.34472746476084015, 0.34362802663826664, 0.34478578488627687, 0.3439603684172361, 0.3448331693724914, 0.34502362382481055, 0.3436393875759606, 0.34465863427168186, 0.3450831273488044, 0.3459670042042438, 0.3464885273636706, 0.3470769342204962, 0.3466971466686623, 0.3474644147469083, 0.3481294617665608, 0.34700364613722834, 0.3485236214757977, 0.34909794551196827, 0.34919330121889536, 0.3487464808216203, 0.3491088500152606, 0.3491180023591735, 0.34926321629830737, 0.3493933459259258, 0.3492306603921065, 0.3478700573890085, 0.34903086084465873, 0.34875561760392176, 0.34859171868618094, 0.3486304410296502, 0.34848740892547325, 0.34820403488750196, 0.34891214485788313, 0.3484382377985303, 0.3482434428992806, 0.34832421414603887, 0.3477015502677436, 0.34893813937946927, 0.3475301786185544, 0.34895222830299755, 0.3485632815247845, 0.3489376571405586, 0.3483756486723754, 0.3489176590895312, 0.34924444120809306, 0.349285668158737, 0.34937590590269446, 0.34928663963677775, 0.3489121090805803, 0.34915216840671937, 0.34932232278446257, 0.34908717117644417, 0.34909357081913944, 0.3491669869362821, 0.34913072881373897, 0.34890407937392737, 0.34894726031438306, 0.3493891556217624, 0.34924497096804696, 0.3494156311802874, 0.34899487420538905, 0.3492295615520375, 0.3495179536357683, 0.34952285614396394, 0.34946362041815615, 0.34920929756759655, 0.3495168776100452, 0.34944174771052894, 0.34909866896559566, 0.3495003875782248, 0.3494904773502596, 0.3491489832074453, 0.34933403847892175, 0.3490765598177482, 0.3493135699720472, 0.3494751844649157], "num_shots": [4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000]} -------------------------------------------------------------------------------- /docs/media/base_estimator.drawio: -------------------------------------------------------------------------------- 1 | 7Vhdk5owFP01PNYBAtR9FD+2O+3OtN12ttu3CBFQJDYElf763kjCh1S7u+OIdnZ8MPfk3pCce08ug4aGy+0tw6vwnvok1kzd32popJmmYfR1+BNIXiBO3yiAgEW+dKqAh+g3kaCMC7LIJ2nDkVMa82jVBD2aJMTjDQwzRjdNtxmNm09d4YC0gAcPx230MfJ5WKC2o1f4BxIFoXwyUvteYuUrgTTEPt3UIDTW0JBRyovRcjskseBO0VLETQ7MlvtiJOHPCfj20b+/faSjn7/usi/r+fj7/G7xTq6yxnEmz6sNTW3gRgknbIY9IkzX1Uwnhoe4UwajQIxKRAEuTsk45RGcmzI1C7uZVhE7FniumA35MoaRUc6tCeNke/B4RkkaFBuhS8JZDi4yAHJShMg6syTtmypphspNWEuYLTEs6yQoV664hIGk8wXU9lsHJj5UljQp4yENaILjcYW6jGaJT8SqOliVzydKV5KqOeE8lzLBGacA1YgEslj+Q8T3bGU+1edGW7l4YeXK2ka8FgbWU22mChKGivFxGu72WizuD4TawJzG1FsU0CQSnO28CzYEBccTDIzRjHnkCLOmvAYwCwj/V3G3C4aRGPNo3dzHydNvtpT1wDEna7ijKKsJ5bAoZkDekMbgJGbQxBK/04hFiUNqxbD/Ihb7nGIxzGtSy8sq/zXaOqFa0JnUsgsFJnBec1hR6CRpbeXPAqgK0bhp3trGfhPb8zf7+jF/GBQ7qCqxPMrrixO1pOxibwFJ7lrG6vSKjfJdozsd6y0q/hsdn1CT1jV0MKtV9gPSeecy9b0Lo/vWZbSoeCv5Nk32Jbchy0KX34bslh6/ZgmokXStSfT+4l4n0Zsmn6FJ55I1iZwr0KTT0mSv1+uqNd6c720QzOrLVUFm9fkPjf8A -------------------------------------------------------------------------------- /docs/media/base_estimator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/prototype-zne/c6a9e767dab3801972991548f5c859816ab1bbc0/docs/media/base_estimator.png -------------------------------------------------------------------------------- /docs/media/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/prototype-zne/c6a9e767dab3801972991548f5c859816ab1bbc0/docs/media/cover.png -------------------------------------------------------------------------------- /docs/media/extrapolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/prototype-zne/c6a9e767dab3801972991548f5c859816ab1bbc0/docs/media/extrapolation.png -------------------------------------------------------------------------------- /docs/media/noise_amplification-folding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/prototype-zne/c6a9e767dab3801972991548f5c859816ab1bbc0/docs/media/noise_amplification-folding.png -------------------------------------------------------------------------------- /docs/media/noise_amplification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/prototype-zne/c6a9e767dab3801972991548f5c859816ab1bbc0/docs/media/noise_amplification.png -------------------------------------------------------------------------------- /docs/media/noise_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/prototype-zne/c6a9e767dab3801972991548f5c859816ab1bbc0/docs/media/noise_profile.png -------------------------------------------------------------------------------- /docs/media/runtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/prototype-zne/c6a9e767dab3801972991548f5c859816ab1bbc0/docs/media/runtime.png -------------------------------------------------------------------------------- /docs/media/zne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/prototype-zne/c6a9e767dab3801972991548f5c859816ab1bbc0/docs/media/zne.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core >=3.2,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "prototype-zne" 7 | authors = [ 8 | {name = "Pedro Rivero", email = "pedro.rivero@ibm.com"}, 9 | {name = "Friederike Metz"}, 10 | {name = "Areeq Hasan"}, 11 | {name = "Caleb Johnson", email = "caleb.johnson@ibm.com"}, 12 | {name = "Agata Branczyk", email = "agata.branczyk@ibm.com"}, 13 | ] 14 | maintainers = [ 15 | {name = "Pedro Rivero", email = "pedro.rivero@ibm.com"}, 16 | ] 17 | keywords = [ 18 | "error mitigation", 19 | "zero noise extrapolation", 20 | "quantum computing", 21 | "quantum information science", 22 | "qiskit", 23 | ] 24 | readme = "README.md" 25 | license = {file = "LICENSE.txt"} 26 | classifiers = [ 27 | "Development Status :: 5 - Production/Stable", 28 | "Intended Audience :: Developers", 29 | "Intended Audience :: Education", 30 | "Intended Audience :: Science/Research", 31 | "License :: OSI Approved :: Apache Software License", 32 | "Natural Language :: English", 33 | "Operating System :: MacOS", 34 | "Operating System :: POSIX :: Linux", 35 | "Operating System :: Microsoft :: Windows", 36 | "Programming Language :: Python :: 3.8", 37 | "Programming Language :: Python :: 3.9", 38 | "Programming Language :: Python :: 3.10", 39 | "Programming Language :: Python :: 3.11", 40 | "Programming Language :: Python :: 3.12", 41 | "Topic :: Scientific/Engineering :: Physics", 42 | ] 43 | dynamic = ["version", "description"] 44 | requires-python = ">=3.8" 45 | dependencies = [ 46 | "qiskit >= 0.45.1", 47 | ] 48 | 49 | [project.optional-dependencies] 50 | dev = [ 51 | "tox >= 4.4.6", 52 | "pre-commit >= 2.19.0", 53 | "commitizen >= 2.28.0", 54 | ] 55 | test = [ 56 | "pytest >= 7.1.2", 57 | "pytest-cov >= 3.0.0", 58 | "pytest-randomly >= 3.12.0", 59 | "qiskit-aer >= 0.13.1", 60 | ] 61 | lint = [ 62 | "autoflake >= 1.7.6", 63 | "black[jupyter] ~= 22.6.0", 64 | "flake8 >= 4.0.1", 65 | "isort >= 5.10.1", 66 | "mypy >= 0.961", 67 | "mypy-extensions >= 0.4.3", 68 | "pylint >= 2.14.4", 69 | ] 70 | docs = [ 71 | "jupyter-sphinx >= 0.3.2", 72 | "nbsphinx >= 0.8.8", 73 | "reno >= 3.5.0", 74 | "sphinx-autodoc-typehints >= 1.17.0", 75 | ] 76 | notebook = [ 77 | "jupyter >= 1.0.0", 78 | "notebook >= 6.4.12", 79 | "qiskit[visualization] >= 0.45.1", 80 | "qiskit-aer >= 0.13.1", 81 | "nbqa >= 1.3.1", 82 | "treon >= 0.1.3", 83 | ] 84 | 85 | [project.urls] 86 | "Home" = "https://qiskit-community.github.io/prototypes/" 87 | "Documentation" = "https://github.com/qiskit-community/prototype-zne/tree/main/docs/" 88 | "Repository" = "https://github.com/qiskit-community/prototype-zne/" 89 | "Download" = "https://pypi.org/project/prototype-zne/" 90 | 91 | [tool.flit.module] 92 | name = "zne" 93 | 94 | 95 | ############################################################ 96 | ## TOOLS 97 | ############################################################ 98 | [tool.black] 99 | line-length = 100 100 | exclude = '\.ipynb$' 101 | extend-exclude = '.*/qiskit/.*' 102 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | minversion = 7.0 3 | addopts = 4 | -v --doctest-modules 5 | --cov --cov-report html 6 | --cov-report term:skip-covered 7 | python_files = 8 | __init__.py 9 | test_*.py 10 | testpaths = 11 | test 12 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from collections.abc import Iterable 14 | 15 | from zne import __version__ 16 | 17 | 18 | def test_version(): 19 | assert __version__ == "1.3.1" 20 | 21 | 22 | ################################################################################ 23 | ## DEFINITIONS 24 | ################################################################################ 25 | TYPES = [ 26 | INT := 0, 27 | FLOAT := 0.0, 28 | NAN := float("NaN"), 29 | INF := float("Inf"), 30 | MINF := float("-Inf"), 31 | COMPLEX := complex(0, 0), 32 | STR := "0", 33 | BOOL := True, 34 | NONE := None, 35 | LIST := [0], 36 | TUPLE := (0,), 37 | DICT := {0: 0}, 38 | ] 39 | NO_INTS = [t for t in TYPES if not isinstance(t, int)] 40 | NO_NONE = [t for t in TYPES if t is not None] 41 | NO_INTS_NONE = [t for t in NO_INTS if t is not None] 42 | NO_REAL = [t for t in NO_INTS if not isinstance(t, float)] 43 | NO_NUM = [t for t in NO_REAL if not isinstance(t, complex)] 44 | ITERS = [t for t in TYPES if isinstance(t, Iterable)] 45 | NO_ITERS = [t for t in TYPES if not isinstance(t, Iterable)] 46 | NO_ITERS_NONE = [t for t in NO_ITERS if t is not None] 47 | 48 | 49 | ################################################################################ 50 | ## ALL 51 | ################################################################################ 52 | __all__ = [ 53 | "TYPES", 54 | "NO_INTS", 55 | "NO_INTS_NONE", 56 | "NO_REAL", 57 | "NO_NUM", 58 | "ITERS", 59 | "NO_ITERS", 60 | ] 61 | -------------------------------------------------------------------------------- /test/conftest.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | -------------------------------------------------------------------------------- /test/extrapolation/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | -------------------------------------------------------------------------------- /test/extrapolation/test_exponential_extrapolator.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | 14 | from test import NO_INTS 15 | 16 | from numpy import allclose, random 17 | from pytest import mark, raises 18 | 19 | from zne.extrapolation import ( 20 | BiExponentialExtrapolator, 21 | ExponentialExtrapolator, 22 | MonoExponentialExtrapolator, 23 | MultiExponentialExtrapolator, 24 | ) 25 | from zne.utils.unset import UNSET 26 | 27 | ################################################################################ 28 | ## AUXILIARY 29 | ################################################################################ 30 | ATOL: float = 1e-2 31 | RTOL: float = 1e-5 32 | 33 | MAX_NUM_TERMS: int = 1 34 | 35 | 36 | def extrapolate_zero_test_cases(max_num_terms): 37 | for num_terms in range(1, max_num_terms + 1): # All num_terms up to max 38 | min_points = num_terms * 2 + 1 39 | for coefficients in ( 40 | [1 for _ in range(min_points)], 41 | [(-1) ** (c % 2) for c in range(min_points)], 42 | [1 + c for c in range(min_points)], 43 | [1 / (1 + c) for c in range(min_points)], 44 | [(-1) ** (c % 2) * (1 + c) for c in range(min_points)], 45 | [(-1) ** (c % 2) / (1 + c) for c in range(min_points)], 46 | ): # Different curves 47 | model = lambda x: MultiExponentialExtrapolator(num_terms)._model(x, *coefficients) 48 | for extra in range(5): # Different number of data points 49 | x_data = [1 + x for x in range(min_points + extra)] 50 | y_data = [model(x) + random.normal(0, 1e-6) for x in x_data] 51 | sigma_y = [random.normal(0.1, 1e-4) for _ in y_data] 52 | expected = model(0) 53 | yield num_terms, x_data, y_data, sigma_y, expected 54 | 55 | 56 | ################################################################################ 57 | ## EXTRAPOLATORS 58 | ################################################################################ 59 | class TestMultiExponentialExtrapolator: 60 | """Test polynomial extrapolator.""" 61 | 62 | ################################################################################ 63 | ## TESTS 64 | ################################################################################ 65 | @mark.parametrize( 66 | "num_terms", 67 | cases := range(1, 5 + 1), 68 | ids=[f"num_terms = {num_terms!r}" for num_terms in cases], 69 | ) 70 | def test_num_terms(self, num_terms): 71 | """Test num_terms.""" 72 | extrapolator = MultiExponentialExtrapolator(num_terms=num_terms) 73 | assert isinstance(extrapolator.num_terms, int) 74 | assert extrapolator.num_terms == num_terms 75 | extrapolator = MultiExponentialExtrapolator(num_terms=str(num_terms)) 76 | assert isinstance(extrapolator.num_terms, int) 77 | assert extrapolator.num_terms == num_terms 78 | extrapolator = MultiExponentialExtrapolator(num_terms=float(num_terms)) 79 | assert isinstance(extrapolator.num_terms, int) 80 | assert extrapolator.num_terms == num_terms 81 | 82 | @mark.parametrize( 83 | "num_terms", 84 | cases := [d for d in NO_INTS if not isinstance(d, (str, float))], 85 | ids=[f"num_terms = {num_terms!r}" for num_terms in cases], 86 | ) 87 | def test_num_terms_type_error(self, num_terms): 88 | """Test num_terms type error.""" 89 | with raises(TypeError): 90 | MultiExponentialExtrapolator(num_terms) 91 | 92 | @mark.parametrize( 93 | "num_terms", 94 | cases := list(range(0, -5, -1)) + [str(float(d)) for d in range(1, 5 + 1)], 95 | ids=[f"num_terms = {num_terms!r}" for num_terms in cases], 96 | ) 97 | def test_num_terms_value_error(self, num_terms): 98 | """Test num_terms value error.""" 99 | with raises(ValueError): 100 | MultiExponentialExtrapolator(num_terms) 101 | 102 | @mark.parametrize( 103 | "num_terms", cases := range(1, 5 + 1), ids=[f"{num_terms=}" for num_terms in cases] 104 | ) 105 | def test_min_points(self, num_terms): 106 | """Test min points.""" 107 | extrapolator = MultiExponentialExtrapolator(num_terms=num_terms) 108 | assert extrapolator.min_points == num_terms * 2 + 1 109 | 110 | @mark.parametrize( 111 | "num_terms, x_data, y_data, sigma_y, expected", 112 | [*extrapolate_zero_test_cases(MAX_NUM_TERMS)], 113 | ) 114 | def test_extrapolate_zero(self, num_terms, x_data, y_data, sigma_y, expected): 115 | """Test extrapolate zero.""" 116 | extrapolator = MultiExponentialExtrapolator(num_terms) 117 | value, std_error, metadata = extrapolator.extrapolate_zero(x_data, y_data, sigma_y=sigma_y) 118 | assert allclose(value, expected, atol=ATOL, rtol=RTOL) 119 | assert isinstance(std_error, float) # TODO: test value 120 | for key in ["coefficients", "covariance_matrix", "residuals", "R2"]: 121 | assert metadata.get(key, UNSET) is not UNSET # TODO: test values 122 | 123 | 124 | ################################################################################ 125 | ## FACADES 126 | ################################################################################ 127 | class TestFacades: 128 | """Test polynomial extrapolator facades.""" 129 | 130 | @mark.parametrize( 131 | "cls, configs", 132 | [ 133 | (ExponentialExtrapolator, {"num_terms": 1}), 134 | (MonoExponentialExtrapolator, {"num_terms": 1}), 135 | (BiExponentialExtrapolator, {"num_terms": 2}), 136 | ], 137 | ) 138 | def test_facades(self, cls, configs): 139 | """Test polynomial extrapolator facades.""" 140 | # Note: using `strategy` decorator functionality 141 | assert cls() == MultiExponentialExtrapolator(**configs) 142 | -------------------------------------------------------------------------------- /test/extrapolation/test_polynomial_extrapolator.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | 14 | from test import NO_INTS 15 | 16 | from numpy import allclose, random 17 | from pytest import mark, raises 18 | 19 | from zne.extrapolation import ( 20 | CubicExtrapolator, 21 | LinearExtrapolator, 22 | PolynomialExtrapolator, 23 | QuadraticExtrapolator, 24 | QuarticExtrapolator, 25 | ) 26 | from zne.utils.unset import UNSET 27 | 28 | ################################################################################ 29 | ## AUXILIARY 30 | ################################################################################ 31 | ATOL: float = 1e-2 32 | RTOL: float = 1e-5 33 | 34 | MAX_DEGREE: int = 5 35 | 36 | 37 | def extrapolate_zero_test_cases(max_degree): 38 | for degree in range(1, max_degree + 1): # All degrees up to max 39 | min_points = degree + 1 40 | for coefficients in ( 41 | [1 for _ in range(min_points)], 42 | [-1 for _ in range(min_points)], 43 | [c for c in range(min_points)], 44 | [1 + c for c in range(min_points)], 45 | [1 - c for c in range(min_points)], 46 | ): # Different curves 47 | model = lambda x: PolynomialExtrapolator(degree)._model(x, *coefficients) 48 | for extra in range(5): # Different number of data points 49 | x_data = [1 + x for x in range(min_points + extra)] 50 | y_data = [model(x) + random.normal(0, 1e-6) for x in x_data] 51 | sigma_y = [random.normal(0.1, 1e-4) for _ in y_data] 52 | expected = model(0) 53 | yield degree, x_data, y_data, sigma_y, expected 54 | 55 | 56 | ################################################################################ 57 | ## EXTRAPOLATORS 58 | ################################################################################ 59 | class TestPolynomialExtrapolator: 60 | """Test polynomial extrapolator.""" 61 | 62 | ################################################################################ 63 | ## TESTS 64 | ################################################################################ 65 | @mark.parametrize( 66 | "degree", cases := range(1, 5 + 1), ids=[f"degree = {degree!r}" for degree in cases] 67 | ) 68 | def test_degree(self, degree): 69 | """Test degree.""" 70 | extrapolator = PolynomialExtrapolator(degree=degree) 71 | assert isinstance(extrapolator.degree, int) 72 | assert extrapolator.degree == degree 73 | extrapolator = PolynomialExtrapolator(degree=str(degree)) 74 | assert isinstance(extrapolator.degree, int) 75 | assert extrapolator.degree == degree 76 | extrapolator = PolynomialExtrapolator(degree=float(degree)) 77 | assert isinstance(extrapolator.degree, int) 78 | assert extrapolator.degree == degree 79 | 80 | @mark.parametrize( 81 | "degree", 82 | cases := [d for d in NO_INTS if not isinstance(d, (str, float))], 83 | ids=[f"degree = {degree!r}" for degree in cases], 84 | ) 85 | def test_degree_type_error(self, degree): 86 | """Test degree type error.""" 87 | with raises(TypeError): 88 | PolynomialExtrapolator(degree) 89 | 90 | @mark.parametrize( 91 | "degree", 92 | cases := list(range(0, -5, -1)) + [str(float(d)) for d in range(1, 5 + 1)], 93 | ids=[f"degree = {degree!r}" for degree in cases], 94 | ) 95 | def test_degree_value_error(self, degree): 96 | """Test degree value error.""" 97 | with raises(ValueError): 98 | PolynomialExtrapolator(degree) 99 | 100 | @mark.parametrize("degree", cases := range(1, 5 + 1), ids=[f"{degree=}" for degree in cases]) 101 | def test_min_points(self, degree): 102 | """Test min points.""" 103 | extrapolator = PolynomialExtrapolator(degree=degree) 104 | assert extrapolator.min_points == degree + 1 105 | 106 | @mark.parametrize( 107 | "degree, x_data, y_data, sigma_y, expected", 108 | [*extrapolate_zero_test_cases(MAX_DEGREE)], 109 | ) 110 | def test_extrapolate_zero(self, degree, x_data, y_data, sigma_y, expected): 111 | """Test extrapolate zero.""" 112 | extrapolator = PolynomialExtrapolator(degree) 113 | value, std_error, metadata = extrapolator.extrapolate_zero(x_data, y_data, sigma_y=sigma_y) 114 | assert allclose(value, expected, atol=ATOL, rtol=RTOL) 115 | assert isinstance(std_error, float) # TODO: test value 116 | for key in ["coefficients", "covariance_matrix", "residuals", "R2"]: 117 | assert metadata.get(key, UNSET) is not UNSET # TODO: test values 118 | 119 | 120 | ################################################################################ 121 | ## FACADES 122 | ################################################################################ 123 | class TestFacades: 124 | """Test polynomial extrapolator facades.""" 125 | 126 | @mark.parametrize( 127 | "cls, configs", 128 | [ 129 | (LinearExtrapolator, {"degree": 1}), 130 | (QuadraticExtrapolator, {"degree": 2}), 131 | (CubicExtrapolator, {"degree": 3}), 132 | (QuarticExtrapolator, {"degree": 4}), 133 | ], 134 | ) 135 | def test_facades(self, cls, configs): 136 | """Test polynomial extrapolator facades.""" 137 | # Note: using `strategy` decorator functionality 138 | assert cls() == PolynomialExtrapolator(**configs) 139 | -------------------------------------------------------------------------------- /test/meta/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | -------------------------------------------------------------------------------- /test/meta/test_call.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from unittest.mock import Mock 14 | 15 | from pytest import mark, raises 16 | 17 | from zne.meta.call import zne_call 18 | 19 | 20 | ################################################################################ 21 | ## DECORATOR 22 | ################################################################################ 23 | def test_zne_call(): 24 | call = zne_call(Mock()) 25 | with raises(TypeError): 26 | call("self", "circuits", "observables") 27 | 28 | 29 | @mark.parametrize("call", [None, Ellipsis, True, 0, 1.0, 1j, "call"]) 30 | def test_type_error(call): 31 | with raises(TypeError): 32 | zne_call(call) 33 | -------------------------------------------------------------------------------- /test/meta/test_cls.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | 14 | from unittest.mock import Mock, patch 15 | 16 | from pytest import fixture, mark, raises 17 | from qiskit.primitives import BaseEstimator 18 | 19 | from zne import ZNEStrategy 20 | from zne.meta import zne 21 | from zne.meta.cls import _get_zne_strategy, _set_zne_strategy 22 | 23 | from .. import TYPES 24 | 25 | 26 | class TestZNE: 27 | ################################################################################ 28 | ## FIXTURES 29 | ################################################################################ 30 | @fixture(scope="function") 31 | def Estimator(self): 32 | class Estimator(BaseEstimator): 33 | def _call(self): 34 | pass 35 | 36 | def _run(self): 37 | pass 38 | 39 | return Estimator 40 | 41 | @fixture(scope="function") 42 | def ZNEE(self, Estimator): 43 | return zne(Estimator) 44 | 45 | ################################################################################ 46 | ## TESTS 47 | ################################################################################ 48 | def test_zne_cls(self, Estimator): 49 | mocks = {p: Mock(return_value=p) for p in ("zne_init", "zne_call", "zne_run")} 50 | with patch.multiple("zne.meta.cls", **mocks): 51 | ZNEE = zne(Estimator) 52 | assert ZNEE.__init__ == "zne_init" 53 | mocks["zne_init"].assert_called_once() 54 | assert ZNEE.__call__ == "zne_call" 55 | mocks["zne_call"].assert_called_once() 56 | assert ZNEE._run == "zne_run" 57 | mocks["zne_run"].assert_called_once() 58 | assert hasattr(ZNEE, "zne_strategy") 59 | assert isinstance(ZNEE.zne_strategy, property) 60 | 61 | @mark.parametrize("obj", TYPES) 62 | def test_zne_cls_type_error(self, obj): 63 | with raises(TypeError): 64 | zne(obj) 65 | with raises(TypeError): 66 | zne(type(obj)) 67 | 68 | def test_zne_strategy(self, ZNEE, Estimator): 69 | estimator = ZNEE() 70 | 71 | # None zne_strategy 72 | estimator.zne_strategy = None 73 | assert isinstance(estimator.zne_strategy, ZNEStrategy) 74 | assert estimator.zne_strategy is estimator._zne_strategy 75 | 76 | # Invalid zne_strategy 77 | with raises(TypeError): 78 | estimator.zne_strategy = "ERROR" 79 | 80 | # Custom zne_strategy 81 | zne_strategy = ZNEStrategy() 82 | estimator.zne_strategy = zne_strategy 83 | assert estimator.zne_strategy is zne_strategy 84 | assert estimator.zne_strategy is estimator._zne_strategy 85 | 86 | # Unset zne_strategy 87 | estimator = Estimator() # instantiation before class update 88 | Estimator.zne_strategy = property(_get_zne_strategy, _set_zne_strategy) 89 | with raises(AttributeError): 90 | estimator._zne_strategy 91 | assert isinstance(estimator.zne_strategy, ZNEStrategy) 92 | assert estimator.zne_strategy is estimator._zne_strategy 93 | -------------------------------------------------------------------------------- /test/meta/test_init.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from unittest.mock import Mock 14 | 15 | from pytest import fixture, mark, raises 16 | 17 | from zne.meta.init import zne_init 18 | from zne.zne_strategy import ZNEStrategy 19 | 20 | 21 | class TestZNEInit: 22 | ################################################################################ 23 | ## FIXTURES 24 | ################################################################################ 25 | @fixture(scope="function") 26 | def init(_self): 27 | return zne_init(Mock()) 28 | 29 | @fixture(scope="function") 30 | def self(_self): 31 | return Mock() 32 | 33 | ################################################################################ 34 | ## TESTS 35 | ################################################################################ 36 | def test_deprecation(_self, init): 37 | with raises(TypeError): 38 | init("self", circuits="circuits") 39 | with raises(TypeError): 40 | init("self", observables="observables") 41 | with raises(TypeError): 42 | init("self", parameters="parameters") 43 | 44 | def test_base_init(_self, self): 45 | base_init = Mock() 46 | init = zne_init(base_init) 47 | kwargs = {"Tolkien": "Silmarilion", "Moises": "Genesis"} 48 | 49 | # Without zne_strategy 50 | _ = init(self, options="options", **kwargs) 51 | base_init.assert_called_once_with(self, options="options", **kwargs) 52 | 53 | # With zne_strategy 54 | base_init.reset_mock() 55 | zne_strategy = ZNEStrategy() 56 | _ = init(self, options="options", zne_strategy=zne_strategy, **kwargs) 57 | base_init.assert_called_once_with(self, options="options", **kwargs) 58 | assert self.zne_strategy is zne_strategy 59 | 60 | 61 | @mark.parametrize("init", [None, Ellipsis, True, 0, 1.0, 1j, "init"]) 62 | def test_type_error(init): 63 | with raises(TypeError): 64 | zne_init(init) 65 | -------------------------------------------------------------------------------- /test/meta/test_run.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from unittest.mock import Mock 14 | 15 | from pytest import fixture, mark, raises 16 | 17 | from zne.meta.job import ZNEJob 18 | from zne.meta.run import zne_run 19 | from zne.zne_strategy import ZNEStrategy 20 | 21 | 22 | class TestZNERun: 23 | ################################################################################ 24 | ## FIXTURES 25 | ################################################################################ 26 | @fixture(scope="function") 27 | def self(_self): 28 | self = Mock() 29 | self.zne_strategy = ZNEStrategy() 30 | return self 31 | 32 | @fixture(scope="function") 33 | def run_options(_self): 34 | return {"Forrest": "Gump", "Charriots": "Fire"} 35 | 36 | ################################################################################ 37 | ## TESTS 38 | ################################################################################ 39 | def test_base_run_noop_zne_strategy(_self, self, run_options): 40 | base_run = Mock() 41 | run = zne_run(base_run) 42 | self.zne_strategy = ZNEStrategy(noise_factors=range(1, 4)) 43 | _ = run( 44 | self, 45 | circuits="circuits", 46 | observables="observables", 47 | parameter_values="parameter_values", 48 | zne_strategy=None, 49 | **run_options, 50 | ) 51 | base_run.assert_called_once_with( 52 | self, 53 | circuits="circuits", 54 | observables="observables", 55 | parameter_values="parameter_values", 56 | **run_options, 57 | ) 58 | 59 | def test_base_run_default_zne_strategy(_self, self, run_options): 60 | base_run = Mock() 61 | run = zne_run(base_run) 62 | self.zne_strategy = ZNEStrategy(noise_factors=range(1, 4)) 63 | build_noisy_circuits = Mock(return_value="noisy_circuits") 64 | self.zne_strategy.build_noisy_circuits = build_noisy_circuits 65 | map_to_noisy_circuits = Mock(side_effect=lambda x: "noisy_" + x) 66 | self.zne_strategy.map_to_noisy_circuits = map_to_noisy_circuits 67 | _ = run( 68 | self, 69 | circuits="circuits", 70 | observables="observables", 71 | parameter_values="parameter_values", 72 | **run_options, 73 | ) 74 | build_noisy_circuits.assert_called_once_with("circuits") 75 | map_to_noisy_circuits.assert_any_call("observables") 76 | map_to_noisy_circuits.assert_any_call("parameter_values") 77 | assert map_to_noisy_circuits.call_count == 2 78 | base_run.assert_called_once_with( 79 | self, 80 | circuits="noisy_circuits", 81 | observables="noisy_observables", 82 | parameter_values="noisy_parameter_values", 83 | **run_options, 84 | ) 85 | 86 | def test_base_run_custom_zne_strategy(_self, self, run_options): 87 | base_run = Mock() 88 | run = zne_run(base_run) 89 | zne_strategy = ZNEStrategy(noise_factors=range(1, 7)) 90 | build_noisy_circuits = Mock(return_value="noisy_circuits") 91 | zne_strategy.build_noisy_circuits = build_noisy_circuits 92 | map_to_noisy_circuits = Mock(side_effect=lambda x: "noisy_" + x) 93 | zne_strategy.map_to_noisy_circuits = map_to_noisy_circuits 94 | _ = run( 95 | self, 96 | circuits="circuits", 97 | observables="observables", 98 | parameter_values="parameter_values", 99 | zne_strategy=zne_strategy, 100 | **run_options, 101 | ) 102 | build_noisy_circuits.assert_called_once_with("circuits") 103 | map_to_noisy_circuits.assert_any_call("observables") 104 | map_to_noisy_circuits.assert_any_call("parameter_values") 105 | assert map_to_noisy_circuits.call_count == 2 106 | base_run.assert_called_once_with( 107 | self, 108 | circuits="noisy_circuits", 109 | observables="noisy_observables", 110 | parameter_values="noisy_parameter_values", 111 | **run_options, 112 | ) 113 | 114 | def test_zne_strategy_type_error(_self, self, run_options): 115 | base_run = Mock() 116 | run = zne_run(base_run) 117 | with raises(TypeError): 118 | run( 119 | self, 120 | circuits="circuits", 121 | observables="observables", 122 | parameter_values="parameter_values", 123 | zne_strategy="zne_strategy", 124 | **run_options, 125 | ) 126 | 127 | def test_zne_job(_self, self, run_options): 128 | base_run = Mock(return_value="job") 129 | run = zne_run(base_run) 130 | job = run( 131 | self, 132 | circuits="circuits", 133 | observables="observables", 134 | parameter_values="parameter_values", 135 | **run_options, 136 | ) 137 | assert isinstance(job, ZNEJob) 138 | assert job.base_job == "job" 139 | assert job.zne_strategy is self.zne_strategy 140 | assert job.target_num_experiments == len("circuits") 141 | 142 | 143 | @mark.parametrize("run", [None, Ellipsis, True, 0, 1.0, 1j, "run"]) 144 | def test_type_error(run): 145 | with raises(TypeError): 146 | zne_run(run) 147 | -------------------------------------------------------------------------------- /test/noise_amplification/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | -------------------------------------------------------------------------------- /test/noise_amplification/folding_amplifier/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | -------------------------------------------------------------------------------- /test/noise_amplification/folding_amplifier/conftest.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from pytest import fixture 14 | from qiskit.circuit.random import random_circuit 15 | 16 | 17 | @fixture(scope="module") 18 | def get_random_circuit(): 19 | def factory_method(seed): 20 | return random_circuit(2, 2, seed=seed) 21 | 22 | return factory_method 23 | 24 | 25 | @fixture(scope="module") 26 | def circuit(get_random_circuit): 27 | return get_random_circuit(0) 28 | -------------------------------------------------------------------------------- /test/noise_amplification/test_noise_amplifier.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | 14 | from itertools import product 15 | from unittest.mock import Mock, patch 16 | 17 | from pytest import mark 18 | from qiskit.circuit.random import random_circuit 19 | from qiskit.quantum_info.operators import Operator 20 | from qiskit.transpiler import TransformationPass 21 | 22 | from zne import NOISE_AMPLIFIER_LIBRARY 23 | 24 | 25 | @mark.parametrize( 26 | "NoiseAmplifier", 27 | NOISE_AMPLIFIER_LIBRARY.values(), 28 | ids=NOISE_AMPLIFIER_LIBRARY.keys(), 29 | ) 30 | class TestNoiseAmplifier: 31 | @mark.parametrize( 32 | "circuit, noise_factor", 33 | cases := tuple( 34 | product( 35 | [ 36 | random_circuit(5, 3, seed=0), 37 | random_circuit(5, 3, seed=5), 38 | random_circuit(5, 6, seed=66), 39 | random_circuit(5, 6, seed=1081), 40 | ], 41 | [1, 3, 5], 42 | ) 43 | ), 44 | ids=[f"{type(c).__name__}<{len(c)}>-{nf}" for c, nf in cases], 45 | ) 46 | @mark.filterwarnings("ignore::UserWarning") 47 | def test_circuit_equivalence(self, NoiseAmplifier, circuit, noise_factor): 48 | noisy_circuit = NoiseAmplifier().amplify_circuit_noise(circuit.copy(), noise_factor) 49 | assert Operator(noisy_circuit).equiv(Operator(circuit)) 50 | 51 | ################################################################################ 52 | ## TESTS 53 | ################################################################################ 54 | @mark.parametrize("noise_factor", [1, 1.2, 2.4, -3.14]) 55 | def test_build_transpiler_pass(self, NoiseAmplifier, noise_factor): 56 | DAG = f"DAG:{noise_factor}" 57 | AMP_DAG = f"AMP_DAG:{noise_factor}" 58 | noise_amplifier = NoiseAmplifier() 59 | noise_amplifier.amplify_dag_noise = Mock(return_value=AMP_DAG) 60 | transpiler_pass = noise_amplifier.build_transpiler_pass(noise_factor) 61 | assert isinstance(transpiler_pass, TransformationPass) 62 | assert AMP_DAG == transpiler_pass.run(DAG) 63 | noise_amplifier.amplify_dag_noise.assert_called_once_with(DAG, noise_factor) 64 | 65 | @mark.parametrize("noise_factor", [1, 1.2, 2.4, -3.14]) 66 | def test_build_pass_manager(self, NoiseAmplifier, noise_factor): 67 | target = "zne.noise_amplification.noise_amplifier" 68 | target_pass_builder = target + ".NoiseAmplifier.build_transpiler_pass" 69 | target_pass_manager_class = target + ".PassManager" 70 | TRANSPILER_PASS = f"" 71 | with patch(target_pass_builder, return_value=TRANSPILER_PASS) as pass_builder, patch( 72 | target_pass_manager_class 73 | ) as pass_manager_class: 74 | _ = NoiseAmplifier().build_pass_manager(noise_factor) 75 | pass_builder.assert_called_once_with(noise_factor) 76 | pass_manager_class.assert_called_once_with(TRANSPILER_PASS) 77 | -------------------------------------------------------------------------------- /test/test_immutable_strategy.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from inspect import signature 14 | from random import seed as set_random_seed 15 | from random import shuffle 16 | from unittest.mock import Mock 17 | 18 | from pytest import fixture, mark 19 | 20 | from zne import NOISE_AMPLIFIER_LIBRARY 21 | from zne.immutable_strategy import ImmutableStrategy 22 | 23 | STRATEGIES = {**NOISE_AMPLIFIER_LIBRARY} 24 | 25 | INIT_OPTIONS = [ 26 | ((), {}), 27 | ((), {"hello": "world"}), 28 | ((1, 2), {}), 29 | ((1.1, 2.2), {"Qiskit": True}), 30 | (("A", 12), {"pain": None, "gain": None}), 31 | ] 32 | 33 | 34 | class TestImmutableStrategy: 35 | 36 | ################################################################################ 37 | ## FIXTURES 38 | ################################################################################ 39 | @fixture 40 | def DummyStrategy(self): 41 | class DummyStrategy(ImmutableStrategy): 42 | _save_init_options = Mock() 43 | 44 | def __init__(self, *args, **kwargs): 45 | pass 46 | 47 | return DummyStrategy 48 | 49 | @fixture 50 | def get_init_options(self): 51 | def factory_method(cls): 52 | init_signature = signature(cls.__init__) 53 | init_keys = init_signature.parameters.keys() 54 | init_options = {k: f"Mock({k})" for k in init_keys if k != "self"} 55 | return init_options 56 | 57 | return factory_method 58 | 59 | @fixture 60 | def shuffle_init_options(self, get_init_options): 61 | def factory_method(cls, seed=None): 62 | set_random_seed(seed) 63 | ordered_options = get_init_options(cls) 64 | items = list(ordered_options.items()) 65 | shuffle(items) 66 | return dict(items) 67 | 68 | return factory_method 69 | 70 | ################################################################################ 71 | ## TESTS 72 | ################################################################################ 73 | @mark.parametrize( 74 | "init_args, init_kwargs", 75 | [ 76 | ((), {}), 77 | ((), {"hello": "world"}), 78 | ((1, 2), {}), 79 | ((1.1, 2.2), {"Qiskit": True}), 80 | (("A", 12), {"pain": None, "gain": None}), 81 | ], 82 | ) 83 | def test_new(self, init_args, init_kwargs, DummyStrategy): 84 | strategy = DummyStrategy(*init_args, **init_kwargs) 85 | strategy._save_init_options.assert_called_once_with(*init_args, **init_kwargs) 86 | 87 | @mark.parametrize("init_args, init_kwargs", INIT_OPTIONS) 88 | def test_save_init_options(self, init_args, init_kwargs, DummyStrategy): 89 | strategy = DummyStrategy() 90 | assert strategy._init_options == {} 91 | strategy._save_init_options(*init_args, **init_kwargs) 92 | assert strategy._init_options == {} 93 | 94 | @mark.parametrize("Strategy", STRATEGIES.values(), ids=STRATEGIES.keys()) 95 | def test_name(self, Strategy): 96 | strategy = Strategy() 97 | assert strategy.name == Strategy.__name__ 98 | 99 | @mark.parametrize("Strategy", STRATEGIES.values(), ids=STRATEGIES.keys()) 100 | def test_options(self, Strategy, shuffle_init_options): 101 | options = shuffle_init_options(Strategy, seed=0) 102 | strategy = Strategy.__new__(Strategy, **options) 103 | options.pop("warn_user", None) 104 | assert strategy.options == dict(sorted(options.items())) 105 | 106 | @mark.parametrize("Strategy", STRATEGIES.values(), ids=STRATEGIES.keys()) 107 | def test_repr(self, Strategy): 108 | strategy = Strategy() 109 | options = dict({"example": "option"}) 110 | strategy._init_options = options 111 | assert repr(strategy) == f"<{Strategy.__name__}:{options}>" 112 | 113 | @mark.parametrize("Strategy", STRATEGIES.values(), ids=STRATEGIES.keys()) 114 | def test_eq(self, Strategy, shuffle_init_options): 115 | options_1 = shuffle_init_options(Strategy, seed=0) 116 | options_2 = shuffle_init_options(Strategy, seed=1) 117 | assert Strategy.__new__(Strategy, **options_1) == Strategy.__new__(Strategy, **options_2) 118 | -------------------------------------------------------------------------------- /test/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | -------------------------------------------------------------------------------- /test/utils/test_classconstant.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from pytest import mark, raises 14 | 15 | from zne.utils.classconstant import classconstant 16 | 17 | 18 | class TestClassConstant: 19 | """Test `classconstant` descriptor.""" 20 | 21 | @mark.parametrize("value", [0, 1, "pi"]) 22 | def test_get(self, value): 23 | """Test get.""" 24 | cls = type("cls", (), {"CONSTANT": classconstant(value)}) 25 | assert cls.CONSTANT == value 26 | assert cls().CONSTANT == value 27 | 28 | def test_set(self): 29 | """Test set.""" 30 | cls = type("cls", (), {"CONSTANT": classconstant(0)}) 31 | with raises(AttributeError): 32 | cls().CONSTANT = "value" 33 | 34 | def test_delete(self): 35 | """Test delete.""" 36 | cls = type("cls", (), {"CONSTANT": classconstant(0)}) 37 | with raises(AttributeError): 38 | del cls().CONSTANT 39 | -------------------------------------------------------------------------------- /test/utils/test_grouping.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from test import NO_INTS, NO_ITERS 14 | 15 | from pytest import mark, raises 16 | 17 | from zne.utils.grouping import ( 18 | from_common_key, 19 | group_elements, 20 | merge_dicts, 21 | squash_for_equal_elements, 22 | ) 23 | 24 | 25 | @mark.parametrize( 26 | "elements, size, expected", 27 | cases := [ 28 | ([], 1, []), 29 | ([], 2, []), 30 | ([], 3, []), 31 | ([0], 1, [(0,)]), 32 | ([0], 2, [(0,)]), 33 | ([0], 3, [(0,)]), 34 | ([0, 1], 1, [(0,), (1,)]), 35 | ([0, 1], 2, [(0, 1)]), 36 | ([0, 1], 3, [(0, 1)]), 37 | ([0, 1, 2], 1, [(0,), (1,), (2,)]), 38 | ([0, 1, 2], 2, [(0, 1), (2,)]), 39 | ([0, 1, 2], 3, [(0, 1, 2)]), 40 | ([0, 1, 2, 3], 1, [(0,), (1,), (2,), (3,)]), 41 | ([0, 1, 2, 3], 2, [(0, 1), (2, 3)]), 42 | ([0, 1, 2, 3], 3, [(0, 1, 2), (3,)]), 43 | ], 44 | ids=[f"list<{len(value)}>-{size}" for value, size, expected in cases], 45 | ) 46 | def test_group_elements(elements, size, expected): 47 | groups = group_elements(elements, group_size=size) 48 | assert groups == (expected) 49 | 50 | 51 | @mark.parametrize("elements", NO_ITERS, ids=[str(type(i).__name__) for i in NO_ITERS]) 52 | def test_group_elements_type_error_iter(elements): 53 | with raises(TypeError): 54 | assert group_elements(elements, group_size=1) 55 | 56 | 57 | @mark.parametrize("size", NO_INTS, ids=[str(type(i).__name__) for i in NO_INTS]) 58 | def test_group_elements_type_error_int(size): 59 | with raises(TypeError): 60 | assert group_elements(elements=[], group_size=size) 61 | 62 | 63 | @mark.parametrize("size", [0, -1]) 64 | def test_group_elements_value_error(size): 65 | with raises(ValueError): 66 | assert group_elements(elements=[], group_size=size) 67 | 68 | 69 | @mark.parametrize( 70 | "dict_list, expected", 71 | [ 72 | ([{}, {}], {}), 73 | ([{0: 1}, {}], {0: 1}), 74 | ([{}, {0: 1}], {0: 1}), 75 | ([{0: 1}, {0: 1}], {0: 1}), 76 | ([{0: 2}, {0: 1}], {0: 1}), 77 | ([{0: 1}, {0: 2}], {0: 2}), 78 | ([{0: 1}, {2: 3}], {0: 1, 2: 3}), 79 | ([{2: 3}, {0: 1}], {2: 3, 0: 1}), 80 | ([{0: 1, 2: 3}, {4: 5}], {0: 1, 2: 3, 4: 5}), 81 | ([{0: 1, 4: 5}, {2: 3}], {0: 1, 4: 5, 2: 3}), 82 | ([{4: 5, 0: 1}, {2: 3}], {4: 5, 0: 1, 2: 3}), 83 | ([{4: 5, 2: 3}, {0: 1}], {4: 5, 2: 3, 0: 1}), 84 | ([{0: 1, 2: 3}, {0: 2}], {0: 2, 2: 3}), 85 | ([{4: 5}, {0: 1, 2: 3}], {4: 5, 0: 1, 2: 3}), 86 | ([{2: 3}, {0: 1, 4: 5}], {2: 3, 0: 1, 4: 5}), 87 | ([{2: 3}, {4: 5, 0: 1}], {2: 3, 4: 5, 0: 1}), 88 | ([{0: 1}, {4: 5, 2: 3}], {0: 1, 4: 5, 2: 3}), 89 | ([{0: 1}, {0: 2, 2: 3}], {0: 2, 2: 3}), 90 | ], 91 | ) 92 | def test_merge_dicts(dict_list, expected): 93 | assert merge_dicts(dict_list) == expected 94 | 95 | 96 | @mark.parametrize( 97 | "dict_list, key, expected", 98 | tuple( 99 | zip( 100 | [ 101 | ({"key": 0}, {"key": 1}), 102 | ({"key": 0}, {}), 103 | ({}, {}), 104 | ], 105 | ["key", "key", "key"], 106 | [(0, 1), (0, None), (None, None)], 107 | ) 108 | ), 109 | ) 110 | def test_from_common_key(dict_list, key, expected): 111 | assert from_common_key(dict_list, key) == expected 112 | 113 | 114 | @mark.parametrize( 115 | "sequence, expected", 116 | tuple( 117 | zip( 118 | [(0, 0), (1, 1), (0, 1)], 119 | [0, 1, (0, 1)], 120 | ) 121 | ), 122 | ) 123 | def test_squash_for_equal_elements(sequence, expected): 124 | assert squash_for_equal_elements(sequence) == expected 125 | 126 | 127 | @mark.parametrize("obj", NO_ITERS, ids=[str(type(i).__name__) for i in NO_ITERS]) 128 | def test_squash_for_equal_elements_type_error(obj): 129 | with raises(TypeError): 130 | assert squash_for_equal_elements(obj) 131 | 132 | 133 | def test_squash_for_equal_elements_value_error(): 134 | with raises(ValueError): 135 | assert squash_for_equal_elements([]) 136 | -------------------------------------------------------------------------------- /test/utils/test_serialization.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from os.path import join as join_path 14 | from tempfile import TemporaryDirectory 15 | 16 | from numpy import array 17 | from pytest import mark, raises 18 | from qiskit.primitives import EstimatorResult 19 | 20 | from zne.utils.serialization import ( 21 | DumpEncoder, 22 | EstimatorResultEncoder, 23 | NumPyEncoder, 24 | ReprEncoder, 25 | ) 26 | 27 | 28 | @mark.parametrize( 29 | "obj, expected", 30 | [ 31 | (0, "0"), 32 | ([0], "[0]"), 33 | ((0,), "[0]"), 34 | ([0, 1], "[0, 1]"), 35 | ((0, 1), "[0, 1]"), 36 | (["0", "1"], '["0", "1"]'), 37 | (("0", "1"), '["0", "1"]'), 38 | ([[], 1], "[[], 1]"), 39 | (([], 1), "[[], 1]"), 40 | ([[0], 1], "[[0], 1]"), 41 | (([0], 1), "[[0], 1]"), 42 | ({"key": "value"}, '{"key": "value"}'), 43 | ({0: 1}, '{"0": 1}'), 44 | ({0: (1,)}, '{"0": [1]}'), 45 | ({0: [1]}, '{"0": [1]}'), 46 | ], 47 | ) 48 | class TestDumpEncoder: 49 | def test_dump(self, obj, expected): 50 | with TemporaryDirectory() as tmpdir: 51 | file_path = join_path(tmpdir, "zne-dump") 52 | DumpEncoder.dump(obj, file=file_path) 53 | with open(file_path) as f: 54 | contents = f.read() 55 | assert contents == expected 56 | 57 | def test_dumps(self, obj, expected): 58 | assert DumpEncoder.dumps(obj) == expected 59 | 60 | 61 | class TestReprEncoder: 62 | @mark.parametrize( 63 | "repr_str", 64 | cases := [ 65 | "", 66 | "dummy", 67 | "some spaces here", 68 | "`~!@#$%^&*()-_=+[]\\\"{}|;:',<.>/?", 69 | ], 70 | ids=cases, 71 | ) 72 | def test_default(self, repr_str): 73 | class DummyRepr: 74 | def __repr__(self): 75 | return repr_str 76 | 77 | obj = DummyRepr() 78 | enc = ReprEncoder() 79 | assert enc.default(obj) == repr(obj) 80 | 81 | 82 | class TestNumPyEncoder: 83 | @mark.parametrize( 84 | "array_like", 85 | cases := [ 86 | [0, 1, 2], 87 | (0, 1, 2), 88 | ], 89 | ids=[type(c) for c in cases], 90 | ) 91 | def test_default(self, array_like): 92 | a = array(array_like) 93 | enc = NumPyEncoder() 94 | assert enc.default(a) == a.tolist() 95 | 96 | def test_default_type_error(self): 97 | with raises(TypeError): 98 | _ = NumPyEncoder().default({"call": "super"}) 99 | 100 | 101 | class TestEstimatorResultEncoder: 102 | @mark.parametrize( 103 | "values, metadata", 104 | zip( 105 | [ 106 | array([]), 107 | array([1]), 108 | array([1, 2]), 109 | ], 110 | [ 111 | [], 112 | [{"variance": 0}], 113 | [{"variance": 0}, {"variance": 1}], 114 | ], 115 | ), 116 | ) 117 | def test_default(self, values, metadata): 118 | result = EstimatorResult(values=values, metadata=metadata) 119 | enc = EstimatorResultEncoder() 120 | assert enc.default(result) == {"values": result.values, "metadata": result.metadata} 121 | 122 | def test_numpy_subclass(self): 123 | enc = EstimatorResultEncoder() 124 | assert isinstance(enc, NumPyEncoder) 125 | a = array([0, 1, 2]) 126 | assert enc.default(a) == a.tolist() 127 | 128 | def test_repr_subclass(self): 129 | enc = EstimatorResultEncoder() 130 | assert isinstance(enc, ReprEncoder) 131 | obj = {"call": "super"} 132 | assert enc.default(obj) == repr(obj) 133 | -------------------------------------------------------------------------------- /test/utils/test_typing.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from test import NO_INTS, NO_REAL 14 | 15 | from numpy import array 16 | from pytest import mark 17 | 18 | from zne.utils.typing import isint, isinteger, isreal, normalize_array 19 | 20 | 21 | ################################################################################ 22 | ## TYPE CHECKING 23 | ################################################################################ 24 | @mark.parametrize("object", [0, 1, -1]) 25 | def test_isint_true(object): 26 | """Test isint true.""" 27 | assert isint(object) 28 | 29 | 30 | @mark.parametrize("object", NO_INTS, ids=[str(type(i).__name__) for i in NO_INTS]) 31 | def test_isint_false(object): 32 | """Test isint false.""" 33 | assert not isint(object) 34 | 35 | 36 | @mark.parametrize("object", [0, 1, -1, 1.0, -1.0]) 37 | def test_isinteger_true(object): 38 | """Test isinteger true.""" 39 | assert isinteger(object) 40 | 41 | 42 | @mark.parametrize("object", [1.2, -2.4]) 43 | def test_isinteger_false(object): 44 | """Test isinteger false.""" 45 | assert not isinteger(object) 46 | 47 | 48 | @mark.parametrize("object", [0, 1, -1, 1.2, -2.4]) 49 | def test_isreal_true(object): 50 | """Test isreal true.""" 51 | assert isreal(object) 52 | 53 | 54 | @mark.parametrize("object", NO_REAL, ids=[str(type(i).__name__) for i in NO_REAL]) 55 | def test_isreal_false(object): 56 | """Test isreal false.""" 57 | assert not isreal(object) 58 | 59 | 60 | @mark.parametrize( 61 | "arr, expected", 62 | [ 63 | (1, 1), 64 | (1.0, 1.0), 65 | (1j, 1j), 66 | (None, None), 67 | (dict(), dict()), 68 | (set(), set()), 69 | (list(), tuple()), 70 | (tuple(), tuple()), 71 | ([1], (1,)), 72 | ((1,), (1,)), 73 | ([1, 2], (1, 2)), 74 | ((1, 2), (1, 2)), 75 | ([[1, 2]], ((1, 2),)), 76 | (((1, 2),), ((1, 2),)), 77 | ([[1], [2]], ((1,), (2,))), 78 | (([1], [2]), ((1,), (2,))), 79 | (((1,), (2,)), ((1,), (2,))), 80 | ([[[1]]], (((1,),),)), 81 | ([[[1, 2], [3, 4]]], (((1, 2), (3, 4)),)), 82 | ], 83 | ) 84 | def test_normalize_array(arr, expected): 85 | """Test normalize array.""" 86 | assert normalize_array(arr) == expected 87 | arr = array(arr) 88 | assert normalize_array(arr) == expected 89 | -------------------------------------------------------------------------------- /test/utils/test_unset.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | from copy import copy, deepcopy 14 | 15 | from zne.utils.unset import UNSET, UnsetType 16 | 17 | 18 | class TestUnsetType: 19 | """Test `UnsetType` class.""" 20 | 21 | def test_singleton(self): 22 | """Test that class is a singleton.""" 23 | assert UnsetType() is UnsetType() 24 | assert UNSET is UnsetType() 25 | 26 | def test_bool(self): 27 | """Test that instances evaluate to `False`.""" 28 | assert not UNSET 29 | assert not UnsetType() 30 | 31 | def test_eq(self): 32 | """Test equality.""" 33 | assert UnsetType() == UnsetType() 34 | assert UNSET == UnsetType() 35 | 36 | def test_repr(sefl): 37 | """Test string representation.""" 38 | assert str(UNSET) == "UNSET" 39 | assert repr(UNSET) == "UNSET" 40 | 41 | def test_copy(self): 42 | """Test copy.""" 43 | assert copy(UNSET) is UNSET 44 | 45 | def test_deepcopy(self): 46 | """Test deepcopy.""" 47 | assert deepcopy(UNSET, memo := {}) is UNSET 48 | assert not memo 49 | -------------------------------------------------------------------------------- /tools/extremal_dependency_versions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This code is part of Qiskit. 4 | # 5 | # (C) Copyright IBM 2023. 6 | # 7 | # This code is licensed under the Apache License, Version 2.0. You may 8 | # obtain a copy of this license in the LICENSE.txt file in the root directory 9 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 10 | # 11 | # Any modifications or derivative works of this code must retain this 12 | # copyright notice, and modified files need to carry a notice indicating 13 | # that they have been altered from the originals. 14 | 15 | import re 16 | import configparser 17 | 18 | import toml 19 | import fire 20 | 21 | 22 | def mapfunc_dev(dep): 23 | """Load the development version(s) of certain Qiskit-related packages""" 24 | # https://peps.python.org/pep-0440/#direct-references 25 | return re.sub( 26 | r"^(qiskit).*$", 27 | r"\1 @ git+https://github.com/Qiskit/\1.git", 28 | dep, 29 | ) 30 | 31 | 32 | def mapfunc_min(dep): 33 | """Set each dependency to its minimum version""" 34 | return re.sub(r"[>~]=", r"==", dep) 35 | 36 | 37 | def inplace_map(fun, lst: list): 38 | """In-place version of Python's `map` function""" 39 | for i, x in enumerate(lst): 40 | lst[i] = fun(x) 41 | 42 | 43 | def process_dependencies_in_place(d: dict, mapfunc): 44 | """Given a parsed `pyproject.toml`, process dependencies according to `mapfunc`""" 45 | proj = d["project"] 46 | 47 | try: 48 | deps = proj["dependencies"] 49 | except KeyError: 50 | pass # no dependencies; that's unusual, but fine. 51 | else: 52 | inplace_map(mapfunc, deps) 53 | 54 | try: 55 | opt_deps = proj["optional-dependencies"] 56 | except KeyError: 57 | pass # no optional dependencies; that's fine. 58 | else: 59 | for dependencies_list in opt_deps.values(): 60 | inplace_map(mapfunc, dependencies_list) 61 | 62 | try: 63 | build_system = d["build-system"] 64 | except KeyError: 65 | pass 66 | else: 67 | try: 68 | build_system_requires = build_system["requires"] 69 | except KeyError: 70 | pass 71 | else: 72 | inplace_map(mapfunc, build_system_requires) 73 | 74 | 75 | class CLI: 76 | """Command-line interface class for Fire""" 77 | 78 | def get_tox_minversion(self): 79 | """Extract tox minversion from `tox.ini`""" 80 | config = configparser.ConfigParser() 81 | config.read("tox.ini") 82 | print(config["tox"]["minversion"]) 83 | 84 | def pin_dependencies(self, strategy, inplace: bool = False): 85 | """Pin the dependencies in `pyproject.toml` according to `strategy`""" 86 | mapfunc = { 87 | "dev": mapfunc_dev, 88 | "min": mapfunc_min, 89 | }[strategy] 90 | 91 | with open("pyproject.toml") as f: 92 | d = toml.load(f) 93 | process_dependencies_in_place(d, mapfunc) 94 | 95 | if inplace: 96 | with open("pyproject.toml", "w") as f: 97 | toml.dump(d, f) 98 | else: 99 | print(toml.dumps(d)) 100 | 101 | 102 | if __name__ == "__main__": 103 | fire.Fire(CLI) 104 | -------------------------------------------------------------------------------- /tools/symlink_submodule.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ## _____ _____ 4 | ## | __ \| __ \ AUTHOR: Pedro Rivero 5 | ## | |__) | |__) | --------------------------------- 6 | ## | ___/| _ / DATE: Feb 21, 2023 7 | ## | | | | \ \ --------------------------------- 8 | ## |_| |_| \_\ https://github.com/pedrorrivero 9 | ## 10 | 11 | ## Copyright 2023 Pedro Rivero 12 | ## 13 | ## Licensed under the Apache License, Version 2.0 (the "License"); 14 | ## you may not use this file except in compliance with the License. 15 | ## You may obtain a copy of the License at 16 | ## 17 | ## http://www.apache.org/licenses/LICENSE-2.0 18 | ## 19 | ## Unless required by applicable law or agreed to in writing, software 20 | ## distributed under the License is distributed on an "AS IS" BASIS, 21 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 | ## See the License for the specific language governing permissions and 23 | ## limitations under the License. 24 | 25 | """Create symlinks to submodule files in base repo, adding all missing directories.""" 26 | 27 | from pathlib import Path 28 | 29 | 30 | EXCLUDE = {"README.md", ".git", ".gitignore"} 31 | 32 | 33 | def symlink_files(original: Path, host_dir: Path, relative: Path = None) -> None: 34 | """Recursively create symlinks(s) to file(s) in original path into host directory. 35 | 36 | Args: 37 | original: path to original file or directory. 38 | host_dir: directory to host the symlinks. 39 | relative: relative path to build the symlinks from. 40 | """ 41 | # Input standardization 42 | original = original.resolve() 43 | host_dir = host_dir.resolve() 44 | if not host_dir.is_dir(): 45 | host_dir = host_dir.parent 46 | if relative is None: 47 | relative = Path() 48 | else: 49 | relative = relative.resolve() 50 | 51 | # Logic 52 | host = host_dir.joinpath(original.name) 53 | if original.is_file(): 54 | original = original.relative_to(relative) 55 | host.symlink_to(original) 56 | elif original.is_dir(): 57 | host.mkdir(parents=True, exist_ok=True) 58 | for path in original.iterdir(): 59 | symlink_files(path, host, relative) 60 | 61 | 62 | if __name__ == "__main__": 63 | file = Path(__file__).resolve() 64 | tools = file.parent 65 | submodule = tools.parent 66 | repo = submodule.parent 67 | for file in submodule.iterdir(): 68 | if file.name not in EXCLUDE: 69 | symlink_files(file, repo, repo) 70 | -------------------------------------------------------------------------------- /tools/travis_before_script.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | if [ -z "${STRATEGY:-}" ]; then 6 | # There's nothing to do 7 | exit 0 8 | fi 9 | 10 | if [ "$STRATEGY" == "min" ]; then 11 | # Install the minversion of tox as specified in tox.ini 12 | pip install "tox==$(./tools/extremal_dependency_versions.py get_tox_minversion)" 13 | fi 14 | 15 | if [ "$STRATEGY" == "dev" ]; then 16 | # Install Rust, which we'll need to build Qiskit 17 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 18 | fi 19 | 20 | # Update pyproject.toml with the pinned dependencies 21 | ./tools/extremal_dependency_versions.py pin_dependencies "$STRATEGY" --inplace 22 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 4.4.0 3 | requires = 4 | tox-ignore-env-name-mismatch ~= 0.2.0 5 | envlist = style, lint, coverage 6 | isolated_build = true 7 | # CI: skip-next-line 8 | usedevelop = true 9 | # CI: skip-next-line 10 | skip_missing_interpreters = true 11 | 12 | 13 | [testenv] 14 | description = {envname} tests 15 | runner = ignore_env_name_mismatch 16 | install_command = pip install -U {opts} {packages} 17 | setenv = 18 | VIRTUAL_ENV={envdir} 19 | LANGUAGE=en_US 20 | LC_ALL=en_US.utf-8 21 | extras = 22 | test 23 | commands = 24 | pip check 25 | pytest --no-cov 26 | 27 | 28 | ################################################################################ 29 | ## SOURCE 30 | ################################################################################ 31 | [testenv:coverage] 32 | description = coverage tests 33 | envdir = {toxworkdir}/py312 34 | basepython = python3.12 35 | setenv = 36 | {[testenv]setenv} 37 | commands = 38 | pytest --cov zne 39 | 40 | 41 | [testenv:lint] 42 | description = lint checks 43 | envdir = {toxworkdir}/lint 44 | basepython = python3.12 45 | extras = 46 | lint 47 | commands = 48 | flake8 zne test docs 49 | isort zne test docs --check-only 50 | black zne test docs --check 51 | pylint -rn zne 52 | mypy -p zne 53 | 54 | 55 | [testenv:style] 56 | description = style formatter 57 | envdir = {toxworkdir}/lint 58 | basepython = python3.12 59 | extras = 60 | lint 61 | commands = 62 | autoflake -i --remove-all-unused-imports -r zne test docs 63 | isort zne test docs 64 | black zne test docs 65 | 66 | 67 | ################################################################################ 68 | ## DOCS 69 | ################################################################################ 70 | [testenv:docs] 71 | description = build sphinx documentation 72 | basepython = python3 73 | extras = 74 | docs 75 | skip_install = false 76 | commands = 77 | sphinx-build -b html -W {posargs} docs/ docs/_build/html 78 | 79 | 80 | ################################################################################ 81 | ## NOTEBOOK 82 | ################################################################################ 83 | [testenv:py3{8,9,10,11,12}-notebook] 84 | extras = 85 | notebook 86 | lint 87 | commands = 88 | nbqa flake8 docs --ignore=E203,E266,E402 89 | nbqa isort docs --check-only 90 | nbqa black docs --check 91 | ; nbqa pylint -rn docs 92 | nbqa mypy docs 93 | treon docs --threads 2 94 | 95 | 96 | [testenv:style-notebook] 97 | description = jupyter notebook style formatter 98 | envdir = {toxworkdir}/notebook 99 | basepython = python3 100 | extras = 101 | notebook 102 | lint 103 | commands = 104 | nbqa autoflake -i --remove-all-unused-imports -r docs 105 | nbqa isort docs 106 | nbqa black docs 107 | -------------------------------------------------------------------------------- /zne/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Zero Noise Extrapolation (ZNE) prototype for error mitigation on 14 | Qiskit's Estimator primitive.""" 15 | 16 | from .extrapolation import EXTRAPOLATOR_LIBRARY 17 | from .meta import ZNEJob, zne 18 | from .noise_amplification import NOISE_AMPLIFIER_LIBRARY 19 | from .zne_strategy import ZNEStrategy 20 | 21 | __copyright__ = "(C) Copyright IBM 2022" 22 | __version__ = "1.3.1" 23 | 24 | 25 | __all__ = [ 26 | "__copyright__", 27 | "__version__", 28 | "zne", 29 | "ZNEJob", 30 | "ZNEStrategy", 31 | "EXTRAPOLATOR_LIBRARY", 32 | "NOISE_AMPLIFIER_LIBRARY", 33 | ] 34 | -------------------------------------------------------------------------------- /zne/extrapolation/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Extrapolation library.""" 14 | 15 | from .exponential_extrapolator import ( 16 | BiExponentialExtrapolator, 17 | ExponentialExtrapolator, 18 | MonoExponentialExtrapolator, 19 | MultiExponentialExtrapolator, 20 | ) 21 | from .extrapolator import Extrapolator, OLSExtrapolator, ReckoningResult 22 | from .polynomial_extrapolator import ( 23 | CubicExtrapolator, 24 | LinearExtrapolator, 25 | PolynomialExtrapolator, 26 | QuadraticExtrapolator, 27 | QuarticExtrapolator, 28 | ) 29 | 30 | EXTRAPOLATOR_LIBRARY = { 31 | cls.__name__: cls 32 | for cls in ( 33 | PolynomialExtrapolator, 34 | LinearExtrapolator, 35 | QuadraticExtrapolator, 36 | CubicExtrapolator, 37 | QuarticExtrapolator, 38 | MultiExponentialExtrapolator, 39 | ExponentialExtrapolator, 40 | MonoExponentialExtrapolator, 41 | BiExponentialExtrapolator, 42 | ) 43 | } 44 | 45 | __all__ = [ 46 | "Extrapolator", 47 | "OLSExtrapolator", 48 | "ReckoningResult", 49 | *EXTRAPOLATOR_LIBRARY.keys(), 50 | ] 51 | -------------------------------------------------------------------------------- /zne/extrapolation/polynomial_extrapolator.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Polynomial extrapolator.""" 14 | 15 | from __future__ import annotations 16 | 17 | from collections import namedtuple 18 | 19 | from numpy import array, ndarray, sqrt, zeros 20 | from scipy.optimize import curve_fit 21 | 22 | from .extrapolator import OLSExtrapolator, ReckoningResult 23 | 24 | ################################################################################ 25 | ## GENERAL 26 | ################################################################################ 27 | _RegressionData = namedtuple("_RegressionData", ("x_data", "y_data", "sigma_x", "sigma_y")) 28 | 29 | 30 | class PolynomialExtrapolator(OLSExtrapolator): 31 | """Polynomial ordinary-least-squares (OLS) extrapolator. 32 | 33 | Args: 34 | degree: The degree of the polynomial regression curve. 35 | """ 36 | 37 | def __init__(self, degree: int = 1): # pylint: disable=super-init-not-called 38 | self._set_degree(degree) 39 | 40 | ################################################################################ 41 | ## PROPERTIES 42 | ################################################################################ 43 | @property 44 | def degree(self) -> int: 45 | """The degree of the regression polynomial.""" 46 | return self._degree 47 | 48 | def _set_degree(self, degree: int) -> None: 49 | """Degree setter.""" 50 | degree = int(degree) 51 | if degree < 1: 52 | raise ValueError("Polynomial degree must be at least 1.") 53 | self._degree: int = degree 54 | 55 | ################################################################################ 56 | ## IMPLEMENTATION 57 | ################################################################################ 58 | @property 59 | def min_points(self) -> int: 60 | return self.degree + 1 61 | 62 | # pylint: disable=duplicate-code 63 | def _extrapolate_zero( 64 | self, 65 | x_data: tuple[float, ...], 66 | y_data: tuple[float, ...], 67 | sigma_x: tuple[float, ...], 68 | sigma_y: tuple[float, ...], 69 | ) -> ReckoningResult: 70 | # TODO: if curve fit fails (e.g. p-value test) warn and return closest to zero 71 | regression_data = _RegressionData(x_data, y_data, sigma_x, sigma_y) 72 | return self._infer(0, regression_data) 73 | 74 | def _model(self, x, *coefficients) -> ndarray: # pylint: disable=invalid-name 75 | """Polynomial regression model for curve fitting.""" 76 | x = array(x) 77 | return sum(c * (x**i) for i, c in enumerate(coefficients)) 78 | 79 | ################################################################################ 80 | ## AUXILIARY 81 | ################################################################################ 82 | def _infer(self, target: float, regression_data: _RegressionData) -> ReckoningResult: 83 | """Fit regression model from data and infer evaluation for target value. 84 | 85 | Args: 86 | target: The target X value to infer a Y value for. 87 | regression_data: A four-tuple of tuples representing X-data, Y-data, 88 | and corresponding std errors for the X and Y data respectively. 89 | 90 | Returns: 91 | Reckoning result holding the inferred value, std error, and metadata about 92 | the curve fit procedure. 93 | """ 94 | coefficients, covariance_matrix = curve_fit( 95 | self._model, 96 | regression_data.x_data, 97 | regression_data.y_data, 98 | sigma=self._compute_sigma(regression_data.y_data, regression_data.sigma_y), 99 | absolute_sigma=True, 100 | p0=zeros(self.degree + 1), # Note: Initial point determines number of d.o.f. 101 | ) 102 | target_powers = array([target**p for p in range(self.degree + 1)]) 103 | value = target_powers @ coefficients # Note: == self._model(target, *coefficients) 104 | variance = target_powers @ covariance_matrix @ target_powers 105 | std_error = sqrt(variance) 106 | metadata = self._build_metadata( 107 | array(regression_data.x_data), 108 | array(regression_data.y_data), 109 | coefficients, 110 | covariance_matrix, 111 | ) 112 | return ReckoningResult(value.tolist(), std_error.tolist(), metadata) 113 | 114 | 115 | ################################################################################ 116 | ## FACADES 117 | ################################################################################ 118 | class LinearExtrapolator(PolynomialExtrapolator): 119 | """Linear ordinary-least-squares (OLS) extrapolator.""" 120 | 121 | def __init__(self): 122 | super().__init__(degree=1) 123 | 124 | 125 | class QuadraticExtrapolator(PolynomialExtrapolator): 126 | """Quadratic ordinary-least-squares (OLS) extrapolator.""" 127 | 128 | def __init__(self): 129 | super().__init__(degree=2) 130 | 131 | 132 | class CubicExtrapolator(PolynomialExtrapolator): 133 | """Cubic ordinary-least-squares (OLS) extrapolator.""" 134 | 135 | def __init__(self): 136 | super().__init__(degree=3) 137 | 138 | 139 | class QuarticExtrapolator(PolynomialExtrapolator): 140 | """Quartic ordinary-least-squares (OLS) extrapolator.""" 141 | 142 | def __init__(self): 143 | super().__init__(degree=4) 144 | -------------------------------------------------------------------------------- /zne/immutable_strategy.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ZNE strategy class to provide common base functionality.""" 14 | 15 | from __future__ import annotations 16 | 17 | from inspect import signature 18 | from typing import Any 19 | 20 | 21 | class ImmutableStrategy: # Note: pending deprecation # pragma: no cover 22 | """Immutable strategy class to provide common base functionality. 23 | 24 | Note that all :class:`ImmutableStrategy` instance objects and their attributes are immutable by 25 | construction. If another strategy with different options needs to be utilized, a new instance 26 | of the class should be created. 27 | """ 28 | 29 | def __new__(cls, *args, **kwargs) -> ImmutableStrategy: 30 | """ZNE strategy constructor.""" 31 | self = super().__new__(cls) 32 | self.__dict__.update({"_init_options": {}}) 33 | self._save_init_options(*args, **kwargs) 34 | return self 35 | 36 | def _save_init_options(self, *args, **kwargs) -> None: 37 | """Saves strategy init options as a dict.""" 38 | init_signature = signature(self.__init__) # type: ignore 39 | bound_signature = init_signature.bind(*args, **kwargs) 40 | bound_signature.apply_defaults() 41 | bound_args = bound_signature.arguments 42 | self._init_options: dict = dict(sorted(bound_args.items())) 43 | 44 | def __init__(self) -> None: 45 | """Base init method with no arguments, can be overriden.""" 46 | 47 | @property 48 | def name(self) -> str: 49 | """Strategy name.""" 50 | return type(self).__name__ 51 | 52 | @property 53 | def options(self) -> dict: 54 | """Strategy options.""" 55 | return self._init_options 56 | 57 | def __repr__(self) -> str: 58 | return f"<{self.name}:{self.options}>" if self.options else self.name 59 | 60 | def __eq__(self, other: Any) -> bool: 61 | return repr(self) == repr(other) 62 | -------------------------------------------------------------------------------- /zne/meta/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ZNE meta programming capabilities. 14 | 15 | Enables injecting error mitigation functionility to classes implementing the 16 | :class:`qiskit.primitives.BaseEstimator` interface. 17 | """ 18 | 19 | from zne.meta.cls import zne 20 | from zne.meta.job import ZNEJob 21 | 22 | __all__ = [ 23 | "zne", 24 | "ZNEJob", 25 | ] 26 | -------------------------------------------------------------------------------- /zne/meta/call.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ZNE functionality for :method:`qiskit.primitives.BaseEstimator.__call__` method.""" 14 | from __future__ import annotations 15 | 16 | from collections.abc import Callable, Sequence 17 | from functools import wraps 18 | 19 | from numpy import ndarray 20 | from qiskit.circuit import QuantumCircuit 21 | from qiskit.primitives import EstimatorResult 22 | from qiskit.quantum_info.operators import SparsePauliOp 23 | from qiskit.utils.deprecation import deprecate_arguments 24 | 25 | Sequence.register(ndarray) 26 | 27 | 28 | ################################################################################ 29 | ## DECORATOR 30 | ################################################################################ 31 | def zne_call(call: Callable) -> Callable: 32 | """Add ZNE functionality to :method:`qiskit.primitives.BaseEstimator.__call__`.""" 33 | 34 | if not callable(call): 35 | raise TypeError("Invalid `call` argument, expected callable.") 36 | 37 | @wraps(call) 38 | @deprecate_arguments({"circuit_indices": "circuits", "observable_indices": "observables"}) 39 | def _zne_call( 40 | self, 41 | circuits: Sequence[int | QuantumCircuit], 42 | observables: Sequence[int | SparsePauliOp], 43 | parameter_values: Sequence[Sequence[float]] | None = None, 44 | **run_options, 45 | ) -> EstimatorResult: 46 | raise TypeError( 47 | "The BaseEstimator.__call__ method is deprecated as of Qiskit Terra 0.22.0. " 48 | "Use the 'run' method instead.", 49 | ) 50 | 51 | return _zne_call 52 | -------------------------------------------------------------------------------- /zne/meta/cls.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ZNE meta programming capabilities. 14 | 15 | Enables injecting error mitigation functionality to classes implementing the 16 | :class:`qiskit.primitives.BaseEstimator` interface. 17 | """ 18 | 19 | from __future__ import annotations 20 | 21 | from qiskit.primitives import BaseEstimator 22 | 23 | from zne.meta.call import zne_call 24 | from zne.meta.init import zne_init 25 | from zne.meta.run import zne_run 26 | from zne.zne_strategy import ZNEStrategy 27 | 28 | 29 | def zne(cls: type) -> type: # TODO: integration tests 30 | """Add ZNE functionality to input class. 31 | 32 | Args: 33 | cls: class implementing the :class:`qiskit.primitives.BaseEstimator` interface. 34 | 35 | Returns: 36 | A subclass of the input class extended with ZNE functionality. 37 | """ 38 | if not isinstance(cls, type) or not issubclass(cls, BaseEstimator): 39 | raise TypeError("Invalid class, does not implement the BaseEstimator interface.") 40 | namespace = { 41 | "__init__": zne_init(cls.__init__), # type: ignore # TODO: update docstring 42 | "__call__": zne_call(cls.__call__), # TODO: deprecate 43 | "_run": zne_run(cls._run), # type: ignore # pylint: disable=protected-access 44 | "zne_strategy": property(_get_zne_strategy, _set_zne_strategy), 45 | } 46 | return type(f"ZNE{cls.__name__}", (cls,), namespace) 47 | 48 | 49 | def _get_zne_strategy(self) -> ZNEStrategy: 50 | """ZNE strategy for error mitigation.""" 51 | try: 52 | return self._zne_strategy # pylint: disable=protected-access 53 | except AttributeError: 54 | self.zne_strategy = None 55 | return self.zne_strategy 56 | 57 | 58 | def _set_zne_strategy(self, zne_strategy: ZNEStrategy | None) -> None: 59 | if zne_strategy is None: 60 | zne_strategy = ZNEStrategy.noop() 61 | elif not isinstance(zne_strategy, ZNEStrategy): 62 | raise TypeError("Invalid zne_strategy object, expected ZNEStrategy.") 63 | self._zne_strategy = zne_strategy # pylint: disable=protected-access 64 | -------------------------------------------------------------------------------- /zne/meta/init.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ZNE functionality for :method:`qiskit.primitives.BaseEstimator.__init__` method.""" 14 | from __future__ import annotations 15 | 16 | from collections.abc import Callable, Iterable, Sequence 17 | from functools import wraps 18 | 19 | from numpy import ndarray 20 | from qiskit.circuit import Parameter, QuantumCircuit 21 | from qiskit.quantum_info.operators import SparsePauliOp 22 | 23 | from ..zne_strategy import ZNEStrategy 24 | 25 | Sequence.register(ndarray) 26 | 27 | 28 | def zne_init(init: Callable) -> Callable: 29 | """Add ZNE functionality to :method:`qiskit.primitives.BaseEstimator.__init__`.""" 30 | 31 | if not callable(init): 32 | raise TypeError("Invalid `init` argument, expected callable.") 33 | 34 | @wraps(init) 35 | def _zne_init( # pylint: disable=too-many-arguments 36 | self, 37 | circuits: Iterable[QuantumCircuit] | QuantumCircuit | None = None, 38 | observables: Iterable[SparsePauliOp] | SparsePauliOp | None = None, 39 | parameters: Iterable[Iterable[Parameter]] | None = None, 40 | options: dict | None = None, 41 | zne_strategy: ZNEStrategy | None = None, 42 | **kwargs, 43 | ) -> None: 44 | if circuits is not None or observables is not None or parameters is not None: 45 | raise TypeError( 46 | "The BaseEstimator `circuits`, `observables`, `parameters` kwarg are " 47 | "deprecated as of Qiskit Terra 0.22.0. Use the 'run' method instead.", 48 | ) 49 | self.zne_strategy = zne_strategy 50 | return init(self, options=options, **kwargs) 51 | 52 | return _zne_init 53 | -------------------------------------------------------------------------------- /zne/meta/job.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Estimator Job with embeded ZNE parsing.""" 14 | 15 | from __future__ import annotations 16 | 17 | from typing import Any 18 | 19 | from qiskit.primitives import EstimatorResult 20 | from qiskit.providers import JobV1 as Job 21 | 22 | from ..zne_strategy import ZNEStrategy 23 | 24 | 25 | class ZNEJob(Job): 26 | """Estimator Job with embeded ZNE parsing.""" 27 | 28 | def __init__( # pylint: disable=super-init-not-called 29 | self, 30 | base_job: Job, 31 | zne_strategy: ZNEStrategy, 32 | target_num_experiments: int, 33 | ) -> None: 34 | self._base_job: Job = base_job 35 | self._zne_strategy: ZNEStrategy = zne_strategy 36 | self._target_num_experiments: int = target_num_experiments 37 | 38 | ################################################################################ 39 | ## PROPERTIES 40 | ################################################################################ 41 | @property 42 | def base_job(self) -> Job: 43 | """Base Job for noisy results.""" 44 | return self._base_job 45 | 46 | @property 47 | def zne_strategy(self) -> ZNEStrategy: 48 | """ZNE strategy object.""" 49 | return self._zne_strategy 50 | 51 | @property 52 | def target_num_experiments(self) -> int: 53 | """Expected number of results after ZNE is performed.""" 54 | return self._target_num_experiments 55 | 56 | ################################################################################ 57 | ## ZNE 58 | ################################################################################ 59 | def result(self) -> EstimatorResult: 60 | """Return the results of the job.""" 61 | result: EstimatorResult = self.base_job.result() 62 | if self.zne_strategy.performs_zne: 63 | result = self.zne_strategy.mitigate_noisy_result(result) 64 | if len(result.values) != self.target_num_experiments: 65 | # TODO: consider warning instead -> should be in integration tests 66 | raise RuntimeError( 67 | "Number of experiments in EstimatorResult object does not match " 68 | "the requested number of experiments." 69 | ) 70 | return result 71 | 72 | ################################################################################ 73 | ## NESTED JOB DELEGATION 74 | ################################################################################ 75 | _ZNE_ATTRIBUTES: set = { 76 | "base_job", 77 | "_base_job", 78 | "zne_strategy", 79 | "_zne_strategy", 80 | "target_num_experiments", 81 | "_target_num_experiments", 82 | "result", 83 | } 84 | 85 | def __getattribute__(self, name: str) -> Any: 86 | if name == "_ZNE_ATTRIBUTES" or name in self._ZNE_ATTRIBUTES: 87 | return super().__getattribute__(name) 88 | return getattr(self.base_job, name) 89 | 90 | def __setattr__(self, name: str, value: Any) -> None: 91 | if name in self._ZNE_ATTRIBUTES: 92 | return super().__setattr__(name, value) 93 | return setattr(self.base_job, name, value) 94 | 95 | def __delattr__(self, name: str) -> None: 96 | if name in self._ZNE_ATTRIBUTES: 97 | return super().__delattr__(name) 98 | return delattr(self.base_job, name) 99 | 100 | ################################################################################ 101 | ## DUMMY ABSTRACT METHOD IMPLEMENTATION 102 | ################################################################################ 103 | def submit(self): 104 | """Submit the job to the backend for execution.""" 105 | return self.base_job.submit() 106 | 107 | def status(self): 108 | """Return the status of the job, among the values of ``JobStatus``.""" 109 | return self.base_job.status() 110 | -------------------------------------------------------------------------------- /zne/meta/run.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ZNE functionality for :method:`qiskit.primitives.BaseEstimator._run` method.""" 14 | 15 | from __future__ import annotations 16 | 17 | from collections.abc import Callable 18 | from functools import wraps 19 | 20 | from qiskit.circuit import QuantumCircuit 21 | from qiskit.providers import JobV1 as Job 22 | from qiskit.quantum_info.operators.base_operator import BaseOperator 23 | 24 | from ..zne_strategy import ZNEStrategy 25 | from .job import ZNEJob 26 | 27 | 28 | def zne_run(run: Callable) -> Callable: 29 | """Add ZNE functionality to :method:`qiskit.primitives.BaseEstimator._run`.""" 30 | 31 | if not callable(run): 32 | raise TypeError("Invalid `run` argument, expected callable.") 33 | 34 | @wraps(run) 35 | def _zne_run( 36 | self, 37 | circuits: tuple[QuantumCircuit, ...], 38 | observables: tuple[BaseOperator, ...], 39 | parameter_values: tuple[tuple[float, ...], ...], 40 | zne_strategy: ZNEStrategy | None = ..., # type: ignore 41 | **run_options, 42 | ) -> Job: 43 | # Strategy 44 | if zne_strategy is Ellipsis: 45 | zne_strategy = self.zne_strategy 46 | elif zne_strategy is None: 47 | zne_strategy = ZNEStrategy.noop() 48 | elif not isinstance(zne_strategy, ZNEStrategy): 49 | raise TypeError("Invalid zne_strategy object, expected ZNEStrategy.") 50 | 51 | # ZNE 52 | target_num_experiments: int = len(circuits) 53 | if zne_strategy.performs_noise_amplification: 54 | circuits = zne_strategy.build_noisy_circuits(circuits) 55 | observables = zne_strategy.map_to_noisy_circuits(observables) 56 | parameter_values = zne_strategy.map_to_noisy_circuits(parameter_values) 57 | 58 | # Job 59 | job: Job = run( 60 | self, 61 | circuits=circuits, 62 | observables=observables, 63 | parameter_values=parameter_values, 64 | **run_options, 65 | ) 66 | return ZNEJob(job, zne_strategy, target_num_experiments) 67 | 68 | return _zne_run 69 | -------------------------------------------------------------------------------- /zne/noise_amplification/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Noise amplification library.""" 14 | 15 | from .folding_amplifier import ( 16 | CxAmplifier, 17 | GlobalFoldingAmplifier, 18 | LocalFoldingAmplifier, 19 | MultiQubitAmplifier, 20 | TwoQubitAmplifier, 21 | ) 22 | from .noise_amplifier import CircuitNoiseAmplifier, DAGNoiseAmplifier, NoiseAmplifier 23 | 24 | NOISE_AMPLIFIER_LIBRARY = { 25 | cls.__name__: cls 26 | for cls in ( 27 | GlobalFoldingAmplifier, 28 | LocalFoldingAmplifier, 29 | CxAmplifier, 30 | TwoQubitAmplifier, 31 | MultiQubitAmplifier, 32 | ) 33 | } 34 | 35 | __all__ = [ 36 | "NoiseAmplifier", 37 | "CircuitNoiseAmplifier", 38 | "DAGNoiseAmplifier", 39 | *NOISE_AMPLIFIER_LIBRARY.keys(), 40 | ] 41 | -------------------------------------------------------------------------------- /zne/noise_amplification/folding_amplifier/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Noise amplification strategy via unitary/inverse repetition.""" 14 | 15 | from .global_folding_amplifier import GlobalFoldingAmplifier 16 | from .local_folding_amplifier import ( 17 | CxAmplifier, 18 | LocalFoldingAmplifier, 19 | MultiQubitAmplifier, 20 | TwoQubitAmplifier, 21 | ) 22 | 23 | __all__ = [ 24 | "GlobalFoldingAmplifier", 25 | "LocalFoldingAmplifier", 26 | "CxAmplifier", 27 | "TwoQubitAmplifier", 28 | "MultiQubitAmplifier", 29 | ] 30 | -------------------------------------------------------------------------------- /zne/noise_amplification/folding_amplifier/global_folding_amplifier.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Noise amplification strategy via circuit/circuit-inverse repetition.""" 14 | 15 | 16 | from qiskit.circuit import QuantumCircuit 17 | 18 | from .folding_amplifier import FoldingAmplifier 19 | 20 | 21 | class GlobalFoldingAmplifier(FoldingAmplifier): 22 | """Alternatingly composes the circuit and its inverse as many times as indicated by the 23 | ``noise_factor``. 24 | 25 | If ``noise_factor`` is not an odd integer, a sub part of the full circle is folded such that the 26 | resultant noise scaling :math:`\\lambda` equals :math:`\\lambda=(2n+1)+2s/d`, where :math:`d` 27 | are the number of layers of the original circuit, :math:`n` is the number of global circuit 28 | foldings, and :math:`s` is the number of gates that are sub-folded [1]. 29 | 30 | The gates of the full circuit that are used for the sub-folding can be the first :math:`s` 31 | gates, the last :math:`s` gates or a random selection of :math:`s` gates. This is specified by 32 | ``sub_folding_option``. 33 | 34 | Note that all instance objects and their attributes are immutable by construction. If another 35 | strategy with different options needs to be utilized, a new instance of the class should be 36 | created. 37 | 38 | References: 39 | [1] T. Giurgica-Tiron et al. (2020). 40 | Digital zero noise extrapolation for quantum error mitigation. 41 | `` 42 | """ 43 | 44 | def amplify_circuit_noise(self, circuit: QuantumCircuit, noise_factor: float) -> QuantumCircuit: 45 | self._validate_noise_factor(noise_factor) 46 | num_full_foldings, num_sub_foldings = self._compute_folding_nums(noise_factor, len(circuit)) 47 | noisy_circuit = circuit.copy_empty_like() 48 | noisy_circuit = self._apply_full_folding(noisy_circuit, circuit, num_full_foldings) 49 | noisy_circuit = self._apply_sub_folding(noisy_circuit, circuit, num_sub_foldings) 50 | return noisy_circuit 51 | 52 | def _apply_full_folding( 53 | self, noisy_circuit: QuantumCircuit, original_circuit: QuantumCircuit, num_foldings: int 54 | ) -> QuantumCircuit: 55 | """Fully folds the original circuit a number of ``num_foldings`` times. 56 | 57 | Args: 58 | noisy_circuit: The noise amplified circuit to which the gates are added. 59 | original_circuit: The original circuit without foldings. 60 | num_foldings: Number of times the circuit should be folded. 61 | 62 | Returns: 63 | The noise amplified circuit. 64 | """ 65 | noise_factor: int = self.folding_to_noise_factor(num_foldings) # type: ignore 66 | for i in range(noise_factor): 67 | if i % 2 == 0: 68 | noisy_circuit.compose(original_circuit, inplace=True) 69 | else: 70 | noisy_circuit.compose(original_circuit.inverse(), inplace=True) 71 | noisy_circuit = self._apply_barrier(noisy_circuit) 72 | return noisy_circuit 73 | 74 | def _apply_sub_folding( 75 | self, noisy_circuit: QuantumCircuit, original_circuit: QuantumCircuit, num_foldings: int 76 | ) -> QuantumCircuit: 77 | """Folds a subset of gates of the original circuit. 78 | 79 | Args: 80 | noisy_circuit: The noise amplified circuit to which the gates are added. 81 | original_circuit: The original circuit without foldings. 82 | num_foldings: Number of gates to be folded. 83 | 84 | Returns: 85 | The noise amplified circuit. 86 | """ 87 | if num_foldings == 0: 88 | return noisy_circuit 89 | sub_circuit: QuantumCircuit = self._get_sub_folding(original_circuit, num_foldings) 90 | noisy_circuit.compose(sub_circuit.inverse(), inplace=True) 91 | noisy_circuit.compose(sub_circuit, inplace=True) 92 | return noisy_circuit 93 | 94 | def _get_sub_folding(self, circuit: QuantumCircuit, size: int) -> QuantumCircuit: 95 | """Returns sub circuit to be folded. 96 | 97 | Args: 98 | circuit: The original circuit without foldings. 99 | size: Number of gates to be sub-folded. 100 | 101 | Returns: 102 | The sub circuit. 103 | """ 104 | sub_circuit = circuit.copy_empty_like() 105 | if self._sub_folding_option == "from_first": 106 | sub_data = circuit.data[:size] 107 | elif self._sub_folding_option == "from_last": 108 | sub_data = circuit.data[-size:] 109 | else: 110 | instruction_idxs = sorted(self._rng.choice(len(circuit), size=size, replace=False)) 111 | sub_data = [circuit.data[i] for i in instruction_idxs] 112 | for instruction, qargs, cargs in sub_data: 113 | # sub_circuit.barrier(qargs) # TODO: avoid sub-folded gates simplification 114 | sub_circuit.append(instruction, qargs, cargs) 115 | return sub_circuit 116 | -------------------------------------------------------------------------------- /zne/noise_amplification/noise_amplifier.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Interface for noise amplification strategies.""" 14 | 15 | from abc import ABC, abstractmethod 16 | 17 | from qiskit.circuit import QuantumCircuit 18 | from qiskit.converters import circuit_to_dag, dag_to_circuit 19 | from qiskit.dagcircuit import DAGCircuit 20 | from qiskit.transpiler import PassManager, TransformationPass 21 | 22 | from ..immutable_strategy import ImmutableStrategy 23 | 24 | 25 | class NoiseAmplifier(ImmutableStrategy, ABC): 26 | """Interface for noise amplification strategies.""" 27 | 28 | @abstractmethod 29 | def amplify_circuit_noise( 30 | self, circuit: QuantumCircuit, noise_factor: float 31 | ) -> QuantumCircuit: # pragma: no cover 32 | """Noise amplification strategy over :class:`~qiskit.circuit.QuantumCircuit`. 33 | 34 | Args: 35 | circuit: The original quantum circuit. 36 | noise_factor: The noise amplification factor by which to amplify the circuit noise. 37 | 38 | Returns: 39 | The noise amplified quantum circuit 40 | """ 41 | dag: DAGCircuit = circuit_to_dag(circuit) 42 | dag = self.amplify_dag_noise(dag, noise_factor) 43 | return dag_to_circuit(dag) 44 | 45 | @abstractmethod 46 | def amplify_dag_noise( 47 | self, dag: DAGCircuit, noise_factor: float 48 | ) -> DAGCircuit: # pragma: no cover 49 | """Noise amplification strategy over :class:`~qiskit.dagcircuit.DAGCircuit`. 50 | 51 | Args: 52 | dag: The original dag circuit. 53 | noise_factor: The noise amplification factor by which to amplify the circuit noise. 54 | 55 | Returns: 56 | The noise amplified dag circuit 57 | """ 58 | circuit: QuantumCircuit = dag_to_circuit(dag) 59 | circuit = self.amplify_circuit_noise(circuit, noise_factor) 60 | return circuit_to_dag(circuit) 61 | 62 | def build_transpiler_pass(self, noise_factor: float) -> TransformationPass: 63 | """Builds transpiler pass to perform noise amplification as specified in the strategy. 64 | 65 | Args: 66 | noise_factor: The noise factor used to amplify circuit noise. 67 | 68 | Returns: 69 | NoiseAmplificationPass: Instance of TransformationPass. 70 | """ 71 | 72 | class NoiseAmplificationPass(TransformationPass): 73 | """Noise amplification transpiler pass.""" 74 | 75 | def __init__(self, noise_amplifier: NoiseAmplifier) -> None: 76 | super().__init__() 77 | self._noise_amplifier: NoiseAmplifier = noise_amplifier 78 | 79 | @property 80 | def noise_amplifier(self) -> NoiseAmplifier: 81 | """Underlying noise amplifier.""" 82 | return self._noise_amplifier 83 | 84 | def run(self, dag: DAGCircuit) -> DAGCircuit: 85 | """Run a pass on the DAGCircuit.""" 86 | return self.noise_amplifier.amplify_dag_noise(dag, noise_factor) 87 | 88 | return NoiseAmplificationPass(noise_amplifier=self) 89 | 90 | def build_pass_manager(self, noise_factor: float) -> PassManager: 91 | """Builds a pass manager holding a single transpiler pass for noise amplification. 92 | 93 | Args: 94 | noise_factor: The noise factor used to amplify circuit noise. 95 | 96 | Returns: 97 | PassManager: Wrapper for :class:`NoiseAmplificationPass`. 98 | """ 99 | transpiler_pass: TransformationPass = self.build_transpiler_pass(noise_factor) 100 | return PassManager(transpiler_pass) 101 | 102 | 103 | # TODO: deprecate 104 | class CircuitNoiseAmplifier(NoiseAmplifier): # pragma: no cover 105 | """Interface for noise amplification strategies over :class:`~qiskit.dagcircuit.DAGCircuit`.""" 106 | 107 | # pylint: disable=useless-parent-delegation 108 | def amplify_dag_noise(self, dag: DAGCircuit, noise_factor: float) -> DAGCircuit: 109 | return super().amplify_dag_noise(dag, noise_factor) 110 | 111 | 112 | # TODO: deprecate 113 | class DAGNoiseAmplifier(NoiseAmplifier): # pragma: no cover 114 | """Interface for noise amplification strategies over :class:`~qiskit.dagcircuit.DAGCircuit`.""" 115 | 116 | # pylint: disable=useless-parent-delegation 117 | def amplify_circuit_noise(self, circuit: QuantumCircuit, noise_factor: float) -> QuantumCircuit: 118 | return super().amplify_circuit_noise(circuit, noise_factor) 119 | -------------------------------------------------------------------------------- /zne/qiskit/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Qiskit mock for upgraded functionality.""" 14 | -------------------------------------------------------------------------------- /zne/qiskit/primitives/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Qiskit mock for upgraded functionality.""" 14 | -------------------------------------------------------------------------------- /zne/types.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Type definitions for the Zero Noise Extrapolation (ZNE) Estimator class.""" 14 | 15 | from typing import Any, Dict, Sequence, Tuple, Union 16 | 17 | from qiskit import QuantumCircuit 18 | 19 | Metadata = Dict[str, Any] 20 | EstimatorResultData = Dict[str, Union[float, Metadata]] 21 | ParameterVector = Sequence[float] # TODO: qiskit.circuit::ParameterVector 22 | CircuitKey = tuple 23 | NoiseFactor = float 24 | ZNECacheKey = Tuple[CircuitKey, NoiseFactor] 25 | ZNECache = Dict[ZNECacheKey, QuantumCircuit] 26 | -------------------------------------------------------------------------------- /zne/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Generic utils library.""" 14 | 15 | from .standard_gates import STANDARD_GATES 16 | 17 | __all__ = [ 18 | "STANDARD_GATES", 19 | ] 20 | -------------------------------------------------------------------------------- /zne/utils/classconstant.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Class constant descriptor.""" 14 | 15 | from __future__ import annotations 16 | 17 | from copy import deepcopy 18 | from typing import Any 19 | 20 | 21 | class classconstant: # pylint: disable=invalid-name 22 | 23 | """Class constant descriptor. 24 | 25 | Args: 26 | value: the constant value to store at the class level. 27 | copy: whether to return a deepcopy of the value or the original reference. 28 | 29 | Note: returning constant values by reference is more performant but opens the up 30 | the possibility to mutating them. For this reason, if the value type is not 31 | immutable, setting `copy` to `True` is recommended. 32 | """ 33 | 34 | def __init__(self, value: Any, copy: bool = False) -> None: 35 | self.name: str = "" 36 | self.value: Any = value 37 | self.copy: bool = copy 38 | 39 | def __set_name__(self, owner: type, name: str) -> None: 40 | self.name = name 41 | 42 | def __get__(self, obj: object, objtype: type = None) -> Any: 43 | return deepcopy(self.value) if self.copy else self.value 44 | 45 | def __set__(self, obj: object, value: Any) -> None: 46 | raise AttributeError(f"Class constant '{self.name}' cannot be assigned.") 47 | 48 | def __delete__(self, obj: object) -> None: 49 | raise AttributeError(f"Class constant '{self.name}' cannot be deleted.") 50 | -------------------------------------------------------------------------------- /zne/utils/docstrings.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Docstrings utils module.""" 14 | 15 | from __future__ import annotations 16 | 17 | SECTIONS = ["Args", "Returns", "Yields", "Raises"] # TODO add header section 18 | 19 | 20 | def insert_into_docstring(original_docstring: str, new_content_list: list[tuple[str, str]]) -> str: 21 | """Inserts new lines of content into existing docstring. 22 | 23 | Args: 24 | original_docstring: The docstring where to insert new lines. 25 | new_content_list: A list of tuples. The first element of a tuple specifies the section to 26 | append to, while the second element contains the new information to be added to that 27 | section, e.g.: [("Args", "param: example"), ("Raises", "Error: example")]. 28 | 29 | Returns: 30 | Updated docstring. 31 | """ 32 | docstring_lines = original_docstring.split("\n") 33 | docstring_lines = remove_all_leading_spaces_in_docstring(docstring_lines) 34 | for section, new_content in new_content_list: 35 | validate_section(section) 36 | docstring_lines = insert_into_docstring_lines(docstring_lines, new_content, section) 37 | return "\n".join(docstring_lines) 38 | 39 | 40 | def remove_all_leading_spaces_in_docstring(docstring_lines: list[str]) -> list[str]: 41 | """Removes all common leading spaces in each line. 42 | 43 | Args: 44 | docstring_lines: A list of docstring lines. 45 | 46 | Returns: 47 | Docstring lines with leading spaces removed. 48 | """ 49 | num_leading_spaces = get_num_leading_spaces(docstring_lines) 50 | docstring_lines[1:] = [line[num_leading_spaces:] for line in docstring_lines[1:]] 51 | return docstring_lines 52 | 53 | 54 | def get_num_leading_spaces(docstring_lines: list[str]) -> int: 55 | """Returns the number of leading spaces in docstring. 56 | 57 | Args: 58 | docstring_lines: A list of docstring lines. 59 | 60 | Returns: 61 | The number of leading spaces. 62 | 63 | Raises: 64 | RuntimeError: If docstring is empty or one-liner. 65 | """ 66 | for line in docstring_lines[1:]: 67 | if len(line) > 0: 68 | return len(line) - len(line.lstrip(" ")) 69 | raise ValueError("Functionality not supported for single-line docstrings.") 70 | 71 | 72 | def validate_section(section: str) -> None: 73 | """Validates provided docstring section. 74 | 75 | Args: 76 | section: The docstring section to be validated. 77 | 78 | Raises: 79 | ValueError: If section not a valid docstring section. 80 | """ 81 | if section not in SECTIONS: 82 | raise ValueError(f"section has to be one of {SECTIONS}. Received {section} instead.") 83 | 84 | 85 | def insert_into_docstring_lines( 86 | docstring_lines: list[str], new_content: str, section: str 87 | ) -> list[str]: 88 | """Inserts new entry into list of docstring lines. 89 | 90 | Args: 91 | docstring_lines: A list of docstring lines. 92 | new_content: The new content. 93 | section: The section to append to. 94 | 95 | Returns: 96 | The updated list of docstring lines. 97 | """ 98 | section_exists = check_section_exists_in_docstring(docstring_lines, section) 99 | insert_location = get_insert_location_in_docstring(docstring_lines, section) 100 | if not section_exists: 101 | docstring_lines, insert_location = insert_new_section_into_docstring( 102 | docstring_lines, insert_location, section 103 | ) 104 | docstring_lines, insert_location = insert_new_content_into_docstring( 105 | docstring_lines, insert_location, new_content 106 | ) 107 | return docstring_lines 108 | 109 | 110 | def check_section_exists_in_docstring(docstring_lines: list[str], section: str) -> bool: 111 | """Checks whether section already exists in docstring. 112 | 113 | Args: 114 | docstring_lines: A list of docstring lines. 115 | section: The section to append to. 116 | 117 | Returns: 118 | True if section already exists and False otherwise. 119 | """ 120 | for line in docstring_lines: 121 | if section in line: 122 | return True 123 | return False 124 | 125 | 126 | def get_insert_location_in_docstring(docstring_lines: list[str], section: str) -> int: 127 | """Returns index at which to insert into docstring. 128 | 129 | Args: 130 | docstring_lines: A list of docstring lines. 131 | section: The section to append to. 132 | 133 | Returns: 134 | The index. 135 | """ 136 | section_idx = SECTIONS.index(section) 137 | truncated_sections = SECTIONS[section_idx + 1 :] 138 | for idx, line in enumerate(docstring_lines): 139 | if any(section in line for section in truncated_sections): 140 | return idx - 1 141 | return len(docstring_lines) - 1 142 | 143 | 144 | def insert_new_section_into_docstring( 145 | docstring_lines: list[str], insert_location: int, section: str 146 | ) -> tuple[list[str], int]: 147 | """Inserts a new section heading into docstring. 148 | 149 | Args: 150 | docstring_lines: A list of docstring lines. 151 | insert_location: The index at which to insert. 152 | section: The section to append to. 153 | 154 | Returns: 155 | A tuple containing the updated list of docstring lines and insert location. 156 | """ 157 | docstring_lines.insert(insert_location, "") 158 | docstring_lines.insert(insert_location + 1, section + ":") 159 | return docstring_lines, insert_location + 2 160 | 161 | 162 | def insert_new_content_into_docstring( 163 | docstring_lines: list[str], insert_location: int, new_content: str 164 | ) -> tuple[list[str], int]: 165 | """Inserts a new line of content into docstring. 166 | 167 | Args: 168 | docstring_lines: A list of docstring lines. 169 | insert_location: The index at which to insert. 170 | new_content: The new content. 171 | 172 | Returns: 173 | A tuple containing the updated list of docstring lines and insert location. 174 | """ 175 | new_content_lines = new_content.split("\n") 176 | counter = 0 177 | for counter, line in enumerate(new_content_lines): 178 | # TODO infer tab size instead of hardcoding it 179 | num_leading_spaces = 4 if counter == 0 else 8 180 | docstring_lines.insert(insert_location + counter, " " * num_leading_spaces + line) 181 | return docstring_lines, insert_location + counter + 1 182 | -------------------------------------------------------------------------------- /zne/utils/grouping.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Grouping utils module.""" 14 | 15 | from __future__ import annotations 16 | 17 | from collections.abc import Iterable, Iterator, Sequence 18 | from typing import Any 19 | 20 | 21 | def group_elements(elements: Sequence, group_size: int) -> list[tuple]: 22 | """Group elements in iterable into tuples of a given size. 23 | 24 | Args: 25 | elements: A list of elements to be grouped 26 | group_size: The size of grouped tuples 27 | 28 | Returns: 29 | List of grouped tuples 30 | """ 31 | return list(group_elements_gen(elements, group_size)) 32 | 33 | 34 | def group_elements_gen(elements: Sequence, group_size: int) -> Iterator[tuple]: 35 | """Generate groups of elements from iterable as tuples of a given size. 36 | 37 | Args: 38 | elements: A list of elements to be grouped 39 | group_size: The size of grouped tuples 40 | 41 | Yields: 42 | The next grouped tuple 43 | """ 44 | if not isinstance(elements, Iterable): 45 | raise TypeError("Values argument must be iterable.") 46 | if not isinstance(group_size, int): 47 | raise TypeError("Size argument must be non-zero positive int.") 48 | if group_size < 1: 49 | raise ValueError("Size argument must be non-zero positive int.") 50 | elements = list(elements) 51 | while elements: 52 | yield tuple(elements[:group_size]) 53 | del elements[:group_size] 54 | 55 | 56 | def merge_dicts(dict_list: Sequence[dict]) -> dict: 57 | """Given a sequence of dictionaries merge them all into one. 58 | 59 | Args: 60 | dict_list: A sequence of dictuinaries. 61 | 62 | Returns: 63 | A dictionary resulting from merging all the input ones. In case of key collision, 64 | only the latest value will be preserved. 65 | """ 66 | dictionary: dict = {} 67 | for dct in dict_list: 68 | dictionary.update(dct) 69 | return dictionary 70 | 71 | 72 | def from_common_key(dict_list: Sequence[dict], key: Any) -> tuple: 73 | """Given a sequence of dictionaries and a common key extract all values. 74 | 75 | Args: 76 | dict_list: A sequence of dictuinaries 77 | 78 | Returns: 79 | A tuple with all values extracted from the input dictionaries. Whenever key is not 80 | present in a dictionary, ``None`` will be inserted instead of the corresponding value. 81 | """ 82 | return tuple(d.get(key, None) for d in dict_list) 83 | 84 | 85 | def squash_for_equal_elements(sequence: Sequence) -> Any: 86 | """Squash values in sequence to a single output if all are equal. 87 | 88 | Args: 89 | sequence: the sequence of values to collapse. 90 | 91 | Returns: 92 | The common value if all elements are equal, or the input sequence otherwise. 93 | """ 94 | if not isinstance(sequence, Sequence): 95 | raise TypeError("Expected Sequence object, received {type(self.noise_amplifier)} instead.") 96 | if len(sequence) == 0: 97 | raise ValueError("Empty Sequence provided.") 98 | first = sequence[0] 99 | if all(element == first for element in sequence): 100 | return first 101 | return sequence 102 | -------------------------------------------------------------------------------- /zne/utils/serialization.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Serialization utils module.""" 14 | 15 | from __future__ import annotations 16 | 17 | from datetime import datetime 18 | from json import JSONEncoder, dump, dumps 19 | from typing import Any 20 | 21 | from numpy import ndarray 22 | from qiskit.primitives import EstimatorResult 23 | 24 | 25 | class DumpEncoder(JSONEncoder): 26 | """JSON encoder with extra methods for dumping.""" 27 | 28 | @classmethod 29 | def dump( 30 | cls, 31 | obj: Any, 32 | file: str | None = None, 33 | indent: int | None = None, 34 | ) -> None: 35 | """Dump object to file using self as JSON encoder.""" 36 | if file is None: # pragma: no cover 37 | file = f"{datetime.utcnow().isoformat()}Z.json" 38 | with open(file, "w", encoding="utf8") as fp: # pylint: disable=invalid-name 39 | return dump(obj, fp, indent=indent, cls=cls) 40 | 41 | @classmethod 42 | def dumps(cls, obj: Any, indent: int | None = None) -> str: 43 | """Dump object to string using self as JSON encoder.""" 44 | return dumps(obj, indent=indent, cls=cls) 45 | 46 | 47 | class ReprEncoder(DumpEncoder): 48 | """JSON encoder that falls back to `repr` if TypeError is raised.""" 49 | 50 | def default(self, o): 51 | try: 52 | return super().default(o) 53 | except TypeError: 54 | return repr(o) 55 | 56 | 57 | class NumPyEncoder(DumpEncoder): 58 | """JSON encoder for NumPy's :class:`numpy.ndarray` objects.""" 59 | 60 | def default(self, o): 61 | if isinstance(o, ndarray): 62 | return o.tolist() 63 | return super().default(o) 64 | 65 | 66 | class EstimatorResultEncoder(NumPyEncoder, ReprEncoder): 67 | """JSON encoder for :class:`EstimatorResult` objects.""" 68 | 69 | def default(self, o): 70 | if isinstance(o, EstimatorResult): 71 | return {"values": o.values, "metadata": o.metadata} 72 | return super().default(o) 73 | -------------------------------------------------------------------------------- /zne/utils/standard_gates.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Build dictionary library util.""" 14 | 15 | from qiskit.circuit import ControlledGate, Gate, QuantumCircuit 16 | 17 | 18 | def build_method_to_gate_dict() -> dict: 19 | """Returns dictionary mapping gate names to gate classes.""" 20 | method_to_gate = {} 21 | gates = Gate.__subclasses__() + ControlledGate.__subclasses__() 22 | for gate in gates: 23 | name = gate.__name__.lower() 24 | if name[-4:] == "gate": 25 | method = name[:-4] 26 | if hasattr(QuantumCircuit, method): 27 | method_to_gate[method] = gate 28 | return method_to_gate 29 | 30 | 31 | STANDARD_GATES = frozenset(build_method_to_gate_dict()) 32 | -------------------------------------------------------------------------------- /zne/utils/typing.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Type checking utils module.""" 14 | 15 | from typing import Any 16 | 17 | from numpy import array 18 | 19 | 20 | def isint(obj: Any) -> bool: 21 | """Check if object is int""" 22 | return isinstance(obj, int) and not isinstance(obj, bool) 23 | 24 | 25 | def isinteger(obj: Any) -> bool: 26 | """Check if object is an integer number""" 27 | return isint(obj) or isinstance(obj, float) and obj.is_integer() 28 | 29 | 30 | def isreal(obj: Any) -> bool: 31 | """Check if object is a real number: int or float minus ``±Inf`` and ``NaN``.""" 32 | return isint(obj) or isinstance(obj, float) and float("-Inf") < obj < float("Inf") 33 | 34 | 35 | def normalize_array(arr: Any) -> Any: 36 | """Normalize array-like objects (i.e. as in numpy) to tuples of the same shape. 37 | 38 | If zero dimensional, the object is preserved or casted to a core python type. 39 | """ 40 | arr = array(arr) 41 | num_dimensions = len(arr.shape) 42 | if num_dimensions == 0: 43 | return arr.tolist() 44 | if num_dimensions == 1: 45 | return tuple(arr.tolist()) 46 | return tuple(map(normalize_array, arr)) 47 | -------------------------------------------------------------------------------- /zne/utils/unset.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Module for singleton type to represent unset args.""" 14 | 15 | from __future__ import annotations 16 | 17 | from typing import Any 18 | 19 | 20 | class UnsetType: 21 | """Singleton type to represent unset args.""" 22 | 23 | __slots__ = () 24 | 25 | def __new__(cls) -> UnsetType: 26 | if not hasattr(cls, "instance"): 27 | cls.instance = super().__new__(cls) 28 | return cls.instance 29 | 30 | def __bool__(self) -> bool: 31 | return False 32 | 33 | def __eq__(self, __o: Any) -> bool: 34 | return self is __o 35 | 36 | def __repr__(self) -> str: 37 | return "UNSET" 38 | 39 | def __copy__(self) -> UnsetType: 40 | return self 41 | 42 | def __deepcopy__(self, memo: dict) -> UnsetType: 43 | return self 44 | 45 | 46 | UNSET = UnsetType() 47 | -------------------------------------------------------------------------------- /zne/utils/validation.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022-2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Validation utils module.""" 14 | 15 | from __future__ import annotations 16 | 17 | from collections.abc import Callable 18 | from copy import deepcopy 19 | from typing import Any 20 | 21 | from .unset import UNSET, UnsetType 22 | 23 | 24 | class quality: # pylint: disable=invalid-name 25 | """Quality attribute similar to property but geared towards custom validation. 26 | 27 | Args: 28 | fval: function to be used for validating an attribute value on `__set__` 29 | feff: function to be used for performing side-effects on `__set__` and 30 | `__delete__` (e.g. erasing cache) 31 | default: value to assume if set to `...` 32 | null: value to assume if set to `None` 33 | """ 34 | 35 | __slots__ = "fval", "feff", "_default", "_null", "_name" 36 | 37 | # TODO: update doc to fval's like property does for fget 38 | def __init__( 39 | self, 40 | fval: Callable[[Any, Any], Any] | None = None, 41 | feff: Callable[[Any], None] | None = None, 42 | *, 43 | default: Any = UNSET, 44 | null: Any = UNSET, 45 | ) -> None: 46 | self.fval: Callable[[Any, Any], Any] | None = fval 47 | self.feff: Callable[[Any], None] | None = feff 48 | self._default: Any = default 49 | self._null: Any = null 50 | self._name: str | None = None 51 | 52 | def __call__( 53 | self, 54 | fval: Callable[[Any, Any], Any] | None | UnsetType = UNSET, 55 | feff: Callable[[Any], None] | None | UnsetType = UNSET, 56 | *, 57 | default: Any = UNSET, 58 | null: Any = UNSET, 59 | ) -> quality: 60 | """Returns a shallow copy of the quality with updated attributes.""" 61 | copy = self.__class__( 62 | fval=fval or self.fval, # type: ignore 63 | feff=feff or self.feff, # type: ignore 64 | default=default or self.default, 65 | null=null or self.null, 66 | ) 67 | copy._name = self.name 68 | return copy 69 | 70 | def __copy__(self) -> quality: 71 | return self() 72 | 73 | def __deepcopy__(self, memo: dict) -> quality: 74 | fval = deepcopy(self.fval, memo) 75 | feff = deepcopy(self.feff, memo) 76 | default = deepcopy(self._default, memo) 77 | null = deepcopy(self._null, memo) 78 | return self(fval, feff, default=default, null=null) 79 | 80 | ################################################################################ 81 | ## PROPERTIES 82 | ################################################################################ 83 | @property 84 | def default(self) -> Any: 85 | """Default value for the managed attribute to be used for `...`. 86 | 87 | Enforced immutable to avoid side-effects. 88 | """ 89 | return deepcopy(self._default) 90 | 91 | @property 92 | def null(self) -> Any: 93 | """Null value for the managed attribute to be used for `None`. 94 | 95 | Enforced immutable to avoid side-effects. 96 | """ 97 | return deepcopy(self._null) 98 | 99 | @property 100 | def name(self) -> str: 101 | """Name for attr in managed instance as provided by `__set_name__`.""" 102 | return self._name 103 | 104 | @property 105 | def private_name(self) -> str: 106 | """Private name to store attr value at in managed instance.""" 107 | if self.name is None: 108 | return None 109 | return "__quality_" + self.name 110 | 111 | ################################################################################ 112 | ## DESCRIPTOR PROTOCOL 113 | ################################################################################ 114 | def __set_name__(self, owner: type, name: str) -> None: 115 | self._name = name 116 | 117 | def __get__(self, obj: object, objtype: type = None) -> Any: 118 | if obj is None: 119 | return self 120 | if hasattr(obj, self.private_name): 121 | return getattr(obj, self.private_name) 122 | setattr(obj, self.name, UNSET) 123 | return getattr(obj, self.name) 124 | 125 | def __set__(self, obj: object, value: Any) -> None: 126 | value = self._apply_defaults(value) 127 | if self.fval is not None: 128 | value = self.fval(obj, value) 129 | setattr(obj, self.private_name, value) 130 | if self.feff is not None: 131 | self.feff(obj) 132 | 133 | def __delete__(self, obj: object) -> None: 134 | if hasattr(obj, self.private_name): 135 | delattr(obj, self.private_name) 136 | if self.feff is not None: 137 | self.feff(obj) 138 | 139 | ################################################################################ 140 | ## DECORATORS 141 | ################################################################################ 142 | def validator(self, fval: Callable[[Any, Any], Any] | None) -> quality: 143 | """Returns a copy of the quality with a different validator.""" 144 | return self(fval=fval) 145 | 146 | def side_effect(self, feff: Callable[[Any], None] | None) -> quality: 147 | """Returns a copy of the quality with a different side effect.""" 148 | return self(feff=feff) 149 | 150 | ################################################################################ 151 | ## AUXILIARY 152 | ################################################################################ 153 | def _apply_defaults(self, value: Any) -> Any: 154 | """Apply defaults to user input value.""" 155 | default = self.default or self.null 156 | null = self.null or self.default 157 | if value is UNSET: 158 | value = default or None 159 | elif value is Ellipsis and default is not UNSET: 160 | value = default 161 | elif value is None and null is not UNSET: 162 | value = null 163 | return value 164 | --------------------------------------------------------------------------------