SAPI REST Interface#
This section includes the following information:
Introduction introduces HTTP codes, problem lifecycle, SAPI versioning, etc.
Resources describes the Solver Application Programming Interface (SAPI) resources and provides example usages.
End-to-End Examples demonstrates using the RESTful interface, without Ocean software, on problems to a quantum computer and to a hybrid solver.
Introduction#
The Solver API (SAPI) is an interface to D-Wave quantum computers and hybrid and classical solvers in the Leap service. This section describes the SAPI interface, including the calls you may use to submit and manage problems and access the available solvers.
A REST-based web services API, SAPI is fairly simple to use; it exposes only a few basic calls. Most of the work for you will be restructuring your problem into a format that the API can answer, and then parsing results.
Note
D-Wave provides a full-stack open-source Software Development Kit (SDK), Ocean SDK. It is recommended that you develop your applications on top of that.
This document is intended for developers who want to build their own applications on top of the SAPI REST interface. Readers should be familiar with the HTTP protocol.
HTTP Response Codes#
You can see a list of HTTP response codes here. The following table describes some of the most frequently encountered codes.
Code |
Explanation |
---|---|
200 OK |
No error. |
201 CREATED |
Creation of a resource was successful. |
202 ACCEPTED |
Request such as problem cancellation was received. |
304 NOT MODIFIED |
The requested resource has not changed since
the time specified in the request’s
|
400 BAD REQUEST |
Invalid request URI or header, or unsupported nonstandard parameter. |
401 UNAUTHORIZED |
Authorization required. This error can also mean that incorrect user credentials were sent with the request. |
403 FORBIDDEN |
Unsupported standard parameter, or authentication or authorization failed. |
404 NOT FOUND |
Resource (such as a problem) not found. |
409 CONFLICT |
Conflict such as a request for problem cancellation. for a problem that has already terminated. |
429 TOO MANY REQUESTS |
API request rate exceeds the permissible limit. |
500 INTERNAL SERVER ERROR |
Internal error. This is the default code that is used for all unrecognized server errors. |
SAPI Problem Lifecycle#
A problem may have one of the following statuses:
Status |
Description |
---|---|
PENDING |
Problem was submitted and is queued for processing. |
IN_PROGRESS |
Problem is currently being processed. |
COMPLETED |
Problem completed successfully. |
FAILED |
Problem processing failed. |
CANCELLED |
Problem was cancelled. |

