Skip to main content

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

  1. Python 3.10: Ensure you have Python 3.10 installed.
  2. 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 SamplerDescriptionBackends (see catalog for complete list)options
D-WaveD-Wave's quantum annealing hardware relies on metal loops of niobium that have tiny electrical currents running through themAdvantage_system6.2, Advantage2_prototype1.1, hybrid_constrained_quadratic_model_version1p, dwave.hybrid_discrete_quadratic_model_version1p,num_reads (int), chain_strength (int), time_limit (float)
GurobiGurobi is an industry-leading solver for mathematical optimizationqubo, mpsmax_seconds (float)
QuantagoniaQuantagonia offers a Hybrid Quantum Platform for solving complex computational problemsqubo, mpssense (str: "MAXIMIZE" or "MINIMIZE"), samples (int)
JiJJiJ is part of the Strangeworks Syndicate and is working on developing quantum annealing devicesSA, SQA, LeapHybridCQMfeed_dict (int), num_search (int)
NECNEC is developing quantum annealers using superconducting parametron qubits with a greater number of all-to-all connected qubits.vector_annealeroffset (float), num_reads (int), num_results (int), timeout (int)
ToshibaToshiba has developed the Simulated Bifurcation Machine (SBM), a combinatorial optimization solver that utilizes the Simulated Bifurcation Algorithmtoshibaalgo (str), steps (int), loops (int), timeout (int), target (float), maxout (int), dt (float), C (float), auto (bool)
AquilaAquila 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 qubitsaquilaunit_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.2"

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.2"
# 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.2"
# 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.parameter_models import GurobiParameterModel

sw.authenticate('your-api-key')

file = "unitcal_7.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.parameter_models import QuantagoniaParameterModel
from dimod import BinaryQuadraticModel

sw.authenticate('your-api-key')

file = "unitcal_7.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)

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:

  1. D-Wave QUBO Guide
  2. Quantum Approximate Optimization Algorithm
  3. List of QUBO Formulations