Getting Started with D-Wave Ocean SDK
Learn D-Wave's Ocean SDK for quantum annealing, formulate combinatorial optimisation problems as QUBO models and solve them on the world's largest quantum annealing processors.
Circuit diagrams
D-Wave builds a fundamentally different kind of quantum computer. Where IBM, Google, and IonQ construct universal gate-based machines that execute sequences of quantum gates, D-Wave builds quantum annealers optimized to find low-energy configurations in energy landscapes. This specialization is intentional: the annealing approach allows D-Wave to build machines with 5,000+ qubits today, far more than any gate-based device, at the cost of running only one type of computation: optimization.
There are no quantum gates, no circuits, and no notion of gate depth. Instead, you define an objective function (an energy landscape), and the quantum annealer searches for the input that minimizes it. If your problem fits naturally into this optimization framework, D-Wave can tackle it at a scale that gate-based hardware cannot yet reach.
Ocean SDK is D-Wave’s open-source Python toolkit. It provides everything you need to formulate optimization problems, submit them to D-Wave hardware or classical simulators, and interpret results.
pip install dwave-ocean-sdk
Set up your D-Wave Leap credentials (free account at cloud.dwavesys.com):
dwave setup
# Follow prompts to enter your API token
How Quantum Annealing Works
Quantum annealing exploits quantum mechanics to search for the lowest-energy state of a physical system. Understanding the process helps you reason about when and why it works.
The annealing schedule proceeds in three stages:
-
Initialization. The annealer places all qubits into an equal superposition of 0 and 1. At this point, a strong transverse magnetic field dominates the system, and every qubit fluctuates freely between states. The system sits in the known ground state of this simple, uniform Hamiltonian.
-
Evolution. Over a timescale of 1 to 2,000 microseconds (configurable per problem), the annealer gradually reduces the transverse field while simultaneously increasing the strength of the problem Hamiltonian, which encodes your objective function. During this transition, quantum tunneling allows the system to pass through energy barriers between candidate solutions, not just over them.
-
Readout. By the end of the anneal, the transverse field has vanished. The qubits have collapsed into a classical configuration (all 0s and 1s) that, ideally, corresponds to the minimum energy state of your problem Hamiltonian.
Quantum tunneling vs. classical thermal hopping. Classical simulated annealing escapes local minima by using thermal energy to jump over barriers. The taller the barrier, the more temperature you need, and the longer it takes. Quantum annealing uses tunneling to pass through barriers. A tall but thin barrier is easy for quantum tunneling to penetrate, even when classical approaches struggle. This distinction gives quantum annealing a potential advantage on problems with many narrow energy barriers.
The annealer does not guarantee finding the global optimum on every run. It is a heuristic. You typically perform many reads (hundreds or thousands of annealing cycles) and take the lowest-energy result.
The Core Concept: QUBO Problems
D-Wave solves QUBO (Quadratic Unconstrained Binary Optimization) problems. You express your problem as minimising:
where each is a binary variable and is a matrix of coefficients you define.
Equivalently you can use Ising models with spin variables .
Understanding QUBO Coefficients
The Q matrix has two types of entries, and each type controls a different aspect of the solution.
Linear terms (diagonal entries, Q[i,i]) control individual variables:
- Q[i,i] < 0 means variable “wants” to be 1, because setting adds a negative (lower) contribution to the energy.
- Q[i,i] > 0 means variable “wants” to be 0, because keeping avoids adding positive energy.
Quadratic terms (off-diagonal entries, Q[i,j]) control relationships between pairs:
- Q[i,j] < 0 means and “want” to agree. When both are 1, the product adds a negative contribution, rewarding agreement.
- Q[i,j] > 0 means and “want” to differ. When both are 1, the product adds a positive penalty, discouraging agreement.
A Tiny Example: Two Variables
Consider two variables with the QUBO matrix:
This means Q[0,0] = -1, Q[1,1] = -1, and Q[0,1] = 2. Both variables individually want to be 1 (negative diagonal), but the pair wants to differ (positive off-diagonal). Enumerate all four possibilities:
| Energy: | ||
|---|---|---|
| 0 | 0 | 0 |
| 1 | 0 | -1 |
| 0 | 1 | -1 |
| 1 | 1 | 0 |
The minimum energy is -1, achieved when exactly one variable is 1. The quadratic penalty for both being 1 exactly cancels the linear rewards. This is the kind of trade-off the annealer navigates across thousands of variables simultaneously.
Example: Solving a Max-Cut Problem
What is Max-Cut?
Given an undirected graph, Max-Cut asks: partition the nodes into two groups (call them group 0 and group 1) to maximize the number of edges that cross between the two groups. An edge is “cut” when its endpoints land in different groups.
Max-Cut is NP-hard in general, which makes it a natural candidate for quantum annealing.
Deriving the QUBO
Assign a binary variable to each node: means node is in group 0, and means group 1. An edge is cut when , which happens when .
To maximize the number of cut edges, maximize:
Since D-Wave minimizes, negate the objective:
For each edge , this contributes:
- -1 to Q[i,i] (linear term for node )
- -1 to Q[j,j] (linear term for node )
- +2 to Q[i,j] (quadratic term penalizing both nodes in the same group)
This is exactly what the code below constructs.
import dimod
import networkx as nx
# Define a small graph
G = nx.Graph()
G.add_edges_from([(0,1), (0,2), (1,2), (1,3), (2,3)])
# Build the QUBO matrix for Max-Cut
Q = {}
for i, j in G.edges():
Q[(i, i)] = Q.get((i, i), 0) - 1
Q[(j, j)] = Q.get((j, j), 0) - 1
Q[(i, j)] = Q.get((i, j), 0) + 2
print("QUBO matrix:", Q)
Interpreting the Solution
A solution like {0: 1, 1: 0, 2: 0, 3: 1} means nodes 0 and 3 are in group 1, while nodes 1 and 2 are in group 0. Every edge that connects a node in group 0 to a node in group 1 is cut. You can verify by checking which edges cross the partition: (0,1), (0,2), (1,3), and (2,3) are all cut, giving a maximum cut of 4 out of 5 edges.
Running on the Simulator (No Hardware Needed)
import dimod
# Use the classical simulated annealing sampler (no API key needed)
sampler = dimod.SimulatedAnnealingSampler()
response = sampler.sample_qubo(Q, num_reads=100)
best = response.first
print("Best solution:", best.sample)
print("Energy:", best.energy)
# Best solution: {0: 1, 1: 0, 2: 0, 3: 1} ← nodes split into two groups
Running on Real D-Wave Hardware
from dwave.system import DWaveSampler, EmbeddingComposite
# EmbeddingComposite automatically maps your variables to physical qubits
sampler = EmbeddingComposite(DWaveSampler())
response = sampler.sample_qubo(Q, num_reads=1000)
for sample, energy, num_occ in response.data(['sample', 'energy', 'num_occurrences']):
print(f"Sample: {sample} Energy: {energy} Occurrences: {num_occ}")
Higher-Level: BinaryQuadraticModel
Ocean provides BinaryQuadraticModel (BQM) as a more convenient interface:
import dimod
# Build a BQM directly
bqm = dimod.BinaryQuadraticModel(vartype='BINARY')
# Add linear biases (single-variable terms)
bqm.add_variable('x0', -1.0)
bqm.add_variable('x1', -1.0)
# Add quadratic biases (interaction terms)
bqm.add_interaction('x0', 'x1', 2.0)
sampler = dimod.SimulatedAnnealingSampler()
result = sampler.sample(bqm, num_reads=500)
print(result.first.sample)
Why Use BQM Instead of Raw Dictionaries?
For small examples, raw QUBO dictionaries work fine. As problems grow, BQM provides several advantages:
Named variables. Use descriptive strings like 'warehouse_A' or 'shift_monday_am' instead of integer indices. This makes debugging and interpreting results far easier.
Programmatic construction. Add and remove variables and interactions incrementally. You can build a BQM in a loop, conditionally add constraints, or merge multiple BQMs together.
Representation conversion. Convert freely between BINARY (0/1), SPIN (-1/+1), and Ising representations. Some problems formulate more naturally in one representation than another.
Structured access. Inspect linear biases, quadratic interactions, and the offset as dictionaries.
# Inspect the BQM structure
print("Variables:", list(bqm.variables))
print("Linear biases:", dict(bqm.linear))
print("Quadratic interactions:", dict(bqm.quadratic))
print("Variable type:", bqm.vartype)
# Convert to Ising representation
ising_linear, ising_quadratic, ising_offset = bqm.to_ising()
print("Ising linear:", ising_linear)
print("Ising quadratic:", ising_quadratic)
print("Ising offset:", ising_offset)
# Convert to spin vartype
spin_bqm = bqm.change_vartype('SPIN', inplace=False)
print("Spin linear biases:", dict(spin_bqm.linear))
Where D-Wave Excels
D-Wave is not a general-purpose quantum computer. It solves optimization problems, and it solves them at a scale no gate-based machine can match today. The following domains map naturally to QUBO formulations.
Combinatorial optimization. Scheduling (job shop, nurse rostering, exam timetabling), vehicle routing, bin packing, and resource allocation problems all reduce to minimizing a cost function over binary decisions. These are the bread and butter of quantum annealing.
Financial optimization. Portfolio selection (choosing assets to maximize return for a given risk), risk analysis, and asset allocation involve optimizing over discrete choices with quadratic interactions between assets.
Logistics. Last-mile delivery routing, warehouse layout optimization, and supply chain network design all involve choosing from combinatorially many configurations, exactly the structure QUBO captures well.
Machine learning. Training restricted Boltzmann machines (RBMs), feature selection (choosing which input features to keep), and clustering all reduce to energy minimization over binary variables.
Drug discovery. Protein conformation search (finding low-energy 3D structures), molecular docking (positioning a drug molecule against a target), and pharmacophore identification involve searching vast discrete configuration spaces.
D-Wave vs. Gate-Based Quantum Computing
| Property | D-Wave (Quantum Annealing) | Gate-Based (IBM/Google/IonQ) |
|---|---|---|
| Problem type | Optimization only | General algorithms |
| Qubit count (2026) | 5,000+ | 100-1,000+ |
| Programming model | Energy function (QUBO/Ising) | Gate sequences (circuit) |
| Gate universality | No | Yes |
| Best for | Logistics, scheduling, QUBO | Chemistry, cryptography, ML |
| Roadmap to fault tolerance | Not applicable | Active research |
The key trade-off: D-Wave sacrifices universality for scale. If your problem is natively an optimization problem, D-Wave lets you work with thousands of qubits right now. If you need Shor’s algorithm, Grover’s search, quantum phase estimation, or variational circuits, you need a gate-based machine.
Common Mistakes
Testing on hardware before validating locally. Always run your QUBO through SimulatedAnnealingSampler or ExactSolver (for small problems) first. If the simulated annealer returns nonsensical results, your QUBO formulation is wrong, and submitting to hardware will just waste QPU time. Debug the math before spending credits.
Setting chain strength too low. Your logical problem graph rarely matches D-Wave’s physical qubit topology. EmbeddingComposite maps each logical variable to a “chain” of physical qubits that must agree. If chain strength is too low, chains break: physical qubits within the same chain return different values, producing meaningless results. A common starting point is to set chain strength to the largest absolute value in your QUBO matrix, then increase if you see broken chains in the response.
Expecting exact solutions. The quantum annealer is a heuristic sampler. Even with thousands of reads, the best sample may not be the global optimum, especially for large problems. Treat results as high-quality approximate solutions. Compare against known bounds when they exist.
Confusing minimization with maximization. D-Wave minimizes energy. If your original problem is a maximization (like Max-Cut), you must negate all coefficients in the QUBO. Forgetting this inversion is one of the most common sources of incorrect results.
Ignoring the sample distribution. Do not look only at the single best sample. The full distribution of returned energies tells you about the problem landscape. If many samples share the same low energy, you have high confidence in that solution. If energies are spread widely, consider increasing num_reads or adjusting annealing parameters like annealing_time.
Next Steps
-
Run the Max-Cut example above with
SimulatedAnnealingSamplerand verify the output matches the expected partition. Modify the graph and observe how solutions change. -
Sign up for D-Wave Leap at cloud.dwavesys.com. The free tier provides one minute of QPU time per month, enough to run hundreds of small problems.
-
Formulate your own problem as a QUBO. Pick a small constraint satisfaction problem (e.g., graph coloring with 4 nodes, a simple scheduling conflict) and derive the Q matrix by hand. Validate it with
dimod.ExactSolver()before scaling up. -
Learn about hybrid solvers. For problems with more than a few hundred variables, D-Wave’s hybrid solvers (accessible via
LeapHybridSampler) combine classical heuristics with quantum annealing to handle problems with up to a million variables. -
Explore the Ocean documentation. The D-Wave Ocean docs cover constrained quadratic models (CQMs), which let you express constraints directly instead of folding penalty terms into the QUBO.
-
Study embedding and topology. Read about the Pegasus graph topology used in the Advantage system. Understanding how your problem maps to physical qubits helps you write better formulations and diagnose chain-break issues.
Was this tutorial helpful?