Skip to content

Commit

Permalink
Add AWS Braket support for qiskit service
Browse files Browse the repository at this point in the history
  • Loading branch information
DominikVoigt committed Sep 14, 2023
1 parent a965bd5 commit de8087b
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 49 deletions.
16 changes: 12 additions & 4 deletions app/aws_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,24 @@
# 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(qpu_name):
def get_qpu(access_key, secret_access_key, qpu_name, region_name='us-west-1'):
boto_session = boto3.Session(
aws_access_key_id=access_key,
aws_secret_access_key=secret_access_key,
region_name=region_name
)
session = AwsSession(boto_session)
provider = AWSBraketProvider()
backend = provider.get_backend(qpu_name)
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."""

Expand All @@ -43,7 +52,6 @@ def execute_job(transpiled_circuit, shots, backend):
job_result_dict = job_result.to_dict()
print(job_result_dict)

# TODO: I am currently not sure whether this is still consistent in AWS Braket as they use the getCounts method, we will see when we try
try:
statevector = job_result.get_statevector()
print("\nState vector:")
Expand All @@ -67,4 +75,4 @@ def execute_job(transpiled_circuit, shots, backend):
print("No unitary available!")
return {'job_result_raw': job_result_dict, 'statevector': statevector, 'counts': counts, 'unitary': unitary}
except Exception:
return None
return None
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
98 changes: 66 additions & 32 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, aws_handler, 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 @@ -45,12 +46,23 @@ def transpile_circuit():
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)

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 @@ -94,9 +106,10 @@ 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.")
Expand All @@ -107,8 +120,8 @@ def transpile_circuit():
return jsonify({'error': str(e)}), 200

backend = None
credentials = {}
if provider == 'ibmq':
credentials = {}
if 'url' in input_params:
credentials['url'] = input_params['url']
if 'hub' in input_params:
Expand All @@ -119,13 +132,15 @@ def transpile_circuit():
credentials['project'] = input_params['project']
backend = ibmq_handler.get_qpu(token, qpu_name, **credentials)
elif provider == 'aws':
backend = aws_handler.get_qpu(qpu_name)
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:
app.logger.warn(f"{qpu_name} not found.")
abort(404)

try:
# TODO: Not sure whether this call will work for AWS?
transpiled_circuit = transpile(circuit, backend=backend, optimization_level=3)
print("Transpiled Circuit")
print(transpiled_circuit)
Expand Down Expand Up @@ -170,7 +185,6 @@ 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)

Expand Down Expand Up @@ -220,9 +234,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 @@ -248,6 +263,7 @@ def execute_circuit():

# 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 @@ -270,28 +286,46 @@ def execute_circuit():
optimization_level = request.json.get('transpilation-optimization-level', 3)
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)

# 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']
backend = ibmq_handler.get_qpu(token, qpu_name, **credentials)
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', 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
28 changes: 17 additions & 11 deletions app/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
from qiskit.transpiler.exceptions import TranspilerError
from rq import get_current_job
from qiskit.utils.measurement_error_mitigation import get_measured_qubits
from qiskit.providers.aer import AerSimulator
from qiskit.providers.aer.noise import NoiseModel
from qiskit import IBMQ, transpile, QuantumCircuit

from qiskit_aer.noise import NoiseModel
from qiskit import transpile, QuantumCircuit, Aer

Expand All @@ -35,15 +33,19 @@
import base64


def execute(provider, impl_url, impl_data, impl_language, transpiled_qasm, input_params, token, qpu_name, optimization_level, noise_model, only_measurement_errors, shots, bearer_token, qasm_string, **kwargs):
def execute(provider, impl_url, impl_data, impl_language, transpiled_qasm, input_params, token_ibmq, access_key_aws,
secret_access_key_aws, qpu_name,
optimization_level, noise_model, only_measurement_errors, shots, bearer_token, qasm_string, **kwargs):
"""Create database entry for result. Get implementation code, prepare it, and execute it. Save result in db"""
app.logger.info("Starting execute task...")
job = get_current_job()

if provider == 'ibmq':
backend = ibmq_handler.get_qpu(token, qpu_name, **kwargs)
backend = ibmq_handler.get_qpu(token_ibmq, qpu_name, **kwargs)
elif provider == 'aws':
backend = aws_handler.get_qpu(qpu_name)
backend = aws_handler.get_qpu(access_key=access_key_aws, secret_access_key=secret_access_key_aws,
qpu_name=qpu_name,
**kwargs)
if not backend:
result = Result.query.get(job.get_id())
result.result = json.dumps({'error': 'qpu-name or token wrong'})
Expand All @@ -61,7 +63,8 @@ def execute(provider, impl_url, impl_data, impl_language, transpiled_qasm, input
# list of circuits
circuits = [implementation_handler.prepare_code_from_qasm_url(url, bearer_token) for url in impl_url]
else:
circuits = [implementation_handler.prepare_code_from_url(url, input_params, bearer_token) for url in impl_url]
circuits = [implementation_handler.prepare_code_from_url(url, input_params, bearer_token) for url in
impl_url]
elif impl_data:
impl_data = [base64.b64decode(data.encode()).decode() for data in impl_data]
if impl_language.lower() == 'openqasm':
Expand All @@ -76,7 +79,7 @@ def execute(provider, impl_url, impl_data, impl_language, transpiled_qasm, input
app.logger.info('Start transpiling...')

if noise_model:
noisy_qpu = ibmq_handler.get_qpu(token, noise_model, **kwargs)
noisy_qpu = ibmq_handler.get_qpu(token_ibmq, noise_model, **kwargs)
noise_model = NoiseModel.from_backend(noisy_qpu)
properties = noisy_qpu.properties()
configuration = noisy_qpu.configuration()
Expand Down Expand Up @@ -109,7 +112,12 @@ def execute(provider, impl_url, impl_data, impl_language, transpiled_qasm, input
db.session.commit()

app.logger.info('Start executing...')
job_result = ibmq_handler.execute_job(transpiled_circuits, shots, backend, noise_model)
if provider == 'aws' and not noise_model:
# Note: AWS cannot handle such a noise model
job_result = aws_handler.execute_job(transpiled_circuits, shots, backend)
else:
# If we need a noise model, we have to use IBM Q
job_result = ibmq_handler.execute_job(transpiled_circuits, shots, backend, noise_model)

if job_result:
result = Result.query.get(job.get_id())
Expand All @@ -122,8 +130,6 @@ def execute(provider, impl_url, impl_data, impl_language, transpiled_qasm, input
result.complete = True
db.session.commit()

# ibmq_handler.delete_token()


def get_measurement_qubits_from_transpiled_circuit(transpiled_circuit):
qubit_index, qubit_mappings = get_measured_qubits([transpiled_circuit])
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@ websockets==11.0
Werkzeug==2.2.3
wrapt==1.15.0
zipp==3.15.0
boto3~=1.28.40

0 comments on commit de8087b

Please sign in to comment.