Strangeworks Optimization SDK
With the rapid advancements in quantum computing, the need for a comprehensive toolkit that bridges various quantum backends is evident. The strangeworks
Python SDK addresses this by offering a seamless interface to tap into a myriad of quantum computing backends, both simulated and physical.
Overview
Strangeworks delivers a quantum computing platform designed for developers, aiming to harness the power of quantum algorithms across diverse backend systems. This SDK encapsulates various optimization tasks. The abstraction layer ensures a hassle-free experience for users while retaining the freedom to pick the most appropriate backend for any given task.
Usage
Prerequisites
- Python 3.10: Ensure you have Python 3.10 installed.
- Virtual Environments: Be familiar with setting up and using virtual environments.
Installation
pip install strangeworks-optimization
Authentication
Before you start making API calls, you'll need to authenticate your requests. To do this, you'll use an API key.
Getting Your API Key
Once you have created your account with Strangeworks, navigate to your profile settings. Under the Portal homepage, you'll find your unique API key.
Your API key is associated with the workspace you are currently using. If your account is associated with multiple workspaces please ensure you are using the correct API key.
Setting Up Your API Key
import strangeworks as sw
sw.authenticate('YOUR_API_KEY')
Warning: Keep your API key secure. Avoid hardcoding it directly into scripts, especially if you're pushing to public repositories. Consider using environment variables or secret management tools.
Resources
In the Strangeworks portal, activate the Optimization Service to create a resource.
Most users will only have a single optimization resource, in which case it is not necessary to specify the resource when creating the optimizer
.
Multiple resources
If you have multiple optimization resources, simply specify the resource_slug
when creating the optimizer
:
from strangeworks_optimization import StrangeworksOptimizer
optimizer = StrangeworksOptimizer(
model=model, solver=solver, resource_slug=resource_slug
)
Note: This may be useful if you are managing multiple resources or if you are using the SDK in a multi-user environment.
Throughout the rest of this tutorial, we will assume that you have a single optimization resource.
Backends
To check available backends we will create a StrangeworksOptimizer
object and call the backends
method. We can print the names of the available backends with the following code:
from strangeworks_optimization import StrangeworksOptimizer
optimizer = StrangeworksOptimizer()
backends = optimizer.backends()
for backend in backends:
print(backend.name)
Here's are some available backend samplers supported through Strangeworks Optimization:
Backend Sampler | Description | Backends (see catalog for complete list) | options |
---|---|---|---|
D-Wave | D-Wave's quantum annealing hardware relies on metal loops of niobium that have tiny electrical currents running through them | Advantage_system6.3 , Advantage2_prototype1.1 , hybrid_constrained_quadratic_model_version1p , dwave.hybrid_discrete_quadratic_model_version1p , | num_reads (int), chain_strength (int), time_limit (float) |
Gurobi | Gurobi is an industry-leading solver for mathematical optimization | qubo , mps | max_seconds (float) |
Quantagonia | Quantagonia offers a Hybrid Quantum Platform for solving complex computational problems | qubo , mps | sense (str: "MAXIMIZE" or "MINIMIZE"), samples (int) |
JiJ | JiJ is part of the Strangeworks Syndicate and is working on developing quantum annealing devices | SA , SQA , LeapHybridCQM | feed_dict (int), num_search (int) |
NEC | NEC is developing quantum annealers using superconducting parametron qubits with a greater number of all-to-all connected qubits. | vector_annealer | offset (float), num_reads (int), num_results (int), timeout (int) |
Toshiba | Toshiba has developed the Simulated Bifurcation Machine (SBM), a combinatorial optimization solver that utilizes the Simulated Bifurcation Algorithm | toshiba | algo (str), steps (int), loops (int), timeout (int), target (float), maxout (int), dt (float), C (float), auto (bool) |
Hitachi | CMOS annealing machine is a non-Neumann architecture computer that Hitachi has developed by utilizing the structure of SRAM, a storage device, to perform optimization processing with the Ising model. | cmos_annealer | type (int), num_executions (int), model (list), parameters (dict), outputs (dict) |
Aquila | Aquila is QuEra's 256-qubit neutral-atom quantum computer. It operates as an analog Hamiltonian simulator on a user-configurable architecture, executing programmable coherent quantum dynamics on up to 256 neutral-atom qubits | aquila | unit_disk_radius (float), shots (int) |
Note: This list is not exhaustive. For a complete list of backends, see the catalog or reach out via Slack here.
Usage
The Strangeworks Optimizer involves specifying a problem model
, a solver
, and options
required by the solver. For a list of options for your backend, see the backends table above or the catalog.
- StrangeworksOptimizer: This is the primary interface provided by Strangeworks to interact with various quantum optimization backends. It abstracts the differences between them, allowing users to submit jobs.
- D-Wave, Gurobi, Quantagonia, etc.: These are backend providers. Strangeworks provides a unified interface to interact with them, but each has its own unique characteristics and advantages.
The problem model is a representation of the problem you want to solve. The solver is the backend
that will be used to solve the problem. The solver options
are parameters that are specific to the solver. For example, the number of samples
to take when solving the problem.
Here is an example of how to use the Strangeworks Optimizer to run a QUBO problem:
from strangeworks_optimization import StrangeworksOptimizer
from dimod import BinaryQuadraticModel
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
solver = "dwave.Advantage_system6.3"
optimizer = StrangeworksOptimizer(model=model, solver=solver)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
The job slug
is a unique identifier for the job. The job status
is a string that indicates the status of the job. You can see these on the portal as well as retrieve them using the SDK.
After running the problem we can get the results with the following code:
results = optimizer.results(sw_job.slug)
print(f"Best solution:\n{results.solution.first}")
🥳 Result! You have successfully used the Strangeworks Optimization Service to solve a QUBO problem.
😅 Something went wrong? Find us in Slack!
Examples
D-Wave Sampler
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import DwaveSamplerParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
options = DwaveSamplerParameterModel(num_reads=100, chain_strength=50)
solver = "dwave.Advantage_system6.3"
# solver = "dwave.Advantage2_prototype1.1"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
print(f"Best solution:\n{results.solution.first}")
D-Wave LeapHybrid
Here are some examples of how to run a LeapHybrid job on the D-Wave backend.
BQM
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import DwaveSamplerParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
solver = "dwave.hybrid_binary_quadratic_model_version2p"
optimizer = StrangeworksOptimizer(model=model, solver=solver)
sw_job = optimizer.run()
CQM
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import DwaveSamplerParameterModel
from dimod import Binary, ConstrainedQuadraticModel
sw.authenticate('your-api-key')
weights = [0.9, 0.7, 0.2, 0.1]
capacity = 1
y = [Binary(f"y_{j}") for j in range(len(weights))]
x = [[Binary(f"x_{i}_{j}") for j in range(len(weights))] for i in range(len(weights))]
model = ConstrainedQuadraticModel()
model.set_objective(sum(y))
for i in range(len(weights)):
model.add_constraint(sum(x[i]) == 1, label=f"item_placing_{i}")
for j in range(len(weights)):
model.add_constraint(
sum(weights[i] * x[i][j] for i in range(len(weights))) - y[j] * capacity <= 0,
label=f"capacity_bin_{j}",
)
solver = "dwave.hybrid_constrained_quadratic_model_version1p"
optimizer = StrangeworksOptimizer(model=model, solver=solver)
sw_job = optimizer.run()
DQM
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import DwaveSamplerParameterModel
from dimod import DiscreteQuadraticModel
import random
sw.authenticate('your-api-key')
model = DiscreteQuadraticModel()
for i in range(10):
model.add_variable(4)
for i in range(9):
for j in range(i + 1, 10):
model.set_quadratic_case(i, random.randrange(0, 4), j, random.randrange(0, 4), random.random())
solver = "dwave.hybrid_discrete_quadratic_model_version1p"
optimizer = StrangeworksOptimizer(model=model, solver=solver)
sw_job = optimizer.run()
D-Wave: Strangeworks Hybrid Workflow
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import DwaveSamplerParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
solver = "dwave.sw_hybrid.Advantage_system6.3"
# solver = "dwave.sw_hybrid.Advantage2_prototype1.1"
optimizer = StrangeworksOptimizer(
model=model, solver=solver, resource_slug=resource_slug
)
sw_job = optimizer.run()
Gurobi
BQM
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import GurobiParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
options = GurobiParameterModel(max_seconds=60)
solver = "gurobi.qubo"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
MPS
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.problem_models import MPSFile
from strangeworks_optimization_models.parameter_models import GurobiParameterModel
sw.authenticate('your-api-key')
file = "example.mps"
model = MPSFile.read_file(file)
options = GurobiParameterModel(max_seconds=60)
solver = "gurobi.mps"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(optimizer.status(sw_job.slug))
results = optimizer.results(sw_job.slug)
Quantagonia
BQM
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import QuantagoniaParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
options = QuantagoniaParameterModel(sense="MINIMIZE")
solver = "quantagonia.qubo"
optimizer = StrangeworksOptimizer(
model=model, solver=solver, options=options, resource_slug=resource_slug
)
sw_job = optimizer.run()
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
MPS
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.problem_models import MPSFile
from strangeworks_optimization_models.parameter_models import QuantagoniaParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
file = "example.mps"
model = MPSFile.read_file(file)
options = QuantagoniaParameterModel(sense="MINIMIZE")
solver = "quantagonia.mps"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
JiJ
BQM
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import (
JijLeapHybridCQMParameterModel,
JijSAParameterModel,
JijSQAParameterModel,
)
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
options = JijSAParameterModel(num_sweeps=2000, num_reads=100)
solver = "jij.SA"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
JiJ Problem
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import (
JijLeapHybridCQMParameterModel,
JijSAParameterModel,
JijSQAParameterModel,
)
import jijmodeling as jm
d = jm.Placeholder("d", ndim=1) # Define variable d
d.len_at(0, latex="N") # Set latex expression of the length of d
x = jm.BinaryVar("x", shape=(d.shape[0],)) # Define binary variable
i = jm.Element("i", belong_to=(0, d.shape[0])) # Define dummy index in summation
model = jm.Problem("simple problem") # Create problem instance
model += jm.sum(i, d[i] * x[i]) # Add objective function
model += jm.Constraint("one hot", jm.sum(i, x[i]) == 1) # Add constraint condition
model # Display the problem
options = JijSAParameterModel(
multipliers={"one-hot": 1.0}, feed_dict={"d": [1.0, 0.1, -2.0, 1.0]}, num_sweeps=2000, num_reads=100
)
solver = "jij.SA"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
NEC
BQM
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import NECParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
options = NECParameterModel(offset=0, num_reads=2, num_results=2, timeout=100)
solver = "nec.vector_annealer"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
Dictionary
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import NECParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
model = QuboDict({("x1", "x1"): 1.0, ("x1", "x2"): -2.0})
options = NECParameterModel(offset=0, num_reads=2, num_results=2, timeout=100)
solver = "nec.vector_annealer"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
Toshiba
BQM
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import ToshibaParameterModel
from dimod import BinaryQuadraticModel
sw.authenticate('your-api-key')
linear = {1: -2, 2: -2, 3: -3, 4: -3, 5: -2}
quadratic = {(1, 2): 2, (1, 3): 2, (2, 4): 2, (3, 4): 2, (3, 5): 2, (4, 5): 2}
model = BinaryQuadraticModel(linear, quadratic, "BINARY")
options = ToshibaParameterModel(offset=0, num_reads=2, num_results=2, timeout=100)
solver = "azure.toshiba"
optimizer = StrangeworksOptimizer(model=model, solver=solver, options=options)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
Hitachi
Ising Model
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.problem_models import HitachiModelList
from strangeworks_optimization_models.parameter_models import HitachiParameterModel
sw.authenticate('your-api-key')
model = HitachiModelList(
[
[0, 0, 0, 1, 117],
[0, 0, 1, 0, 104],
[0, 0, 1, 1, 65],
[0, 1, 1, 0, 72],
[0, 1, 1, 1, 45],
[1, 0, 1, 1, 40],
]
)
options = HitachiParameterModel(solver_type=5)
solver = 'hitachi.cmos_annealer'
optimizer = StrangeworksOptimizer(
model=model,
options=options,
solver=solver
)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
print(results.solution)
For more information about the Hitachi solver, see the Hitachi Annealing Cloud API Reference.
Quera Aquila
Solving the Unit Disk Graph
import strangeworks as sw
from strangeworks_optimization import StrangeworksOptimizer
from strangeworks_optimization_models.parameter_models import AquilaParameterModel
import numpy as np
sw.authenticate('your-api-key')
model = AquilaNDArray(np.array([[1, 2], [0, 0], [1, 0], [1, 1], [2, 0], [2, 1]]))
options = AquilaParameterModel(unit_disk_radius=1.68, shots=1000)
solver = "braket.aquila"
optimizer = StrangeworksOptimizer(
model=model, solver=solver, options=options, resource_slug=resource_slug
)
sw_job = optimizer.run()
print(f"Job slug: {sw_job.slug}")
print(f"Job status: {optimizer.status(sw_job.slug)}")
results = optimizer.results(sw_job.slug)
Further Reading
Expand your knowledge on QUBO and quantum optimization through these resources: