37
$\begingroup$

I would like to know how a job for a D-Wave device is written in code and submitted to the device.

In the answer it would be best to see a specific example of this for a simple problem. I guess that the "Hello World" of a D-Wave device would be something like finding the ground states of a simple 2D Ising model, since this is the kind of problem directly realized by the hardware. So perhaps this would be a nice example to see. But if those with expertise thing an alternative example would be suitable, I'd be happy to see an alternative.

$\endgroup$
0

3 Answers 3

28
$\begingroup$

The 'Hello World' equivalent in the D-Wave world is the 2D checkerboard example. In this example, you are given the following square graph with 4 nodes:

                                                  square_graph

Let's define that we colour vertex $\sigma_{i}$ black if $\sigma_{i} = -1$ and white if $\sigma_{i} = +1$. The goal is to create a checkerboard pattern with the four vertices in the graph. There is various ways of defining $h$ and $J$ to achieve this result. First of all, there are two possible solutions to this problem:

               checkerboard_solutions

The D-Wave quantum annealer minimizes the Ising Hamiltonian that we define and it is important to understand the effect of the different coupler settings. Consider for example the $J_{0,1}$ coupler:

If we set it to $J_{0,1}=-1$, the Hamiltonian is minimized if both qubits take the same value. We say negative couplers correlate. Whereas if we set it to $J_{0,1}=+1$, the Hamiltonian is minimized if the two qubits take opposite values. Thus, positive couplers anti-correlate.

In the checkerboard example, we want to anti-correlate each pair of neighbouring qubits which gives rise to the following Hamiltonian:

$$H = \sigma_{0}\sigma_{1} + \sigma_{0}\sigma_{2} + \sigma_{1}\sigma_{3} + \sigma_{2}\sigma_{3}$$

For the sake of demonstration, we also add a bias term on the $0$-th qubit such that we only get solution #1. This solution requires $\sigma_{0}=-1$ and we therefore set its bias $h_{0}=1$. The final Hamiltonian is now:

$$H = \sigma_{0} + \sigma_{0}\sigma_{1} + \sigma_{0}\sigma_{2} + \sigma_{1}\sigma_{3} + \sigma_{2}\sigma_{3}$$

So let's code it up!

NOTE: You DO NEED access to D-Wave's Cloud Service for anything to work.

