Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disambiguate some xrefs #294

Merged
merged 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions docs/intro/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,33 @@
Introduction
============

**dwave-hybrid** provides a framework for iterating arbitrary-sized sets of samples
through parallel solvers to find an optimal solution.
**dwave-hybrid** provides a framework for iterating arbitrary-sized sets of
samples through parallel solvers to find an optimal solution.

For the documentation of a particular code element, see the :ref:`reference_hybrid` section.
This introduction gives an overview of the package; steps you through using it,
starting with running a provided hybrid solver that handles arbitrary-sized QUBOs; and
points out the way to developing your own components in the framework.
For the documentation of a particular code element, see the
:ref:`reference_hybrid` section. This introduction gives an overview of the
package; steps you through using it, starting with running a provided hybrid
solver that handles arbitrary-sized QUBOs; and points out the way to developing
your own components in the framework.

* :ref:`overview_hybrid` presents the framework and explains key concepts.
* :ref:`overview_hybrid` presents the framework and explains key concepts.

* :ref:`using_framework` shows how to use the framework. You can quickly get started by using a
provided reference sampler built with this framework, Kerberos, to solve a problem too
large to :term:`minor-embed` on a D-Wave system. Next, use the framework
to build (hybrid) workflows; for example, a solver similar to :std:doc:`qbsolv <oceandocs:docs_qbsolv>`,
which can employ tabu search on a whole problem while submitting parts of the problem
to a D-Wave system.
* :ref:`using_framework` shows how to use the framework. You can quickly get
started by using a provided reference sampler built with this framework,
:class:`Kerberos <oceandocs:hybrid.reference.kerberos.KerberosSampler>`, to
solve a problem too large to :term:`minor-embed` on a D-Wave system. Next,
use the framework to build (hybrid) workflows; for example, a workflow for
larger-than-QPU lattice-structured problems.

* :ref:`developing_hybrid` guides you to developing your own hybrid components.
* :ref:`developing_hybrid` guides you to developing your own hybrid components.

* :ref:`reference_examples_hybrid` describes some workflow examples included in the code.
* :ref:`reference_examples_hybrid` describes some workflow examples included in the code.

.. toctree::
:maxdepth: 2
:hidden:
:maxdepth: 2
:hidden:

overview
using
developing
reference_examples
overview
using
developing
reference_examples
Comment on lines +25 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frivolous indentation changes are best coupled with bigger semantic changes to not pollute/add noise to git history. Alternatively, one can separate formatting changes in a separate commit.

Copy link
Contributor Author

@JoelPasvolsky JoelPasvolsky Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I regret not fixing the indentation in a separate commit. I deny the charge of frivolity.

197 changes: 105 additions & 92 deletions docs/intro/using.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
Using the Framework
===================

This section helps you quickly use a provided reference sampler to solve arbitrary-sized
problems and then shows you how to build (hybrid) workflows using provided components.
This section helps you quickly use a provided reference sampler to solve
arbitrary-sized problems and then shows you how to build (hybrid) workflows
using provided components.

Reference Hybrid Sampler: Kerberos
==================================

*dwave-hybrid* includes a reference example sampler built using the framework:
Kerberos is a dimod-compatible hybrid asynchronous decomposition sampler that enables
you to solve problems of arbitrary structure and size. It finds best samples
by running in parallel tabu search, simulated annealing, and D-Wave subproblem sampling
on problem variables that have high-energy impact.
Kerberos is a dimod-compatible hybrid asynchronous decomposition sampler that
enables you to solve problems of arbitrary structure and size. It finds best
samples by running in parallel tabu search, simulated annealing, and D-Wave
subproblem sampling on problem variables that have high-energy impact.

The example below uses Kerberos to solve a large QUBO.

Expand All @@ -31,24 +32,25 @@ The example below uses Kerberos to solve a large QUBO.
Building Workflows
==================

As shown in the :ref:`overview_hybrid` section, you build hybrid solvers by arranging components such
as samplers in a workflow.
As shown in the :ref:`overview_hybrid` section, you build hybrid solvers by
arranging components such as samplers in a workflow.

Building Blocks
---------------

The basic components---building blocks---you use are based on the :class:`.Runnable`
class: decomposers, samplers, and composers. Such components input a set of samples,
a :class:`.SampleSet`, and output updated samples. A :class:`State` associated
with such an iteration of a component holds the problem, samples, and optionally
additional information.
The basic components---building blocks---you use are based on the
:class:`.Runnable` class: decomposers, samplers, and composers. Such components
input a set of samples, a :class:`~hybrid.core.SampleSet`, and output updated
samples. A :class:`State` associated with such an iteration of a component holds
the problem, samples, and optionally additional information.

