From dbd0f8567d91ca2745f3ada8f61b713ba0c85c60 Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Mon, 14 Nov 2022 14:07:30 -0800 Subject: [PATCH] Move the source code to dwave-samplers --- .circleci/config.yml | 239 ++++++------------ .readthedocs.yml | 12 - MANIFEST.in | 2 - README.rst | 20 +- benchmarks/asv.conf.json | 164 ------------ benchmarks/benchmarks/__init__.py | 0 benchmarks/benchmarks/descent.py | 34 --- benchmarks/benchmarks/sampler.py | 108 -------- benchmarks/requirements.txt | 5 - docs/LICENSE | 1 - docs/Makefile | 24 -- docs/README.rst | 1 - docs/_static/cookie_notice.css | 31 --- docs/_static/cookie_notice.js | 29 --- docs/conf.py | 122 --------- docs/index.rst | 56 ----- docs/installation.rst | 6 - docs/license.rst | 4 - docs/make.bat | 35 --- docs/reference/composites.rst | 38 --- docs/reference/index.rst | 10 - docs/reference/samplers.rst | 52 ---- docs/requirements.txt | 3 - docs/sdk_index.rst | 19 -- greedy/composite.py | 76 +----- greedy/{decl.pxd => descent.py} | 17 +- greedy/descent.pyx | 117 --------- greedy/sampler.py | 266 +------------------- greedy/src/descent.cpp | 403 ------------------------------ greedy/src/descent.h | 65 ----- pyproject.toml | 23 -- requirements.txt | 10 +- setup.cfg | 6 +- setup.py | 44 +--- tests/Makefile | 8 - tests/test_descent.cpp | 64 ----- 36 files changed, 92 insertions(+), 2022 deletions(-) delete mode 100644 .readthedocs.yml delete mode 100644 MANIFEST.in delete mode 100644 benchmarks/asv.conf.json delete mode 100644 benchmarks/benchmarks/__init__.py delete mode 100644 benchmarks/benchmarks/descent.py delete mode 100644 benchmarks/benchmarks/sampler.py delete mode 100644 benchmarks/requirements.txt delete mode 120000 docs/LICENSE delete mode 100644 docs/Makefile delete mode 120000 docs/README.rst delete mode 100644 docs/_static/cookie_notice.css delete mode 100644 docs/_static/cookie_notice.js delete mode 100644 docs/conf.py delete mode 100644 docs/index.rst delete mode 100644 docs/installation.rst delete mode 100644 docs/license.rst delete mode 100644 docs/make.bat delete mode 100644 docs/reference/composites.rst delete mode 100644 docs/reference/index.rst delete mode 100644 docs/reference/samplers.rst delete mode 100644 docs/requirements.txt delete mode 100644 docs/sdk_index.rst rename greedy/{decl.pxd => descent.py} (55%) delete mode 100644 greedy/descent.pyx delete mode 100644 greedy/src/descent.cpp delete mode 100644 greedy/src/descent.h delete mode 100644 pyproject.toml delete mode 100644 tests/Makefile delete mode 100644 tests/test_descent.cpp diff --git a/.circleci/config.yml b/.circleci/config.yml index 376df97..8827903 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,194 +1,121 @@ version: 2.1 -parameters: - package: - type: string - default: dwave-greedy - orbs: - ocean: dwave/ocean@1.4 + ocean: dwave/ocean@1.3 + win: circleci/windows@2.2.0 environment: PIP_PROGRESS_BAR: 'off' jobs: - test-install: + test-linux: + parameters: + python-version: + type: string + docker: - - image: circleci/python:3.7 # last version pip==8.1.0 works on + - image: circleci/python:<< parameters.python-version >> + + working_directory: ~/repo + + steps: + - checkout + + - ocean/pip-install: + requirements: tests/requirements.txt + cache: true + + - ocean/pip-install: + packages: . + cache: false + + - ocean/coverage-run-unittest + + test-macos: + parameters: + python-version: + type: string + + executor: + name: ocean/macos + xcode: "13.2.0" steps: - checkout - - attach_workspace: &attach-dist - at: dist + - ocean/brew-install-pyenv + + - ocean/pyenv-install-python: + python-version: << parameters.python-version >> + cache: true + + - ocean/pip-install: + requirements: tests/requirements.txt + cache: true + + - ocean/pip-install: + packages: . + cache: false + + - ocean/coverage-run-unittest + + deploy: + docker: + - image: circleci/python:3.7 + + working_directory: ~/repo + + steps: + - checkout - run: - # pip 19.0 is the first to support pyproject.toml build config *with* - # environment markers (note: we split numpy on py310), and - # *manylinux2010* wheels (required by the numpy version we use) - name: Install from sdist with pip==19.0 + name: create virtual env command: | - python -m venv env-sdist-pip19 - . env-sdist-pip19/bin/activate - pip install 'pip==19.0' - pip install ./dist/dwave-greedy*.tar.gz + python -m venv env - run: - name: Install from sdist with latest pip + name: verify version matches tag command: | - python -m venv env-sdist - . env-sdist/bin/activate - pip install -U pip - pip install ./dist/dwave-greedy*.tar.gz + . env/bin/activate + pip install . + echo $CIRCLE_TAG + [[ "$(pip show dwave-greedy 2>/dev/null | grep Version)" == "Version: $CIRCLE_TAG" ]] - run: - # pip 8.1.0 is the first version that supports manylinux1 - name: Install from wheel with pip==8.1.0 + name: build sdist and bdist command: | - python -m venv env-wheel-pip8 - . env-wheel-pip8/bin/activate - pip install 'pip==8.1.0' - # simulate install from wheel on pypi (but use local dist) - # (1) install greedy only (no deps) from local dist - pip install --no-index --find-links=dist --only-binary=dwave-greedy dwave-greedy --no-deps - # (2) install deps only from pypi - pip install --find-links=dist --only-binary=:all: dwave-greedy + . env/bin/activate + pip install -U pip setuptools wheel + python setup.py sdist + python setup.py bdist_wheel - run: - name: Install from wheel with latest pip + name: upload command: | - python -m venv env-wheel - . env-wheel/bin/activate - pip install -U pip - pip install -r requirements.txt - pip install --no-index --find-links=dist --only-binary=dwave-greedy dwave-greedy - + . env/bin/activate + pip install twine + twine check dist/* + twine upload -u "$PYPI_USERNAME" -p "$PYPI_PASSWORD" --skip-existing ./dist/* workflows: - build-test-deploy: - jobs: - - ocean/build-sdist: - filters: &always-run # required because it's indirectly required by the deploy job that runs on tags only - tags: - only: /.*/ + version: 2 - - ocean/build-manylinux-wheel: - name: build-<< matrix.manylinux-tag >>_<< matrix.manylinux-arch >>-py<< matrix.python-version >> + test: + jobs: + - test-linux: matrix: parameters: - manylinux-tag: ["manylinux1", "manylinux2014"] - manylinux-arch: ["x86_64"] python-version: &python-versions ["3.7.9", "3.8.9", "3.9.4", "3.10.0"] - exclude: - # py310 not available in manylinux1_x86_64 - - manylinux-tag: "manylinux1" - manylinux-arch: "x86_64" - python-version: "3.10.0" - filters: - <<: *always-run - - - ocean/cibw-build-linux-aarch64-wheel: - name: build-<< matrix.manylinux-tag >>_aarch64-py<< matrix.python-version >> - matrix: - parameters: - manylinux-tag: ["manylinux2014"] - python-version: *python-versions - filters: - <<: *always-run - - - ocean/test-linux-from-dist: - name: test-linux-py<< matrix.python-version >>|<< matrix.constraints >> - requires: - - ocean/build-sdist - - ocean/build-manylinux-wheel - - ocean/cibw-build-linux-aarch64-wheel - post-steps: - - run: make -C tests/ - matrix: - parameters: - python-version: *python-versions - package: [<< pipeline.parameters.package >>] - constraints: ["dimod~=0.9.0,>=0.9.2 dwave-system~=1.9.0", "dimod~=0.10.0", "dimod==0.11.0"] - exclude: - # dimod < 0.10 not supported on py310 - - python-version: "3.10.0" - package: << pipeline.parameters.package >> - constraints: "dimod~=0.9.0,>=0.9.2 dwave-system~=1.9.0" - filters: - <<: *always-run - - - test-install: - requires: - - ocean/build-sdist - - ocean/build-manylinux-wheel - filters: - <<: *always-run - - - ocean/test-docs: - name: test-docs - requires: - - ocean/build-sdist - - ocean/build-manylinux-wheel - matrix: - parameters: - package: [<< pipeline.parameters.package >>] - filters: - <<: *always-run - - - ocean/cibw-build-macos-wheel: - name: build-macos-py<< matrix.python-version >> - matrix: - parameters: - python-version: *python-versions - cibw-archs: ["x86_64 universal2"] - filters: - <<: *always-run - - - ocean/test-macos-from-dist: - name: test-macos-py<< matrix.python-version >> - requires: - - ocean/build-sdist - - ocean/cibw-build-macos-wheel - post-steps: - - run: make -C tests/ + - test-macos: matrix: parameters: python-version: *python-versions - xcode: ["13.2.0"] - package: [<< pipeline.parameters.package >>] - filters: - <<: *always-run - - ocean/build-windows-wheel: - name: build-win-<< matrix.python-arch >>-py<< matrix.python-version >> - matrix: - parameters: - python-version: *python-versions - python-arch: &python-archs-win [""] - filters: - <<: *always-run - - - ocean/test-windows-from-dist: - name: test-win-<< matrix.python-arch >>-py<< matrix.python-version >> - requires: - - ocean/build-windows-wheel - matrix: - parameters: - python-version: *python-versions - python-arch: *python-archs-win - package: [<< pipeline.parameters.package >>] - filters: - <<: *always-run - - - ocean/pypi-deploy: - requires: - - ocean/test-linux-from-dist - - ocean/test-macos-from-dist - - ocean/test-windows-from-dist - - ocean/test-docs - - test-install - filters: + deploy: + jobs: + - deploy: + filters: &deploy-filters tags: - only: /^[0-9]+(\.[0-9]+)*((\.dev|rc)([0-9]+)?)?$/ + only: /^[0-9]+(\.[0-9]+)*(\.dev([0-9]+)?)?$/ branches: ignore: /.*/ diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index 787168a..0000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: 2 - -sphinx: - configuration: docs/conf.py - -python: - version: 3.9 - install: - - requirements: requirements.txt - - requirements: docs/requirements.txt - - method: pip - path: . diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index f73ec3c..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include pyproject.toml -recursive-include greedy *.h *.cpp *.pxd *.pyx diff --git a/README.rst b/README.rst index 905cea0..40f6f98 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,5 @@ +> :warning: **Note**: *dwave-greedy* is deprecated in favor of `dwave-samplers `_. + .. image:: https://circleci.com/gh/dwavesystems/dwave-greedy.svg?style=svg :target: https://circleci.com/gh/dwavesystems/dwave-greedy :alt: Linux/MacOS/Windows build status @@ -58,24 +60,6 @@ Install from a package on PyPI: pip install dwave-greedy -or install from source: - -.. code-block:: bash - - pip install git+https://github.com/dwavesystems/dwave-greedy.git#egg=dwave-greeedy - -Note: installation from source involves a "cythonization" step. To install -project requirements automatically, make sure to use a PEP-517 compliant pip, -e.g. ``pip>=10.0``. - -To build from source: - -.. code-block:: bash - - pip install -r requirements.txt - python setup.py build_ext --inplace - python setup.py install - .. installation-end-marker diff --git a/benchmarks/asv.conf.json b/benchmarks/asv.conf.json deleted file mode 100644 index 3b5c4c1..0000000 --- a/benchmarks/asv.conf.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - // The version of the config file format. Do not change, unless - // you know what you are doing. - "version": 1, - - // The name of the project being benchmarked - "project": "dwave-greedy", - - // The project's homepage - "project_url": "https://github.com/dwavesystems/dwave-greedy", - - // The URL or local path of the source code repository for the - // project being benchmarked - "repo": "..", - - // The Python project's subdirectory in your repo. If missing or - // the empty string, the project is assumed to be located at the root - // of the repository. - // "repo_subdir": "", - - // Customizable commands for building, installing, and - // uninstalling the project. See asv.conf.json documentation. - // - // "install_command": ["in-dir={env_dir} python -mpip install {wheel_file}"], - "install_command": [ - "in-dir={build_dir} python -m pip install -r benchmarks/requirements.txt", - "in-dir={env_dir} python -m pip install {wheel_file}" - ], - // "uninstall_command": ["return-code=any python -mpip uninstall -y {project}"], - "build_command": [ - "USE_CYTHON=1 python setup.py build_ext --inplace", - "PIP_NO_BUILD_ISOLATION=false python -m pip wheel --no-deps --no-index -w {build_cache_dir} {build_dir}" - ], - - // List of branches to benchmark. If not provided, defaults to "master" - // (for git) or "default" (for mercurial). - // "branches": ["master"], // for git - // "branches": ["default"], // for mercurial - - // The DVCS being used. If not set, it will be automatically - // determined from "repo" by looking at the protocol in the URL - // (if remote), or by looking for special directories, such as - // ".git" (if local). - // "dvcs": "git", - - // The tool to use to create environments. May be "conda", - // "virtualenv" or other value depending on the plugins in use. - // If missing or the empty string, the tool will be automatically - // determined by looking for tools on the PATH environment - // variable. - "environment_type": "virtualenv", - - // timeout in seconds for installing any dependencies in environment - // defaults to 10 min - //"install_timeout": 600, - - // the base URL to show a commit for the project. - // "show_commit_url": "http://github.com/owner/project/commit/", - - // The Pythons you'd like to test against. If not provided, defaults - // to the current version of Python used to run `asv`. - // "pythons": ["2.7", "3.6"], - - // The list of conda channel names to be searched for benchmark - // dependency packages in the specified order - // "conda_channels": ["conda-forge", "defaults"], - - // The matrix of dependencies to test. Each key is the name of a - // package (in PyPI) and the values are version numbers. An empty - // list or empty string indicates to just test against the default - // (latest) version. null indicates that the package is to not be - // installed. If the package to be tested is only available from - // PyPi, and the 'environment_type' is conda, then you can preface - // the package name by 'pip+', and the package will be installed via - // pip (with all the conda available packages installed first, - // followed by the pip installed packages). - // - // "matrix": { - // "numpy": ["1.6", "1.7"], - // "six": ["", null], // test with and without six installed - // "pip+emcee": [""], // emcee is only available for install with pip. - // }, - - // Combinations of libraries/python versions can be excluded/included - // from the set to test. Each entry is a dictionary containing additional - // key-value pairs to include/exclude. - // - // An exclude entry excludes entries where all values match. The - // values are regexps that should match the whole string. - // - // An include entry adds an environment. Only the packages listed - // are installed. The 'python' key is required. The exclude rules - // do not apply to includes. - // - // In addition to package names, the following keys are available: - // - // - python - // Python version, as in the *pythons* variable above. - // - environment_type - // Environment type, as above. - // - sys_platform - // Platform, as in sys.platform. Possible values for the common - // cases: 'linux2', 'win32', 'cygwin', 'darwin'. - // - // "exclude": [ - // {"python": "3.2", "sys_platform": "win32"}, // skip py3.2 on windows - // {"environment_type": "conda", "six": null}, // don't run without six on conda - // ], - // - // "include": [ - // // additional env for python2.7 - // {"python": "2.7", "numpy": "1.8"}, - // // additional env if run on windows+conda - // {"platform": "win32", "environment_type": "conda", "python": "2.7", "libpython": ""}, - // ], - - // The directory (relative to the current directory) that benchmarks are - // stored in. If not provided, defaults to "benchmarks" - // "benchmark_dir": "benchmarks", - - // The directory (relative to the current directory) to cache the Python - // environments in. If not provided, defaults to "env" - "env_dir": ".asv/env", - - // The directory (relative to the current directory) that raw benchmark - // results are stored in. If not provided, defaults to "results". - "results_dir": ".asv/results", - - // The directory (relative to the current directory) that the html tree - // should be written to. If not provided, defaults to "html". - "html_dir": ".asv/html", - - // The number of characters to retain in the commit hashes. - // "hash_length": 8, - - // `asv` will cache results of the recent builds in each - // environment, making them faster to install next time. This is - // the number of builds to keep, per environment. - // "build_cache_size": 2, - - // The commits after which the regression search in `asv publish` - // should start looking for regressions. Dictionary whose keys are - // regexps matching to benchmark names, and values corresponding to - // the commit (exclusive) after which to start looking for - // regressions. The default is to start from the first commit - // with results. If the commit is `null`, regression detection is - // skipped for the matching benchmark. - // - // "regressions_first_commits": { - // "some_benchmark": "352cdf", // Consider regressions only after this commit - // "another_benchmark": null, // Skip regression detection altogether - // }, - - // The thresholds for relative change in results, after which `asv - // publish` starts reporting regressions. Dictionary of the same - // form as in ``regressions_first_commits``, with values - // indicating the thresholds. If multiple entries match, the - // maximum is taken. If no entry matches, the default is 5%. - // - // "regressions_thresholds": { - // "some_benchmark": 0.01, // Threshold of 1% - // "another_benchmark": 0.5, // Threshold of 50% - // }, -} diff --git a/benchmarks/benchmarks/__init__.py b/benchmarks/benchmarks/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/benchmarks/benchmarks/descent.py b/benchmarks/benchmarks/descent.py deleted file mode 100644 index ae59918..0000000 --- a/benchmarks/benchmarks/descent.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019 D-Wave Systems Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -import greedy - - -class SteepestGradientDescentCython(object): - params = [1, 1000, 1000000] - param_names = ['num_samples'] - repeat = (3, 10, 60) - - def setup(self, num_samples): - self.linear_biases = [2, 2] - self.coupler_starts = [0] - self.coupler_ends = [1] - self.coupler_weights = [-1] - self.initial_states = np.tile(np.array([1, 1], dtype=np.int8), (num_samples, 1)) - - def time_single_flip(self, num_samples): - samples, energies, num_steps = greedy.descent.steepest_gradient_descent( - num_samples, self.linear_biases, self.coupler_starts, - self.coupler_ends, self.coupler_weights, self.initial_states) diff --git a/benchmarks/benchmarks/sampler.py b/benchmarks/benchmarks/sampler.py deleted file mode 100644 index 87ad411..0000000 --- a/benchmarks/benchmarks/sampler.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright 2019 D-Wave Systems Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import networkx as nx - -import dimod -import greedy - - -class SteepestDescentSimple(object): - params = [1, 1000, 1000000] - param_names = ['num_reads'] - repeat = (3, 10, 60) - - def setup(self, num_reads): - self.sampler = greedy.SteepestDescentSampler() - self.h = {0: 2, 1: 2} - self.J = {(0, 1): -1} - - def time_single_flip(self, num_reads): - self.sampler.sample_ising(self.h, self.J, num_reads=num_reads, seed=0) - - -class SteepestDescentComplete(object): - """Test steepest descent on complete graphs.""" - - params = ([100, 1000, 2000], [1, 10]) - param_names = ['graph_size', 'num_reads'] - repeat = (3, 10, 60) - timeout = 300 - - def setup(self, graph_size, num_reads): - self.sampler = greedy.SteepestDescentSampler() - self.bqm = dimod.generators.random.ran_r(r=1, graph=graph_size, seed=0) - - def time_ran1(self, graph_size, num_reads): - self.sampler.sample(self.bqm, num_reads=num_reads, seed=0) - - -class SteepestDescentSparse(object): - """Test steepest descent on Erdős-Rényi sparse graphs with varying density.""" - - params = ([100, 1000, 2000], [0.05, 0.1, 0.25, 0.5], [1, 10]) - param_names = ['graph_size', 'graph_density', 'num_reads'] - repeat = (3, 10, 60) - timeout = 300 - - def setup(self, graph_size, graph_density, num_reads): - self.sampler = greedy.SteepestDescentSampler() - self.graph = nx.fast_gnp_random_graph(n=graph_size, p=graph_density, seed=0) - self.bqm = dimod.generators.random.ran_r(r=1, graph=self.graph, seed=0) - - def time_ran1(self, graph_size, graph_density, num_reads): - self.sampler.sample(self.bqm, num_reads=num_reads, seed=0) - - -class SteepestDescentLargeSparse(object): - """Test steepest descent on large and sparse Erdős-Rényi graphs. - - Note: - Because of practical limitations on BQM size in `dimod`, we limit - the number of edges in test problems below to ~5M. - """ - - params = ([2000, 10000, 15000], [0.01, 0.05], [1, 10]) - param_names = ['graph_size', 'graph_density', 'num_reads'] - repeat = (3, 10, 60) - timeout = 300 - - def setup(self, graph_size, graph_density, num_reads): - self.sampler = greedy.SteepestDescentSampler() - self.graph = nx.fast_gnp_random_graph(n=graph_size, p=graph_density, seed=0) - self.bqm = dimod.generators.random.ran_r(r=1, graph=self.graph, seed=0) - - def time_ran1(self, graph_size, graph_density, num_reads): - self.sampler.sample(self.bqm, num_reads=num_reads, seed=0, large_sparse_opt=True) - - -class SteepestDescentCompleteOverBQMTypes(object): - """Test steepest descent on complete graphs.""" - - params = ([dimod.AdjArrayBQM, - dimod.AdjDictBQM, - dimod.AdjMapBQM, - dimod.AdjVectorBQM], [1, 10]) - param_names = ['bqm_type', 'num_reads'] - repeat = (3, 10, 60) - timeout = 300 - graph_size = 1000 - - def setup(self, bqm_type, num_reads): - self.sampler = greedy.SteepestDescentSampler() - self.bqm = dimod.generators.random.ran_r( - r=1, graph=self.graph_size, cls=bqm_type, seed=0) - - def time_ran1(self, bqm_type, num_reads): - self.sampler.sample(self.bqm, num_reads=num_reads, seed=0) diff --git a/benchmarks/requirements.txt b/benchmarks/requirements.txt deleted file mode 100644 index 75a2aea..0000000 --- a/benchmarks/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ --r ../requirements.txt - -asv -virtualenv -networkx diff --git a/docs/LICENSE b/docs/LICENSE deleted file mode 120000 index ea5b606..0000000 --- a/docs/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index f994c83..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -clean: - -rm -rf build/* - -rm -rf reference/generated/* - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/README.rst b/docs/README.rst deleted file mode 120000 index 89a0106..0000000 --- a/docs/README.rst +++ /dev/null @@ -1 +0,0 @@ -../README.rst \ No newline at end of file diff --git a/docs/_static/cookie_notice.css b/docs/_static/cookie_notice.css deleted file mode 100644 index 42ed667..0000000 --- a/docs/_static/cookie_notice.css +++ /dev/null @@ -1,31 +0,0 @@ -#cookie-notice { - display: none; - bottom: 0; - position: fixed; - width: 100%; - background: white; - z-index: 1000000; - height: auto; - text-align: center; -} -.cookie-button { - border: none; - display: inline-block; - justify-content: center; - height: 48px; - font-size: 16px; - font-weight: 600; - border-radius: 4px; - background: rgb(41, 128, 185); - box-shadow: 0 5px 14px 0 rgba(29,30,36,0.19); - color: #fff; - cursor: pointer; - padding-top: 12px; - padding-bottom: 12px; - padding-left: 42px; - padding-right: 42px; - margin: 1em; - margin-left: 2em; - white-space: nowrap; - vertical-align: middle; -} diff --git a/docs/_static/cookie_notice.js b/docs/_static/cookie_notice.js deleted file mode 100644 index 93ac3f7..0000000 --- a/docs/_static/cookie_notice.js +++ /dev/null @@ -1,29 +0,0 @@ -const send_ga = () => { - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-124167090-6', 'auto'); - ga('send', 'pageview'); -} - -let cookieAccepted = localStorage.cookieAccepted; -if (cookieAccepted == undefined) { - $(document).ready(function () { - var cookieDiv = document.createElement('div') - cookieDiv.id = 'cookie-notice' - 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.