First of all, make sure you have the dwave_sapi2 (https://cloud.dwavesys.com/qubist/downloads/) Python package installed. Everything is going to be Python 2.7 since D-Wave currently doesn't support any higher Python version. That being said, let's import the essentials:

from dwave_sapi2.core import solve_ising
from dwave_sapi2.embedding import find_embedding, embed_problem, unembed_answer
from dwave_sapi2.util import get_hardware_adjacency
from dwave_sapi2.remote import RemoteConnection

In order to connect to the D-Wave Solver API you will need a valid API token for their SAPI solver, the SAPI URL and you need to decide which quantum processor you want to use:

DWAVE_SAPI_URL = 'https://cloud.dwavesys.com/sapi'
DWAVE_TOKEN = [your D-Wave API token]
DWAVE_SOLVER = 'DW_2000Q_VFYC_1'

I recommend using the D-Wave 2000Q Virtual Full Yield Chimera (VFYC) which is a fully functional chip without any dead qubits! Here's the Chimera chip layout:

dwave_chimera

At this point I am splitting the tutorial into two distinct pieces. In the first section, we are manually embedding the problem onto the Chimera hardware graph and in the second section we are using D-Wave's embedding heuristics to find a hardware embedding.

Manual embedding


The unit cell in the top left corner on the D-Wave 2000Q chip layout above looks like this:

physical_qubits

Note, that not all couplers are visualized in this image. As you can see, there is no coupler between qubit $0$ and qubit $1$ which we would need to directly implement our square graph above. That's why we are now redefining $0\rightarrow0$, $1\rightarrow4$, $2\rightarrow7$ and $3\rightarrow3$. We then go on and define $h$ as a list and $J$ as a dictionary:

J = {(0,4): 1, (4,3): 1, (3,7): 1, (7,0): 1}
h = [-1,0,0,0,0,0,0,0,0]

$h$ has 8 entries since we use qubits 0 to 7. We now establish connection to the Solver API and request the D-Wave 2000Q VFYC solver:

connection = RemoteConnection(DWAVE_SAPI_URL, DWAVE_TOKEN)
solver = connection.get_solver(DWAVE_SOLVER)

Now, we can define the number of readouts and choose answer_mode to be "histogram" which already sorts the results by the number of occurrences for us. We are now ready to solve the Ising instance with the D-Wave quantum annealer:

params = {"answer_mode": 'histogram', "num_reads": 10000}
results = solve_ising(solver, h, J, **params)
print results

You should get the following result:

{
  'timing': {
    'total_real_time': 1655206,
    'anneal_time_per_run': 20,
    'post_processing_overhead_time': 13588,
    'qpu_sampling_time': 1640000,
    'readout_time_per_run': 123,
    'qpu_delay_time_per_sample': 21,
    'qpu_anneal_time_per_sample': 20,
    'total_post_processing_time': 97081,
    'qpu_programming_time': 8748,
    'run_time_chip': 1640000,
    'qpu_access_time': 1655206,
    'qpu_readout_time_per_sample': 123
  },
  'energies': [-5.0],
  'num_occurrences': [10000],
  'solutions': [
      [1, 3, 3, 1, -1, 3, 3, -1, {
          lots of 3 's that I am omitting}]]}

As you can see we got the correct ground state energy (energies) of $-5.0$. The solution string is full of $3$'s which is the default outcome for unused/unmeasured qubits and if we apply the reverse transformations - $0\rightarrow0$, $4\rightarrow1$, $7\rightarrow2$ and $3\rightarrow3$ - we get the correct solution string $[1, -1, -1, 1]$. Done!

Heuristic embedding


If you start creating larger and larger Ising instances you will not be able to perform manual embedding. So let's suppose we can't manually embed our 2D checkerboard example. $J$ and $h$ then remain unchanged from our initial definitions:

J = {(0,1): 1, (0,2): 1, (1,3): 1, (2,3): 1}
h = [-1,0,0,0]

We again establish the remote connection and get the D-Wave 2000Q VFYC solver instance:

connection = RemoteConnection(DWAVE_SAPI_URL, DWAVE_TOKEN)
solver = connection.get_solver(DWAVE_SOLVER)

In order to find an embedding of our problem, we need to first get the adjacency matrix of the current hardware graph:

adjacency = get_hardware_adjacency(solver)

Now let's try to find an embedding of our problem:

embedding = find_embedding(J.keys(), adjacency)

If you are dealing with large Ising instances you might want to search for embeddings in multiple threads (parallelized over multiple CPUs) and then select the embedding with the smallest chain length! A chain is when multiple qubits are forced to act as a single qubit in order to increase the degree of connectivity. However, the longer the chain the more likely that it breaks. And broken chains give bad results!

We are now ready to embed our problem onto the graph:

[h, j0, jc, embeddings] = embed_problem(h, J, embedding, adjacency)

j0 contains the original couplings that we defined and jc contains the couplings that enforce the integrity of the chains (they correlate the qubits within the chains). Thus, we need to combine them again into one big $J$ dictionary:

J = j0.copy()
J.update(jc)

Now, we're ready to solve the embedded problem:

params = {"answer_mode": 'histogram', "num_reads": 10000}
raw_results = solve_ising(solver, h, J, **params)

print 'Lowest energy found: {}'.format(raw_results['energies'])
print 'Number of occurences: {}'.format(raw_results['num_occurrences'])

The raw_results will not make sense to us unless we unembed the problem. In case, some chains broke we are fixing them through a majority vote as defined by the optional argument broken_chains:

unembedded_results = unembed_answer(raw_results['solutions'],
                                    embedding, broken_chains='vote')

print 'Solution string: {}'.format(unembedded_results)

If you run this, you should get the correct result in all readouts:

Lowest energy found: [-5.0]
Number of occurences: [10000]
Solution string: [[1, -1, -1, 1]]

I hope this answered your question and I highly recommend checking out all the additional parameters that you can pass to the solve_ising function to improve the quality of your solutions such as num_spin_reversal_transforms or postprocess.

$\endgroup$
9
$\begingroup$

The title and question body seem to ask two different questions. In the title you ask "How do you write a simple program for a D-Wave device?", while in the question body you ask how to find the ground states of a simple 2D Ising model using the underlying hardware of the D-Wave device, and what the corresponding code would be (which is a more specific question).

I will answer the former, since it is the more general question.

According to the D-Wave Software page:

The D-Wave 2000Q system provides a standard Internet API (based on RESTful services), with client libraries available for C/C++, Python, and MATLAB. This interface allows users to access the system either as a cloud resource over a network, or integrated into their high-performance computing (HPC) environments and data centers. Access is also available through D-Wave’s hosted cloud service. Using D-Wave’s development tools and client libraries, developers can create algorithms and applications within their existing environments using industry-standard tools.

While users can submit problems to the system in a number of different ways, ultimately a problem represents a set of values that correspond to the weights of the qubits and the strength of the couplers. The system takes these values along with other user-specified parameters and sends a single quantum machine instruction (QMI) to the QPU. Problem solutions correspond to the optimal configuration of qubits found; that is, the lowest points in the energy landscape. These values are returned to the user program over the network.

Because quantum computers are probabilistic rather than deterministic, multiple values can be returned, providing not only the best solution found, but also other very good alternatives from which to choose. Users can specify the number of solutions they want the system to return.

Users can submit problems to the D-Wave quantum computer in several ways:

  1. Using a program in C, C++, Python, or MATLAB to create and execute QMIs
  2. Using a D-Wave tool such as:

    • QSage, a translator designed for optimization problems

    • ToQ, a high level language translator used for constraint satisfaction problems and designed to let users “speak” in the language of their problem domain

    • qbsolv, an open-source, hybrid partitioning optimization solver for problems that are larger than will fit natively on the QPU. Qbsolv can
      be downloaded here.

    • dw, which executes QMIs created via a text editor

  3. By directly programming the system via QMIs

Download this white paper to learn more about the programming model for a D-Wave system

$\endgroup$
6
$\begingroup$

The inputs to the D-Wave are a list of interactions and more recently the annealing time of the qubits.

As you mentioned the Ising problem is one of the easiest with $J_{ij} = 1$ in the problem Hamiltonian however it's not very interesting.

I recommend the appendices in this paper for a concise description of how the D-Wave hardware operates. (Full disclosure: I'm a coauthor.)

$\endgroup$
1
  • 2
    $\begingroup$ It might be even better if you include a larger part of the answer here, rather than in the link? Being a coauthor in the paper, you are probably best suited to make a good summary. $\endgroup$ Commented May 1, 2018 at 5:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.