├── .circleci └── config.yml ├── .coveragerc ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── README.rst ├── docs ├── Makefile ├── README.rst ├── _images │ ├── ChimeraUnitCell.png │ ├── Embedding_Chimera_AND.png │ └── chimera.png ├── _static │ ├── cookie_notice.css │ └── cookie_notice.js ├── api_ref.rst ├── composites.rst ├── conf.py ├── embedding.rst ├── index.rst ├── make.bat ├── requirements.txt ├── samplers.rst ├── utilities.rst └── warnings.rst ├── dwave ├── embedding │ ├── __init__.py │ ├── chain_breaks.py │ ├── chain_strength.py │ ├── chimera.py │ ├── diagnostic.py │ ├── drawing.py │ ├── exceptions.py │ ├── pegasus.py │ ├── polynomialembedder.py │ ├── transforms.py │ ├── utils.py │ └── zephyr.py └── system │ ├── __init__.py │ ├── composites │ ├── __init__.py │ ├── cutoffcomposite.py │ ├── embedding.py │ ├── linear_ancilla.py │ ├── parallel_embeddings.py │ ├── reversecomposite.py │ ├── tiling.py │ └── virtual_graph.py │ ├── coupling_groups.py │ ├── exceptions.py │ ├── flux_bias_offsets.py │ ├── package_info.py │ ├── samplers │ ├── __init__.py │ ├── clique.py │ ├── dwave_sampler.py │ └── leap_hybrid_sampler.py │ ├── schedules.py │ ├── temperatures.py │ ├── testing.py │ ├── utilities.py │ └── warnings.py ├── releasenotes └── notes │ └── add-linear-ancilla-composite-3281ed6733b0f0c7.yaml ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── qpu ├── __init__.py ├── test_dwavecliquesampler.py ├── test_dwavesampler.py ├── test_embeddingcomposite.py ├── test_leaphybrid_common.py ├── test_leaphybridcqmsampler.py ├── test_leaphybriddqmsampler.py ├── test_leaphybridsampler.py ├── test_schedules.py └── test_virtual_graph_composite.py ├── requirements.txt ├── test_composites_common.py ├── test_cutoffcomposite.py ├── test_dwavecliquesampler.py ├── test_dwavesampler.py ├── test_embedding_chain_breaks.py ├── test_embedding_chain_strength.py ├── test_embedding_composite.py ├── test_embedding_diagnostic.py ├── test_embedding_transforms.py ├── test_embedding_utils.py ├── test_leaphybridcqmsampler.py ├── test_leaphybriddqmsolver.py ├── test_leaphybridnlsampler.py ├── test_leaphybridsolver.py ├── test_linear_ancilla_composite.py ├── test_mock_sampler.py ├── test_parallel_embedding_composite.py ├── test_polycutoffcomposite.py ├── test_reverse_composite.py ├── test_schedules.py ├── test_temperatures.py ├── test_tiling_composite.py ├── test_utilities.py └── test_virtual_graph_composite.py /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | parameters: 4 | cache-generation: 5 | type: integer 6 | default: 2 7 | 8 | orbs: 9 | win: circleci/windows@5.0 10 | codecov: codecov/codecov@3 11 | 12 | environment: 13 | PIP_PROGRESS_BAR: 'off' 14 | 15 | jobs: 16 | test-linux: 17 | parameters: 18 | python-version: 19 | type: string 20 | dependency-specifiers: 21 | type: string 22 | integration-test-python-version: 23 | type: string 24 | 25 | docker: 26 | - image: python:<< parameters.python-version >> 27 | 28 | steps: 29 | - checkout 30 | 31 | - run: 32 | name: create virtual env and install dependencies 33 | command: | 34 | python -V 35 | python -m venv env 36 | . env/bin/activate 37 | pip install -U pip setuptools 38 | pip install -r requirements.txt -r tests/requirements.txt 39 | 40 | - run: &run-python-tests 41 | name: run unittests 42 | command: | 43 | . env/bin/activate 44 | if [[ << parameters.integration-test-python-version >> != << parameters.python-version >> ]]; then 45 | export SKIP_INT_TESTS=1 46 | fi 47 | coverage run -m unittest discover 48 | coverage xml 49 | 50 | - run: 51 | name: create virtual env and install latest dependencies 52 | command: | 53 | rm -rf env/ 54 | python -V 55 | python -m venv env 56 | . env/bin/activate 57 | pip install -U pip setuptools 58 | pip install -r tests/requirements.txt 59 | pip install -e . 60 | 61 | - when: 62 | condition: << parameters.dependency-specifiers >> 63 | steps: 64 | - run: 65 | name: sweep dependencies 66 | command: | 67 | . env/bin/activate 68 | pip install << parameters.dependency-specifiers >> 69 | 70 | - run: *run-python-tests 71 | 72 | - codecov/upload: &upload-python-code-coverage 73 | file: coverage.xml 74 | 75 | test-doctest: 76 | docker: 77 | - image: python:3.12 78 | 79 | steps: 80 | - checkout 81 | 82 | - run: 83 | name: create virtual env and install dependencies 84 | command: | 85 | python -m venv env 86 | . env/bin/activate 87 | pip install -r requirements.txt -r docs/requirements.txt 88 | 89 | - run: 90 | name: doctest 91 | command: | 92 | . env/bin/activate 93 | make -C docs/ html doctest 94 | 95 | - run: 96 | name: linkcheck 97 | command: | 98 | . env/bin/activate 99 | make -C docs/ linkcheck 100 | 101 | test-osx: 102 | parameters: 103 | python-version: 104 | type: string 105 | integration-test-python-version: 106 | type: string 107 | xcode: 108 | type: string 109 | default: "16.0.0" 110 | 111 | macos: 112 | xcode: << parameters.xcode >> 113 | 114 | steps: 115 | - checkout 116 | 117 | - restore_cache: &restore-cache-pyenv 118 | key: &brew-pyenv-cache-key v<< pipeline.parameters.cache-generation >>-pyenv-{{ .Environment.CIRCLE_JOB }}-xcode-<< parameters.xcode >> 119 | 120 | - run: 121 | name: install pyenv 122 | command: | 123 | brew install pyenv 124 | 125 | - run: 126 | name: install python 127 | command: | 128 | pyenv install << parameters.python-version>> -s 129 | 130 | - save_cache: &save-cache-pyenv 131 | key: *brew-pyenv-cache-key 132 | paths: 133 | - /Users/distiller/Library/Caches/Homebrew 134 | - /usr/local/Homebrew 135 | - ~/.pyenv 136 | 137 | - run: 138 | name: create virtual env and install dependencies 139 | command: | 140 | eval "$(pyenv init --path 2>/dev/null || true)" 141 | eval "$(pyenv init -)" 142 | pyenv shell << parameters.python-version >> 143 | python -V 144 | python -m venv env 145 | . env/bin/activate 146 | pip install -U pip 147 | pip install -r requirements.txt -r tests/requirements.txt 148 | 149 | - run: *run-python-tests 150 | 151 | test-windows: 152 | parameters: 153 | python-version: 154 | type: string 155 | 156 | executor: 157 | name: win/default 158 | 159 | steps: 160 | - checkout 161 | 162 | - run: 163 | name: Install python and create virtualenv 164 | shell: bash -eo pipefail 165 | command: | 166 | # resolve python MAJOR.MINOR version to latest MAJOR.MINOR.PATCH version available on NuGet 167 | full_version=$( 168 | curl -s 'https://azuresearch-usnc.nuget.org/query?q=python' \ 169 | | jq -r '.data[] | select(.id == "python") .versions[] | .version' \ 170 | | awk -F. -v ver='<< parameters.python-version >>' \ 171 | 'index($0, ver".") == 1 && $3 >= m { m = $3; v = $0 } END { print v }' 172 | ) 173 | nuget install python -Version "$full_version" -ExcludeVersion 174 | python/tools/python -V 175 | python/tools/python -m venv env 176 | 177 | - run: 178 | name: Install requirements and the package 179 | command: | 180 | env\Scripts\activate.ps1 181 | pip install -U pip 182 | pip install -r requirements.txt -r tests\requirements.txt 183 | 184 | - run: 185 | name: Run tests 186 | command: | 187 | env\Scripts\activate.ps1 188 | coverage run -m unittest discover 189 | 190 | deploy: 191 | docker: 192 | - image: python:3.12 193 | 194 | steps: 195 | - checkout 196 | 197 | - run: 198 | name: create virtual env and install dependencies 199 | command: | 200 | python -m venv env 201 | . env/bin/activate 202 | pip install . 203 | pip install -r requirements.txt 204 | pip install twine wheel setuptools 205 | 206 | - run: 207 | name: verify version matches tag 208 | command: | 209 | . env/bin/activate 210 | [[ "$(pip show dwave-system 2>/dev/null | grep Version)" == "Version: $CIRCLE_TAG" ]] 211 | 212 | - run: 213 | name: build sdist and bdist 214 | command: | 215 | . env/bin/activate 216 | python setup.py sdist bdist_wheel 217 | 218 | - run: 219 | name: upload 220 | command: | 221 | . env/bin/activate 222 | twine upload -u $PYPI_USERNAME -p $PYPI_PASSWORD --skip-existing ./dist/* 223 | 224 | workflows: 225 | test: 226 | jobs: 227 | - test-linux: 228 | name: test-linux-<< matrix.python-version >> | << matrix.dependency-specifiers >> 229 | matrix: 230 | parameters: 231 | python-version: &python-versions ["3.9", "3.10", "3.11", "3.12", &latest-python "3.13"] 232 | dependency-specifiers: 233 | - "dwave-cloud-client~=0.12.0" 234 | - "dwave-cloud-client~=0.13.5" 235 | integration-test-python-version: &integration-python-versions [*latest-python] 236 | exclude: 237 | - python-version: "3.13" 238 | dependency-specifiers: "dwave-cloud-client~=0.12.0" 239 | integration-test-python-version: "3.13" 240 | 241 | - test-osx: 242 | name: test-osx-<< matrix.python-version >> 243 | matrix: 244 | parameters: 245 | python-version: *python-versions 246 | integration-test-python-version: *integration-python-versions 247 | 248 | - test-windows: 249 | name: test-windows-<< matrix.python-version >> 250 | matrix: 251 | parameters: 252 | python-version: *python-versions 253 | 254 | - test-doctest 255 | 256 | deploy: 257 | jobs: 258 | - deploy: 259 | filters: 260 | tags: 261 | only: /^[0-9]+(\.[0-9]+)*(\.dev([0-9]+)?)?$/ 262 | branches: 263 | ignore: /.*/ 264 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | # omit virtualenv and test files 3 | omit = venv/*, */tests/* 4 | source=dwave.system 5 | 6 | [report] 7 | include_namespace_packages = True 8 | exclude_lines: 9 | pragma: no cover -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Description** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Stack Overflow provides an excellent guide on [how to create a Minimal, Complete and Verifiable example.](https://stackoverflow.com/help/mcve) 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **Environment:** 17 | - OS: [Ubuntu 16.04.4 LTS] 18 | - Python version: [e.g. 3.7.0] 19 | 20 | **Additional context** 21 | Add any other context about the problem here. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Current Problem** 8 | Is your feature request related to a problem? Please provide a clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Proposed Solution** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Alternatives Considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv*/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | # pycharm 104 | .idea/ 105 | 106 | *.sublime-project 107 | *.sublime-workspace 108 | 109 | generated/ -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://img.shields.io/pypi/v/dwave-system.svg 2 | :target: https://pypi.org/project/dwave-system 3 | 4 | .. image:: https://img.shields.io/pypi/pyversions/dwave-system.svg?style=flat 5 | :target: https://pypi.org/project/dwave-system 6 | :alt: PyPI - Python Version 7 | 8 | .. image:: https://codecov.io/gh/dwavesystems/dwave-system/branch/master/graph/badge.svg 9 | :target: https://codecov.io/gh/dwavesystems/dwave-system 10 | 11 | .. image:: https://circleci.com/gh/dwavesystems/dwave-system.svg?style=shield 12 | :target: https://circleci.com/gh/dwavesystems/dwave-system 13 | 14 | 15 | ============ 16 | dwave-system 17 | ============ 18 | 19 | .. start_system_about 20 | 21 | `dwave-system` is a basic API for easily incorporating the D-Wave system as a 22 | sampler in the 23 | `Ocean software stack `_, 24 | directly or through `Leap `_ service's 25 | cloud-based hybrid solvers. It includes ``DWaveSampler``, a dimod sampler that 26 | accepts and passes system parameters such as system identification and 27 | authentication down the stack, ``LeapHybridSampler``, for Leap's hybrid solvers, 28 | and others. It also includes several useful composites---layers of pre- and 29 | post-processing---that can be used with ``DWaveSampler`` to handle 30 | minor-embedding, optimize chain strength, etc. 31 | 32 | .. end_system_about 33 | 34 | Installation 35 | ============ 36 | 37 | **Installation from PyPI:** 38 | 39 | .. code-block:: bash 40 | 41 | pip install dwave-system 42 | 43 | .. note:: 44 | As of ``dwave-system`` 1.28.0, support for ``dwave-drivers`` is removed (it 45 | was used for calibration of qubits in chains via ``VirtualGraphComposite``, 46 | but it's no longer required due to improved calibration of newer QPUs). 47 | 48 | **Installation from source:** 49 | 50 | .. code-block:: bash 51 | 52 | pip install -r requirements.txt 53 | python setup.py install 54 | 55 | License 56 | ======= 57 | 58 | Released under the Apache License 2.0. See LICENSE file. 59 | 60 | Contributing 61 | ============ 62 | 63 | Ocean's `contributing guide `_ 64 | has guidelines for contributing to Ocean packages. -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = -q 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = PROJECTNAME 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | clean: 18 | -rm -rf $(BUILDDIR) 19 | -rm -rf reference/generated 20 | 21 | # Catch-all target: route all unknown targets to Sphinx using the new 22 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 23 | %: Makefile 24 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 25 | -------------------------------------------------------------------------------- /docs/README.rst: -------------------------------------------------------------------------------- 1 | ../README.rst -------------------------------------------------------------------------------- /docs/_images/ChimeraUnitCell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwavesystems/dwave-system/7e9b6dad09bcce00c2b80bc0f48d9f3197769f30/docs/_images/ChimeraUnitCell.png -------------------------------------------------------------------------------- /docs/_images/Embedding_Chimera_AND.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwavesystems/dwave-system/7e9b6dad09bcce00c2b80bc0f48d9f3197769f30/docs/_images/Embedding_Chimera_AND.png -------------------------------------------------------------------------------- /docs/_images/chimera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwavesystems/dwave-system/7e9b6dad09bcce00c2b80bc0f48d9f3197769f30/docs/_images/chimera.png -------------------------------------------------------------------------------- /docs/_static/cookie_notice.css: -------------------------------------------------------------------------------- 1 | #cookie-notice { 2 | display: none; 3 | bottom: 0; 4 | position: fixed; 5 | width: 100%; 6 | background: white; 7 | z-index: 1000000; 8 | height: auto; 9 | text-align: center; 10 | } 11 | .cookie-button { 12 | border: none; 13 | display: inline-block; 14 | justify-content: center; 15 | height: 48px; 16 | font-size: 16px; 17 | font-weight: 600; 18 | border-radius: 4px; 19 | background: rgb(41, 128, 185); 20 | box-shadow: 0 5px 14px 0 rgba(29,30,36,0.19); 21 | color: #fff; 22 | cursor: pointer; 23 | padding-top: 12px; 24 | padding-bottom: 12px; 25 | padding-left: 42px; 26 | padding-right: 42px; 27 | margin: 1em; 28 | margin-left: 2em; 29 | white-space: nowrap; 30 | vertical-align: middle; 31 | } 32 | -------------------------------------------------------------------------------- /docs/_static/cookie_notice.js: -------------------------------------------------------------------------------- 1 | const send_ga = () => { 2 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 3 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 4 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 5 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 6 | 7 | ga('create', 'UA-124167090-6', 'auto'); 8 | ga('send', 'pageview'); 9 | } 10 | 11 | let cookieAccepted = localStorage.cookieAccepted; 12 | if (cookieAccepted == undefined) { 13 | $(document).ready(function () { 14 | var cookieDiv = document.createElement('div') 15 | cookieDiv.id = 'cookie-notice' 16 | cookieDiv.innerHTML = "

Privacy and Cookies: This site uses cookies. By continuing to use this website, you agree to their use. To find out more, see our Privacy Policy.