" - - $('body').append(cookieDiv).ready(() => { - $('#cookie-notice').fadeIn('slow'); - $("#cookie-accept").click(function () { - localStorage.setItem("cookieAccepted", true) - $('#cookie-notice').fadeOut('slow'); - send_ga() - }); - }) - }) -} else if (cookieAccepted == "true") { - send_ga() -} diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 6e3f546..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) -# sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -# instead, make sure the package is installed when building docs - - -# -- Project information ----------------------------------------------------- - -from greedy import package_info -project = 'D-Wave Greedy' -copyright = '2019, D-Wave Systems Inc.' -author = package_info.__author__ -version = package_info.__version__ -release = package_info.__version__ - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autosummary', - 'sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.mathjax', - 'sphinx.ext.napoleon', - 'sphinx.ext.todo', - 'sphinx.ext.viewcode', - 'sphinx.ext.ifconfig' -] - -autosummary_generate = True - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -source_suffix = ['.rst', '.md'] -source_parsers = {'.md': 'recommonmark.parser.CommonMarkParser'} - -# The master toctree document. -master_doc = 'index' - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -add_module_names = False - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'sdk_index.rst'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True - -modindex_common_prefix = ['greedy.'] - -doctest_global_setup = """ -import dimod -import greedy - -from unittest.mock import Mock -from dwave.system.testing import MockDWaveSampler -import dwave.system -dwave.system.DWaveSampler = Mock() -dwave.system.DWaveSampler.side_effect = MockDWaveSampler -""" - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -import sphinx_rtd_theme -html_theme = 'sphinx_rtd_theme' -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -def setup(app): - app.add_css_file('cookie_notice.css') - app.add_js_file('cookie_notice.js') - app.add_config_value('target', 'repo', 'env') - - -intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), - 'networkx': ('https://networkx.github.io/documentation/stable/', None), - 'oceandocs': ('https://docs.ocean.dwavesys.com/en/stable/', None), - 'sysdocs_gettingstarted': ('https://docs.dwavesys.com/docs/latest/', None), -} diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 2d0d669..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _index_greedy: - -============ -dwave-greedy -============ - -.. include:: README.rst - :start-after: index-start-marker - :end-before: index-end-marker - - -Documentation -------------- - -.. only:: html - -.. note:: This documentation is for the latest version of - `dwave-greedy `_. - Documentation for the version currently installed by - `dwave-ocean-sdk `_ - is here: :std:doc:`dwave-greedy `. - -.. sdk-start-marker - -.. toctree:: - :maxdepth: 1 - - reference/index - -.. sdk-end-marker - -.. toctree:: - :caption: Code - :maxdepth: 1 - - Source - installation - license - -.. toctree:: - :caption: D-Wave's Ocean Software - :maxdepth: 1 - - Ocean Home - Ocean Documentation - Ocean Glossary - -.. toctree:: - :caption: D-Wave - :maxdepth: 1 - - D-Wave - Leap - D-Wave System Documentation diff --git a/docs/installation.rst b/docs/installation.rst deleted file mode 100644 index 67b8c51..0000000 --- a/docs/installation.rst +++ /dev/null @@ -1,6 +0,0 @@ -Installation -============ - -.. include:: README.rst - :start-after: installation-start-marker - :end-before: installation-end-marker diff --git a/docs/license.rst b/docs/license.rst deleted file mode 100644 index 71e7788..0000000 --- a/docs/license.rst +++ /dev/null @@ -1,4 +0,0 @@ -License -======= - -.. include:: LICENSE diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 2119f51..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/reference/composites.rst b/docs/reference/composites.rst deleted file mode 100644 index 74cab38..0000000 --- a/docs/reference/composites.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. _included_composites: - -========== -Composites -========== - -The `dwave-greedy` package currently includes just one composite, -:class:`~greedy.composite.SteepestDescentComposite`. - -.. currentmodule:: greedy.composite - - -SteepestDescentComposite -======================== - -Class ------ - -.. autoclass:: SteepestDescentComposite - -Attributes ----------- - -.. autosummary:: - :toctree: generated/ - - SteepestDescentComposite.properties - SteepestDescentComposite.parameters - -Methods -------- - -.. autosummary:: - :toctree: generated/ - - SteepestDescentComposite.sample - SteepestDescentComposite.sample_ising - SteepestDescentComposite.sample_qubo diff --git a/docs/reference/index.rst b/docs/reference/index.rst deleted file mode 100644 index 1461a5f..0000000 --- a/docs/reference/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _reference_greedy: - -Reference Documentation -*********************** - -.. toctree:: - :maxdepth: 2 - - samplers - composites diff --git a/docs/reference/samplers.rst b/docs/reference/samplers.rst deleted file mode 100644 index 17757c5..0000000 --- a/docs/reference/samplers.rst +++ /dev/null @@ -1,52 +0,0 @@ -.. _included_samplers: - -======== -Samplers -======== - -The `dwave-greedy` package currently includes just one sampler, -:class:`~greedy.sampler.SteepestDescentSampler`, which is an alias for -:class:`~greedy.sampler.SteepestDescentSolver`. - -A :term:`sampler` accepts a :term:`binary quadratic model` (BQM) and returns -variable assignments. Samplers generally try to find minimizing values but can -also sample from distributions defined by the BQM. - -.. currentmodule:: greedy.sampler - - -SteepestDescentSolver -===================== - -Class ------ - -.. autoclass:: SteepestDescentSolver - -Attributes ----------- - -.. autosummary:: - :toctree: generated/ - - SteepestDescentSolver.properties - SteepestDescentSolver.parameters - -Methods -------- - -.. autosummary:: - :toctree: generated/ - - SteepestDescentSolver.sample - SteepestDescentSolver.sample_ising - SteepestDescentSolver.sample_qubo - - -SteepestDescentSampler -====================== - -Class ------ - -.. autoclass:: SteepestDescentSampler diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 19c2587..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -sphinx>=4.0.0,<5.0.0 -sphinx_rtd_theme -recommonmark diff --git a/docs/sdk_index.rst b/docs/sdk_index.rst deleted file mode 100644 index 4ff4051..0000000 --- a/docs/sdk_index.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _sdk_index_greedy: - -============ -dwave-greedy -============ - -.. include:: README.rst - :start-after: index-start-marker - :end-before: index-end-marker - -.. include:: index.rst - :start-after: sdk-start-marker - :end-before: sdk-end-marker - -.. toctree:: - :caption: Code - :maxdepth: 1 - - Source diff --git a/greedy/composite.py b/greedy/composite.py index 7e8a60b..3d49419 100644 --- a/greedy/composite.py +++ b/greedy/composite.py @@ -16,78 +16,4 @@ samples. """ -import dimod -import greedy - -__all__ = ['SteepestDescentComposite'] - - -class SteepestDescentComposite(dimod.ComposedSampler): - """Runs greedy local optimization (steepest descent) on input problem, - seeded with samples from the sampler. - - Args: - child_sampler (:class:`dimod.Sampler`): - A dimod sampler, such as a :class:`~dwave.system.samplers.DWaveSampler`. - - Examples: - >>> from dwave.system import DWaveSampler - >>> from greedy import SteepestDescentComposite - ... - >>> sampler = SteepestDescentComposite(DWaveSampler()) - >>> h = {0: -1, 4: 2} - >>> J = {(0, 4): 1.5} - >>> sampleset = sampler.sample_ising(h, J) - >>> sampleset.first.energy - -4.5 - - """ - - def __init__(self, child_sampler): - self.children = [child_sampler] - - # set the parameters - self.parameters = child_sampler.parameters.copy() - - # set the properties - self.properties = dict(child_properties=child_sampler.properties.copy()) - - parameters = None # overwritten by init - """dict[str, list]: Parameters in the form of a dict. - - For an instantiated composed sampler, keys are the keyword parameters - accepted by the child sampler and parameters added by the composite. - """ - - children = None # overwritten by init - """list [child_sampler]: List containing the structured sampler.""" - - properties = None # overwritten by init - """dict: Properties in the form of a dict. - - Contains the properties of the child sampler. - """ - - def sample(self, bqm, **parameters): - """Sample from the provided binary quadratic model. - - Args: - bqm (:class:`dimod.BinaryQuadraticModel`): - Binary quadratic model to be sampled from. - - **parameters: - Parameters for the sampling method, specified by the child - sampler. - - Returns: - :class:`dimod.SampleSet` - """ - - # solve the problem on the child system - child = self.child - - sampleset = child.sample(bqm, **parameters) - - greedy_sampler = greedy.SteepestDescentSolver() - - return greedy_sampler.sample(bqm, initial_states=sampleset) +from dwave.samplers.greedy import SteepestDescentComposite diff --git a/greedy/decl.pxd b/greedy/descent.py similarity index 55% rename from greedy/decl.pxd rename to greedy/descent.py index aa3bba2..451b50e 100644 --- a/greedy/decl.pxd +++ b/greedy/descent.py @@ -12,19 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -from libcpp cimport bool -from libcpp.vector cimport vector - -cdef extern from "descent.h": - - unsigned int steepest_gradient_descent( - char* states, - double* energies, - unsigned* num_steps, - const int num_samples, - const vector[double]& linear_biases, - const vector[int]& coupler_starts, - const vector[int]& coupler_ends, - const vector[double]& coupler_weights, - bool large_sparse_opt - ) nogil +from dwave.samplers.greedy.descent import steepest_gradient_descent diff --git a/greedy/descent.pyx b/greedy/descent.pyx deleted file mode 100644 index 3c36252..0000000 --- a/greedy/descent.pyx +++ /dev/null @@ -1,117 +0,0 @@ -# distutils: language = c++ -# distutils: include_dirs = greedy/src/ -# distutils: sources = greedy/src/descent.cpp - -# Copyright 2019 D-Wave Systems Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from libcpp cimport bool -from libcpp.vector cimport vector - -import numpy as np -cimport numpy as np - -cimport decl - - -def steepest_gradient_descent(num_samples, - linear_biases, - coupler_starts, coupler_ends, coupler_weights, - np.ndarray[char, ndim=2, mode="c"] states_numpy, - large_sparse_opt=False): - - """Wraps `steepest_gradient_descent` from `descent.cpp`. Accepts - an Ising problem defined on a general graph and returns samples - using steepest gradient descent seeded with initial states from - `states_numpy`. - - Parameters - ---------- - num_samples : int - Number of samples to get from the sampler. - - linear_biases : list(float) - The linear biases or field values for the problem. - - coupler_starts : list(int) - A list of the start variable of each coupler. For a problem - with the couplers (0, 1), (1, 2), and (3, 1), `coupler_starts` - should be [0, 1, 3]. - - coupler_ends : list(int) - A list of the end variable of each coupler. For a problem - with the couplers (0, 1), (1, 2), and (3, 1), `coupler_ends` - should be [1, 2, 1]. - - coupler_weights : list(float) - A list of the J values or weight on each coupler, in the same - order as `coupler_starts` and `coupler_ends`. - - states_numpy : np.ndarray[char, ndim=2, mode="c"], values in (-1, 1) - The initial seeded states of the gradient descent runs. Should be of - a contiguous numpy.ndarray of shape (num_samples, num_variables). - - large_sparse_opt : bool - When set to True, large-and-sparse problem graph optimizations are used. - - Returns - ------- - samples : numpy.ndarray - A 2D numpy array where each row is a sample. - - energies: numpy.ndarray - Sample energies. - - num_steps: numpy.ndarray - Number of downhill steps per sample. - """ - num_vars = len(linear_biases) - - # short-circuit null edge cases - if num_samples == 0 or num_vars == 0: - states = np.empty((num_samples, num_vars), dtype=np.int8) - return (states, - np.zeros(num_samples, dtype=np.double), - np.zeros(num_samples, dtype=np.uint32)) - - # allocate ndarray for energies - energies_numpy = np.empty(num_samples, dtype=np.float64) - cdef double[:] energies = energies_numpy - - # allocate ndarray for steps - num_steps_numpy = np.empty(num_samples, dtype=np.uint32) - cdef unsigned[:] num_steps = num_steps_numpy - - # explicitly convert all Python types to C while we have the GIL - cdef char* _states = &states_numpy[0, 0] - cdef double* _energies = &energies[0] - cdef unsigned* _num_steps = &num_steps[0] - cdef int _num_samples = num_samples - cdef bool _large_sparse_opt = large_sparse_opt - cdef vector[double] _linear_biases = linear_biases - - # TODO: in dimod 0.10+, coupler indices default to int64, but we downcast - # them still to int. We can update this when we upgrade to a more efficient - # bqm data interface. Until then, beware of the 2B num_variables limit. - cdef vector[int] _coupler_starts = coupler_starts - cdef vector[int] _coupler_ends = coupler_ends - cdef vector[double] _coupler_weights = coupler_weights - - with nogil: - decl.steepest_gradient_descent( - _states, _energies, _num_steps, _num_samples, - _linear_biases, _coupler_starts, _coupler_ends, _coupler_weights, - _large_sparse_opt) - - return states_numpy, energies_numpy, num_steps_numpy diff --git a/greedy/sampler.py b/greedy/sampler.py index 6a86341..e46d590 100644 --- a/greedy/sampler.py +++ b/greedy/sampler.py @@ -14,268 +14,4 @@ """A dimod sampler that uses the steepest gradient descent.""" -from numbers import Integral - -import dimod -import numpy as np - -from greedy.descent import steepest_gradient_descent - -__all__ = ["SteepestDescentSolver", "SteepestDescentSampler"] - - -class SteepestDescentSolver(dimod.Sampler, dimod.Initialized): - """Steepest descent solver/sampler for binary quadratic models. - - Steepest descent is the discrete analogue of gradient descent, but the best - move is computed using a local minimization rather rather than computing a - gradient. At each step, we determine the dimension along which to descend - based on the highest energy drop caused by a variable flip. - - Solves convex problems to optimality. - - Number of downhill runs (samples produced) is determined by ``num_reads``, - number of ``initial_states``, or a combination of the two, depending on - the ``initial_states_generator``. - - For a given input model's graph :math:`G = (V, E)`, :math:`V` being a set of - graph vertices and :math:`E` a set of edges, runtime complexity of the - underlying C++ implementation is :math:`O(|E|)` for initialization phase - and :math:`O(|V|)` per downhill step. - - In the ``large_sparse_opt`` mode, runtime complexity on sparse graphs is - :math:`O(|V|*log|V|)` for initialization and :math:`O(max\_degree * log|V|)` - per downhill step. - - Aliased as :class:`~greedy.sampler.SteepestDescentSampler`. - - Examples: - Solve a simple Ising problem: - - >>> import greedy - ... - >>> sampler = greedy.SteepestDescentSampler() - >>> samples = sampler.sample_ising({0: 2, 1: 2}, {(0, 1): -1}) - ... - >>> print(samples) # doctest: +SKIP - 0 1 energy num_oc. num_st. - 0 -1 -1 -5.0 1 2 - ['SPIN', 1 rows, 1 samples, 2 variables] - - Post-processes samples generated by another sampler (simulated annealing - in this example): - - .. code-block:: python - - import neal - import dimod - import greedy - import networkx - - bqm = dimod.generators.ran_r(5, networkx.complete_graph('abc')) - - samples = neal.SimulatedAnnealingSampler().sample(bqm) - - postprocessed = greedy.SteepestDescentSampler().sample(bqm, initial_states=samples) - - - For additional examples, see :meth:`.sample`. - """ - - parameters = None - """dict: Keyword arguments accepted by the sampling methods. - - Exactly equal to:: - - { - 'num_reads': [], - 'initial_states': [], - 'initial_states_generator': ['initial_states_generators'], - 'seed': [], - 'large_sparse_opt': ['large_sparse_opt_values'], - } - - """ - - properties = None - """dict: Values for parameters accepted by the sampling methods. - - Exactly equal to:: - - { - 'initial_states_generators': ('none', 'tile', 'random'), - 'large_sparse_opt_values': (True, False), - } - - """ - - def __init__(self): - # create a copy (isolate from subclass) - self.parameters = { - 'num_reads': [], - 'initial_states': [], - 'initial_states_generator': ['initial_states_generators'], - 'seed': [], - 'large_sparse_opt': ['large_sparse_opt_values'], - } - self.properties = { - 'initial_states_generators': ('none', 'tile', 'random'), - 'large_sparse_opt_values': (True, False), - } - - def sample(self, bqm, num_reads=None, initial_states=None, - initial_states_generator="random", seed=None, - large_sparse_opt=False, **kwargs): - """Sample from a binary quadratic model. - - Starts from ``initial_states``, and converges to local minima using - discrete steepest-descent method. - - Args: - bqm (:class:`~dimod.BinaryQuadraticModel`): - The binary quadratic model to be sampled. - - num_reads (int, optional, default=len(initial_states) or 1): - Number of reads. Each read is generated by one run of the steepest - descent algorithm. If `num_reads` is not explicitly given, it is - selected to match the number of initial states given. If initial states - are not provided, only one read is performed. - - initial_states (samples-like, optional, default=None): - One or more samples, each defining an initial state for all the - problem variables. Initial states are given one per read, but - if fewer than `num_reads` initial states are defined, additional - values are generated as specified by `initial_states_generator`. - See :func:`dimod.as_samples` for a description of "samples-like". - - initial_states_generator ({'none', 'tile', 'random'}, optional, default='random'): - Defines the expansion of `initial_states` if fewer than - `num_reads` are specified: - - * "none": - If the number of initial states specified is smaller than - `num_reads`, raises ValueError. - - * "tile": - Reuses the specified initial states if fewer than `num_reads` - or truncates if greater. - - * "random": - Expands the specified initial states with randomly generated - states if fewer than `num_reads` or truncates if greater. - - seed (int (32-bit unsigned integer), optional): - Seed to use for the PRNG. Specifying a particular seed with a - constant set of parameters produces identical results. If not - provided, a random seed is chosen. - - large_sparse_opt (bool, optional, default=False): - Use optimizations for large and sparse problems (search tree for - next descent variable instead of linear array). - - Returns: - :class:`dimod.SampleSet`: A `dimod` :class:`~dimod.SampleSet` object. - - Number of descends (single variable flips) taken to reach the local - minimum for each sample is stored in a data vector called - ``num_steps``. - - Examples: - This example samples a simple two-variable Ising model. - - >>> import dimod - >>> bqm = dimod.BQM.from_ising({}, {'ab': 1}) - ... - >>> import greedy - >>> sampler = greedy.SteepestDescentSampler() - ... - >>> samples = sampler.sample(bqm) - >>> samples.first.energy - -1.0 - - Run steepest descent one million times (takes ~150ms on an average - laptop), converging to local minima, each time starting from a - random state: - - >>> bqm = dimod.BQM.from_ising({}, {'ab': 1}) - >>> sampler = greedy.SteepestDescentSampler() - ... - >>> samples = sampler.sample(bqm, num_reads=10**6) - >>> print(samples.aggregate()) # doctest: +SKIP - a b energy num_oc. num_st. - 0 -1 +1 -1.0 500115 1 - 1 +1 -1 -1.0 499885 1 - ['SPIN', 2 rows, 1000000 samples, 2 variables] - - Use a combination of one fixed initial state and two randomly - generated ones to sample from a simple convex Ising problem with - global minimum at ``(-1,-1)``: - - >>> bqm = dimod.BQM.from_ising({'x': 2, 'y': 2}, {'xy': -1}) - >>> sampler = greedy.SteepestDescentSampler() - ... - >>> samples = sampler.sample(bqm, initial_states=([1, 1], 'xy'), num_reads=3) - >>> print(samples) # doctest: +SKIP - x y energy num_oc. num_st. - 0 -1 -1 -5.0 1 2 - 1 -1 -1 -5.0 1 1 - 2 -1 -1 -5.0 1 2 - ['SPIN', 3 rows, 3 samples, 2 variables] - - Notice it required 2 variable flips (``num_steps`` field in the last - column) to reach the minimum state, ``(-1, -1)``, from the initial - state, ``(1, 1)``. - """ - - # get the original vartype so we can return consistently - original_vartype = bqm.vartype - - # convert to spin - if bqm.vartype is not dimod.SPIN: - bqm = bqm.change_vartype(dimod.SPIN, inplace=False) - - # validate seed - if not (seed is None or isinstance(seed, Integral)): - raise TypeError("'seed' should be None or a positive 32-bit integer") - if isinstance(seed, Integral) and not 0 <= seed <= 2**32 - 1: - raise ValueError("'seed' should be an integer between 0 and 2**32 - 1 inclusive") - - # parse initial_states et al - parsed_initial_states = self.parse_initial_states( - bqm, - num_reads=num_reads, - initial_states=initial_states, - initial_states_generator=initial_states_generator, - seed=seed) - - num_reads = parsed_initial_states.num_reads - initial_states = parsed_initial_states.initial_states - - # get linear/quadratic data - linear, (coupler_starts, coupler_ends, coupler_weights), offset = \ - bqm.to_numpy_vectors(variable_order=initial_states.variables) - - # we need initial states as contiguous numpy array - initial_states_array = \ - np.ascontiguousarray(initial_states.record.sample, dtype=np.int8) - - # run the steepest descent - samples, energies, num_steps = steepest_gradient_descent( - num_reads, - linear, coupler_starts, coupler_ends, coupler_weights, - initial_states_array, large_sparse_opt) - - # resulting sampleset - result = dimod.SampleSet.from_samples( - (samples, initial_states.variables), - energy=energies + offset, - vartype=dimod.SPIN, - num_steps=num_steps, - ) - - result.change_vartype(original_vartype, inplace=True) - - return result - - -SteepestDescentSampler = SteepestDescentSolver +from dwave.samplers.greedy import SteepestDescentSolver, SteepestDescentSampler diff --git a/greedy/src/descent.cpp b/greedy/src/descent.cpp deleted file mode 100644 index 8892857..0000000 --- a/greedy/src/descent.cpp +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright 2019 D-Wave Systems Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include "descent.h" - -using std::vector; -using std::set; -using std::runtime_error; - - -// Returns the energy delta from flipping a SPIN variable at index `var`. -// -// @param var the index of the variable to flip -// @param state the current state of all variables -// @param linear_biases vector of h or field value on each variable -// @param neighbors lists of the neighbors of each variable, such that -// neighbors[i][j] is the jth neighbor of variable i. -// @param neighbour_couplings same as neighbors, but instead has the J value. -// neighbour_couplings[i][j] is the J value or weight on the coupling -// between variables i and neighbors[i][j]. -// -// @return delta energy -double get_flip_energy( - int var, - char *state, - const vector& linear_biases, - const vector>& neighbors, - const vector>& neighbour_couplings -) { - // var -1 flips to +1 => delta is +2 - // var +1 flips to -1 => delta is -2 - double delta = -2 * state[var]; - - // flip energy = delta * h[var] - // + sum_over_var_neighbors(delta * J[var][neigh] * state[neigh])) - double contrib = linear_biases[var]; - for (int idx = 0; idx < neighbors[var].size(); idx++) { - contrib += state[neighbors[var][idx]] * neighbour_couplings[var][idx]; - } - - return delta * contrib; -} - - -// Returns the energy of a given state for the input problem. -// -// @param state a char array containing the spin state to compute the energy of -// @param linear_biases vector of h or field value on each variable -// @param coupler_starts an int vector containing the variables of one side of -// each coupler in the problem -// @param coupler_ends an int vector containing the variables of the other side -// of each coupler in the problem -// @param coupler_weights a double vector containing the weights of the -// couplers in the same order as coupler_starts and coupler_ends -// -// @return A double corresponding to the energy for `state` on the problem -// defined by linear_biases and the couplers passed in -double get_state_energy( - char* state, - const vector& linear_biases, - const vector& coupler_starts, - const vector& coupler_ends, - const vector& coupler_weights -) { - double energy = 0.0; - - // sum the energy due to local fields on variables - for (unsigned int var = 0; var < linear_biases.size(); var++) { - energy += state[var] * linear_biases[var]; - } - - // sum the energy due to coupling weights - for (unsigned int c = 0; c < coupler_starts.size(); c++) { - energy += state[coupler_starts[c]] * coupler_weights[c] * state[coupler_ends[c]]; - } - - return energy; -} - - -// One run of the steepest gradient descent on the input Ising model. -// -// Linear search for the steepest descent variable. Fastest approach for -// complete and/or dense problem graphs. -// -// @param state a signed char array where each char holds the state of a -// variable. Note that this will be used as the initial state of the -// run. -// @param linear_biases vector of h or field value on each variable -// @param neighbors lists of the neighbors of each variable, such that -// neighbors[i][j] is the jth neighbor of variable i. Note -// @param neighbour_couplings same as neighbors, but instead has the J value. -// neighbour_couplings[i][j] is the J value or weight on the coupling -// between variables i and neighbors[i][j]. -// @param flip_energies vector used for caching of variable flip delta -// energies -// -// @return number of downhill steps; `state` contains the result of the run. -unsigned int steepest_gradient_descent_solver( - char* state, - const vector& linear_biases, - const vector>& neighbors, - const vector>& neighbour_couplings, - vector& flip_energies -) { - const int num_vars = linear_biases.size(); - - // short-circuit on empty models - if (num_vars < 1) { - return 0; - } - - // calculate flip energies for all variables, based on the current - // state (loop invariant) ~ O(num_vars * max_degree) - for (int var = 0; var < num_vars; var++) { - flip_energies[var] = get_flip_energy( - var, state, linear_biases, neighbors, neighbour_couplings - ); - } - - // descend ~ O(downhill_steps * num_vars) - unsigned int steps = 0; - while (true) { - - // calculate the gradient: on binary models this translates to finding - // a dimension with the greatest flip energy - int best_var = -1; - double best_flip_energy = 0; - - // find the variable flipping of which results with the steepest - // descent in energy landscape - for (int var = 0; var < num_vars; var++) { - double flip_energy = flip_energies[var]; - - if (flip_energy < best_flip_energy) { - best_flip_energy = flip_energy; - best_var = var; - } - } - - // are we in a minimum already? - if (best_var == -1) { - break; - } - - // otherwise, we can improve the solution by descending down the - // `best_var` dimension - state[best_var] *= -1; - - // but to maintain the `flip_energies` invariant (after flipping - // `best_var`), we need to update flip energies for the flipped var and - // all neighbors of the flipped var - flip_energies[best_var] *= -1; - - for (int n_idx = 0; n_idx < neighbors[best_var].size(); n_idx++) { - int n_var = neighbors[best_var][n_idx]; - double w = neighbour_couplings[best_var][n_idx]; - // flip energy for each `neighbor` includes the - // `2 * state[neighbor] * coupling weight * state[best_var]` term. - // the change of the flip energy due to flipping `best_var` is - // twice that, hence the factor 4 below - flip_energies[n_var] -= 4 * state[best_var] * w * state[n_var]; - } - - steps++; - } - - return steps; -} - - -struct EnergyVar { - double energy; - int var; -}; - -struct EnergyVarCmp { - bool operator()(const EnergyVar& lhs, const EnergyVar& rhs) const { - return lhs.energy < rhs.energy || (lhs.energy <= rhs.energy && lhs.var < rhs.var); - } -}; - - -// One run of the steepest gradient descent on the input Ising model. -// -// Flip energies are kept in an ordered set (balanced binary tree) resulting in -// faster steepest descent variable lookup, but higher constant overhead of -// maintaining the order. Scaling advantage only for very *large* (and *sparse*) -// problem graphs. -// -// @param state a signed char array where each char holds the state of a -// variable. Note that this will be used as the initial state of the -// run. -// @param linear_biases vector of h or field value on each variable -// @param neighbors lists of the neighbors of each variable, such that -// neighbors[i][j] is the jth neighbor of variable i. Note -// @param neighbour_couplings same as neighbors, but instead has the J value. -// neighbour_couplings[i][j] is the J value or weight on the coupling -// between variables i and neighbors[i][j]. -// @param flip_energies_vector vector used for caching of variable flip delta -// energies -// -// @return number of downhill steps; `state` contains the result of the run. -unsigned int steepest_gradient_descent_ls_solver( - char* state, - const vector& linear_biases, - const vector>& neighbors, - const vector>& neighbour_couplings, - vector& flip_energies_vector -) { - const int num_vars = linear_biases.size(); - - // short-circuit on empty models - if (num_vars < 1) { - return 0; - } - - // calculate flip energies for all variables, based on the current - // state (loop invariant); store them in: - // (1) vector for fast var-to-energy look-up - // (2) ordered set (rb-tree) for fast best var energy look-up - // ~ O(N * (max_degree + 1 + logN)) - // => ~ O(N^2) for dense graphs, ~ O(N*logN) for sparse - set flip_energies_set; - - for (int var = 0; var < num_vars; var++) { - double energy = get_flip_energy( - var, state, linear_biases, neighbors, neighbour_couplings - ); - flip_energies_vector[var] = energy; - flip_energies_set.insert({energy, var}); - } - - // descend ~ O(downhill_steps * max_degree * logN) - unsigned int steps = 0; - while (true) { - // find the variable flipping of which results with the steepest - // descent in energy landscape ~ O(1) - auto best_energy_var_iter = flip_energies_set.begin(); - int best_var = best_energy_var_iter->var; - double best_energy = best_energy_var_iter->energy; - - // are we in a minimum already? - if (best_energy >= 0) { - break; - } - - // otherwise, we can improve the solution by descending down the - // `best_var` dimension - - // but to maintain the `flip_energies` invariant (after flipping - // `best_var`), we need to update flip energies for the flipped var and - // all neighbors of the flipped var - - // update flip energies (and their ordered set) of all `best_var`'s - // neighbors ~ O(max_degree * logN) - for (int n_idx = 0; n_idx < neighbors[best_var].size(); n_idx++) { - int n_var = neighbors[best_var][n_idx]; - double w = neighbour_couplings[best_var][n_idx]; - // flip energy for each `neighbor` includes the - // `2 * state[neighbor] * coupling weight * state[best_var]` term. - // the change of the flip energy due to flipping `best_var` is - // twice that, hence the factor 4 below - double n_energy_inc = 4 * state[best_var] * w * state[n_var]; - - // to update the neighbor in our ordered set: - // 1) remove the neighbor from the set - double n_energy = flip_energies_vector[n_var]; - auto search = flip_energies_set.find({n_energy, n_var}); - assert(search != flip_energies_set.end()); - flip_energies_set.erase(search); - - // 2) insert new (energy, var) element to reflect changed flip energy - n_energy += n_energy_inc; - flip_energies_set.insert({n_energy, n_var}); - - // also update energy in the vector - flip_energies_vector[n_var] = n_energy; - } - - // finally, descend down the `var` dim (flip it) - state[best_var] *= -1; - - // and update flip energy for the flipped best_var, in both vector and set - best_energy *= -1; - flip_energies_vector[best_var] = best_energy; - flip_energies_set.erase(best_energy_var_iter); - flip_energies_set.insert({best_energy, best_var}); - - steps++; - } - - return steps; -} - - -// Perform `num_samples` runs of steepest gradient descent on a general problem. -// -// @param states char array of size num_samples * number of variables in the -// problem. Will be overwritten by this function as samples are filled -// in. The initial state of the samples are used to seed the gradient -// descent runs. -// @param energies a double array of size num_samples. Will be overwritten by -// this function as energies are filled in. -// @param steps an unsigned int array of size num_samples. Will be overwritten -// by this function as number of downhill steps per sample are received. -// @param num_samples the number of samples to get -// @param linear_biases vector of linear bias or field value on each variable -// @param coupler_starts an int vector containing the variables of one side of -// each coupler in the problem -// @param coupler_ends an int vector containing the variables of the other side -// of each coupler in the problem -// @param coupler_weights a double vector containing the weights of the couplers -// in the same order as coupler_starts and coupler_ends -// @param large_sparse_opt boolean that determines -// if linear search or balanced tree search should be used for descent. -// -// @return Nothing. Results are in `states` buffer. -void steepest_gradient_descent( - char* states, - double* energies, - unsigned* num_steps, - const int num_samples, - const vector& linear_biases, - const vector& coupler_starts, - const vector& coupler_ends, - const vector& coupler_weights, - bool large_sparse_opt -) { - // the number of variables in the problem - const int num_vars = linear_biases.size(); - if (coupler_starts.size() != coupler_ends.size() || - coupler_starts.size() != coupler_weights.size() - ) { - throw runtime_error("coupler vectors have mismatched lengths"); - } - - // neighbors is a vector of vectors, such that neighbors[i][j] is the jth - // neighbor of variable i - vector> neighbors(num_vars); - // neighbour_couplings is another vector of vectors with the same structure - // except neighbour_couplings[i][j] is the weight on the coupling between i - // and its jth neighbor - vector> neighbour_couplings(num_vars); - - // build the neighbors, and neighbour_couplings vectors by iterating over - // the input coupler vectors - for (unsigned coupler = 0; coupler < coupler_starts.size(); coupler++) { - int u = coupler_starts[coupler]; - int v = coupler_ends[coupler]; - - if (u < 0 || v < 0 || u >= num_vars || v >= num_vars) { - throw runtime_error("coupler indexes contain an invalid variable"); - } - - // add v to u's neighbors list and vice versa - neighbors[u].push_back(v); - neighbors[v].push_back(u); - // add the weights - neighbour_couplings[u].push_back(coupler_weights[coupler]); - neighbour_couplings[v].push_back(coupler_weights[coupler]); - } - - // variable flip energies cache - vector flip_energies_vector(num_vars); - - // run the steepest descent for `num_samples` times, - // each time seeded with the initial state from `states` - for (int sample = 0; sample < num_samples; sample++) { - // get initial state from states buffer; the solution overwrites the same buffer - char *state = states + sample * num_vars; - - if (large_sparse_opt) { - num_steps[sample] = steepest_gradient_descent_ls_solver( - state, linear_biases, neighbors, neighbour_couplings, flip_energies_vector - ); - } else { - num_steps[sample] = steepest_gradient_descent_solver( - state, linear_biases, neighbors, neighbour_couplings, flip_energies_vector - ); - } - - // compute the energy of the sample - energies[sample] = get_state_energy( - state, linear_biases, coupler_starts, coupler_ends, coupler_weights - ); - } -} diff --git a/greedy/src/descent.h b/greedy/src/descent.h deleted file mode 100644 index d1a9fb0..0000000 --- a/greedy/src/descent.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 D-Wave Systems Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _DESCENT_H -#define _DESCENT_H - -#include - -using std::vector; - - -double get_flip_energy( - int var, - char *state, - const vector& linear_biases, - const vector>& neighbors, - const vector>& neighbour_couplings -); - -double get_state_energy( - char* state, - const vector& linear_biases, - const vector& coupler_starts, - const vector& coupler_ends, - const vector& coupler_weights -); - -unsigned steepest_gradient_descent_solver( - char* state, - const vector& linear_biases, - const vector>& neighbors, - const vector>& neighbour_couplings -); - -unsigned steepest_gradient_descent_ls_solver( - char* state, - const vector& linear_biases, - const vector>& neighbors, - const vector>& neighbour_couplings -); - -void steepest_gradient_descent( - char* states, - double* energies, - unsigned* num_steps, - const int num_samples, - const vector& linear_biases, - const vector& coupler_starts, - const vector& coupler_ends, - const vector& coupler_weights, - bool large_sparse_opt=false -); - -#endif diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 5b39a9f..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,23 +0,0 @@ -[build-system] -requires = [ - "setuptools>=46.4.0", # PEP-420 support, PEP-517/518 support, setup.cfg attr: support - "wheel>=0.30.0", # limited python api support - "cython>=0.29.24,<3.0", - 'numpy==1.19.5; python_version < "3.10"', - 'numpy==1.21.4; python_version >= "3.10"', -] -build-backend = "setuptools.build_meta" - -[tool.coverage.run] -source = ["greedy"] -omit = [ - "env/*", - "*/tests/*", -] - -[tool.coverage.report] -exclude_lines = [ - "pragma: no cover", - "raise NotImplementedError", - "if __name__ == .__main__.:", -] diff --git a/requirements.txt b/requirements.txt index 7b14fbb..e902496 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1 @@ -numpy==1.19.5; python_version < "3.10" -numpy==1.21.4; python_version >= "3.10" -dimod==0.10.10 -cython==0.29.26 -importlib-metadata>=1.0; python_version < "3.8" - -# build -wheel>=0.30.0 -setuptools>=56.2.0 +dwave-samplers==1.0.0.dev2 diff --git a/setup.cfg b/setup.cfg index b5552e7..540ea32 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,13 +27,9 @@ classifiers = [options] packages = greedy -zip_safe = false -include_package_data = true python_requires = >=3.7 install_requires = - numpy>=1.17.3,<2.0.0 - dimod>=0.9.2,<0.13.0 - importlib-metadata>=1.0; python_version < "3.8" + dwave-samplers>=1.0.0.dev2 [aliases] dists = clean --all sdist bdist_wheel diff --git a/setup.py b/setup.py index a09027b..4d2a02a 100644 --- a/setup.py +++ b/setup.py @@ -12,46 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from setuptools import setup, Extension -from setuptools.command.build_ext import build_ext -from Cython.Build import cythonize -import numpy +from setuptools import setup - -class build_ext_with_args(build_ext): - """Add compiler-specific compile/link flags.""" - - extra_compile_args = { - 'msvc': ['/std:c++14'], - 'unix': ['-std=c++11'], - } - - extra_link_args = { - 'msvc': [], - 'unix': ['-std=c++11'], - } - - def build_extensions(self): - compiler = self.compiler.compiler_type - - compile_args = self.extra_compile_args[compiler] - for ext in self.extensions: - ext.extra_compile_args = compile_args - - link_args = self.extra_link_args[compiler] - for ext in self.extensions: - ext.extra_compile_args = link_args - - super().build_extensions() - - -extensions = [Extension( - name='greedy.descent', - sources=['greedy/descent.pyx'], - include_dirs=[numpy.get_include()], -)] - -setup( - ext_modules=cythonize(extensions), - cmdclass={'build_ext': build_ext_with_args}, -) +setup() diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index b34cd73..0000000 --- a/tests/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -ROOT := .. -SRC := $(ROOT)/greedy/src - -smoke: test_descent.out - ./test_descent.out - -%.out: %.cpp - g++ -std=c++11 $< $(SRC)/*.cpp -I $(SRC) -o $@ diff --git a/tests/test_descent.cpp b/tests/test_descent.cpp deleted file mode 100644 index 341f169..0000000 --- a/tests/test_descent.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 D-Wave Systems Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// g++ tests/test_descent.cpp greedy/src/descent.cpp -std=c++11 -I greedy/src/ - -#include -#include -#include -#include "../greedy/src/descent.h" - -#define len(arr) (sizeof(arr) / sizeof((arr)[0])) - -using namespace std; - -int main() { - int num_samples = 1; - - char states[3] = {1, 1, 1}, min_states[3] = {-1, 1, 1}; - double energies[1] = {0}, min_energies[1] = {-1}; - unsigned num_steps[1] = {0}; - - // bqm ~ {(0, 1): 1, (1, 2): 1, (2, 0): 1}) - vector linear_biases {0, 0, 0}; - vector coupler_starts {0, 1, 2}; - vector coupler_ends {1, 2, 0}; - vector coupler_weights {1.0, 1.0, 1.0}; - - steepest_gradient_descent( - states, energies, num_steps, num_samples, - linear_biases, coupler_starts, coupler_ends, coupler_weights - ); - - // debug output - cerr << "Local minimum state: " - << (int)states[0] << ", " << (int)states[1] << ", " << (int)states[2] - << "; Energy: " << energies[0] << endl; - - cerr << "Expected state: " - << (int)min_states[0] << ", " << (int)min_states[1] << ", " << (int)min_states[2] - << "; Energy: " << min_energies[0] << endl; - - // assert correct solution - for (auto i = 0; i < len(states); i++) { - assert(states[i] == min_states[i]); - } - for (auto i = 0; i < len(energies); i++) { - assert(energies[i] == min_energies[i]); - } - - cerr << "OK" << endl; - - return 0; -} \ No newline at end of file