The following example demonstrates a simple workflow that uses just one :class:`.Runnable`,
a sampler representing the classical tabu search algorithm, to solve a problem
(fully classically, without decomposition). The example solves a small problem of a
triangle graph of nodes identically coupled. An initial :class:`.State` of all-zero
samples is set as a starting point. The solution, `new_state`, is derived from a single
iteration of the `TabuProblemSampler` :class:`.Runnable`.
The following example demonstrates a simple workflow that uses just one
:class:`.Runnable`, a sampler representing the classical tabu search algorithm,
to solve a problem (fully classically, without decomposition). The example
solves a small problem of a triangle graph of nodes identically coupled. An
initial :class:`.State` of all-zero samples is set as a starting point. The
solution, `new_state`, is derived from a single iteration of the
`TabuProblemSampler` :class:`.Runnable`.

>>> import dimod
>>> # Define a problem
Expand All @@ -66,15 +68,17 @@ iteration of the `TabuProblemSampler` :class:`.Runnable`.
Flow Structuring
----------------

The framework provides classes for structuring workflows that use the "building-block"
components. As shown in the :ref:`overview_hybrid` section, you can create a *branch* of :class:`Runnable`
classes; for example :code:`decomposer | sampler | composer`, which delegates part
of a problem to a sampler such as the D-Wave system.
The framework provides classes for structuring workflows that use the
"building-block" components. As shown in the :ref:`overview_hybrid` section,
you can create a *branch* of :class:`Runnable` classes; for example
:code:`decomposer | sampler | composer`, which delegates part of a problem to a
sampler such as a D-Wave quantum computer.

The following example shows a branch comprising a decomposer, local Tabu solver, and a composer.
A 10-variable binary quadratic model is decomposed by the energy impact of its variables
into a 6-variable subproblem to be sampled twice. An initial state of all -1 values
is set using the utility function :meth:`~hybrid.utils.min_sample`.
The following example shows a branch comprising a decomposer, local Tabu
solver, and a composer. A 10-variable binary quadratic model is decomposed by
the energy impact of its variables into a 6-variable subproblem to be sampled
twice. An initial state of all -1 values is set using the utility function
:meth:`~hybrid.utils.min_sample`.

>>> import dimod # Create a binary quadratic model
>>> bqm = dimod.BinaryQuadraticModel({t: 0 for t in range(10)},
Expand All @@ -90,40 +94,42 @@ is set using the utility function :meth:`~hybrid.utils.min_sample`.
1 +1 -1 -1 +1 -1 +1 -5.0 1
['SPIN', 2 rows, 2 samples, 6 variables]

Such :class:`.Branch` classes can be run in parallel using the :class:`.RacingBranches` class.
From the outputs of these parallel branches, :class:`.ArgMin` selects a new current sample.
And instead of a single iteration on the sample set, you can use the :class:`.Loop`
to iterate a set number of times or until a convergence criteria is met.
Such :class:`.Branch` classes can be run in parallel using the
:class:`.RacingBranches` class. From the outputs of these parallel branches,
:class:`.ArgMin` selects a new current sample. And instead of a single iteration
on the sample set, you can use the :class:`.Loop` to iterate a set number of
times or until a convergence criteria is met.

This example of :ref:`racingBranches1` solves a binary quadratic model by iteratively producing best samples.
Similar to :std:doc:`qbsolv <oceandocs:docs_qbsolv>`, it employs both tabu search on the entire
problem and a D-Wave system on subproblems. In addition to building-block components
such as employed above, this example also uses infrastructure classes to manage the
decomposition and parallel running of branches.
This example of :ref:`racingBranches1` solves a binary quadratic model by
iteratively producing best samples. It employs both tabu search on the entire
problem and a D-Wave quantum computer on subproblems. In addition to
building-block components such as employed above, this example also uses
infrastructure classes to manage the decomposition and parallel running of
branches.

.. figure:: ../_images/racing_branches_1.png
:name: racingBranches1
:scale: 70 %
:alt: Racing Branches
:name: racingBranches1
:scale: 70 %
:alt: Racing Branches

Racing Branches
Racing Branches

.. include:: ../README.rst
:start-after: example-start-marker
:end-before: example-end-marker
:start-after: example-start-marker
:end-before: example-end-marker

Flow Refining
-------------

The framework enables quick modification of work flows to improve solutions and performance.
For example, after verifying the :ref:`racingBranches1` workflow above on its small problem,
you might make a series of modifications such as the examples below to better fit it to problems
with large numbers of variables.
The framework enables quick modification of work flows to improve solutions and
performance. For example, after verifying the :ref:`racingBranches1` workflow
above on its small problem, you might make a series of modifications such as the
examples below to better fit it to problems with large numbers of variables.

1. Configure a decomposition window that moves down a fraction of problem variables,
ordered from highest to lower energy impact, and submit those subproblems to the D-Wave system
while tabu searches globally. This example submits 50-variable subproblems on
up to 15% of the total variables.
1. Configure a decomposition window that moves down a fraction of problem
variables, ordered from highest to lower energy impact, and submit those
subproblems to a D-Wave quantum computer while tabu searches globally. This
example submits 50-variable subproblems on up to 15% of the total variables.

.. code-block:: python

Expand All @@ -138,14 +144,15 @@ with large numbers of variables.

workflow = hybrid.LoopUntilNoImprovement(iteration, convergence=3)

2. Instead of sequentially producing a sample per subproblem, a further modification might be to
process all the subproblems in parallel and merge the returned samples. Here the
:class:`~hybrid.decomposers.EnergyImpactDecomposer` is iterated until it raises a
:meth:`~hybrid.exceptions.EndOfStream` exception when it reaches 15% of the variables,
and then all the 50-variable subproblems are submitted to the D-Wave system in parallel.
Subsamples returned by the QPU are disjoint in variables, so we can easily reduce them
all to a single subsample, which is then merged with the input sample using
:class:`~hybrid.composers.SplatComposer`:
2. Instead of sequentially producing a sample per subproblem, a further
modification might be to process all the subproblems in parallel and merge
the returned samples. Here the :class:`~hybrid.decomposers.EnergyImpactDecomposer`
is iterated until it raises a :meth:`~hybrid.exceptions.EndOfStream` exception
when it reaches 15% of the variables, and then all the 50-variable subproblems
are submitted to the D-Wave quantum computer in parallel. Subsamples returned
by the QPU are disjoint in variables, so we can easily reduce them all to
a single subsample, which is then merged with the input sample using
:class:`~hybrid.composers.SplatComposer`:

.. code-block:: python

Expand All @@ -166,54 +173,57 @@ with large numbers of variables.
hybrid.Lambda(merge_substates)
) | hybrid.SplatComposer()