" 17 | 18 | $('body').append(cookieDiv).ready(() => { 19 | $('#cookie-notice').fadeIn('slow'); 20 | $("#cookie-accept").click(function () { 21 | localStorage.setItem("cookieAccepted", true) 22 | $('#cookie-notice').fadeOut('slow'); 23 | send_ga() 24 | }); 25 | }) 26 | }) 27 | } else if (cookieAccepted == "true") { 28 | send_ga() 29 | } 30 | -------------------------------------------------------------------------------- /docs/api_ref.rst: -------------------------------------------------------------------------------- 1 | .. _system_api_ref: 2 | 3 | ============= 4 | API Reference 5 | ============= 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | 10 | samplers 11 | composites 12 | embedding 13 | utilities 14 | warnings -------------------------------------------------------------------------------- /docs/composites.rst: -------------------------------------------------------------------------------- 1 | .. _system_composites: 2 | 3 | ========== 4 | Composites 5 | ========== 6 | 7 | :ref:`dimod composites ` that provide layers of pre- and 8 | post-processing (e.g., :term:`minor-embedding`) when using the D-Wave system: 9 | 10 | .. currentmodule:: dwave.system.composites 11 | 12 | Other Ocean packages provide additional composites; for example, 13 | :ref:`dimod ` provides composites that operate 14 | on the problem (e.g., scaling values), track inputs and outputs for debugging, 15 | and other useful functionality relevant to generic samplers. 16 | 17 | CutOffs 18 | ======= 19 | 20 | Prunes the binary quadratic model (BQM) submitted to the child sampler by retaining 21 | only interactions with values commensurate with the sampler’s precision. 22 | 23 | CutOffComposite 24 | --------------- 25 | 26 | .. autoclass:: CutOffComposite 27 | 28 | 29 | Properties 30 | ~~~~~~~~~~ 31 | 32 | .. autosummary:: 33 | :toctree: generated/ 34 | 35 | CutOffComposite.child 36 | CutOffComposite.children 37 | CutOffComposite.parameters 38 | CutOffComposite.properties 39 | 40 | Methods 41 | ~~~~~~~ 42 | 43 | .. autosummary:: 44 | :toctree: generated/ 45 | 46 | CutOffComposite.sample 47 | CutOffComposite.sample_ising 48 | CutOffComposite.sample_qubo 49 | 50 | PolyCutOffComposite 51 | ------------------- 52 | 53 | Prunes the polynomial submitted to the child sampler by retaining 54 | only interactions with values commensurate with the sampler’s precision. 55 | 56 | .. autoclass:: PolyCutOffComposite 57 | 58 | Properties 59 | ~~~~~~~~~~ 60 | 61 | .. autosummary:: 62 | :toctree: generated/ 63 | 64 | PolyCutOffComposite.child 65 | PolyCutOffComposite.children 66 | PolyCutOffComposite.parameters 67 | PolyCutOffComposite.properties 68 | 69 | Methods 70 | ~~~~~~~ 71 | 72 | .. autosummary:: 73 | :toctree: generated/ 74 | 75 | PolyCutOffComposite.sample_poly 76 | PolyCutOffComposite.sample_hising 77 | PolyCutOffComposite.sample_hubo 78 | 79 | 80 | 81 | Embedding 82 | ========= 83 | 84 | :term:`Minor-embed` a problem :term:`BQM` into a D-Wave system. 85 | 86 | .. automodule:: dwave.system.composites.embedding 87 | 88 | .. currentmodule:: dwave.system.composites 89 | 90 | AutoEmbeddingComposite 91 | ---------------------- 92 | 93 | .. autoclass:: AutoEmbeddingComposite 94 | 95 | Properties 96 | ~~~~~~~~~~ 97 | 98 | .. autosummary:: 99 | :toctree: generated/ 100 | 101 | AutoEmbeddingComposite.child 102 | AutoEmbeddingComposite.parameters 103 | AutoEmbeddingComposite.properties 104 | 105 | Methods 106 | ~~~~~~~ 107 | 108 | .. autosummary:: 109 | :toctree: generated/ 110 | 111 | AutoEmbeddingComposite.sample 112 | AutoEmbeddingComposite.sample_ising 113 | AutoEmbeddingComposite.sample_qubo 114 | 115 | 116 | EmbeddingComposite 117 | ------------------ 118 | 119 | .. autoclass:: EmbeddingComposite 120 | 121 | Properties 122 | ~~~~~~~~~~ 123 | 124 | .. autosummary:: 125 | :toctree: generated/ 126 | 127 | EmbeddingComposite.child 128 | EmbeddingComposite.parameters 129 | EmbeddingComposite.properties 130 | EmbeddingComposite.return_embedding_default 131 | EmbeddingComposite.warnings_default 132 | 133 | Methods 134 | ~~~~~~~ 135 | 136 | .. autosummary:: 137 | :toctree: generated/ 138 | 139 | EmbeddingComposite.sample 140 | EmbeddingComposite.sample_ising 141 | EmbeddingComposite.sample_qubo 142 | 143 | 144 | FixedEmbeddingComposite 145 | ----------------------- 146 | 147 | .. autoclass:: FixedEmbeddingComposite 148 | :show-inheritance: 149 | 150 | Properties 151 | ~~~~~~~~~~ 152 | 153 | .. autosummary:: 154 | :toctree: generated/ 155 | 156 | FixedEmbeddingComposite.adjacency 157 | FixedEmbeddingComposite.child 158 | FixedEmbeddingComposite.children 159 | FixedEmbeddingComposite.edgelist 160 | FixedEmbeddingComposite.nodelist 161 | FixedEmbeddingComposite.parameters 162 | FixedEmbeddingComposite.properties 163 | FixedEmbeddingComposite.structure 164 | 165 | Methods 166 | ~~~~~~~ 167 | 168 | .. autosummary:: 169 | :toctree: generated/ 170 | 171 | FixedEmbeddingComposite.sample 172 | FixedEmbeddingComposite.sample_ising 173 | FixedEmbeddingComposite.sample_qubo 174 | 175 | 176 | LazyFixedEmbeddingComposite 177 | --------------------------- 178 | 179 | .. autoclass:: LazyFixedEmbeddingComposite 180 | 181 | Properties 182 | ~~~~~~~~~~ 183 | 184 | .. autosummary:: 185 | :toctree: generated/ 186 | 187 | 188 | LazyFixedEmbeddingComposite.adjacency 189 | LazyFixedEmbeddingComposite.edgelist 190 | LazyFixedEmbeddingComposite.nodelist 191 | LazyFixedEmbeddingComposite.parameters 192 | LazyFixedEmbeddingComposite.properties 193 | LazyFixedEmbeddingComposite.structure 194 | 195 | Methods 196 | ~~~~~~~ 197 | 198 | .. autosummary:: 199 | :toctree: generated/ 200 | 201 | LazyFixedEmbeddingComposite.sample 202 | LazyFixedEmbeddingComposite.sample_ising 203 | LazyFixedEmbeddingComposite.sample_qubo 204 | 205 | 206 | ParallelEmbeddingComposite 207 | -------------------------- 208 | 209 | .. autoclass:: ParallelEmbeddingComposite 210 | 211 | 212 | TilingComposite 213 | --------------- 214 | 215 | .. autoclass:: TilingComposite 216 | 217 | Properties 218 | ~~~~~~~~~~ 219 | 220 | .. autosummary:: 221 | :toctree: generated/ 222 | 223 | TilingComposite.adjacency 224 | TilingComposite.child 225 | TilingComposite.children 226 | TilingComposite.edgelist 227 | TilingComposite.embeddings 228 | TilingComposite.nodelist 229 | TilingComposite.num_tiles 230 | TilingComposite.parameters 231 | TilingComposite.properties 232 | TilingComposite.structure 233 | 234 | Methods 235 | ~~~~~~~ 236 | 237 | .. autosummary:: 238 | :toctree: generated/ 239 | 240 | TilingComposite.sample 241 | TilingComposite.sample_ising 242 | TilingComposite.sample_qubo 243 | 244 | VirtualGraphComposite 245 | --------------------- 246 | 247 | .. autoclass:: VirtualGraphComposite 248 | 249 | Properties 250 | ~~~~~~~~~~ 251 | 252 | .. autosummary:: 253 | :toctree: generated/ 254 | 255 | VirtualGraphComposite.adjacency 256 | VirtualGraphComposite.child 257 | VirtualGraphComposite.children 258 | VirtualGraphComposite.edgelist 259 | VirtualGraphComposite.nodelist 260 | VirtualGraphComposite.parameters 261 | VirtualGraphComposite.properties 262 | VirtualGraphComposite.structure 263 | 264 | Methods 265 | ~~~~~~~ 266 | 267 | .. autosummary:: 268 | :toctree: generated/ 269 | 270 | VirtualGraphComposite.sample 271 | VirtualGraphComposite.sample_ising 272 | VirtualGraphComposite.sample_qubo 273 | 274 | 275 | 276 | Linear Bias 277 | =========== 278 | 279 | Composite for using auxiliary qubits to bias problem qubits. 280 | 281 | 282 | LinearAncillaComposite 283 | ----------------------- 284 | 285 | .. autoclass:: LinearAncillaComposite 286 | 287 | Properties 288 | ~~~~~~~~~~ 289 | 290 | .. autosummary:: 291 | :toctree: generated/ 292 | 293 | LinearAncillaComposite.child 294 | LinearAncillaComposite.children 295 | LinearAncillaComposite.parameters 296 | LinearAncillaComposite.properties 297 | 298 | Methods 299 | ~~~~~~~ 300 | 301 | .. autosummary:: 302 | :toctree: generated/ 303 | 304 | LinearAncillaComposite.sample 305 | LinearAncillaComposite.sample_ising 306 | LinearAncillaComposite.sample_qubo 307 | 308 | 309 | Reverse Anneal 310 | ============== 311 | 312 | Composites that do batch operations for reverse annealing based on sets of initial 313 | states or anneal schedules. 314 | 315 | ReverseBatchStatesComposite 316 | --------------------------- 317 | 318 | .. autoclass:: ReverseBatchStatesComposite 319 | 320 | 321 | Properties 322 | ~~~~~~~~~~ 323 | 324 | .. autosummary:: 325 | :toctree: generated/ 326 | 327 | ReverseBatchStatesComposite.child 328 | ReverseBatchStatesComposite.children 329 | ReverseBatchStatesComposite.parameters 330 | ReverseBatchStatesComposite.properties 331 | 332 | Methods 333 | ~~~~~~~ 334 | 335 | .. autosummary:: 336 | :toctree: generated/ 337 | 338 | ReverseBatchStatesComposite.sample 339 | ReverseBatchStatesComposite.sample_ising 340 | ReverseBatchStatesComposite.sample_qubo 341 | 342 | ReverseAdvanceComposite 343 | ----------------------- 344 | 345 | .. autoclass:: ReverseAdvanceComposite 346 | 347 | Properties 348 | ~~~~~~~~~~ 349 | 350 | .. autosummary:: 351 | :toctree: generated/ 352 | 353 | ReverseAdvanceComposite.child 354 | ReverseAdvanceComposite.children 355 | ReverseAdvanceComposite.parameters 356 | ReverseAdvanceComposite.properties 357 | 358 | Methods 359 | ~~~~~~~ 360 | 361 | .. autosummary:: 362 | :toctree: generated/ 363 | 364 | ReverseAdvanceComposite.sample 365 | ReverseAdvanceComposite.sample_ising 366 | ReverseAdvanceComposite.sample_qubo 367 | 368 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | import os 8 | import sys 9 | sys.path.insert(0, os.path.abspath('.')) 10 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 11 | 12 | 13 | # -- General configuration ------------------------------------------------ 14 | 15 | extensions = [ 16 | 'sphinx.ext.autosummary', 17 | 'sphinx.ext.autodoc', 18 | 'sphinx.ext.coverage', 19 | 'sphinx.ext.doctest', 20 | 'sphinx.ext.intersphinx', 21 | 'sphinx.ext.mathjax', 22 | 'sphinx.ext.napoleon', 23 | 'sphinx.ext.todo', 24 | 'sphinx.ext.viewcode', 25 | 'sphinx.ext.ifconfig' 26 | ] 27 | 28 | autosummary_generate = True 29 | 30 | # The suffix(es) of source filenames. 31 | source_suffix = '.rst' 32 | 33 | master_doc = 'index' 34 | 35 | # General information about the project. 36 | project = u'dwave-system' 37 | copyright = u'2018, D-Wave Systems Inc' 38 | author = u'D-Wave Systems Inc' 39 | 40 | import dwave.system.package_info 41 | version = dwave.system.package_info.__version__ 42 | release = dwave.system.package_info.__version__ 43 | 44 | language = "en" 45 | 46 | add_module_names = False 47 | 48 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'sdk_index.rst'] 49 | 50 | linkcheck_retries = 2 51 | linkcheck_anchors = False 52 | linkcheck_ignore = [r'https://cloud.dwavesys.com/leap', # redirects, many checks 53 | r'https://www.jstor.org/stable', 54 | r'https://doi.org/'] 55 | 56 | pygments_style = 'sphinx' 57 | 58 | todo_include_todos = True 59 | 60 | modindex_common_prefix = ['dwave-system.'] 61 | 62 | doctest_global_setup = """ 63 | import dimod 64 | from dwave.embedding import * 65 | 66 | from unittest.mock import Mock 67 | from dwave.system.testing import MockDWaveSampler 68 | import dwave.system 69 | dwave.system.DWaveSampler = Mock() 70 | dwave.system.DWaveSampler.side_effect = MockDWaveSampler 71 | from dwave.system import * 72 | """ 73 | 74 | # -- Options for HTML output ---------------------------------------------- 75 | html_theme = 'pydata_sphinx_theme' 76 | html_theme_options = { 77 | "collapse_navigation": True, 78 | "show_prev_next": False, 79 | } 80 | html_sidebars = {"**": ["search-field", "sidebar-nav-bs"]} # remove ads 81 | 82 | intersphinx_mapping = {'python': ('https://docs.python.org/3', None), 83 | 'networkx': ('https://networkx.github.io/documentation/stable/', None), 84 | 'numpy': ('http://numpy.org/doc/stable/', None), 85 | 'dwave': ('https://docs.dwavequantum.com/en/latest/', None), 86 | } 87 | 88 | # global substitutions 89 | rst_epilog = """ 90 | .. |array-like| replace:: array-like .. used in dwave-optimization 91 | .. _array-like: https://numpy.org/devdocs/glossary.html#term-array_like 92 | .. |adv2| unicode:: Advantage2 93 | """ -------------------------------------------------------------------------------- /docs/embedding.rst: -------------------------------------------------------------------------------- 1 | .. _system_embedding: 2 | 3 | ========= 4 | Embedding 5 | ========= 6 | 7 | Provides functions that map :term:`binary quadratic model`\ s and samples between 8 | a :term:`source` :term:`graph` and a :term:`target` graph. 9 | 10 | For an introduction to :term:`minor-embedding`, see the 11 | :ref:`qpu_embedding_intro` section. 12 | 13 | Generators 14 | ========== 15 | 16 | Tools for finding embeddings. 17 | 18 | Generic 19 | ------- 20 | 21 | :ref:`minorminer ` is a heuristic tool for minor embedding: given a 22 | minor and target graph, it tries to find a mapping that embeds the minor into the target. 23 | 24 | .. autosummary:: 25 | :toctree: generated/ 26 | 27 | minorminer.find_embedding 28 | 29 | .. currentmodule:: dwave.embedding 30 | 31 | Chimera 32 | ------- 33 | 34 | Minor-embedding in :term:`Chimera`\ -structured target graphs. 35 | 36 | .. autosummary:: 37 | :toctree: generated/ 38 | 39 | chimera.find_clique_embedding 40 | chimera.find_biclique_embedding 41 | chimera.find_grid_embedding 42 | 43 | Pegasus 44 | ------- 45 | 46 | Minor-embedding in :term:`Pegasus`\ -structured target graphs. 47 | 48 | .. autosummary:: 49 | :toctree: generated/ 50 | 51 | pegasus.find_clique_embedding 52 | pegasus.find_biclique_embedding 53 | 54 | Zephyr 55 | ------- 56 | 57 | Minor-embedding in :term:`Zephyr`-structured target graphs. 58 | 59 | .. autosummary:: 60 | :toctree: generated/ 61 | 62 | zephyr.find_clique_embedding 63 | zephyr.find_biclique_embedding 64 | 65 | Utilities 66 | ========= 67 | 68 | 69 | .. autosummary:: 70 | :toctree: generated/ 71 | 72 | embed_bqm 73 | embed_ising 74 | embed_qubo 75 | unembed_sampleset 76 | 77 | Diagnostics 78 | =========== 79 | 80 | .. autosummary:: 81 | :toctree: generated/ 82 | 83 | chain_break_frequency 84 | diagnose_embedding 85 | is_valid_embedding 86 | verify_embedding 87 | 88 | Chain Strength 89 | ============== 90 | 91 | .. automodule:: dwave.embedding.chain_strength 92 | .. currentmodule:: dwave.embedding 93 | 94 | .. autosummary:: 95 | :toctree: generated/ 96 | 97 | chain_strength.uniform_torque_compensation 98 | chain_strength.scaled 99 | 100 | Chain-Break Resolution 101 | ====================== 102 | 103 | .. automodule:: dwave.embedding.chain_breaks 104 | .. currentmodule:: dwave.embedding 105 | 106 | Generators 107 | ---------- 108 | 109 | .. autosummary:: 110 | :toctree: generated/ 111 | 112 | chain_breaks.discard 113 | chain_breaks.majority_vote 114 | chain_breaks.weighted_random 115 | 116 | Callable Objects 117 | ---------------- 118 | 119 | .. autosummary:: 120 | :toctree: generated/ 121 | 122 | chain_breaks.MinimizeEnergy 123 | 124 | Exceptions 125 | ========== 126 | 127 | .. autosummary:: 128 | :toctree: generated/ 129 | 130 | exceptions.EmbeddingError 131 | exceptions.MissingChainError 132 | exceptions.ChainOverlapError 133 | exceptions.DisconnectedChainError 134 | exceptions.InvalidNodeError 135 | exceptions.MissingEdgeError 136 | 137 | Classes 138 | ======= 139 | 140 | .. autoclass:: EmbeddedStructure 141 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. _index_system: 2 | 3 | ============ 4 | dwave-system 5 | ============ 6 | 7 | .. toctree:: 8 | :caption: Reference documentation for dwave-system: 9 | :maxdepth: 1 10 | 11 | api_ref 12 | 13 | 14 | About dwave-system 15 | ================== 16 | 17 | .. include:: README.rst 18 | :start-after: start_system_about 19 | :end-before: end_system_about 20 | 21 | .. note:: For applications that require detailed control of communication 22 | with the remote compute resource (a quantum computer or hybrid solver), 23 | see :ref:`dwave-cloud-client `. 24 | 25 | Usage Information 26 | ================= 27 | 28 | * :ref:`index_concepts` for terminology. 29 | * :ref:`qpu_quantum_solvers_intro` for an introduction to sampling with a 30 | quantum computer. 31 | * :ref:`opt_index_hybrid` for an introduction to using :term:`hybrid` 32 | :term:`solvers `. 33 | * :ref:`qpu_index_examples_beginner` for simple usage examples of using the 34 | :class:`~dwave.system.samplers.DWaveSampler` class. -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=PROJECTNAME 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | pydata-sphinx-theme 2 | sphinx -------------------------------------------------------------------------------- /docs/samplers.rst: -------------------------------------------------------------------------------- 1 | .. _system_samplers: 2 | 3 | ======== 4 | Samplers 5 | ======== 6 | 7 | A :term:`sampler` accepts a problem in :term:`quadratic model` (e.g., BQM, CQM) or 8 | :term:`nonlinear model` format and returns variable assignments. 9 | Samplers generally try to find minimizing values but can also sample from 10 | distributions defined by the problem. 11 | 12 | .. currentmodule:: dwave.system.samplers 13 | 14 | These samplers are non-blocking: the returned :class:`~dimod.SampleSet` is constructed 15 | from a :class:`~concurrent.futures.Future`-like object that is resolved on the first 16 | read of any of its properties; for example, by printing the results. Your code can 17 | query its status with the :meth:`~dimod.SampleSet.done` method or ensure resolution 18 | with the :meth:`~dimod.SampleSet.resolve` method. 19 | 20 | Other Ocean packages provide additional samplers; for example, 21 | :ref:`dimod ` provides samplers for testing 22 | your code. 23 | 24 | DWaveSampler 25 | ============ 26 | 27 | .. autoclass:: DWaveSampler 28 | :show-inheritance: 29 | 30 | Properties 31 | ---------- 32 | 33 | For parameters and properties of D-Wave systems, see the 34 | :ref:`qpu_index_solver_properties` and :ref:`qpu_solver_parameters` sections. 35 | 36 | .. autosummary:: 37 | :toctree: generated/ 38 | 39 | DWaveSampler.properties 40 | DWaveSampler.parameters 41 | DWaveSampler.nodelist 42 | DWaveSampler.edgelist 43 | DWaveSampler.adjacency 44 | DWaveSampler.structure 45 | 46 | Methods 47 | ------- 48 | 49 | .. autosummary:: 50 | :toctree: generated/ 51 | 52 | DWaveSampler.sample 53 | DWaveSampler.sample_ising 54 | DWaveSampler.sample_qubo 55 | DWaveSampler.validate_anneal_schedule 56 | DWaveSampler.to_networkx_graph 57 | DWaveSampler.close 58 | 59 | DWaveCliqueSampler 60 | ================== 61 | 62 | .. autoclass:: DWaveCliqueSampler 63 | 64 | Properties 65 | ---------- 66 | 67 | .. autosummary:: 68 | :toctree: generated/ 69 | 70 | DWaveCliqueSampler.largest_clique_size 71 | DWaveCliqueSampler.qpu_linear_range 72 | DWaveCliqueSampler.qpu_quadratic_range 73 | DWaveCliqueSampler.properties 74 | DWaveCliqueSampler.parameters 75 | DWaveCliqueSampler.target_graph 76 | 77 | 78 | Methods 79 | ------- 80 | 81 | .. autosummary:: 82 | :toctree: generated/ 83 | 84 | DWaveCliqueSampler.largest_clique 85 | DWaveCliqueSampler.sample 86 | DWaveCliqueSampler.sample_ising 87 | DWaveCliqueSampler.sample_qubo 88 | DWaveCliqueSampler.close 89 | 90 | 91 | LeapHybridSampler 92 | ================= 93 | 94 | .. autoclass:: LeapHybridSampler 95 | 96 | Properties 97 | ---------- 98 | 99 | .. autosummary:: 100 | :toctree: generated/ 101 | 102 | LeapHybridSampler.properties 103 | LeapHybridSampler.parameters 104 | LeapHybridSampler.default_solver 105 | 106 | 107 | Methods 108 | ------- 109 | 110 | .. autosummary:: 111 | :toctree: generated/ 112 | 113 | LeapHybridSampler.sample 114 | LeapHybridSampler.sample_ising 115 | LeapHybridSampler.sample_qubo 116 | LeapHybridSampler.min_time_limit 117 | LeapHybridSampler.close 118 | 119 | LeapHybridCQMSampler 120 | ==================== 121 | 122 | .. autoclass:: LeapHybridCQMSampler 123 | 124 | Properties 125 | ---------- 126 | 127 | .. autosummary:: 128 | :toctree: generated/ 129 | 130 | LeapHybridCQMSampler.properties 131 | LeapHybridCQMSampler.parameters 132 | 133 | 134 | Methods 135 | ------- 136 | 137 | .. autosummary:: 138 | :toctree: generated/ 139 | 140 | LeapHybridCQMSampler.sample_cqm 141 | LeapHybridCQMSampler.min_time_limit 142 | LeapHybridCQMSampler.close 143 | 144 | LeapHybridNLSampler 145 | ==================== 146 | 147 | .. autoclass:: LeapHybridNLSampler 148 | 149 | Properties 150 | ---------- 151 | 152 | .. autosummary:: 153 | :toctree: generated/ 154 | 155 | LeapHybridNLSampler.properties 156 | LeapHybridNLSampler.parameters 157 | LeapHybridNLSampler.default_solver 158 | 159 | Methods 160 | ------- 161 | 162 | .. autosummary:: 163 | :toctree: generated/ 164 | 165 | LeapHybridNLSampler.sample 166 | LeapHybridNLSampler.estimated_min_time_limit 167 | LeapHybridNLSampler.close 168 | 169 | LeapHybridDQMSampler 170 | ==================== 171 | 172 | .. autoclass:: LeapHybridDQMSampler 173 | 174 | Properties 175 | ---------- 176 | 177 | .. autosummary:: 178 | :toctree: generated/ 179 | 180 | LeapHybridDQMSampler.properties 181 | LeapHybridDQMSampler.parameters 182 | LeapHybridDQMSampler.default_solver 183 | 184 | 185 | Methods 186 | ------- 187 | 188 | .. autosummary:: 189 | :toctree: generated/ 190 | 191 | LeapHybridDQMSampler.sample_dqm 192 | LeapHybridDQMSampler.min_time_limit 193 | LeapHybridDQMSampler.close 194 | -------------------------------------------------------------------------------- /docs/utilities.rst: -------------------------------------------------------------------------------- 1 | .. _system_utilities: 2 | 3 | ========= 4 | Utilities 5 | ========= 6 | 7 | .. automodule:: dwave.system.utilities 8 | 9 | .. currentmodule:: dwave.system 10 | 11 | .. autosummary:: 12 | :toctree: generated/ 13 | 14 | ~utilities.anneal_schedule_with_offset 15 | ~utilities.common_working_graph 16 | ~coupling_groups.coupling_groups 17 | ~utilities.energy_scales_custom_schedule 18 | 19 | Temperature and Unit-Conversion Utilities 20 | ----------------------------------------- 21 | 22 | .. automodule:: dwave.system.temperatures 23 | 24 | .. currentmodule:: dwave.system.temperatures 25 | 26 | .. autosummary:: 27 | :toctree: generated/ 28 | 29 | background_susceptibility_bqm 30 | background_susceptibility_ising 31 | effective_field 32 | fast_effective_temperature 33 | fluxbias_to_h 34 | freezeout_effective_temperature 35 | h_to_fluxbias 36 | Ip_in_units_of_B 37 | maximum_pseudolikelihood 38 | maximum_pseudolikelihood_temperature 39 | 40 | 41 | .. [Chat2007] 42 | Chatterjee, Sourav. 43 | “Estimation in Spin Glasses: A First Step.” 44 | The Annals of Statistics 35, no. 5 (2007): 1931-46. 45 | http://www.jstor.org/stable/25464568 -------------------------------------------------------------------------------- /docs/warnings.rst: -------------------------------------------------------------------------------- 1 | .. _system_warnings: 2 | 3 | ======== 4 | Warnings 5 | ======== 6 | 7 | The ``dwave-system`` package supports various warning classes and provides the 8 | ability to configure warning handling. 9 | 10 | 11 | Supported Warnings 12 | ================== 13 | 14 | The following warnings are currently supported. 15 | 16 | .. currentmodule:: dwave.system.warnings 17 | 18 | .. autoclass:: ChainBreakWarning 19 | .. autoclass:: ChainLengthWarning 20 | .. autoclass:: ChainStrengthWarning 21 | .. autoclass:: EnergyScaleWarning 22 | .. autoclass:: TooFewSamplesWarning 23 | 24 | Related Information 25 | ------------------- 26 | 27 | * :ref:`qpu_embedding_intro` and :ref:`qpu_embedding_guidance` describe chains 28 | and how to deal with broken chains. 29 | * :ref:`qpu_basic_config` and :ref:`qpu_solver_configuration` provide basic 30 | and advanced information on configuring QPU parameters and best practices. 31 | 32 | Configuring Warnings 33 | ==================== 34 | 35 | Settings for raising warnings may be configured by tools such as composites or 36 | the :ref:`index_inspector` tool. 37 | 38 | This example configures warnings for an instance of the 39 | :class:`~dwave.system.composites.EmbeddingComposite()` class used on a sampler 40 | structured to represent variable ``a`` with a long chain. 41 | 42 | >>> import networkx as nx 43 | >>> import dimod 44 | >>> import dwave.samplers 45 | ... 46 | >>> G = nx.Graph() 47 | >>> G.add_edges_from([(n, n + 1) for n in range(10)]) 48 | >>> sampler = dimod.StructureComposite(dwave.samplers.SteepestDescentSampler(), G.nodes, G.edges) 49 | >>> sampleset = EmbeddingComposite(sampler).sample_ising({}, {("a", "b"): -1}, 50 | ... return_embedding=True, 51 | ... embedding_parameters={"fixed_chains": {"a": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}}, 52 | ... warnings=dwave.system.warnings.SAVE) 53 | >>> "warnings" in sampleset.info 54 | True 55 | 56 | .. currentmodule:: dwave.system.warnings 57 | 58 | .. autoclass:: WarningAction 59 | .. autoclass:: WarningHandler 60 | 61 | .. autosummary:: 62 | :toctree: generated/ 63 | 64 | WarningHandler.chain_break 65 | WarningHandler.chain_length 66 | WarningHandler.chain_strength 67 | WarningHandler.energy_scale 68 | WarningHandler.too_few_samples 69 | WarningHandler.issue -------------------------------------------------------------------------------- /dwave/embedding/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import minorminer # into this namespace 16 | import dwave.embedding.chimera 17 | import dwave.embedding.pegasus 18 | import dwave.embedding.zephyr 19 | import dwave.embedding.drawing 20 | import dwave.embedding.exceptions 21 | 22 | from dwave.embedding.diagnostic import diagnose_embedding, is_valid_embedding, verify_embedding 23 | 24 | from dwave.embedding.chain_breaks import broken_chains 25 | from dwave.embedding.chain_breaks import discard, majority_vote, weighted_random, MinimizeEnergy 26 | 27 | from dwave.embedding.transforms import embed_bqm, embed_ising, embed_qubo, unembed_sampleset, EmbeddedStructure 28 | 29 | from dwave.embedding.utils import target_to_source, chain_to_quadratic, chain_break_frequency 30 | -------------------------------------------------------------------------------- /dwave/embedding/chain_strength.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Utility functions for calculating chain strength. 16 | 17 | Examples: 18 | This example uses :func:`uniform_torque_compensation`, given a prefactor of 2, 19 | to calculate a chain strength that :class:`EmbeddingComposite` then uses. 20 | 21 | >>> from functools import partial 22 | >>> from dwave.system import EmbeddingComposite, DWaveSampler 23 | >>> from dwave.embedding.chain_strength import uniform_torque_compensation 24 | ... 25 | >>> Q = {(0,0): 1, (1,1): 1, (2,3): 2, (1,2): -2, (0,3): -2} 26 | >>> sampler = EmbeddingComposite(DWaveSampler()) 27 | >>> # partial() can be used when the BQM or embedding is not accessible 28 | >>> chain_strength = partial(uniform_torque_compensation, prefactor=2) 29 | >>> sampleset = sampler.sample_qubo(Q, chain_strength=chain_strength, return_embedding=True) 30 | >>> sampleset.info['embedding_context']['chain_strength'] 31 | 1.224744871391589 32 | 33 | """ 34 | import math 35 | import numpy as np 36 | 37 | __all__ = ['uniform_torque_compensation', 'scaled'] 38 | 39 | def uniform_torque_compensation(bqm, embedding=None, prefactor=1.414): 40 | r"""Chain strength that attempts to compensate for chain-breaking torque. 41 | 42 | The problem's connectivity\ [#]_ and quadratic biases are used to calculate 43 | a value of chain strength that preforms reasonably well on many problems. 44 | 45 | As the quantum annealing progresses, and the amplitude of the transverse 46 | field (:math:`A(s)` in the :ref:`qpu_qa_implementation` section) decreases, 47 | the wavefunction representing the QPU's quantum state develops long-range 48 | order. A chain strength that increases the correlation of the chains' qubits 49 | together with the development of this long-range order produces efficient 50 | quantum dynamics [Ray2020]_. For many hard, frustrated problems (such as 51 | spin glasses) with typical chain topology (path or tree-like chains), the 52 | optimal chain strength is proportional to the root of typical variable 53 | connectivity and the root mean square (RMS) of coupling strength 54 | (:math:`\sqrt{\text{connectivity}} \times \text{coupling}_{RMS}`). 55 | 56 | This chain strength, chosen for its dynamically-efficient scaling, also 57 | meets another requirement, even in challenging models: it must be large 58 | enough so that, toward the end of the quantum annealing, intact chains of 59 | the embedded problem (the programmed Hamiltonian) have lower energy than 60 | broken chains for the ground state. This allows the embedded problem to 61 | recover the ground state in the adiabatic limit. Consider the following 62 | observation: in a frustrated problem, such as a spin glass, qubits in chains 63 | are subject to random energy signals from neighbors. If you split the chain 64 | in two with equal numbers of neighboring chains, the central limit theorem 65 | dictates that the signal in each half has zero mean and variance 66 | proportional to the connectivity and the RMS of the coupling values. In 67 | combination, these can create a random torque on the chain, favouring 68 | misalignment (a broken chain). For the central coupling to maintain 69 | alignment of the two halves, it needs an energy penalty larger than the 70 | torque signal, and so scales as 71 | :math:`\sqrt{\text{connectivity}} \times \text{coupling}_{RMS}`. 72 | 73 | .. [#] 74 | A measure of the density of interactions between variables; for example 75 | the average degree (number of edges per node in the graph representing 76 | the problem). 77 | 78 | Args: 79 | bqm (:obj:`.BinaryQuadraticModel`): 80 | A binary quadratic model. 81 | 82 | embedding (dict/:class:`.EmbeddedStructure`, default=None): 83 | Included to satisfy the `chain_strength` callable specifications 84 | for `embed_bqm`. 85 | 86 | prefactor (float, optional, default=1.414): 87 | Prefactor used for scaling. For non-pathological problems, the recommended 88 | range of prefactors to try is [0.5, 2]. 89 | 90 | Returns: 91 | float: The chain strength, or 1 if chain strength is not applicable. 92 | 93 | """ 94 | num_interactions = bqm.num_interactions 95 | 96 | # NumPy arrays improves performance through vectorization 97 | quadratic_array = np.fromiter(bqm.quadratic.values(), dtype=float, count=num_interactions) 98 | 99 | if num_interactions: 100 | squared_j = quadratic_array**2 101 | 102 | rms = math.sqrt(squared_j.sum() / num_interactions) 103 | avg_degree = bqm.degrees(array=True).mean() 104 | 105 | return prefactor * rms * math.sqrt(avg_degree) 106 | 107 | # won't matter (chain strength isn't needed to embed this problem) 108 | return 1 109 | 110 | def scaled(bqm, embedding=None, prefactor=1.0): 111 | """Chain strength that is scaled to the problem bias range. 112 | 113 | Args: 114 | bqm (:obj:`.BinaryQuadraticModel`): 115 | A binary quadratic model. 116 | 117 | embedding (dict/:class:`.EmbeddedStructure`, default=None): 118 | Included to satisfy the `chain_strength` callable specifications 119 | for `embed_bqm`. 120 | 121 | prefactor (float, optional, default=1.0): 122 | Prefactor used for scaling. 123 | 124 | Returns: 125 | float: The chain strength, or 1 if chain strength is not applicable. 126 | 127 | """ 128 | if bqm.num_interactions > 0: 129 | max_bias = max( 130 | bqm.linear.max(), -bqm.linear.min(), bqm.quadratic.max(), -bqm.quadratic.min() 131 | ) 132 | return prefactor * max_bias 133 | 134 | # won't matter (chain strength isn't needed to embed this problem) 135 | return 1 136 | -------------------------------------------------------------------------------- /dwave/embedding/chimera.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from minorminer.utils.chimera import * 16 | -------------------------------------------------------------------------------- /dwave/embedding/diagnostic.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from minorminer.utils.diagnostic import * 16 | -------------------------------------------------------------------------------- /dwave/embedding/drawing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from math import ceil, sqrt 16 | 17 | from dwave_networkx import chimera_graph, draw_chimera 18 | 19 | __all__ = ['draw_chimera_bqm'] 20 | 21 | def draw_chimera_bqm(bqm, width=None, height=None): 22 | """Draws a Chimera Graph representation of a Binary Quadratic Model. 23 | 24 | If cell width and height not provided assumes square cell dimensions. 25 | Throws an error if drawing onto a Chimera graph of the given dimensions fails. 26 | 27 | Args: 28 | bqm (:obj:`dimod.BinaryQuadraticModel`): 29 | Should be equivalent to a Chimera graph or a subgraph of a Chimera graph produced by dnx.chimera_graph. 30 | The nodes and edges should have integer variables as in the dnx.chimera_graph. 31 | width (int, optional): 32 | An integer representing the number of cells of the Chimera graph will be in width. 33 | height (int, optional): 34 | An integer representing the number of cells of the Chimera graph will be in height. 35 | 36 | Examples: 37 | >>> from dwave.embedding.drawing import draw_chimera_bqm 38 | >>> from dimod import BinaryQuadraticModel 39 | >>> Q={(0, 0): 2, (1, 1): 1, (2, 2): 0, (3, 3): -1, (4, 4): -2, (5, 5): -2, (6, 6): -2, (7, 7): -2, 40 | ... (0, 4): 2, (0, 4): -1, (1, 7): 1, (1, 5): 0, (2, 5): -2, (2, 6): -2, (3, 4): -2, (3, 7): -2} 41 | >>> draw_chimera_bqm(BinaryQuadraticModel.from_qubo(Q), width=1, height=1) 42 | 43 | """ 44 | 45 | linear = bqm.linear.keys() 46 | quadratic = bqm.quadratic.keys() 47 | 48 | if width is None and height is None: 49 | # Create a graph large enough to fit the input networkx graph. 50 | graph_size = ceil(sqrt((max(linear) + 1) / 8.0)) 51 | width = graph_size 52 | height = graph_size 53 | 54 | if not width or not height: 55 | raise Exception("Both dimensions must be defined, not just one.") 56 | 57 | # A background image of the same size is created to show the complete graph. 58 | G0 = chimera_graph(height, width, 4) 59 | G = chimera_graph(height, width, 4) 60 | 61 | 62 | # Check if input graph is chimera graph shaped, by making sure that no edges are invalid. 63 | # Invalid edges can also appear if the size of the chimera graph is incompatible with the input graph in cell dimensions. 64 | non_chimera_nodes = [] 65 | non_chimera_edges = [] 66 | for node in linear: 67 | if not node in G.nodes: 68 | non_chimera_nodes.append(node) 69 | for edge in quadratic: 70 | if not edge in G.edges: 71 | non_chimera_edges.append(edge) 72 | 73 | linear_set = set(linear) 74 | g_node_set = set(G.nodes) 75 | 76 | quadratic_set = set(map(frozenset, quadratic)) 77 | g_edge_set = set(map(frozenset, G.edges)) 78 | 79 | non_chimera_nodes = linear_set - g_node_set 80 | non_chimera_edges = quadratic_set - g_edge_set 81 | 82 | if non_chimera_nodes or non_chimera_edges: 83 | raise Exception("Input graph is not a chimera graph: Nodes: %s Edges: %s" % (non_chimera_nodes, non_chimera_edges)) 84 | 85 | 86 | # Get lists of nodes and edges to remove from the complete graph to turn the complete graph into your graph. 87 | remove_nodes = list(g_node_set - linear_set) 88 | remove_edges = list(g_edge_set - quadratic_set) 89 | 90 | # Remove the nodes and edges from the graph. 91 | for edge in remove_edges: 92 | G.remove_edge(*edge) 93 | for node in remove_nodes: 94 | G.remove_node(node) 95 | 96 | node_size = 100 97 | # Draw the complete chimera graph as the background. 98 | draw_chimera(G0, node_size=node_size*0.5, node_color='black', edge_color='black') 99 | # Draw your graph over the complete graph to show the connectivity. 100 | draw_chimera(G, node_size=node_size, linear_biases=bqm.linear, quadratic_biases=bqm.quadratic, 101 | width=3) 102 | return -------------------------------------------------------------------------------- /dwave/embedding/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from minorminer.utils.exceptions import * 16 | -------------------------------------------------------------------------------- /dwave/embedding/pegasus.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from minorminer.utils.pegasus import * 16 | -------------------------------------------------------------------------------- /dwave/embedding/polynomialembedder.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from minorminer.utils.polynomialembedder import * 16 | -------------------------------------------------------------------------------- /dwave/embedding/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import dimod 16 | import numpy as np 17 | 18 | from dwave.embedding.chain_breaks import broken_chains 19 | 20 | 21 | __all__ = ['target_to_source', 22 | 'chain_to_quadratic', 23 | 'chain_break_frequency', 24 | 'adjacency_to_edges'] 25 | 26 | 27 | def target_to_source(target_adjacency, embedding): 28 | """Derive the source adjacency from an embedding and target adjacency. 29 | 30 | Args: 31 | target_adjacency (dict/:class:`networkx.Graph`): 32 | A dict of the form {v: Nv, ...} where v is a node in the target graph and Nv is the 33 | neighbors of v as an iterable. This can also be a networkx graph. 34 | 35 | embedding (dict): 36 | A mapping from a source graph to a target graph. 37 | 38 | Returns: 39 | dict: The adjacency of the source graph. 40 | 41 | Raises: 42 | ValueError: If any node in the target_adjacency is assigned more 43 | than one node in the source graph by embedding. 44 | 45 | Examples: 46 | 47 | >>> target_adjacency = {0: {1, 3}, 1: {0, 2}, 2: {1, 3}, 3: {0, 2}} # a square graph 48 | >>> embedding = {'a': {0}, 'b': {1}, 'c': {2, 3}} 49 | >>> source_adjacency = dwave.embedding.target_to_source(target_adjacency, embedding) 50 | >>> # triangle graph: 51 | >>> source_adjacency # doctest: +SKIP 52 | {'a': {'b', 'c'}, 'b': {'a', 'c'}, 'c': {'a', 'b'}} 53 | 54 | This function also works with networkx graphs. 55 | 56 | >>> import networkx as nx 57 | >>> target_graph = nx.complete_graph(5) 58 | >>> embedding = {'a': {0, 1, 2}, 'b': {3, 4}} 59 | >>> dwave.embedding.target_to_source(target_graph, embedding) # doctest: +SKIP 60 | {'a': {'b'}, 'b': {'a'}} 61 | 62 | """ 63 | # the nodes in the source adjacency are just the keys of the embedding 64 | source_adjacency = {v: set() for v in embedding} 65 | 66 | # we need the mapping from each node in the target to its source node 67 | reverse_embedding = {} 68 | for v, chain in embedding.items(): 69 | for u in chain: 70 | if u in reverse_embedding: 71 | raise ValueError("target node {} assigned to more than one source node".format(u)) 72 | reverse_embedding[u] = v 73 | 74 | # v is node in target, n node in source 75 | for v, n in reverse_embedding.items(): 76 | neighbors = target_adjacency[v] 77 | 78 | # u is node in target 79 | for u in neighbors: 80 | 81 | # some nodes might not be assigned to chains 82 | if u not in reverse_embedding: 83 | continue 84 | 85 | # m is node in source 86 | m = reverse_embedding[u] 87 | 88 | if m == n: 89 | continue 90 | 91 | source_adjacency[n].add(m) 92 | source_adjacency[m].add(n) 93 | 94 | return source_adjacency 95 | 96 | 97 | def chain_to_quadratic(chain, target_adjacency, chain_strength): 98 | """Determine the quadratic biases that induce the given chain. 99 | 100 | Args: 101 | chain (iterable): 102 | The variables that make up a chain. 103 | 104 | target_adjacency (dict/:class:`networkx.Graph`): 105 | Should be a dict of the form {s: Ns, ...} where s is a variable 106 | in the target graph and Ns is the set of neighbours of s. 107 | 108 | chain_strength (float): 109 | The magnitude of the quadratic bias that should be used to create chains. 110 | 111 | Returns: 112 | dict[edge, float]: The quadratic biases that induce the given chain. 113 | 114 | Raises: 115 | ValueError: If the variables in chain do not form a connected subgraph of target. 116 | 117 | Examples: 118 | 119 | >>> chain = {1, 2} 120 | >>> target_adjacency = {0: {1, 2}, 1: {0, 2}, 2: {0, 1}} 121 | >>> dimod.embedding.chain_to_quadratic(chain, target_adjacency, 1) 122 | {(1, 2): -1} 123 | 124 | """ 125 | quadratic = {} # we will be adding the edges that make the chain here 126 | 127 | # do a breadth first search 128 | seen = set() 129 | try: 130 | next_level = {next(iter(chain))} 131 | except StopIteration: 132 | raise ValueError("chain must have at least one variable") 133 | while next_level: 134 | this_level = next_level 135 | next_level = set() 136 | for v in this_level: 137 | if v not in seen: 138 | seen.add(v) 139 | 140 | for u in target_adjacency[v]: 141 | if u not in chain: 142 | continue 143 | next_level.add(u) 144 | if u != v and (u, v) not in quadratic: 145 | quadratic[(v, u)] = -chain_strength 146 | 147 | if len(chain) != len(seen): 148 | raise ValueError('{} is not a connected chain'.format(chain)) 149 | 150 | return quadratic 151 | 152 | 153 | def chain_break_frequency(samples_like, embedding): 154 | """Determine the frequency of chain breaks in the given samples. 155 | 156 | Args: 157 | samples_like (samples_like/:obj:`dimod.SampleSet`): 158 | A collection of raw samples. 'samples_like' is an extension of NumPy's array_like. 159 | See :func:`dimod.as_samples`. 160 | 161 | embedding (dict): 162 | Mapping from source graph to target graph as a dict of form {s: {t, ...}, ...}, 163 | where s is a source-model variable and t is a target-model variable. 164 | 165 | Returns: 166 | dict: Frequency of chain breaks as a dict in the form {s: f, ...}, where s 167 | is a variable in the source graph and float f the fraction 168 | of broken chains. 169 | 170 | Examples: 171 | This example embeds a single source node, 'a', as a chain of two target nodes (0, 1) 172 | and uses :func:`.chain_break_frequency` to show that out of two synthetic samples, 173 | one ([-1, +1]) represents a broken chain. 174 | 175 | >>> import numpy as np 176 | ... 177 | >>> samples = np.array([[-1, +1], [+1, +1]]) 178 | >>> embedding = {'a': {0, 1}} 179 | >>> print(dwave.embedding.chain_break_frequency(samples, embedding)['a']) 180 | 0.5 181 | 182 | 183 | """ 184 | if isinstance(samples_like, dimod.SampleSet): 185 | labels = samples_like.variables 186 | samples = samples_like.record.sample 187 | num_occurrences = samples_like.record.num_occurrences 188 | else: 189 | samples, labels = dimod.as_samples(samples_like) 190 | num_occurrences = np.ones(samples.shape[0]) 191 | 192 | if not all(v == idx for idx, v in enumerate(labels)): 193 | labels_to_idx = {v: idx for idx, v in enumerate(labels)} 194 | embedding = {v: {labels_to_idx[u] for u in chain} for v, chain in embedding.items()} 195 | 196 | if not embedding: 197 | return {} 198 | 199 | variables, chains = zip(*embedding.items()) 200 | 201 | broken = broken_chains(samples, chains) 202 | 203 | return {v: float(np.average(broken[:, cidx], weights=num_occurrences)) 204 | for cidx, v in enumerate(variables)} 205 | 206 | 207 | def edgelist_to_adjacency(edgelist): 208 | """Converts an iterator of edges to an adjacency dict. 209 | 210 | Args: 211 | edgelist (iterable): 212 | An iterator over 2-tuples where each 2-tuple is an edge. 213 | 214 | Returns: 215 | dict: The adjacency dict. A dict of the form `{v: Nv, ...}` where `v` is 216 | a node in a graph and `Nv` is the neighbors of `v` as an set. 217 | 218 | """ 219 | adjacency = dict() 220 | for u, v in edgelist: 221 | if u in adjacency: 222 | adjacency[u].add(v) 223 | else: 224 | adjacency[u] = {v} 225 | if v in adjacency: 226 | adjacency[v].add(u) 227 | else: 228 | adjacency[v] = {u} 229 | return adjacency 230 | 231 | def adjacency_to_edges(adjacency): 232 | """Converts an adjacency dict, networkx graph, or bqm to an edge iterator. 233 | 234 | Args: 235 | adjacency (dict/:class:`networkx.Graph`/:class:`dimod.BQM`): 236 | Should be a dict of the form {s: Ns, ...} where s is a variable 237 | in the graph and Ns is the set of neighbours of s. 238 | 239 | Yields: 240 | tuple: A 2-tuple, corresponding to an edge in the provided graph 241 | 242 | """ 243 | if hasattr(adjacency, 'edges'): 244 | yield from adjacency.edges() 245 | 246 | elif hasattr(adjacency, 'quadratic'): 247 | yield from adjacency.quadratic 248 | 249 | elif hasattr(adjacency, 'items'): 250 | seen = set() 251 | for v, Nv in adjacency.items(): 252 | seen.add(v) 253 | for u in Nv: 254 | if u not in seen: 255 | yield (u, v) 256 | else: 257 | raise TypeError("unrecognized type for adjacency -- provide a dict, " 258 | "Mapping, networkx.Graph or dimod.BQM") 259 | 260 | class intlabel_disjointsets: 261 | """A disjoint sets implementation with size and path-halving, for graphs 262 | labeled [0, ..., n-1] 263 | 264 | Args: 265 | n (int): 266 | The number of items in the disjoint sets 267 | 268 | """ 269 | def __init__(self, n): 270 | self._parent = list(range(n)) 271 | self._size = [1] * n 272 | 273 | def find(self, q): 274 | """Find the current root for q. 275 | 276 | Args: 277 | q (int): 278 | A number in range(n) 279 | 280 | Returns: 281 | int: the root of the set containing q 282 | 283 | """ 284 | parent = self._parent 285 | p = parent[q] 286 | while q != p: 287 | r = parent[q] = parent[p] 288 | q, p = p, r 289 | return p 290 | 291 | def union(self, p, q): 292 | """Merges the sets containing p and q. 293 | 294 | Args: 295 | p (int): 296 | A number in range(n) 297 | q (int): 298 | A number in range(n) 299 | 300 | """ 301 | p = self.find(p) 302 | q = self.find(q) 303 | a = self._size[p] 304 | b = self._size[q] 305 | if p == q: 306 | return 307 | if a > b: 308 | p, q = q, p 309 | self._parent[p] = q 310 | self._size[q] = a + b 311 | 312 | def size(self, q): 313 | """Returns the size of the set containing q. 314 | 315 | Args: 316 | p (int): 317 | A number in range(n) 318 | 319 | Returns: 320 | int: the size of the set containing q 321 | """ 322 | return self._size[self.find(q)] 323 | 324 | 325 | -------------------------------------------------------------------------------- /dwave/embedding/zephyr.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from minorminer.utils.zephyr import * 16 | -------------------------------------------------------------------------------- /dwave/system/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import dwave.system.flux_bias_offsets 16 | 17 | from dwave.system.samplers import * 18 | import dwave.system.samplers 19 | 20 | from dwave.system.temperatures import * 21 | import dwave.system.temperatures 22 | 23 | from dwave.system.composites import * 24 | import dwave.system.composites 25 | 26 | from dwave.system.utilities import * 27 | 28 | from dwave.system.package_info import __version__ 29 | -------------------------------------------------------------------------------- /dwave/system/composites/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 D-Wave Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from dwave.system.composites.cutoffcomposite import * 16 | from dwave.system.composites.embedding import * 17 | from dwave.system.composites.linear_ancilla import * 18 | from dwave.system.composites.tiling import * 19 | from dwave.system.composites.virtual_graph import * 20 | from dwave.system.composites.reversecomposite import * 21 | from dwave.system.composites.parallel_embeddings import * 22 | -------------------------------------------------------------------------------- /dwave/system/composites/linear_ancilla.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 D-Wave Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Composite to implement linear coefficients with ancilla qubits biased with flux-bias offsets. 16 | """ 17 | 18 | import numbers 19 | 20 | from collections import defaultdict 21 | from typing import Sequence, Mapping, Any 22 | 23 | import dimod 24 | import numpy as np 25 | 26 | from dimod.decorators import nonblocking_sample_method 27 | 28 | 29 | __all__ = ["LinearAncillaComposite"] 30 | 31 | 32 | class LinearAncillaComposite(dimod.ComposedSampler, dimod.Structured): 33 | """Implements linear biases as ancilla qubits polarized with strong flux biases. 34 | 35 | Linear bias :math:`h_i` of qubit :math:`i` is implemented through a coupling 36 | :math:`J_{ij}` between the qubit and a neighboring qubit :math:`j` that has a 37 | large flux-bias offset. 38 | 39 | Args: 40 | child_sampler (:class:`dimod.Sampler`): 41 | A dimod sampler, such as a :class:`~dwave.system.samplers.DWaveSampler()`, 42 | that has flux bias controls. 43 | 44 | .. versionadded:: 1.30.0 45 | Support for context manager protocol with :meth:`dimod.Scoped` 46 | implemented. 47 | 48 | Examples: 49 | This example submits a two-qubit problem consisting of linear biases with opposed signs 50 | and anti-ferromagnetic coupling. A D-Wave quantum computer solves it with the fast-anneal 51 | protocol using ancilla qubits to represent the linear biases. 52 | 53 | >>> from dwave.system import DWaveSampler, EmbeddingComposite, LinearAncillaComposite 54 | ... 55 | >>> with EmbeddingComposite(LinearAncillaComposite(DWaveSampler())) as sampler: # doctest: +SKIP 56 | ... sampleset = sampler.sample_ising({0:1, 1:-1}, {(0, 1): 1}, fast_anneal=True) 57 | ... sampleset.first.energy 58 | -3 59 | """ 60 | 61 | def __init__( 62 | self, 63 | child_sampler: dimod.Sampler, 64 | ): 65 | self.children = [child_sampler] 66 | self.parameters = child_sampler.parameters.copy() 67 | self.properties = dict(child_properties=child_sampler.properties.copy()) 68 | self.nodelist = child_sampler.nodelist 69 | self.edgelist = child_sampler.edgelist 70 | 71 | def nodelist(self): 72 | pass # overwritten by init 73 | 74 | def edgelist(self): 75 | pass # overwritten by init 76 | 77 | children = None # overwritten by init 78 | """list [child_sampler]: List containing the structured sampler.""" 79 | 80 | parameters = None # overwritten by init 81 | """dict[str, list]: Parameters in the form of a dict. 82 | 83 | For an instantiated composed sampler, keys are the keyword parameters 84 | accepted by the child sampler and parameters added by the composite. 85 | """ 86 | 87 | properties = None # overwritten by init 88 | """dict: Properties in the form of a dict. 89 | 90 | Contains the properties of the child sampler. 91 | """ 92 | 93 | @nonblocking_sample_method 94 | def sample( 95 | self, 96 | bqm: dimod.BinaryQuadraticModel, 97 | *, 98 | h_tolerance: numbers.Number = 0, 99 | default_flux_bias_range: tuple[float, float] = (-0.005, 0.005), 100 | **parameters, 101 | ): 102 | """Sample from the provided binary quadratic model. 103 | 104 | .. note:: 105 | This composite does not support the :ref:`parameter_qpu_auto_scale` parameter; use the 106 | :class:`~dwave.preprocessing.composites.ScaleComposite` for scaling. 107 | 108 | Args: 109 | bqm (:class:`~dimod.binary.BinaryQuadraticModel`): 110 | Binary quadratic model to be sampled from. 111 | 112 | h_tolerance (:class:`numbers.Number`): 113 | Magnitude of the linear bias to be set directly on problem qubits; above this the bias 114 | is emulated by the flux-bias offset to an ancilla qubit. Assumed to be positive. 115 | Defaults to zero. 116 | 117 | default_flux_bias_range (:class:`tuple`): 118 | Flux-bias range, as a two-tuple, supported by the QPU. The values must be large enough to 119 | ensure qubits remain polarized throughout the annealing process. 120 | 121 | **parameters: 122 | Parameters for the sampling method, specified by the child 123 | sampler. 124 | 125 | Returns: 126 | :class:`~dimod.SampleSet`. 127 | 128 | """ 129 | if h_tolerance < 0: 130 | raise ValueError("h_tolerance needs to be positive or zero") 131 | 132 | child = self.child 133 | qpu_properties = _innermost_child_properties(child) 134 | target_graph = child.to_networkx_graph() 135 | source_graph = dimod.to_networkx_graph(bqm) 136 | extended_j_range = qpu_properties["extended_j_range"] 137 | # flux_bias_range is not supported at the moment 138 | flux_bias_range = qpu_properties.get("flux_bias_range", default_flux_bias_range) 139 | 140 | # Positive couplings tend to have smaller control error, 141 | # we default to them if they have the same magnitude than negative couplings 142 | # See the ICE documentation at https://docs.dwavequantum.com/en/latest/quantum_research/errors.html#ice 143 | largest_j = max(extended_j_range[::-1], key=abs) 144 | largest_j_sign = np.sign(largest_j) 145 | 146 | # To implement the bias sign through flux bias sign, 147 | # we pick a range (magnitude) that we can sign-flip 148 | fb_magnitude = min(abs(b) for b in flux_bias_range) 149 | flux_biases = [0] * qpu_properties["num_qubits"] 150 | 151 | _bqm = bqm.copy() 152 | used_ancillas = defaultdict(list) 153 | for variable, bias in bqm.iter_linear(): 154 | if abs(bias) <= h_tolerance: 155 | continue 156 | if abs(bias) - h_tolerance > abs(largest_j): 157 | return ValueError( 158 | "linear biases larger than the strongest coupling are not supported" 159 | ) # TODO: implement larger biases through multiple ancillas 160 | 161 | available_ancillas = set(target_graph.adj[variable]) - source_graph.nodes() 162 | if not len(available_ancillas): 163 | raise ValueError(f"variable {variable} has no ancillas available") 164 | unused_ancillas = available_ancillas - used_ancillas.keys() 165 | if len(unused_ancillas): 166 | ancilla = unused_ancillas.pop() 167 | # bias sign is handled by the flux bias 168 | flux_biases[ancilla] = np.sign(bias) * largest_j_sign * fb_magnitude 169 | _bqm.add_interaction( 170 | variable, ancilla, (abs(bias) - h_tolerance) * largest_j_sign 171 | ) 172 | else: 173 | if qpu_properties["j_range"][0] <= bias <= qpu_properties["j_range"][1]: 174 | # If j can be sign-flipped, select the least used ancilla regardless of the flux bias sign 175 | ancilla = sorted( 176 | list(available_ancillas), key=lambda x: len(used_ancillas[x]) 177 | )[0] 178 | _bqm.add_interaction( 179 | variable, 180 | ancilla, 181 | (bias - h_tolerance * np.sign(bias)) 182 | * np.sign([flux_biases[ancilla]]), 183 | ) 184 | else: 185 | # Ancilla sharing is limited to flux biases with appropiate sign 186 | signed_ancillas = [ 187 | ancilla 188 | for ancilla in available_ancillas 189 | if largest_j_sign 190 | == np.sign(flux_biases[ancilla] * bias) 191 | ] 192 | if not len(signed_ancillas): 193 | return ValueError( 194 | f"variable {variable} has no ancillas available" 195 | ) 196 | else: 197 | ancilla = sorted( 198 | list(signed_ancillas), key=lambda x: len(used_ancillas[x]) 199 | )[0] 200 | _bqm.add_interaction( 201 | variable, 202 | ancilla, 203 | largest_j_sign * (abs(bias) - h_tolerance), 204 | ) 205 | 206 | used_ancillas[ancilla].append(variable) 207 | _bqm.set_linear(variable, h_tolerance * np.sign(bias)) 208 | 209 | sampleset = self.child.sample(_bqm, flux_biases=flux_biases, **parameters) 210 | yield 211 | yield dimod.SampleSet.from_samples_bqm( 212 | [ 213 | {k: v for k, v in sample.items() if k not in used_ancillas} 214 | for sample in sampleset.samples() 215 | ], 216 | bqm=bqm, 217 | info=sampleset.info.update(used_ancillas), 218 | ) 219 | 220 | 221 | def _innermost_child_properties(sampler: dimod.Sampler) -> Mapping[str, Any]: 222 | """Returns the properties of the inner-most child sampler in a composite. 223 | 224 | Args: 225 | sampler: A dimod sampler 226 | 227 | Returns: 228 | properties (dict): The properties of the inner-most sampler 229 | 230 | """ 231 | 232 | try: 233 | return _innermost_child_properties(sampler.child) 234 | except AttributeError: 235 | return sampler.properties 236 | -------------------------------------------------------------------------------- /dwave/system/composites/virtual_graph.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Deprecated and functionality removed. Behaves identically to 16 | :class:`~dwave.system.composites.embedding.FixedEmbeddingComposite`, and will 17 | be completely removed in dwave-system 2.0. 18 | 19 | Virtual graphs are deprecated due to improved calibration of newer QPUs; to 20 | calibrate chains for residual biases, follow the instructions in the 21 | `shimming tutorial `_. 22 | 23 | A :ref:`dimod composite ` that 24 | uses the D-Wave virtual graph feature for improved 25 | :term:`minor-embedding`. 26 | 27 | D-Wave *virtual graphs* simplify the process of minor-embedding by enabling you 28 | to more easily create, optimize, use, and reuse an embedding for a given working 29 | graph. When you submit an embedding and specify a chain strength using these 30 | tools, they automatically calibrate the qubits in a chain to compensate for the 31 | effects of biases that may be introduced as a result of strong couplings. 32 | 33 | See the :ref:`index_concepts` section 34 | for explanations of technical terms in descriptions of Ocean tools. 35 | """ 36 | 37 | import warnings 38 | 39 | import dimod 40 | 41 | from dwave.system.composites.embedding import FixedEmbeddingComposite 42 | 43 | __all__ = ['VirtualGraphComposite'] 44 | 45 | 46 | class VirtualGraphComposite(FixedEmbeddingComposite): 47 | """Removed. Used to provide access to the D-Wave virtual graph feature for 48 | minor-embedding, but now is just a thin wrapper around the 49 | :class:`~dwave.system.composites.embedding.FixedEmbeddingComposite`. 50 | 51 | .. deprecated:: 1.25.0 52 | This class is deprecated due to improved calibration of newer QPUs and 53 | will be removed in 1.27.0; to calibrate chains for residual biases, 54 | follow the instructions in the 55 | `shimming tutorial `_. 56 | 57 | .. versionremoved:: 1.28.0 58 | This class is now only a pass-through wrapper around the 59 | :class:`~dwave.system.composites.embedding.FixedEmbeddingComposite`. 60 | 61 | It will be completely removed in dwave-system 2.0. 62 | 63 | For removal reasons and alternatives, see the deprecation note above. 64 | """ 65 | 66 | def __init__(self, sampler, embedding, chain_strength=None, **kwargs): 67 | super(VirtualGraphComposite, self).__init__(sampler, embedding) 68 | 69 | warnings.warn( 70 | "'VirtualGraphComposite' functionality is removed due to improved " 71 | "calibration of newer QPUs and in future will raise an exception. " 72 | "Currently it's equivalent to 'FixedEmbeddingComposite'. If needed, " 73 | "follow the instructions in the shimming tutorial at " 74 | "https://github.com/dwavesystems/shimming-tutorial instead.", 75 | DeprecationWarning, stacklevel=2 76 | ) 77 | 78 | if chain_strength is not None: 79 | warnings.warn( 80 | "'chain_strength' parameter is ignored since dwave-system 1.28.", 81 | DeprecationWarning, stacklevel=2) 82 | 83 | # for API backwards compatibility 84 | self.parameters.update(apply_flux_bias_offsets=[]) 85 | self.chain_strength = chain_strength 86 | self.flux_biases = None 87 | 88 | @dimod.bqm_structured 89 | def sample(self, bqm, apply_flux_bias_offsets=True, **kwargs): 90 | return super(VirtualGraphComposite, self).sample(bqm, **kwargs) 91 | -------------------------------------------------------------------------------- /dwave/system/coupling_groups.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import dwave_networkx as dnx 16 | 17 | 18 | def coupling_groups(hardware_graph): 19 | """Generate groups of couplers for which a limit on total coupling applies for each group. 20 | 21 | Args: 22 | hardware_graph (:class:`networkx.Graph`): The hardware graph of a QPU. Note that only 23 | :term:`Zephyr` graphs have coupling groups. 24 | 25 | Yields: 26 | Lists of tuples, where each list is a group of couplers in ``hardware_graph``. 27 | 28 | Examples: 29 | This example prints the first coupling group of an |adv2| QPU. 30 | 31 | >>> from dwave.system.coupling_groups import coupling_groups 32 | >>> from dwave.system import DWaveSampler 33 | ... 34 | >>> qpu = DWaveSampler(solver=dict(topology__type='zephyr')) 35 | >>> couplings = coupling_groups(qpu.to_networkx_graph()) 36 | >>> print(next(couplings)) # doctest:+SKIP 37 | [(1, 0), (12, 0), (2496, 0), (2520, 0), (2544, 0), (2568, 0)] 38 | """ 39 | 40 | if hardware_graph.graph.get('family') != 'zephyr': 41 | return 42 | 43 | relabel = dnx.zephyr_coordinates(hardware_graph.graph['rows']).linear_to_zephyr 44 | 45 | for q in hardware_graph: 46 | groups = [], [] 47 | U, W, K, J, Z = relabel(q) 48 | 49 | for p in hardware_graph[q]: 50 | u, w, k, j, z = relabel(p) 51 | if U != u: 52 | groups[2*Z+J+1-w].append((p, q)) 53 | elif J != j: 54 | groups[Z+J-z].append((p, q)) 55 | else: 56 | groups[(z-Z)//2].append((p, q)) 57 | 58 | yield from groups 59 | -------------------------------------------------------------------------------- /dwave/system/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | class FailoverCondition(Exception): 17 | """QPU or Solver API call failed with an error that might be mitigated by 18 | retrying on a different solver. 19 | """ 20 | 21 | 22 | class RetryCondition(FailoverCondition): 23 | """QPU or Solver API call failed with an error that might be mitigated by 24 | retrying on the same solver. 25 | """ 26 | -------------------------------------------------------------------------------- /dwave/system/flux_bias_offsets.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Copyright 2018 D-Wave Systems Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import warnings 17 | 18 | 19 | def get_flux_biases(*args, **kwargs): 20 | """Removed. Used to get the flux bias offsets for sampler and embedding, 21 | but now just returns an empty dictionary. 22 | 23 | .. versionremoved:: 1.28.0 24 | Due to improved calibration of newer QPUs, balancing chains with 25 | the flux biases, as it used to be implemented by this function (and the 26 | obsoleted package ``dwave-drivers``) is no longer supported. 27 | 28 | To calibrate chains for residual biases, follow the instructions in the 29 | `shimming tutorial `_. 30 | 31 | This function (and its submodule) will be completely removed in 32 | dwave-system 2.0. 33 | 34 | Returns: 35 | dict: 36 | An empty dict, since dwave-system 1.28. 37 | Flux biases are not calculated/set for nodes/chains anymore. 38 | """ 39 | 40 | warnings.warn( 41 | "'get_flux_biases' functionality is removed due to improved calibration " 42 | "of newer QPUs and in future will raise an exception; if needed, " 43 | "follow the instructions in the shimming tutorial at " 44 | "https://github.com/dwavesystems/shimming-tutorial instead. ", 45 | DeprecationWarning, stacklevel=2 46 | ) 47 | 48 | return {} 49 | -------------------------------------------------------------------------------- /dwave/system/package_info.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | __all__ = ['__version__', '__author__', '__authoremail__', '__description__'] 16 | 17 | __version__ = '1.32.0' 18 | __author__ = 'D-Wave Systems Inc.' 19 | __authoremail__ = 'tools@dwavesys.com' 20 | __description__ = 'All things D-Wave System.' 21 | -------------------------------------------------------------------------------- /dwave/system/samplers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import typing 16 | 17 | 18 | class ResultInfoDict(typing.TypedDict, total=False): 19 | """Returned in ``SampleSet.info`` and ``LeapHybridNLSampler.SampleResult.info``. 20 | 21 | Not all fields defined below are always set, and additional might be set. 22 | """ 23 | 24 | timing: dict[str, float] 25 | warnings: list[str] 26 | problem_id: str 27 | problem_label: str 28 | problem_data_id: str 29 | 30 | 31 | from dwave.system.samplers.clique import * 32 | from dwave.system.samplers.dwave_sampler import * 33 | from dwave.system.samplers.leap_hybrid_sampler import * 34 | -------------------------------------------------------------------------------- /dwave/system/schedules.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | def ramp(s, width, annealing_time): 17 | """Schedule with a ramp shape. 18 | 19 | Args: 20 | s (float): 21 | The mid-point of the ramp, as a fraction of the annealing time. 22 | 23 | width (float): 24 | The width of the ramp, as a fraction of the annealing time. Note 25 | that QPUs have a maximum slope. 26 | 27 | annealing_time (float): 28 | The total annealing time, in microseconds. 29 | 30 | Returns: 31 | list[2-tuple]: The points defining in a piece-wise curve in the shape of 32 | a ramp. 33 | 34 | Examples: 35 | This example constructs a schedule for a QPU that supports 36 | configuring an `h_gain_schedule`. 37 | 38 | >>> sampler = DWaveSampler(solver=dict(annealing_time=True, 39 | h_gain_schedule=True)) 40 | >>> h = {v: -1 for v in sampler.nodelist} 41 | >>> schedule = ramp(.5, .2, sampler.properties['default_annealing_time']) 42 | >>> sampleset = sampler.sample_ising(h, {}, h_gain_schedule=schedule) 43 | 44 | """ 45 | if s <= 0 or s >= 1: 46 | raise ValueError("s should be in interval (0, 1)") 47 | if width >= min(s, 1 - s) / 2: 48 | raise ValueError("given width takes curve outside of [0, 1] interval") 49 | 50 | return [(0, 0), 51 | (annealing_time * (s - width / 2), 0), 52 | (annealing_time * (s + width / 2), 1), 53 | (annealing_time, 1)] 54 | -------------------------------------------------------------------------------- /releasenotes/notes/add-linear-ancilla-composite-3281ed6733b0f0c7.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add `LinearAncillaComposite` for implementing linear coefficients through ancilla qubits polarized with strong flux biases 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | dimod==0.12.20 2 | dwave-optimization==0.4.0 3 | dwave-preprocessing==0.6.7 4 | dwave-cloud-client==0.12.0 5 | dwave-networkx==0.8.14 6 | dwave-samplers==1.4.0 7 | homebase==1.0.1 8 | minorminer==0.2.19 9 | networkx~=3.0 10 | 11 | numpy==1.23.5;python_version<"3.11" # for compatibility with scipy 1.9.3 12 | numpy==2.1.0;python_version>="3.13" 13 | 14 | scipy==1.9.3;python_version<"3.11" 15 | scipy==1.14.1;python_version>="3.11" 16 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description_file = README.rst 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | 17 | from setuptools import setup, find_namespace_packages 18 | 19 | 20 | # change directories so this works when called from other locations. Useful in build systems. 21 | setup_folder_loc = os.path.dirname(os.path.abspath(__file__)) 22 | os.chdir(setup_folder_loc) 23 | 24 | exec(open(os.path.join(".", "dwave", "system", "package_info.py")).read()) 25 | 26 | 27 | install_requires = ['dimod>=0.12.20,<0.14.0', 28 | 'dwave-optimization>=0.1.0,<0.8', 29 | 'dwave-cloud-client>=0.12.0,<0.14.0', 30 | 'dwave-networkx>=0.8.10', 31 | 'dwave-preprocessing>=0.5.0', 32 | 'homebase>=1.0.0,<2.0.0', 33 | 'minorminer>=0.2.19,<0.3.0', # lower bound for parallel embedding support 34 | 'numpy>=1.21.6', # minimum inherited from minorminer 35 | 'dwave-samplers>=1.0.0', 36 | 'scipy>=1.7.3', 37 | ] 38 | 39 | python_requires = '>=3.9' 40 | 41 | packages = find_namespace_packages(include=['dwave.*']) 42 | 43 | classifiers = [ 44 | 'License :: OSI Approved :: Apache Software License', 45 | 'Operating System :: OS Independent', 46 | 'Development Status :: 3 - Alpha', 47 | 'Programming Language :: Python :: 3 :: Only', 48 | 'Programming Language :: Python :: 3.9', 49 | 'Programming Language :: Python :: 3.10', 50 | 'Programming Language :: Python :: 3.11', 51 | 'Programming Language :: Python :: 3.12', 52 | 'Programming Language :: Python :: 3.13', 53 | ] 54 | 55 | setup( 56 | name='dwave-system', 57 | version=__version__, 58 | author=__author__, 59 | author_email=__authoremail__, 60 | description=__description__, 61 | long_description=open('README.rst').read(), 62 | url='https://github.com/dwavesystems/dwave-system', 63 | license='Apache 2.0', 64 | packages=packages, 65 | install_requires=install_requires, 66 | python_requires=python_requires, 67 | classifiers=classifiers, 68 | zip_safe=False 69 | ) 70 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwavesystems/dwave-system/7e9b6dad09bcce00c2b80bc0f48d9f3197769f30/tests/__init__.py -------------------------------------------------------------------------------- /tests/qpu/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwavesystems/dwave-system/7e9b6dad09bcce00c2b80bc0f48d9f3197769f30/tests/qpu/__init__.py -------------------------------------------------------------------------------- /tests/qpu/test_dwavecliquesampler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import itertools 16 | import os 17 | import unittest 18 | 19 | import dimod 20 | 21 | import dwave.cloud.exceptions 22 | from dwave.system import DWaveCliqueSampler 23 | 24 | from parameterized import parameterized 25 | 26 | _SAMPLERS = {} 27 | 28 | 29 | def get_sampler(topology): 30 | if topology in _SAMPLERS: 31 | return _SAMPLERS[topology] 32 | try: 33 | _SAMPLERS[topology] = DWaveCliqueSampler(solver=dict(topology__type=topology.lower())) 34 | return _SAMPLERS[topology] 35 | except (ValueError, 36 | dwave.cloud.exceptions.ConfigFileError, 37 | dwave.cloud.exceptions.SolverNotFoundError): 38 | raise unittest.SkipTest(f"no {topology}-structured QPU available") 39 | 40 | 41 | def tearDownModule(): 42 | # make sure all cached samplers are closed and resources released at exit 43 | for sampler in _SAMPLERS.values(): 44 | sampler.close() 45 | 46 | 47 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 48 | class TestDWaveCliqueSampler(unittest.TestCase): 49 | @parameterized.expand([['Pegasus'], ['Zephyr']]) 50 | def test_bias_range(self, topology): 51 | sampler = get_sampler(topology) 52 | n = sampler.largest_clique_size 53 | sampler.sample_ising({v: 7*187 for v in range(n)}, {}).resolve() 54 | 55 | @parameterized.expand([['Pegasus'], ['Zephyr']]) 56 | def test_maximum_ferromagnet(self, topology): 57 | sampler = get_sampler(topology) 58 | 59 | dimod.testing.assert_sampler_api(sampler) 60 | 61 | bqm = dimod.BinaryQuadraticModel('SPIN') 62 | for u, v in itertools.combinations(sampler.largest_clique(), 2): 63 | bqm.quadratic[u, v] = -1 64 | 65 | sampler.sample(bqm).resolve() 66 | 67 | @parameterized.expand(itertools.product(('Pegasus', 'Zephyr'), (None, 0.5))) 68 | def test_per_qubit_coupling_range(self, topology, chain_strength): 69 | sampler = get_sampler(topology) 70 | n = sampler.largest_clique_size 71 | 72 | bqm = dimod.BinaryQuadraticModel({}, 73 | {(u, v): -2 for u in range(n) for v in range(u+1, n)}, 'SPIN') 74 | 75 | # if the range was not adjusted, this would raise an error. 76 | sampler.sample(bqm, chain_strength=chain_strength).resolve() 77 | 78 | @unittest.skipUnless(hasattr(dwave.cloud.exceptions, 'UseAfterClose'), 'dwave-cloud-client>=0.13.3 required') 79 | def test_close(self): 80 | sampler = DWaveCliqueSampler() 81 | sampler.close() 82 | 83 | with self.assertRaises(dwave.cloud.exceptions.UseAfterCloseError): 84 | sampler.sample_qubo({(0, 1): 1}) 85 | -------------------------------------------------------------------------------- /tests/qpu/test_dwavesampler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import threading 16 | import unittest 17 | import os 18 | from unittest import mock 19 | 20 | import numpy 21 | 22 | import dimod 23 | from dwave.cloud.exceptions import ConfigFileError, SolverNotFoundError 24 | from dwave.cloud.client import Client 25 | 26 | from dwave.system.samplers import DWaveSampler 27 | 28 | 29 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 30 | class TestDWaveSampler(unittest.TestCase): 31 | 32 | @classmethod 33 | def setUpClass(cls): 34 | try: 35 | cls.qpu = DWaveSampler() 36 | except (ValueError, ConfigFileError, SolverNotFoundError): 37 | raise unittest.SkipTest("no qpu available") 38 | 39 | @classmethod 40 | def tearDownClass(cls): 41 | cls.qpu.client.close() 42 | 43 | def test_close(self): 44 | n_initial = threading.active_count() 45 | sampler = DWaveSampler() 46 | n_active = threading.active_count() 47 | sampler.close() 48 | n_closed = threading.active_count() 49 | 50 | with self.subTest('verify all client threads shutdown'): 51 | self.assertGreater(n_active, n_initial) 52 | self.assertEqual(n_closed, n_initial) 53 | 54 | try: 55 | # requires `dwave-cloud-client>=0.13.3` 56 | from dwave.cloud.exceptions import UseAfterCloseError 57 | except: 58 | pass 59 | else: 60 | with self.subTest('verify use after close disallowed'): 61 | with self.assertRaises(UseAfterCloseError): 62 | h, J = self.nonzero_ising_problem(sampler) 63 | sampler.sample_ising(h, J) 64 | 65 | with self.subTest('verify context manager calls close'): 66 | n_initial = threading.active_count() 67 | with DWaveSampler(): 68 | n_active = threading.active_count() 69 | n_closed = threading.active_count() 70 | 71 | self.assertGreater(n_active, n_initial) 72 | self.assertEqual(n_closed, n_initial) 73 | 74 | @staticmethod 75 | def nonzero_ising_problem(sampler): 76 | max_h = max(sampler.properties.get('h_range', [-1, 1])) 77 | max_j = max(sampler.properties.get('j_range', [-1, 1])) 78 | 79 | h = {v: max_h for v in sampler.nodelist} 80 | J = {interaction: max_j for interaction in sampler.edgelist} 81 | 82 | return h, J 83 | 84 | def test_sample_ising(self): 85 | sampler = self.qpu 86 | 87 | h, J = self.nonzero_ising_problem(sampler) 88 | 89 | sampleset = sampler.sample_ising(h, J) 90 | 91 | bqm = dimod.BQM.from_ising(h, J) 92 | numpy.testing.assert_array_almost_equal( 93 | bqm.energies(sampleset), sampleset.record.energy) 94 | 95 | def test_sample_qubo(self): 96 | sampler = self.qpu 97 | 98 | Q, _ = dimod.ising_to_qubo(*self.nonzero_ising_problem(sampler)) 99 | 100 | sampleset = sampler.sample_qubo(Q) 101 | 102 | bqm = dimod.BQM.from_qubo(Q) 103 | numpy.testing.assert_array_almost_equal( 104 | bqm.energies(sampleset), sampleset.record.energy) 105 | 106 | def test_sample_bqm_ising(self): 107 | sampler = self.qpu 108 | 109 | bqm = dimod.BQM.from_ising(*self.nonzero_ising_problem(sampler)) 110 | 111 | sampleset = sampler.sample(bqm) 112 | 113 | numpy.testing.assert_array_almost_equal( 114 | bqm.energies(sampleset), sampleset.record.energy) 115 | 116 | def test_sample_bqm_qubo(self): 117 | sampler = self.qpu 118 | 119 | Q, _ = dimod.ising_to_qubo(*self.nonzero_ising_problem(sampler)) 120 | bqm = dimod.BQM.from_qubo(Q) 121 | 122 | sampleset = sampler.sample(bqm) 123 | 124 | numpy.testing.assert_array_almost_equal( 125 | bqm.energies(sampleset), sampleset.record.energy) 126 | 127 | def test_mismatched_ising(self): 128 | sampler = self.qpu 129 | 130 | h = {len(sampler.nodelist)*100: 1} # get a qubit we know isn't there 131 | J = {} 132 | 133 | with self.assertRaises(dimod.exceptions.BinaryQuadraticModelStructureError): 134 | sampler.sample_ising(h, J).resolve() 135 | 136 | def test_mismatched_qubo(self): 137 | sampler = self.qpu 138 | 139 | v = len(sampler.nodelist)*100 140 | Q = {(v, v): -1} 141 | 142 | with self.assertRaises(dimod.exceptions.BinaryQuadraticModelStructureError): 143 | sampler.sample_qubo(Q).resolve() 144 | 145 | def test_problem_labelling(self): 146 | sampler = self.qpu 147 | h, J = self.nonzero_ising_problem(sampler) 148 | label = 'problem label' 149 | 150 | # label set 151 | sampleset = sampler.sample_ising(h, J, label=label) 152 | self.assertIn('problem_label', sampleset.info) 153 | self.assertEqual(sampleset.info['problem_label'], label) 154 | 155 | # label unset 156 | sampleset = sampler.sample_ising(h, J) 157 | self.assertNotIn('problem_label', sampleset.info) 158 | 159 | 160 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 161 | class TestMissingQubits(unittest.TestCase): 162 | 163 | @classmethod 164 | def setUpClass(cls): 165 | try: 166 | with Client.from_config() as client: 167 | solvers = client.get_solvers(qpu=True) 168 | 169 | if not solvers: 170 | raise unittest.SkipTest("no qpu found") 171 | 172 | # select a QPU with less than 100% yield 173 | for solver in solvers: 174 | if solver.num_active_qubits < solver.num_qubits: 175 | cls.qpu = DWaveSampler(solver=solver.id) 176 | return 177 | 178 | raise unittest.SkipTest("no qpu with less than 100% yield found") 179 | 180 | except (ValueError, ConfigFileError, SolverNotFoundError): 181 | raise unittest.SkipTest("no qpu available") 182 | 183 | @classmethod 184 | def tearDownClass(cls): 185 | cls.qpu.client.close() 186 | 187 | def test_sample_ising_h_list(self): 188 | sampler = self.qpu 189 | 190 | h = [0 for _ in range(self.qpu.solver.num_qubits)] 191 | J = {edge: 0 for edge in sampler.edgelist} 192 | 193 | sampleset = sampler.sample_ising(h, J) 194 | 195 | self.assertEqual(set(sampleset.variables), set(sampler.nodelist)) 196 | self.assertLessEqual(len(sampleset.variables), self.qpu.solver.num_qubits) # sanity check 197 | 198 | 199 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 200 | class TestClientSelection(unittest.TestCase): 201 | 202 | def test_client_type(self): 203 | with mock.patch('dwave.cloud.client.qpu.Client') as qpu: 204 | self.assertEqual(DWaveSampler().client, qpu()) 205 | self.assertEqual(DWaveSampler(client='qpu').client, qpu()) 206 | 207 | with mock.patch('dwave.cloud.client.sw.Client') as sw: 208 | self.assertEqual(DWaveSampler(client='sw').client, sw()) 209 | 210 | with mock.patch('dwave.cloud.client.hybrid.Client') as hybrid: 211 | self.assertEqual(DWaveSampler(client='hybrid').client, hybrid()) 212 | 213 | def test_base_client(self): 214 | # to test 'base' client instantiation offline, 215 | # we would need a mock client and a mock solver 216 | try: 217 | self.assertEqual(type(DWaveSampler(client=None).client), Client) 218 | self.assertEqual(type(DWaveSampler(client='base').client), Client) 219 | except (ValueError, ConfigFileError): 220 | raise unittest.SkipTest("no API token available") 221 | -------------------------------------------------------------------------------- /tests/qpu/test_embeddingcomposite.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import unittest 17 | import warnings 18 | 19 | import dimod 20 | 21 | from dwave.cloud.exceptions import ConfigFileError, SolverNotFoundError 22 | 23 | from dwave.system import DWaveSampler, EmbeddingComposite 24 | 25 | 26 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 27 | class TestEmbeddingCompositeExactSolver(unittest.TestCase): 28 | 29 | @classmethod 30 | def setUpClass(cls): 31 | try: 32 | cls.qpu = DWaveSampler( 33 | solver=dict(initial_state=True, anneal_schedule=True)) 34 | except (ValueError, ConfigFileError, SolverNotFoundError): 35 | raise unittest.SkipTest("no qpu available") 36 | 37 | @classmethod 38 | def tearDownClass(cls): 39 | cls.qpu.client.close() 40 | 41 | def test_initial_state(self): 42 | sampler = EmbeddingComposite(self.qpu) 43 | 44 | bqm = dimod.BinaryQuadraticModel.from_ising({'a': 2.0, 'b': -2.0}, 45 | {('a', 'b'): -1}) 46 | 47 | kwargs = {'initial_state': {'a': 1, 'b': 1}, 48 | 'anneal_schedule': [(0, 1), (55.0, 0.45), 49 | (155.0, 0.45), (210.0, 1)]} 50 | 51 | sampler.sample(bqm, **kwargs).resolve() 52 | 53 | def test_many_bqm_async(self): 54 | sampler = EmbeddingComposite(self.qpu) 55 | 56 | # in the future it would be good to test a wide variety of BQMs, 57 | # see https://github.com/dwavesystems/dimod/issues/671 58 | # but for now let's just test a few 59 | bqm0 = dimod.BinaryQuadraticModel.from_ising({'a': 2.0, 'b': -2.0}, 60 | {('a', 'b'): -1}) 61 | bqm1 = dimod.BinaryQuadraticModel.from_ising({2: 4}, 62 | {(0, 1): 1.5, (1, 2): 5}) 63 | 64 | samplesets0 = [] 65 | samplesets1 = [] 66 | for _ in range(10): 67 | # this should be async 68 | samplesets0.append(sampler.sample(bqm0)) 69 | samplesets1.append(sampler.sample(bqm1)) 70 | 71 | if all(ss.done() for ss in samplesets0): 72 | warnings.warn("Sampler calls appear to be synchronous") 73 | 74 | for ss0, ss1 in zip(samplesets0, samplesets1): 75 | dimod.testing.assert_sampleset_energies(ss0, bqm0) 76 | dimod.testing.assert_sampleset_energies(ss1, bqm1) 77 | 78 | self.assertTrue(all(ss.done() for ss in samplesets0)) 79 | self.assertTrue(all(ss.done() for ss in samplesets1)) 80 | -------------------------------------------------------------------------------- /tests/qpu/test_leaphybrid_common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | import os 17 | import json 18 | import threading 19 | import unittest 20 | 21 | from parameterized import parameterized_class 22 | 23 | import dimod 24 | from dwave.cloud.exceptions import ConfigFileError, SolverNotFoundError 25 | from dwave.cloud.package_info import __version__ as cc_version 26 | from dwave.cloud.testing import isolated_environ 27 | 28 | from dwave.system import LeapHybridSampler, LeapHybridDQMSampler, LeapHybridCQMSampler 29 | 30 | 31 | @parameterized_class( 32 | ("problem_type", "sampler_cls"), [ 33 | ("bqm", LeapHybridSampler), 34 | ("dqm", LeapHybridDQMSampler), 35 | ("cqm", LeapHybridCQMSampler), 36 | ]) 37 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 38 | class TestLegacySolverSelection(unittest.TestCase): 39 | 40 | def test_baseline(self): 41 | # kwarg-level override should always work 42 | try: 43 | sampler = self.sampler_cls(solver=self.sampler_cls.default_solver) 44 | except (ValueError, ConfigFileError): 45 | raise unittest.SkipTest("config error") 46 | 47 | self.assertIn(self.problem_type, sampler.solver.supported_problem_types) 48 | 49 | sampler.close() 50 | 51 | def test_new_positive(self): 52 | # given valid solver spec in env, init succeeds 53 | solver = json.dumps(self.sampler_cls.default_solver) 54 | with isolated_environ(add={'DWAVE_API_SOLVER': solver}, 55 | remove=('DWAVE_FEATURE_FLAGS',)): 56 | try: 57 | sampler = self.sampler_cls() 58 | except (ValueError, ConfigFileError): 59 | raise unittest.SkipTest("config error") 60 | 61 | self.assertIn(self.problem_type, sampler.solver.supported_problem_types) 62 | 63 | sampler.close() 64 | 65 | def test_new_negative(self): 66 | # given invalid solver spec in env, init fails 67 | solver = json.dumps(dict(qpu=True)) 68 | with isolated_environ(add={'DWAVE_API_SOLVER': solver}, 69 | remove=('DWAVE_FEATURE_FLAGS',)): 70 | with self.assertRaises(SolverNotFoundError): 71 | try: 72 | sampler = self.sampler_cls() 73 | except (ValueError, ConfigFileError): 74 | raise unittest.SkipTest("config error") 75 | 76 | def test_old(self): 77 | # given invalid solver spec in env, init succeeds 78 | solver = json.dumps(dict(qpu=True)) 79 | flags = json.dumps(dict(hss_solver_config_override=True)) 80 | with isolated_environ(add={'DWAVE_API_SOLVER': solver, 81 | 'DWAVE_FEATURE_FLAGS': flags}): 82 | try: 83 | sampler = self.sampler_cls() 84 | except (ValueError, ConfigFileError): 85 | raise unittest.SkipTest("config error") 86 | 87 | self.assertIn(self.problem_type, sampler.solver.supported_problem_types) 88 | 89 | sampler.close() 90 | 91 | 92 | @parameterized_class( 93 | ("sampler_cls", "sample_meth", "problem_gen"), [ 94 | (LeapHybridSampler, "sample", lambda self: dimod.BQM.from_qubo({})), 95 | (LeapHybridDQMSampler, "sample_dqm", lambda self: dimod.DQM.from_numpy_vectors([0], [0], ([], [], []))), 96 | (LeapHybridCQMSampler, "sample_cqm", lambda self: dimod.CQM.from_bqm(dimod.BQM.from_qubo({'ab': 1}))), 97 | ]) 98 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 99 | class TestSamplerInterface(unittest.TestCase): 100 | 101 | @classmethod 102 | def setUpClass(cls): 103 | try: 104 | cls.sampler = cls.sampler_cls() 105 | except (ValueError, ConfigFileError, SolverNotFoundError): 106 | raise unittest.SkipTest(f"{cls.sampler_cls} not available") 107 | 108 | @classmethod 109 | def tearDownClass(cls): 110 | cls.sampler.close() 111 | 112 | def test_sampleset_wait_id_availability(self): 113 | # verify https://github.com/dwavesystems/dwave-system/issues/540 is fixed 114 | problem = self.problem_gen() 115 | ss = getattr(self.sampler, self.sample_meth)(problem) 116 | 117 | with self.subTest("sampleset.wait_id() exists"): 118 | pid = ss.wait_id() 119 | self.assertIsInstance(pid, str) 120 | 121 | with self.subTest("sampleset.wait_id() exists post-resolve"): 122 | ss.resolve() 123 | pid_post = ss.wait_id() 124 | self.assertEqual(pid, pid_post) 125 | 126 | @unittest.skipIf(tuple(map(int, cc_version.split('.'))) < (0, 13, 5), 127 | "'dwave-cloud-client>=0.13.5' required") 128 | def test_problem_data_id_available(self): 129 | problem = self.problem_gen() 130 | ss = getattr(self.sampler, self.sample_meth)(problem) 131 | 132 | self.assertIn('problem_id', ss.info) 133 | self.assertIn('problem_data_id', ss.info) 134 | 135 | def test_close(self): 136 | n_initial = threading.active_count() 137 | sampler = self.sampler_cls() 138 | n_active = threading.active_count() 139 | sampler.close() 140 | n_closed = threading.active_count() 141 | 142 | with self.subTest('verify all client threads shutdown'): 143 | self.assertGreater(n_active, n_initial) 144 | self.assertEqual(n_closed, n_initial) 145 | 146 | try: 147 | # requires `dwave-cloud-client>=0.13.3` 148 | from dwave.cloud.exceptions import UseAfterCloseError 149 | except: 150 | pass 151 | else: 152 | with self.subTest('verify use after close disallowed'): 153 | with self.assertRaises(UseAfterCloseError): 154 | problem = self.problem_gen() 155 | getattr(sampler, self.sample_meth)(problem) 156 | 157 | with self.subTest('verify context manager calls close'): 158 | n_initial = threading.active_count() 159 | with self.sampler_cls(): 160 | n_active = threading.active_count() 161 | n_closed = threading.active_count() 162 | 163 | self.assertGreater(n_active, n_initial) 164 | self.assertEqual(n_closed, n_initial) 165 | -------------------------------------------------------------------------------- /tests/qpu/test_leaphybridcqmsampler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import unittest 17 | 18 | import dimod 19 | from dwave.cloud.exceptions import ConfigFileError, SolverNotFoundError 20 | 21 | from dwave.system import LeapHybridCQMSampler 22 | 23 | 24 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 25 | class TestLeapHybridSampler(unittest.TestCase): 26 | 27 | @classmethod 28 | def setUpClass(cls): 29 | try: 30 | cls.sampler = LeapHybridCQMSampler() 31 | except (ValueError, ConfigFileError, SolverNotFoundError): 32 | raise unittest.SkipTest("hybrid sampler not available") 33 | 34 | @classmethod 35 | def tearDownClass(cls): 36 | cls.sampler.close() 37 | 38 | def assertConsistent(self, cqm, sampleset): 39 | self.assertEqual(cqm.variables, sampleset.variables) 40 | 41 | # todo: check the vartypes etc 42 | 43 | def test_small(self): 44 | # submit 3-variable CQMs of binary, spin, integer and mixed 45 | objectives = dict() 46 | 47 | objectives['binary'] = dimod.BQM({'a': 1, 'b': 1, 'c': 1}, {}, 0, 'BINARY') 48 | objectives['spin'] = dimod.BQM({'a': 1, 'b': 1, 'c': 1}, {}, 0, 'SPIN') 49 | 50 | objectives['integer'] = integer = dimod.QM() 51 | integer.add_variables_from('INTEGER', 'abc') 52 | 53 | objectives['mixed'] = mixed = dimod.QM() 54 | mixed.add_variable('BINARY', 'a') 55 | mixed.add_variable('SPIN', 'b') 56 | mixed.add_variable('INTEGER', 'c') 57 | 58 | for vartype, model in objectives.items(): 59 | with self.subTest(f"one constraint, vartype={vartype}"): 60 | cqm = dimod.ConstrainedQuadraticModel() 61 | cqm.set_objective(model) 62 | cqm.add_constraint(model, rhs=0, sense='==') 63 | sampleset = self.sampler.sample_cqm(cqm) 64 | self.assertConsistent(cqm, sampleset) 65 | 66 | with self.subTest(f"no constraints, vartype={vartype}"): 67 | cqm = dimod.ConstrainedQuadraticModel() 68 | cqm.set_objective(model) 69 | sampleset = self.sampler.sample_cqm(cqm) 70 | self.assertConsistent(cqm, sampleset) 71 | 72 | def test_other_quadratic_model(self): 73 | with self.assertRaises(TypeError): 74 | self.sampler.sample_cqm(dimod.BQM('SPIN')) 75 | -------------------------------------------------------------------------------- /tests/qpu/test_leaphybriddqmsampler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | import os 17 | import unittest 18 | 19 | import dimod 20 | import numpy as np 21 | from dwave.cloud.exceptions import ConfigFileError, SolverNotFoundError 22 | 23 | from dwave.system import LeapHybridDQMSampler 24 | 25 | 26 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 27 | class TestLeapHybridSampler(unittest.TestCase): 28 | 29 | @classmethod 30 | def setUpClass(cls): 31 | try: 32 | cls.sampler = LeapHybridDQMSampler() 33 | except (ValueError, ConfigFileError, SolverNotFoundError): 34 | raise unittest.SkipTest("hybrid sampler not available") 35 | 36 | @classmethod 37 | def tearDownClass(cls): 38 | cls.sampler.close() 39 | 40 | def test_smoke(self): 41 | dqm = dimod.DQM() 42 | u = dqm.add_variable(2, 'a') 43 | v = dqm.add_variable(3) 44 | dqm.set_linear(u, [1, 2]) 45 | dqm.set_quadratic(u, v, {(0, 1): 1, (0, 2): 1}) 46 | 47 | try: 48 | # dimod 0.10+ 49 | dqm.offset += 3 50 | except AttributeError: 51 | pass 52 | 53 | sampleset = self.sampler.sample_dqm(dqm) 54 | 55 | np.testing.assert_array_almost_equal(dqm.energies(sampleset), 56 | sampleset.record.energy) 57 | 58 | def test_smoke_case_label(self): 59 | try: 60 | from dimod import CaseLabelDQM 61 | except ImportError: 62 | self.skipTest("need dimod 0.10+") 63 | 64 | dqm = CaseLabelDQM() 65 | u = dqm.add_variable({'red', 'green', 'blue'}, shared_labels=True) 66 | dqm.offset = 5 67 | 68 | sampleset = self.sampler.sample_dqm(dqm) 69 | 70 | np.testing.assert_array_almost_equal(dqm.energies(sampleset), 71 | sampleset.record.energy) 72 | 73 | def test_problem_labelling(self): 74 | dqm = dimod.DQM() 75 | u = dqm.add_variable(2, 'a') 76 | v = dqm.add_variable(3) 77 | dqm.set_linear(u, [1, 2]) 78 | dqm.set_quadratic(u, v, {(0, 1): 1, (0, 2): 1}) 79 | label = 'problem label' 80 | 81 | sampleset = self.sampler.sample_dqm(dqm, label=label) 82 | self.assertIn('problem_id', sampleset.info) 83 | self.assertIn('problem_label', sampleset.info) 84 | self.assertEqual(sampleset.info['problem_label'], label) 85 | -------------------------------------------------------------------------------- /tests/qpu/test_leaphybridsampler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | import os 17 | import unittest 18 | 19 | import dimod 20 | from dwave.cloud.exceptions import ConfigFileError, SolverNotFoundError 21 | 22 | from dwave.system import LeapHybridSampler 23 | 24 | 25 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 26 | # @dimod.testing.load_sampler_bqm_tests(sampler) # these take a while 27 | class TestLeapHybridSampler(unittest.TestCase): 28 | 29 | @classmethod 30 | def setUpClass(cls): 31 | try: 32 | cls.sampler = LeapHybridSampler() 33 | except (ValueError, ConfigFileError, SolverNotFoundError): 34 | raise unittest.SkipTest("hybrid sampler not available") 35 | 36 | @classmethod 37 | def tearDownClass(cls): 38 | cls.sampler.close() 39 | 40 | def test_smoke(self): 41 | sampleset = self.sampler.sample_ising({'a': -1}, {'ab': 1}) 42 | sampleset.resolve() 43 | 44 | def test_problem_labelling(self): 45 | bqm = dimod.BQM.from_ising({'a': -1}, {'ab': 1}) 46 | label = 'problem label' 47 | 48 | # label set 49 | sampleset = self.sampler.sample(bqm, label=label) 50 | self.assertIn('problem_id', sampleset.info) 51 | self.assertIn('problem_label', sampleset.info) 52 | self.assertEqual(sampleset.info['problem_label'], label) 53 | 54 | # label unset 55 | sampleset = self.sampler.sample(bqm) 56 | self.assertIn('problem_id', sampleset.info) 57 | self.assertNotIn('problem_label', sampleset.info) 58 | -------------------------------------------------------------------------------- /tests/qpu/test_schedules.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import unittest 17 | 18 | from dwave.cloud.exceptions import ConfigFileError, SolverNotFoundError 19 | 20 | from dwave.system.schedules import ramp 21 | from dwave.system.samplers import DWaveSampler 22 | 23 | 24 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 25 | class TestRamp(unittest.TestCase): 26 | @classmethod 27 | def setUpClass(cls): 28 | try: 29 | cls.qpu = DWaveSampler(solver=dict(h_gain_schedule=True)) 30 | except (ValueError, ConfigFileError, SolverNotFoundError): 31 | raise unittest.SkipTest("no qpu available") 32 | 33 | @classmethod 34 | def tearDownClass(cls): 35 | cls.qpu.client.close() 36 | 37 | def test_with_h_gain_schedule(self): 38 | sampler = self.qpu 39 | 40 | schedule = ramp(.5, .2, sampler.properties['default_annealing_time']) 41 | 42 | sampler.validate_anneal_schedule(schedule) 43 | 44 | h = {v: 1 for v in sampler.nodelist} 45 | 46 | sampleset = sampler.sample_ising(h, {}, h_gain_schedule=schedule) 47 | sampleset.record # resolve the future 48 | -------------------------------------------------------------------------------- /tests/qpu/test_virtual_graph_composite.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import itertools 17 | import unittest 18 | 19 | import minorminer 20 | import dimod.testing as dtest 21 | 22 | from dwave.cloud.exceptions import ConfigFileError, SolverNotFoundError 23 | 24 | from dwave.system.composites import VirtualGraphComposite 25 | from dwave.system.samplers import DWaveSampler 26 | 27 | 28 | @unittest.skipIf(os.getenv('SKIP_INT_TESTS'), "Skipping integration test.") 29 | class TestVirtualGraphComposite(unittest.TestCase): 30 | 31 | @classmethod 32 | def setUpClass(cls): 33 | try: 34 | cls.qpu = DWaveSampler(solver=dict(flux_biases=True)) 35 | except (ValueError, ConfigFileError, SolverNotFoundError): 36 | raise unittest.SkipTest("no qpu available") 37 | 38 | @classmethod 39 | def tearDownClass(cls): 40 | cls.qpu.client.close() 41 | 42 | def test_construction(self): 43 | child_sampler = self.qpu 44 | 45 | # get an embedding 46 | K10_edges = list(itertools.combinations(range(10), 2)) 47 | embedding = minorminer.find_embedding(K10_edges, child_sampler.edgelist) 48 | 49 | sampler = VirtualGraphComposite(child_sampler, embedding, 50 | flux_bias_num_reads=10) 51 | 52 | dtest.assert_sampler_api(sampler) 53 | 54 | h = {} 55 | J = {edge: -1 for edge in K10_edges} 56 | 57 | # run with fbo 58 | sampler.sample_ising(h, J).resolve() 59 | 60 | # and again without 61 | sampler.sample_ising(h, J, apply_flux_bias_offsets=False).resolve() 62 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | parameterized==0.7.4 2 | 3 | coverage>=7.0.0 # native namespace package support added in 7.0.0 4 | codecov 5 | -------------------------------------------------------------------------------- /tests/test_composites_common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 D-Wave 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | from unittest import mock 17 | 18 | import dimod 19 | from parameterized import parameterized_class 20 | 21 | from dwave.system.testing import MockDWaveSampler 22 | from dwave.system.composites import ( 23 | CutOffComposite, PolyCutOffComposite, 24 | EmbeddingComposite, FixedEmbeddingComposite, LazyFixedEmbeddingComposite, AutoEmbeddingComposite, 25 | LinearAncillaComposite, 26 | TilingComposite, 27 | ReverseAdvanceComposite, ReverseBatchStatesComposite, 28 | ) 29 | 30 | 31 | class MockPolySampler(dimod.PolySampler): 32 | parameters = None 33 | properties = None 34 | 35 | def sample_poly(self, poly, **kwargs): 36 | pass 37 | 38 | 39 | @parameterized_class([ 40 | # dwave.system.composites.cutoffcomposite 41 | dict(composite_cls=CutOffComposite, params=dict(cutoff=0)), 42 | dict(composite_cls=PolyCutOffComposite, sampler_cls=MockPolySampler, params=dict(cutoff=0)), 43 | # dwave.system.composites.embedding 44 | dict(composite_cls=EmbeddingComposite), 45 | dict(composite_cls=FixedEmbeddingComposite, params=dict(embedding={})), 46 | dict(composite_cls=LazyFixedEmbeddingComposite), 47 | dict(composite_cls=AutoEmbeddingComposite), 48 | # dwave.system.composites.linear_ancilla 49 | dict(composite_cls=LinearAncillaComposite), 50 | # dwave.system.composites.tiling 51 | dict(composite_cls=TilingComposite, params=dict(sub_m=1, sub_n=1)), 52 | # dwave.system.composites.reversecomposite 53 | dict(composite_cls=ReverseAdvanceComposite), 54 | dict(composite_cls=ReverseBatchStatesComposite), 55 | ]) 56 | class TestScoped(unittest.TestCase): 57 | """Test all composites defined in dwave-system are properly scoped, 58 | i.e. they propagate `close()` to samplers and they implement the context 59 | manager protocol. 60 | """ 61 | 62 | def get_composite(self) -> tuple[dimod.Sampler, dimod.Composite]: 63 | params = getattr(self, 'params', None) 64 | if params is None: 65 | params = {} 66 | 67 | sampler = getattr(self, 'sampler_cls', MockDWaveSampler)() 68 | sampler.close = mock.MagicMock() 69 | 70 | composite = self.composite_cls(sampler, **params) 71 | 72 | return sampler, composite 73 | 74 | def test_close_propagation(self): 75 | sampler, composite = self.get_composite() 76 | 77 | composite.close() 78 | 79 | sampler.close.assert_called_once() 80 | 81 | def test_context_manager(self): 82 | sampler, composite = self.get_composite() 83 | 84 | with composite: 85 | ... 86 | 87 | sampler.close.assert_called_once() 88 | -------------------------------------------------------------------------------- /tests/test_cutoffcomposite.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | import dimod 18 | import dimod.testing as dtest 19 | 20 | from dwave.system import CutOffComposite 21 | 22 | 23 | class CutoffChecker(dimod.Sampler): 24 | def __init__(self, child_sampler, expected_bqm): 25 | 26 | self.child = child_sampler 27 | self.bqm = expected_bqm 28 | 29 | def sample(self, bqm, **parameters): 30 | assert self.bqm == bqm, '{} != {}'.format(self.bqm, bqm) 31 | return self.child.sample(bqm, **parameters) 32 | 33 | def parameters(self): 34 | return self.child.parameters() 35 | 36 | def properties(self): 37 | return self.child.properties() 38 | 39 | 40 | class TestConstruction(unittest.TestCase): 41 | def test_instantiation_smoketest(self): 42 | sampler = CutOffComposite(dimod.ExactSolver(), 0) 43 | dtest.assert_sampler_api(sampler) 44 | 45 | 46 | class TestCutoffIsing(unittest.TestCase): 47 | def test_no_cutoff(self): 48 | h = {'a': -4.0, 'b': -4.0} 49 | J = {('a', 'b'): 3.2, ('b', 'c'): 0.1} 50 | cutoff = -1 51 | 52 | bqm = dimod.BinaryQuadraticModel.from_ising(h, J) 53 | 54 | checker = CutoffChecker(dimod.ExactSolver(), bqm) 55 | samples = CutOffComposite(checker, cutoff).sample_ising(h, J) 56 | 57 | dimod.testing.assert_response_energies(samples, bqm) 58 | 59 | def test_triange_to_3path(self): 60 | h = {} 61 | J = {'ab': -1, 'bc': -1, 'ac': .5} 62 | cutoff = .75 63 | cut = dimod.BinaryQuadraticModel.from_ising({}, {'ab': -1, 'bc': -1}) 64 | 65 | checker = CutoffChecker(dimod.ExactSolver(), cut) 66 | samples = CutOffComposite(checker, cutoff).sample_ising(h, J) 67 | 68 | dimod.testing.assert_response_energies(samples, dimod.BinaryQuadraticModel.from_ising(h, J)) 69 | 70 | def test_triangle_to_2path(self): 71 | h = {'a': 1.2, 'b': 1, 'c': .5} 72 | J = {'ab': -1, 'bc': -.5, 'ac': -.5} 73 | cutoff = .75 74 | cut = dimod.BinaryQuadraticModel.from_ising({'a': 1.2, 'b': 1}, {'ab': -1}) 75 | 76 | checker = CutoffChecker(dimod.ExactSolver(), cut) 77 | samples = CutOffComposite(checker, cutoff).sample_ising(h, J) 78 | 79 | dimod.testing.assert_response_energies(samples, dimod.BinaryQuadraticModel.from_ising(h, J)) 80 | 81 | # check that we picked the right minimizing value for the isolated 82 | self.assertEqual(samples.first.sample['c'], -1) 83 | 84 | def test_triangle_to_empty(self): 85 | h = {'a': 1.2, 'b': 1, 'c': .5} 86 | J = {'ab': -.5, 'bc': -.5, 'ac': -.5} 87 | cutoff = .75 88 | cut = dimod.BinaryQuadraticModel.from_ising({}, {}) 89 | 90 | # we cannot check in this case because all variables are isolated 91 | # this results in exactly one variable being sent to ExactSolver and 92 | # we don't know which one it will be, so we just check the correctness 93 | # of the output 94 | samples = CutOffComposite(dimod.ExactSolver(), cutoff).sample_ising(h, J) 95 | 96 | dimod.testing.assert_response_energies(samples, dimod.BinaryQuadraticModel.from_ising(h, J)) 97 | 98 | # check that we picked the right minimizing value for the isolated 99 | self.assertEqual(samples.first.sample['a'], -1) 100 | self.assertEqual(samples.first.sample['b'], -1) 101 | self.assertEqual(samples.first.sample['c'], -1) 102 | 103 | def test_4_path_isolated_tail(self): 104 | h = {} 105 | J = {'ab': -1, 'bc': -.5, 'cd': -.5, 'de': -.5} 106 | cutoff = .75 107 | cut = dimod.BinaryQuadraticModel.from_ising({}, {'ab': -1}) 108 | 109 | checker = CutoffChecker(dimod.ExactSolver(), cut) 110 | samples = CutOffComposite(checker, cutoff).sample_ising(h, J) 111 | 112 | dimod.testing.assert_response_energies(samples, dimod.BinaryQuadraticModel.from_ising(h, J)) 113 | -------------------------------------------------------------------------------- /tests/test_dwavecliquesampler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import itertools 16 | import unittest 17 | import unittest.mock 18 | 19 | from parameterized import parameterized 20 | 21 | import dimod 22 | import dwave_networkx as dnx 23 | 24 | from dwave.cloud import exceptions 25 | from dwave.cloud import computation 26 | from dwave.system import DWaveCliqueSampler, DWaveSampler 27 | from dwave.system.exceptions import FailoverCondition, RetryCondition 28 | 29 | 30 | class MockDWaveSampler(dimod.RandomSampler, dimod.Structured): 31 | # contains the minimum needed to work with DWaveCliqueSampler 32 | 33 | edgelist = None 34 | nodelist = None 35 | 36 | def __init__(self, **kwargs): 37 | self.properties = dict(h_range=[-2, 2], 38 | j_range=[-1, 1], 39 | extended_j_range=[-2, 1],) 40 | self.parameters = {'auto_scale': None} 41 | 42 | def sample(self, bqm, auto_scale=True): 43 | assert not auto_scale 44 | assert bqm.vartype is dimod.SPIN 45 | 46 | h_range = self.properties['h_range'] 47 | j_range = self.properties['extended_j_range'] 48 | 49 | for bias in bqm.linear.values(): 50 | assert h_range[0] <= bias <= h_range[1] 51 | 52 | for bias in bqm.quadratic.values(): 53 | assert j_range[0] <= bias <= j_range[1] 54 | 55 | return super().sample(bqm) 56 | 57 | def trigger_failover(self): 58 | pass 59 | 60 | to_networkx_graph = DWaveSampler.to_networkx_graph 61 | 62 | 63 | class MockChimeraDWaveSampler(MockDWaveSampler): 64 | def __init__(self, **config): 65 | super().__init__() 66 | 67 | self.properties.update(topology=dict(shape=[4, 4, 4], type='chimera')) 68 | 69 | G = dnx.chimera_graph(4, 4, 4) 70 | 71 | self.nodelist = list(G.nodes) 72 | self.edgelist = list(G.edges) 73 | 74 | def sample(self, bqm, **kwargs): 75 | 76 | # per_qubit_coupling_range 77 | ran = (-9, 6) 78 | 79 | # check the total coupling range 80 | for v in bqm.variables: 81 | bias = sum(bqm.adj[v].values()) 82 | assert ran[0] <= bias <= ran[1] 83 | 84 | return super().sample(bqm, **kwargs) 85 | 86 | 87 | class MockPegasusDWaveSampler(MockDWaveSampler): 88 | def __init__(self, **config): 89 | super().__init__() 90 | 91 | self.properties.update(topology=dict(shape=[6], type='pegasus')) 92 | 93 | G = dnx.pegasus_graph(6) 94 | 95 | self.nodelist = list(G.nodes) 96 | self.edgelist = list(G.edges) 97 | 98 | 99 | with unittest.mock.patch('dwave.system.samplers.clique.DWaveSampler', 100 | MockChimeraDWaveSampler): 101 | chimera_sampler = DWaveCliqueSampler() 102 | 103 | with unittest.mock.patch('dwave.system.samplers.clique.DWaveSampler', 104 | MockPegasusDWaveSampler): 105 | pegasus_sampler = DWaveCliqueSampler() 106 | 107 | 108 | @dimod.testing.load_sampler_bqm_tests(chimera_sampler) 109 | @dimod.testing.load_sampler_bqm_tests(pegasus_sampler) 110 | class TestDWaveCliqueSampler(unittest.TestCase): 111 | def test_api(self): 112 | dimod.testing.assert_sampler_api(chimera_sampler) 113 | dimod.testing.assert_sampler_api(pegasus_sampler) 114 | 115 | def test_clique(self): 116 | self.assertEqual(len(chimera_sampler.clique(2)), 2) 117 | self.assertEqual(len(chimera_sampler.clique(16)), 16) 118 | 119 | self.assertEqual(len(chimera_sampler.clique(['a', 1])), 2) 120 | self.assertEqual(set(chimera_sampler.clique(['a', 1])), {'a', 1}) 121 | 122 | def test_largest_clique(self): 123 | self.assertEqual(len(chimera_sampler.largest_clique()), 16) 124 | 125 | def test_ferromagnet_chimera(self): 126 | # submit a maximum ferromagnet 127 | bqm = dimod.BinaryQuadraticModel('SPIN') 128 | for u, v in itertools.combinations(chimera_sampler.largest_clique(), 2): 129 | bqm.quadratic[u, v] = -1 130 | 131 | chimera_sampler.sample(bqm).resolve() 132 | 133 | def test_too_large(self): 134 | num_variables = chimera_sampler.largest_clique_size + 1 135 | 136 | bqm = dimod.BinaryQuadraticModel(num_variables, 'SPIN') 137 | 138 | with self.assertRaises(ValueError): 139 | chimera_sampler.sample(bqm) 140 | 141 | def test_qubit_coupling_range(self): 142 | n = pegasus_sampler.largest_clique_size 143 | 144 | bqm = dimod.BinaryQuadraticModel({}, 145 | {(u, v): -2 for u in range(n) for v in range(u+1, n)}, 'SPIN') 146 | 147 | pegasus_sampler.sample(bqm, chain_strength=-0.5).resolve() 148 | 149 | 150 | class TestFailover(unittest.TestCase): 151 | 152 | @staticmethod 153 | def patch_solver(solver, exc): 154 | """Patch mock `solver` to fail with `exc` on sampleset resolve.""" 155 | 156 | # clique sampler needs a valid topology 157 | solver.properties = { 158 | 'topology': { 159 | 'type': 'pegasus', 160 | 'shape': [4] 161 | }, 162 | 'parameters': {}, 163 | 'h_range': [-2, 2], 164 | 'j_range': [-1, 1] 165 | } 166 | 167 | # simulate solver failure on sampleset resolve 168 | fut = computation.Future(solver, None) 169 | fut._set_exception(exc) 170 | solver.sample_bqm = unittest.mock.Mock() 171 | solver.sample_bqm.return_value = fut 172 | 173 | return solver 174 | 175 | @unittest.mock.patch('dwave.system.samplers.dwave_sampler.Client') 176 | def test_default(self, mock_client): 177 | sampler = DWaveCliqueSampler() 178 | 179 | self.patch_solver(sampler.child.solver, exceptions.SolverOfflineError) 180 | 181 | with self.assertRaises(exceptions.SolverOfflineError): 182 | sampler.sample_ising({}, {}).resolve() 183 | 184 | @parameterized.expand([ 185 | (exceptions.InvalidAPIResponseError, FailoverCondition), 186 | (exceptions.SolverNotFoundError, FailoverCondition), 187 | (exceptions.SolverOfflineError, FailoverCondition), 188 | (exceptions.SolverError, FailoverCondition), 189 | (exceptions.PollingTimeout, RetryCondition), 190 | (exceptions.SolverAuthenticationError, exceptions.SolverAuthenticationError), # auth error propagated 191 | (KeyError, KeyError), # unrelated errors propagated 192 | ]) 193 | @unittest.mock.patch('dwave.system.samplers.dwave_sampler.Client') 194 | def test_async_failover(self, source_exc, target_exc, mock_client): 195 | sampler = DWaveCliqueSampler(failover=True) 196 | 197 | self.patch_solver(sampler.child.solver, source_exc) 198 | 199 | with self.assertRaises(target_exc): 200 | sampler.sample_ising({}, {}).resolve() 201 | 202 | @unittest.mock.patch('dwave.system.samplers.clique.DWaveSampler', 203 | MockChimeraDWaveSampler) 204 | def test_properties_reinit(self): 205 | sampler = DWaveCliqueSampler() 206 | 207 | G = sampler.target_graph 208 | qlr = sampler.qpu_linear_range 209 | qqr = sampler.qpu_quadratic_range 210 | 211 | sampler.trigger_failover() 212 | 213 | self.assertIsNot(G, sampler.target_graph) 214 | self.assertIsNot(qlr, sampler.qpu_linear_range) 215 | self.assertIsNot(qqr, sampler.qpu_quadratic_range) 216 | 217 | @unittest.mock.patch('dwave.system.samplers.clique.DWaveSampler') 218 | def test_close(self, mock_child_sampler): 219 | sampler = DWaveCliqueSampler() 220 | sampler.close() 221 | 222 | mock_child_sampler.return_value.close.assert_called_once() 223 | 224 | @unittest.mock.patch('dwave.system.samplers.clique.DWaveSampler') 225 | def test_context_manager(self, mock_child_sampler): 226 | with DWaveCliqueSampler() as sampler: 227 | ... 228 | 229 | mock_child_sampler.return_value.close.assert_called_once() 230 | -------------------------------------------------------------------------------- /tests/test_embedding_chain_strength.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | import networkx as nx 18 | 19 | import dimod 20 | import dwave.embedding 21 | from dwave.embedding.chain_strength import uniform_torque_compensation, scaled 22 | 23 | class TestUniformTorqueCompensation(unittest.TestCase): 24 | def setUp(self): 25 | h = {0: 0, 1: 0, 2: 0} 26 | J = {(0, 1): 1, (1, 2): 1, (0, 2): 1} 27 | self.bqm = dimod.BinaryQuadraticModel.from_ising(h, J) 28 | 29 | def test_empty(self): 30 | empty_bqm = dimod.BinaryQuadraticModel({}, {}, 0, 'SPIN') 31 | chain_strength = uniform_torque_compensation(empty_bqm, prefactor=2) 32 | self.assertEqual(chain_strength, 1) 33 | 34 | def test_default_prefactor(self): 35 | chain_strength = uniform_torque_compensation(self.bqm) 36 | self.assertAlmostEqual(chain_strength, 1.9997, places=4) 37 | 38 | def test_typical(self): 39 | chain_strength = uniform_torque_compensation(self.bqm, prefactor=2) 40 | self.assertAlmostEqual(chain_strength, 2.8284, places=4) 41 | 42 | def test_as_callable(self): 43 | embedding = {0: {0}, 1: {1}, 2: {2, 3}} 44 | embedded_bqm = dwave.embedding.embed_bqm(self.bqm, embedding, nx.cycle_graph(4), 45 | chain_strength=uniform_torque_compensation) 46 | 47 | expected_bqm = dimod.BinaryQuadraticModel({0: 0, 1: 0, 2: 0, 3: 0}, 48 | {(0, 1): 1, (1, 2): 1, (2, 3): -1.9997, (0, 3): 1}, 49 | 1.9997, # offset the energy from satisfying chains 50 | dimod.SPIN) 51 | 52 | dimod.testing.assert_bqm_almost_equal(embedded_bqm, expected_bqm, places=4) 53 | 54 | class TestScaled(unittest.TestCase): 55 | def setUp(self): 56 | h = {0: 0, 1: -1, 2: 2} 57 | J = {(0, 1): 1, (1, 2): -2, (0, 2): -1} 58 | self.bqm = dimod.BinaryQuadraticModel.from_ising(h, J) 59 | 60 | def test_empty(self): 61 | empty_bqm = dimod.BinaryQuadraticModel({}, {}, 0, 'SPIN') 62 | chain_strength = scaled(empty_bqm, prefactor=2) 63 | self.assertEqual(chain_strength, 1) 64 | 65 | def test_default_prefactor(self): 66 | chain_strength = scaled(self.bqm) 67 | self.assertEqual(chain_strength, 2) 68 | 69 | def test_typical(self): 70 | chain_strength = scaled(self.bqm, prefactor=1.5) 71 | self.assertEqual(chain_strength, 3) 72 | 73 | def test_as_callable(self): 74 | embedding = {0: {0}, 1: {1}, 2: {2, 3}} 75 | embedded_bqm = dwave.embedding.embed_bqm(self.bqm, embedding, nx.cycle_graph(4), 76 | chain_strength=scaled) 77 | 78 | expected_bqm = dimod.BinaryQuadraticModel({0: 0, 1: -1, 2: 1, 3: 1}, 79 | {(0, 1): 1, (1, 2): -2, (2, 3): -2, (0, 3): -1}, 80 | 2, # offset the energy from satisfying chains 81 | dimod.SPIN) 82 | self.assertEqual(embedded_bqm, expected_bqm) 83 | -------------------------------------------------------------------------------- /tests/test_embedding_diagnostic.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # ================================================================================================ 16 | import unittest 17 | 18 | import networkx as nx 19 | 20 | from dwave.embedding.exceptions import MissingChainError, ChainOverlapError, DisconnectedChainError 21 | from dwave.embedding.exceptions import InvalidNodeError, MissingEdgeError 22 | from dwave.embedding.diagnostic import diagnose_embedding, is_valid_embedding, verify_embedding 23 | 24 | 25 | class TestEmbeddingDiagnostic(unittest.TestCase): 26 | def test_missing_chain(self): 27 | k2 = nx.complete_graph(2) 28 | emb = {0: [0]} 29 | 30 | self.assertRaises(MissingChainError, lambda: verify_embedding(emb, k2, k2)) 31 | self.assertFalse(is_valid_embedding(emb, k2, k2)) 32 | 33 | etypes = set() 34 | for err in diagnose_embedding(emb, k2, k2): 35 | etypes.add(err[0]) 36 | self.assertEqual(etypes, {MissingChainError}) 37 | 38 | def test_chain_overlap(self): 39 | k2 = nx.complete_graph(2) 40 | emb = {0: [0], 1: [0, 1]} 41 | 42 | self.assertRaises(ChainOverlapError, lambda: verify_embedding(emb, k2, k2)) 43 | self.assertFalse(is_valid_embedding(emb, k2, k2)) 44 | 45 | etypes = set() 46 | for err in diagnose_embedding(emb, k2, k2): 47 | etypes.add(err[0]) 48 | self.assertEqual(etypes, {ChainOverlapError}) 49 | 50 | def test_chain_overlap_with_edges(self): 51 | #this is made for compatibility with minorminer; to verify that "overlapped 52 | #embeddings" don't report spurious MissingEdgeErrors 53 | k5 = nx.complete_graph(5) 54 | k4 = nx.complete_graph(4) 55 | emb = {i:[i%4, (i+1)%4] for i in k5} 56 | 57 | self.assertRaises(ChainOverlapError, lambda: verify_embedding(emb, k5, k4)) 58 | self.assertFalse(is_valid_embedding(emb, k5, k4)) 59 | 60 | etypes = set() 61 | for err in diagnose_embedding(emb, k5, k4): 62 | etypes.add(err[0]) 63 | self.assertEqual(etypes, {ChainOverlapError}) 64 | 65 | def test_chain_disconnect(self): 66 | k2 = nx.complete_graph(2) 67 | p3 = nx.path_graph(3) 68 | emb = {0: [1], 1: [0, 2]} 69 | 70 | self.assertRaises(DisconnectedChainError, lambda: verify_embedding(emb, k2, p3)) 71 | self.assertFalse(is_valid_embedding(emb, k2, p3)) 72 | 73 | etypes = set() 74 | for err in diagnose_embedding(emb, k2, p3): 75 | etypes.add(err[0]) 76 | self.assertEqual(etypes, {DisconnectedChainError}) 77 | 78 | def test_invalid_node(self): 79 | k2 = nx.complete_graph(2) 80 | emb = {0: [0], 1: [2]} 81 | 82 | self.assertRaises(InvalidNodeError, lambda: verify_embedding(emb, k2, k2)) 83 | self.assertFalse(is_valid_embedding(emb, k2, k2)) 84 | 85 | etypes = set() 86 | for err in diagnose_embedding(emb, k2, k2): 87 | etypes.add(err[0]) 88 | self.assertEqual(etypes, {InvalidNodeError}) 89 | 90 | def test_missing_edge(self): 91 | k2 = nx.complete_graph(2) 92 | e2 = nx.empty_graph(2) 93 | emb = {0: [0], 1: [1]} 94 | 95 | self.assertRaises(MissingEdgeError, lambda: verify_embedding(emb, k2, e2)) 96 | self.assertFalse(is_valid_embedding(emb, k2, e2)) 97 | 98 | etypes = set() 99 | for err in diagnose_embedding(emb, k2, e2): 100 | etypes.add(err[0]) 101 | self.assertEqual(etypes, {MissingEdgeError}) 102 | 103 | def test_valid(self): 104 | k2 = nx.complete_graph(2) 105 | emb = {0: [0], 1: [1]} 106 | 107 | verify_embedding(emb, k2, k2) 108 | self.assertTrue(is_valid_embedding(emb, k2, k2)) 109 | 110 | for err in diagnose_embedding(emb, k2, k2): 111 | raise err[0](*err[1:]) 112 | -------------------------------------------------------------------------------- /tests/test_embedding_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import itertools 17 | import random 18 | 19 | from collections.abc import Mapping 20 | 21 | import networkx as nx 22 | import numpy as np 23 | import numpy.testing as npt 24 | 25 | import dimod 26 | import dwave.embedding 27 | 28 | 29 | class TestTargetToSource(unittest.TestCase): 30 | def test_identity_embedding(self): 31 | """a 1-to-1 embedding should not change the adjacency""" 32 | target_adj = nx.karate_club_graph() 33 | 34 | embedding = {v: {v} for v in target_adj} 35 | 36 | source_adj = dwave.embedding.target_to_source(target_adj, embedding) 37 | 38 | # test the adjacencies are equal (source_adj is a dict and target_adj is a networkx graph) 39 | for v in target_adj: 40 | self.assertIn(v, source_adj) 41 | for u in target_adj[v]: 42 | self.assertIn(u, source_adj[v]) 43 | 44 | for v in source_adj: 45 | self.assertIn(v, target_adj) 46 | for u in source_adj[v]: 47 | self.assertIn(u, target_adj[v]) 48 | 49 | def test_embedding_to_one_node(self): 50 | """an embedding that maps everything to one node should result in a singleton graph""" 51 | target_adj = nx.barbell_graph(16, 7) 52 | embedding = {'a': set(target_adj)} # all map to 'a' 53 | 54 | source_adj = dwave.embedding.target_to_source(target_adj, embedding) 55 | self.assertEqual(source_adj, {'a': set()}) 56 | 57 | embedding = {'a': {0, 1}} # not every node is assigned to a chain 58 | source_adj = dwave.embedding.target_to_source(target_adj, embedding) 59 | self.assertEqual(source_adj, {'a': set()}) 60 | 61 | def test_embedding_overlap(self): 62 | """overlapping embeddings should raise an error""" 63 | target_adj = nx.complete_graph(5) 64 | embedding = {'a': {0, 1}, 'b': {1, 2}} # overlap 65 | 66 | with self.assertRaises(ValueError): 67 | source_adj = dwave.embedding.target_to_source(target_adj, embedding) 68 | 69 | def test_square_to_triangle(self): 70 | target_adjacency = {0: {1, 3}, 1: {0, 2}, 2: {1, 3}, 3: {0, 2}} # a square graph 71 | embedding = {'a': {0}, 'b': {1}, 'c': {2, 3}} 72 | source_adjacency = dwave.embedding.target_to_source(target_adjacency, embedding) 73 | self.assertEqual(source_adjacency, {'a': {'b', 'c'}, 'b': {'a', 'c'}, 'c': {'a', 'b'}}) 74 | 75 | 76 | class TestEdgelistToAdjacency(unittest.TestCase): 77 | def test_typical(self): 78 | graph = nx.barbell_graph(17, 8) 79 | 80 | edgelist = set(graph.edges()) 81 | 82 | adj = dwave.embedding.utils.edgelist_to_adjacency(edgelist) 83 | 84 | # test that they're equal 85 | for u, v in edgelist: 86 | self.assertIn(u, adj) 87 | self.assertIn(v, adj) 88 | self.assertIn(u, adj[v]) 89 | self.assertIn(v, adj[u]) 90 | 91 | for u in adj: 92 | for v in adj[u]: 93 | self.assertTrue((u, v) in edgelist or (v, u) in edgelist) 94 | self.assertFalse((u, v) in edgelist and (v, u) in edgelist) 95 | 96 | 97 | class TestAdjacencyToEdgeIter(unittest.TestCase): 98 | def test_dict(self): 99 | graph = nx.barbell_graph(17, 8) 100 | adj = {v: set(graph[v]) for v in graph} 101 | 102 | edgelist = dwave.embedding.utils.adjacency_to_edges(adj) 103 | new_adj = {} 104 | for u, v in edgelist: 105 | new_adj.setdefault(u, set()).add(v) 106 | new_adj.setdefault(v, set()).add(u) 107 | 108 | self.assertEqual(adj, new_adj) 109 | 110 | def test_nxgraph(self): 111 | graph = nx.barbell_graph(17, 8) 112 | 113 | edgelist = dwave.embedding.utils.adjacency_to_edges(graph) 114 | 115 | edges0 = sorted(map(sorted, graph.edges())) 116 | edges1 = sorted(map(sorted, edgelist)) 117 | 118 | self.assertEqual(edges0, edges1) 119 | 120 | def test_bqm(self): 121 | graph = nx.barbell_graph(17, 8) 122 | 123 | bqm = dimod.BQM(vartype = dimod.SPIN) 124 | 125 | bqm.add_interactions_from((u, v, 1) for u, v in graph.edges()) 126 | 127 | edgelist = dwave.embedding.utils.adjacency_to_edges(bqm) 128 | 129 | edges0 = sorted(map(sorted, graph.edges())) 130 | edges1 = sorted(map(sorted, edgelist)) 131 | 132 | self.assertEqual(edges0, edges1) 133 | 134 | def test_bad(self): 135 | with self.assertRaises(TypeError): 136 | edgelist = list(dwave.embedding.utils.adjacency_to_edges([])) 137 | 138 | class TestChainToQuadratic(unittest.TestCase): 139 | def test_K5(self): 140 | """Test that when given a chain, the returned Jc uses all 141 | available edges.""" 142 | chain_variables = set(range(5)) 143 | 144 | # fully connected 145 | adjacency = {u: set(chain_variables) for u in chain_variables} 146 | for v, neighbors in adjacency.items(): 147 | neighbors.remove(v) 148 | 149 | Jc = dwave.embedding.chain_to_quadratic(chain_variables, adjacency, 1.0) 150 | 151 | for u, v in itertools.combinations(chain_variables, 2): 152 | self.assertFalse((u, v) in Jc and (v, u) in Jc) 153 | self.assertTrue((u, v) in Jc or (v, u) in Jc) 154 | for u in chain_variables: 155 | self.assertFalse((u, u) in Jc) 156 | 157 | def test_5_cycle(self): 158 | chain_variables = set(range(5)) 159 | 160 | # now try a cycle 161 | adjacency = {v: {(v + 1) % 5, (v - 1) % 5} for v in chain_variables} 162 | 163 | Jc = dwave.embedding.chain_to_quadratic(chain_variables, adjacency, 1.0) 164 | 165 | for u in adjacency: 166 | for v in adjacency[u]: 167 | self.assertFalse((u, v) in Jc and (v, u) in Jc) 168 | self.assertTrue((u, v) in Jc or (v, u) in Jc) 169 | 170 | def test_disconnected(self): 171 | chain_variables = {0, 2} 172 | 173 | adjacency = {0: {1}, 1: {0, 2}, 2: {1}} 174 | 175 | with self.assertRaises(ValueError): 176 | dwave.embedding.chain_to_quadratic(chain_variables, adjacency, 1.0) 177 | 178 | 179 | class TestChainBreakFrequency(unittest.TestCase): 180 | def test_matrix_all_ones(self): 181 | """should have no breaks""" 182 | 183 | samples = np.ones((10, 5)) 184 | 185 | embedding = {'a': {2, 4}, 'b': {1, 3}} 186 | 187 | freq = dwave.embedding.chain_break_frequency(samples, embedding) 188 | 189 | self.assertEqual(freq, {'a': 0, 'b': 0}) 190 | 191 | def test_matrix_all_zeros(self): 192 | """should have no breaks""" 193 | 194 | samples = np.zeros((10, 5)) 195 | 196 | embedding = {'a': {2, 4}, 'b': {1, 3}} 197 | 198 | freq = dwave.embedding.chain_break_frequency(samples, embedding) 199 | 200 | self.assertEqual(freq, {'a': 0, 'b': 0}) 201 | 202 | def test_matrix_mix(self): 203 | samples = np.array([[-1, 1], [1, 1]]) 204 | 205 | embedding = {'a': {0, 1}} 206 | 207 | freq = dwave.embedding.chain_break_frequency(samples, embedding) 208 | 209 | self.assertEqual(freq, {'a': .5}) 210 | 211 | def test_mix_string_labels(self): 212 | response = dimod.SampleSet.from_samples([{'a': 1, 'b': 0}, {'a': 0, 'b': 0}], 213 | energy=[1, 0], info={}, vartype=dimod.BINARY) 214 | embedding = {0: {'a', 'b'}} 215 | freq = dwave.embedding.chain_break_frequency(response, embedding) 216 | 217 | self.assertEqual(freq, {0: .5}) 218 | 219 | def test_with_num_occurences(self): 220 | samples = [[-1, -1, +1], 221 | [-1, +1, +1], 222 | [-1, +1, +1], 223 | [-1, -1, -1], 224 | [-1, +1, +1]] 225 | labels = 'abc' 226 | 227 | sampleset = dimod.SampleSet.from_samples((samples, labels), energy=0, 228 | vartype=dimod.SPIN) 229 | sampleset = sampleset.aggregate() 230 | 231 | embedding = {0: 'ab', 1: 'c'} 232 | 233 | freq = dwave.embedding.chain_break_frequency(sampleset, embedding) 234 | 235 | self.assertEqual(freq, {0: 3./5, 1: 0}) 236 | 237 | class TestIntLabelDisjointSets(unittest.TestCase): 238 | def test(self): 239 | components = map(list, [range(1), range(1, 3), range(3, 6), range(6, 12)]) 240 | djs = dwave.embedding.utils.intlabel_disjointsets(12) 241 | 242 | for x in range(12): 243 | self.assertEqual(djs.find(x), x) 244 | self.assertEqual(djs.size(x), 1) 245 | 246 | for c in components: 247 | for i, j in zip(c, c[1:]): 248 | djs.union(i, j) 249 | 250 | for c in components: 251 | root = djs.find(c[0]) 252 | for x in c: 253 | self.assertEqual(djs.find(x), root) 254 | self.assertEqual(djs.size(x), len(c)) 255 | 256 | for c in components: 257 | for i, j in combinations(c, 2): 258 | djs.union(i, j) 259 | 260 | for c in components: 261 | root = djs.find(c[0]) 262 | for x in c: 263 | self.assertEqual(djs.find(x), root) 264 | self.assertEqual(djs.size(x), len(c)) 265 | 266 | djs.union(0, 1) 267 | djs.union(2, 3) 268 | djs.union(5, 6) 269 | 270 | root = djs.find(0) 271 | for x in range(12): 272 | self.assertEqual(djs.find(x), root) 273 | self.assertEqual(djs.size(x), 12) 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /tests/test_leaphybridcqmsampler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import unittest.mock 17 | 18 | import dimod 19 | 20 | from dwave.system import LeapHybridCQMSampler 21 | 22 | 23 | class TestTimeLimit(unittest.TestCase): 24 | class MockClient: 25 | @classmethod 26 | def from_config(cls, *args, **kwargs): 27 | return cls() 28 | 29 | def get_solver(self, *args, **kwargs): 30 | class MockSolver: 31 | properties = dict( 32 | category='hybrid', 33 | minimum_time_limit_s=0, 34 | maximum_number_of_constraints=100, 35 | maximum_number_of_variables=500, 36 | maximum_number_of_biases=2000, 37 | maximum_number_of_quadratic_variables=200, 38 | ) 39 | supported_problem_types = ['cqm'] 40 | 41 | def sample_cqm(self, cqm, time_limit): 42 | # return the time_limit rather than a sampleset 43 | ret = unittest.mock.Mock() 44 | ret.sampleset = time_limit 45 | return ret 46 | 47 | def upload_problem(self, *args, **kwargs): 48 | class MockResult: 49 | @staticmethod 50 | def result(): 51 | return 52 | 53 | return MockResult 54 | 55 | return MockSolver() 56 | 57 | @unittest.mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client', MockClient) 58 | def test_time_limit_serialization(self): 59 | """Check that the same time_limit is generated for the model before and 60 | after serialization. 61 | 62 | See https://github.com/dwavesystems/dwave-system/issues/482 63 | """ 64 | sampler = LeapHybridCQMSampler() 65 | 66 | cqm = dimod.ConstrainedQuadraticModel() 67 | cqm.add_variables('INTEGER', 500) 68 | cqm.add_constraint([(0, 1, 1)], '==', 0) 69 | 70 | with cqm.to_file() as f: 71 | new = dimod.ConstrainedQuadraticModel().from_file(f) 72 | 73 | self.assertEqual(sampler.sample_cqm(new), sampler.sample_cqm(cqm)) 74 | 75 | @unittest.mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client') 76 | def test_close(self, mock_client): 77 | mock_solver = mock_client.from_config.return_value.get_solver.return_value 78 | mock_solver.properties = {'category': 'hybrid'} 79 | mock_solver.supported_problem_types = ['cqm'] 80 | 81 | with self.subTest('manual close'): 82 | sampler = LeapHybridCQMSampler() 83 | sampler.close() 84 | mock_client.from_config.return_value.close.assert_called_once() 85 | 86 | mock_client.reset_mock() 87 | 88 | with self.subTest('context manager'): 89 | with LeapHybridCQMSampler(): 90 | ... 91 | mock_client.from_config.return_value.close.assert_called_once() 92 | -------------------------------------------------------------------------------- /tests/test_leaphybriddqmsolver.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | from unittest.mock import patch 18 | 19 | import dimod 20 | 21 | from dwave.system import LeapHybridDQMSampler 22 | 23 | 24 | class TestLeapHybridDQMSampler(unittest.TestCase): 25 | def test_time_limit_exceptions(self): 26 | 27 | class MockSolver(): 28 | properties = dict(category='hybrid', 29 | minimum_time_limit=[[20000, 5.0], 30 | [100000, 6.0], 31 | [200000, 13.0], 32 | [500000, 34.0], 33 | [1000000, 71.0], 34 | [2000000, 152.0], 35 | [5000000, 250.0], 36 | [20000000, 400.0], 37 | [250000000, 1200.0]], 38 | maximum_time_limit_hrs=24.0, 39 | ) 40 | supported_problem_types = ['dqm'] 41 | 42 | def sample_dqm(self, *args, **kwargs): 43 | raise RuntimeError 44 | 45 | class MockClient(): 46 | @classmethod 47 | def from_config(cls, *args, **kwargs): 48 | return cls() 49 | 50 | def get_solver(self, *args, **kwargs): 51 | return MockSolver() 52 | 53 | with patch('dwave.system.samplers.leap_hybrid_sampler.Client', MockClient): 54 | sampler = LeapHybridDQMSampler() 55 | 56 | dqm = dimod.DQM() 57 | 58 | with self.assertRaises(ValueError): 59 | sampler.sample_dqm(dqm, time_limit=1) 60 | 61 | with self.assertRaises(ValueError): 62 | sampler.sample_dqm(dqm, time_limit=10000000) 63 | 64 | @unittest.mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client') 65 | def test_close(self, mock_client): 66 | mock_solver = mock_client.from_config.return_value.get_solver.return_value 67 | mock_solver.properties = {'category': 'hybrid'} 68 | mock_solver.supported_problem_types = ['dqm'] 69 | 70 | with self.subTest('manual close'): 71 | sampler = LeapHybridDQMSampler() 72 | sampler.close() 73 | mock_client.from_config.return_value.close.assert_called_once() 74 | 75 | mock_client.reset_mock() 76 | 77 | with self.subTest('context manager'): 78 | with LeapHybridDQMSampler(): 79 | ... 80 | mock_client.from_config.return_value.close.assert_called_once() 81 | -------------------------------------------------------------------------------- /tests/test_leaphybridnlsampler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import io 16 | import concurrent.futures 17 | import unittest 18 | 19 | from dwave.optimization import Model 20 | from dwave.cloud.client import Client 21 | from dwave.cloud.computation import Future 22 | from dwave.cloud.solver import StructuredSolver, NLSolver 23 | from dwave.cloud.testing.mocks import qpu_pegasus_solver_data, hybrid_nl_solver_data 24 | 25 | from dwave.system import LeapHybridNLSampler 26 | 27 | 28 | class TestNLSampler(unittest.TestCase): 29 | 30 | class mock_client_factory: 31 | @classmethod 32 | def from_config(cls, **kwargs): 33 | # keep instantiation local, so we can later mock BaseUnstructuredSolver 34 | mock_nl_solver = NLSolver(client=None, data=hybrid_nl_solver_data()) 35 | mock_qpu_solver = StructuredSolver(client=None, data=qpu_pegasus_solver_data(2)) 36 | kwargs.setdefault('endpoint', 'mock') 37 | kwargs.setdefault('token', 'mock') 38 | client = Client(**kwargs) 39 | client._fetch_solvers = lambda **kwargs: [mock_qpu_solver, mock_nl_solver] 40 | return client 41 | 42 | @unittest.mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client', mock_client_factory) 43 | def test_solver_selection(self): 44 | sampler = LeapHybridNLSampler() 45 | self.assertIn("nl", sampler.properties.get('supported_problem_types', [])) 46 | 47 | @unittest.mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client', mock_client_factory) 48 | @unittest.mock.patch('dwave.cloud.solver.BaseUnstructuredSolver.sample_problem') 49 | @unittest.mock.patch('dwave.cloud.solver.NLSolver.upload_nlm') 50 | @unittest.mock.patch('dwave.cloud.solver.NLSolver.decode_response') 51 | def test_state_resolve(self, decode_response, upload_nlm, base_sample_problem): 52 | sampler = LeapHybridNLSampler() 53 | 54 | # create model 55 | model = Model() 56 | x = model.list(10) 57 | model.minimize(x.sum()) 58 | num_states = 5 59 | model.states.resize(num_states) 60 | model.lock() 61 | 62 | # save states 63 | self.assertEqual(model.states.size(), num_states) 64 | states_file = io.BytesIO() 65 | model.states.into_file(states_file) 66 | states_file.seek(0) 67 | 68 | # reset states 69 | # (mock upload a smaller number of states) 70 | model.states.resize(2) 71 | self.assertEqual(model.states.size(), 2) 72 | 73 | # upload is tested in dwave-cloud-client, we can just mock it here 74 | mock_problem_id = '321' 75 | mock_problem_data_id = '123' 76 | mock_problem_label = 'mock-label' 77 | mock_timing = {'qpu_access_time': 1, 'run_time': 2} 78 | mock_warnings = ['solved by classical presolve techniques'] 79 | 80 | # note: instead of simply mocking `sampler.solver`, we mock a set of 81 | # solver methods minimally required to fully test `NLSolver.sample_problem` 82 | 83 | upload_nlm.return_value.result.return_value = mock_problem_data_id 84 | 85 | base_sample_problem.return_value = Future(solver=sampler.solver, id_=mock_problem_id) 86 | base_sample_problem.return_value._set_message({"answer": {}}) 87 | base_sample_problem.return_value.label = mock_problem_label 88 | 89 | def mock_decode_response(msg, answer_data: io.IOBase): 90 | # copy model states to the "received" answer_data 91 | answer_data.write(states_file.read()) 92 | answer_data.seek(0) 93 | return { 94 | 'problem_type': 'nl', 95 | 'timing': mock_timing | dict(warnings=mock_warnings), 96 | 'shape': {}, 97 | 'answer': answer_data 98 | } 99 | decode_response.side_effect = mock_decode_response 100 | 101 | time_limit = 5 102 | 103 | result = sampler.sample(model, time_limit=time_limit, label=mock_problem_label) 104 | 105 | with self.subTest('low-level sample_nlm called'): 106 | base_sample_problem.assert_called_with( 107 | mock_problem_data_id, label=mock_problem_label, 108 | upload_params=None, time_limit=time_limit) 109 | 110 | with self.subTest('max_num_states is respected on upload'): 111 | upload_nlm.assert_called_with( 112 | model, max_num_states=sampler.properties['maximum_number_of_states']) 113 | 114 | with self.subTest('timing returned in sample result'): 115 | self.assertIsInstance(result, concurrent.futures.Future) 116 | self.assertIsInstance(result.result(), LeapHybridNLSampler.SampleResult) 117 | self.assertEqual(result.result().info['timing'], mock_timing) 118 | # warnings separate from timing 119 | self.assertEqual(result.result().info['warnings'], mock_warnings) 120 | 121 | with self.subTest('problem info returned in sample result'): 122 | self.assertEqual(result.result().info['problem_id'], mock_problem_id) 123 | # self.assertEqual(result.result().info['problem_label'], mock_problem_label) 124 | self.assertEqual(result.result().info['problem_data_id'], mock_problem_data_id) 125 | 126 | with self.subTest('model states updated'): 127 | self.assertEqual(result.result().model.states.size(), num_states) 128 | self.assertEqual(model.states.size(), num_states) 129 | 130 | with self.subTest('warnings raised'): 131 | with self.assertWarns(UserWarning, msg=mock_warnings[0]): 132 | result = sampler.sample(model, time_limit=time_limit) 133 | result.result() 134 | 135 | @unittest.mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client') 136 | def test_close(self, mock_client): 137 | mock_solver = mock_client.from_config.return_value.get_solver.return_value 138 | mock_solver.properties = {'category': 'hybrid'} 139 | mock_solver.supported_problem_types = ['nl'] 140 | 141 | with self.subTest('manual close'): 142 | sampler = LeapHybridNLSampler() 143 | sampler.close() 144 | mock_client.from_config.return_value.close.assert_called_once() 145 | 146 | mock_client.reset_mock() 147 | 148 | with self.subTest('context manager'): 149 | with LeapHybridNLSampler(): 150 | ... 151 | mock_client.from_config.return_value.close.assert_called_once() 152 | -------------------------------------------------------------------------------- /tests/test_leaphybridsolver.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import unittest.mock as mock 17 | import numpy as np 18 | 19 | import dimod 20 | from dwave.cloud import Client 21 | from dwave.cloud.exceptions import SolverNotFoundError 22 | 23 | from dwave.system.samplers import LeapHybridSampler 24 | from dwave.system.testing import MockLeapHybridSolver 25 | 26 | # Called only for named solver 27 | class MockBadLeapHybridSolver: 28 | 29 | properties = {'category': 'not hybrid'} 30 | 31 | class MockClient: 32 | 33 | def __init__(self, **kwargs): 34 | 35 | self.args = kwargs 36 | 37 | def get_solver(self, **filters): 38 | 39 | if self.args.get('solver') == 'not_hybrid_solver': 40 | return MockBadLeapHybridSolver() 41 | 42 | if self.args.get('client', 'base') not in ['base', 'hybrid']: 43 | raise SolverNotFoundError 44 | 45 | return MockLeapHybridSolver() 46 | 47 | class TestLeapHybridSampler(unittest.TestCase): 48 | 49 | @mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client') 50 | def test_solver_init(self, mock_client): 51 | mock_client.from_config.side_effect = MockClient 52 | 53 | default_solver = dict( 54 | supported_problem_types__contains='bqm', 55 | order_by='-properties.version') 56 | self.assertEqual(LeapHybridSampler.default_solver, default_solver) 57 | 58 | defaults = dict(solver=default_solver) 59 | 60 | # Default call 61 | mock_client.reset_mock() 62 | LeapHybridSampler() 63 | mock_client.from_config.assert_called_once_with( 64 | client='hybrid', 65 | connection_close=True, 66 | defaults=defaults) 67 | 68 | # Non-hybrid client setting 69 | mock_client.reset_mock() 70 | with self.assertRaises(SolverNotFoundError): 71 | LeapHybridSampler(client='qpu') 72 | 73 | # Explicitly set solver def 74 | mock_client.reset_mock() 75 | LeapHybridSampler(solver={'supported_problem_types__contains': 'bqm'}) 76 | mock_client.from_config.assert_called_once_with( 77 | client='hybrid', 78 | solver={'supported_problem_types__contains': 'bqm'}, 79 | connection_close=True, 80 | defaults=defaults) 81 | 82 | # Named solver 83 | solver_name = 'hybrid-solver-name' 84 | mock_client.reset_mock() 85 | LeapHybridSampler(solver=solver_name) 86 | mock_client.from_config.assert_called_once_with( 87 | client='hybrid', 88 | solver=solver_name, 89 | connection_close=True, 90 | defaults=defaults) 91 | 92 | # Named solver: non-hybrid 93 | with self.assertRaises(ValueError): 94 | LeapHybridSampler(solver='not_hybrid_solver') 95 | 96 | # Set connection_close to False 97 | mock_client.reset_mock() 98 | LeapHybridSampler(connection_close=False) 99 | mock_client.from_config.assert_called_once_with( 100 | client='hybrid', 101 | connection_close=False, 102 | defaults=defaults) 103 | 104 | mock_client.reset_mock() 105 | LeapHybridSampler(connection_close=False, solver=solver_name) 106 | mock_client.from_config.assert_called_once_with( 107 | client='hybrid', 108 | solver=solver_name, 109 | connection_close=False, 110 | defaults=defaults) 111 | 112 | @mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client') 113 | def test_close(self, mock_client): 114 | mock_solver = mock_client.from_config.return_value.get_solver.return_value 115 | mock_solver.properties = {'category': 'hybrid'} 116 | mock_solver.supported_problem_types = ['bqm'] 117 | 118 | with self.subTest('manual close'): 119 | sampler = LeapHybridSampler() 120 | sampler.close() 121 | mock_client.from_config.return_value.close.assert_called_once() 122 | 123 | mock_client.reset_mock() 124 | 125 | with self.subTest('context manager'): 126 | with LeapHybridSampler(): 127 | ... 128 | mock_client.from_config.return_value.close.assert_called_once() 129 | 130 | @mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client') 131 | def test_sample_bqm(self, mock_client): 132 | 133 | mock_client.from_config.side_effect = MockClient 134 | 135 | bqm = dimod.BinaryQuadraticModel({'a': -1, 'b': 1, 'c': 1}, 136 | {'ab': -0.8, 'ac': -0.7, 'bc': -1}, 0, dimod.SPIN) 137 | 138 | sampler = LeapHybridSampler() 139 | 140 | response = sampler.sample(bqm) 141 | 142 | rows, cols = response.record.sample.shape 143 | 144 | self.assertEqual(cols, 3) 145 | self.assertFalse(np.any(response.record.sample == 0)) 146 | self.assertIs(response.vartype, dimod.SPIN) 147 | self.assertIn('num_occurrences', response.record.dtype.fields) 148 | 149 | @mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client') 150 | def test_sample_ising_variables(self, mock_client): 151 | 152 | mock_client.from_config.side_effect = MockClient 153 | 154 | sampler = LeapHybridSampler() 155 | 156 | response = sampler.sample_ising({0: -1, 1: 1}, {}) 157 | 158 | rows, cols = response.record.sample.shape 159 | 160 | self.assertEqual(cols, 2) 161 | 162 | response = sampler.sample_ising({}, {(0, 4): 1}) 163 | 164 | rows, cols = response.record.sample.shape 165 | self.assertEqual(cols, 2) 166 | self.assertFalse(np.any(response.record.sample == 0)) 167 | self.assertIs(response.vartype, dimod.SPIN) 168 | 169 | @mock.patch('dwave.system.samplers.leap_hybrid_sampler.Client') 170 | def test_sample_qubo_variables(self, mock_client): 171 | 172 | mock_client.from_config.side_effect = MockClient 173 | 174 | sampler = LeapHybridSampler() 175 | 176 | response = sampler.sample_qubo({(0, 0): -1, (1, 1): 1}) 177 | 178 | rows, cols = response.record.sample.shape 179 | 180 | self.assertEqual(cols, 2) 181 | 182 | response = sampler.sample_qubo({(0, 0): -1, (1, 1): 1}) 183 | 184 | rows, cols = response.record.sample.shape 185 | 186 | self.assertEqual(cols, 2) 187 | self.assertTrue(np.all(response.record.sample >= 0)) 188 | self.assertIs(response.vartype, dimod.BINARY) 189 | -------------------------------------------------------------------------------- /tests/test_linear_ancilla_composite.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 D-Wave Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import collections 16 | import unittest 17 | 18 | import dimod 19 | import networkx as nx 20 | import numpy as np 21 | 22 | from dwave.system.testing import MockDWaveSampler 23 | from dwave.system import LinearAncillaComposite 24 | 25 | 26 | class TestLinearAncillaComposite(unittest.TestCase): 27 | def setUp(self): 28 | self.qpu = MockDWaveSampler(properties=dict(extended_j_range=[-2, 1])) 29 | self.tracked_qpu = dimod.TrackingComposite(self.qpu) 30 | 31 | self.sampler = LinearAncillaComposite( 32 | dimod.StructureComposite( 33 | self.tracked_qpu, 34 | nodelist=self.qpu.nodelist, 35 | edgelist=self.qpu.edgelist, 36 | ) 37 | ) 38 | 39 | self.submask = nx.subgraph( 40 | self.sampler.to_networkx_graph(), 41 | list(self.sampler.nodelist)[::2], 42 | ) 43 | 44 | # this problem should run 45 | self.linear_problem = dimod.BinaryQuadraticModel.from_ising( 46 | {i: (-1) ** i for i in self.submask.nodes()}, 47 | {}, 48 | ) 49 | 50 | # this problem shouldn't run 51 | self.linear_problem_full_graph = dimod.BinaryQuadraticModel.from_ising( 52 | {i: (-1) ** i for i in self.qpu.nodelist}, 53 | {}, 54 | ) 55 | 56 | def test_only_quadratic(self): 57 | """if no linear biases, the bqm remains intact""" 58 | 59 | bqm = dimod.generators.ran_r(1, self.submask, seed=1) 60 | self.sampler.sample(bqm) 61 | self.assertEqual(bqm, self.tracked_qpu.input["bqm"]) 62 | 63 | def test_h_tolerance_too_large(self): 64 | """if h tolerance is larger than the linear biases, 65 | the bqm remains intact 66 | """ 67 | 68 | self.sampler.sample(self.linear_problem, h_tolerance=1.01) 69 | self.assertEqual(self.linear_problem, self.tracked_qpu.input["bqm"]) 70 | 71 | def test_intermediate_h_tolerance(self): 72 | """check the desired h-tolerance is left in the qubit bias""" 73 | 74 | h_tolerance = 0.5 75 | self.sampler.sample(self.linear_problem, h_tolerance=h_tolerance) 76 | for variable, bias in self.tracked_qpu.input["bqm"].linear.items(): 77 | if variable in self.linear_problem.variables: # skip the ancillas 78 | self.assertEqual( 79 | bias, 80 | np.sign(self.linear_problem.get_linear(variable)) * h_tolerance, 81 | ) 82 | 83 | def test_no_ancillas_available(self): 84 | """send a problem that uses all the qubits, not leaving any ancillas available""" 85 | 86 | with self.assertRaises(ValueError): 87 | ss = self.sampler.sample(self.linear_problem_full_graph) 88 | 89 | def test_ancillas_present(self): 90 | """check the solver used ancillas""" 91 | 92 | self.sampler.sample(self.linear_problem) 93 | self.assertGreater( 94 | len(self.tracked_qpu.input["bqm"].variables), 95 | len(self.linear_problem.variables), 96 | ) 97 | 98 | def test_ancilla_cleanup(self): 99 | """check the problem returned has no additional variables""" 100 | 101 | sampleset = self.sampler.sample(self.linear_problem) 102 | self.assertEqual( 103 | len(self.linear_problem.variables), 104 | len(sampleset.variables), 105 | ) 106 | 107 | def test_flux_biases_present(self): 108 | """check flux biases are applied to non-data qubits""" 109 | 110 | self.sampler.sample(self.linear_problem) 111 | flux_biases = np.array(self.tracked_qpu.input["flux_biases"]) 112 | 113 | # flux biases are used 114 | self.assertGreater(sum(flux_biases != 0), 0) 115 | 116 | # the qubits with flux biases are not data qubits 117 | for qubit, flux_bias in enumerate(flux_biases): 118 | if flux_bias != 0: 119 | self.assertNotIn(qubit, self.linear_problem.variables) 120 | -------------------------------------------------------------------------------- /tests/test_polycutoffcomposite.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | import dimod 18 | 19 | from dwave.system import PolyCutOffComposite 20 | 21 | 22 | class CutoffChecker(dimod.PolySampler): 23 | def __init__(self, child_sampler, expected_poly): 24 | 25 | self.child = child_sampler 26 | self.poly = expected_poly 27 | 28 | def sample_poly(self, poly, **parameters): 29 | assert self.poly == poly, '{} != {}'.format(self.poly, poly) 30 | return self.child.sample_poly(poly, **parameters) 31 | 32 | def parameters(self): 33 | return self.child.parameters() 34 | 35 | def properties(self): 36 | return self.child.properties() 37 | 38 | 39 | class TestConstruction(unittest.TestCase): 40 | def test_instantiation_smoketest(self): 41 | sampler = PolyCutOffComposite(dimod.HigherOrderComposite(dimod.ExactSolver()), 0) 42 | 43 | self.assertTrue(hasattr(sampler, 'sample_poly')) 44 | self.assertTrue(hasattr(sampler, 'sample_hising')) 45 | self.assertTrue(hasattr(sampler, 'sample_hubo')) 46 | 47 | def test_wrap_bqm(self): 48 | with self.assertRaises(TypeError): 49 | PolyCutOffComposite(dimod.ExactSolver(), -1) 50 | 51 | 52 | class TestSampleHising(unittest.TestCase): 53 | def setUp(self): 54 | self.child = dimod.HigherOrderComposite(dimod.ExactSolver()) 55 | 56 | def test_empty(self): 57 | h = {} 58 | J = {} 59 | cutoff = 1 60 | expected = dimod.BinaryPolynomial({}, dimod.SPIN) 61 | 62 | checker = CutoffChecker(self.child, expected) 63 | samples = PolyCutOffComposite(checker, cutoff).sample_hising(h, J) 64 | 65 | self.assertEqual(samples.record.sample.shape[1], 0) # no variables 66 | 67 | def test_linear(self): 68 | # they are all isolated 69 | h = {'a': -1, 'b': .5} 70 | J = {} 71 | cutoff = 1 72 | 73 | # we cannot check in this case because all variables are isolated 74 | # this results in exactly one variable being sent to ExactSolver and 75 | # we don't know which one it will be, so we just check the correctness 76 | # of the output 77 | samples = PolyCutOffComposite(self.child, cutoff).sample_hising(h, J) 78 | 79 | poly = dimod.BinaryPolynomial.from_hising(h, J) 80 | for sample, energy in samples.data(['sample', 'energy']): 81 | self.assertAlmostEqual(energy, poly.energy(sample)) 82 | 83 | def test_4_path_isolated_tail(self): 84 | h = {} 85 | J = {'ab': -1, 'bc': -.5, 'cd': -.5, 'de': -.5} 86 | cutoff = .75 87 | expected = dimod.BinaryPolynomial({'ab': -1}, dimod.SPIN) 88 | 89 | checker = CutoffChecker(self.child, expected) 90 | samples = PolyCutOffComposite(checker, cutoff).sample_hising(h, J) 91 | 92 | poly = dimod.BinaryPolynomial.from_hising(h, J) 93 | for sample, energy in samples.data(['sample', 'energy']): 94 | self.assertAlmostEqual(energy, poly.energy(sample)) 95 | 96 | def test_triangle(self): 97 | h = {'a': -1} 98 | J = {'abde': -1, 'bc': -.5, 'ca': -.5} 99 | cutoff = .75 100 | expected = dimod.BinaryPolynomial({'a': -1, 'abde': -1}, dimod.SPIN) 101 | 102 | checker = CutoffChecker(self.child, expected) 103 | samples = PolyCutOffComposite(checker, cutoff).sample_hising(h, J) 104 | 105 | poly = dimod.BinaryPolynomial.from_hising(h, J) 106 | for sample, energy in samples.data(['sample', 'energy']): 107 | self.assertAlmostEqual(energy, poly.energy(sample)) 108 | 109 | # 'c' was isolated, should be 1 when restored with the ground state 110 | self.assertEqual(samples.first.sample['c'], 1) 111 | 112 | 113 | class TestSamplePoly(unittest.TestCase): 114 | def test_isolated(self): 115 | poly = dimod.BinaryPolynomial({'a': 3, 'abc': 4, 'ac': 0.2}, dimod.SPIN) 116 | sampler = dimod.HigherOrderComposite(dimod.ExactSolver()) 117 | sampleset = PolyCutOffComposite(sampler, 4.1).sample_poly(poly) 118 | -------------------------------------------------------------------------------- /tests/test_schedules.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | from dwave.system.schedules import ramp 18 | 19 | class TestRamp(unittest.TestCase): 20 | def test_typical(self): 21 | schedule = ramp(.5, .2, 1) 22 | self.assertEqual(schedule, [(0, 0), (.4, 0), (.6, 1), (1, 1)]) 23 | 24 | def test_width_exception(self): 25 | with self.assertRaises(ValueError): 26 | ramp(.1, .2, 1) # curve would begin at (0, 0) 27 | 28 | def test_s_exception(self): 29 | with self.assertRaises(ValueError): 30 | ramp(-1, 0, 1) 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/test_tiling_composite.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import random 17 | 18 | import dimod 19 | import dwave_networkx as dnx 20 | 21 | from dwave.system.testing import MockDWaveSampler 22 | from dwave.system.composites import TilingComposite 23 | 24 | 25 | class TestTiling(unittest.TestCase): 26 | def test_pegasus_single_cell(self): 27 | #Test trivial case of single cell (K4,4+4*odd) embedding over defect free 28 | mock_sampler = MockDWaveSampler(topology_type='pegasus') # P3 structured sampler 29 | self.assertTrue('topology' in mock_sampler.properties and 'type' in mock_sampler.properties['topology']) 30 | self.assertTrue(mock_sampler.properties['topology']['type'] == 'pegasus' and 'shape' in mock_sampler.properties['topology']) 31 | sampler = TilingComposite(mock_sampler, 1, 1) 32 | h = {node: random.uniform(-1, 1) for node in sampler.structure.nodelist} 33 | J = {(u, v): random.uniform(-1, 1) for u, v in sampler.structure.edgelist} 34 | 35 | m = n = mock_sampler.properties['topology']['shape'][0] - 1 36 | expected_number_of_cells = m*n*3 37 | num_reads = 10 38 | response = sampler.sample_ising(h, J, num_reads=num_reads) 39 | self.assertTrue(sum(response.record.num_occurrences)==expected_number_of_cells*num_reads) 40 | 41 | def test_pegasus_multi_cell(self): 42 | #Test case of 2x3 cell embedding over defect free 43 | mock_sampler = MockDWaveSampler(topology_type='pegasus',topology_shape=[8]) # P8 structured sampler 44 | self.assertTrue('topology' in mock_sampler.properties and 'type' in mock_sampler.properties['topology']) 45 | self.assertTrue(mock_sampler.properties['topology']['type'] == 'pegasus' and 'shape' in mock_sampler.properties['topology']) 46 | sampler = TilingComposite(mock_sampler, 1, 1) 47 | h = {node: random.uniform(-1, 1) for node in sampler.structure.nodelist} 48 | J = {(u, v): random.uniform(-1, 1) for u, v in sampler.structure.edgelist} 49 | 50 | m_sub = 2 51 | n_sub = 3 52 | sampler = TilingComposite(mock_sampler, m_sub, n_sub) 53 | h = {node: random.uniform(-1, 1) for node in sampler.structure.nodelist} 54 | J = {(u, v): random.uniform(-1, 1) for u, v in sampler.structure.edgelist} 55 | 56 | m = n = mock_sampler.properties['topology']['shape'][0] - 1 57 | expected_number_of_cells = (m//m_sub)*(n//3)*3 58 | num_reads = 1 59 | response = sampler.sample_ising(h, J, num_reads = num_reads) 60 | self.assertTrue(sum(response.record.num_occurrences)==expected_number_of_cells*num_reads) 61 | 62 | 63 | def test_sample_ising(self): 64 | mock_sampler = MockDWaveSampler() # C4 structured sampler 65 | 66 | sampler = TilingComposite(mock_sampler, 2, 2) 67 | 68 | h = {node: random.uniform(-1, 1) for node in sampler.structure.nodelist} 69 | J = {(u, v): random.uniform(-1, 1) for u, v in sampler.structure.edgelist} 70 | 71 | response = sampler.sample_ising(h, J) 72 | 73 | def test_tile_around_hole(self): 74 | 75 | # Create a Chimera C4 structured sampler with a node missing, so that 76 | # we have a defect pattern: 77 | # OOOX 78 | # OOOO 79 | # OOOO 80 | # OOOO 81 | # where O: complete cell, X: incomplete cell 82 | mock_sampler = MockDWaveSampler(broken_nodes=[8 * 3]) 83 | hardware_graph = dnx.chimera_graph(4) # C4 84 | 85 | # Tile with 2x2 cells: 86 | sampler = TilingComposite(mock_sampler, 2, 2, 4) 87 | # Given the above chimera graph, check that the embeddings are as 88 | # follows: 89 | # 00XX 90 | # 0011 91 | # 2211 92 | # 22XX 93 | # where 0,1,2: belongs to correspoding embedding, X: not used in any 94 | # embedding 95 | self.assertSetEqual({v for s in sampler.embeddings[0].values() for v in s}, 96 | {linear_index for linear_index, (i, j, u, k) 97 | in hardware_graph.nodes(data='chimera_index') 98 | if i in (0, 1) and j in (0, 1)}) 99 | self.assertSetEqual({v for s in sampler.embeddings[1].values() for v in s}, 100 | {linear_index for linear_index, (i, j, u, k) 101 | in hardware_graph.nodes(data='chimera_index') 102 | if i in (1, 2) and j in (2, 3)}) 103 | self.assertSetEqual({v for s in sampler.embeddings[2].values() for v in s}, 104 | {linear_index for linear_index, (i, j, u, k) 105 | in hardware_graph.nodes(data='chimera_index') 106 | if i in (2, 3) and j in (0, 1)}) 107 | 108 | def test_tile_around_node_defects_pegasus(self): 109 | pegasus_shape = [5] 110 | # Create a pegasus P5 structured solver subject to node defects with the 111 | # following (nice-coordinate) 3x4x4 cell-level structure: 112 | # OOOX OOOO OOOO 113 | # OOOO OOOO OOOO 114 | # OOOO OOOO OOOO 115 | # OOOO OOOO OOOX 116 | # where O: complete cell, X: incomplete cell 117 | broken_node_nice_coordinates = [(0,0,3,0,1), (2,3,3,1,3)] 118 | broken_node_linear_coordinates = [ 119 | dnx.pegasus_coordinates(pegasus_shape[0]).nice_to_linear(coord) 120 | for coord in broken_node_nice_coordinates] 121 | mock_sampler = MockDWaveSampler(topology_type='pegasus', 122 | topology_shape=pegasus_shape, 123 | broken_nodes=broken_node_linear_coordinates) 124 | # Tile with 2x2 cells: 125 | sampler = TilingComposite(mock_sampler, 2, 2, 4) 126 | 127 | # Given the above pegasus graph, check that the embeddings are as 128 | # follows: 129 | # 00XX 3344 7788 130 | # 0011 3344 7788 131 | # 2211 5566 99XX 132 | # 22XX 5566 99XX 133 | 134 | # Check correct number of embeddings and size of each is sufficient, 135 | # given chimera test checks detailed position: 136 | self.assertTrue(len(sampler.embeddings) == 10) 137 | self.assertFalse(any([len(emb) != 32 for emb in sampler.embeddings])) 138 | 139 | #Can be refined to check exact positioning, but a lot of ugly code: 140 | #For visualization in coordinate scheme use: 141 | #for emb in sampler.embeddings: 142 | # print({key: dnx.pegasus_coordinates(pegasus_shape[0]).linear_to_nice(next(iter(val))) 143 | # for key,val in emb.items()}) 144 | 145 | def test_tile_around_edge_defects_pegasus(self): 146 | pegasus_shape = [5] 147 | 148 | # P5 structured sampler with one missing external edge that does not p 149 | # prevent tesselation of 2x2 blocks (12 tiles, equivalent to full yield) 150 | broken_edges_nice_coordinates = [(0,1,0,0,0), (0,2,0,0,0)] 151 | broken_edges = [tuple( 152 | dnx.pegasus_coordinates(pegasus_shape[0]).nice_to_linear(coord) 153 | for coord in broken_edges_nice_coordinates)] 154 | mock_sampler = MockDWaveSampler(topology_type='pegasus', 155 | topology_shape=pegasus_shape, 156 | broken_edges=broken_edges) 157 | sampler = TilingComposite(mock_sampler, 2, 2, 4) 158 | self.assertTrue(len(sampler.embeddings) == 12) 159 | 160 | # P5 structured sampler with one missing internal edge that prevents 161 | # tesselation of 2x2 blocks (otherwise 12 tiles, with edge defect 11) 162 | broken_edge_nice_coordinates = [(0,0,0,0,0), (0,0,0,1,0)] 163 | broken_edges = [tuple( 164 | dnx.pegasus_coordinates(pegasus_shape[0]).nice_to_linear(coord) 165 | for coord in broken_edge_nice_coordinates)] 166 | mock_sampler = MockDWaveSampler(topology_type='pegasus', 167 | topology_shape=pegasus_shape, 168 | broken_edges=broken_edges) 169 | sampler = TilingComposite(mock_sampler, 2, 2, 4) 170 | self.assertTrue(len(sampler.embeddings) == 11) 171 | 172 | def test_sample_ising(self): 173 | sampler = TilingComposite(MockDWaveSampler(), 2, 2) 174 | 175 | h = {node: random.uniform(-1, 1) for node in sampler.structure.nodelist} 176 | J = {(u, v): random.uniform(-1, 1) for u, v in sampler.structure.edgelist} 177 | 178 | response = sampler.sample_ising(h, J) 179 | 180 | # nothing failed and we got at least one response back per tile 181 | self.assertGreaterEqual(len(response), len(sampler.embeddings)) 182 | 183 | for sample in response.samples(): 184 | for v in h: 185 | self.assertIn(v, sample) 186 | 187 | for sample, energy in response.data(['sample', 'energy']): 188 | self.assertAlmostEqual(dimod.ising_energy(sample, h, J), energy) 189 | 190 | def test_sample_qubo(self): 191 | sampler = TilingComposite(MockDWaveSampler(), 2, 2) 192 | 193 | Q = {(u, v): random.uniform(-1, 1) for u, v in sampler.structure.edgelist} 194 | Q.update({(node, node): random.uniform(-1, 1) for node in sampler.structure.nodelist}) 195 | 196 | response = sampler.sample_qubo(Q) 197 | 198 | # nothing failed and we got at least one response back per tile 199 | self.assertGreaterEqual(len(response), len(sampler.embeddings)) 200 | 201 | for sample in response.samples(): 202 | for u, v in Q: 203 | self.assertIn(v, sample) 204 | self.assertIn(u, sample) 205 | 206 | for sample, energy in response.data(['sample', 'energy']): 207 | self.assertAlmostEqual(dimod.qubo_energy(sample, Q), energy) 208 | 209 | def test_too_many_nodes(self): 210 | mock_sampler = MockDWaveSampler() # C4 structured sampler 211 | 212 | sampler = TilingComposite(mock_sampler, 2, 2) 213 | 214 | h = {0: -1, 1: 1} 215 | J = {} 216 | 217 | response = sampler.sample_ising(h, J) 218 | 219 | __, num_columns = response.record.sample.shape 220 | 221 | self.assertEqual(num_columns, 2) 222 | -------------------------------------------------------------------------------- /tests/test_virtual_graph_composite.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 D-Wave Systems Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | import collections 17 | 18 | import dimod 19 | 20 | from dwave.system.composites import VirtualGraphComposite 21 | 22 | from dwave.system.testing import MockDWaveSampler 23 | 24 | 25 | class TestVirtualGraphWithMockDWaveSampler(unittest.TestCase): 26 | def setUp(self): 27 | self.sampler = MockDWaveSampler() 28 | 29 | def test_smoke(self): 30 | child_sampler = MockDWaveSampler() 31 | with self.assertWarns(DeprecationWarning): 32 | sampler = VirtualGraphComposite(child_sampler, {'a': [0]}, flux_bias_num_reads=1) 33 | 34 | # depending on how recenlty flux bias data was gathered, this may be true 35 | child_sampler.flux_biases_flag = False 36 | child_sampler.mocked_parameters.add('flux_biases') # Don't raise warning 37 | if sampler.flux_biases: 38 | sampler.sample_ising({'a': -1}, {}) 39 | self.assertTrue(child_sampler.flux_biases_flag) # true when some have been provided to sample_ising 40 | 41 | def test_structure_keyword_setting(self): 42 | with self.assertWarns(DeprecationWarning): 43 | sampler = VirtualGraphComposite(self.sampler, embedding={'a': set(range(8)), 44 | 'b': set(range(8, 16)), 45 | 'c': set(range(16, 24))}, 46 | flux_biases=False) 47 | 48 | nodelist, edgelist, adj = sampler.structure 49 | self.assertEqual(nodelist, ['a', 'b', 'c']) 50 | self.assertEqual(edgelist, [('a', 'b'), ('b', 'c')]) 51 | self.assertEqual(adj, {'a': {'b'}, 'b': {'a', 'c'}, 'c': {'b'}}) 52 | 53 | # unlike variable names 54 | with self.assertWarns(DeprecationWarning): 55 | sampler = VirtualGraphComposite(self.sampler, embedding={'a': set(range(8)), 56 | 1: set(range(8, 16)), 57 | 'c': set(range(16, 24))}, 58 | flux_biases=False) 59 | nodelist, edgelist, adj = sampler.structure 60 | self.assertEqual(set(nodelist), {'a', 1, 'c'}) 61 | self.assertEqual(adj, {'a': {1}, 1: {'a', 'c'}, 'c': {1}}) 62 | self.assertIsInstance(edgelist, list) 63 | self.assertIsInstance(nodelist, list) 64 | 65 | # edges should still be unique 66 | for u in adj: 67 | for v in adj[u]: 68 | if (u, v) in edgelist: 69 | assert (v, u) not in edgelist 70 | if (v, u) in edgelist: 71 | assert (u, v) not in edgelist 72 | assert (u, v) in edgelist or (v, u) in edgelist 73 | 74 | def test_embedding_parameter(self): 75 | """Given embedding should be saved as a parameter""" 76 | sampler = self.sampler 77 | 78 | __, __, adj = sampler.structure 79 | embedding = {v: (v,) for v in adj} 80 | 81 | with self.assertWarns(DeprecationWarning): 82 | sampler = VirtualGraphComposite(sampler, embedding=embedding, flux_biases=False) 83 | 84 | self.assertEqual(sampler.embedding, embedding) 85 | 86 | def test_simple_complete_graph_sample_ising(self): 87 | """sample_ising on a K4.""" 88 | 89 | with self.assertWarns(DeprecationWarning): 90 | K4 = VirtualGraphComposite(self.sampler, embedding={0: {0, 4}, 91 | 1: {1, 5}, 92 | 2: {2, 6}, 93 | 3: {3, 7}}, 94 | flux_biases=False) 95 | 96 | K4.sample_ising({0: .1, 1: .2}, {(0, 1): 1.5}) 97 | --------------------------------------------------------------------------------