├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── BUG_REPORT.yaml
│ └── FEATURE_REQUEST.yaml
├── PULL_REQUEST_TEMPLATE.md
├── actions
│ ├── install-main-dependencies
│ │ └── action.yml
│ ├── install-qopt
│ │ └── action.yml
│ └── run-tests
│ │ └── action.yml
└── workflows
│ └── main.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── constraints.txt
├── demos
└── qiskit_patterns
│ ├── 0_setup_instructions.md
│ ├── 1_test_installation.ipynb
│ ├── 2_qiskit_patterns.ipynb
│ ├── data
│ ├── 125node_eagle_depth_one.qpy
│ ├── 125node_eagle_depth_zero.qpy
│ ├── 125node_example.lp
│ ├── 125node_example.png
│ ├── 125node_example_ising.txt
│ ├── 125node_heron_depth_one.qpy
│ └── 125node_heron_depth_zero.qpy
│ ├── demo_src
│ ├── graph.py
│ ├── map.py
│ ├── post.py
│ ├── run.py
│ └── transpile.py
│ ├── imgs
│ ├── circuit-0.png
│ ├── circuit.png
│ ├── eagle-heron-0.png
│ ├── eagle-heron.png
│ ├── exec-modes.png
│ ├── max-cut.png
│ └── patterns.png
│ ├── requirements.txt
│ └── sampler_data
│ ├── eagle_87ea531a.json
│ └── heron_215ebe55.json
├── how_tos
├── __init__.py
├── data
│ ├── graph_2layers_0seed.json
│ └── naive_transpilation.json
├── how_to_apply_optimal_qaoa_transpilation.ipynb
├── how_to_build_qaoa_swap_circuit.ipynb
├── how_to_optimize_qaoa_for_hw.ipynb
├── how_to_run_custom_cost_functions.ipynb
└── how_to_select_qubits.ipynb
├── pyproject.toml
├── qopt_best_practices
├── VERSION.txt
├── __init__.py
├── cost_function
│ ├── __init__.py
│ └── cost_utils.py
├── error_mitigation
│ └── __init__.py
├── qubit_selection
│ ├── __init__.py
│ ├── backend_evaluator.py
│ ├── metric_evaluators.py
│ └── qubit_subset_finders.py
├── sat_mapping
│ ├── __init__.py
│ └── sat_mapper.py
├── swap_strategies
│ ├── __init__.py
│ └── build_circuit.py
├── transpilation
│ ├── __init__.py
│ ├── preset_qaoa_passmanager.py
│ ├── qaoa_construction_pass.py
│ └── swap_cancellation_pass.py
└── utils
│ ├── __init__.py
│ └── graph_utils.py
├── requirements-dev.txt
├── requirements.txt
├── run
├── __init__.py
├── data
│ └── hardware_native_127.json
└── hw_native_test_1_circ.py
├── setup.py
├── stestr.conf
├── test
├── __init__.py
├── data
│ └── graph_2layers_0seed.json
├── test_cost_function.py
├── test_graph_utils.py
├── test_qaoa_construction.py
├── test_qubit_selection.py
├── test_sat_mapping.py
├── test_swap_cancellation.py
└── test_swap_strategies.py
└── tox.ini
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This file defines the persons who will be assigned as reviewers for PRs that
2 | # modify particular files in the repo. The PR can be merged when approved by at
3 | # least one codeowner. However, all Qiskit team members can (and should!) review the PRs.
4 |
5 | # Global rule, unless specialized by a later one
6 | * @eggerdj @ElePT @Cryoris
7 |
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml:
--------------------------------------------------------------------------------
1 | name: 🐛 Bug report
2 | description: Create a report to help us improve 🤔.
3 | labels: ["bug"]
4 |
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: Thank you for reporting! Please also use the search to see if there are any other relevant issues or pull requests.
9 |
10 | - type: textarea
11 | attributes:
12 | label: Environment
13 | description: For the version of Qiskit, please give the actual version number (_e.g._ 0.18.3) if you are using a release version, or the first 7-8 characters of the commit hash if you have installed from `git`. If anything else is relevant, you can add it to the list.
14 | # The trailing spaces on the following lines are to make filling the form
15 | # in easier. The type is 'textarea' rather than three separate 'input's
16 | # to make the resulting issue body less noisy with headings.
17 | value: |
18 | - **Qiskit version**:
19 | - **Python version**:
20 | - **Operating system**:
21 | validations:
22 | required: true
23 |
24 | - type: textarea
25 | attributes:
26 | label: What is happening?
27 | description: A short description of what is going wrong, in words.
28 | validations:
29 | required: true
30 |
31 | - type: textarea
32 | attributes:
33 | label: How can we reproduce the issue?
34 | description: Give some steps that show the bug. A [minimal working example](https://stackoverflow.com/help/minimal-reproducible-example) of code with output is best. If you are copying in code, please remember to enclose it in triple backticks (` ``` [multiline code goes here] ``` `) so that it [displays correctly](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax).
35 | validations:
36 | required: true
37 |
38 | - type: textarea
39 | attributes:
40 | label: What should happen?
41 | description: A short description, including about the expected output of any code in the previous section.
42 | validations:
43 | required: true
44 |
45 | - type: textarea
46 | attributes:
47 | label: Any suggestions?
48 | 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.
49 | validations:
50 | required: false
51 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yaml:
--------------------------------------------------------------------------------
1 | name: 🚀 Feature request
2 | description: Suggest an idea for this project 💡!
3 | labels: ["type: feature request"]
4 |
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: Please make sure to browse the opened and closed issues to make sure that this idea has not previously been discussed.
9 |
10 | - type: textarea
11 | attributes:
12 | label: What should we add?
13 | validations:
14 | required: true
15 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
9 |
10 | ### Summary
11 |
12 |
13 |
14 | ### Details and comments
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.github/actions/install-main-dependencies/action.yml:
--------------------------------------------------------------------------------
1 | # This code is part of a Qiskit project.
2 | #
3 | # (C) Copyright IBM 2021, 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 | name: 'Install Main Dependencies'
14 | description: 'Installs Python dependencies from Main'
15 | inputs:
16 | os:
17 | description: 'OS'
18 | required: true
19 | python-version:
20 | description: 'Python version'
21 | required: true
22 |
23 | runs:
24 | using: "composite"
25 | steps:
26 | - name: Get main last commit ids
27 | run: |
28 | echo "QISKIT_HASH=$(git ls-remote --heads https://github.com/Qiskit/qiskit.git refs/heads/main | awk '{print $1}')" >> $GITHUB_ENV
29 | shell: bash
30 | - name: Qiskit Cache
31 | env:
32 | CACHE_VERSION: v1
33 | id: qiskit-cache
34 | uses: actions/cache@v3
35 | with:
36 | path: qiskit-cache
37 | key: qiskit-cache-${{ inputs.os }}-${{ inputs.python-version }}-${{ env.QISKIT_HASH }}-${{ env.CACHE_VERSION }}
38 | - name: Install Qiskit from Main
39 | env:
40 | MACOSX_DEPLOYMENT_TARGET: 10.15
41 | run: |
42 | echo 'Install Qiskit from Stable'
43 | pip install -U qiskit
44 | shell: bash
45 |
--------------------------------------------------------------------------------
/.github/actions/install-qopt/action.yml:
--------------------------------------------------------------------------------
1 | # This code is part of a Qiskit project.
2 | #
3 | # (C) Copyright IBM 2021.
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 | name: 'Install qopt_best_practices'
14 | description: 'Installs qopt_best_practices'
15 |
16 | runs:
17 | using: "composite"
18 | steps:
19 | - run : |
20 | pip install -e .
21 | pip install -U -c constraints.txt -r requirements-dev.txt
22 | shell: bash
23 |
--------------------------------------------------------------------------------
/.github/actions/run-tests/action.yml:
--------------------------------------------------------------------------------
1 | # This code is part of a Qiskit project.
2 | #
3 | # (C) Copyright IBM 2021, 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 | name: 'Run Unit Tests'
14 | description: 'Run Unit Tests'
15 | inputs:
16 | os:
17 | description: 'OS'
18 | required: true
19 | event-name:
20 | description: 'Actions event'
21 | required: true
22 | run-slow:
23 | description: 'Run slow tests or not'
24 | required: true
25 | python-version:
26 | description: 'Python version'
27 | required: true
28 | runs:
29 | using: "composite"
30 | steps:
31 | - name: Run Unit Tests
32 | env:
33 | PYTHONWARNINGS: default
34 | run: |
35 | # run slow tests only on scheduled event or if input flag is set
36 | if [ "${{ inputs.os }}" == "ubuntu-latest" ] && [ "${{ inputs.python-version }}" == "3.8" ]; then
37 | export PYTHON="coverage3 run --source qopt_best_practices --parallel-mode"
38 | fi
39 | stestr --test-path test run 2> >(tee /dev/stderr out.txt > /dev/null)
40 | shell: bash
41 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This code is part of a Qiskit project.
2 | #
3 | # (C) Copyright IBM 2021, 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 | name: qopt-best-practices Unit Tests
14 |
15 | on:
16 | push:
17 | branches:
18 | - main
19 | - 'stable/**'
20 | pull_request:
21 | branches:
22 | - main
23 | - 'stable/**'
24 |
25 | concurrency:
26 | group: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }}
27 | cancel-in-progress: true
28 |
29 | jobs:
30 | Checks:
31 | if: github.repository_owner == 'qiskit-community'
32 | runs-on: ${{ matrix.os }}
33 | strategy:
34 | matrix:
35 | os: [ubuntu-latest]
36 | python-version: [3.8]
37 | steps:
38 | - name: Print Concurrency Group
39 | env:
40 | CONCURRENCY_GROUP: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }}
41 | run: |
42 | echo -e "\033[31;1;4mConcurrency Group\033[0m"
43 | echo -e "$CONCURRENCY_GROUP\n"
44 | shell: bash
45 | - uses: actions/checkout@v3
46 | with:
47 | fetch-depth: 0
48 | - uses: actions/setup-python@v4
49 | with:
50 | python-version: ${{ matrix.python-version }}
51 | cache: 'pip'
52 | cache-dependency-path: |
53 | setup.py
54 | requirements.txt
55 | requirements-dev.txt
56 | - uses: ./.github/actions/install-main-dependencies
57 | with:
58 | os: ${{ matrix.os }}
59 | python-version: ${{ matrix.python-version }}
60 | qiskit-main: "false"
61 | if: ${{ !startsWith(github.ref, 'refs/heads/stable') && !startsWith(github.base_ref, 'stable/') }}
62 | - uses: ./.github/actions/install-qopt
63 | - run: pip check
64 | if: ${{ !cancelled() }}
65 | shell: bash
66 | - name: Style Check
67 | run: |
68 | make style
69 | if: ${{ !cancelled() }}
70 | shell: bash
71 | qopt:
72 | if: github.repository_owner == 'qiskit-community'
73 | runs-on: ${{ matrix.os }}
74 | strategy:
75 | fail-fast: false
76 | matrix:
77 | os: [ubuntu-latest]
78 | python-version: [3.8, 3.11]
79 | include:
80 | - os: macos-latest
81 | python-version: 3.8
82 | - os: macos-latest
83 | python-version: 3.11
84 | - os: windows-latest
85 | python-version: 3.8
86 | - os: windows-latest
87 | python-version: 3.11
88 | steps:
89 | - uses: actions/checkout@v3
90 | - uses: actions/setup-python@v4
91 | with:
92 | python-version: ${{ matrix.python-version }}
93 | cache: 'pip'
94 | cache-dependency-path: |
95 | setup.py
96 | requirements.txt
97 | requirements-dev.txt
98 | - uses: ./.github/actions/install-main-dependencies
99 | with:
100 | os: ${{ matrix.os }}
101 | python-version: ${{ matrix.python-version }}
102 | qiskit-main: "false"
103 | if: ${{ !startsWith(github.ref, 'refs/heads/stable') && !startsWith(github.base_ref, 'stable/') }}
104 | - uses: ./.github/actions/install-qopt
105 | - run: make lint
106 | shell: bash
107 | - name: qopt Unit Tests under Python ${{ matrix.python-version }}
108 | uses: ./.github/actions/run-tests
109 | with:
110 | os: ${{ matrix.os }}
111 | event-name: ${{ github.event_name }}
112 | run-slow: ${{ contains(github.event.pull_request.labels.*.name, 'run_slow') }}
113 | python-version: ${{ matrix.python-version }}
114 | if: ${{ !cancelled() }}
115 | - name: Coverage combine
116 | run: |
117 | mkdir ./ci-artifact-data
118 | coverage3 combine
119 | mv .coverage ./ci-artifact-data/qopt.dat
120 | if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.8 }}
121 | shell: bash
122 | - uses: actions/upload-artifact@v3
123 | with:
124 | name: ${{ matrix.os }}-${{ matrix.python-version }}
125 | path: ./ci-artifact-data/*
126 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .*
2 | */.*
3 | # Byte-compiled / optimized / DLL files
4 | __pycache__/
5 | *.py[cod]
6 | *$py.class
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | build/
14 | develop-eggs/
15 | dist/
16 | downloads/
17 | eggs/
18 | .eggs/
19 | lib/
20 | lib64/
21 | parts/
22 | sdist/
23 | var/
24 | wheels/
25 | share/python-wheels/
26 | *.egg-info/
27 | .installed.cfg
28 | *.egg
29 | MANIFEST
30 |
31 | # PyInstaller
32 | # Usually these files are written by a python script from a template
33 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
34 | *.manifest
35 | *.spec
36 |
37 | # Installer logs
38 | pip-log.txt
39 | pip-delete-this-directory.txt
40 |
41 | # Unit test / coverage reports
42 | htmlcov/
43 | .tox/
44 | .nox/
45 | .coverage
46 | .coverage.*
47 | .cache
48 | nosetests.xml
49 | coverage.xml
50 | *.cover
51 | *.py,cover
52 | .hypothesis/
53 | .pytest_cache/
54 | cover/
55 |
56 | # Translations
57 | *.mo
58 | *.pot
59 |
60 | # Django stuff:
61 | *.log
62 | local_settings.py
63 | db.sqlite3
64 | db.sqlite3-journal
65 |
66 | # Flask stuff:
67 | instance/
68 | .webassets-cache
69 |
70 | # Scrapy stuff:
71 | .scrapy
72 |
73 | # Sphinx documentation
74 | docs/_build/
75 |
76 | # PyBuilder
77 | .pybuilder/
78 | target/
79 |
80 | # Jupyter Notebook
81 | .ipynb_checkpoints
82 |
83 | # IPython
84 | profile_default/
85 | ipython_config.py
86 |
87 | # pyenv
88 | # For a library or package, you might want to ignore these files since the code is
89 | # intended to run in multiple environments; otherwise, check them in:
90 | # .python-version
91 |
92 | # pipenv
93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
96 | # install all needed dependencies.
97 | #Pipfile.lock
98 |
99 | # poetry
100 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
101 | # This is especially recommended for binary packages to ensure reproducibility, and is more
102 | # commonly ignored for libraries.
103 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
104 | #poetry.lock
105 |
106 | # pdm
107 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
108 | #pdm.lock
109 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
110 | # in version control.
111 | # https://pdm.fming.dev/#use-with-ide
112 | .pdm.toml
113 |
114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115 | __pypackages__/
116 |
117 | # Celery stuff
118 | celerybeat-schedule
119 | celerybeat.pid
120 |
121 | # SageMath parsed files
122 | *.sage.py
123 |
124 | # Environments
125 | .env
126 | .venv
127 | env/
128 | venv/
129 | ENV/
130 | env.bak/
131 | venv.bak/
132 |
133 | # Spyder project settings
134 | .spyderproject
135 | .spyproject
136 |
137 | # Rope project settings
138 | .ropeproject
139 |
140 | # mkdocs documentation
141 | /site
142 |
143 | # mypy
144 | .mypy_cache/
145 | .dmypy.json
146 | dmypy.json
147 |
148 | # Pyre type checker
149 | .pyre/
150 |
151 | # pytype static type analyzer
152 | .pytype/
153 |
154 | # Cython debug symbols
155 | cython_debug/
156 |
157 | # PyCharm
158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160 | # and can be added to the global gitignore or merged into this file. For a more nuclear
161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162 | #.idea/
163 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | OS := $(shell uname -s)
2 |
3 | ifeq ($(OS), Linux)
4 | NPROCS := $(shell grep -c ^processor /proc/cpuinfo)
5 | else ifeq ($(OS), Darwin)
6 | NPROCS := 2
7 | else
8 | NPROCS := 0
9 | endif # $(OS)
10 |
11 | ifeq ($(NPROCS), 2)
12 | CONCURRENCY := 2
13 | else ifeq ($(NPROCS), 1)
14 | CONCURRENCY := 1
15 | else ifeq ($(NPROCS), 3)
16 | CONCURRENCY := 3
17 | else ifeq ($(NPROCS), 0)
18 | CONCURRENCY := 0
19 | else
20 | CONCURRENCY := $(shell echo "$(NPROCS) 2" | awk '{printf "%.0f", $$1 / $$2}')
21 | endif
22 |
23 | .PHONY: lint style black test test_ci coverage clean
24 |
25 | all_check: style lint
26 |
27 | lint:
28 | pylint -rn qopt_best_practices test
29 |
30 | black:
31 | python -m black qopt_best_practices test
32 |
33 | style:
34 | python -m black --check qopt_best_practices test
35 |
36 | test:
37 | python -m unittest discover -v test
38 |
39 | test_ci:
40 | echo "Detected $(NPROCS) CPUs running with $(CONCURRENCY) workers"
41 | python -m stestr run --concurrency $(CONCURRENCY)
42 |
43 | coverage:
44 | python -m coverage3 run --source qopt_best_practices -m unittest discover -s test -q
45 | python -m coverage3 report
46 |
47 | coverage_erase:
48 | python -m coverage erase
49 |
50 | clean: coverage_erase;
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # q-optimization-best-practices
2 |
3 | A collection of guidelines to run quantum optimization algorithms on superconducting qubits with Qiskit,
4 | using as reference the Quantum Approximate Optimization Algorithm (QAOA) workflow.
5 |
6 | This repository shows how to combine methods employed in the QAOA literature to get good results on hardware [[1]](https://arxiv.org/abs/2307.14427),
7 | such as SWAP strategies [[2]](https://arxiv.org/abs/2202.03459),
8 | SAT mapping [[3]](https://arxiv.org/abs/2212.05666),
9 | pulse-efficient transpilation and dynamical-decoupling [coming soon]. In the future, it will be expanded
10 | to include a broader range of quantum algorithms for combinatorial optimization.
11 |
12 | The `qopt_best_practices` directory contains a series of reference implementations for the key
13 | strategies mentioned in the description. These are not intended to be feature-complete, they are
14 | prototypes that should be easy to test and try out in different settings
15 | (that's why the library is pip-installable!) and can also serve as a guide for your
16 | own advanced implementations of these techniques.
17 |
18 | The `how-tos` directory contains a series of notebooks you can run to see these techniques in action.
19 | In many cases, the helper methods provided for a specific task (for example, swap mapping), are just
20 | light wrappers over already-available Qiskit utilities (transpiler passes). How-tos that focus
21 | on a specific tasks will probably show direct use of the Qiskit code, while how-tos that lay out a full
22 | workflow will likely rely on the provided wrappers for a more general overview of the steps to follow.
23 |
24 |
25 | This is only a relatively polished repository. If you see any bug or feature you'd like to add, this is a community
26 | effort, contributions are welcome. Don't hesitate to open an issue or a PR.
27 |
28 | ## Quick Start
29 |
30 | 1. run `git clone https://github.com/qiskit-community/q-optimization-best-practices.git` in your local environment
31 | 2. do `pip install -r requirements.txt`
32 | 3. do `pip install .`
33 | 4. navigate to the `how-tos` section and run the notebook of your choice!
34 |
35 |
36 | ## Table of Contents
37 |
38 | The contents of the `qopt_best_practices` package are structured around the key strategies that
39 | can be applied to best run quantum optimization algorithms on real hardware:
40 |
41 | 1. Qubit Selection -> `qubit_selection`
42 | 2. SAT Mapping -> `sat_mapping`
43 | 3. Application of SWAP strategies -> `swap_strategies`
44 | 4. QAOA cost function -> `cost_function`
45 |
46 | ## QAOA Formulation Convention
47 |
48 | The convention in this repository is that the (depth-one) QAOA should create an Ansatz of the form:
49 |
50 | $$
51 | \begin{align}
52 | \exp\left(-i\beta H_m\right)\exp\left(-i\gamma H_c\right)\vert+\rangle^{\otimes n}
53 | \end{align}
54 | $$
55 |
56 | to **minimize** the energy of $H_c$. Since we are minimizing $\\langle H_c\\rangle$,
57 | the initial state of the ansatz should be the ground state of the mixer $H_m$. This enforces:
58 |
59 | $$
60 | \\begin{align}
61 | H_m=-\\sum_iX_i
62 | \\end{align}
63 | $$
64 |
65 | As cost operator we apply:
66 |
67 | $$
68 | \\begin{align}
69 | H_c=\\sum_{i,j=0}^{n-1}w_{i,j}Z_iZ_j
70 | \\end{align}
71 | $$
72 |
73 | where the sum runs over $i < j$.
74 |
75 | At the circuit level, these definitions imply that the exponential of the mixer is built
76 | with $R_x(-2\\beta)$ rotations and the exponential of the cost operator is built from $R_{zz}(2\\gamma w_{i,j})$.
77 |
78 |
79 | ## References
80 | 1. Sack, S. H., & Egger, D. J. (2023). Large-scale quantum approximate optimization on non-planar graphs with machine learning noise mitigation. arXiv preprint arXiv:2307.14427. [Link](https://arxiv.org/pdf/2307.14427.pdf).
81 | 2. Weidenfeller, J., Valor, L. C., Gacon, J., Tornow, C., Bello, L., Woerner, S., & Egger, D. J. (2022). Scaling of the quantum approximate optimization algorithm on superconducting qubit based hardware. Quantum, 6, 870. [Link](https://arxiv.org/abs/2202.03459).
82 | 3. Matsuo, A., Yamashita, S., & Egger, D. J. (2023). A SAT approach to the initial mapping problem in SWAP gate insertion for commuting gates. IEICE Transactions on Fundamentals of Electronics, Communications and Computer Sciences, 2022EAP1159. [Link](https://arxiv.org/abs/2212.05666).
83 |
--------------------------------------------------------------------------------
/constraints.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/constraints.txt
--------------------------------------------------------------------------------
/demos/qiskit_patterns/0_setup_instructions.md:
--------------------------------------------------------------------------------
1 | # Qiskit Patterns Lab Prep Work
2 |
3 | ## Requirements
4 |
5 | - Laptop (Windows, MacOS or Linux), access to a terminal
6 |
7 | ## Instructions
8 |
9 | 1. Follow the instructions on https://docs.quantum.ibm.com/start/install#install-and-set-up-qiskit-with-the-qiskit-runtime-client
10 | to install and set up a Python environment with Qiskit. Activate your environment.
11 | 2. Follow the instructions on https://docs.quantum.ibm.com/start/setup-channel#iqp to set up your access credentials to use
12 | IBM Quantum systems. Only focus on the IBM Quantum Platform instructions, no need to worry about the IBM Cloud setup.
13 | 3. Clone this repository running `git clone https://github.com/qiskit-community/qopt-best-practices.git` or downloading the zip file:
14 | - go to: https://github.com/qiskit-community/qopt-best-practices
15 | - click on code > download zip
16 |
17 |
18 | 4. Navigate to the `demos/qiskit_patterns` directory using the terminal
19 | 5. Run `pip install -r requirements.txt`
20 | 6. Activate your jupyter environment running `jupyter notebook` in your environment, this should open a browser window
21 | that shows the contents of the `qiskit_patterns` directory
22 | 7. Open `1_test_installation.ipynb` and follow the instructions to test that the installation was successful
23 |
--------------------------------------------------------------------------------
/demos/qiskit_patterns/1_test_installation.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "pycharm": {
7 | "name": "#%% md\n"
8 | }
9 | },
10 | "source": [
11 | "# Installation Test\n",
12 | "\n",
13 | "Run the cell below (press `Shift` + `Enter`) to check that all the imports work correctly. If you see the 🎉 emoji as the cell output, your installation was successful."
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {
20 | "pycharm": {
21 | "name": "#%%\n"
22 | }
23 | },
24 | "outputs": [],
25 | "source": [
26 | "import matplotlib.pyplot\n",
27 | "import networkx as nx\n",
28 | "from qiskit_ibm_runtime.fake_provider import FakeVigoV2\n",
29 | "from qiskit import qpy\n",
30 | "from qiskit_aer import Aer\n",
31 | "from qiskit_optimization.applications import Maxcut\n",
32 | "print(\"Your installation works 🎉!\")"
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {},
38 | "source": [
39 | "If you have followed the installation instructions (i.e. you have run `pip install -r requirements.txt`) and you get a `module not found` error, your jupyter environment might not be using the right Python kernel. You can try following the following instructions to register your kernel: https://saturncloud.io/blog/how-to-add-a-python-3-kernel-to-jupyter-ipython/."
40 | ]
41 | },
42 | {
43 | "cell_type": "markdown",
44 | "metadata": {
45 | "pycharm": {
46 | "name": "#%%\n"
47 | }
48 | },
49 | "source": [
50 | "If you have not done so yet, you can use the cell below to check that your IBM Quantum Platform account registration has been successful. The backend retrieval step may take a couple of seconds."
51 | ]
52 | },
53 | {
54 | "cell_type": "code",
55 | "execution_count": null,
56 | "metadata": {
57 | "pycharm": {
58 | "name": "#%%\n"
59 | }
60 | },
61 | "outputs": [],
62 | "source": [
63 | "from qiskit_ibm_runtime import QiskitRuntimeService\n",
64 | "\n",
65 | "service = QiskitRuntimeService()\n",
66 | "print(\"Your available backends are: \", service.backends())"
67 | ]
68 | },
69 | {
70 | "cell_type": "markdown",
71 | "metadata": {
72 | "pycharm": {
73 | "name": "#%% md\n"
74 | }
75 | },
76 | "source": [
77 | "If your service authentication fails (you get an `IBMNotAuthorizedError`), you can try saving your account again. Make sure to replace `\"\"` with your token in string format (example: `service = QiskitRuntimeService(channel=\"ibm_quantum\", token=\"1234abcd\")`)."
78 | ]
79 | },
80 | {
81 | "cell_type": "code",
82 | "execution_count": null,
83 | "metadata": {
84 | "pycharm": {
85 | "name": "#%%\n"
86 | }
87 | },
88 | "outputs": [],
89 | "source": [
90 | "from qiskit_ibm_runtime import QiskitRuntimeService\n",
91 | "\n",
92 | "# Save an IBM Quantum account and set it as your default account.\n",
93 | "QiskitRuntimeService.save_account(channel=\"ibm_quantum\", token=\"\", overwrite=True, set_as_default=True)\n",
94 | "\n",
95 | "# Load saved credentials\n",
96 | "service = QiskitRuntimeService()"
97 | ]
98 | }
99 | ],
100 | "metadata": {
101 | "kernelspec": {
102 | "display_name": "ibm_tech_clean",
103 | "language": "python",
104 | "name": "ibm_tech_clean"
105 | },
106 | "language_info": {
107 | "codemirror_mode": {
108 | "name": "ipython",
109 | "version": 3
110 | },
111 | "file_extension": ".py",
112 | "mimetype": "text/x-python",
113 | "name": "python",
114 | "nbconvert_exporter": "python",
115 | "pygments_lexer": "ipython3",
116 | "version": "3.10.0"
117 | }
118 | },
119 | "nbformat": 4,
120 | "nbformat_minor": 1
121 | }
--------------------------------------------------------------------------------
/demos/qiskit_patterns/data/125node_eagle_depth_one.qpy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/data/125node_eagle_depth_one.qpy
--------------------------------------------------------------------------------
/demos/qiskit_patterns/data/125node_eagle_depth_zero.qpy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/data/125node_eagle_depth_zero.qpy
--------------------------------------------------------------------------------
/demos/qiskit_patterns/data/125node_example.lp:
--------------------------------------------------------------------------------
1 | \ This file has been generated by DOcplex
2 | \ ENCODING=ISO-8859-1
3 | \Problem name: CPLEX
4 |
5 | Minimize
6 | obj: [ - 8 x0^2 + 8 x0*x1 + 8 x0*x13 - 8 x1^2 + 8 x1*x2 - 8 x2*x3 + 8 x3*x4
7 | - 4 x4^2 - 8 x4*x5 + 8 x4*x14 + 8 x5*x6 - 8 x6^2 + 8 x6*x7 - 8 x7^2
8 | + 8 x7*x8 - 12 x8^2 + 8 x8*x9 + 8 x8*x15 - 8 x9*x10 + 8 x10^2 - 8 x10*x11
9 | + 8 x11*x12 - 8 x12*x16 - 8 x13*x17 - 8 x14*x21 - 8 x15*x25 + 8 x16^2
10 | - 8 x16*x29 + 8 x17*x18 - 8 x18^2 + 8 x18*x19 + 4 x19^2 - 8 x19*x20
11 | - 8 x19*x32 + 8 x20*x21 - 4 x21^2 + 8 x21*x22 - 8 x22^2 + 8 x22*x23
12 | - 12 x23^2 + 8 x23*x24 + 8 x23*x33 - 8 x24^2 + 8 x24*x25 + 4 x25^2
13 | - 8 x25*x26 + 8 x26*x27 - 4 x27^2 + 8 x27*x28 - 8 x27*x34 - 8 x28*x29
14 | + 12 x29^2 - 8 x29*x30 + 8 x30^2 - 8 x30*x31 + 8 x31^2 - 8 x31*x35
15 | + 8 x32*x38 - 8 x33*x42 + 8 x34^2 - 8 x34*x46 + 8 x35^2 - 8 x35*x50
16 | - 8 x36^2 + 8 x36*x37 + 8 x36*x51 - 8 x37*x38 - 4 x38^2 + 8 x38*x39
17 | - 8 x39*x40 + 12 x40^2 - 8 x40*x41 - 8 x40*x52 + 8 x41^2 - 8 x41*x42
18 | + 12 x42^2 - 8 x42*x43 + 8 x43*x44 - 4 x44^2 + 8 x44*x45 - 8 x44*x53
19 | - 8 x45*x46 + 12 x46^2 - 8 x46*x47 + 8 x47*x48 + 4 x48^2 - 8 x48*x49
20 | - 8 x48*x54 + 8 x49^2 - 8 x49*x50 + 8 x50^2 - 8 x51^2 + 8 x51*x55
21 | + 8 x52*x59 + 8 x53^2 - 8 x53*x63 + 8 x54*x67 - 8 x55^2 + 8 x55*x56
22 | - 8 x56^2 + 8 x56*x57 - 4 x57^2 - 8 x57*x58 + 8 x57*x70 + 8 x58^2
23 | - 8 x58*x59 - 4 x59^2 + 8 x59*x60 - 8 x60^2 + 8 x60*x61 - 4 x61^2
24 | - 8 x61*x62 + 8 x61*x71 + 8 x62^2 - 8 x62*x63 + 4 x63^2 + 8 x63*x64
25 | - 8 x64^2 + 8 x64*x65 + 4 x65^2 - 8 x65*x66 - 8 x65*x72 + 8 x66*x67
26 | - 12 x67^2 + 8 x67*x68 - 8 x68^2 + 8 x68*x69 - 8 x69^2 + 8 x69*x73
27 | - 8 x70^2 + 8 x70*x76 - 8 x71^2 + 8 x71*x80 + 8 x72*x84 - 8 x73*x88
28 | - 8 x74*x75 + 8 x74*x89 + 8 x75*x76 - 12 x76^2 + 8 x76*x77 - 8 x77*x78
29 | + 4 x78^2 + 8 x78*x79 - 8 x78*x90 - 8 x79^2 + 8 x79*x80 - 4 x80^2
30 | - 8 x80*x81 + 8 x81^2 - 8 x81*x82 - 4 x82^2 + 8 x82*x83 + 8 x82*x91
31 | - 8 x83^2 + 8 x83*x84 - 12 x84^2 + 8 x84*x85 - 8 x85^2 + 8 x85*x86
32 | - 4 x86^2 + 8 x86*x87 - 8 x86*x92 - 8 x87^2 + 8 x87*x88 - 8 x89*x93
33 | + 8 x90^2 - 8 x90*x97 - 8 x91*x101 + 8 x92^2 - 8 x92*x105 + 8 x93^2
34 | - 8 x93*x94 + 8 x94*x95 - 12 x95^2 + 8 x95*x96 + 8 x95*x108 - 8 x96*x97
35 | + 4 x97^2 + 8 x97*x98 - 8 x98*x99 + 12 x99^2 - 8 x99*x100 - 8 x99*x109
36 | + 8 x100*x101 - 4 x101^2 + 8 x101*x102 - 8 x102^2 + 8 x102*x103 + 4 x103^2
37 | - 8 x103*x104 - 8 x103*x110 + 8 x104*x105 + 4 x105^2 - 8 x105*x106
38 | + 8 x106^2 - 8 x106*x107 + 8 x107^2 - 8 x107*x111 - 8 x108*x112
39 | + 8 x109*x116 + 8 x110^2 - 8 x110*x120 + 8 x111*x124 + 8 x112^2
40 | - 8 x112*x113 + 8 x113*x114 - 8 x114*x115 + 8 x115^2 - 8 x115*x116
41 | - 4 x116^2 + 8 x116*x117 - 8 x117^2 + 8 x117*x118 - 8 x118*x119
42 | + 8 x119*x120 + 4 x120^2 - 8 x120*x121 + 8 x121^2 - 8 x121*x122 + 8 x122^2
43 | - 8 x122*x123 + 8 x123*x124 - 8 x124^2 ]/2 + 4
44 | Subject To
45 |
46 | Bounds
47 | 0 <= x0 <= 1
48 | 0 <= x1 <= 1
49 | 0 <= x2 <= 1
50 | 0 <= x3 <= 1
51 | 0 <= x4 <= 1
52 | 0 <= x5 <= 1
53 | 0 <= x6 <= 1
54 | 0 <= x7 <= 1
55 | 0 <= x8 <= 1
56 | 0 <= x9 <= 1
57 | 0 <= x10 <= 1
58 | 0 <= x11 <= 1
59 | 0 <= x12 <= 1
60 | 0 <= x13 <= 1
61 | 0 <= x14 <= 1
62 | 0 <= x15 <= 1
63 | 0 <= x16 <= 1
64 | 0 <= x17 <= 1
65 | 0 <= x18 <= 1
66 | 0 <= x19 <= 1
67 | 0 <= x20 <= 1
68 | 0 <= x21 <= 1
69 | 0 <= x22 <= 1
70 | 0 <= x23 <= 1
71 | 0 <= x24 <= 1
72 | 0 <= x25 <= 1
73 | 0 <= x26 <= 1
74 | 0 <= x27 <= 1
75 | 0 <= x28 <= 1
76 | 0 <= x29 <= 1
77 | 0 <= x30 <= 1
78 | 0 <= x31 <= 1
79 | 0 <= x32 <= 1
80 | 0 <= x33 <= 1
81 | 0 <= x34 <= 1
82 | 0 <= x35 <= 1
83 | 0 <= x36 <= 1
84 | 0 <= x37 <= 1
85 | 0 <= x38 <= 1
86 | 0 <= x39 <= 1
87 | 0 <= x40 <= 1
88 | 0 <= x41 <= 1
89 | 0 <= x42 <= 1
90 | 0 <= x43 <= 1
91 | 0 <= x44 <= 1
92 | 0 <= x45 <= 1
93 | 0 <= x46 <= 1
94 | 0 <= x47 <= 1
95 | 0 <= x48 <= 1
96 | 0 <= x49 <= 1
97 | 0 <= x50 <= 1
98 | 0 <= x51 <= 1
99 | 0 <= x52 <= 1
100 | 0 <= x53 <= 1
101 | 0 <= x54 <= 1
102 | 0 <= x55 <= 1
103 | 0 <= x56 <= 1
104 | 0 <= x57 <= 1
105 | 0 <= x58 <= 1
106 | 0 <= x59 <= 1
107 | 0 <= x60 <= 1
108 | 0 <= x61 <= 1
109 | 0 <= x62 <= 1
110 | 0 <= x63 <= 1
111 | 0 <= x64 <= 1
112 | 0 <= x65 <= 1
113 | 0 <= x66 <= 1
114 | 0 <= x67 <= 1
115 | 0 <= x68 <= 1
116 | 0 <= x69 <= 1
117 | 0 <= x70 <= 1
118 | 0 <= x71 <= 1
119 | 0 <= x72 <= 1
120 | 0 <= x73 <= 1
121 | 0 <= x74 <= 1
122 | 0 <= x75 <= 1
123 | 0 <= x76 <= 1
124 | 0 <= x77 <= 1
125 | 0 <= x78 <= 1
126 | 0 <= x79 <= 1
127 | 0 <= x80 <= 1
128 | 0 <= x81 <= 1
129 | 0 <= x82 <= 1
130 | 0 <= x83 <= 1
131 | 0 <= x84 <= 1
132 | 0 <= x85 <= 1
133 | 0 <= x86 <= 1
134 | 0 <= x87 <= 1
135 | 0 <= x88 <= 1
136 | 0 <= x89 <= 1
137 | 0 <= x90 <= 1
138 | 0 <= x91 <= 1
139 | 0 <= x92 <= 1
140 | 0 <= x93 <= 1
141 | 0 <= x94 <= 1
142 | 0 <= x95 <= 1
143 | 0 <= x96 <= 1
144 | 0 <= x97 <= 1
145 | 0 <= x98 <= 1
146 | 0 <= x99 <= 1
147 | 0 <= x100 <= 1
148 | 0 <= x101 <= 1
149 | 0 <= x102 <= 1
150 | 0 <= x103 <= 1
151 | 0 <= x104 <= 1
152 | 0 <= x105 <= 1
153 | 0 <= x106 <= 1
154 | 0 <= x107 <= 1
155 | 0 <= x108 <= 1
156 | 0 <= x109 <= 1
157 | 0 <= x110 <= 1
158 | 0 <= x111 <= 1
159 | 0 <= x112 <= 1
160 | 0 <= x113 <= 1
161 | 0 <= x114 <= 1
162 | 0 <= x115 <= 1
163 | 0 <= x116 <= 1
164 | 0 <= x117 <= 1
165 | 0 <= x118 <= 1
166 | 0 <= x119 <= 1
167 | 0 <= x120 <= 1
168 | 0 <= x121 <= 1
169 | 0 <= x122 <= 1
170 | 0 <= x123 <= 1
171 | 0 <= x124 <= 1
172 |
173 | Binaries
174 | x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21
175 | x22 x23 x24 x25 x26 x27 x28 x29 x30 x31 x32 x33 x34 x35 x36 x37 x38 x39 x40 x41
176 | x42 x43 x44 x45 x46 x47 x48 x49 x50 x51 x52 x53 x54 x55 x56 x57 x58 x59 x60 x61
177 | x62 x63 x64 x65 x66 x67 x68 x69 x70 x71 x72 x73 x74 x75 x76 x77 x78 x79 x80 x81
178 | x82 x83 x84 x85 x86 x87 x88 x89 x90 x91 x92 x93 x94 x95 x96 x97 x98 x99 x100
179 | x101 x102 x103 x104 x105 x106 x107 x108 x109 x110 x111 x112 x113 x114 x115 x116
180 | x117 x118 x119 x120 x121 x122 x123 x124
181 | End
182 |
--------------------------------------------------------------------------------
/demos/qiskit_patterns/data/125node_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/data/125node_example.png
--------------------------------------------------------------------------------
/demos/qiskit_patterns/data/125node_example_ising.txt:
--------------------------------------------------------------------------------
1 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ, 1.0
2 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZI, 1.0
3 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII, -1.0
4 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII, 1.0
5 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIII, 1.0
6 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII, -1.0
7 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIII, 1.0
8 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIII, 1.0
9 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIII, 1.0
10 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIII, 1.0
11 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIII, -1.0
12 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIII, -1.0
13 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIII, 1.0
14 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZ, 1.0
15 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIII, -1.0
16 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIII, 1.0
17 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIII, -1.0
18 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIII, -1.0
19 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIII, 1.0
20 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIII, 1.0
21 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIII, -1.0
22 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIII, -1.0
23 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIII, 1.0
24 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIII, -1.0
25 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIII, 1.0
26 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIII, 1.0
27 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIII, 1.0
28 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIII, -1.0
29 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIII, 1.0
30 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
31 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
32 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
33 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
34 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
35 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
36 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
37 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
38 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIII, 1.0
39 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
40 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
41 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
42 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
43 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
44 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
45 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
46 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
47 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
48 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
49 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
50 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
51 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
52 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
53 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
54 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
55 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
56 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
57 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
58 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
59 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
60 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
61 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
62 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
63 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
64 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
65 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
66 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
67 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
68 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
69 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
70 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
71 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
72 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
73 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
74 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
75 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
76 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
77 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
78 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
79 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
80 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
81 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
82 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
83 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
84 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
85 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
86 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
87 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
88 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
89 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
90 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
91 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
92 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
93 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
94 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
95 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
96 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
97 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
98 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
99 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
100 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
101 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
102 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
103 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
104 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
105 | IIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
106 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
107 | IIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
108 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
109 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
110 | IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
111 | IIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
112 | IIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
113 | IIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
114 | IIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
115 | IIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
116 | IIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
117 | IIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
118 | IIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
119 | IIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
120 | IIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
121 | IIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
122 | IIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
123 | IIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
124 | IIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
125 | IIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
126 | IIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
127 | IIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
128 | ZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
129 | IIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
130 | IIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
131 | IIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
132 | IIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
133 | IIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
134 | IIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
135 | IIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
136 | IIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
137 | IIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
138 | IIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
139 | IIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
140 | IIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
141 | IZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, -1.0
142 | ZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII, 1.0
143 |
--------------------------------------------------------------------------------
/demos/qiskit_patterns/data/125node_heron_depth_one.qpy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/data/125node_heron_depth_one.qpy
--------------------------------------------------------------------------------
/demos/qiskit_patterns/data/125node_heron_depth_zero.qpy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/data/125node_heron_depth_zero.qpy
--------------------------------------------------------------------------------
/demos/qiskit_patterns/demo_src/graph.py:
--------------------------------------------------------------------------------
1 | import networkx as nx
2 | import numpy as np
3 | import matplotlib.pyplot as plt
4 |
5 |
6 | def generate_demo_graph():
7 | # Generating a graph of 5 nodes
8 | n = 5 # Number of nodes in graph
9 | G = nx.Graph()
10 | G.add_nodes_from(np.arange(0, n, 1))
11 | elist = [(0, 1, 1.0), (0, 2, 1.0), (0, 4, 1.0), (1, 2, 1.0), (2, 3, 1.0), (3, 4, 1.0)]
12 | # tuple is (i,j,weight) where (i,j) is the edge
13 | G.add_weighted_edges_from(elist)
14 | return G
15 |
16 |
17 | def draw_graph(G):
18 | colors = ["tab:grey" for node in G.nodes()]
19 | pos = nx.spring_layout(G)
20 | default_axes = plt.axes(frameon=True)
21 | nx.draw_networkx(G, node_color=colors, node_size=600, alpha=0.8, ax=default_axes, pos=pos)
22 | edge_labels = nx.get_edge_attributes(G, "weight")
23 | nx.draw_networkx_edge_labels(G, pos=pos, edge_labels=edge_labels)
24 |
--------------------------------------------------------------------------------
/demos/qiskit_patterns/demo_src/map.py:
--------------------------------------------------------------------------------
1 | from qiskit_optimization.applications import Maxcut
2 | from qiskit.circuit.library import QAOAAnsatz
3 |
4 |
5 | def map_graph_to_qubo(graph):
6 | max_cut = Maxcut(graph)
7 | qp = max_cut.to_quadratic_program()
8 | return qp
9 |
10 |
11 | def map_qubo_to_ising(qubo):
12 | qubitOp, offset = qubo.to_ising()
13 | return qubitOp, offset
14 |
15 |
16 | def map_ising_to_circuit(hamiltonian, num_layers):
17 | ansatz = QAOAAnsatz(cost_operator=hamiltonian, reps=num_layers)
18 | ansatz.measure_all()
19 | return ansatz
20 |
--------------------------------------------------------------------------------
/demos/qiskit_patterns/demo_src/post.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 | import json
3 | import matplotlib
4 | import matplotlib.pyplot as plt
5 | import networkx as nx
6 | import numpy as np
7 | import os
8 |
9 | from qiskit_optimization.applications import Maxcut
10 |
11 |
12 | # auxiliary functions to sample most likely bitstring
13 | def to_bitstring(integer, num_bits):
14 | result = np.binary_repr(integer, width=num_bits)
15 | return [int(digit) for digit in result]
16 |
17 |
18 | def sample_most_likely(state_vector, num_bits):
19 | keys = list(state_vector.keys())
20 | values = list(state_vector.values())
21 | most_likely = keys[np.argmax(np.abs(values))]
22 | most_likely_bitstring = to_bitstring(most_likely, num_bits)
23 | most_likely_bitstring.reverse()
24 | return np.asarray(most_likely_bitstring)
25 |
26 |
27 | # auxiliary function to plot graphs
28 | def plot_result(G, x):
29 | colors = ["tab:grey" if i == 0 else "tab:purple" for i in x]
30 | pos, default_axes = nx.spring_layout(G), plt.axes(frameon=True)
31 | nx.draw_networkx(G, node_color=colors, node_size=600, alpha=0.8, pos=pos)
32 |
33 |
34 | def plot_distribution(final_distribution):
35 | matplotlib.rcParams.update({"font.size": 10})
36 | final_bits = final_distribution.binary_probabilities()
37 | values = np.abs(list(final_bits.values()))
38 | top_4_values = sorted(values, reverse=True)[:4]
39 | positions = []
40 | for value in top_4_values:
41 | positions.append(np.where(values == value)[0])
42 | fig = plt.figure(figsize=(11, 6))
43 | ax = fig.add_subplot(1, 1, 1)
44 | plt.xticks(rotation=45)
45 | plt.title("Result Distribution")
46 | plt.xlabel("Bitstrings (reversed)")
47 | plt.ylabel("Probability")
48 | ax.bar(list(final_bits.keys()), list(final_bits.values()), color="tab:grey")
49 | for p in positions:
50 | ax.get_children()[int(p)].set_color("tab:purple")
51 | plt.show()
52 |
53 |
54 | # auxiliary function to convert bit-strings to objective values
55 | def samples_to_objective_values(samples, qp):
56 | """Convert the samples to values of the objective function."""
57 | objective_values = defaultdict(float)
58 | for bit_str, prob in samples.items():
59 | # Qiskit use little endian hence the [::-1]
60 | candidate_sol = [int(bit) for bit in bit_str[::-1]]
61 | fval = qp.objective.evaluate(candidate_sol)
62 | objective_values[fval] += prob
63 |
64 | return objective_values
65 |
66 |
67 | # auxiliary function to load saved samples
68 | def load_data(qp):
69 | depth_one_heron, depth_zero_heron, depth_one_eagle, depth_zero_eagle = {}, {}, {}, {}
70 | for file in os.listdir("sampler_data/"):
71 | with open(f"sampler_data/{file}", "r") as fin:
72 | data = json.load(fin)
73 |
74 | if file.startswith("heron"):
75 | depth_one_heron.update(data["depth-one"])
76 | depth_zero_heron.update(data["depth-zero"])
77 | else:
78 | depth_one_eagle.update(data["depth-one"])
79 | depth_zero_eagle.update(data["depth-zero"])
80 |
81 | depth_zero_heron = samples_to_objective_values(depth_zero_heron, qp)
82 | depth_one_heron = samples_to_objective_values(depth_one_heron, qp)
83 | depth_zero_eagle = samples_to_objective_values(depth_zero_eagle, qp)
84 | depth_one_eagle = samples_to_objective_values(depth_one_eagle, qp)
85 |
86 | return depth_one_heron, depth_zero_heron, depth_one_eagle, depth_zero_eagle
87 |
88 |
89 | # auxiliary function to load the QP from the saved Paulis
90 | def load_qp():
91 | # First, load the Paulis that encode the MaxCut problem.
92 | with open("data/125node_example_ising.txt", "r") as fin:
93 | paulis, lines = [], fin.read()
94 | for edge in lines.split("\n"):
95 | try:
96 | pauli, coefficient = edge.split(", ")
97 | paulis.append((pauli, float(coefficient)))
98 | except ValueError:
99 | pass
100 |
101 | # Next, convert the Paulis to a weighted graph.
102 | wedges = []
103 | for pauli_str, coefficient in paulis:
104 | wedges.append(
105 | [idx for idx, char in enumerate(pauli_str[::-1]) if char == "Z"]
106 | + [{"weight": coefficient}]
107 | )
108 |
109 | weighted_graph = nx.DiGraph(wedges)
110 |
111 | # Create the Quadratic program to return form the weighted graph.
112 | mc = Maxcut(weighted_graph)
113 | qp = mc.to_quadratic_program()
114 |
115 | # Finding the min and max requires CPLEX. If this is not installed we use
116 | # hard coded values for the sake of the demo.
117 | try:
118 | from qiskit_optimization.algorithms import CplexOptimizer
119 | from qiskit_optimization.problems.quadratic_objective import ObjSense
120 |
121 | # Mximization gives the max cut
122 | sol = CplexOptimizer().solve(qp)
123 |
124 | # Minimization gives the min cut
125 | qp2 = mc.to_quadratic_program()
126 | qp2.objective._sense = ObjSense.MINIMIZE
127 | sol2 = CplexOptimizer().solve(qp2)
128 |
129 | max_cut, min_cut = sol.fval, sol2.fval
130 | except:
131 | max_cut, min_cut = 67, -63
132 |
133 | return qp, max_cut, min_cut
134 |
135 |
136 | # auxiliary function to help plot cumulative distribution functions
137 | def _plot_cdf(objective_values: dict, ax, label):
138 | x_vals = sorted(objective_values.keys())
139 | y_vals = np.cumsum([objective_values[x] for x in x_vals])
140 | ax.plot(x_vals, y_vals, label=label)
141 |
142 |
143 | def plot_cdf(depth_zero, depth_one, max_cut, min_cut, ax, title):
144 | _plot_cdf(depth_zero, ax, "Depth-zero")
145 | _plot_cdf(depth_one, ax, "Depth-one")
146 | ax.vlines(max_cut, 0, 1, "k", linestyle="--")
147 | ax.vlines(max(list(depth_one.keys())), 0, 1, "C1", linestyle="--")
148 | approx = 100 * (max(list(depth_one.keys())) - min_cut) / (max_cut - min_cut)
149 | ax.set_title(title + f" {approx}%")
150 | ax.set_xlabel("Objective function value")
151 | ax.set_ylabel("Cumulative distribution function")
152 | ax.legend(loc=2)
153 |
--------------------------------------------------------------------------------
/demos/qiskit_patterns/demo_src/run.py:
--------------------------------------------------------------------------------
1 | from uuid import uuid4
2 | import json
3 |
4 |
5 | # auxiliary function to save result data
6 | def save_result(result, backend_type, path="sampler_data"):
7 | results_to_save = {
8 | "depth-one": result.quasi_dists[0].binary_probabilities(),
9 | "depth-zero": result.quasi_dists[1].binary_probabilities(),
10 | }
11 |
12 | with open(f"{path}/{backend_type}_{str(uuid4())[0:8]}.json", "w") as fout:
13 | json.dump(results_to_save, fout)
14 |
--------------------------------------------------------------------------------
/demos/qiskit_patterns/demo_src/transpile.py:
--------------------------------------------------------------------------------
1 | from qiskit import transpile
2 |
3 |
4 | def optimize_circuit(circuit, backend):
5 | return transpile(circuit, backend=backend)
6 |
--------------------------------------------------------------------------------
/demos/qiskit_patterns/imgs/circuit-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/imgs/circuit-0.png
--------------------------------------------------------------------------------
/demos/qiskit_patterns/imgs/circuit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/imgs/circuit.png
--------------------------------------------------------------------------------
/demos/qiskit_patterns/imgs/eagle-heron-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/imgs/eagle-heron-0.png
--------------------------------------------------------------------------------
/demos/qiskit_patterns/imgs/eagle-heron.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/imgs/eagle-heron.png
--------------------------------------------------------------------------------
/demos/qiskit_patterns/imgs/exec-modes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/imgs/exec-modes.png
--------------------------------------------------------------------------------
/demos/qiskit_patterns/imgs/max-cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/imgs/max-cut.png
--------------------------------------------------------------------------------
/demos/qiskit_patterns/imgs/patterns.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/demos/qiskit_patterns/imgs/patterns.png
--------------------------------------------------------------------------------
/demos/qiskit_patterns/requirements.txt:
--------------------------------------------------------------------------------
1 | qiskit>=1.0.0
2 | qiskit_ibm_runtime>=0.18.0
3 | qiskit_optimization>=0.6.0
4 | qiskit_aer
5 | matplotlib
6 | notebook
7 | ipykernel
8 | pylatexenc
--------------------------------------------------------------------------------
/how_tos/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/how_tos/__init__.py
--------------------------------------------------------------------------------
/how_tos/data/graph_2layers_0seed.json:
--------------------------------------------------------------------------------
1 | {"Original graph": [[0, 7], [0, 3], [0, 5], [7, 9], [7, 8], [1, 2], [1, 4], [1, 6], [2, 3], [2, 8], [5, 8], [5, 3], [4, 9], [4, 6], [9, 6]], "SAT mapping": {"0": 6, "1": 2, "2": 7, "3": 8, "4": 1, "5": 9, "6": 3, "7": 5, "8": 4, "9": 0}, "paulis": [["IIIIIZZIII", 1.0], ["IIIIIIZIZI", 1.0], ["IIIIIIZIIZ", 1.0], ["ZIIIIZIIII", 1.0], ["IIIIZZIIII", 1.0], ["IIZIIIIZII", 1.0], ["IZZIIIIIII", 1.0], ["IIZZIIIIII", 1.0], ["IIIIIIIZZI", 1.0], ["IIIIZIIZII", 1.0], ["IIIIZIIIIZ", 1.0], ["IIIIIIIIZZ", 1.0], ["ZZIIIIIIII", 1.0], ["IZIZIIIIII", 1.0], ["ZIIZIIIIII", 1.0]], "description": "paulis are remapped with `SAT mapping`.", "min swap layers": 2, "nx seed": 0}
--------------------------------------------------------------------------------
/how_tos/data/naive_transpilation.json:
--------------------------------------------------------------------------------
1 | {"method": "Naive", "time": 2178.7279443740845, "ops": {"sx": 24249, "rz": 18917, "cz": 13298, "measure": 133, "x": 43, "barrier": 1}, "depth": 6814}
--------------------------------------------------------------------------------
/how_tos/how_to_select_qubits.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "collapsed": true,
7 | "jupyter": {
8 | "outputs_hidden": true
9 | },
10 | "pycharm": {
11 | "name": "#%% md\n"
12 | }
13 | },
14 | "source": [
15 | "# How to select the qubit subset to run your circuit on HW\n",
16 | "\n",
17 | "The `BackendEvaluator` util allows you to find the best subset of qubits of a given **geometry** according to a given **metric**. This is useful to define the initial layout for the circuit transpilation to run on hardware."
18 | ]
19 | },
20 | {
21 | "cell_type": "markdown",
22 | "metadata": {
23 | "collapsed": false,
24 | "jupyter": {
25 | "outputs_hidden": false
26 | }
27 | },
28 | "source": [
29 | "\n",
30 | "## 1. Running with default settings\n",
31 | "\n",
32 | "By default, the `BackendEvaluator` class will try to find the line (geometry) witht the best 2-qubit gate fidelity (metric) for a given backend. Let's see this in action for 10 qubits in a fake 16-qubit backend."
33 | ]
34 | },
35 | {
36 | "cell_type": "code",
37 | "execution_count": 1,
38 | "metadata": {
39 | "collapsed": false,
40 | "jupyter": {
41 | "outputs_hidden": false
42 | },
43 | "pycharm": {
44 | "name": "#%%\n"
45 | }
46 | },
47 | "outputs": [],
48 | "source": [
49 | "# SIMULATED FAKE BACKEND\n",
50 | "from qiskit_ibm_runtime.fake_provider import FakeGuadalupe\n",
51 | "\n",
52 | "backend = FakeGuadalupe()"
53 | ]
54 | },
55 | {
56 | "cell_type": "code",
57 | "execution_count": 2,
58 | "metadata": {
59 | "collapsed": false,
60 | "jupyter": {
61 | "outputs_hidden": false
62 | },
63 | "pycharm": {
64 | "name": "#%%\n"
65 | }
66 | },
67 | "outputs": [
68 | {
69 | "data": {
70 | "image/png": "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",
71 | "text/plain": [
72 | ""
73 | ]
74 | },
75 | "execution_count": 2,
76 | "metadata": {},
77 | "output_type": "execute_result"
78 | }
79 | ],
80 | "source": [
81 | "from qiskit.visualization import plot_gate_map\n",
82 | "\n",
83 | "plot_gate_map(backend)"
84 | ]
85 | },
86 | {
87 | "cell_type": "code",
88 | "execution_count": 3,
89 | "metadata": {
90 | "collapsed": false,
91 | "jupyter": {
92 | "outputs_hidden": false
93 | },
94 | "pycharm": {
95 | "name": "#%%\n"
96 | }
97 | },
98 | "outputs": [
99 | {
100 | "name": "stdout",
101 | "output_type": "stream",
102 | "text": [
103 | "Best path: [1, 4, 7, 10, 12, 13, 14, 11, 8, 5]\n",
104 | "Best path fidelity 0.9099375156097996\n",
105 | "Num. evaluated paths 20\n"
106 | ]
107 | }
108 | ],
109 | "source": [
110 | "from qopt_best_practices.qubit_selection import BackendEvaluator\n",
111 | "\n",
112 | "num_qubits = 10\n",
113 | "path_finder = BackendEvaluator(backend)\n",
114 | "path, fidelity, num_subsets = path_finder.evaluate(num_qubits)\n",
115 | "\n",
116 | "print(\"Best path: \", path)\n",
117 | "print(\"Best path fidelity\", fidelity)\n",
118 | "print(\"Num. evaluated paths\", num_subsets)"
119 | ]
120 | },
121 | {
122 | "cell_type": "markdown",
123 | "metadata": {
124 | "collapsed": false,
125 | "jupyter": {
126 | "outputs_hidden": false
127 | },
128 | "pycharm": {
129 | "name": "#%% md\n"
130 | }
131 | },
132 | "source": [
133 | "## 2. Running with custom evaluation criteria\n",
134 | "\n",
135 | "The evaluation criteria can also be defined by the user and given to the `BackendEvaluator` class as a callable:\n"
136 | ]
137 | },
138 | {
139 | "cell_type": "code",
140 | "execution_count": 4,
141 | "metadata": {
142 | "collapsed": false,
143 | "jupyter": {
144 | "outputs_hidden": false
145 | },
146 | "pycharm": {
147 | "name": "#%%\n"
148 | }
149 | },
150 | "outputs": [],
151 | "source": [
152 | "from __future__ import annotations\n",
153 | "import numpy as np\n",
154 | "import rustworkx as rx\n",
155 | "\n",
156 | "from qiskit.transpiler import CouplingMap\n",
157 | "\n",
158 | "\n",
159 | "def find_lines(length: int, backend, coupling_map: CouplingMap | None = None) -> list[int]:\n",
160 | " \"\"\"Finds all possible lines of length `length` for a specific backend topology.\n",
161 | "\n",
162 | " This method can take quite some time to run on large devices since there\n",
163 | " are many paths.\n",
164 | "\n",
165 | " Returns:\n",
166 | " The found paths.\n",
167 | " \"\"\"\n",
168 | "\n",
169 | " # might make sense to make backend the only input for simplicity\n",
170 | " if coupling_map is None:\n",
171 | " coupling_map = CouplingMap(backend.configuration().coupling_map)\n",
172 | "\n",
173 | " all_paths = rx.all_pairs_all_simple_paths(\n",
174 | " coupling_map.graph,\n",
175 | " min_depth=length,\n",
176 | " cutoff=length,\n",
177 | " ).values()\n",
178 | "\n",
179 | " paths = np.asarray(\n",
180 | " [\n",
181 | " (list(c), list(sorted(list(c))))\n",
182 | " for a in iter(all_paths)\n",
183 | " for b in iter(a)\n",
184 | " for c in iter(a[b])\n",
185 | " ]\n",
186 | " )\n",
187 | "\n",
188 | " # filter out duplicated paths\n",
189 | " _, unique_indices = np.unique(paths[:, 1], return_index=True, axis=0)\n",
190 | " paths = paths[:, 0][unique_indices].tolist()\n",
191 | "\n",
192 | " return paths"
193 | ]
194 | },
195 | {
196 | "cell_type": "code",
197 | "execution_count": 5,
198 | "metadata": {
199 | "collapsed": false,
200 | "jupyter": {
201 | "outputs_hidden": false
202 | },
203 | "pycharm": {
204 | "name": "#%%\n"
205 | }
206 | },
207 | "outputs": [],
208 | "source": [
209 | "def evaluate_fidelity(path: list[int], backend, edges) -> float:\n",
210 | " \"\"\"Evaluates fidelity on a given list of qubits based on the two-qubit gate error\n",
211 | " for a specific backend.\n",
212 | "\n",
213 | " Returns:\n",
214 | " Path fidelity.\n",
215 | " \"\"\"\n",
216 | "\n",
217 | " two_qubit_fidelity = {}\n",
218 | " props = backend.properties()\n",
219 | "\n",
220 | " if \"cx\" in backend.configuration().basis_gates:\n",
221 | " gate_name = \"cx\"\n",
222 | " elif \"ecr\" in backend.configuration().basis_gates:\n",
223 | " gate_name = \"ecr\"\n",
224 | " else:\n",
225 | " raise ValueError(\"Could not identify two-qubit gate\")\n",
226 | "\n",
227 | " for edge in edges:\n",
228 | " try:\n",
229 | " cx_error = props.gate_error(gate_name, edge)\n",
230 | "\n",
231 | " except:\n",
232 | " cx_error = props.gate_error(gate_name, edge[::-1])\n",
233 | "\n",
234 | " two_qubit_fidelity[tuple(edge)] = 1 - cx_error\n",
235 | "\n",
236 | " if not path or len(path) == 1:\n",
237 | " return 0.0\n",
238 | "\n",
239 | " fidelity = 1.0\n",
240 | " for idx in range(len(path) - 1):\n",
241 | " fidelity *= two_qubit_fidelity[(path[idx], path[idx + 1])]\n",
242 | "\n",
243 | " return fidelity"
244 | ]
245 | },
246 | {
247 | "cell_type": "code",
248 | "execution_count": 6,
249 | "metadata": {
250 | "collapsed": false,
251 | "jupyter": {
252 | "outputs_hidden": false
253 | },
254 | "pycharm": {
255 | "name": "#%%\n"
256 | }
257 | },
258 | "outputs": [
259 | {
260 | "name": "stdout",
261 | "output_type": "stream",
262 | "text": [
263 | "Best path: [1, 4, 7, 10, 12, 13, 14, 11, 8, 5]\n",
264 | "Best path fidelity 0.9099375156097996\n",
265 | "Num. evaluated paths 20\n"
266 | ]
267 | }
268 | ],
269 | "source": [
270 | "num_qubits = 10\n",
271 | "path_finder = BackendEvaluator(backend)\n",
272 | "path, fidelity, num_subsets = path_finder.evaluate(\n",
273 | " num_qubits, subset_finder=find_lines, metric_eval=evaluate_fidelity\n",
274 | ")\n",
275 | "\n",
276 | "print(\"Best path: \", path)\n",
277 | "print(\"Best path fidelity\", fidelity)\n",
278 | "print(\"Num. evaluated paths\", num_subsets)\n"
279 | ]
280 | }
281 | ],
282 | "metadata": {
283 | "kernelspec": {
284 | "display_name": "ibm_tech",
285 | "language": "python",
286 | "name": "ibm_tech"
287 | },
288 | "language_info": {
289 | "codemirror_mode": {
290 | "name": "ipython",
291 | "version": 3
292 | },
293 | "file_extension": ".py",
294 | "mimetype": "text/x-python",
295 | "name": "python",
296 | "nbconvert_exporter": "python",
297 | "pygments_lexer": "ipython3",
298 | "version": "3.10.0"
299 | }
300 | },
301 | "nbformat": 4,
302 | "nbformat_minor": 4
303 | }
304 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools"]
3 | build-backend = "setuptools.build_meta"
4 |
5 | [tool.black]
6 | line-length = 100
7 | target-version = ['py38', 'py39', 'py310', 'py311']
8 |
9 | [tool.pylint.main]
10 | py-version = "3.8" # update it when bumping minimum supported python version
11 |
12 | [tool.pylint.basic]
13 | good-names = ["a", "b", "i", "j", "k", "d", "n", "m", "ex", "v", "w", "x", "y", "z", "Run", "_", "logger", "q", "c", "r", "qr", "cr", "qc", "nd", "pi", "op", "b", "ar", "br", "p", "cp", "ax", "dt", "__unittest", "iSwapGate", "mu"]
14 | method-rgx = "(([a-z_][a-z0-9_]{2,49})|(assert[A-Z][a-zA-Z0-9]{2,43})|(test_[_a-zA-Z0-9]{2,}))$"
15 | variable-rgx = "[a-z_][a-z0-9_]{1,30}$"
16 |
17 | [tool.pylint.format]
18 | max-line-length = 105 # default 100
19 |
20 | [tool.pylint."messages control"]
21 | disable = [
22 | # intentionally disabled:
23 | "spelling", # too noisy
24 | "fixme", # disabled as TODOs would show up as warnings
25 | "protected-access", # disabled as we don't follow the public vs private convention strictly
26 | "duplicate-code", # disabled as it is too verbose
27 | "redundant-returns-doc", # for @abstractmethod, it cannot interpret "pass"
28 | "too-many-lines", "too-many-branches", "too-many-locals", "too-many-nested-blocks", "too-many-statements",
29 | "too-many-instance-attributes", "too-many-arguments", "too-many-public-methods", "too-few-public-methods", "too-many-ancestors",
30 | "unnecessary-pass", # allow for methods with just "pass", for clarity
31 | "no-else-return", # relax "elif" after a clause with a return
32 | "docstring-first-line-empty", # relax docstring style
33 | "import-outside-toplevel", "import-error", # overzealous with our optionals/dynamic packages
34 | # TODO(#9614): these were added in modern Pylint. Decide if we want to enable them. If so,
35 | # remove from here and fix the issues. Else, move it above this section and add a comment
36 | # with the rationale
37 | "arguments-renamed",
38 | "broad-exception-raised",
39 | "consider-iterating-dictionary",
40 | "consider-using-dict-items",
41 | "consider-using-enumerate",
42 | "consider-using-f-string",
43 | "modified-iterating-list",
44 | "nested-min-max",
45 | "no-member",
46 | "no-value-for-parameter",
47 | "non-ascii-name",
48 | "not-context-manager",
49 | "superfluous-parens",
50 | "unknown-option-value",
51 | "unexpected-keyword-arg",
52 | "unnecessary-dict-index-lookup",
53 | "unnecessary-direct-lambda-call",
54 | "unnecessary-dunder-call",
55 | "unnecessary-ellipsis",
56 | "unnecessary-lambda-assignment",
57 | "unnecessary-list-index-lookup",
58 | "unspecified-encoding",
59 | "unsupported-assignment-operation",
60 | "use-dict-literal",
61 | "use-list-literal",
62 | "use-implicit-booleaness-not-comparison",
63 | "use-maxsplit-arg",
64 | ]
65 |
66 | enable = [
67 | "use-symbolic-message-instead"
68 | ]
69 |
70 | [tool.pylint.spelling]
71 | spelling-private-dict-file = ".pylintdict"
72 | spelling-store-unknown-words = "n"
73 |
--------------------------------------------------------------------------------
/qopt_best_practices/VERSION.txt:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/qopt_best_practices/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/qopt_best_practices/__init__.py
--------------------------------------------------------------------------------
/qopt_best_practices/cost_function/__init__.py:
--------------------------------------------------------------------------------
1 | """Cost function"""
2 |
3 | from .cost_utils import evaluate_sparse_pauli, qaoa_sampler_cost_fun
4 |
5 | __all__ = ["evaluate_sparse_pauli", "qaoa_sampler_cost_fun"]
6 |
--------------------------------------------------------------------------------
/qopt_best_practices/cost_function/cost_utils.py:
--------------------------------------------------------------------------------
1 | """QAOA Cost function utils"""
2 |
3 | import numpy as np
4 | from qiskit.quantum_info import SparsePauliOp
5 |
6 | _PARITY = np.array([-1 if bin(i).count("1") % 2 else 1 for i in range(256)], dtype=np.complex128)
7 |
8 |
9 | def evaluate_sparse_pauli(state: int, observable: SparsePauliOp) -> complex:
10 | """Utility for the evaluation of the expectation value of a measured state."""
11 | packed_uint8 = np.packbits(observable.paulis.z, axis=1, bitorder="little")
12 | state_bytes = np.frombuffer(state.to_bytes(packed_uint8.shape[1], "little"), dtype=np.uint8)
13 | reduced = np.bitwise_xor.reduce(packed_uint8 & state_bytes, axis=1)
14 | return np.sum(observable.coeffs * _PARITY[reduced])
15 |
16 |
17 | def qaoa_sampler_cost_fun(params, ansatz, hamiltonian, sampler):
18 | """Standard sampler-based QAOA cost function to be plugged into optimizer routines."""
19 | job = sampler.run(ansatz, params)
20 | sampler_result = job.result()
21 | sampled = sampler_result.quasi_dists[0]
22 |
23 | # a dictionary containing: {state: (measurement probability, value)}
24 | evaluated = {
25 | state: (probability, evaluate_sparse_pauli(state, hamiltonian))
26 | for state, probability in sampled.items()
27 | }
28 |
29 | result = sum(probability * value for probability, value in evaluated.values())
30 |
31 | return result
32 |
--------------------------------------------------------------------------------
/qopt_best_practices/error_mitigation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/qopt_best_practices/error_mitigation/__init__.py
--------------------------------------------------------------------------------
/qopt_best_practices/qubit_selection/__init__.py:
--------------------------------------------------------------------------------
1 | """Qubit selection"""
2 |
3 | from .backend_evaluator import BackendEvaluator
4 | from .metric_evaluators import evaluate_fidelity
5 | from .qubit_subset_finders import find_lines
6 |
7 | __all__ = ["BackendEvaluator", "evaluate_fidelity", "find_lines"]
8 |
--------------------------------------------------------------------------------
/qopt_best_practices/qubit_selection/backend_evaluator.py:
--------------------------------------------------------------------------------
1 | """Backend Evaluator"""
2 |
3 | from __future__ import annotations
4 | from collections.abc import Callable
5 |
6 | from qiskit.transpiler import CouplingMap
7 | from qiskit.providers import Backend
8 |
9 | from .metric_evaluators import evaluate_fidelity
10 | from .qubit_subset_finders import find_lines
11 |
12 |
13 | class BackendEvaluator:
14 | """
15 | Finds best subset of qubits for a given device that maximizes a given
16 | metric for a given geometry.
17 | This subset can be provided as an initial_layout for the SwapStrategy
18 | transpiler pass.
19 | """
20 |
21 | def __init__(self, backend: Backend):
22 | self.backend = backend
23 | if backend.version == 2:
24 | coupling_map = CouplingMap(backend.coupling_map)
25 | else:
26 | coupling_map = CouplingMap(backend.configuration().coupling_map)
27 | self.coupling_map = coupling_map
28 | if not self.coupling_map.is_symmetric:
29 | self.coupling_map.make_symmetric()
30 |
31 | def evaluate(
32 | self,
33 | num_qubits: int,
34 | subset_finder: Callable | None = None,
35 | metric_eval: Callable | None = None,
36 | ):
37 | """
38 | Args:
39 | num_qubits: the number of qubits
40 | subset_finder: callable, will default to "find_line"
41 | metric_eval: callable, will default to "evaluate_fidelity"
42 | """
43 |
44 | if metric_eval is None:
45 | metric_eval = evaluate_fidelity
46 |
47 | if subset_finder is None:
48 | subset_finder = find_lines
49 |
50 | # TODO: add callbacks
51 | qubit_subsets = subset_finder(num_qubits, self.backend)
52 |
53 | # evaluating the subsets
54 | scores = [
55 | metric_eval(subset, self.backend, self.coupling_map.get_edges())
56 | for subset in qubit_subsets
57 | ]
58 |
59 | # Return the best subset sorted by score
60 | best_subset, best_score = min(zip(qubit_subsets, scores), key=lambda x: -x[1])
61 | num_subsets = len(qubit_subsets)
62 |
63 | return best_subset, best_score, num_subsets
64 |
--------------------------------------------------------------------------------
/qopt_best_practices/qubit_selection/metric_evaluators.py:
--------------------------------------------------------------------------------
1 | """Subset finders. Currently contains reference implementation
2 | to evaluate 2-qubit gate fidelity."""
3 |
4 | from __future__ import annotations
5 | from qiskit.providers import Backend
6 | import rustworkx as rx
7 |
8 | TWO_Q_GATES = ["cx", "ecr", "cz"]
9 |
10 |
11 | def evaluate_fidelity(path: list[int], backend: Backend, edges: rx.EdgeList) -> float:
12 | """Evaluates fidelity on a given list of qubits based on the two-qubit gate error
13 | for a specific backend.
14 |
15 | Returns:
16 | Path fidelity.
17 | """
18 |
19 | two_qubit_fidelity = {}
20 |
21 | if backend.version == 2:
22 | target = backend.target
23 | try:
24 | gate_name = list(set(TWO_Q_GATES).intersection(backend.operation_names))[0]
25 | except IndexError as exc:
26 | raise ValueError("Could not identify two-qubit gate") from exc
27 |
28 | for edge in edges:
29 | try:
30 | cx_error = target[gate_name][edge].error
31 | except: # pylint: disable=bare-except
32 | cx_error = target[gate_name][edge[::-1]].error
33 |
34 | two_qubit_fidelity[tuple(edge)] = 1 - cx_error
35 | else:
36 | props = backend.properties()
37 | try:
38 | gate_name = list(set(TWO_Q_GATES).intersection(backend.configuration().basis_gates))[0]
39 | except IndexError as exc:
40 | raise ValueError("Could not identify two-qubit gate") from exc
41 |
42 | for edge in edges:
43 | try:
44 | cx_error = props.gate_error(gate_name, edge)
45 | except: # pylint: disable=bare-except
46 | cx_error = props.gate_error(gate_name, edge[::-1])
47 |
48 | two_qubit_fidelity[tuple(edge)] = 1 - cx_error
49 |
50 | if not path or len(path) == 1:
51 | return 0.0
52 |
53 | fidelity = 1.0
54 | for idx in range(len(path) - 1):
55 | fidelity *= two_qubit_fidelity[(path[idx], path[idx + 1])]
56 | return fidelity
57 |
--------------------------------------------------------------------------------
/qopt_best_practices/qubit_selection/qubit_subset_finders.py:
--------------------------------------------------------------------------------
1 | """Subset finders. Currently contains reference implementation
2 | to find lines."""
3 |
4 | from __future__ import annotations
5 | import numpy as np
6 | import rustworkx as rx
7 |
8 | from qiskit.transpiler import CouplingMap
9 | from qiskit.providers import Backend
10 |
11 |
12 | def find_lines(length: int, backend: Backend) -> list[int]:
13 | """Finds all possible lines of length `length` for a specific backend topology.
14 |
15 | This method can take quite some time to run on large devices since there
16 | are many paths.
17 |
18 | Returns:
19 | The found paths.
20 | """
21 |
22 | if backend.version == 2:
23 | coupling_map = CouplingMap(backend.coupling_map)
24 | else:
25 | coupling_map = CouplingMap(backend.configuration().coupling_map)
26 |
27 | if not coupling_map.is_symmetric:
28 | coupling_map.make_symmetric()
29 |
30 | all_paths = rx.all_pairs_all_simple_paths(
31 | coupling_map.graph,
32 | min_depth=length,
33 | cutoff=length,
34 | ).values()
35 |
36 | paths = np.asarray(
37 | [
38 | (list(c), list(sorted(list(c))))
39 | for a in iter(all_paths)
40 | for b in iter(a)
41 | for c in iter(a[b])
42 | ]
43 | )
44 |
45 | # filter out duplicated paths
46 | _, unique_indices = np.unique(paths[:, 1], return_index=True, axis=0)
47 | paths = paths[:, 0][unique_indices].tolist()
48 |
49 | return paths
50 |
--------------------------------------------------------------------------------
/qopt_best_practices/sat_mapping/__init__.py:
--------------------------------------------------------------------------------
1 | """SAT Mapping"""
2 |
3 | from .sat_mapper import SATMapper, SATResult
4 |
5 | __all__ = ["SATMapper", "SATResult"]
6 |
--------------------------------------------------------------------------------
/qopt_best_practices/sat_mapping/sat_mapper.py:
--------------------------------------------------------------------------------
1 | """A class to solve the SWAP gate insertion initial mapping problem
2 | using the SAT approach from https://arxiv.org/abs/2212.05666.
3 | """
4 |
5 | from __future__ import annotations
6 |
7 | from dataclasses import dataclass
8 | from itertools import combinations
9 | from threading import Timer
10 |
11 | import networkx as nx
12 | import numpy as np
13 |
14 | from pysat.formula import CNF, IDPool
15 | from pysat.solvers import Solver
16 |
17 | from qiskit.transpiler.passes.routing.commuting_2q_gate_routing import SwapStrategy
18 |
19 |
20 | @dataclass
21 | class SATResult:
22 | """A data class to hold the result of a SAT solver."""
23 |
24 | satisfiable: bool # Satisfiable is True if the SAT model could be solved in a given time.
25 | solution: dict # The solution to the SAT problem if it is satisfiable.
26 | mapping: list # The mapping of nodes in the pattern graph to nodes in the target graph.
27 | elapsed_time: float # The time it took to solve the SAT model.
28 |
29 |
30 | class SATMapper:
31 | r"""A class to introduce a SAT-approach to solve
32 | the initial mapping problem in SWAP gate insertion for commuting gates.
33 |
34 | When this pass is run on a DAG it will look for the first instance of
35 | :class:`.Commuting2qBlock` and use the program graph :math:`P` of this block of gates to
36 | find a layout for a given swap strategy. This layout is found with a
37 | binary search over the layers :math:`l` of the swap strategy. At each considered layer
38 | a subgraph isomorphism problem formulated as a SAT is solved by a SAT solver. Each instance
39 | is whether it is possible to embed the program graph :math:`P` into the effective
40 | connectivity graph :math:`C_l` that is achieved by applying :math:`l` layers of the
41 | swap strategy to the coupling map :math:`C_0` of the backend. Since solving SAT problems
42 | can be hard, a ``time_out`` fixes the maximum time allotted to the SAT solver for each
43 | instance. If this time is exceeded the considered problem is deemed unsatisfiable and
44 | the binary search proceeds to the next number of swap layers :math:``l``.
45 | """
46 |
47 | def __init__(self, timeout: int = 60):
48 | """Initialize the SATMapping.
49 |
50 | Args:
51 | timeout: The allowed time in seconds for each iteration of the SAT solver. This
52 | variable defaults to 60 seconds.
53 | """
54 | self.timeout = timeout
55 |
56 | def find_initial_mappings(
57 | self,
58 | program_graph: nx.Graph,
59 | swap_strategy: SwapStrategy,
60 | min_layers: int | None = None,
61 | max_layers: int | None = None,
62 | ) -> dict[int, SATResult]:
63 | r"""Find an initial mapping for a given swap strategy. Perform a binary search
64 | over the number of swap layers, and for each number of swap layers solve a
65 | subgraph isomorphism problem formulated as a SAT problem.
66 |
67 | Args:
68 | program_graph (nx.Graph): The program graph with commuting gates, where
69 | each edge represents a two-qubit gate.
70 | swap_strategy (SwapStrategy): The swap strategy to use to find the initial mapping.
71 | min_layers (int): The minimum number of swap layers to consider. Defaults to
72 | the maximum degree of the program graph - 2.
73 | max_layers (int): The maximum number of swap layers to consider. Defaults to
74 | the number of qubits in the swap strategy - 2.
75 |
76 | Returns:
77 | dict[int, SATResult]: A dictionary containing the results of the SAT solver for
78 | each number of swap layers.
79 | """
80 | # pylint: disable=too-many-locals
81 | num_nodes_g1 = len(program_graph.nodes)
82 | num_nodes_g2 = swap_strategy.distance_matrix.shape[0]
83 | if num_nodes_g1 > num_nodes_g2:
84 | return SATResult(False, [], [], 0)
85 | if min_layers is None:
86 | # use the maximum degree of the program graph - 2 as the lower bound.
87 | min_layers = max((d for _, d in program_graph.degree)) - 2
88 | if max_layers is None:
89 | max_layers = num_nodes_g2 - 1
90 |
91 | variable_pool = IDPool(start_from=1)
92 | variables = np.array(
93 | [
94 | [variable_pool.id(f"v_{i}_{j}") for j in range(num_nodes_g2)]
95 | for i in range(num_nodes_g1)
96 | ],
97 | dtype=int,
98 | )
99 | vid2mapping = {v: idx for idx, v in np.ndenumerate(variables)}
100 | binary_search_results = {}
101 |
102 | def interrupt(solver):
103 | # This function is called to interrupt the solver when the timeout is reached.
104 | solver.interrupt()
105 |
106 | # Make a cnf for the one-to-one mapping constraint
107 | cnf1 = []
108 | for i in range(num_nodes_g1):
109 | clause = variables[i, :].tolist()
110 | cnf1.append(clause)
111 | for k, m in combinations(clause, 2):
112 | cnf1.append([-1 * k, -1 * m])
113 | for j in range(num_nodes_g2):
114 | clause = variables[:, j].tolist()
115 | for k, m in combinations(clause, 2):
116 | cnf1.append([-1 * k, -1 * m])
117 |
118 | # Perform a binary search over the number of swap layers to find the minimum
119 | # number of swap layers that satisfies the subgraph isomorphism problem.
120 | while min_layers < max_layers:
121 | num_layers = (min_layers + max_layers) // 2
122 |
123 | # Create the connectivity matrix. Note that if the swap strategy cannot reach
124 | # full connectivity then its distance matrix will have entries with -1. These
125 | # entries must be treated as False.
126 | d_matrix = swap_strategy.distance_matrix
127 | connectivity_matrix = ((-1 < d_matrix) & (d_matrix <= num_layers)).astype(int)
128 | # Make a cnf for the adjacency constraint
129 | cnf2 = []
130 | for e_0, e_1 in program_graph.edges:
131 | clause_matrix = np.multiply(connectivity_matrix, variables[e_1, :])
132 | clause = np.concatenate(
133 | (
134 | [[-variables[e_0, i]] for i in range(num_nodes_g2)],
135 | clause_matrix,
136 | ),
137 | axis=1,
138 | )
139 | # Remove 0s from each clause
140 | cnf2.extend([c[c != 0].tolist() for c in clause])
141 |
142 | cnf = CNF(from_clauses=cnf1 + cnf2)
143 |
144 | with Solver(bootstrap_with=cnf, use_timer=True) as solver:
145 | # Solve the SAT problem with a timeout.
146 | # Timer is used to interrupt the solver when the timeout is reached.
147 | timer = Timer(self.timeout, interrupt, [solver])
148 | timer.start()
149 | status = solver.solve_limited(expect_interrupt=True)
150 | timer.cancel()
151 | # Get the solution and the elapsed time.
152 | sol = solver.get_model()
153 | e_time = solver.time()
154 |
155 | if status:
156 | # If the SAT problem is satisfiable, convert the solution to a mapping.
157 | mapping = [vid2mapping[idx] for idx in sol if idx > 0]
158 | binary_search_results[num_layers] = SATResult(status, sol, mapping, e_time)
159 | max_layers = num_layers
160 | else:
161 | # If the SAT problem is unsatisfiable, return the last satisfiable solution.
162 | binary_search_results[num_layers] = SATResult(status, sol, [], e_time)
163 | min_layers = num_layers + 1
164 |
165 | return binary_search_results
166 |
167 | def remap_graph_with_sat(
168 | self, graph: nx.Graph, swap_strategy
169 | ) -> tuple[int, dict, list] | tuple[None, None, None]:
170 | """Applies the SAT mapping.
171 |
172 | Args:
173 | graph (nx.Graph): The graph to remap.
174 | swap_strategy (SwapStrategy): The swap strategy to use to find the initial mapping.
175 |
176 | Returns:
177 | tuple: A tuple containing the remapped graph, the edge map, and the number of layers of
178 | the swap strategy that was used to find the initial mapping. If no solution is found
179 | then the tuple contains None for each element.
180 | Note the returned edge map `{k: v}` means that node `k` in the original
181 | graph gets mapped to node `v` in the Pauli strings.
182 | """
183 | num_nodes = len(graph.nodes)
184 | results = self.find_initial_mappings(graph, swap_strategy, 0, num_nodes - 1)
185 | solutions = [k for k, v in results.items() if v.satisfiable]
186 | if len(solutions):
187 | min_k = min(solutions)
188 | edge_map = dict(results[min_k].mapping)
189 | remapped_graph = nx.relabel_nodes(graph, edge_map)
190 | return remapped_graph, edge_map, min_k
191 | else:
192 | return None, None, None
193 |
--------------------------------------------------------------------------------
/qopt_best_practices/swap_strategies/__init__.py:
--------------------------------------------------------------------------------
1 | """SWAP strategies"""
2 |
3 | from .build_circuit import (
4 | create_qaoa_swap_circuit,
5 | make_meas_map,
6 | apply_swap_strategy,
7 | apply_qaoa_layers,
8 | )
9 |
10 | __all__ = [
11 | "create_qaoa_swap_circuit",
12 | "make_meas_map",
13 | "apply_swap_strategy",
14 | "apply_qaoa_layers",
15 | ]
16 |
--------------------------------------------------------------------------------
/qopt_best_practices/swap_strategies/build_circuit.py:
--------------------------------------------------------------------------------
1 | """Circuit utils"""
2 |
3 | from __future__ import annotations
4 |
5 | from qiskit.circuit import QuantumCircuit
6 | from qiskit.quantum_info import SparsePauliOp
7 |
8 | from qiskit.transpiler import PassManager
9 | from qiskit.transpiler.passes.routing.commuting_2q_gate_routing import (
10 | SwapStrategy,
11 | FindCommutingPauliEvolutions,
12 | Commuting2qGateRouter,
13 | )
14 |
15 | from qiskit.circuit.library import QAOAAnsatz
16 | from qiskit.circuit import ParameterVector
17 |
18 |
19 | def make_meas_map(circuit: QuantumCircuit) -> dict:
20 | """Return a mapping from qubit index (the key) to classical bit (the value).
21 |
22 | This allows us to account for the swapping order introduced by the SwapStrategy.
23 | """
24 | creg = circuit.cregs[0]
25 | qreg = circuit.qregs[0]
26 |
27 | meas_map = {}
28 | for inst in circuit.data:
29 | if inst.operation.name == "measure":
30 | meas_map[qreg.index(inst.qubits[0])] = creg.index(inst.clbits[0])
31 |
32 | return meas_map
33 |
34 |
35 | def apply_swap_strategy(
36 | circuit: QuantumCircuit,
37 | swap_strategy: SwapStrategy,
38 | edge_coloring: dict[tuple[int, int], int] | None = None,
39 | ) -> QuantumCircuit:
40 | """Transpile with a SWAP strategy.
41 |
42 | Returns:
43 | A quantum circuit transpiled with the given swap strategy.
44 | """
45 |
46 | pm_pre = PassManager(
47 | [
48 | FindCommutingPauliEvolutions(),
49 | Commuting2qGateRouter(
50 | swap_strategy,
51 | edge_coloring,
52 | ),
53 | ]
54 | )
55 | return pm_pre.run(circuit)
56 |
57 |
58 | def apply_qaoa_layers( # pylint: disable=too-many-arguments,too-many-locals,too-many-positional-arguments
59 | cost_layer: QuantumCircuit,
60 | meas_map: dict,
61 | num_layers: int,
62 | gamma: list[float] | ParameterVector = None,
63 | beta: list[float] | ParameterVector = None,
64 | initial_state: QuantumCircuit = None,
65 | mixer: QuantumCircuit = None,
66 | ):
67 | """Applies QAOA layers to construct circuit.
68 |
69 | First, the initial state is applied. If `initial_state` is None we begin in the
70 | initial superposition state. Next, we alternate between layers of the cot operator
71 | and the mixer. The cost operator is alternatively applied in order and in reverse
72 | instruction order. This allows us to apply the swap-strategy on odd `p` layers
73 | and undo the swap strategy on even `p` layers.
74 | """
75 |
76 | num_qubits = cost_layer.num_qubits
77 | new_circuit = QuantumCircuit(num_qubits, num_qubits)
78 |
79 | if initial_state is not None:
80 | new_circuit.append(initial_state, range(num_qubits))
81 | else:
82 | # all h state by default
83 | new_circuit.h(range(num_qubits))
84 |
85 | if gamma is None or beta is None:
86 | gamma = ParameterVector("γ", num_layers)
87 | if mixer is None or mixer.num_parameters == 0:
88 | beta = ParameterVector("β", num_layers)
89 | else:
90 | beta = ParameterVector("β", num_layers * mixer.num_parameters)
91 |
92 | if mixer is not None:
93 | mixer_layer = mixer
94 | else:
95 | mixer_layer = QuantumCircuit(num_qubits)
96 | mixer_layer.rx(-2 * beta[0], range(num_qubits))
97 |
98 | for layer in range(num_layers):
99 | bind_dict = {cost_layer.parameters[0]: gamma[layer]}
100 | cost_layer_ = cost_layer.assign_parameters(bind_dict)
101 | bind_dict = {
102 | mixer_layer.parameters[i]: beta[layer + i] for i in range(mixer_layer.num_parameters)
103 | }
104 | layer_mixer = mixer_layer.assign_parameters(bind_dict)
105 |
106 | if layer % 2 == 0:
107 | new_circuit.append(cost_layer_, range(num_qubits))
108 | else:
109 | new_circuit.append(cost_layer_.reverse_ops(), range(num_qubits))
110 |
111 | new_circuit.append(layer_mixer, range(num_qubits))
112 |
113 | for qidx, cidx in meas_map.items():
114 | new_circuit.measure(qidx, cidx)
115 |
116 | return new_circuit
117 |
118 |
119 | def create_qaoa_swap_circuit( # pylint: disable=too-many-arguments,too-many-positional-arguments
120 | cost_operator: SparsePauliOp,
121 | swap_strategy: SwapStrategy,
122 | edge_coloring: dict = None,
123 | theta: list[float] = None,
124 | qaoa_layers: int = 1,
125 | initial_state: QuantumCircuit = None,
126 | mixer: QuantumCircuit = None,
127 | ):
128 | """Create the circuit for QAOA.
129 |
130 | Notes: This circuit construction for QAOA works for quadratic terms in `Z` and will be
131 | extended to first-order terms in `Z`. Higher-orders are not supported.
132 |
133 | Args:
134 | cost_operator: the cost operator.
135 | swap_strategy: selected swap strategy
136 | edge_coloring: A coloring of edges that should correspond to the coupling
137 | map of the hardware. It defines the order in which we apply the Rzz
138 | gates. This allows us to choose an ordering such that `Rzz` gates will
139 | immediately precede SWAP gates to leverage CNOT cancellation.
140 | theta: The QAOA angles.
141 | qaoa_layers: The number of layers of the cost-operator and the mixer operator.
142 | initial_state: The initial state on which we apply layers of cost-operator
143 | and mixer.
144 | mixer: The QAOA mixer. It will be applied as is onto the QAOA circuit. Therefore,
145 | its output must have the same ordering of qubits as its input.
146 | """
147 |
148 | num_qubits = cost_operator.num_qubits
149 |
150 | if theta is not None:
151 | gamma = theta[: len(theta) // 2]
152 | beta = theta[len(theta) // 2 :]
153 | qaoa_layers = len(theta) // 2
154 | else:
155 | gamma = beta = None
156 |
157 | # First, create the ansatz of 1 layer of QAOA without mixer
158 | cost_layer = QAOAAnsatz(
159 | cost_operator,
160 | reps=1,
161 | initial_state=QuantumCircuit(num_qubits),
162 | mixer_operator=QuantumCircuit(num_qubits),
163 | ).decompose()
164 |
165 | # This will allow us to recover the permutation of the measurements that the swap introduce.
166 | cost_layer.measure_all()
167 |
168 | # Now, apply the swap strategy for commuting pauli evolution gates
169 | cost_layer = apply_swap_strategy(cost_layer, swap_strategy, edge_coloring)
170 |
171 | # Compute the measurement map (qubit to classical bit).
172 | # we will apply this for qaoa_layers % 2 == 1.
173 | if qaoa_layers % 2 == 1:
174 | meas_map = make_meas_map(cost_layer)
175 | else:
176 | meas_map = {idx: idx for idx in range(num_qubits)}
177 |
178 | cost_layer.remove_final_measurements()
179 |
180 | # Finally, introduce the mixer circuit and add measurements following measurement map
181 | circuit = apply_qaoa_layers(
182 | cost_layer, meas_map, qaoa_layers, gamma, beta, initial_state, mixer
183 | )
184 |
185 | return circuit
186 |
--------------------------------------------------------------------------------
/qopt_best_practices/transpilation/__init__.py:
--------------------------------------------------------------------------------
1 | """Module with transpiler methods for QAOA like circuits."""
2 |
3 | from .preset_qaoa_passmanager import qaoa_swap_strategy_pm
4 |
--------------------------------------------------------------------------------
/qopt_best_practices/transpilation/preset_qaoa_passmanager.py:
--------------------------------------------------------------------------------
1 | """Make a pass manager to transpile QAOA."""
2 |
3 | from typing import Any, Dict
4 |
5 | from qiskit.transpiler import PassManager
6 | from qiskit.transpiler.passes import HighLevelSynthesis, InverseCancellation
7 | from qiskit.transpiler.passes.routing.commuting_2q_gate_routing import (
8 | FindCommutingPauliEvolutions,
9 | Commuting2qGateRouter,
10 | )
11 | from qiskit.circuit.library import CXGate
12 |
13 | from qopt_best_practices.transpilation.qaoa_construction_pass import QAOAConstructionPass
14 | from qopt_best_practices.transpilation.swap_cancellation_pass import SwapToFinalMapping
15 |
16 |
17 | def qaoa_swap_strategy_pm(config: Dict[str, Any]):
18 | """Provide a pass manager to build the QAOA cirucit.
19 |
20 | This function will be extended in the future.
21 | """
22 |
23 | num_layers = config.get("num_layers", 1)
24 | swap_strategy = config.get("swap_strategy", None)
25 | edge_coloring = config.get("edge_coloring", None)
26 | basis_gates = config.get("basis_gates", ["sx", "x", "rz", "cx", "id"])
27 | construct_qaoa = config.get("construct_qaoa", True)
28 |
29 | if swap_strategy is None:
30 | raise ValueError("No swap_strategy provided in config.")
31 |
32 | if edge_coloring is None:
33 | raise ValueError("No edge_coloring provided in config.")
34 |
35 | # 2. define pass manager for cost layer
36 | qaoa_passes = [
37 | HighLevelSynthesis(basis_gates=["PauliEvolution"]),
38 | FindCommutingPauliEvolutions(),
39 | Commuting2qGateRouter(
40 | swap_strategy,
41 | edge_coloring,
42 | ),
43 | SwapToFinalMapping(),
44 | HighLevelSynthesis(basis_gates=basis_gates),
45 | InverseCancellation(gates_to_cancel=[CXGate()]),
46 | ]
47 |
48 | if construct_qaoa:
49 | qaoa_passes.append(QAOAConstructionPass(num_layers))
50 |
51 | return PassManager(qaoa_passes)
52 |
--------------------------------------------------------------------------------
/qopt_best_practices/transpilation/qaoa_construction_pass.py:
--------------------------------------------------------------------------------
1 | """A pass to build a full QAOA ansatz circuit."""
2 |
3 | from typing import Optional
4 |
5 | from qiskit.converters import circuit_to_dag, dag_to_circuit
6 | from qiskit.circuit import QuantumCircuit, ParameterVector, Parameter
7 | from qiskit.dagcircuit import DAGCircuit
8 | from qiskit.transpiler import TranspilerError
9 | from qiskit.transpiler.basepasses import TransformationPass
10 |
11 |
12 | class QAOAConstructionPass(TransformationPass):
13 | """Build the QAOAAnsatz from a transpiled cost operator.
14 |
15 | This pass takes as input a single layer of a transpiled QAOA operator.
16 | It then repeats this layer the appropriate number of time and adds (i)
17 | the initial state, (ii) the mixer operator, and (iii) the measurements.
18 | The measurements are added in such a fashion so as to undo the local
19 | permuttion induced by the SWAP gates in the cost layer.
20 | """
21 |
22 | def __init__(
23 | self,
24 | num_layers: int,
25 | init_state: Optional[QuantumCircuit] = None,
26 | mixer_layer: Optional[QuantumCircuit] = None,
27 | ):
28 | """Initialize the pass
29 |
30 | Limitations: The current implementation of the pass does not permute the mixer.
31 | Therefore mixers with local bias fields, such as in warm-start methods, will not
32 | result in the correct circuits.
33 |
34 | Args:
35 | num_layers: The number of QAOA layers to apply.
36 | init_state: The initial state to use. This must match the anticipated number
37 | of qubits otherwise an error will be raised when `run` is called. If this
38 | is not given we will default to the equal superposition initial state.
39 | mixer_layer: The mixer layer to use. This must match the anticipated number
40 | of qubits otherwise an error will be raised when `run` is called. If this
41 | is not given we default to the standard mixer made of X gates.
42 | """
43 | super().__init__()
44 |
45 | self.num_layers = num_layers
46 | self.init_state = init_state
47 | self.mixer_layer = mixer_layer
48 |
49 | def run(self, cost_layer_dag: DAGCircuit):
50 | num_qubits = cost_layer_dag.num_qubits()
51 |
52 | # Make the initial state and the mixer.
53 | if self.init_state is None:
54 | init_state = QuantumCircuit(num_qubits)
55 | init_state.h(range(num_qubits))
56 | else:
57 | init_state = self.init_state
58 |
59 | if self.mixer_layer is None:
60 | mixer_layer = QuantumCircuit(num_qubits)
61 | beta = Parameter("β")
62 | mixer_layer.rx(2 * beta, range(num_qubits))
63 | else:
64 | mixer_layer = self.mixer_layer
65 |
66 | # Do some sanity checks on qubit numbers.
67 | if init_state.num_qubits != num_qubits:
68 | raise TranspilerError(
69 | "Number of qubits in the initial state does not match the number in the DAG. "
70 | f"{init_state.num_qubits} != {num_qubits}"
71 | )
72 |
73 | if mixer_layer.num_qubits != num_qubits:
74 | raise TranspilerError(
75 | "Number of qubits in the mixer does not match the number in the DAG. "
76 | f"{init_state.num_qubits} != {num_qubits}"
77 | )
78 |
79 | # Note: converting to circuit is inefficent. Update to DAG only.
80 | cost_layer = dag_to_circuit(cost_layer_dag)
81 | qaoa_circuit = QuantumCircuit(num_qubits, num_qubits)
82 |
83 | # Re-parametrize the circuit
84 | gammas = ParameterVector("γ", self.num_layers)
85 | betas = ParameterVector("β", self.num_layers)
86 |
87 | # Add initial state
88 | qaoa_circuit.compose(init_state, inplace=True)
89 |
90 | # iterate over number of qaoa layers
91 | # and alternate cost/reversed cost and mixer
92 | for layer in range(self.num_layers):
93 | bind_dict = {cost_layer.parameters[0]: gammas[layer]}
94 | bound_cost_layer = cost_layer.assign_parameters(bind_dict)
95 |
96 | bind_dict = {mixer_layer.parameters[0]: betas[layer]}
97 | bound_mixer_layer = mixer_layer.assign_parameters(bind_dict)
98 |
99 | if layer % 2 == 0:
100 | # even layer -> append cost
101 | qaoa_circuit.compose(bound_cost_layer, range(num_qubits), inplace=True)
102 | else:
103 | # odd layer -> append reversed cost
104 | qaoa_circuit.compose(
105 | bound_cost_layer.reverse_ops(), range(num_qubits), inplace=True
106 | )
107 |
108 | # the mixer layer is not reversed and not permuted.
109 | qaoa_circuit.compose(bound_mixer_layer, range(num_qubits), inplace=True)
110 |
111 | if self.num_layers % 2 == 1:
112 | # iterate over layout permutations to recover measurements
113 | if self.property_set["virtual_permutation_layout"]:
114 | for cidx, qidx in (
115 | self.property_set["virtual_permutation_layout"].get_physical_bits().items()
116 | ):
117 | qaoa_circuit.measure(qidx, cidx)
118 | else:
119 | print("layout not found, assigining trivial layout")
120 | for idx in range(num_qubits):
121 | qaoa_circuit.measure(idx, idx)
122 | else:
123 | for idx in range(num_qubits):
124 | qaoa_circuit.measure(idx, idx)
125 |
126 | return circuit_to_dag(qaoa_circuit)
127 |
--------------------------------------------------------------------------------
/qopt_best_practices/transpilation/swap_cancellation_pass.py:
--------------------------------------------------------------------------------
1 | """Pass to remove SWAP gates that are not needed."""
2 |
3 | from qiskit.dagcircuit import DAGOutNode, DAGCircuit
4 | from qiskit.transpiler import TransformationPass
5 |
6 |
7 | class SwapToFinalMapping(TransformationPass):
8 | """Absorb any redundent SWAPs in the final layout.
9 |
10 | This pass should be executed after a SWAPStrategy has been applied to a block
11 | of commuting gates. It will remove any final redundent SWAP gates and absorb
12 | them into the virtual layout. This effectively undoes any possibly redundent
13 | SWAP gates that the SWAPStrategy may have inserted.
14 | """
15 |
16 | def run(self, dag: DAGCircuit):
17 | """run the pass."""
18 |
19 | qmap = self.property_set["virtual_permutation_layout"]
20 |
21 | # This will remove SWAP gates that are applied before anything else
22 | # This remove is executed multiple times until there are no more SWAP
23 | # gates left to remove. Note: a more inteligent DAG traversal could
24 | # be implemented here.
25 |
26 | done = False
27 |
28 | while not done:
29 | permuted = False
30 | for node in dag.topological_op_nodes():
31 | if node.op.name == "swap":
32 | successors = list(dag.successors(node))
33 | if len(successors) == 2:
34 | if all(isinstance(successors[idx], DAGOutNode) for idx in [0, 1]):
35 | qmap.swap(node.qargs[0], node.qargs[1])
36 | dag.remove_op_node(node)
37 | permuted = True
38 |
39 | done = not permuted
40 |
41 | return dag
42 |
--------------------------------------------------------------------------------
/qopt_best_practices/utils/__init__.py:
--------------------------------------------------------------------------------
1 | """Utils"""
2 |
3 |
4 | from .graph_utils import build_max_cut_graph, build_max_cut_paulis
5 |
6 | __all__ = ["build_max_cut_graph", "build_max_cut_paulis"]
7 |
--------------------------------------------------------------------------------
/qopt_best_practices/utils/graph_utils.py:
--------------------------------------------------------------------------------
1 | """Graph utils"""
2 |
3 | from __future__ import annotations
4 | import networkx as nx
5 |
6 |
7 | def build_max_cut_graph(paulis: list[tuple[str, float]]) -> nx.Graph:
8 | """Create a graph by parsing the pauli strings.
9 |
10 | Args:
11 | paulis: A list of Paulis given as tuple of Pauli string and
12 | coefficient. E.g., `[("IZZI", 1.0), ("ZIZI", 1.0)]`. Each
13 | pauli is guaranteed to have two Z's.
14 |
15 | Returns:
16 | A networkx graph.
17 | """
18 | graph = nx.Graph()
19 |
20 | for pauli_str, weight in paulis:
21 | edge = [idx for idx, char in enumerate(pauli_str[::-1]) if char == "Z"]
22 | graph.add_edge(edge[0], edge[1], weight=weight)
23 |
24 | return graph
25 |
26 |
27 | def build_max_cut_paulis(graph: nx.Graph) -> list[tuple[str, float]]:
28 | """Convert the graph to Pauli list.
29 |
30 | This function does the inverse of `build_max_cut_graph`
31 | """
32 | pauli_list = []
33 | for edge in graph.edges():
34 | paulis = ["I"] * len(graph)
35 | paulis[edge[0]], paulis[edge[1]] = "Z", "Z"
36 |
37 | weight = graph.get_edge_data(edge[0], edge[1]).get("weight", 1.0)
38 |
39 | pauli_list.append(("".join(paulis)[::-1], weight))
40 |
41 | return pauli_list
42 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | coverage>=4.4.0,<7.0
2 | black[jupyter]~=22.0
3 | pylint>=2.15.0
4 | ddt>=1.2.0,!=1.4.0,!=1.4.3
5 | stestr>=2.0.0
6 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | python-sat
2 | networkx
3 | qiskit>=1.0
4 | qiskit-ibm-runtime
5 |
--------------------------------------------------------------------------------
/run/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/run/__init__.py
--------------------------------------------------------------------------------
/run/data/hardware_native_127.json:
--------------------------------------------------------------------------------
1 | {"paulis": [["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZI", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIZIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIZIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIZ", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIZIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIIIZIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIZIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["ZIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIIIIZIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1], ["IIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["IZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", -1], ["ZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 1]]}
--------------------------------------------------------------------------------
/run/hw_native_test_1_circ.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | file = "data/hardware_native_127.json"
4 | data = json.load(open(file, "r"))
5 | paulis = data["paulis"]
6 | num_qubits = len(paulis[0][0])
7 | print(num_qubits)
8 |
9 | from qiskit.quantum_info import SparsePauliOp
10 |
11 | # define a qiskit SparsePauliOp from the list of paulis
12 | hamiltonian = SparsePauliOp.from_list(paulis)
13 | print(hamiltonian)
14 |
15 | from qiskit.circuit.library import QAOAAnsatz
16 |
17 | qaoa_circ = QAOAAnsatz(hamiltonian, reps=3)
18 | qaoa_circ.measure_all()
19 | print(qaoa_circ.num_parameters)
20 |
21 | from qiskit import transpile
22 |
23 | basis_gates = ["rz", "sx", "x", "ecr"]
24 | # Now transpile to sx, rz, x, cx basis
25 | qaoa_circ = transpile(qaoa_circ, basis_gates=basis_gates)
26 | print("transpilation done")
27 |
28 | from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
29 |
30 | service = QiskitRuntimeService(channel="ibm_quantum")
31 | backend = service.get_backend("ibm_nazca")
32 | options = Options()
33 | options.transpilation.skip_transpilation = True
34 | options.execution.shots = 100000
35 |
36 | sampler = Sampler(backend=backend, options=options)
37 |
38 | import numpy as np
39 |
40 | # TQA initialization parameters
41 | dt = 0.75
42 | p = 3 # 3 qaoa layers
43 | grid = np.arange(1, p + 1) - 0.5
44 | init_params = np.concatenate((1 - grid * dt / p, grid * dt / p))
45 | print(init_params)
46 |
47 | job = sampler.run(qaoa_circ, init_params)
48 |
49 | print(job.job_id())
50 | print(job.result())
51 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | import setuptools
3 |
4 | long_description = """Repository for best practices in quantum optimization."""
5 |
6 | with open("requirements.txt") as f:
7 | REQUIREMENTS = f.read().splitlines()
8 |
9 | VERSION_PATH = os.path.join(os.path.dirname(__file__), "qopt_best_practices", "VERSION.txt")
10 | with open(VERSION_PATH, "r") as version_file:
11 | VERSION = version_file.read().strip()
12 |
13 | setuptools.setup(
14 | name="qopt_best_practices",
15 | version=VERSION,
16 | description="Best practices quantum opt.",
17 | long_description=long_description,
18 | long_description_content_type="text/markdown",
19 | url="https://github.com/ElePT/q-optimization-best-practices",
20 | author="Quantum optimization working group",
21 | license="Apache 2.0",
22 | classifiers=[
23 | "Environment :: Console",
24 | "License :: OSI Approved :: Apache Software License",
25 | "Intended Audience :: Developers",
26 | "Intended Audience :: Science/Research",
27 | "Operating System :: Microsoft :: Windows",
28 | "Operating System :: MacOS",
29 | "Operating System :: POSIX :: Linux",
30 | "Programming Language :: Python :: 3 :: Only",
31 | "Programming Language :: Python :: 3.6",
32 | "Programming Language :: Python :: 3.7",
33 | "Programming Language :: Python :: 3.8",
34 | "Programming Language :: Python :: 3.9",
35 | "Topic :: Scientific/Engineering",
36 | ],
37 | keywords="qiskit quantum optimization",
38 | packages=setuptools.find_packages(include=["qopt_best_practices", "qopt_best_practices.*"]),
39 | install_requires=REQUIREMENTS,
40 | include_package_data=True,
41 | python_requires=">=3.7",
42 | zip_safe=False,
43 | )
44 |
--------------------------------------------------------------------------------
/stestr.conf:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | test_path=./test
--------------------------------------------------------------------------------
/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qiskit-community/qopt-best-practices/4e4ee2859ce95f75d5c84dad991b949ac06caa45/test/__init__.py
--------------------------------------------------------------------------------
/test/data/graph_2layers_0seed.json:
--------------------------------------------------------------------------------
1 | {"Original graph": [[0, 7], [0, 3], [0, 5], [7, 9], [7, 8], [1, 2], [1, 4], [1, 6], [2, 3], [2, 8], [5, 8], [5, 3], [4, 9], [4, 6], [9, 6]], "SAT mapping": {"0": 6, "1": 2, "2": 7, "3": 8, "4": 1, "5": 9, "6": 3, "7": 5, "8": 4, "9": 0}, "paulis": [["IIIIIZZIII", 1.0], ["IIIIIIZIZI", 1.0], ["IIIIIIZIIZ", 1.0], ["ZIIIIZIIII", 1.0], ["IIIIZZIIII", 1.0], ["IIZIIIIZII", 1.0], ["IZZIIIIIII", 1.0], ["IIZZIIIIII", 1.0], ["IIIIIIIZZI", 1.0], ["IIIIZIIZII", 1.0], ["IIIIZIIIIZ", 1.0], ["IIIIIIIIZZ", 1.0], ["ZZIIIIIIII", 1.0], ["IZIZIIIIII", 1.0], ["ZIIZIIIIII", 1.0]], "description": "paulis are remapped with `SAT mapping`.", "min swap layers": 2, "nx seed": 0}
--------------------------------------------------------------------------------
/test/test_cost_function.py:
--------------------------------------------------------------------------------
1 | """Tests for Cost Function Utils"""
2 |
--------------------------------------------------------------------------------
/test/test_graph_utils.py:
--------------------------------------------------------------------------------
1 | """Tests for Graph Utils"""
2 |
3 | from unittest import TestCase
4 | import networkx as nx
5 |
6 | from qopt_best_practices.utils import build_max_cut_graph, build_max_cut_paulis
7 |
8 |
9 | class TestGraphRoundTrip(TestCase):
10 | """Test that we can convert between graph and Paulis."""
11 |
12 | @staticmethod
13 | def _test_edge_equality(graph_1: nx.Graph, graph_2: nx.Graph):
14 | """Test equality of edges."""
15 | if len(graph_1.edges) != len(graph_2.edges):
16 | return False
17 |
18 | g_set = set(graph_1.edges)
19 | for edge in graph_2.edges:
20 | if edge not in g_set and edge[::-1] not in g_set:
21 | return False
22 |
23 | return True
24 |
25 | def test_round_trip(self):
26 | """Test that we can easily round-trip Pauli the graphs."""
27 |
28 | for seed in range(5):
29 | graph1 = nx.random_regular_graph(3, 10, seed=seed)
30 | graph2 = build_max_cut_graph(build_max_cut_paulis(graph1))
31 |
32 | self.assertTrue(self._test_edge_equality(graph1, graph2))
33 |
34 | def test_weighted_graph(self):
35 | """Test the construction of a weighted graph."""
36 |
37 | graph = build_max_cut_graph([("IIZZ", 1), ("IZZI", -1), ("ZIZI", 1)])
38 |
39 | self.assertEqual(graph.get_edge_data(0, 1)["weight"], 1)
40 | self.assertEqual(graph.get_edge_data(1, 2)["weight"], -1)
41 | self.assertEqual(graph.get_edge_data(1, 3)["weight"], 1)
42 |
--------------------------------------------------------------------------------
/test/test_qaoa_construction.py:
--------------------------------------------------------------------------------
1 | """Test the construction of the QAOA ansatz."""
2 |
3 |
4 | from unittest import TestCase
5 |
6 | from qiskit import QuantumCircuit, transpile
7 | from qiskit.circuit import Parameter
8 | from qiskit.circuit.library import QAOAAnsatz
9 | from qiskit.primitives import StatevectorEstimator
10 | from qiskit.quantum_info import SparsePauliOp
11 | from qiskit.transpiler import PassManager
12 | from qiskit.transpiler.passes import HighLevelSynthesis
13 | from qiskit.transpiler.passes.routing.commuting_2q_gate_routing import (
14 | SwapStrategy,
15 | FindCommutingPauliEvolutions,
16 | Commuting2qGateRouter,
17 | )
18 |
19 | from qopt_best_practices.transpilation.qaoa_construction_pass import QAOAConstructionPass
20 | from qopt_best_practices.transpilation.preset_qaoa_passmanager import qaoa_swap_strategy_pm
21 | from qopt_best_practices.transpilation.swap_cancellation_pass import SwapToFinalMapping
22 |
23 |
24 | class TestQAOAConstruction(TestCase):
25 | """Test the construction of the QAOA ansatz."""
26 |
27 | def setUp(self):
28 | """Set up re-used variables."""
29 | self.estimator = StatevectorEstimator()
30 |
31 | gamma = Parameter("γ")
32 | cost_op = QuantumCircuit(4)
33 | cost_op.rzz(2 * gamma, 0, 1)
34 | cost_op.rzz(2 * gamma, 2, 3)
35 | cost_op.swap(0, 1)
36 | cost_op.swap(2, 3)
37 | cost_op.rzz(2 * gamma, 1, 2)
38 |
39 | self.cost_op_circ = transpile(cost_op, basis_gates=["sx", "cx", "x", "rz"])
40 |
41 | self.cost_op = SparsePauliOp.from_list([("IIZZ", 1), ("ZZII", 1), ("ZIIZ", 1)])
42 |
43 | self.config = {
44 | "swap_strategy": SwapStrategy.from_line(list(range(4))),
45 | "edge_coloring": {(idx, idx + 1): (idx + 1) % 2 for idx in range(4)},
46 | }
47 |
48 | def test_depth_one(self):
49 | """Compare the pass with the SWAPs and ensure the measurements are ordered properly."""
50 | qaoa_pm = qaoa_swap_strategy_pm(self.config)
51 |
52 | cost_op_circ = QAOAAnsatz(
53 | self.cost_op, initial_state=QuantumCircuit(4), mixer_operator=QuantumCircuit(4)
54 | ).decompose(reps=1)
55 |
56 | ansatz = qaoa_pm.run(cost_op_circ)
57 |
58 | # 1. Check the measurement map
59 | qreg = ansatz.qregs[0]
60 | creg = ansatz.cregs[0]
61 |
62 | expected_meas_map = {0: 1, 1: 0, 2: 3, 3: 2}
63 |
64 | for inst in ansatz.data:
65 | if inst.operation.name == "measure":
66 | qubit = qreg.index(inst.qubits[0])
67 | cbit = creg.index(inst.clbits[0])
68 | self.assertEqual(cbit, expected_meas_map[qubit])
69 |
70 | # 2. Check the expectation value. Note that to use the estimator we need to
71 | # Remove the final measurements and correspondingly permute the cost op.
72 | ansatz.remove_final_measurements(inplace=True)
73 | permuted_cost_op = SparsePauliOp.from_list([("IIZZ", 1), ("ZZII", 1), ("IZZI", 1)])
74 | value = self.estimator.run([(ansatz, permuted_cost_op, [1, 2])]).result()[0].data.evs
75 |
76 | library_ansatz = QAOAAnsatz(self.cost_op, reps=1)
77 | library_ansatz = transpile(library_ansatz, basis_gates=["cx", "rz", "rx", "h"])
78 |
79 | expected = self.estimator.run([(library_ansatz, self.cost_op, [1, 2])]).result()[0].data.evs
80 |
81 | self.assertAlmostEqual(value, expected)
82 |
83 | def test_depth_two_qaoa_pass(self):
84 | """Compare the pass with the SWAPs to an all-to-all construction.
85 |
86 | Note: this test only works as is because p is even and we don't have the previous
87 | passes to give us the qubit permutations.
88 | """
89 | qaoa_pm = PassManager([QAOAConstructionPass(num_layers=2)])
90 |
91 | ansatz = qaoa_pm.run(self.cost_op_circ)
92 | ansatz.remove_final_measurements(inplace=True)
93 |
94 | value = self.estimator.run([(ansatz, self.cost_op, [1, 2, 3, 4])]).result()[0].data.evs
95 |
96 | library_ansatz = QAOAAnsatz(self.cost_op, reps=2)
97 | library_ansatz = transpile(library_ansatz, basis_gates=["cx", "rz", "rx", "h"])
98 |
99 | expected = (
100 | self.estimator.run([(library_ansatz, self.cost_op, [1, 2, 3, 4])]).result()[0].data.evs
101 | )
102 |
103 | self.assertAlmostEqual(value, expected)
104 |
105 | def test_swap_construction(self):
106 | """Test that redundent SWAP gates are removed."""
107 | cost_op = SparsePauliOp.from_list(
108 | [("IIIIZZ", 1), ("IIZZII", 1), ("ZZIIII", 1), ("IIZIIZ", 1)],
109 | )
110 |
111 | ansatz = QAOAAnsatz(
112 | cost_op, reps=1, initial_state=QuantumCircuit(6), mixer_operator=QuantumCircuit(6)
113 | )
114 |
115 | # Test with the SWAP removal
116 | qaoa_pm = PassManager(
117 | [
118 | HighLevelSynthesis(basis_gates=["PauliEvolution"]),
119 | FindCommutingPauliEvolutions(),
120 | Commuting2qGateRouter(SwapStrategy.from_line(range(6))),
121 | SwapToFinalMapping(),
122 | ]
123 | )
124 |
125 | self.assertEqual(qaoa_pm.run(ansatz).count_ops()["swap"], 2)
126 |
127 | # Test without the SWAP removal
128 | qaoa_pm = PassManager(
129 | [
130 | HighLevelSynthesis(basis_gates=["PauliEvolution"]),
131 | FindCommutingPauliEvolutions(),
132 | Commuting2qGateRouter(SwapStrategy.from_line(range(6))),
133 | ]
134 | )
135 |
136 | self.assertEqual(qaoa_pm.run(ansatz).count_ops()["swap"], 3)
137 |
--------------------------------------------------------------------------------
/test/test_qubit_selection.py:
--------------------------------------------------------------------------------
1 | """Tests for Qubit Selection Utils"""
2 |
3 | import json
4 | import os
5 | from unittest import TestCase
6 |
7 | from qiskit_ibm_runtime.fake_provider import FakeSherbrooke, FakeHanoiV2
8 | from qiskit.providers.fake_provider import GenericBackendV2
9 |
10 |
11 | from qopt_best_practices.utils import build_max_cut_graph
12 | from qopt_best_practices.qubit_selection import BackendEvaluator, find_lines
13 |
14 |
15 | class TestQubitSelection(TestCase):
16 | """Unit test for QAOA workflow."""
17 |
18 | def setUp(self):
19 | super().setUp()
20 |
21 | # load data
22 | graph_file = os.path.join(os.path.dirname(__file__), "data/graph_2layers_0seed.json")
23 | with open(graph_file, "r") as file:
24 | data = json.load(file)
25 |
26 | # 10 qubit graph
27 | self.mapped_paulis = [tuple(pauli) for pauli in data["paulis"]]
28 | self.mapped_graph = build_max_cut_graph(self.mapped_paulis)
29 | self.backend = FakeSherbrooke()
30 |
31 | def test_find_lines(self):
32 | """Test backend evaluation"""
33 | paths = find_lines(len(self.mapped_graph), self.backend)
34 |
35 | self.assertEqual(len(paths), 1336)
36 | self.assertEqual(len(paths[0]), len(self.mapped_graph))
37 | self.assertIsInstance(paths[0][0], int)
38 |
39 | def test_find_lines_directed(self):
40 | """Test backend with directed (asymmetric) coupling map"""
41 | directed_fake_backend = GenericBackendV2(4, coupling_map=[[0, 1], [1, 2], [3, 2], [3, 0]])
42 | lines = find_lines(3, backend=directed_fake_backend)
43 |
44 | expected_lines = [[0, 1, 2], [0, 3, 2], [1, 2, 3], [1, 0, 3]]
45 | self.assertEqual(set(tuple(i) for i in lines), set(tuple(i) for i in expected_lines))
46 |
47 | def test_qubit_selection(self):
48 | """Test backend evaluation"""
49 | path_finder = BackendEvaluator(self.backend)
50 | path, _, _ = path_finder.evaluate(len(self.mapped_graph))
51 |
52 | expected_path = [33, 39, 40, 72, 41, 81, 53, 60, 61, 62]
53 | self.assertEqual(set(path), set(expected_path))
54 |
55 | def test_qubit_selection_v2(self):
56 | """Test backend evaluation for 10 qubit line"""
57 | path_finder = BackendEvaluator(FakeHanoiV2())
58 | path, _, _ = path_finder.evaluate(len(self.mapped_graph))
59 | expected_path = [8, 9, 11, 12, 13, 14, 15, 18, 21, 23]
60 | self.assertEqual(set(path), set(expected_path))
61 |
--------------------------------------------------------------------------------
/test/test_sat_mapping.py:
--------------------------------------------------------------------------------
1 | """Tests for SAT Mapping Utils"""
2 |
3 | from unittest import TestCase
4 | import json
5 | import os
6 | import networkx as nx
7 |
8 | from qiskit.transpiler import CouplingMap
9 | from qiskit.transpiler.passes.routing.commuting_2q_gate_routing import SwapStrategy
10 |
11 | from qopt_best_practices.utils import build_max_cut_graph, build_max_cut_paulis
12 | from qopt_best_practices.sat_mapping import SATMapper
13 |
14 |
15 | class TestSwapStrategies(TestCase):
16 | """Unit test for SWAP strategies functionality."""
17 |
18 | def setUp(self):
19 | super().setUp()
20 |
21 | # load data
22 | graph_file = os.path.join(os.path.dirname(__file__), "data/graph_2layers_0seed.json")
23 |
24 | with open(graph_file, "r") as file:
25 | data = json.load(file)
26 |
27 | self.original_graph = nx.from_edgelist(data["Original graph"])
28 | self.original_paulis = build_max_cut_paulis(self.original_graph)
29 |
30 | self.mapped_paulis = [tuple(pauli) for pauli in data["paulis"]]
31 | self.mapped_graph = build_max_cut_graph(self.mapped_paulis)
32 |
33 | self.sat_mapping = {int(key): value for key, value in data["SAT mapping"].items()}
34 | self.min_k = data["min swap layers"]
35 | self.swap_strategy = SwapStrategy.from_line(list(range(len(self.original_graph.nodes))))
36 | self.basic_graphs = [nx.path_graph(5), nx.cycle_graph(7)]
37 |
38 | def test_find_initial_mappings(self):
39 | """Test find_initial_mappings"""
40 |
41 | mapper = SATMapper()
42 |
43 | results = mapper.find_initial_mappings(self.original_graph, self.swap_strategy)
44 | min_k = min((k for k, v in results.items() if v.satisfiable))
45 | # edge_map = dict(results[min_k].mapping)
46 |
47 | # edge maps are not equal, but same min_k
48 | self.assertEqual(min_k, self.min_k)
49 |
50 | # Find better test
51 | # self.assertEqual(edge_map, self.sat_mapping)
52 |
53 | def test_remap_graph_with_sat(self):
54 | """Test remap_graph_with_sat"""
55 |
56 | mapper = SATMapper()
57 |
58 | remapped_g, _, _ = mapper.remap_graph_with_sat(
59 | graph=self.original_graph, swap_strategy=self.swap_strategy
60 | )
61 |
62 | self.assertTrue(nx.is_isomorphic(remapped_g, self.mapped_graph))
63 |
64 | def test_deficient_strategy(self):
65 | """Test that the SAT mapper works when the SWAP strategy is deficient.
66 |
67 | Note: a deficient strategy does not result in full connectivity but
68 | may still be useful.
69 | """
70 | cmap = CouplingMap([(idx, idx + 1) for idx in range(10)])
71 |
72 | # This swap strategy is deficient but can route the graph below.
73 | swaps = (
74 | ((0, 1), (2, 3), (4, 5), (6, 7), (8, 9)),
75 | ((1, 2), (3, 4), (5, 6), (7, 8)),
76 | ((2, 3), (4, 5), (6, 7), (8, 9)),
77 | (),
78 | (),
79 | (),
80 | (),
81 | (),
82 | )
83 | swap_strategy = SwapStrategy(cmap, swaps)
84 | graph = nx.random_regular_graph(3, 10, seed=2)
85 |
86 | mapper = SATMapper()
87 |
88 | _, permutation, min_layer = mapper.remap_graph_with_sat(graph, swap_strategy)
89 |
90 | # Spot check a few permutations.
91 | self.assertEqual(permutation[0], 9)
92 | self.assertEqual(permutation[8], 1)
93 |
94 | # Crucially, if the `connectivity_matrix` in `find_initial_mappings` we get a wrong result.
95 | self.assertEqual(min_layer, 3)
96 |
97 | def test_full_connectivity(self):
98 | """Test that the SAT mapper works when the SWAP strategy has full connectivity."""
99 | graph = nx.random_regular_graph(3, 6, seed=1)
100 | swap_strategy = SwapStrategy.from_line(list(range(6)))
101 | sat_mapper = SATMapper()
102 | _, _, min_sat_layers = sat_mapper.remap_graph_with_sat(
103 | graph=graph,
104 | swap_strategy=swap_strategy,
105 | )
106 | self.assertEqual(min_sat_layers, 4)
107 |
108 | def test_unable_to_remap(self):
109 | """Test that the SAT mapper works when the SWAP strategy is unable to remap."""
110 | graph = nx.random_regular_graph(3, 6, seed=1)
111 | cmap = CouplingMap([(idx, idx + 1) for idx in range(5)])
112 | swap_strategy = SwapStrategy(cmap, [])
113 | sat_mapper = SATMapper()
114 | remapped_g, edge_map, min_sat_layers = sat_mapper.remap_graph_with_sat(
115 | graph=graph,
116 | swap_strategy=swap_strategy,
117 | )
118 | self.assertIsNone(remapped_g)
119 | self.assertIsNone(edge_map)
120 | self.assertIsNone(min_sat_layers)
121 |
--------------------------------------------------------------------------------
/test/test_swap_cancellation.py:
--------------------------------------------------------------------------------
1 | """The the cancellation of unneeded SWAP gates."""
2 |
3 |
4 | from unittest import TestCase
5 |
6 | from qiskit import QuantumCircuit
7 | from qiskit.converters import circuit_to_dag
8 | from qiskit.transpiler import Layout
9 |
10 | from qopt_best_practices.transpilation.swap_cancellation_pass import SwapToFinalMapping
11 |
12 |
13 | class TestSwapCancellation(TestCase):
14 | """Test the swap cancellation pass."""
15 |
16 | def test_simple(self):
17 | """Simple test."""
18 |
19 | qc = QuantumCircuit(4, 4)
20 | qc.swap(3, 2)
21 | qc.rzz(1.234, 1, 2)
22 | qc.swap(1, 2)
23 |
24 | qreg = next(iter(qc.qregs))
25 |
26 | swap_pass = SwapToFinalMapping()
27 | layout = Layout(
28 | {
29 | 0: qreg[0],
30 | 1: qreg[2],
31 | 2: qreg[3],
32 | 3: qreg[1],
33 | }
34 | )
35 | swap_pass.property_set["virtual_permutation_layout"] = layout
36 |
37 | dag = circuit_to_dag(qc)
38 | _ = swap_pass.run(dag)
39 |
40 | new_layout = swap_pass.property_set["virtual_permutation_layout"]
41 |
42 | self.assertTrue(new_layout[0] == qreg[0])
43 | self.assertTrue(new_layout[1] == qreg[1])
44 | self.assertTrue(new_layout[2] == qreg[3])
45 | self.assertTrue(new_layout[3] == qreg[2])
46 |
--------------------------------------------------------------------------------
/test/test_swap_strategies.py:
--------------------------------------------------------------------------------
1 | """Tests for Swap Strategies"""
2 |
3 | from unittest import TestCase
4 | import json
5 | import os
6 |
7 | from qiskit.transpiler.passes.routing.commuting_2q_gate_routing import SwapStrategy
8 | from qiskit.quantum_info import SparsePauliOp
9 |
10 | from qopt_best_practices.swap_strategies import create_qaoa_swap_circuit
11 |
12 |
13 | class TestSwapStrategies(TestCase):
14 | """Unit test for SWAP strategies functionality."""
15 |
16 | def setUp(self):
17 | super().setUp()
18 |
19 | # load data
20 | graph_file = os.path.join(os.path.dirname(__file__), "data/graph_2layers_0seed.json")
21 |
22 | with open(graph_file, "r") as file:
23 | data = json.load(file)
24 |
25 | self.mapped_paulis = [tuple(pauli) for pauli in data["paulis"]]
26 |
27 | self.hamiltonian = SparsePauliOp.from_list(self.mapped_paulis)
28 |
29 | self.swap_strategy = SwapStrategy.from_line(list(range(self.hamiltonian.num_qubits)))
30 |
31 | def test_qaoa_circuit(self):
32 | """Test building the QAOA circuit."""
33 |
34 | edge_coloring = {
35 | (idx, idx + 1): (idx + 1) % 2 for idx in range(self.hamiltonian.num_qubits)
36 | }
37 |
38 | for layers in range(1, 6):
39 | qaoa_circ = create_qaoa_swap_circuit(
40 | self.hamiltonian, self.swap_strategy, edge_coloring, qaoa_layers=layers
41 | )
42 |
43 | self.assertEqual(len(qaoa_circ.parameters), layers * 2)
44 |
45 | # TODO: expand tests
46 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | # Sets this min.version because of differences with env_tmp_dir env.
3 | minversion = 4.0.2
4 | envlist = py38, py39, py310, py311, lint
5 | skipsdist = True
6 |
7 | [testenv]
8 | usedevelop = True
9 | install_command = pip install -c constraints.txt -U {opts} {packages}
10 | passenv = *
11 | setenv =
12 | VIRTUAL_ENV={envdir}
13 | LANGUAGE=en_US
14 | LC_ALL=en_US.utf-8
15 | ARGS="-V"
16 | deps = git+https://github.com/Qiskit/qiskit.git
17 | -r{toxinidir}/requirements.txt
18 | -r{toxinidir}/requirements-dev.txt
19 | commands =
20 | stestr run {posargs}
21 |
22 | [testenv:lint]
23 | envdir = .tox/lint
24 | basepython = python3
25 | commands =
26 | black --check {posargs} qopt_best_practices test
27 | pylint -rn qopt_best_practices test
28 |
29 | [testenv:black]
30 | envdir = .tox/lint
31 | commands = black {posargs} qopt_best_practices test
32 |
33 | [testenv:coverage]
34 | basepython = python3
35 | setenv =
36 | {[testenv]setenv}
37 | PYTHON=coverage3 run --source qopt_best_practices --parallel-mode
38 | commands =
39 | stestr run {posargs}
40 | coverage3 combine
41 | coverage3 report
42 |
--------------------------------------------------------------------------------