3. Change the criterion for selecting subproblems. By default, the variables are selected by maximal
energy impact but selection can be better tailored to a problem's structure.
3. Change the criterion for selecting subproblems. By default, the variables
are selected by maximal energy impact but selection can be better tailored
to a problem's structure.

For example, for binary quadratic model representing the problem graph shown in the
:ref:`eidEnergy` graphic, if you select a subproblem size of four, these nodes selected by
descending energy impact are not directly connected (no shared edges, and might not represent
a local structure of the problem).
For example, for binary quadratic model representing the problem graph shown
in the :ref:`eidEnergy` graphic, if you select a subproblem size of four,
these nodes selected by descending energy impact are not directly connected
(no shared edges, and might not represent a local structure of the problem).

.. figure:: ../_images/eid_energy.png
:name: eidEnergy
:scale: 70 %
:alt: EID energy
:name: eidEnergy
:scale: 70 %
:alt: EID energy

Traversal by Energy Impact
Traversal by Energy Impact

Configuring a mode of traversal such as breadth-first (BFS) or priority-first selection (PFS)
can capture features that represent local structures within a problem.
Configuring a mode of traversal such as breadth-first (BFS) or priority-first selection (PFS)
can capture features that represent local structures within a problem.

.. code-block:: python

# Redefine the workflow: subproblem selection
subproblem = hybrid.Unwind(
hybrid.EnergyImpactDecomposer(size=50, rolling_history=0.15, traversal='bfs'))

These two selection modes are shown in the :ref:`eidBfsPfs` graphic. BFS starts with
the node with maximal energy impact, from which its graph traversal proceeds to directly connected
nodes, then nodes directly connected to those, and so on, with graph traversal
ordered by node index. In PFS, graph traversal selects the node with highest
energy impact among unselected nodes directly connected to any already selected node.
These two selection modes are shown in the :ref:`eidBfsPfs` graphic. BFS
starts with the node with maximal energy impact, from which its graph
traversal proceeds to directly connected nodes, then nodes directly connected
to those, and so on, with graph traversal ordered by node index. In PFS,
graph traversal selects the node with highest energy impact among unselected
nodes directly connected to any already selected node.

.. figure:: ../_images/eid_bfs_pfs.png
:name: eidBfsPfs
:scale: 70 %
:alt: EID BFS
:name: eidBfsPfs
:scale: 70 %
:alt: EID BFS

Traversal by BFS or PFS
Traversal by BFS or PFS