Fig. 225 Initially a problem is in the PENDING state. When a solver starts to process a problem, its state changes to IN_PROGRESS. After completion, the problem status changes to either COMPLETED or FAILED (if an error occurred). COMPLETED, FAILED, and CANCELLED are all terminal states. After a problem enters a terminal state, its status does not change. Users can cancel a problem at any time before it reaches its terminal state.#
SAPI Version#
To ensure compatibility, verify the version of the SAPI resource you are using in your code. You can see the version number in the response header sent from SAPI.
The base URL and authorization token used in the following example are explained in the Set Up the Session subsection.
>>> import requests
...
>>> SAPI_HOME = "https://na-west-1.cloud.dwavesys.com/sapi/v2"
... # Replace with your API token
>>> SAPI_TOKEN = "ABC-1234567...345678"
...
>>> r = requests.get(f"{SAPI_HOME}/problems/?max_results=3",
... headers={'X-Auth-Token': SAPI_TOKEN})
>>> print(r.headers["Content-Type"])
application/vnd.dwave.sapi.problems+json; version=2.1.0; charset=utf-8
Resources#
This chapter describes supported Solver Application Programming Interface (SAPI) resources and provides example usages.
Examples Setup#
The examples below use the following setup:
>>> import base64
>>> import hashlib
>>> import json
>>> import os
>>> import requests
>>> import struct
>>> from urllib.parse import urlencode
...
>>> SAPI_HOME = "https://na-west-1.cloud.dwavesys.com/sapi/v2"
... # Replace with your API token
>>> SAPI_TOKEN = "ABC-1234567...345678"
...
>>> session = requests.Session()
>>> session.headers = {'X-Auth-Token': SAPI_TOKEN,
... 'Content-type': 'application/json'}
For some examples of uploading problems to the Leap service’s quantum-classical hybrid solvers, a serialized binary quadratic model (\(\text{E} = -ab\)) is also used. The following code uses Ocean software to serialize the problem to the required format.[1]
>>> import dimod
>>> bqm = dimod.BinaryQuadraticModel({}, {'xy': -1}, 'BINARY')
>>> bqm_ser = bqm.to_file().read()
$ export SAPI_HOME=https://na-west-1.cloud.dwavesys.com/sapi/v2
$ export SAPI_TOKEN=ABC-1234567...345678 # Replace with your API token
For some examples of uploading problems to the Leap service’s
quantum-classical hybrid solvers, a serialized binary quadratic model
(\(\text{E} = -ab\)) is also used.
The dimod to_file()
method is used to serialize the problem, which is saved to a file.
Example serialized file
The serialized file may look like this:
b'DIMODBQM\x02\x00\xb2\x00\x00\x00{"dtype": "float64", "itype": "int32",
"ntype": "int32", "shape": [2, 1], "type": "BinaryQuadraticModel", "variables": true,
"vartype": "BINARY"}\n \x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xbf
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xbfVARS8\x00\x00
\x00["x", "y"]
SAPI Resources#
SAPI provides the following resources and methods.
Resource |
Method |
Reference |
---|---|---|
/bqm/multipart |
POST |
|
/bqm/multipart/<problem_data_id>/part/<part> |
PUT |
|
/bqm/multipart/<problem_data_id>/combine |
POST |
|
/bqm/multipart/<problem_data_id>/status |
GET |
|
/problems/ |
POST |
|
/problems/ |
DELETE |
|
/problems/<problem_id> |
DELETE |
|
/problems/ |
GET |
|
/problems/<problem_id>/ |
GET |
|
/problems/<problem_id>/info |
GET |
|
/problems/<problem_id>/answer/ |
GET |
|
/problems/<problem_id>/messages/ |
GET |
|
/solvers/remote/ |
GET |
|
/solvers/remote/<solver_id>/ |
GET |
Initiate Upload of Problem Data#
For quantum-classical hybrid solvers in the Leap service, you must upload your
problem using the $SAPI_HOME/bqm/multipart/*
endpoints before submitting a
SAPI request to run the problem. For large problems (starting from several
megabytes and higher), upload the problem data in multiple parts.
Maximum problem size currently supported is 50 GB.
Size of all parts must be 5M (5242880 bytes), except the last, which may be smaller.
To initiate the uploading of a problem, in a single part for small problems or
multiple parts for problems of several megabytes and higher, send an HTTP POST
request to bqm/multipart
.
The POST request body should contain the number of bytes—size
, as an
integer—required to store the problem.
SAPI returns an identifier, of type UUID, for the uploaded problem.
This example initiates an upload of the serialized BQM, bqm_ser
,
created in the Examples Setup section.
>>> size = len(bqm_ser)
>>> r = session.post(f"{SAPI_HOME}/bqm/multipart",
... json={"size": size})
This example sets the size of the uploaded problem, size
, to the
number of bytes of the serialized BQM of the
Examples Setup section.
$ size=312
$ curl -H "X-Auth-Token: $SAPI_TOKEN" -H "Content-type: application/json" \
-X POST $SAPI_HOME/bqm/multipart/ -d '{"size": '"$size"'}'
{"id":"be806ff1-09d3-49d4-bba1-3008790c99d6"}
2xx responses
SAPI returns a 200 OK
response with an identifier for the problem data
that is to be uploaded.
>>> problem_data_id = r.json()["id"]
>>> print(problem_data_id)
be806ff1-09d3-49d4-bba1-3008790c99d6
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
400
for a failure to initiate upload
Below are some example error responses:
>>> r.status_code
401
>>> r.reason
'Unauthorized'
Upload Problem Data#
To upload a problem, or parts of a large problem, send an HTTP PUT request to
bqm/multipart/<problem_data_id>/part/<part>
(for each part, where <part>
is an integer beginning at 1).
The POST request body should contain the data for (one part of) the problem,
encoded as described in the dimod
to_file()
method, and the header should contain the
MD5 checksum.
For a multi-part upload, data size for each part should be 5 MB (5242880 bytes), except for the last part, which may be smaller.
Calculating the MD5 Hash
The MD5 checksum can be found, for example, using the Python
hashlib
library. The following code example finds the checksum
for the problem created in the Examples Setup section.
>>> import hashlib
>>> import base64
...
>>> # For small problems:
>>> hash_md5 = hashlib.md5()
>>> hash_md5.update(bqm_ser)
>>> part_hash = base64.b64encode(hash_md5.digest()).decode('utf-8')
>>> print(part_hash)
mkDiHuw5xZD3ocYSikE4nw==
>>> # For large problems:
>>> hash_md5 = hashlib.md5()
>>> with bqm.to_file() as f: # for a saved file: with open("filename", "r")
... for chunk in iter(lambda: f.read(4096), b""):
... hash_md5.update(chunk)
>>> part_hash = (base64.b64encode(hash_md5.digest()).decode('utf-8'))
>>> print(part_hash)
mkDiHuw5xZD3ocYSikE4nw==
This example uses the identifier problem_data_id
returned from the
Initiate Upload of Problem Data example and an MD5 hash calculated above.
>>> r = session.put(f"{SAPI_HOME}/bqm/multipart/{problem_data_id}/part/1",
... headers={"Content-type": "application/octet-stream",
... "Content-MD5": "mkDiHuw5xZD3ocYSikE4nw=="},
... data=bqm_ser)
This example sets the identifier, problem_data_id
, of the problem
data to be uploaded to the value returned from the
Initiate Upload of Problem Data example, the MD5 checksum, md5
, to the
value calculated above, and the uploaded data to the BQM of the
Examples Setup section saved to file my_bqm.txt
.
$ problem_data_id="be806ff1-09d3-49d4-bba1-3008790c99d6"
$ md5="mkDiHuw5xZD3ocYSikE4nw=="
$ curl -H "X-Auth-Token: $SAPI_TOKEN" -H "Content-MD5: $md5" -H \
"Content-type: application/octet-stream" -X PUT \
$SAPI_HOME/bqm/multipart/$problem_data_id/part/1 -T my_bqm.txt
2xx responses
SAPI returns a 200 OK
response.
You can check the upload status using the method shown in the Status of a Problem Upload section.
>>> session.get(f"{SAPI_HOME}/bqm/multipart/{problem_data_id}/status").json()
{'status': 'UPLOAD_IN_PROGRESS',
'parts': [{'part_number': 1, 'checksum': '9a40e21eec39c590f7a1c6128a41389f'}]}
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
400
for a failure to upload or invalid checksum404
for a failure to find the specified upload415
for incorrect media type
Below is and example error response to an incorrect MD5:
>>> r.status_code
401
>>> r.reason
'Bad Request'
Below is and example error response to an incorrect content type:
>>> r.status_code
415
>>> r.reason
'Unsupported Media Type'
Complete the Problem Upload#
To complete an upload of problem data, send an HTTP POST request to
bqm/multipart/<problem_data_id>/combine
.
The POST request body should contain the checksum for the entire problem, as a string.
Calculating the Checksum
The Leap service calculates the checksum of the combined file in a particular way: it concatenates all the checksums of the parts and then calculates the checksum of that concatenation. Your checksum must follow this same format.
The checksum can be found, for example, using the Python hashlib
library. The examples below find the concatenated checksum for the problem
of the Examples Setup section and for an artificial
problem that reuses that same problem to represent two parts for the sole
purpose of constructing a combined checksum.
>>> # For a problem uploaded in a single part:
>>> hash_md5 = hashlib.md5()
>>> hash_md5.update(bqm_ser)
>>> problem_checksum = (hashlib.md5(bytes.fromhex(hash_md5.hexdigest()))).hexdigest()
>>> print(problem_checksum)
baf79ab99e269f7fda21e927b33345e9
>>> # For large problems:
>>> hash_md5_1 = hashlib.md5()
>>> hash_md5_1.update(bqm_ser) # replace bqm_ser with the part 1 data
>>> hash_md5_2 = hashlib.md5()
>>> hash_md5_2.update(bqm_ser) # replace bqm_ser with the part 2 data
>>> print((hashlib.md5(bytes.fromhex(
... hash_md5_1.hexdigest() +
... hash_md5_2.hexdigest())
... )).hexdigest())
65e5a89d30c855758d71844a927a8133
This example uses the identifier problem_data_id
returned from the
Initiate Upload of Problem Data example and a checksum calculated above.
>>> r = session.post(f"{SAPI_HOME}/bqm/multipart/{problem_data_id}/combine",
... json={"checksum": "baf79ab99e269f7fda21e927b33345e9"})
This example sets the identifier, problem_data_id
, of the problem
data to be uploaded to the value returned from the
Initiate Upload of Problem Data example and the checksum, checksum
, to
the value calculated above.
$ problem_data_id="be806ff1-09d3-49d4-bba1-3008790c99d6"
$ checksum="baf79ab99e269f7fda21e927b33345e9"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" -H "Content-type: application/json" \
-X POST $SAPI_HOME/bqm/multipart/$problem_data_id/combine/ -d '{"checksum":'"$checksum"'}'
2xx responses
SAPI returns a 200 OK
response.
You can check the upload status using the method shown in the Status of a Problem Upload section.
>>> session.get(f"{SAPI_HOME}/bqm/multipart/{problem_data_id}/status").json()
{'status': 'UPLOAD_COMPLETED', 'parts': []}
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
400
for a failure to upload or missing or invalid checksum404
for a failure to find the specified upload
Below are some example error responses:
>>> r.reason
Failed to combine the multipart upload
Status of a Problem Upload#
To query the status of an in-progress problem upload, send an HTTP GET request
to bqm/multipart/<problem_data_id>/status
.
The GET request should contain no body.
SAPI returns the status as a string with parts and checksums.
This example uses the identifier problem_data_id
returned from the
Initiate Upload of Problem Data example.
>>> r = session.get(f"{SAPI_HOME}/bqm/multipart/{problem_data_id}/status")
This example sets the identifier, problem_data_id
, of the problem
data being uploaded to the value returned from the
Initiate Upload of Problem Data example.
$ problem_data_id="be806ff1-09d3-49d4-bba1-3008790c99d6"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/bqm/multipart/$problem_data_id/status
2xx responses
SAPI returns a 200 OK
response.
For an ongoing upload, SAPI returns a status similar to the following:
>>> r.json()
{'status': 'UPLOAD_IN_PROGRESS',
'parts': [{'part_number': 1, 'checksum': '9a40e21eec39c590f7a1c6128a41389f'}]}
For a successfully completed upload, SAPI returns a status similar to the following:
>>> r.json()
{'status': 'UPLOAD_COMPLETED', 'parts': []}
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
404
for a failure to find the specified upload
Submit Problems#
To submit problems to a solver in the Leap service, send an HTTP POST request to
problems
.
The POST request body should contain the JSON-encoded fields described below. When possible, if you have more than one problem, submit them in a single query.[2]
Fields in Request Body
The JSON data for the submitted problem includes the following key-value pairs:
Key |
Value |
---|---|
data |
Encoded problem data; see below. |
label |
Optional user-defined string to label the problem. |
params |
Solver-specific hybrid parameters or QPU parameters. |
solver |
Solver to be used. |
type |
One of the supported problem types (on QPUs, see the equivalent property for hybrid solvers here). |
Key |
Value and Encoding |
---|---|
format |
Format of the problem data as a string. Supported values are:
|
lin |
For Set values for all qubits of the QPU’s working graph in the same order as the qubits solver property.
For example, before encoding, a Not used for |
quad |
For Set one value per active coupler in the same order as the couplers solver property. An active coupler means that both qubits the coupler is incident to are active. NaN values are not permitted. For example, before encoding, a Not used for |
For an example of sumitting a problem to a quantum processing unit (QPU) sampler,see the End-to-End Examples section.
This example uses the identifier problem_data_id
returned from the
Initiate Upload of Problem Data example and hybrid solver
solver_hybrid_bqm
selected as in
the Submit a Problem to a Quantum-Classical Hybrid Solver section.
>>> r = session.post(f"{SAPI_HOME}/problems",
... json=[{"solver": solver_hybrid_bqm,
... "label": "REST Submission to hybrid BQM solver",
... "data": {"format": "ref", "data": problem_data_id},
... "type": "bqm",
... "params": {"time_limit": 5}}])
This example sets the identifier, problem_data_id
, of the problem
data being uploaded to the value returned from the
Initiate Upload of Problem Data example and sets the requested solver to
hybrid_binary_quadratic_model_version2p
, a solver available to the
user account that executed this example.
$ problem_data_id="be806ff1-09d3-49d4-bba1-3008790c99d6"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/problems -X POST \
-d '[{"type":"bqm","label": "REST Submission to hybrid BQM solver", \
"solver":"hybrid_binary_quadratic_model_version2p", \
"data":{"format": "ref", "data": "'"$problem_data_id"'"}, \
"params":{"time_limit":5}}]'
The request body is JSON data enclosed in a list. For submitting multiple
problems in a single query, the list is the comma-separated fields of each
problem: [{"solver": ...},{"solver": ...},...{"solver": ...}]
.
2xx responses
SAPI returns a 200 OK
response with the problem identifier.
Below are some example 201 responses:
>>> r.json()
{'status': 'IN_PROGRESS',
'id': 'a239c000-10de-4663-b562-af8029c0c470',
'solver': 'hybrid_binary_quadratic_model_version2p',
'type': 'bqm',
'submitted_on': '2022-09-01T16:48:36.052206Z',
'label': 'REST Submission to hybrid BQM solver'}
>>> r.json()
{'status': 'COMPLETED',
'id': '51fa2c98-3f19-4dd9-9f2d-c4295278218f',
'solver': 'Advantage_system4.1',
'type': 'ising',
'submitted_on': '2022-09-01T17:55:00.877038Z',
'label': 'QPU REST submission 1',
'solved_on': '2022-09-01T17:55:01.087126Z',
'answer': {'solutions': 'AMA=',
'energies': 'AAAAAAAA8L8AAAAAAADwvw==',
... # SNIPPED FOR BREVITY
'num_variables': 5760}
}
The following table describes fields in the responses above.
Key |
Value |
---|---|
answer |
Content of the answer depends on the solver and parameters used. |
id |
Unique identifier of the problem. Can be used to retrieve problem information, solutions, and messages. |
label |
Optional user-defined string (label) for a problem. |
status |
One of the problem states as defined in
SAPI Problem Lifecycle.
For example, |
submitted_on |
Time when problem was submitted. |
solved_on |
If this problem is in terminal state ( |
type |
One of the supported values for the
|
The following table describes the answer
field for a QPU solver.
Key |
Value |
---|---|
format |
String: |
num_variables |
Total number of variables (active or otherwise) that the solver has. JSON integer. |
solutions |
Base-64–encoded string of bit-packed solutions (with
0 = -1 for Ising problems). Bits are in little-endian
order. Each solution is padded to end on a byte boundary
and contains values for active qubits only. Ordered by the
values of |
energies |
Base-64–encoded string of energies, each a little-endian 8-byte floating-point number (doubles). Ordered from low to high. |
active_variables |
Base-64–encoded string of the indices of the problem’s active variables. The indices are 4-byte little-endian integers. |
num_occurrences |
Base-64–encoded string of the number of occurrences of
each solution when answer_mode is
|
timing |
Solver-specific JSON object reporting the time that the solver took to handle the problem. |
For quantum-classical hybrid solvers, the answer
field differs. For
example, format
might be bq
and fields might include the type of
variables (e.g., INTEGER
), version, etc.
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
400
for various failures
Below are some example error responses:
>>> r.json()
{'error_code': 400,
'error_msg': 'Problem type (cqm) is not supported by the solver.'}
>>> r.json()
{'error_code': 400,
'error_msg': 'Attempting to run a problem for less than the allowed minimum time_limit 3.0 s'}
>>> r.json()
{'error_code': 400,
'error_msg': 'Solver does not exist or apitoken does not have access'}
Cancel Multiple Problems#
To cancel pending problems (problems with status PENDING
), send an HTTP
DELETE request to problems
.
The request body should be a JSON-encoded list of problem IDs; if the request body is empty, the request has no effect.
When possible, if you have more than one problem to cancel, submit them in a single query.
>>> id_list = ["74d9344c-0160-47bc-b7b1-e245b2ffd955",
... "25f98470-bc55-476c-9042-120bbc0336cf"]
...
>>> r = session.delete(f"{SAPI_HOME}/problems", json=id_list)
$ id_list=[\"74d9344c-0160-47bc-b7b1-e245b2ffd955\",\"25f98470-bc55-476c-9042-120bbc0336cf\"]
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/problems -X DELETE -d $id_list
2xx responses
SAPI returns a 2xx
response. Below are some example 2xx responses:
200
for cancelled problem202
for cancellation request received for in-progress jobs. The problem is cancelled if the request was received in time; otherwise, it will complete.
>>> r.json()
[{'status': 'CANCELLED',
'id': '74d9344c-0160-47bc-b7b1-e245b2ffd955',
'solver': 'hybrid_binary_quadratic_model_version2p',
'type': 'bqm',
'submitted_on': '2022-09-01T18:20:02.594833Z',
'label': 'REST Submission to hybrid BQM solver',
'solved_on': '2022-09-01T18:20:03.708681Z'}]
>>> r.json()
[{'error_code': 202, 'error_msg': 'Attempting to cancel problem in progress.'},
{'error_code': 202, 'error_msg': 'Attempting to cancel problem in progress.'}]
>>> r = session.get(SAPI_HOME + "/problems/74d9344c-0160-47bc-b7b1-e245b2ffd955/info")
>>> r.json()['metadata']['status']
'CANCELLED'
>>> r.json()
[{'error_code': 202, 'error_msg': 'Attempting to cancel problem in progress.'},
{'error_code': 202, 'error_msg': 'Attempting to cancel problem in progress.'}]
>>> r = session.get(SAPI_HOME + "/problems/7d341c42-0eea-460a-b7d0-82892a4a6726/info")
>>> r.json()['metadata']['status']
'COMPLETED'
The following table describes fields in the responses above.
Key |
Value |
---|---|
answer |
Content of the answer depends on the solver and parameters used. |
id |
Unique identifier of the problem. Can be used to retrieve problem information, solutions, and messages. |
label |
Optional user-defined string (label) for a problem. |
status |
One of the problem states as defined in
SAPI Problem Lifecycle.
For example, |
submitted_on |
Time when problem was submitted. |
solved_on |
If this problem is in terminal state ( |
type |
One of the supported values for the
|
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
404
for nonexistent problem ID409
for a poblem that reached a terminal states before the request
Below are some example error responses:
>>> r.json()
[{'error_code': 409, 'error_msg': 'Problem has been finished.'},
{'error_code': 409, 'error_msg': 'Problem has been finished.'}]
Cancel a Problem by ID#
To cancel a previously submitted problem, make an HTTP DELETE request to
problems/<problem_id>
.
The request should contain no body.
>>> problem_id = "74d9344c-0160-47bc-b7b1-e245b2ffd955"
...
>>> r = session.delete(f"{SAPI_HOME}/problems/{problem_id}")
$ problem_id="74d9344c-0160-47bc-b7b1-e245b2ffd955"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/problems/$problem_id -X DELETE
2xx responses
SAPI returns a 2xx
response. Below are some example 2xx responses:
200
for cancelled problem.202
for cancellation request received for in-progress jobs. The problem is cancelled if the request was received in time; otherwise, it will complete.
>>> r.json()
{'status': 'CANCELLED',
'id': 'd92d5b66-330b-4bfd-b26f-a6220b4d2710',
'solver': 'hybrid_binary_quadratic_model_version2p',
'type': 'bqm',
'submitted_on': '2022-09-01T20:09:55.377688Z',
'label': 'REST Submission to hybrid BQM solver',
'solved_on': '2022-09-01T20:09:56.489498Z'}
The following table describes fields in the response above.
Key |
Value |
---|---|
answer |
Content of the answer depends on the solver and parameters used. |
id |
Unique identifier of the problem. Can be used to retrieve problem information, solutions, and messages. |
label |
Optional user-defined string (label) for a problem. |
status |
One of the problem states as defined in
SAPI Problem Lifecycle.
For example, |
submitted_on |
Time when problem was submitted. |
solved_on |
If this problem is in terminal state ( |
type |
One of the supported values for the
|
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
404
for nonexistent problem ID409
for a poblem that reached a terminal states before the request
Below are some example error responses:
>>> r.json()
{'error_code': 409,
'error_msg': 'Problem has been finished.'}
List Problems#
To retrieve a list of previously submitted problems, send an HTTP GET request to
problems
.
The request should contain no body.
By default, the request blocks for up to one second unless at least one problem has completed processing.
You can customize polling by adding the optional timeout
parameter to
specify the blocking time, in seconds, for this request. Supported values are
integers between 1 to 30.
You can filter the results using one or more optional filter keys, concatenated with an ampersand (”&”).
Filter Keys
Key |
Value |
---|---|
id |
Comma-separated list of problem IDs |
label |
Labels that contain the given text. |
max_results |
Maximum number of results to return. Returns up to 1000 if not specified. |
status |
Problem state: COMPLETED, IN_PROGRESS, PENDING, FAILED, CANCELLED. |
solver |
Solver name (e.g.,
|
>>> r = session.get(f"{SAPI_HOME}/problems/?max_results=3")
$ filter="solver=Advantage_system4.1&max_results=3"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" -X GET "$SAPI_HOME/problems/?$filter"
2xx response
SAPI returns a 200 OK
response.
>>> r.json()
[{'id': 'fe863ae1-1d7e-4444-a18e-c1fe7708b8a0',
'solver': 'Advantage_system4.1',
'type': 'ising',
'label': 'QPU REST submission 1',
'submitted_on': '2022-08-29T15:13:11.532127Z',
'solved_on': '2022-08-29T15:13:11.709085Z',
'status': 'COMPLETED'},
{'id': '25f98470-bc55-476c-9042-120bbc0336cf',
'solver': 'Advantage_system4.1',
'type': 'ising',
'label': 'QPU REST submission 1',
'submitted_on': '2022-08-29T15:12:10.461598Z',
'solved_on': '2022-08-29T15:12:10.635163Z',
'status': 'COMPLETED'},
{'id': 'a56da5c5-f8a9-49ad-885b-d7e1b117639e',
'solver': 'Advantage_system4.1',
'type': 'ising',
'label': 'QPU REST submission 1',
'submitted_on': '2022-08-29T15:11:38.897970Z',
'solved_on': '2022-08-29T15:11:39.088480Z',
'status': 'COMPLETED'}]
The following table describes fields in the response above.
Key |
Value |
---|---|
answer |
Content of the answer depends on the solver and parameters used. |
id |
Unique identifier of the problem. Can be used to retrieve problem information, solutions, and messages. |
label |
Optional user-defined string (label) for a problem. |
status |
One of the problem states as defined in
SAPI Problem Lifecycle.
For example, |
submitted_on |
Time when problem was submitted. |
solved_on |
If this problem is in terminal state ( |
type |
One of the supported values for the
|
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
400
iftimeout
is specified as an unsupported value404
for nonexistent or non-accessible problem ID410
for submitted problems that are no longer accessible
Below are some example error responses:
>>> r.text
'Problem does not exist or apitoken does not have access'
Retrieve a Problem#
To retrieve a previously submitted problem, send an HTTP GET request to
problems/<problem_id>
.
The request should contain no body.
By default, the request blocks for up to one second if a submitted problem has not completed processing.
You can customize polling by adding the optional timeout
parameter to
specify the blocking time, in seconds, for this request. Supported values are
integers between 1 to 30.
>>> problem_id = "74d9344c-0160-47bc-b7b1-e245b2ffd955"
...
>>> r = session.get(f"{SAPI_HOME}/problems/{problem_id}?timeout=5")
$ problem_id="74d9344c-0160-47bc-b7b1-e245b2ffd955"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/problems/$problem_id?timeout=5 -X GET
2xx responses
SAPI returns a 200 OK
response.
>>> r.json()
{'status': 'COMPLETED',
'id': '74d9344c-0160-47bc-b7b1-e245b2ffd955',
'solver': 'hybrid_binary_quadratic_model_version2p',
'type': 'bqm',
'submitted_on': '2022-08-31T14:27:47.822848Z',
'label': 'hybrid BQM solver REST Submission python 2',
'solved_on': '2022-08-31T14:27:55.497511Z',
'answer': {'format': 'bq',
... # SNIPPED FOR BREVITY
'charge_time': 4981622,
'run_time': 4981622}}}}
The following table describes fields in the response above.
Key |
Value |
---|---|
answer |
Content of the answer depends on the solver and parameters used. |
id |
Unique identifier of the problem. Can be used to retrieve problem information, solutions, and messages. |
label |
Optional user-defined string (label) for a problem. |
status |
One of the problem states as defined in
SAPI Problem Lifecycle.
For example, |
submitted_on |
Time when problem was submitted. |
solved_on |
If this problem is in terminal state ( |
type |
One of the supported values for the
|
The following table describes the answer
field for a QPU solver.
Key |
Value |
---|---|
format |
String: |
num_variables |
Total number of variables (active or otherwise) that the solver has. JSON integer. |
solutions |
Base-64–encoded string of bit-packed solutions (with
0 = -1 for Ising problems). Bits are in little-endian
order. Each solution is padded to end on a byte boundary
and contains values for active qubits only. Ordered by the
values of |
energies |
Base-64–encoded string of energies, each a little-endian 8-byte floating-point number (doubles). Ordered from low to high. |
active_variables |
Base-64–encoded string of the indices of the problem’s active variables. The indices are 4-byte little-endian integers. |
num_occurrences |
Base-64–encoded string of the number of occurrences of
each solution when answer_mode is
|
timing |
Solver-specific JSON object reporting the time that the solver took to handle the problem. |
For quantum-classical hybrid solvers, the answer
field differs. For
example, format
might be bq
and fields might include the type of
variables (e.g., INTEGER
), version, etc.
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
400
iftimeout
is specified as an unsupported value404
for nonexistent problem ID410
for submitted problems that are no longer accessible
Below are some example error responses:
>>> r.json()
{'error_code': 404,
'error_msg': 'Problem does not exist or apitoken does not have access'}
Retrieve Problem Information#
To retrieve information about a problem, send an HTTP GET request to
problems/<problem_id>/info
:
The request should contain no body.
>>> problem_id = "74d9344c-0160-47bc-b7b1-e245b2ffd955"
...
>>> r = session.get(f"{SAPI_HOME}/problems/{problem_id}/info")
$ problem_id="74d9344c-0160-47bc-b7b1-e245b2ffd955"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/problems/$problem_id/info -X GET
2xx responses
SAPI returns a 200 OK
response.
>>> r.json()
{'id': '7d341c42-0eea-460a-b7d0-82892a4a6726',
'data': {'format': 'ref', 'data': 'be806ff1-09d3-49d4-bba1-3008790c99d6'},
'params': {'time_limit': 5},
'metadata': {'submitted_by': 'ABC-123456789...',
'solver': 'hybrid_binary_quadratic_model_version2p',
'type': 'bqm',
'submitted_on': '2022-09-01T18:27:30.281252Z',
'solved_on': '2022-09-01T18:27:35.473474Z',
'status': 'COMPLETED',
'messages': [],
'label': 'REST Submission to hybrid BQM solver'},
'answer': {'format': 'bq',
... # SNIPPED FOR BREVITY
'run_time': 4998527}}}}
The following table describes fields in the response above.
Key |
Value |
---|---|
answer |
Content of the answer depends on the solver and parameters used. |
id |
Unique identifier of the problem. Can be used to retrieve problem information, solutions, and messages. |
label |
Optional user-defined string (label) for a problem. |
status |
One of the problem states as defined in
SAPI Problem Lifecycle.
For example, |
submitted_on |
Time when problem was submitted. |
solved_on |
If this problem is in terminal state ( |
type |
One of the supported values for the
|
The following table describes the answer
field for a QPU solver.
Key |
Value |
---|---|
format |
String: |
num_variables |
Total number of variables (active or otherwise) that the solver has. JSON integer. |
solutions |
Base-64–encoded string of bit-packed solutions (with
0 = -1 for Ising problems). Bits are in little-endian
order. Each solution is padded to end on a byte boundary
and contains values for active qubits only. Ordered by the
values of |
energies |
Base-64–encoded string of energies, each a little-endian 8-byte floating-point number (doubles). Ordered from low to high. |
active_variables |
Base-64–encoded string of the indices of the problem’s active variables. The indices are 4-byte little-endian integers. |
num_occurrences |
Base-64–encoded string of the number of occurrences of
each solution when answer_mode is
|
timing |
Solver-specific JSON object reporting the time that the solver took to handle the problem. |
For quantum-classical hybrid solvers, the answer
field differs. For
example, format
might be bq
and fields might include the type of
variables (e.g., INTEGER
), version, etc.
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
404
for nonexistent problem ID410
for submitted problems that are no longer accessible
Below are some example error responses:
>>> r.json()
{'error_code': 404,
'error_msg': 'Problem does not exist or apitoken does not have access'}
Retrieve an Answer#
To retrieve an answer for a problem, send an HTTP GET request to
problems/<problem_id>/answer
. The answer consists of solution data in binary
format or a URL to such solution data as well as additional problem information.
The request should contain no body.
>>> problem_id = "74d9344c-0160-47bc-b7b1-e245b2ffd955"
...
>>> r = session.get(f"{SAPI_HOME}/problems/{problem_id}/answer")
$ problem_id="74d9344c-0160-47bc-b7b1-e245b2ffd955"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/problems/$problem_id/answer -X GET
2xx responses
Quadratic models
SAPI returns a 200 OK
response.
>>> r.json()
{'answer': {'solutions': 'AMA=',
'energies': 'AAAAAAAA8L8AAAAAAADwvw==',
'timing': {'qpu_sampling_time': 769.4,
'qpu_anneal_time_per_sample': 20.0,
'qpu_readout_time_per_sample': 36.4,
'qpu_access_time': 15831.76,
'qpu_access_overhead_time': 6535.24,
'qpu_programming_time': 15062.36,
'qpu_delay_time_per_sample': 20.54,
'total_post_processing_time': 1853.0,
'post_processing_overhead_time': 1853.0},
'num_occurrences': 'BgAAAAQAAAA=',
'format': 'qp',
'active_variables': 'HgAAAB8AAAA=',
'num_variables': 5760}}
The following table describes the answer
field for a QPU solver.
Key |
Value |
---|---|
format |
String: |
num_variables |
Total number of variables (active or otherwise) that the solver has. JSON integer. |
solutions |
Base-64–encoded string of bit-packed solutions (with
0 = -1 for Ising problems). Bits are in little-endian
order. Each solution is padded to end on a byte boundary
and contains values for active qubits only. Ordered by the
values of |
energies |
Base-64–encoded string of energies, each a little-endian 8-byte floating-point number (doubles). Ordered from low to high. |
active_variables |
Base-64–encoded string of the indices of the problem’s active variables. The indices are 4-byte little-endian integers. |
num_occurrences |
Base-64–encoded string of the number of occurrences of
each solution when answer_mode is
|
timing |
Solver-specific JSON object reporting the time that the solver took to handle the problem. |
For quantum-classical hybrid solvers, the answer
field differs. For
example, format
might be bq
and fields might include the type of
variables (e.g., INTEGER
), version, etc.
Nonlinear models
SAPI returns a 200 OK
response.
>>> r.json()
{'answer': {'format': 'binary-ref',
'auth_method': 'sapi-token',
'url': 'https://cloud.dwavesys.com/sapi/v2/problems/74d9344c-0160-47bc-b7b1-e245b2ffd955/answer/data/',
'timing': {'qpu_access_time': 47512, 'warnings': [], 'charge_time': 1000000, 'run_time': 1067327},
'shape': {}}}
Key |
Value |
---|---|
format |
String: |
auth_method |
Authorization method required to download
binary data specified in |
url |
URL to download the solution in binary format. |
timing |
Solver-specific JSON object reporting the time that the solver took to handle the problem. |
shape |
Shape of the nonlinear problem. |
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
404
for nonexistent problem ID410
for submitted problems that are no longer accessible
Below are some example error responses:
>>> r.text
'Problem does not exist or apitoken does not have access'
Retrieve Problem Messages#
To retrieve messages for a problem, send an HTTP GET request to
problems/<problem_id>/messages
.
The request should contain no body.
>>> problem_id = "74d9344c-0160-47bc-b7b1-e245b2ffd955"
...
>>> r = session.get(f"{SAPI_HOME}/problems/{problem_id}/messages")
$ problem_id="74d9344c-0160-47bc-b7b1-e245b2ffd955"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/problems/$problem_id/messages -X GET
2xx responses
SAPI returns a 200 OK
response.
>>> r.json()
[]
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
404
for nonexistent problem ID410
for submitted problems that are no longer accessible
Below are some example error responses:
>>> r.text
'Problem does not exist or apitoken does not have access'
Retrieve Available Solvers#
To retrieve a list of available solvers from the Leap service, send an HTTP GET
request to solvers/remote
.
The request should contain no body.
The request supports the use of the If-None-Match
request header. If the
ETag
(entity tag) value in the request header matches the one on the server,
a 304 (Not Modified)
response is returned; otherwise, the list of solvers is
returned. You can use this feature to manage downloading and caching of this
content.
By default, all solver fields are returned. You can use the filter
parameter
to get a subset of solver fields.
Filtering Solver Fields with the filter
Parameter
To retrieve a subset of solver fields, create a list of the subset of solver
fields by specifying the filter
parameter via the following syntax:
filter={all|none}[,{+|-}field]... [,{+|-}field]
where:
all
Initializes the list with all solver fields. This is the default.
none
Initializes the list with no solver fields.
{+|-}field
Adds (
+
) or removes (-
) a solver field to or from the list. The fields are evaluated left to right; for example, if a field is both added and removed, the last action prevails. To specify the solver properties in a solver’sproperties
dict, use dot notation as follows:properties.property[.property]... [.property]
For example:
properties.problem_timing_data.typical_programming_time
If a field does not exist or a field does not contain a value, nothing is
returned for that field. The maximum size of the SAPI URL in the request is
8 KB; thus, the specified filter
parameter must not extend the URL beyond
this maximum.
>>> filter = urlencode({"filter": "none,+id,+status,+avg_load,+properties.num_qubits,+properties.category"})
...
>>> r = session.get(f"{SAPI_HOME}/solvers/remote/?{filter}")
$ filter="filter=none%2C%2Bid%2C%2Bstatus"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/solvers/remote/?filter
2xx response
SAPI returns a 200 OK
response.
The Cache-Control
response header is returned with the following
directives:
private
max-age
: Specifies the time, in seconds, that the returned solver fields should be cached; the time depends on the expected longevity of the solver field values. Solver fields with values that change more frequently (e.g.,avg_load
) should probably be cached for a shorter time than fields with values that change infrequently (e.g.,description
andid
). The value for this directive is based on the shortest cache-refresh time of the returned fields. (Use thefilter
parameter to return the desired subset of solver fields.)
>>> r = r.json()
>>> for i in range(len(r)):
... print(f"{r[i]['id']} \n\tStatus: {r[i]['status']} Load: {r[i]['avg_load']}")
DW_2000Q_6
Status: ONLINE Load: 0.0
DW_2000Q_VFYC_6
Status: ONLINE Load: 0.0
hybrid_binary_quadratic_model_version2p
Status: ONLINE Load: 0.0
hybrid_discrete_quadratic_model_version1p
Status: ONLINE Load: 0.0
Advantage_system4.1
Status: ONLINE Load: 0.13
hybrid_constrained_quadratic_model_version1p
Status: ONLINE Load: 0.0
Advantage_system6.1
Status: ONLINE Load: 0.01
Advantage2_prototype1.1
Status: ONLINE Load: 0.0
Field |
Description |
---|---|
avg_load |
Average current load for the solver. |
description |
Description of the solver. |
id |
Unique ID (name) of the solver. |
properties |
Solver properties
that reside in the |
status |
Status of the solver; for example, a status of
|
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
304
for unmodified resource
Retrieve Solver Fields#
To retrieve the fields of a solver, send an HTTP GET request to
solvers/remote/<solver_id>
.
The request supports the If-None-Match
request header. If the ETag
(entity tag) value in the request header matches the one on the server, a
304 (Not Modified)
response is returned; otherwise, the solver fields and
values are returned. You can use this feature to manage downloading and caching
of this content.
By default, all solver fields are returned. You can use the filter
parameter
to get a subset of solver fields.
Filtering Solver Fields with the filter
Parameter
To retrieve a subset of solver fields, create a list of the subset of solver
fields by specifying the filter
parameter via the following syntax:
filter={all|none}[,{+|-}field]... [,{+|-}field]
where:
all
Initializes the list with all solver fields. This is the default.
none
Initializes the list with no solver fields.
{+|-}field
Adds (
+
) or removes (-
) a solver field to or from the list. The fields are evaluated left to right; for example, if a field is both added and removed, the last action prevails. To specify the solver properties in a solver’sproperties
dict, use dot notation as follows:properties.property[.property]... [.property]
For example:
properties.problem_timing_data.typical_programming_time
If a field does not exist or a field does not contain a value, nothing is
returned for that field. The maximum size of the SAPI URL in the request is
8 KB; thus, the specified filter
parameter must not extend the URL beyond
this maximum.
>>> solver_name = "Advantage_system4.1"
>>> r = session.get(f"{SAPI_HOME}/solvers/remote/{solver_name}")
$ solver_name="Advantage_system4.1"
$ filter="none%2C%2Bstatus%2C%2Bavg_load%2C%2Bproperties.num_qubits"
$ curl -H "X-Auth-Token: $SAPI_TOKEN" $SAPI_HOME/solvers/remote/$solver_name/?filter
2xx response
SAPI returns a 200 OK
response.
The Cache-Control
response header is returned with the following
directives:
private
max-age
: Specifies the time, in seconds, that the returned solver fields should be cached; the time depends on the expected longevity of the solver field values. Solver fields with values that change more frequently (e.g.,avg_load
) should probably be cached for a shorter time than fields with values that change infrequently (e.g.,description
andid
). The value for this directive is based on the shortest cache-refresh time of the returned fields. (Use thefilter
parameter to return the desired subset of solver fields.)
>>> r.json()['description']
'Advantage performance update'
Field |
Description |
---|---|
avg_load |
Average current load for the solver. |
description |
Description of the solver. |
id |
Unique ID (name) of the solver. |
properties |
Solver properties
that reside in the |
status |
Status of the solver; for example, a status of
|
Error responses
In addition to generic client and server error responses, SAPI may return particular error codes.
304
for unmodified resource
End-to-End Examples#
This chapter demonstrates using the RESTful Solver Application Programming Interface (SAPI) directly—without Ocean software providing the client—with two simple examples: submitting a problem to a quantum processing unit (QPU) and to a quantum-classical hybrid solver.
Attention
The examples in this guide are pedagogical: errors are not handled, for example. For a production-code implementation, see Ocean software’s cloud client package.
Import Python Packages Used in Examples#
The examples in this chapter use Python with the following packages:
Requests as the HTTP library
base64
,hashlib
,json
,struct
andurllib.parse.urlencode()
for encoding
>>> import base64
>>> import hashlib
>>> import json
>>> import requests
>>> import struct
>>> from urllib.parse import urlencode
Set Up the Session#
The Configuring Access to the Leap Service (Basic) section explains how you can find your base URL and API token. Start by setting up the base URL and authentication token used for all SAPI requests.
SAPI endpoints are specific to a region. Examples in this document use the https://na-west-1.cloud.dwavesys.com/sapi/v2 base, for North America.
All requests to SAPI require users to authenticate using an API token. An API token is sent to SAPI in the form of HTTP header
X-Auth-Token
.
>>> SAPI_HOME = "https://na-west-1.cloud.dwavesys.com/sapi/v2"
... # Replace with your API token
>>> SAPI_TOKEN = "ABC-1234567...345678"
...
>>> session = requests.Session()
>>> session.headers = {'X-Auth-Token': SAPI_TOKEN, 'Content-type': 'application/json'}
Retrieve Solvers Available for the Token#
Send a GET
request method to the
/solvers/remote resource. The optional
filter parameter, used to reduce the
quantity of retrieved information, can be omitted.
>>> filter = urlencode({"filter": "none,+id,+status,+avg_load,+properties.num_qubits,+properties.category"})
...
>>> r1 = session.get(f"{SAPI_HOME}/solvers/remote/?{filter}")
>>> print(r1.status_code)
200
The response contains all the solvers available to the API token. The code below lists the solver names, statuses, and current usage loads.
>>> r1 = r1.json()
>>> for i in range(len(r1)):
... print(f"{r1[i]['id']} \n\tStatus: {r1[i]['status']} Load: {r1[i]['avg_load']}")
DW_2000Q_6
Status: ONLINE Load: 0.0
DW_2000Q_VFYC_6
Status: ONLINE Load: 0.0
hybrid_binary_quadratic_model_version2p
Status: ONLINE Load: 0.0
hybrid_discrete_quadratic_model_version1p
Status: ONLINE Load: 0.0
Advantage_system4.1
Status: ONLINE Load: 0.13
hybrid_constrained_quadratic_model_version1p
Status: ONLINE Load: 0.0
Advantage_system6.1
Status: ONLINE Load: 0.01
Advantage2_prototype1.1
Status: ONLINE Load: 0.0
Submit a Problem to a QPU Sampler#
This example of submitting a problem in Ising format, \(\text{E}_{ising}(\vc s) = \sum_{i=1}^N h_i s_i + \sum_{i=1}^N \sum_{j=i+1}^N J_{i,j} s_i s_j\), to a quantum computer uses the following simple problem with a single quadratic interaction:
Here, the linear and quadratic coefficients are stored in a dict.
>>> biases = {'x': -0.5, 'y': 0.5, 'xy': -1}
Select a Solver#
You might decide, for example, that your application is best suited to use an Advantage quantum computer, and select one with the largest working graph. This example selects one of the Advantage quantum computers available at the time of execution for the user account running the example.
>>> advantage_systems = {r1[i]['id']: r1[i]["properties"]["num_qubits"] for
... i in range(len(r1)) if "Advantage_system" in r1[i]['id']}
>>> akeys = list(advantage_systems.keys())
>>> avals = list(advantage_systems.values())
>>> qpu_solver = akeys[avals.index(max(avals))]
>>> print(qpu_solver)
Advantage_system4.1
Send a GET
request method to the
/solvers/remote/<solver_id>
resource.[3]
The response from the previous request using the GET
method to the
/solvers/remote resource can include
all the information acquired here if the
filter parameter is omitted.
>>> r2 = session.get(f"{SAPI_HOME}/solvers/remote/{qpu_solver}")
>>> r2 = r2.json()
Format Your Problem for the Selected Solver#
Typically you map (minor-embed) your variables to a QPU’s qubits using a heuristic tool such as minorminor. This example is simple enough to just select the first coupler and the two qubits it couples.
>>> qubits = r2['properties']['qubits']
>>> couplers = r2['properties']['couplers']
...
>>> xy = couplers[0]
>>> print(f"Variable x is embedded as qubit {xy[0]} and y as {xy[1]}.")
Variable x is embedded as qubit 30 and y as 31.
Next, format the biases as shown in the Submit Problems section. For less simple examples you will likely need to develop encoding functions similar to those of Ocean software’s cloud client package.
>>> lin_vec = len(qubits)*[float('nan')]
>>> lin_vec[qubits.index(xy[0])] = biases['x']
>>> lin_vec[qubits.index(xy[1])] = biases['y']
>>> lin_vec_encode = base64.b64encode(struct.pack('<' + ('d' * len(lin_vec)), *lin_vec))
>>> lin = lin_vec_encode.decode("utf-8")
...
>>> quad_vec = [biases['xy']]
>>> quad_vec_encode = base64.b64encode(struct.pack('<' + ('d' * len(quad_vec)), *quad_vec))
>>> quad = quad_vec_encode.decode("utf-8")
Submit Your SAPI Request#
Send a POST
request method to the
/problems resource with your configured
request body, including the problem and solver parameters described in
the General QPU Solver Properties section. This example sets the number of
required reads (anneals) and a problem label.
You should get a valid problem identifier in the response.
>>> r3 = session.post(f"{SAPI_HOME}/problems",
... json=[{"solver": qpu_solver,
... "label": "QPU REST submission 1",
... "data": {"format": "qp", "lin": lin, "quad": quad},
... "type": "ising",
... "params": {"num_reads": 10}}])
>>> r3 = r3.json()
>>> print(f"ID of submission is {r3[0]['id']}.")
ID of submission is 29776c79-8893-48e7-b35e-d3618553fcb4.
Retrieve Your Solutions#
Send a GET
request method to the
/problems/<problem_id>/answer resource.
>>> r4 = session.get(f"{SAPI_HOME}/problems/{r3[0]['id']}/answer")
>>> r4 = r4.json()
Decode the Response#
As shown in the Retrieve Problem Information section, some fields of the response are binary encoded. Here too, less simple examples will likely require decoding functions similar to those of Ocean software’s cloud client package.
>>> qpu_access_time = r4['answer']['timing']['qpu_access_time']
...
>>> energies = base64.b64decode(r4['answer']['energies'])
>>> energies_decode = struct.unpack('<' + ('d' * (len(energies) // 8)), energies)
...
>>> print(f"Found lowest energy {min(energies_decode)} in {qpu_access_time} microseconds.")
Found lowest energy -1.0 in 15831.76 microseconds.
Submit a Problem to a Quantum-Classical Hybrid Solver#
This example submits a binary quadratic model (BQM) problem, in Ising format, \(\text{E}_{ising}(\vc s) = \sum_{i=1}^N h_i s_i + \sum_{i=1}^N \sum_{j=i+1}^N J_{i,j} s_i s_j\), to a quantum-classical hybrid BQM solver in the Leap service. The following simple problem with a single quadratic interaction is used:
Upload Problem to the Leap Service#
Here, Ocean software is used to serialize the problem
to the format described in the dimod
to_file()
method.
>>> import dimod
>>> bqm = dimod.BinaryQuadraticModel({}, {'xy': -1}, 'BINARY')
>>> bqm_ser = bqm.to_file().read()
Example serialized file
The serialized file may look like this:
b'DIMODBQM\x02\x00\xb2\x00\x00\x00{"dtype": "float64", "itype": "int32",
"ntype": "int32", "shape": [2, 1], "type": "BinaryQuadraticModel", "variables": true,
"vartype": "BINARY"}\n \x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xbf
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xbfVARS8\x00\x00
\x00["x", "y"]
The following code combines the POST request of Initiate Upload of Problem Data, the PUT request of Upload Problem Data, and the POST request of Complete the Problem Upload to execute a multi-part upload of the problem data. Calculations of the checksums for the part(s) and whole are shown there.
>>> size = len(bqm_ser)
>>> r5 = session.post(f"{SAPI_HOME}/bqm/multipart",
... json={"size": size})
...
>>> problem_data_id = r5.json()["id"]
...
>>> hash_md5 = hashlib.md5()
>>> hash_md5.update(bqm_ser)
>>> part_hash = base64.b64encode(hash_md5.digest()).decode('utf-8')
...
>>> r6 = session.put(f"{SAPI_HOME}/bqm/multipart/{problem_data_id}/part/1",
... headers={"Content-type": "application/octet-stream",
... "Content-MD5": part_hash},
... data=bqm_ser)
Select a Solver#
This example selects the first Binary Quadratic Model (BQM) hybrid solver
available at the time of execution for the user account running the example. It
uses the response r1
from the Retrieve Solvers Available for the Token
section above.
>>> hybrid_bqm_solvers = [r1[i]['id'] for i in range(len(r1)) if
... r1[i]['properties']['category'] == "hybrid" and "binary" in r1[i]['id']]
>>> bqm_solver = hybrid_bqm_solvers[0]
>>> print(bqm_solver)
hybrid_binary_quadratic_model_version2p
Submit Your SAPI Request#
Format the request body as shown in the Submit Problems section. Also set the problem and solver parameters described in the General QPU Solver Properties section. This example sets a value for the maximum solver runtime and a problem label.
Send a POST
request method to
the /problems resource. You should get
a valid problem identifier in the response.
>>> r8 = session.post(f"{SAPI_HOME}/problems",
... json=[{"solver": bqm_solver,
... "label": "REST Submission to hybrid BQM solver 2",
... "data": {"format": "ref", "data": problem_data_id},
... "type": "bqm",
... "params": {"time_limit": 10}}])
>>> r8 = r8.json()
>>> problem_id = r8[0]['id']
>>> print(f"Problem identifier is {problem_id}.")
Problem identifier is 8460ca28-ff9a-46be-bc7b-adac13e8348d.
Retrieve Your Solutions#
Send a GET
request method to the
/problems/<problem_id>/answer resource.
Once the problem execution is finished, your solution and additional information
is retrieved. Decode the answer as shown in the Retrieve a Problem
section.
>>> r9 = session.get(f"{SAPI_HOME}/problems/{problem_id}")
>>> r9 = r9.json()
>>> if r9['status'] == 'COMPLETED':
... print(f"Run time was {r9['answer']['data']['info']['run_time']} microseconds.")
... else:
... print("Not completed")
Run time was 9987459 microseconds.