Installing the D-Wave developer pack and an introductory
programming example
Contents
Aim, audience, and required background
This material was developed to help software developers interested in learning how to program D-Wave quantum computing systems to get started. This tutorial should be accessible to all audiences. The high level programming language used in the included examples and source code is Python 2.6. No previous exposure to programming D-Wave quantum computing systems is necessary. Some experience of using the Python programming language is helpful but not required.
Note that this is a quick start tutorial - it is designed to get you programming as quickly as possible. But if you wish to learn more about the system that you are programming, please read the background reading materials, which can be found here:
Software requirements
In order to proceed you should install several free software packages.
What you will learn
By following through the material in this white paper, you will learn:
SECTION 1
1.1 - Installing the developer pack
To begin programming the quantum computer, the first step is to download the developer pack from the DOWNLOAD DEVKITS page. Make sure you download the correct pack for your operating system and version of Python. Once you have the pack downloaded, install it as you would any normal Python package, by moving the executable file to a folder of your choice and double clicking the icon to run the executable. You should see a screen like this:
Installing the Python pack on Windows.
which will guide you through the installation process.
Functions specific
to the quantum computer should now be available through your development environment by importing this module as part of your Python program
using the import dwave_sapi command.
1.2 - Crafting a function
The next thing to do is think up a problem to solve! Here we'll use a simple example of a problem known as the subset sum problem. This is fairly easy problem to describe, but becomes a very hard problem for computers to solve as the size of the problem increases. The basic idea of subset sum is that you have a bunch of numbers, like so:
Figure 1. A set of numbers for our subset sum example
(This is the instance provided in the Wikipedia page on SSP). You must find a subset (group) of these numbers that sum to zero. In this, it is obvious to spot a subset \( \{ 3, -2, 5\} \) which sums to zero, so we can check that our code does indeed find the right answer.
Because the quantum computer deals in binary variables, 0 or 1, we must find some way of representing the question: 'Does any subset of numbers sum to zero?' as a series of 0 or 1 variables. As an applications developer, you will quickly discover that there are often many different ways you can try to do this, but here is one: Define five variables \( (x_1,x_2,x_3,x_4,x_5 ) \) where if a variable is equal to 1 it means "please include me in the sum". So we assign a variable to each of the numbers in our group, like this:
Figure 1. Assigning binary variables to each number in the set
And then we just add up the number multiplied by its accompanying variable. If it gets multiplied by a zero, it isn't included in the sum, and if it gets multiplied by a 1, it IS included in the sum. So we have to write a function that captures this idea. In this case, a valid function for the above example could be:
\[G(x_1,x_2,x_3,x_4,x_5 )=(-7x_1-3x_2-2x_3+5x_4+8x_5)^2 \]where the values of the coefficients are just the numbers in the original set. We take the square of the sum to make sure that it is positive (the quantum computer tries to MINIMIZE functions, so if we are looking for zero, we don't want there to be negative numbers occuring).
You may have spotted a problem with this. The empty subset (if all x variables set to zero) also sums to zero! So we'll add another term to penalizes the solution with all variables equal to zero but not any of the others.
\[G(x_1,x_2,x_3,x_4,x_5 )=(-7x_1-3x_2-2x_3+5x_4+8x_5)^2+\prod_{(k=1)}^5(1-x_k) \]Let's code up this function that we have just crafted and send it to the quantum compiler.
1.3 - Your first program
First, start up your Python environment (you can use IDLE which comes with your Python install if you do not have a dedicated Python environment or IDE). The first thing we do is import some things:
Code Snippet: Importing libraries
from dwave_sapi import local_connection, BlackBoxSolver
from numpy import dot, array, prod
importing the dwave_sapi library allows us to send the problem to the quantum computer.
Numpy is to help us craft code to specify the function that we derived in the previous section.
Next, we define a function object. This is the place where we will put our generating function, G(x), into the code.
There are two parts to the function object. The first part allows a user to pass problem-specific
variables into the function object. This is the section headed by the def __init__ call. This is
where you pass in any variables you'll need to compute the value of the generating function.
So here we will pass in those co-efficients (the numbers in our set), as an array.
The second part is the section headed by the def __call__ call. This is where you actually calculate
the value of the generating function. The way BlackBox works is that it tries out various guesses for the settings of
the variables, and stores the guesses in a list, called states.
numStates is an integer that represents how many guesses being passed
in at that time. So one must write our G function inside a for state_number in range(numstates) loop which loops over all the guesses.
For example, if your
problem had 3 variables, and numStates was 4, then states could be a list consisting of
four possible solutions concatenated together, for example:
states = [-1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1]
(this represents the four guesses [-1, 1, 1], [1, -1, -1], [-1, -1, -1] and [1, 1, 1] all at once for evaluation).
As a developer you won't change the
syntax of the def __call__(self, states, numStates): line - just leave it like this!
OK here goes. First we create the ObjClass class.
Code Snippet: Creating the function as a class
class ObjClass(object):
#
# We send in any parameters we want to. In this case this is the values of our particular subset sum
# instance. (The set of numbers that we are trying to find a subset of)
#
def __init__(self, subset_sum_array):
self.subset_sum_array = subset_sum_array
#
# Now we define and compute the value of the generating function.
#
def __call__(self, states, numStates):
#
# First we get the length of each state; this is just the number of variables in our problem. In our
# example, this will be five.
#
stateLen = len(states)/numStates
#
# An important point is that BlackBox natively solves problems assuming that the variables are +1/-1
# variables. In cases where you'd prefer to use 0/1 variables (such as in this example), you need to
# explicitly convert. Here we convert the states list into a new list called states_bin which stores the
# suggested answers in 0/1 variables.
#
states_bin = [(item+1)/2 for item in states] # converting to 0/1 from -1/+1
#
# We now create a list called ret where we'll store all the results of computing the value of the
# generating function on the states sent in.
#
ret = []
#
# Now cycle over all the states sent in.
#
for state_number in range(numStates):
#
# The w array stores the current state of interest.
#
w = array(states_bin[state_number*stateLen:(state_number+1)*stateLen])
#
# Now we compute the value of the generating function, given the bit string w.
#
result = dot(self.subset_sum_array, w)**2+prod(1-w)
#
# We then append that result to the ret list.
#
ret.append(result)
#
# Once we've done this for all the values sent in, the result is returned as a tuple.
#
return tuple(ret)
#
##########################################################################################################]
Note that our G function is really just one line in all the code above, most of the lines are to handle the multiple-guess part of the code.
To begin coding your own functions, you can use this ObjClass class as a template and just start by changing the result = ... line.
Now that we've coded that up, we are now ready to use BlackBox to solve the problem!
Code Snippet: Main routine
##########################################################################################################
#
# This is the main program for using BlackBox to solve a subset sum instance.
#
# First we establish a connection to a local solver and choose a solver type. Here we'll use the
# optimization solver defined over the full 128 variable
# graph representing a fully functional Rainier processor.
#
solver = local_connection.get_solver('c4-sw_optimize')
#
# The blackbox_parameter is an integer that sets a bunch of solver parameters within the BlackBox
# algorithm. As a developer you actually have a lot more flexibility than just setting a single parameter.
# But for the time being you can just use the rule of thumb that the bigger this is, the more work
# BlackBox will put into finding the best possible solution. Here we'll set it to 10. You can experiment
# with decreasing or increasing it.
#
blackbox_parameter = 10
#
# This is an array giving the values of our initial set.
#
subset_sum_array = array ([-7, -3, -2, 5, 8])
#
# num_vars is just the length of the set.
#
num_vars = len(subset_sum_array)
#
# obj is an object of the class ObjClass. Pass variables in here (in this case subset_sum_array).
#
obj = ObjClass(subset_sum_array)
#
# Set up an instance of BlackBoxSolver
#
blackbox_solver = BlackBoxSolver(solver)
#
# Now run BlackBox. Here you have to pass in obj, num_vars, and cluster_num, but everything else is
# optional. For most users you can just do what I'm doing here and tie all of these parameters to a
# single common value.
#
blackbox_answer = blackbox_solver.solve(obj, num_vars, cluster_num = 10, \
min_iter_inner = blackbox_parameter, max_iter_outer= blackbox_parameter, \
unchanged_threshold=blackbox_parameter, max_unchanged_objective_outer=blackbox_parameter, \
max_unchanged_objective_inner = blackbox_parameter, \
unchanged_best_threshold = blackbox_parameter, verbose=0)
#
# blackbox_answer returns a list of 1/-1 variables denoting the best solution it found. Since we want
# 0/1 variables we convert it to that, and cast it as an array.
#
blackbox_answer_bin = array([(item+1)/2 for item in blackbox_answer])
#
# Now we output the answer!
#
print 'The best bit string we found was:',blackbox_answer_bin
#
# This stores the subset found in a list.
#
subset_list = []
for k in range(num_vars):
if blackbox_answer_bin[k] ==1:
subset_list.append(subset_sum_array[k])
#
print 'The subset this corresponds to is:', subset_list
#
# This computes the value of the generating function at the best answer found.
#
energy_of_best_solution_found = dot(subset_sum_array, blackbox_answer_bin)**2+prod(1-blackbox_answer_bin)
print 'Its energy is:', energy_of_best_solution_found
#
if energy_of_best_solution_found==0:
print 'We found a solution... this set has a subset that sums to zero!'
#
# And that's it!
#
##########################################################################################################
Running this code should produce the following output:
>> The best bit string we found was: [0 1 1 1 0]
>> The subset this corresponds to is: [-3, -2, 5]
>> Its energy is: 0
>> We found a solution... this set has a subset that sums to zero
To change this code to suit your own particular generating function, all you need to do is pass in whatever variables you need to ObjClass, and then modify the definition of the result variable in ObjClass. It's that simple!
1.4 - Your turn!
The above code finds a subset that sums to zero. How would you modify the code so that it looks for a subset that sums to any given number?
As a test case, you can try the set {267, 493, 869, 961, 1000, 1153, 1246, 1598, 1766, 1922} and see if you can find a subset that sums to 5842. This example is from here: https://netfiles.uiuc.edu/c-blair/www/papers/second.pdf
That's the end of the tutorial. You now know how to connect to the quantum solver, send a problem, and interpret the results coming back from the computer. Many of the applications-level programming tutorials are now just variations on this theme - crafting different functions to capture some real world problem in a format where minimizing the function solves the problem, and then sending the resulting function to the quantum compiler.