Skip to content

Commit

Permalink
Merge pull request #9 from UST-QuAntiL/feature/calibration-matrix
Browse files Browse the repository at this point in the history
Feature/calibration matrix
  • Loading branch information
wederbn authored Nov 19, 2020
2 parents 8a51dee + e993bf2 commit 792901d
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 1 deletion.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ Send implementation, input, QPU information, and your IBM Quantum Experience tok

Returns a content location for the result. Access it via `GET`.

## Calibration Matrix Calculation Request
Send QPU information, optional shots, and your IBM Quantum Experience token to the API to calculate the calibration matrix for the given QPU.

`POST /qiskit-service/api/v1.0/calculate-calibration-matrix`
```
{
"qpu-name": "NAME-OF-QPU",
"token": "YOUR-IBMQ-TOKEN",
"shots": "NUMBER-OF-SHOTS"
}
```

Returns a content location for the result. Access it via `GET`.

## 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
27 changes: 27 additions & 0 deletions app/NumpyEncoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# ******************************************************************************
# Copyright (c) 2020 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.
# ******************************************************************************
import json
import numpy as np


class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.ndarray):
return obj.tolist()
return json.JSONEncoder.default(self, obj)
38 changes: 37 additions & 1 deletion app/ibmq_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ******************************************************************************
from time import sleep

from qiskit import IBMQ, assemble, QiskitError
from qiskit import IBMQ, assemble, QiskitError, QuantumRegister, execute
from qiskit.ignis.mitigation import CompleteMeasFitter, complete_meas_cal
from qiskit.providers.jobstatus import JOB_FINAL_STATES
from qiskit.providers.exceptions import JobError, JobTimeoutError
from qiskit.providers.exceptions import QiskitBackendNotFoundError
Expand Down Expand Up @@ -82,3 +84,37 @@ def execute_job(transpiled_circuit, shots, backend):
return {'job_result_raw': job_result_dict, 'statevector': statevector, 'counts': counts, 'unitary': unitary}
except (JobError, JobTimeoutError):
return None


def get_meas_fitter(token, qpu_name, shots):
"""Execute the calibration circuits on the given backend and calculate resulting matrix."""
print("Starting calculation of calibration matrix for QPU: ", qpu_name)

backend = get_qpu(token, qpu_name)

# Generate a calibration circuit for each state
qr = QuantumRegister(len(backend.properties().qubits))
meas_calibs, state_labels = complete_meas_cal(qr=qr, circlabel='mcal')

# Execute each calibration circuit and store results
print('Executing ' + str(len(meas_calibs)) + ' circuits to create calibration matrix...')
cal_results = []
for circuit in meas_calibs:
print('Executing circuit ' + circuit.name)
cal_results.append(execute_calibration_circuit(circuit, shots, backend))

# Generate calibration matrix out of measurement results
meas_fitter = CompleteMeasFitter(cal_results, state_labels, circlabel='mcal')
return meas_fitter.filter


def execute_calibration_circuit(circuit, shots, backend):
"""Execute a calibration circuit on the specified backend"""
job = execute(circuit, backend=backend, shots=shots)

job_status = job.status()
while job_status not in JOB_FINAL_STATES:
print('The execution is still running')
sleep(20)
job_status = job.status()
return job.result()
23 changes: 23 additions & 0 deletions app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,29 @@ def execute_circuit():
return response


@app.route('/qiskit-service/api/v1.0/calculate-calibration-matrix', methods=['POST'])
def calculate_calibration_matrix():
"""Put calibration matrix calculation job in queue. Return location of the later result."""
if not request.json or not 'qpu-name' in request.json or not 'token' in request.json:
abort(400)
qpu_name = request.json['qpu-name']
token = request.json['token']
shots = request.json.get('shots', 8192)

job = app.execute_queue.enqueue('app.tasks.calculate_calibration_matrix', qpu_name=qpu_name, token=token,
shots=shots)
result = Result(id=job.get_id())
db.session.add(result)
db.session.commit()

logging.info('Returning HTTP response to client...')
content_location = '/qiskit-service/api/v1.0/results/' + result.id
response = jsonify({'Location': content_location})
response.status_code = 202
response.headers['Location'] = content_location
return response


@app.route('/qiskit-service/api/v1.0/results/<result_id>', methods=['GET'])
def get_result(result_id):
"""Return result when it is available."""
Expand Down
26 changes: 26 additions & 0 deletions app/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from qiskit import transpile
from qiskit.transpiler.exceptions import TranspilerError
from rq import get_current_job

from app.NumpyEncoder import NumpyEncoder
from app.result_model import Result
import logging
import json
Expand Down Expand Up @@ -71,3 +73,27 @@ def execute(impl_url, input_params, token, qpu_name, shots):
db.session.commit()

# ibmq_handler.delete_token()


def calculate_calibration_matrix(token, qpu_name, shots):
"""Calculate the current calibration matrix for the given QPU and save the result in db"""
job = get_current_job()

backend = ibmq_handler.get_qpu(token, qpu_name)
if backend:
job_result = ibmq_handler.get_meas_fitter(token, qpu_name, shots)
if job_result:
result = Result.query.get(job.get_id())
result.result = json.dumps({'matrix': job_result.cal_matrix}, cls=NumpyEncoder)
result.complete = True
db.session.commit()
else:
result = Result.query.get(job.get_id())
result.result = json.dumps({'error': 'matrix calculation failed'})
result.complete = True
db.session.commit()
else:
result = Result.query.get(job.get_id())
result.result = json.dumps({'error': 'qpu-name or token wrong'})
result.complete = True
db.session.commit()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ rq==1.4.1
SQLAlchemy==1.3.16
urllib3==1.25.9
Werkzeug==1.0.1
numpy~=1.19.0
gunicorn

0 comments on commit 792901d

Please sign in to comment.