Additional Examples
===================

Tailoring State Selection
-------------------------

The next example tailors a state selector for a sampler that does some post-processing
and can alert upon suspect samples. Sampler output modified by ellipses ("...") for readability
is shown below for an Ising model of a triangle problem with zero biases
and interactions all equal to 0.5. The first of three :class:`~hybrid.core.State` classes is
flagged as problematic using the `info` field::
The next example tailors a state selector for a sampler that does some
post-processing and can alert upon suspect samples. Sampler output modified by
ellipses ("...") for readability is shown below for an Ising model of a triangle
problem with zero biases and interactions all equal to 0.5. The first of three
:class:`~hybrid.core.State` classes is flagged as problematic using the ``info``
field::

[{...,'samples': SampleSet(rec.array([([0, 1, 0], 0., 1)], ..., ['a', 'b', 'c'], {'Postprocessor': 'Excessive chain breaks'}, 'SPIN')},
{...,'samples': SampleSet(rec.array([([1, 1, 1], 1.5, 1)], ..., ['a', 'b', 'c'], {}, 'SPIN')},
Expand All @@ -227,8 +237,9 @@ This code snippet defines a metric for the key argument in :class:`~hybrid.flow.
else:
return(si.samples.first.energy)

Using the defined key on the above input, :class:`~hybrid.flow.ArgMin` finds the state
with the lowest energy (zero) excluding the flagged state (which also has energy of zero):
Using the defined key on the above input, :class:`~hybrid.flow.ArgMin` finds the
state with the lowest energy (zero) excluding the flagged state (which also has
energy of zero):

>>> ArgMin(key=preempt).next(states) # doctest: +SKIP
{'problem': BinaryQuadraticModel({'a': 0.0, 'b': 0.0, 'c': 0.0}, {('a', 'b'): 0.5, ('b', 'c'): 0.5, ('c', 'a'): 0.5},
Expand All @@ -238,7 +249,8 @@ dtype=[('sample', 'i1', (3,)), ('energy', '<f8'), ('num_occurrences', '<i4')]),
Parallel Sampling
-----------------

The code snippet below uses :class:`~hybrid.flow.Map` to run a tabu search on two states in parallel.
The code snippet below uses :class:`~hybrid.flow.Map` to run a tabu search on
two states in parallel.

>>> Map(TabuProblemSampler()).run(States( # doctest: +SKIP
State.from_sample({'a': 0, 'b': 0, 'c': 1}, bqm1),
Expand All @@ -259,12 +271,13 @@ Logging and Execution Information

You can see detailed execution information by setting the level of logging.

The package supports logging levels TRACE, DEBUG, INFO, WARNING, ERROR, and CRITICAL
in ascending order of severity. By default, logging level is set to ERROR. You can
select the logging level with environment variable ``DWAVE_HYBRID_LOG_LEVEL``.
The package supports logging levels TRACE, DEBUG, INFO, WARNING, ERROR, and
CRITICAL in ascending order of severity. By default, logging level is set to
ERROR. You can select the logging level with environment variable
``DWAVE_HYBRID_LOG_LEVEL``.

For example, on a Windows operating system, set this environment variable to INFO level
as:
For example, on a Windows operating system, set this environment variable to
INFO level as:

.. code-block:: bash

Expand Down
14 changes: 7 additions & 7 deletions hybrid/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ def __setattr__(self, name, value):


class SampleSet(dimod.SampleSet):
"""The `dimod.SampleSet` extended with a few helper methods.
"""The :class:`dimod.SampleSet` class extended with a few helper methods.

Note: this is basically a staging area for new `dimod.SampleSet` features
before we merge them upstream.
Note: this is basically a staging area for new :class:`dimod.SampleSet`
features before merging these upstream.
"""

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -212,15 +212,15 @@ def from_samples(cls, samples, bqm, **kwargs):

@classmethod
def from_subsample(cls, subsample, bqm, **kwargs):
"""Similar to :meth:`.from_sample`, but initializes `subproblem` and
`subsamples`.
"""Similar to :meth:`~hybrid.core.State.from_sample`, but initializes
``subproblem`` and ``subsamples``.
"""
return cls.from_subsamples(subsample, bqm, **kwargs)

@classmethod
def from_subsamples(cls, subsamples, bqm, **kwargs):
"""Similar to :meth:`.from_samples`, but initializes `subproblem` and
`subsamples`.
"""Similar to :meth:`~hybrid.core.State.from_samples`, but initializes
``subproblem`` and ``subsamples``.
"""
return cls(subproblem=bqm,
subsamples=SampleSet.from_samples_bqm(subsamples, bqm), **kwargs)
Expand Down