Strangeworks Optimization
strangeworks-optimization
Strangeworks Optimization SDK delivers an optimization platform designed for developers. The abstraction layer ensures a hassle-free experience for users while retaining the freedom to pick the most appropriate backend for any given problem.
pip install -U pip && pip install strangeworks-optimization
Authentication
You'll need your API key, which is found on the Strangeworks Portal home page.
import strangeworks as sw
sw.authenticate(api_key)
Quickstart
Required:
Model: See Supported Models
Solver: See Supported Solvers
Optional:
Options: See Supported Options
Tags: See Retrieve Jobs
from strangeworks_optimization import StrangeworksOptimizer
so = StrangeworksOptimizer(
model=model,
solver=solver,
options=options,
tags=tags
)
sw_job = so.run()
results = so.results(sw_job.slug)
The resulting jobs can be viewed on the portal
The Optimization Job collects the results from the Sub Jobs and provides the best solution found.
Each Sub Job represents a single execution of the solver on a specific model configuration and includes product-specific execution data and error logs.
Strangeworks Optimizer
The StrangeworksOptimizer
class employs the run()
method to initiate a job. This method requires model
, solver
, and options
as arguments.
Model
For this example we will use dimod. For all available models see here.
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
To check available backends we will create a StrangeworksOptimizer
object and call the backends
method.
To see all available solvers and backends see Available Solvers or use Python.
from strangeworks_optimization import StrangeworksOptimizer
so = StrangeworksOptimizer()
backends = so.backends()
for backend in backends:
print(backend.name)
dwave.Advantage_system6.4
gives access to the D-Wave Advantage system 6.4 solver.
Options
Each solver has its own Parameter Model available for import from strangeworks_optimization_models.parameter_models
.
To see all available options see Supported Options or use Python.
from strangeworks_optimization_models.parameter_models import DwaveSamplerParameterModel
options = DwaveSamplerParameterModel(
num_reads=100,
chain_strength=50
)
Tags
To enhance organization and searchability, you can assign tags to jobs.
so = StrangeworksOptimizer(
model=model,
solver=solver,
options=options,
tags=["tag1", "tag2"]
)
To find jobs by tag, use the jobs_by_tag
method. You can filter by multiple tags and specify whether to use AND
or OR
for the search to combine the tags.
so = StrangeworksOptimizer()
jobs = so.jobs_by_tag("tag1", "tag2", and_or="AND")
Tags can also be added to jobs after they have been created.
import strangeworks as sw
sw.add_tags(job_slug, ["tag1", "tag2"])
Run
The StrangeworksOptimizer
class uses the run()
method to execute the job with model
, solver
, options
, tags
as arguments.
so = StrangeworksOptimizer(
model=model,
solver=solver,
options=options,
tags=tags
)
sw_job = so.run()
The slug is a unique identifier. You can use it to fetch the job later:
print(f"Job slug: {sw_job.slug}")
🥳 Result! You have successfully used the Strangeworks Optimization Service to solve a QUBO problem.
😅 Something went wrong? Find us in Slack!
Fetching a Run
If you want to fetch a previously submitted run for resubmission or to inspect the contents, you can use the download_input_file
method to recreate the optimizer used to run the job:
so = StrangeworksOptimizer()
optimization_job = so.download_input_file(sw_job.slug)
Status
You can see check progress on the portal as well as from the SDK:
so = StrangeworksOptimizer()
print(f"Job status: {so.status(sw_job.slug)}")
The statuses include:
QUEUED
: The job is waiting to be executed.RUNNING
: The job is currently being executed.COMPLETED
: The job has been executed and the results are available.FAILED
: The job has failed to execute.
💡 Failed Jobs:
If the job status comes back as failed you can view the job page in the portal or use the Strangeworks SDK to retrieve the errors:
import strangeworks as sw
sw.get_error_messages(sw_job.slug)
Multiple Resources
If you have multiple optimization resources, simply specify the resource_slug
when creating the optimizer
This may be useful if you are managing multiple resources or if you are using the SDK in a multi-user environment.
from strangeworks_optimization import StrangeworksOptimizer
so = StrangeworksOptimizer(
model=model, solver=solver, resource_slug=resource_slug
)
Results
Solutions are returned in the dimod.sampleset
format, see here for detailed documentation. This is so that results from different solvers can be easily analyzed and benchmarked together without too much extra work from the user.
When the job is completed you can retrieve the solution.
so = StrangeworksOptimizer()
results = so.results(sw_job.slug)
print(f"Best solution:\n{results.solution.first}")
This will print the best most optimal solution. But there are a variety of ways to extract the full information, the one to use depends on how you plan to use it. You can print the samples:
for sample in results.solution.samples():
print(sample)
Or you can use records to get the results in standard python numpy.ndarray
format:
sample_ndarray = results.solution.record.sample
energy_ndarray = results.solution.record.energy
Aggregated results are also available:
aggregated_results = results.solution.aggregate()
Cost
The cost
attribute of the results object provides the cost of the best solution found.
cost = results.cost
Runtime
The runtime
attribute of the results object provides the runtime of the best solution found.
runtime = results.runtime
Raw Results
Also note that, you can access the raw results in the original format for each solver with this code:
so = StrangeworksOptimizer()
results = so.results(sw_job.slug)
results.solution_options["raw_results"]
Files
Download Job
If you want to fetch a previously submitted run for resubmission or to inspect the contents, you can use the download_input_file
method to recreate the optimizer used to run the job:
so = StrangeworksOptimizer()
optimization_job = so.download_input_file(sw_job.slug)
When a model is uploaded to the platform, it is saved in the workspace and becomes accessible to users. Each model is stored as a RemoteFile
, allowing users to download it for further inspection or reuse.
Download Model
To access the model associated with a previous job, use the download_input_model
method from the StrangeworksOptimizer
class:
so = StrangeworksOptimizer()
model = so.download_input_model(job_slug)
print(type(model))
By default, download_input_model
retrieves a reference to the remote model file rather than downloading the actual file. This reference includes a URL pointing to the model's location. If you need to download the physical model file, set the download_remote=True
parameter:
so = StrangeworksOptimizer()
model = so.download_input_model(job_slug, download_remote=True)
print(type(model))
Remote Files
A RemoteFile
can be submitted directly to the platform.
from strangeworks_optimization_models.problem_models import RemoteFile
model_url = model.model_url
model_type = model.model_type
model = RemoteFile(model_url, model_type)
solver = 'your.solver'
so = StrangeworksOptimizer(model=model, solver=solver)
sw_job = so.run()
If you want to submit a model file you must first retrieve the model_url
and model_type
. Accepted model_type
values are (see Models for more information):
BinaryQuadraticModel
ConstrainedQuadraticModel
DiscreteQuadraticModel
AquilaNDArray
QuboDict
MPSFile
HitachiModel
FujitsuModel
MatrixMarket
QplibFile
Upload Models
To upload a model to the platform, use the upload_model
method from the StrangeworksOptimizer
class:
import strangeworks as sw
from strangeworks_optimizer import StrangeworksOptimizer
from dimod import BinaryQuadraticModel
sw.authenticate(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}
bqm = BinaryQuadraticModel(linear, quadratic, "BINARY")
model = StrangeworksOptimizer().upload_model(bqm)
# model_url = model.model_url
# model_type = model.model_type
Download Remote
Once you have obtained the url of the model, you can retrieve it using:
so = StrangeworksOptimizer()
model = so.download_input_model_from_url(file_url).model
print(type(model))
Batching
You can run multiple jobs in parallel by using a batch.yaml
file (see Remote Files for how to produce a model_url
):
job_name: BatchJob
runs:
- run: "rundwave1"
problem_parameters:
model_url: "https://api.strangeworks.com/workspace//files/"
model_type: "BinaryQuadraticModel"
solver: "dwave.Advantage_system6.4"
solver_options:
num_reads: 50
chain_strength: 30
- run: "rundwave2"
problem_parameters:
model_url: "https://api.strangeworks.com/workspace//files/"
model_type: "BinaryQuadraticModel"
solver: "dwave.Advantage2_prototype2.6"
solver_options:
num_reads: 50
chain_strength: 30
- run: "rungurobi1"
problem_parameters:
model_url: "https://api.strangeworks.com/workspace//files/"
model_type: "BinaryQuadraticModel"
solver: "gurobi.qubo"
solver_options:
TimeLimit: 100
WorkLimit: 1000
Cutoff: 10
BarIterLimit: 1000
BestBdStop: 10
BestObjStop: 10
and the run_batch
method to retrieve a list of Jobs.
batch_file = "batch.yaml"
so = StrangeworksOptimizer()
sw_jobs = so.run_batch(batch_file=batch_file)