Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add aws #52

Merged
merged 14 commits into from
Nov 21, 2023
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ The qiskit-service provides a Swagger UI, specifying the request schemas and sho

_Note_: To run the setup as a developer, see [here](./docs/dev/run-as-dev.md).

## Using AWS Braket QPUs
To execute quantum circuits on AWS Braket QPUs via the qiskit service, an AWS IAM account with the Braket Group membership is required.
The AWS access key and the AWS secret access key have to be provided in the request json body to transpile or execute quantum circuits for/on AWS Braket QPUs.

_Note_: The default region for this request is `eu-west-2` (Europe London) but can also be changed by providing the region under the `region` key within the `input_params` in the request json body.
Do note though that the region has to be selected depending on the QPU to use. For more details, see [here](https://docs.aws.amazon.com/braket/latest/developerguide/braket-regions.html).

## Sample Implementations for Transpilation and Execution
Sample implementations can be found [here](https://github.com/UST-QuAntiL/nisq-analyzer-content/tree/master/example-implementations).
Please use the raw GitHub URL as `impl-url` value (see [example](https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Shor/shor-general-qiskit.py)).
Expand Down
78 changes: 78 additions & 0 deletions app/aws_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# ******************************************************************************
# Copyright (c) 2020-2021 University of Stuttgart
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ******************************************************************************
from qiskit_braket_provider import AWSBraketProvider
from braket.aws.aws_session import AwsSession
import boto3
from qiskit.providers.jobstatus import JOB_FINAL_STATES
from qiskit import QiskitError


def get_qpu(access_key, secret_access_key, qpu_name, region='eu-west-2'):
boto_session = boto3.Session(
aws_access_key_id=access_key,
aws_secret_access_key=secret_access_key,
region_name=region,
)
session = AwsSession(boto_session)
provider = AWSBraketProvider()
backend = provider.get_backend(qpu_name, aws_session=session)
return backend


def execute_job(transpiled_circuit, shots, backend):
"""Generate qObject from transpiled circuit and execute it. Return result."""

try:
job = backend.run(transpiled_circuit, shots=shots)

job_status = job.status()
while job_status not in JOB_FINAL_STATES:
print("The job is still running")
job_status = job.status()

job_result = job.result()
print("\nJob result:")
print(job_result)
job_result_dict = job_result.to_dict()
print(job_result_dict)

try:
statevector = job_result.get_statevector()
print("\nState vector:")
print(statevector)
except QiskitError:
statevector = None
print("No statevector available!")
try:
counts = job_result.get_counts()
print("\nCounts:")
print(counts)
except QiskitError:
counts = None
print("No counts available!")
try:
unitary = job_result.get_unitary()
print("\nUnitary:")
print(unitary)
except QiskitError:
unitary = None
print("No unitary available!")
return {'job_result_raw': job_result_dict, 'statevector': statevector, 'counts': counts, 'unitary': unitary}
except Exception:
return None
2 changes: 1 addition & 1 deletion app/circuit_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@ def get_multi_qubit_gate_depth(transpiled_circuit):
def get_number_of_measurement_operations(transpiled_circuit):
""" Get number of measurement operations in the transpiled circuit """
transpiled_dag = circuit_to_dag(transpiled_circuit)
return transpiled_dag.count_ops().get('measure')
return transpiled_dag.count_ops().get('measure', 0)
2 changes: 0 additions & 2 deletions app/ibmq_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
from qiskit.compiler import assemble
from qiskit.providers.jobstatus import JOB_FINAL_STATES
from qiskit.providers.exceptions import JobError, JobTimeoutError
from qiskit.providers.exceptions import QiskitBackendNotFoundError
from qiskit.providers.ibmq.api.exceptions import RequestsApiError


def get_qpu(token, qpu_name, url='https://auth.quantum-computing.ibm.com/api', hub='ibm-q', group='open', project='main'):
Expand Down
157 changes: 104 additions & 53 deletions app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
# ******************************************************************************
from qiskit.providers.ibmq import IBMQAccountError

from app import app, benchmarking, ibmq_handler, implementation_handler, db, parameters, circuit_analysis, analysis
from app import app, benchmarking, aws_handler, ibmq_handler, implementation_handler, db, parameters, circuit_analysis, \
analysis
from app.benchmark_model import Benchmark
from app.qpu_metrics import generate_deterministic_uuid, get_all_qpus_and_metrics_as_json_str
from app.result_model import Result
Expand All @@ -37,29 +38,32 @@ def transpile_circuit():
if not request.json or not 'qpu-name' in request.json:
abort(400)

# Default value is ibmq for services that do not support multiple providers and expect the IBMQ provider
provider = request.json.get('provider', 'ibmq')
qpu_name = request.json['qpu-name']
impl_language = request.json.get('impl-language', '')
input_params = request.json.get('input-params', "")
impl_url = request.json.get('impl-url', "")
bearer_token = request.json.get("bearer-token", "")
input_params = parameters.ParameterDictionary(input_params)
if 'token' in input_params:
token = input_params['token']
elif 'token' in request.json:
token = request.json.get('token')
else:
abort(400)


credentials = {}
if 'url' in input_params:
credentials['url'] = input_params['url']
if 'hub' in input_params:
credentials['hub'] = input_params['hub']
if 'group' in input_params:
credentials['group'] = input_params['group']
if 'project' in input_params:
credentials['project'] = input_params['project']
if input_params:
input_params = parameters.ParameterDictionary(input_params)

if provider == 'ibmq':
if 'token' in input_params:
token = input_params['token']
elif 'token' in request.json:
token = request.json.get('token')
else:
abort(400)
elif provider == 'aws':
if 'aws-access-key-id' in input_params and 'aws-secret-access-key' in input_params:
aws_access_key_id = input_params['aws-access-key-id']
aws_secret_access_key = input_params['aws_secret_access_key']
elif 'aws-access-key-id' in request.json and 'aws-secret-access-key' in request.json:
aws_access_key_id = request.json.get('aws-access-key-id')
aws_secret_access_key = request.json.get('aws-secret-access-key')
else:
abort(400)

if impl_url is not None and impl_url != "":
impl_url = request.json['impl-url']
Expand Down Expand Up @@ -91,7 +95,7 @@ def transpile_circuit():
abort(400)

try:
print("circuit", circuit)
print(f"Circuit:\n{circuit}")
non_transpiled_depth_old = 0
non_transpiled_depth = circuit.depth()
while non_transpiled_depth_old < non_transpiled_depth:
Expand All @@ -103,28 +107,45 @@ def transpile_circuit():
non_transpiled_number_of_multi_qubit_gates = circuit.num_nonlocal_gates()
non_transpiled_number_of_measurement_operations = circuit_analysis.get_number_of_measurement_operations(circuit)
non_transpiled_number_of_single_qubit_gates = non_transpiled_total_number_of_operations - \
non_transpiled_number_of_multi_qubit_gates - \
non_transpiled_number_of_measurement_operations
non_transpiled_multi_qubit_gate_depth, non_transpiled_circuit = circuit_analysis.get_multi_qubit_gate_depth(circuit.copy())
non_transpiled_number_of_multi_qubit_gates - \
non_transpiled_number_of_measurement_operations
non_transpiled_multi_qubit_gate_depth, non_transpiled_circuit = circuit_analysis.get_multi_qubit_gate_depth(
circuit.copy())
print(f"Non transpiled width {non_transpiled_width} & non transpiled depth {non_transpiled_depth}")
if not circuit:
app.logger.warn(f"{short_impl_name} not found.")
abort(404)
except Exception as e:

app.logger.info(f"Transpile {short_impl_name} for {qpu_name}: {str(e)}")
return jsonify({'error': str(e)}), 200

backend = ibmq_handler.get_qpu(token, qpu_name, **credentials)
backend = None
credentials = {}
if provider == 'ibmq':
if 'url' in input_params:
credentials['url'] = input_params['url']
if 'hub' in input_params:
credentials['hub'] = input_params['hub']
if 'group' in input_params:
credentials['group'] = input_params['group']
if 'project' in input_params:
credentials['project'] = input_params['project']
backend = ibmq_handler.get_qpu(token, qpu_name, **credentials)
elif provider == 'aws':
if 'region' in input_params:
credentials['region'] = input_params['region']
backend = aws_handler.get_qpu(access_key=aws_access_key_id, secret_access_key=aws_secret_access_key,
qpu_name=qpu_name, **credentials)
if not backend:
# ibmq_handler.delete_token()
app.logger.warn(f"{qpu_name} not found.")
abort(404)

try:
transpiled_circuit = transpile(circuit, backend=backend, optimization_level=3)
print("Transpiled Circuit")
print(transpiled_circuit)
if provider == 'aws':
transpiled_circuit = transpile(circuit, backend=backend)
else:
transpiled_circuit = transpile(circuit, backend=backend, optimization_level=3)
print(f"Transpiled Circuit:\n{transpiled_circuit}")
width = circuit_analysis.get_width_of_circuit(transpiled_circuit)
depth = transpiled_circuit.depth()
total_number_of_operations = transpiled_circuit.size()
Expand All @@ -133,8 +154,6 @@ def transpile_circuit():
number_of_single_qubit_gates = total_number_of_operations - number_of_multi_qubit_gates - \
number_of_measurement_operations
multi_qubit_gate_depth, transpiled_circuit = circuit_analysis.get_multi_qubit_gate_depth(transpiled_circuit)
print("After all")
print(transpiled_circuit)

except TranspilerError:
app.logger.info(f"Transpile {short_impl_name} for {qpu_name}: too many qubits required")
Expand Down Expand Up @@ -166,15 +185,15 @@ def transpile_circuit():

@app.route('/qiskit-service/api/v1.0/analyze-original-circuit', methods=['POST'])
def analyze_original_circuit():

if not request.json:
abort(400)

impl_language = request.json.get('impl-language', '')
impl_url = request.json.get('impl-url', "")
input_params = request.json.get('input-params', "")
bearer_token = request.json.get("bearer-token", "")
input_params = parameters.ParameterDictionary(input_params)
if input_params:
input_params = parameters.ParameterDictionary(input_params)

if impl_url is not None and impl_url != "":
impl_url = request.json['impl-url']
Expand Down Expand Up @@ -216,9 +235,10 @@ def analyze_original_circuit():
non_transpiled_number_of_multi_qubit_gates = circuit.num_nonlocal_gates()
non_transpiled_number_of_measurement_operations = circuit_analysis.get_number_of_measurement_operations(circuit)
non_transpiled_number_of_single_qubit_gates = non_transpiled_total_number_of_operations - \
non_transpiled_number_of_multi_qubit_gates - \
non_transpiled_number_of_measurement_operations
non_transpiled_multi_qubit_gate_depth, non_transpiled_circuit = circuit_analysis.get_multi_qubit_gate_depth(circuit)
non_transpiled_number_of_multi_qubit_gates - \
non_transpiled_number_of_measurement_operations
non_transpiled_multi_qubit_gate_depth, non_transpiled_circuit = circuit_analysis.get_multi_qubit_gate_depth(
circuit)
print(circuit)
print(f"Non transpiled width {non_transpiled_width} & non transpiled depth {non_transpiled_depth}")
if not circuit:
Expand All @@ -241,6 +261,10 @@ def execute_circuit():
"""Put execution jobs in queue. Return location of the later results."""
if not request.json or not 'qpu-name' in request.json:
abort(400)

# Default value is ibmq for services that do not support multiple providers and expect the IBMQ provider
provider = request.json.get('provider', 'ibmq')

qpu_name = request.json['qpu-name']
impl_language = request.json.get('impl-language', '')
impl_url = request.json.get('impl-url')
Expand All @@ -261,30 +285,52 @@ def execute_circuit():
noise_model = request.json.get("noise-model")
only_measurement_errors = request.json.get("only-measurement-errors")
optimization_level = request.json.get('transpilation-optimization-level', 3)
input_params = parameters.ParameterDictionary(input_params)
if input_params:
input_params = parameters.ParameterDictionary(input_params)

token = ''
aws_access_key_id = ''
aws_secret_access_key = ''

if provider == 'ibmq':
if 'token' in input_params:
token = input_params['token']
elif 'token' in request.json:
token = request.json.get('token')
else:
abort(400)
elif provider == 'aws':
if 'aws-access-key-id' in input_params and 'aws-secret-access-key' in input_params:
aws_access_key_id = input_params['aws-access-key-id']
aws_secret_access_key = input_params['aws_secret_access_key']
elif 'aws-access-key-id' in request.json and 'aws-secret-access-key' in request.json:
aws_access_key_id = request.json.get('aws-access-key-id')
aws_secret_access_key = request.json.get('aws-secret-access-key')
else:
abort(400)

# Check parameters required for using premium accounts, de.imbq and reservations
credentials = {}
if 'url' in input_params:
credentials['url'] = input_params['url']
if 'hub' in input_params:
credentials['hub'] = input_params['hub']
if 'group' in input_params:
credentials['group'] = input_params['group']
if 'project' in input_params:
credentials['project'] = input_params['project']
if provider == 'ibmq':
if 'url' in input_params:
credentials['url'] = input_params['url']
if 'hub' in input_params:
credentials['hub'] = input_params['hub']
if 'group' in input_params:
credentials['group'] = input_params['group']
if 'project' in input_params:
credentials['project'] = input_params['project']
elif provider == 'aws':
if 'region' in input_params:
credentials['region'] = input_params['region']

shots = request.json.get('shots', 1024)
if 'token' in input_params:
token = input_params['token']
elif 'token' in request.json:
token = request.json.get('token')
else:
abort(400)

job = app.execute_queue.enqueue('app.tasks.execute', impl_url=impl_url, impl_data=impl_data,
job = app.execute_queue.enqueue('app.tasks.execute', provider=provider, impl_url=impl_url, impl_data=impl_data,
impl_language=impl_language, transpiled_qasm=transpiled_qasm, qpu_name=qpu_name,
token=token, input_params=input_params, noise_model=noise_model,
token_ibmq=token, access_key_aws=aws_access_key_id,
secret_access_key_aws=aws_secret_access_key, input_params=input_params,
noise_model=noise_model,
only_measurement_errors=only_measurement_errors,
optimization_level=optimization_level, shots=shots, bearer_token=bearer_token,
qasm_string=qasm_string, **credentials)
Expand Down Expand Up @@ -452,6 +498,11 @@ def get_providers():
"id": str(generate_deterministic_uuid("ibmq", "provider")),
"name": "ibmq",
"offeringURL": "https://quantum-computing.ibm.com/",
},
{
"id": str(generate_deterministic_uuid("aws", "provider")),
"name": "aws",
"offeringURL": "https://aws.amazon.com/braket/",
}
]
}
Expand Down
Loading
Loading