From 83920a7baf2b30011c8b0072a2e5836a92ba8eff Mon Sep 17 00:00:00 2001 From: Marie Salm Date: Thu, 20 Jun 2024 10:10:17 +0200 Subject: [PATCH 1/8] generate circuits and post process --- Dockerfile | 4 +- app/__init__.py | 14 +++++- app/generated_circuit_model.py | 37 +++++++++++++++ app/implementation_handler.py | 40 ++++++++++++++-- app/parameters.py | 16 +++++-- app/result_model.py | 2 + app/routes.py | 71 ++++++++++++++++++++++++++-- app/tasks.py | 61 ++++++++++++++++++++++-- app/tket_handler.py | 5 +- migrations/versions/109764431420_.py | 34 +++++++++++++ migrations/versions/2e3851183200_.py | 36 ++++++++++++++ migrations/versions/3c7255be0c00_.py | 51 ++++++++++++++++++++ migrations/versions/4e54aef7eb94_.py | 34 +++++++++++++ migrations/versions/628de98b3495_.py | 34 +++++++++++++ migrations/versions/75e93863f19a_.py | 32 +++++++++++++ migrations/versions/79e91529e693_.py | 34 +++++++++++++ migrations/versions/978123b2bde5_.py | 34 +++++++++++++ migrations/versions/b9568b12eb91_.py | 34 +++++++++++++ migrations/versions/d57a92b02fa3_.py | 36 ++++++++++++++ migrations/versions/d592480df5a0_.py | 34 +++++++++++++ requirements.txt | 5 ++ 21 files changed, 630 insertions(+), 18 deletions(-) create mode 100644 app/generated_circuit_model.py create mode 100644 migrations/versions/109764431420_.py create mode 100644 migrations/versions/2e3851183200_.py create mode 100644 migrations/versions/3c7255be0c00_.py create mode 100644 migrations/versions/4e54aef7eb94_.py create mode 100644 migrations/versions/628de98b3495_.py create mode 100644 migrations/versions/75e93863f19a_.py create mode 100644 migrations/versions/79e91529e693_.py create mode 100644 migrations/versions/978123b2bde5_.py create mode 100644 migrations/versions/b9568b12eb91_.py create mode 100644 migrations/versions/d57a92b02fa3_.py create mode 100644 migrations/versions/d592480df5a0_.py diff --git a/Dockerfile b/Dockerfile index 5d86b60..4dff586 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,10 @@ FROM python:3.9-slim MAINTAINER Marie Salm "marie.salm@iaas.uni-stuttgart.de" +COPY ./requirements.txt /app/requirements.txt WORKDIR /app RUN apt-get update RUN apt-get install -y gcc python3-dev -COPY ./requirements.txt /app/requirements.txt RUN pip install -r requirements.txt COPY . /app @@ -16,4 +16,4 @@ ENV FLASK_ENV=development ENV FLASK_DEBUG=0 RUN echo "python -m flask db upgrade" > /app/startup.sh RUN echo "gunicorn pytket-service:app -b 0.0.0.0:5015 -w 4 --timeout 500 --log-level info" >> /app/startup.sh - +CMD [ "sh", "/app/startup.sh" ] diff --git a/app/__init__.py b/app/__init__.py index 4f91ff0..8fdae49 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -27,12 +27,22 @@ app = Flask(__name__) app.config.from_object(Config) -db = SQLAlchemy(app) +from sqlalchemy import MetaData + +naming_convention = { + "ix": 'ix_%(column_0_label)s', + "uq": "uq_%(table_name)s_%(column_0_name)s", + "ck": "ck_%(table_name)s_%(column_0_name)s", + "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", + "pk": "pk_%(table_name)s" +} +db = SQLAlchemy(app, metadata=MetaData(naming_convention=naming_convention)) migrate = Migrate(app, db) from app import routes, result_model, errors app.app_context().push() app.redis = Redis.from_url(app.config['REDIS_URL'], port=5040) -app.execute_queue = rq.Queue('pytket-service_execute', connection=app.redis, default_timeout=3600) +app.execute_queue = rq.Queue('pytket-service_execute', connection=app.redis, default_timeout=10000) +app.implementation_queue = rq.Queue('pytket-service_implementation_exe', connection=app.redis, default_timeout=10000) app.logger.setLevel(logging.INFO) diff --git a/app/generated_circuit_model.py b/app/generated_circuit_model.py new file mode 100644 index 0000000..713adfe --- /dev/null +++ b/app/generated_circuit_model.py @@ -0,0 +1,37 @@ +# ****************************************************************************** +# Copyright (c) 2024 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 app import db + + +class Generated_Circuit(db.Model): + id = db.Column(db.String(36), primary_key=True) + generated_circuit = db.Column(db.String(1200), default="") + input_params = db.Column(db.String(1200), default="") + original_depth = db.Column(db.Integer) + original_width = db.Column(db.Integer) + original_total_number_of_operations = db.Column(db.Integer) + original_number_of_multi_qubit_gates = db.Column(db.Integer) + original_number_of_measurement_operations = db.Column(db.Integer) + original_number_of_single_qubit_gates = db.Column(db.Integer) + original_multi_qubit_gate_depth = db.Column(db.Integer) + complete = db.Column(db.Boolean, default=False) + + def __repr__(self): + return 'Generated_Circuit {}'.format(self.generated_circuit) diff --git a/app/implementation_handler.py b/app/implementation_handler.py index 57846ac..eff4987 100644 --- a/app/implementation_handler.py +++ b/app/implementation_handler.py @@ -110,15 +110,21 @@ def prepare_code_from_data(data, input_params): return circuit -def prepare_code_from_url(url, input_params, bearer_token: str = ""): +def prepare_code_from_url(url, input_params, bearer_token: str = "", post_processing=False): """Get implementation code from URL. Set input parameters into implementation. Return circuit.""" try: impl = _download_code(url, bearer_token) except (error.HTTPError, error.URLError): return None - circuit = prepare_code_from_data(impl, input_params) - return circuit + print('SEE THIS THING') + print(post_processing) + if not post_processing: + circuit = prepare_code_from_data(impl, input_params) + return circuit + else: + result = prepare_post_processing_code_from_data(impl, input_params) + return result def prepare_code_from_qasm(qasm): @@ -179,3 +185,31 @@ def _download_code(url: str, bearer_token: str = "") -> str: abort(401) return res.read().decode("utf-8") + + +def prepare_post_processing_code_from_data(data, input_params): + """Get implementation code from data. Set input parameters into implementation. Return circuit.""" + temp_dir = tempfile.mkdtemp() + with open(os.path.join(temp_dir, "__init__.py"), "w") as f: + f.write("") + with open(os.path.join(temp_dir, "downloaded_code.py"), "w") as f: + f.write(data) + sys.path.append(temp_dir) + try: + import downloaded_code + + # deletes every attribute from downloaded_code, except __name__, because importlib.reload + # doesn't reset the module's global variables + for attr in dir(downloaded_code): + if attr != "__name__": + delattr(downloaded_code, attr) + + reload(downloaded_code) + if 'post_processing' in dir(downloaded_code): + result = downloaded_code.post_processing(**input_params) + finally: + sys.path.remove(temp_dir) + shutil.rmtree(temp_dir, ignore_errors=True) + if not result: + raise ValueError + return result \ No newline at end of file diff --git a/app/parameters.py b/app/parameters.py index ed9be52..9cacfff 100644 --- a/app/parameters.py +++ b/app/parameters.py @@ -16,6 +16,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # ****************************************************************************** +import json + class ParameterDictionary(dict): """ @@ -25,7 +27,9 @@ class ParameterDictionary(dict): "String": str, "Integer": int, "Float": float, - "Unknown": str + "Unknown": str, + "Array": list, + "FloatArray": list } """ @@ -42,9 +46,13 @@ def __convert_to_typed_parameter(cls, parameter): return None try: - t = ParameterDictionary.__parameter_types[parameter['type']] - value = parameter['rawValue'] - return t(value) + if parameter["type"] == "FloatArray": + value = json.loads(parameter['rawValue']) + return value + else: + t = ParameterDictionary.__parameter_types[parameter['type']] + value = parameter['rawValue'] + return t(value) except: return None diff --git a/app/result_model.py b/app/result_model.py index 4ae16ac..83754bf 100644 --- a/app/result_model.py +++ b/app/result_model.py @@ -26,6 +26,8 @@ class Result(db.Model): shots = db.Column(db.Integer, default=0) result = db.Column(db.String(1200), default="") complete = db.Column(db.Boolean, default=False) + generated_circuit_id = db.Column(db.String(36), db.ForeignKey('generated__circuit.id'), nullable=True) + post_processing_result = db.Column(db.String(1200), default="") def __repr__(self): return 'Result {}'.format(self.result) diff --git a/app/routes.py b/app/routes.py index c1b889e..6df7f99 100644 --- a/app/routes.py +++ b/app/routes.py @@ -18,6 +18,7 @@ # ****************************************************************************** from app import app, implementation_handler, db, parameters +from app.generated_circuit_model import Generated_Circuit from app.result_model import Result from app.tket_handler import get_backend, is_tk_circuit, setup_credentials, tket_analyze_original_circuit, \ tket_transpile_circuit, UnsupportedGateException, TooManyQubitsException, get_depth_without_barrier, \ @@ -29,6 +30,61 @@ import json import base64 +@app.route('/pytket-service/api/v1.0/generate-circuit', methods=['POST']) +def generate_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", "") + impl_data = '' + if input_params: + input_params = parameters.ParameterDictionary(input_params) + + if impl_url is not None and impl_url != "": + impl_url = request.json['impl-url'] + elif 'impl-data' in request.json: + impl_data = base64.b64decode(request.json.get('impl-data').encode()).decode() + else: + abort(400) + + job = app.implementation_queue.enqueue('app.tasks.generate', impl_url=impl_url, impl_data=impl_data, + impl_language=impl_language, input_params=input_params, + bearer_token=bearer_token) + + result = Generated_Circuit(id=job.get_id()) + db.session.add(result) + db.session.commit() + + app.logger.info('Returning HTTP response to client...') + content_location = '/pytket-service/api/v1.0/generated-circuits/' + result.id + response = jsonify({'Location': content_location}) + response.status_code = 202 + response.headers['Location'] = content_location + response.autocorrect_location_header = True + return response + + +@app.route('/pytket-service/api/v1.0/generated-circuits/', methods=['GET']) +def get_generated_circuit(generated_circuit_id): + """Return result when it is available.""" + generated_circuit = Generated_Circuit.query.get(generated_circuit_id) + if generated_circuit.complete: + input_params_dict = json.loads(generated_circuit.input_params) + return jsonify( + {'id': generated_circuit.id, 'complete': generated_circuit.complete, 'input_params': input_params_dict, + 'generated-circuit': generated_circuit.generated_circuit, + 'original-depth': generated_circuit.original_depth, 'original-width': generated_circuit.original_width, + 'original-total-number-of-operations': generated_circuit.original_total_number_of_operations, + 'original-number-of-multi-qubit-gates': generated_circuit.original_number_of_multi_qubit_gates, + 'original-number-of-measurement-operations': generated_circuit.original_number_of_measurement_operations, + 'original-number-of-single-qubit-gates': generated_circuit.original_number_of_single_qubit_gates, + 'original-multi-qubit-gate-depth': generated_circuit.original_multi_qubit_gate_depth}), 200 + else: + return jsonify({'id': generated_circuit.id, 'complete': generated_circuit.complete}), 200 + @app.route('/pytket-service/api/v1.0/analyze-original-circuit', methods=['POST']) def analyze_original_circuit(): @@ -252,6 +308,7 @@ def execute_circuit(): provider = request.json["provider"] qpu_name = request.json['qpu-name'] + correlation_id = request.json.get('correlation-id', None) impl_url = request.json.get('impl-url') bearer_token = request.json.get("bearer-token", "") impl_language = request.json.get("impl-language") @@ -263,7 +320,7 @@ def execute_circuit(): input_params = parameters.ParameterDictionary(input_params) shots = request.json.get('shots', 1024) - job = app.execute_queue.enqueue('app.tasks.execute', impl_url=impl_url, impl_data=impl_data, + job = app.execute_queue.enqueue('app.tasks.execute', correlation_id=correlation_id, impl_url=impl_url, impl_data=impl_data, transpiled_qasm=transpiled_qasm, transpiled_quil=transpiled_quil, qpu_name=qpu_name, input_params=input_params, shots=shots, provider=provider, @@ -287,8 +344,16 @@ def get_result(result_id): result = Result.query.get(result_id) if result.complete: result_dict = json.loads(result.result) - return jsonify({'id': result.id, 'complete': result.complete, 'result': result_dict, - 'backend': result.backend, 'shots': result.shots}), 200 + if result.post_processing_result: + post_processing_result_dict = json.loads(result.post_processing_result) + return jsonify( + {'id': result.id, 'complete': result.complete, 'result': result_dict, 'backend': result.backend, + 'shots': result.shots, 'generated-circuit-id': result.generated_circuit_id, + 'post-processing-result': post_processing_result_dict}), 200 + else: + return jsonify( + {'id': result.id, 'complete': result.complete, 'result': result_dict, 'backend': result.backend, + 'shots': result.shots}), 200 else: return jsonify({'id': result.id, 'complete': result.complete}), 200 diff --git a/app/tasks.py b/app/tasks.py index ef15218..f2ab67e 100644 --- a/app/tasks.py +++ b/app/tasks.py @@ -18,8 +18,10 @@ # ****************************************************************************** from qiskit_ibm_runtime import Sampler -from app import implementation_handler, db +from app import implementation_handler, db, app, tket_handler from rq import get_current_job + +from app.generated_circuit_model import Generated_Circuit from app.tket_handler import tket_transpile_circuit, UnsupportedGateException, get_backend, setup_credentials from app.result_model import Result import json @@ -57,7 +59,43 @@ def rename_qreg_lowercase(circuit, *regs): return circuit_from_qasm_str(qasm) -def execute(impl_url, impl_data, transpiled_qasm, transpiled_quil, input_params, provider, qpu_name, impl_language, +def generate(impl_url, impl_data, impl_language, input_params, bearer_token): + app.logger.info("Starting generate task...") + job = get_current_job() + + generated_circuit_code = None + if impl_url: + generated_circuit_code = implementation_handler.prepare_code_from_url(impl_url, input_params, bearer_token) + elif impl_data: + generated_circuit_code = implementation_handler.prepare_code_from_data(impl_data, input_params) + else: + generated_circuit_object = Generated_Circuit.query.get(job.get_id()) + generated_circuit_object.generated_circuit = json.dumps({'error': 'generating circuit failed'}) + generated_circuit_object.complete = True + db.session.commit() + + if generated_circuit_code: + non_transpiled_depth_old = 0 + generated_circuit_object = Generated_Circuit.query.get(job.get_id()) + generated_circuit_object.generated_circuit = generated_circuit_code.qasm() + + non_transpiled_depth = generated_circuit_code.depth() + while non_transpiled_depth_old < non_transpiled_depth: + non_transpiled_depth_old = non_transpiled_depth + generated_circuit_code = generated_circuit_code.decompose() + non_transpiled_depth = generated_circuit_code.depth() + + generated_circuit_code, generated_circuit_object.original_width, generated_circuit_object.original_depth, generated_circuit_object.original_multi_qubit_gate_depth, generated_circuit_object.original_total_number_of_operations, generated_circuit_object.original_number_of_multi_qubit_gates, generated_circuit_object.original_number_of_measurement_operations, generated_circuit_object.original_number_of_single_qubit_gates = tket_handler.tket_analyze_original_circuit( + generated_circuit_code, impl_language=impl_language, short_impl_name="Generated circuit", logger=app.logger.info, + precompile_circuit=False) + + generated_circuit_object.input_params = json.dumps(input_params) + app.logger.info(f"Received input params for circuit generation: {generated_circuit_object.input_params}") + generated_circuit_object.complete = True + db.session.commit() + + +def execute(correlation_id, impl_url, impl_data, transpiled_qasm, transpiled_quil, input_params, provider, qpu_name, impl_language, shots, bearer_token: str = ""): """Create database entry for result. Get implementation code, prepare it, and execute it. Save result in db""" job = get_current_job() @@ -67,7 +105,7 @@ def execute(impl_url, impl_data, transpiled_qasm, transpiled_quil, input_params, # Get the backend backend = get_backend(provider, qpu_name) - if not transpiled_qasm and not transpiled_quil: + if (impl_url or impl_data) and not correlation_id: circuit, short_impl_name = implementation_handler.prepare_code(impl_url, impl_data, impl_language, input_params, bearer_token) # Transpile the circuit for the backend try: @@ -138,5 +176,22 @@ def fixed_run(self, **kwargs): counts = job_result.get_counts() print(counts) result.result = convert_counts_to_json(counts) + # check if implementation contains post processing of execution results that has to be executed + if correlation_id and (impl_url or impl_data): + result.generated_circuit_id = correlation_id + # prepare input data containing execution results and initial input params for generating the circuit + generated_circuit = Generated_Circuit.query.get(correlation_id) + input_params_for_post_processing = json.loads(generated_circuit.input_params) + input_params_for_post_processing['counts'] = json.loads(result.result) + + if impl_url: + post_p_result = implementation_handler.prepare_code_from_url(url=impl_url, + input_params=input_params_for_post_processing, + bearer_token=bearer_token, + post_processing=True) + elif impl_data: + post_p_result = implementation_handler.prepare_post_processing_code_from_data(data=impl_data, + input_params=input_params_for_post_processing) + result.post_processing_result = json.loads(post_p_result) result.complete = True db.session.commit() diff --git a/app/tket_handler.py b/app/tket_handler.py index 8f7009d..38a047f 100644 --- a/app/tket_handler.py +++ b/app/tket_handler.py @@ -24,7 +24,7 @@ import pytket.extensions.qiskit from braket.aws.aws_session import AwsSession from pytket.extensions.braket import BraketBackend -from pytket.extensions.qiskit import qiskit_to_tk, IBMQBackend, set_ibmq_config +from pytket.extensions.qiskit import qiskit_to_tk, IBMQBackend, set_ibmq_config, AerBackend from pytket.extensions.pyquil import pyquil_to_tk, tk_to_pyquil from pytket.extensions.ionq import IonQBackend, set_ionq_config from pytket import Circuit as TKCircuit @@ -35,6 +35,7 @@ from qiskit.compiler import transpile from qiskit import IBMQ import qiskit.circuit.library as qiskit_gates +from qiskit_aer import AerSimulator AWS_BRAKET_HOSTED_PROVIDERS = ['rigetti', 'aws'] # Get environment variables @@ -135,6 +136,8 @@ def get_backend(provider, qpu): if provider.lower() == "ibmq": try: + if (qpu == 'ibmq_qasm_simulator') or (qpu == 'aer_simulator'): + return AerBackend() return IBMQBackend(qpu) except ValueError: return None diff --git a/migrations/versions/109764431420_.py b/migrations/versions/109764431420_.py new file mode 100644 index 0000000..92de52c --- /dev/null +++ b/migrations/versions/109764431420_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 109764431420 +Revises: b9568b12eb91 +Create Date: 2024-06-19 16:34:22.440748 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '109764431420' +down_revision = 'b9568b12eb91' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.create_foreign_key(batch_op.f('fk_result_generated_circuit_id_generated__circuit'), 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(batch_op.f('fk_result_generated_circuit_id_generated__circuit'), type_='foreignkey') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/migrations/versions/2e3851183200_.py b/migrations/versions/2e3851183200_.py new file mode 100644 index 0000000..41c21ab --- /dev/null +++ b/migrations/versions/2e3851183200_.py @@ -0,0 +1,36 @@ +"""empty message + +Revision ID: 2e3851183200 +Revises: 3c7255be0c00 +Create Date: 2024-06-19 14:30:39.002044 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '2e3851183200' +down_revision = '3c7255be0c00' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.add_column(sa.Column('post_processing_result', sa.String(length=1200), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('post_processing_result') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/migrations/versions/3c7255be0c00_.py b/migrations/versions/3c7255be0c00_.py new file mode 100644 index 0000000..a22b24e --- /dev/null +++ b/migrations/versions/3c7255be0c00_.py @@ -0,0 +1,51 @@ +"""empty message + +Revision ID: 3c7255be0c00 +Revises: 6201b3fee644 +Create Date: 2024-06-19 14:29:45.725852 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '3c7255be0c00' +down_revision = '6201b3fee644' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('generated__circuit', + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('generated_circuit', sa.String(length=1200), nullable=True), + sa.Column('input_params', sa.String(length=1200), nullable=True), + sa.Column('original_depth', sa.Integer(), nullable=True), + sa.Column('original_width', sa.Integer(), nullable=True), + sa.Column('original_total_number_of_operations', sa.Integer(), nullable=True), + sa.Column('original_number_of_multi_qubit_gates', sa.Integer(), nullable=True), + sa.Column('original_number_of_measurement_operations', sa.Integer(), nullable=True), + sa.Column('original_number_of_single_qubit_gates', sa.Integer(), nullable=True), + sa.Column('original_multi_qubit_gate_depth', sa.Integer(), nullable=True), + sa.Column('complete', sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.add_column(sa.Column('post_processing_result', sa.String(length=1200), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('post_processing_result') + batch_op.drop_column('generated_circuit_id') + + op.drop_table('generated__circuit') + # ### end Alembic commands ### diff --git a/migrations/versions/4e54aef7eb94_.py b/migrations/versions/4e54aef7eb94_.py new file mode 100644 index 0000000..9d231e0 --- /dev/null +++ b/migrations/versions/4e54aef7eb94_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 4e54aef7eb94 +Revises: 79e91529e693 +Create Date: 2024-06-19 16:27:40.167114 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '4e54aef7eb94' +down_revision = '79e91529e693' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/migrations/versions/628de98b3495_.py b/migrations/versions/628de98b3495_.py new file mode 100644 index 0000000..963d61b --- /dev/null +++ b/migrations/versions/628de98b3495_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 628de98b3495 +Revises: 75e93863f19a +Create Date: 2024-06-19 15:48:28.156949 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '628de98b3495' +down_revision = '75e93863f19a' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/migrations/versions/75e93863f19a_.py b/migrations/versions/75e93863f19a_.py new file mode 100644 index 0000000..4840e08 --- /dev/null +++ b/migrations/versions/75e93863f19a_.py @@ -0,0 +1,32 @@ +"""empty message + +Revision ID: 75e93863f19a +Revises: d57a92b02fa3 +Create Date: 2024-06-19 15:47:46.687859 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '75e93863f19a' +down_revision = 'd57a92b02fa3' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('post_processing_result', sa.String(length=1200), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_column('post_processing_result') + + # ### end Alembic commands ### diff --git a/migrations/versions/79e91529e693_.py b/migrations/versions/79e91529e693_.py new file mode 100644 index 0000000..9692d62 --- /dev/null +++ b/migrations/versions/79e91529e693_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 79e91529e693 +Revises: 978123b2bde5 +Create Date: 2024-06-19 16:20:02.906672 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '79e91529e693' +down_revision = '978123b2bde5' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/migrations/versions/978123b2bde5_.py b/migrations/versions/978123b2bde5_.py new file mode 100644 index 0000000..89c6ffc --- /dev/null +++ b/migrations/versions/978123b2bde5_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 978123b2bde5 +Revises: d592480df5a0 +Create Date: 2024-06-19 16:19:35.520988 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '978123b2bde5' +down_revision = 'd592480df5a0' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/migrations/versions/b9568b12eb91_.py b/migrations/versions/b9568b12eb91_.py new file mode 100644 index 0000000..e48bba5 --- /dev/null +++ b/migrations/versions/b9568b12eb91_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: b9568b12eb91 +Revises: 4e54aef7eb94 +Create Date: 2024-06-19 16:33:36.938749 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'b9568b12eb91' +down_revision = '4e54aef7eb94' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/migrations/versions/d57a92b02fa3_.py b/migrations/versions/d57a92b02fa3_.py new file mode 100644 index 0000000..7bb21dc --- /dev/null +++ b/migrations/versions/d57a92b02fa3_.py @@ -0,0 +1,36 @@ +"""empty message + +Revision ID: d57a92b02fa3 +Revises: 2e3851183200 +Create Date: 2024-06-19 15:41:37.468426 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'd57a92b02fa3' +down_revision = '2e3851183200' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.add_column(sa.Column('post_processing_result', sa.String(length=1200), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('post_processing_result') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/migrations/versions/d592480df5a0_.py b/migrations/versions/d592480df5a0_.py new file mode 100644 index 0000000..d7c91b8 --- /dev/null +++ b/migrations/versions/d592480df5a0_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: d592480df5a0 +Revises: 628de98b3495 +Create Date: 2024-06-19 16:11:48.459864 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'd592480df5a0' +down_revision = '628de98b3495' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.add_column(sa.Column('generated_circuit_id', sa.String(length=36), nullable=True)) + batch_op.create_foreign_key(None, 'generated__circuit', ['generated_circuit_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('result', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('generated_circuit_id') + + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index 42eb970..c3a5296 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ amazon-braket-sdk==1.62.1 aniso8601==9.0.1 antlr4-python3-runtime==4.9.2 anyio==4.1.0 +apispec==6.6.1 async-timeout==4.0.3 attrs==21.4.0 backoff==2.2.1 @@ -26,6 +27,7 @@ exceptiongroup==1.2.0 Flask==3.0.0 Flask-Migrate==4.0.5 Flask-RESTful==0.3.10 +flask-smorest==0.44.0 Flask-SQLAlchemy==3.1.1 graphviz==0.20.1 greenlet==3.0.1 @@ -45,6 +47,7 @@ lark==0.11.3 lark-parser==0.12.0 Mako==1.3.0 MarkupSafe==2.1.3 +marshmallow==3.21.3 mpmath==1.3.0 msgpack==1.0.7 mypy-extensions==1.0.0 @@ -58,6 +61,7 @@ opt-einsum==3.3.0 oqpy==0.2.1 packaging==23.2 pbr==6.0.0 +pipdeptree==2.13.1 ply==3.11 psutil==5.9.6 pycparser==2.21 @@ -113,6 +117,7 @@ types-s3transfer==0.7.0 types-urllib3==1.26.25.14 typing_extensions==4.8.0 urllib3==1.26.18 +webargs==8.4.0 websocket-client==1.6.4 websockets==12.0 Werkzeug==3.0.1 From 43adde6194d67b50065347602fe35d56eb79ce4a Mon Sep 17 00:00:00 2001 From: Marie Salm Date: Thu, 20 Jun 2024 10:43:35 +0200 Subject: [PATCH 2/8] add openapi documentation --- app/__init__.py | 9 ++ app/config.py | 20 +++ app/controller/__init__.py | 10 ++ .../analysis_original_circuit/__init__.py | 1 + .../analysis_original_circuit_controller.py | 32 ++++ app/controller/execute/__init__.py | 1 + app/controller/execute/execute_controller.py | 79 ++++++++++ app/controller/generate_circuit/__init__.py | 1 + .../generate_circuit_controller.py | 35 +++++ app/controller/generated_circuit/__init__.py | 1 + .../generated_circuit_controller.py | 12 ++ app/controller/result/__init__.py | 1 + app/controller/result/result_controller.py | 12 ++ app/controller/transpile/__init__.py | 1 + .../transpile/transpile_controller.py | 76 +++++++++ app/model/algorithm_request.py | 59 +++++++ app/model/calculation_request.py | 33 ++++ app/model/circuit_response.py | 147 ++++++++++++++++++ 18 files changed, 530 insertions(+) create mode 100644 app/controller/__init__.py create mode 100644 app/controller/analysis_original_circuit/__init__.py create mode 100644 app/controller/analysis_original_circuit/analysis_original_circuit_controller.py create mode 100644 app/controller/execute/__init__.py create mode 100644 app/controller/execute/execute_controller.py create mode 100644 app/controller/generate_circuit/__init__.py create mode 100644 app/controller/generate_circuit/generate_circuit_controller.py create mode 100644 app/controller/generated_circuit/__init__.py create mode 100644 app/controller/generated_circuit/generated_circuit_controller.py create mode 100644 app/controller/result/__init__.py create mode 100644 app/controller/result/result_controller.py create mode 100644 app/controller/transpile/__init__.py create mode 100644 app/controller/transpile/transpile_controller.py create mode 100644 app/model/algorithm_request.py create mode 100644 app/model/calculation_request.py create mode 100644 app/model/circuit_response.py diff --git a/app/__init__.py b/app/__init__.py index 8fdae49..35c0fab 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -40,9 +40,18 @@ migrate = Migrate(app, db) from app import routes, result_model, errors +from app.controller import register_blueprints +from flask_smorest import Api app.app_context().push() app.redis = Redis.from_url(app.config['REDIS_URL'], port=5040) app.execute_queue = rq.Queue('pytket-service_execute', connection=app.redis, default_timeout=10000) app.implementation_queue = rq.Queue('pytket-service_implementation_exe', connection=app.redis, default_timeout=10000) app.logger.setLevel(logging.INFO) + +api = Api(app) +register_blueprints(api) + +@app.route("/") +def heartbeat(): + return '

pytket-service is running

View the API Docs here

' diff --git a/app/config.py b/app/config.py index 188a256..06eb225 100644 --- a/app/config.py +++ b/app/config.py @@ -27,3 +27,23 @@ class Config(object): SQLALCHEMY_TRACK_MODIFICATIONS = False REDIS_URL = os.environ.get('REDIS_URL') or 'redis://localhost:5040' + + API_TITLE = "pytket-service" + API_VERSION = "0.1" + OPENAPI_VERSION = "3.0.2" + OPENAPI_URL_PREFIX = "/api" + OPENAPI_SWAGGER_UI_PATH = "/swagger-ui" + OPENAPI_SWAGGER_UI_VERSION = "3.24.2" + OPENAPI_SWAGGER_UI_URL = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.24.2/" + + API_SPEC_OPTIONS = { + "info": { + "description": "This is the API Specification of the pytket-service (" + "https://github.com/UST-QuAntiL/pytket-service).", + }, + "license": {"name": "Apache v2 License"}, + } + + @staticmethod + def init_app(app): + pass \ No newline at end of file diff --git a/app/controller/__init__.py b/app/controller/__init__.py new file mode 100644 index 0000000..d70a896 --- /dev/null +++ b/app/controller/__init__.py @@ -0,0 +1,10 @@ +from app.controller import transpile, execute, analysis_original_circuit, result, generated_circuit, generate_circuit + +MODULES = (transpile, execute, analysis_original_circuit, result, + generated_circuit, generate_circuit) + + +def register_blueprints(api): + """Initialize application with all modules""" + for module in MODULES: + api.register_blueprint(module.blp) diff --git a/app/controller/analysis_original_circuit/__init__.py b/app/controller/analysis_original_circuit/__init__.py new file mode 100644 index 0000000..d92042d --- /dev/null +++ b/app/controller/analysis_original_circuit/__init__.py @@ -0,0 +1 @@ +from app.controller.analysis_original_circuit.analysis_original_circuit_controller import blp \ No newline at end of file diff --git a/app/controller/analysis_original_circuit/analysis_original_circuit_controller.py b/app/controller/analysis_original_circuit/analysis_original_circuit_controller.py new file mode 100644 index 0000000..9089325 --- /dev/null +++ b/app/controller/analysis_original_circuit/analysis_original_circuit_controller.py @@ -0,0 +1,32 @@ +from flask_smorest import Blueprint + +from app import routes +from app.model.calculation_request import (AnalysisOriginalCircuitRequest, AnalysisOriginalCircuitRequestSchema) +from app.model.circuit_response import (AnalysisOriginalCircuitResponseSchema) + +blp = Blueprint("Analysis of Original Circuit", __name__, description="Request an analysis of the original circuit.", ) + + +@blp.route("/pytket-service/api/v1.0/analyze-original-circuit", methods=["POST"]) +@blp.arguments(AnalysisOriginalCircuitRequestSchema, description='''\ + \"input-params\" should be of the form: + \"input-params\":{ + \"PARAM-NAME-1\": { + \"rawValue\": \"YOUR-VALUE-1\", + \"type\": \"Integer\" + }, + \"PARAM-NAME-2\": { + \"rawValue\": \"YOUR-VALUE-2\", + \"type\": \"String\" + }''', example={ + "impl-url": "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py", + "impl-language": "qiskit", + "qpu-name": "aer_simulator", + "provider": "ibmq", + "input-params": {}}, + +) +@blp.response(200, AnalysisOriginalCircuitResponseSchema) +def encoding(json: AnalysisOriginalCircuitRequest): + if json: + return routes.analyze_original_circuit(json) diff --git a/app/controller/execute/__init__.py b/app/controller/execute/__init__.py new file mode 100644 index 0000000..f6c36c8 --- /dev/null +++ b/app/controller/execute/__init__.py @@ -0,0 +1 @@ +from app.controller.execute.execute_controller import blp \ No newline at end of file diff --git a/app/controller/execute/execute_controller.py b/app/controller/execute/execute_controller.py new file mode 100644 index 0000000..afd91e2 --- /dev/null +++ b/app/controller/execute/execute_controller.py @@ -0,0 +1,79 @@ +from flask_smorest import Blueprint + +from app import routes +from app.model.circuit_response import ( + ExecuteResponseSchema +) +from app.model.algorithm_request import ( + ExecuteRequest, + ExecuteRequestSchema +) + +blp = Blueprint( + "Execute", + __name__, + description="Send implementation, input, QPU information, and your access token to the API to " + "execute your circuit and get the result.", +) + + +@blp.route("/pytket-service/api/v1.0/execute", methods=["POST"]) +@blp.doc(description="*Note*: \"token\" should either be in \"input-params\" or extra. Both variants are combined " + "here for illustration purposes. *Note*: \"url\", \"hub\", \"group\", \"project\" are optional " + "such that otherwise the standard values are used.") +@blp.arguments( + ExecuteRequestSchema, + description='''\ + Execution via URL: + \"impl-url\": \"URL-OF-IMPLEMENTATION\" + Execution via data: + \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\" + Execution via transpiled OpenQASM String: + \"transpiled-qasm\":\"TRANSPILED-QASM-STRING\" + for Batch Execution of multiple circuits use: + \"impl-url\": [\"URL-OF-IMPLEMENTATION-1\", \"URL-OF-IMPLEMENTATION-2\"] + the \"input-params\"are of the form: + \"input-params\": { + \"PARAM-NAME-1\": { + \"rawValue\": \"YOUR-VALUE-1\", + \"type\": \"Integer\" + }, + \"PARAM-NAME-2\": { + \"rawValue\": \"YOUR-VALUE-2\", + \"type\": \"String\" + }, + ... + \"token\": { + \"rawValue\": \"YOUR-IBMQ-TOKEN\", + \"type\": \"Unknown\" + }, + \"url\": { + \"rawValue\": \"YOUR-IBMQ-AUTHENTICATION-URL\", + \"type\": \"Unknown\" + }, + \"hub\": { + \"rawValue\": \"YOUR-IBMQ-HUB\", + \"type\": \"Unknown\" + }, + \"group\": { + \"rawValue\": \"YOUR-IBMQ-GROUP\", + \"type\": \"Unknown\" + }, + \"project\": { + \"rawValue\": \"YOUR-IBMQ-PROJECT\", + \"type\": \"Unknown\" + }''', + example={ + "impl-url": "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations" + "/Grover-SAT/grover-fix-sat-qiskit.py", + "qpu-name": "aer_simulator", + "provider": "ibmq", + "impl-language": "qiskit", + "token": "YOUR-IBMQ-TOKEN", + "input-params": {} + } +) +@blp.response(200, ExecuteResponseSchema, description="Returns a content location for the result. Access it via GET") +def encoding(json: ExecuteRequest): + if json: + return routes.execute_circuit() diff --git a/app/controller/generate_circuit/__init__.py b/app/controller/generate_circuit/__init__.py new file mode 100644 index 0000000..59c7505 --- /dev/null +++ b/app/controller/generate_circuit/__init__.py @@ -0,0 +1 @@ +from app.controller.generate_circuit.generate_circuit_controller import blp diff --git a/app/controller/generate_circuit/generate_circuit_controller.py b/app/controller/generate_circuit/generate_circuit_controller.py new file mode 100644 index 0000000..6000c29 --- /dev/null +++ b/app/controller/generate_circuit/generate_circuit_controller.py @@ -0,0 +1,35 @@ +from flask_smorest import Blueprint + +from app import routes +from app.model.algorithm_request import (GenerateCircuitRequest, GenerateCircuitRequestSchema) +from app.model.circuit_response import (GenerateCircuitResponseSchema) + +blp = Blueprint("Generate Circuit", __name__, description="Send implementation and input parameters to the API to " + "generate your circuit and get its properties.", ) + + +@blp.route("/pytket-service/api/v1.0/generate-circuit", methods=["POST"]) +@blp.arguments(GenerateCircuitRequestSchema, description='''\ + Generation via URL: + \"impl-url\": \"URL-OF-IMPLEMENTATION\" + Generation via data: + \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\" + the \"input-params\"are of the form: + \"input-params\": { + \"PARAM-NAME-1\": { + \"rawValue\": \"YOUR-VALUE-1\", + \"type\": \"Integer\" + }, + \"PARAM-NAME-2\": { + \"rawValue\": \"YOUR-VALUE-2\", + \"type\": \"String\" + }, + ... + ''', example={ + "impl-url": "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations" + "/Grover-SAT/grover-fix-sat-qiskit.py", "impl-language": "qiskit", "input-params": {}}) +@blp.response(200, GenerateCircuitResponseSchema, + description="Returns a content location for the generated circuit and its properties. Access it via GET") +def encoding(json: GenerateCircuitRequest): + if json: + return routes.generate_circuit() diff --git a/app/controller/generated_circuit/__init__.py b/app/controller/generated_circuit/__init__.py new file mode 100644 index 0000000..6fe0e0e --- /dev/null +++ b/app/controller/generated_circuit/__init__.py @@ -0,0 +1 @@ +from app.controller.generated_circuit.generated_circuit_controller import blp diff --git a/app/controller/generated_circuit/generated_circuit_controller.py b/app/controller/generated_circuit/generated_circuit_controller.py new file mode 100644 index 0000000..2bbf3c0 --- /dev/null +++ b/app/controller/generated_circuit/generated_circuit_controller.py @@ -0,0 +1,12 @@ +from flask_smorest import Blueprint + +from app.model.circuit_response import (GeneratedCircuitsResponseSchema) + +blp = Blueprint("Generated Circuits", __name__, description="Request a generated circuit and its properties.", ) + + +@blp.route("/pytket-service/api/v1.0/generated-circuits/", methods=["GET"]) +@blp.response(200, GeneratedCircuitsResponseSchema) +def encoding(json): + if json: + return diff --git a/app/controller/result/__init__.py b/app/controller/result/__init__.py new file mode 100644 index 0000000..317b69c --- /dev/null +++ b/app/controller/result/__init__.py @@ -0,0 +1 @@ +from app.controller.result.result_controller import blp diff --git a/app/controller/result/result_controller.py b/app/controller/result/result_controller.py new file mode 100644 index 0000000..abd2ce9 --- /dev/null +++ b/app/controller/result/result_controller.py @@ -0,0 +1,12 @@ +from flask_smorest import Blueprint + +from app.model.circuit_response import (ResultsResponseSchema) + +blp = Blueprint("Results", __name__, description="Get execution results of an executed circuit.", ) + + +@blp.route("/pytket-service/api/v1.0/results/", methods=["GET"]) +@blp.response(200, ResultsResponseSchema) +def encoding(json): + if json: + return diff --git a/app/controller/transpile/__init__.py b/app/controller/transpile/__init__.py new file mode 100644 index 0000000..e619433 --- /dev/null +++ b/app/controller/transpile/__init__.py @@ -0,0 +1 @@ +from app.controller.transpile.transpile_controller import blp diff --git a/app/controller/transpile/transpile_controller.py b/app/controller/transpile/transpile_controller.py new file mode 100644 index 0000000..445c260 --- /dev/null +++ b/app/controller/transpile/transpile_controller.py @@ -0,0 +1,76 @@ +from flask_smorest import Blueprint + +from app import routes +from app.model.circuit_response import ( + TranspileResponseSchema, +) +from app.model.algorithm_request import ( + TranspileRequestSchema, + TranspileRequest, +) + +blp = Blueprint( + "Transpile", + __name__, + description="Send implementation, input, QPU information, and your access token to the API to get " + "analyzed properties of the transpiled circuit itself.", +) + + +@blp.route("/pytket-service/api/v1.0/transpile", methods=["POST"]) +@blp.doc(description="*Note*: \"token\" should either be in \"input-params\" or extra. *Note*: \"url\", \"hub\", " + "\"group\", \"project\" are optional such that otherwise the standard values are used.") +@blp.arguments( + TranspileRequestSchema, + description='''\ + Transpile via URL: + \"impl-url\": \"URL-OF-IMPLEMENTATION\" + Transpile via data: + \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\" + Transpile via OpenQASM-String + \"qasm-string\": \"OpenQASM String\" + the \"input-params\"are of the form: + \"input-params\": { + \"PARAM-NAME-1\": { + \"rawValue\": \"YOUR-VALUE-1\", + \"type\": \"Integer\" + }, + \"PARAM-NAME-2\": { + \"rawValue\": \"YOUR-VALUE-2\", + \"type\": \"String\" + }, + ... + \"token\": { + \"rawValue\": \"YOUR-IBMQ-TOKEN\", + \"type\": \"Unknown\" + }, + \"url\": { + \"rawValue\": \"YOUR-IBMQ-AUTHENTICATION-URL\", + \"type\": \"Unknown\" + }, + \"hub\": { + \"rawValue\": \"YOUR-IBMQ-HUB\", + \"type\": \"Unknown\" + }, + \"group\": { + \"rawValue\": \"YOUR-IBMQ-GROUP\", + \"type\": \"Unknown\" + }, + \"project\": { + \"rawValue\": \"YOUR-IBMQ-PROJECT\", + \"type\": \"Unknown\" + }''', + example={ + "impl-url": "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations" + "/Grover-SAT/grover-fix-sat-qiskit.py", + "qpu-name": "ibmq_qasm_simulator", + "impl-language": "qiskit", + "token": "YOUR-IBMQ-TOKEN", + "input-params": {} + }, + +) +@blp.response(200, TranspileResponseSchema) +def encoding(json: TranspileRequest): + if json: + return routes.transpile_circuit(json) diff --git a/app/model/algorithm_request.py b/app/model/algorithm_request.py new file mode 100644 index 0000000..f95d6e9 --- /dev/null +++ b/app/model/algorithm_request.py @@ -0,0 +1,59 @@ +import marshmallow as ma + + +class GenerateCircuitRequest: + def __init__(self, impl_url, impl_language, input_params): + self.impl_url = impl_url + self.impl_language = impl_language + self.input_params = input_params + + +class GenerateCircuitRequestSchema(ma.Schema): + impl_url = ma.fields.String() + impl_language = ma.fields.String() + input_params = ma.fields.List(ma.fields.String()) + + +class TranspileRequest: + def __init__(self, impl_url, impl_language, qpu_name, provider, input_params, token): + self.impl_url = impl_url + self.impl_language = impl_language + self.qpu_name = qpu_name + self.provider = provider + self.input_params = input_params + self.token = token + + +class TranspileRequestSchema(ma.Schema): + impl_url = ma.fields.String() + impl_language = ma.fields.String() + qpu_name = ma.fields.String() + provider = ma.fields.String() + input_params = ma.fields.List(ma.fields.String()) + token = ma.fields.String() + + +class ExecuteRequest: + def __init__(self, impl_url, impl_language, qpu_name, provider, noise_model, only_measurement_errors, input_params, token, + correlation_id, post_processing_result): + self.impl_url = impl_url + self.impl_language = impl_language + self.qpu_name = qpu_name + self.provider = provider + self.noise_model = noise_model + self.only_measurement_errors = only_measurement_errors + self.input_params = input_params + self.token = token + self.correlation_id = correlation_id + + +class ExecuteRequestSchema(ma.Schema): + impl_url = ma.fields.String() + impl_language = ma.fields.String() + qpu_name = ma.fields.String() + provider = ma.fields.String() + input_params = ma.fields.List(ma.fields.String()) + token = ma.fields.String() + noise_model = ma.fields.Str(required=False) + only_measurement_errors = ma.fields.Boolean(required=False) + correlation_id = ma.fields.String() diff --git a/app/model/calculation_request.py b/app/model/calculation_request.py new file mode 100644 index 0000000..c7d9ebe --- /dev/null +++ b/app/model/calculation_request.py @@ -0,0 +1,33 @@ +# ****************************************************************************** +# Copyright (c) 2024 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 marshmallow as ma + + +class AnalysisOriginalCircuitRequest: + def __init__(self, impl_url, impl_language, input_params): + self.impl_url = impl_url + self.impl_language = impl_language + self.input_params = input_params + + +class AnalysisOriginalCircuitRequestSchema(ma.Schema): + impl_url = ma.fields.String() + impl_language = ma.fields.String() + input_params = ma.fields.List(ma.fields.String()) diff --git a/app/model/circuit_response.py b/app/model/circuit_response.py new file mode 100644 index 0000000..fd188dd --- /dev/null +++ b/app/model/circuit_response.py @@ -0,0 +1,147 @@ +import marshmallow as ma + + +class CircuitResponseSchema(ma.Schema): + circuit = ma.fields.String() + circuit_type = ma.fields.String() + n_qubits = ma.fields.Int() + depth = ma.fields.Int() + timestamp = ma.fields.String() + + @property + def input(self): + raise NotImplementedError + + +class GeneratedCircuitsResponse: + def __init__(self, original_depth, original_multi_qubit_gate_depth, original_number_of_measurement_operations, + original_number_of_multi_qubit_gates, original_number_of_single_qubit_gates, + original_total_number_of_operations, generated_circuit, original_width): + super().__init__() + self.original_depth = original_depth + self.original_multi_qubit_gate_depth = original_multi_qubit_gate_depth + self.original_number_of_measurement_operations = original_number_of_measurement_operations + self.original_number_of_multi_qubit_gates = original_number_of_multi_qubit_gates + self.original_number_of_single_qubit_gates = original_number_of_single_qubit_gates + self.original_total_number_of_operations = original_total_number_of_operations + self.generated_circuit = generated_circuit + self.original_width = original_width + + def to_json(self): + json_generate_circuit_response = {"original-depth": self.original_depth, + "original-multi-qubit-gate-depth": self.original_multi_qubit_gate_depth, + "original-number-of-measurement-operations": self.original_number_of_measurement_operations, + "original-number-of-multi_qubit-gates": self.original_number_of_multi_qubit_gates, + "original-number-of-single-qubit-gates": self.original_number_of_single_qubit_gates, + "original-total-number-of-operations": self.original_total_number_of_operations, + "generated-circuit": self.generated_circuit, + "original-width": self.original_width, } + return json_generate_circuit_response + + +class GeneratedCircuitsResponseSchema(ma.Schema): + original_depth = ma.fields.Int() + original_multi_qubit_gate_depth = ma.fields.Int() + original_number_of_measurement_operations = ma.fields.Int() + original_number_of_multi_qubit_gates = ma.fields.Int() + original_number_of_single_qubit_gates = ma.fields.Int() + original_total_number_of_operations = ma.fields.Int() + generated_circuit = ma.fields.String() + original_width = ma.fields.Int() + + @property + def input(self): + raise NotImplementedError + + +class TranspileResponse: + def __init__(self, depth, multi_qubit_gate_depth, number_of_measurement_operations, number_of_multi_qubit_gates, + number_of_single_qubit_gates, total_number_of_operations, transpiled_qasm, width): + super().__init__() + self.depth = depth + self.multi_qubit_gate_depth = multi_qubit_gate_depth + self.number_of_measurement_operations = number_of_measurement_operations + self.number_of_multi_qubit_gates = number_of_multi_qubit_gates + self.number_of_single_qubit_gates = number_of_single_qubit_gates + self.total_number_of_operations = total_number_of_operations + self.transpiled_qasm = transpiled_qasm + self.width = width + + def to_json(self): + json_transpile_response = {"depth": self.depth, "multi_qubit_gate_depth": self.multi_qubit_gate_depth, + "number_of_measurement_operations": self.number_of_measurement_operations, + "number_of_multi_qubit_gates": self.number_of_multi_qubit_gates, + "number_of_single_qubit_gates": self.number_of_single_qubit_gates, + "total_number_of_operations": self.total_number_of_operations, + "transpiled_qasm": self.transpiled_qasm, "width": self.width, } + return json_transpile_response + + +class TranspileResponseSchema(ma.Schema): + depth = ma.fields.Int() + multi_qubit_gate_depth = ma.fields.Int() + number_of_measurement_operations = ma.fields.Int() + number_of_multi_qubit_gates = ma.fields.Int() + number_of_single_qubit_gates = ma.fields.Int() + total_number_of_operations = ma.fields.Int() + transpiled_qasm = ma.fields.String() + width = ma.fields.Int() + + @property + def input(self): + raise NotImplementedError + + +class ExecuteResponseSchema(ma.Schema): + location = ma.fields.String() + + @property + def input(self): + raise NotImplementedError + + +class GenerateCircuitResponseSchema(ma.Schema): + location = ma.fields.String() + + +class ResultsResponseSchema(ma.Schema): + result = ma.fields.List(ma.fields.String()) + post_processing_result = ma.fields.List(ma.fields.String()) + + +class AnalysisOriginalCircuitResponse: + def __init__(self, original_depth, original_multi_qubit_gate_depth, original_number_of_measurement_operations, + original_number_of_multi_qubit_gates, original_number_of_single_qubit_gates, + original_total_number_of_operations, original_width): + super().__init__() + self.original_depth = original_depth + self.original_multi_qubit_gate_depth = original_multi_qubit_gate_depth + self.original_number_of_measurement_operations = original_number_of_measurement_operations + self.original_number_of_multi_qubit_gates = original_number_of_multi_qubit_gates + self.original_number_of_single_qubit_gates = original_number_of_single_qubit_gates + self.original_total_number_of_operations = original_total_number_of_operations + self.original_width = original_width + + def to_json(self): + json_generate_circuit_response = {"original-depth": self.original_depth, + "original-multi-qubit-gate-depth": self.original_multi_qubit_gate_depth, + "original-number-of-measurement-operations": self.original_number_of_measurement_operations, + "original-number-of-multi_qubit-gates": self.original_number_of_multi_qubit_gates, + "original-number-of-single-qubit-gates": self.original_number_of_single_qubit_gates, + "original-total-number-of-operations": self.original_total_number_of_operations, + "original-width": self.original_width, } + return json_generate_circuit_response + + +class AnalysisOriginalCircuitResponseSchema(ma.Schema): + original_depth = ma.fields.Int() + original_multi_qubit_gate_depth = ma.fields.Int() + original_number_of_measurement_operations = ma.fields.Int() + original_number_of_multi_qubit_gates = ma.fields.Int() + original_number_of_single_qubit_gates = ma.fields.Int() + original_total_number_of_operations = ma.fields.Int() + original_width = ma.fields.Int() + + @property + def input(self): + raise NotImplementedError From 86a7a584596bd8d5d3a70aefa76db31f2e468256 Mon Sep 17 00:00:00 2001 From: Marie Salm Date: Thu, 20 Jun 2024 10:58:37 +0200 Subject: [PATCH 3/8] update docs --- README.md | 136 +--------- docs/openapi.json | 614 ++++++++++++++++++++++++++++++++++++++++++++++ docs/openapi.yaml | 481 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1101 insertions(+), 130 deletions(-) create mode 100644 docs/openapi.json create mode 100644 docs/openapi.yaml diff --git a/README.md b/README.md index 75e8796..cfb7d21 100644 --- a/README.md +++ b/README.md @@ -30,137 +30,13 @@ docker-compose pull docker-compose up ``` -## Analysis of Original Circuit - Request an analysis of the original circuit. +## API Documentation +The qiskit-service provides a Swagger UI, specifying the request schemas and showcasing exemplary requests for all API endpoints. +* http://localhost:5013/api/swagger-ui - `POST /pytket-service/api/v1.0/analyze-original-circuit` - ``` - { - "impl-url": "URL-OF-IMPLEMENTATION", - "impl-language": "Qiskit"/"OpenQASM"/"PyQuil", - } - ``` - -## Transpilation/Compilation Request -Send implementation, input, QPU information, and your IBM Quantum Experience token to the API to get properties of the transpiled circuit and the transpiled OpenQASM circuit itself. -`POST /pytket-service/api/v1.0/transpile` - -#### Transpilation/Compilation via URL -``` -{ - "impl-url": "URL-OF-IMPLEMENTATION", - "impl-language": "Qiskit"/"OpenQASM/PyQuil" - "qpu-name": "NAME-OF-QPU", - "provider": "PROVIDER, e.g. IBMQ", - "input-params": { - "PARAM-NAME-1": { - "rawValue": "YOUR-VALUE-1", - "type": "Integer" - }, - "PARAM-NAME-2": { - "rawValue": "YOUR-VALUE-2", - "type": "String" - }, - ... - "token": { - "rawValue": "YOUR-IBMQ-TOKEN", - "type": "Unknown" - } - } -} -``` -#### Transpilation via File -``` -{ - "impl-data": "BASE64-ENCODED-IMPLEMENTATION", - "impl-language": "Qiskit"/"OpenQASM/PyQuil" - "qpu-name": "NAME-OF-QPU", - "provider": "PROVIDER, e.g. IBMQ", - "input-params": { - "PARAM-NAME-1": { - "rawValue": "YOUR-VALUE-1", - "type": "Integer" - }, - "PARAM-NAME-2": { - "rawValue": "YOUR-VALUE-2", - "type": "String" - }, - ... - "token": { - "rawValue": "YOUR-IBMQ-TOKEN", - "type": "Unknown" - } - } -} -``` - -## Execution Request -Send implementation, input, QPU information, and your IBM Quantum Experience token to the API to execute your circuit and get the result. - -`POST /pytket-service/api/v1.0/execute` -#### Execution via URL -``` -{ - "impl-url": "URL-OF-IMPLEMENTATION", - "impl-language": "Qiskit"/"OpenQASM/PyQuil", - "qpu-name": "NAME-OF-QPU", - "provider": "PROVIDER, e.g. IBMQ", - "input-params": { - "PARAM-NAME-1": { - "rawValue": "YOUR-VALUE-1", - "type": "Integer" - }, - "PARAM-NAME-2": { - "rawValue": "YOUR-VALUE-2", - "type": "String" - }, - ... - "token": { - "rawValue": "YOUR-IBMQ-TOKEN", - "type": "Unknown" - } - } -} -``` -#### Execution via data -``` -{ - "impl-data": "BASE64-ENCODED-IMPLEMENTATION", - "impl-language": "Qiskit"/"OpenQASM/PyQuil", - "qpu-name": "NAME-OF-QPU", - "provider": "PROVIDER, e.g. IBMQ", - "input-params": { - "PARAM-NAME-1": { - "rawValue": "YOUR-VALUE-1", - "type": "Integer" - }, - "PARAM-NAME-2": { - "rawValue": "YOUR-VALUE-2", - "type": "String" - }, - ... - "token": { - "rawValue": "YOUR-IBMQ-TOKEN", - "type": "Unknown" - } - } -} -``` -#### Execution via transpiled OpenQASM String -``` -{ - "transpiled-qasm": "TRANSPILED-QASM-STRING", - "qpu-name": "NAME-OF-QPU", - "provider": "PROVIDER, e.g. IBMQ", - "input-params": { - "token": { - "rawValue": "YOUR-IBMQ-TOKEN", - "type": "Unknown" - } - } -} -``` -Returns a content location for the result. Access it via `GET`. +The OpenAPI specifications are also statically available: +[OpenAPI JSON](./docs/openapi.json) +[OpenAPI YAML](./docs/openapi.yaml) ## Haftungsausschluss diff --git a/docs/openapi.json b/docs/openapi.json new file mode 100644 index 0000000..8a403d9 --- /dev/null +++ b/docs/openapi.json @@ -0,0 +1,614 @@ +{ + "openapi" : "3.0.2", + "info" : { + "title" : "pytket-service", + "description" : "This is the API Specification of the pytket-service (https://github.com/UST-QuAntiL/pytket-service).", + "version" : "0.1" + }, + "servers" : [ { + "url" : "/" + } ], + "tags" : [ { + "name" : "Transpile", + "description" : "Send implementation, input, QPU information, and your access token to the API to get analyzed properties of the transpiled circuit itself." + }, { + "name" : "Execute", + "description" : "Send implementation, input, QPU information, and your access token to the API to execute your circuit and get the result." + }, { + "name" : "Analysis of Original Circuit", + "description" : "Request an analysis of the original circuit." + }, { + "name" : "Results", + "description" : "Get execution results of an executed circuit." + }, { + "name" : "Generated Circuits", + "description" : "Request a generated circuit and its properties." + }, { + "name" : "Generate Circuit", + "description" : "Send implementation and input parameters to the API to generate your circuit and get its properties." + } ], + "paths" : { + "/pytket-service/api/v1.0/transpile" : { + "post" : { + "tags" : [ "Transpile" ], + "description" : "*Note*: \"token\" should either be in \"input-params\" or extra. *Note*: \"url\", \"hub\", \"group\", \"project\" are optional such that otherwise the standard values are used.", + "requestBody" : { + "description" : " Transpile via URL:\n \"impl-url\": \"URL-OF-IMPLEMENTATION\" \n Transpile via data:\n \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\"\n Transpile via OpenQASM-String\n \"qasm-string\": \"OpenQASM String\"\n the \"input-params\"are of the form:\n \"input-params\": {\n \"PARAM-NAME-1\": {\n \"rawValue\": \"YOUR-VALUE-1\",\n \"type\": \"Integer\"\n },\n \"PARAM-NAME-2\": {\n \"rawValue\": \"YOUR-VALUE-2\",\n \"type\": \"String\"\n },\n ...\n \"token\": {\n \"rawValue\": \"YOUR-IBMQ-TOKEN\",\n \"type\": \"Unknown\"\n },\n \"url\": {\n \"rawValue\": \"YOUR-IBMQ-AUTHENTICATION-URL\",\n \"type\": \"Unknown\"\n },\n \"hub\": {\n \"rawValue\": \"YOUR-IBMQ-HUB\",\n \"type\": \"Unknown\"\n },\n \"group\": {\n \"rawValue\": \"YOUR-IBMQ-GROUP\",\n \"type\": \"Unknown\"\n },\n \"project\": {\n \"rawValue\": \"YOUR-IBMQ-PROJECT\",\n \"type\": \"Unknown\"\n }", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TranspileRequest" + }, + "example" : { + "impl-url" : "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py", + "qpu-name" : "ibmq_qasm_simulator", + "impl-language" : "qiskit", + "token" : "YOUR-IBMQ-TOKEN", + "input-params" : { } + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TranspileResponse" + } + } + } + }, + "422" : { + "description" : "Unprocessable Entity", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + }, + "default" : { + "description" : "Default error response", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pytket-service/api/v1.0/execute" : { + "post" : { + "tags" : [ "Execute" ], + "description" : "*Note*: \"token\" should either be in \"input-params\" or extra. Both variants are combined here for illustration purposes. *Note*: \"url\", \"hub\", \"group\", \"project\" are optional such that otherwise the standard values are used.", + "requestBody" : { + "description" : " Execution via URL:\n \"impl-url\": \"URL-OF-IMPLEMENTATION\" \n Execution via data:\n \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\"\n Execution via transpiled OpenQASM String:\n \"transpiled-qasm\":\"TRANSPILED-QASM-STRING\" \n for Batch Execution of multiple circuits use:\n \"impl-url\": [\"URL-OF-IMPLEMENTATION-1\", \"URL-OF-IMPLEMENTATION-2\"]\n the \"input-params\"are of the form:\n \"input-params\": {\n \"PARAM-NAME-1\": {\n \"rawValue\": \"YOUR-VALUE-1\",\n \"type\": \"Integer\"\n },\n \"PARAM-NAME-2\": {\n \"rawValue\": \"YOUR-VALUE-2\",\n \"type\": \"String\"\n },\n ...\n \"token\": {\n \"rawValue\": \"YOUR-IBMQ-TOKEN\",\n \"type\": \"Unknown\"\n },\n \"url\": {\n \"rawValue\": \"YOUR-IBMQ-AUTHENTICATION-URL\",\n \"type\": \"Unknown\"\n },\n \"hub\": {\n \"rawValue\": \"YOUR-IBMQ-HUB\",\n \"type\": \"Unknown\"\n },\n \"group\": {\n \"rawValue\": \"YOUR-IBMQ-GROUP\",\n \"type\": \"Unknown\"\n },\n \"project\": {\n \"rawValue\": \"YOUR-IBMQ-PROJECT\",\n \"type\": \"Unknown\"\n }", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ExecuteRequest" + }, + "example" : { + "impl-url" : "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py", + "qpu-name" : "aer_simulator", + "provider" : "ibmq", + "impl-language" : "qiskit", + "token" : "YOUR-IBMQ-TOKEN", + "input-params" : { } + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "Returns a content location for the result. Access it via GET", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ExecuteResponse" + } + } + } + }, + "422" : { + "description" : "Unprocessable Entity", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + }, + "default" : { + "description" : "Default error response", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pytket-service/api/v1.0/analyze-original-circuit" : { + "post" : { + "tags" : [ "Analysis of Original Circuit" ], + "requestBody" : { + "description" : " \"input-params\" should be of the form:\n \"input-params\":{\n \"PARAM-NAME-1\": {\n \"rawValue\": \"YOUR-VALUE-1\",\n \"type\": \"Integer\"\n },\n \"PARAM-NAME-2\": {\n \"rawValue\": \"YOUR-VALUE-2\",\n \"type\": \"String\"\n }", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AnalysisOriginalCircuitRequest" + }, + "example" : { + "impl-url" : "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py", + "impl-language" : "qiskit", + "qpu-name" : "aer_simulator", + "provider" : "ibmq", + "input-params" : { } + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AnalysisOriginalCircuitResponse" + } + } + } + }, + "422" : { + "description" : "Unprocessable Entity", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + }, + "default" : { + "description" : "Default error response", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pytket-service/api/v1.0/results/{id}" : { + "get" : { + "tags" : [ "Results" ], + "parameters" : [ { + "name" : "id", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "minLength" : 1, + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ResultsResponse" + } + } + } + }, + "default" : { + "description" : "Default error response", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pytket-service/api/v1.0/generated-circuits/{id}" : { + "get" : { + "tags" : [ "Generated Circuits" ], + "parameters" : [ { + "name" : "id", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "minLength" : 1, + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GeneratedCircuitsResponse" + } + } + } + }, + "default" : { + "description" : "Default error response", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pytket-service/api/v1.0/generate-circuit" : { + "post" : { + "tags" : [ "Generate Circuit" ], + "requestBody" : { + "description" : " Generation via URL:\n \"impl-url\": \"URL-OF-IMPLEMENTATION\" \n Generation via data:\n \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\"\n the \"input-params\"are of the form:\n \"input-params\": {\n \"PARAM-NAME-1\": {\n \"rawValue\": \"YOUR-VALUE-1\",\n \"type\": \"Integer\"\n },\n \"PARAM-NAME-2\": {\n \"rawValue\": \"YOUR-VALUE-2\",\n \"type\": \"String\"\n },\n ...\n ", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GenerateCircuitRequest" + }, + "example" : { + "impl-url" : "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py", + "impl-language" : "qiskit", + "input-params" : { } + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "Returns a content location for the generated circuit and its properties. Access it via GET", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GenerateCircuitResponse" + } + } + } + }, + "422" : { + "description" : "Unprocessable Entity", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + }, + "default" : { + "description" : "Default error response", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components" : { + "schemas" : { + "Error" : { + "type" : "object", + "properties" : { + "code" : { + "type" : "integer", + "description" : "Error code" + }, + "status" : { + "type" : "string", + "description" : "Error name" + }, + "message" : { + "type" : "string", + "description" : "Error message" + }, + "errors" : { + "type" : "object", + "description" : "Errors" + } + } + }, + "PaginationMetadata" : { + "type" : "object", + "properties" : { + "total" : { + "type" : "integer" + }, + "total_pages" : { + "type" : "integer" + }, + "first_page" : { + "type" : "integer" + }, + "last_page" : { + "type" : "integer" + }, + "page" : { + "type" : "integer" + }, + "previous_page" : { + "type" : "integer" + }, + "next_page" : { + "type" : "integer" + } + } + }, + "TranspileRequest" : { + "type" : "object", + "properties" : { + "impl_url" : { + "type" : "string" + }, + "impl_language" : { + "type" : "string" + }, + "qpu_name" : { + "type" : "string" + }, + "provider" : { + "type" : "string" + }, + "input_params" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "token" : { + "type" : "string" + } + } + }, + "TranspileResponse" : { + "type" : "object", + "properties" : { + "depth" : { + "type" : "integer" + }, + "multi_qubit_gate_depth" : { + "type" : "integer" + }, + "number_of_measurement_operations" : { + "type" : "integer" + }, + "number_of_multi_qubit_gates" : { + "type" : "integer" + }, + "number_of_single_qubit_gates" : { + "type" : "integer" + }, + "total_number_of_operations" : { + "type" : "integer" + }, + "transpiled_qasm" : { + "type" : "string" + }, + "width" : { + "type" : "integer" + } + } + }, + "ExecuteRequest" : { + "type" : "object", + "properties" : { + "impl_url" : { + "type" : "string" + }, + "impl_language" : { + "type" : "string" + }, + "qpu_name" : { + "type" : "string" + }, + "provider" : { + "type" : "string" + }, + "input_params" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "token" : { + "type" : "string" + }, + "noise_model" : { + "type" : "string" + }, + "only_measurement_errors" : { + "type" : "boolean" + }, + "correlation_id" : { + "type" : "string" + } + } + }, + "ExecuteResponse" : { + "type" : "object", + "properties" : { + "location" : { + "type" : "string" + } + } + }, + "AnalysisOriginalCircuitRequest" : { + "type" : "object", + "properties" : { + "impl_url" : { + "type" : "string" + }, + "impl_language" : { + "type" : "string" + }, + "input_params" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "AnalysisOriginalCircuitResponse" : { + "type" : "object", + "properties" : { + "original_depth" : { + "type" : "integer" + }, + "original_multi_qubit_gate_depth" : { + "type" : "integer" + }, + "original_number_of_measurement_operations" : { + "type" : "integer" + }, + "original_number_of_multi_qubit_gates" : { + "type" : "integer" + }, + "original_number_of_single_qubit_gates" : { + "type" : "integer" + }, + "original_total_number_of_operations" : { + "type" : "integer" + }, + "original_width" : { + "type" : "integer" + } + } + }, + "ResultsResponse" : { + "type" : "object", + "properties" : { + "result" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "post_processing_result" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "GeneratedCircuitsResponse" : { + "type" : "object", + "properties" : { + "original_depth" : { + "type" : "integer" + }, + "original_multi_qubit_gate_depth" : { + "type" : "integer" + }, + "original_number_of_measurement_operations" : { + "type" : "integer" + }, + "original_number_of_multi_qubit_gates" : { + "type" : "integer" + }, + "original_number_of_single_qubit_gates" : { + "type" : "integer" + }, + "original_total_number_of_operations" : { + "type" : "integer" + }, + "generated_circuit" : { + "type" : "string" + }, + "original_width" : { + "type" : "integer" + } + } + }, + "GenerateCircuitRequest" : { + "type" : "object", + "properties" : { + "impl_url" : { + "type" : "string" + }, + "impl_language" : { + "type" : "string" + }, + "input_params" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "GenerateCircuitResponse" : { + "type" : "object", + "properties" : { + "location" : { + "type" : "string" + } + } + } + }, + "responses" : { + "UNPROCESSABLE_ENTITY" : { + "description" : "Unprocessable Entity", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + }, + "DEFAULT_ERROR" : { + "description" : "Default error response", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/docs/openapi.yaml b/docs/openapi.yaml new file mode 100644 index 0000000..8eab18d --- /dev/null +++ b/docs/openapi.yaml @@ -0,0 +1,481 @@ +openapi: 3.0.2 +info: + title: pytket-service + description: This is the API Specification of the pytket-service (https://github.com/UST-QuAntiL/pytket-service). + version: "0.1" +servers: +- url: / +tags: +- name: Transpile + description: "Send implementation, input, QPU information, and your access token\ + \ to the API to get analyzed properties of the transpiled circuit itself." +- name: Execute + description: "Send implementation, input, QPU information, and your access token\ + \ to the API to execute your circuit and get the result." +- name: Analysis of Original Circuit + description: Request an analysis of the original circuit. +- name: Results + description: Get execution results of an executed circuit. +- name: Generated Circuits + description: Request a generated circuit and its properties. +- name: Generate Circuit + description: Send implementation and input parameters to the API to generate your + circuit and get its properties. +paths: + /pytket-service/api/v1.0/transpile: + post: + tags: + - Transpile + description: "*Note*: \"token\" should either be in \"input-params\" or extra.\ + \ *Note*: \"url\", \"hub\", \"group\", \"project\" are optional such that\ + \ otherwise the standard values are used." + requestBody: + description: " Transpile via URL:\n \"impl-url\"\ + : \"URL-OF-IMPLEMENTATION\" \n Transpile via data:\n \ + \ \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\"\n \ + \ Transpile via OpenQASM-String\n \"qasm-string\"\ + : \"OpenQASM String\"\n the \"input-params\"are of the form:\n\ + \ \"input-params\": {\n \"PARAM-NAME-1\"\ + : {\n \"rawValue\": \"YOUR-VALUE-1\",\n \ + \ \"type\": \"Integer\"\n },\n\ + \ \"PARAM-NAME-2\": {\n \ + \ \"rawValue\": \"YOUR-VALUE-2\",\n \"type\"\ + : \"String\"\n },\n ...\n\ + \ \"token\": {\n \"rawValue\"\ + : \"YOUR-IBMQ-TOKEN\",\n \"type\": \"Unknown\"\ + \n },\n \"url\": {\n \ + \ \"rawValue\": \"YOUR-IBMQ-AUTHENTICATION-URL\",\n\ + \ \"type\": \"Unknown\"\n \ + \ },\n \"hub\": {\n \ + \ \"rawValue\": \"YOUR-IBMQ-HUB\",\n \"type\"\ + : \"Unknown\"\n },\n \"group\"\ + : {\n \"rawValue\": \"YOUR-IBMQ-GROUP\",\n \ + \ \"type\": \"Unknown\"\n \ + \ },\n \"project\": {\n \ + \ \"rawValue\": \"YOUR-IBMQ-PROJECT\",\n \ + \ \"type\": \"Unknown\"\n }" + content: + application/json: + schema: + $ref: '#/components/schemas/TranspileRequest' + example: + impl-url: https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py + qpu-name: ibmq_qasm_simulator + impl-language: qiskit + token: YOUR-IBMQ-TOKEN + input-params: {} + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TranspileResponse' + "422": + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /pytket-service/api/v1.0/execute: + post: + tags: + - Execute + description: "*Note*: \"token\" should either be in \"input-params\" or extra.\ + \ Both variants are combined here for illustration purposes. *Note*: \"url\"\ + , \"hub\", \"group\", \"project\" are optional such that otherwise the standard\ + \ values are used." + requestBody: + description: " Execution via URL:\n \"impl-url\"\ + : \"URL-OF-IMPLEMENTATION\" \n Execution via data:\n \ + \ \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\"\n \ + \ Execution via transpiled OpenQASM String:\n \ + \ \"transpiled-qasm\":\"TRANSPILED-QASM-STRING\" \n for\ + \ Batch Execution of multiple circuits use:\n \"impl-url\"\ + : [\"URL-OF-IMPLEMENTATION-1\", \"URL-OF-IMPLEMENTATION-2\"]\n \ + \ the \"input-params\"are of the form:\n \"input-params\"\ + : {\n \"PARAM-NAME-1\": {\n \ + \ \"rawValue\": \"YOUR-VALUE-1\",\n \"\ + type\": \"Integer\"\n },\n \ + \ \"PARAM-NAME-2\": {\n \"rawValue\": \"YOUR-VALUE-2\"\ + ,\n \"type\": \"String\"\n \ + \ },\n ...\n \"token\"\ + : {\n \"rawValue\": \"YOUR-IBMQ-TOKEN\",\n \ + \ \"type\": \"Unknown\"\n \ + \ },\n \"url\": {\n \ + \ \"rawValue\": \"YOUR-IBMQ-AUTHENTICATION-URL\",\n \ + \ \"type\": \"Unknown\"\n },\n \ + \ \"hub\": {\n \"rawValue\": \"YOUR-IBMQ-HUB\"\ + ,\n \"type\": \"Unknown\"\n \ + \ },\n \"group\": {\n \ + \ \"rawValue\": \"YOUR-IBMQ-GROUP\",\n \ + \ \"type\": \"Unknown\"\n },\n \ + \ \"project\": {\n \"rawValue\": \"YOUR-IBMQ-PROJECT\"\ + ,\n \"type\": \"Unknown\"\n \ + \ }" + content: + application/json: + schema: + $ref: '#/components/schemas/ExecuteRequest' + example: + impl-url: https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py + qpu-name: aer_simulator + provider: ibmq + impl-language: qiskit + token: YOUR-IBMQ-TOKEN + input-params: {} + required: true + responses: + "200": + description: Returns a content location for the result. Access it via GET + content: + application/json: + schema: + $ref: '#/components/schemas/ExecuteResponse' + "422": + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /pytket-service/api/v1.0/analyze-original-circuit: + post: + tags: + - Analysis of Original Circuit + requestBody: + description: |2- + "input-params" should be of the form: + "input-params":{ + "PARAM-NAME-1": { + "rawValue": "YOUR-VALUE-1", + "type": "Integer" + }, + "PARAM-NAME-2": { + "rawValue": "YOUR-VALUE-2", + "type": "String" + } + content: + application/json: + schema: + $ref: '#/components/schemas/AnalysisOriginalCircuitRequest' + example: + impl-url: https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py + impl-language: qiskit + qpu-name: aer_simulator + provider: ibmq + input-params: {} + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AnalysisOriginalCircuitResponse' + "422": + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /pytket-service/api/v1.0/results/{id}: + get: + tags: + - Results + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + minLength: 1 + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ResultsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /pytket-service/api/v1.0/generated-circuits/{id}: + get: + tags: + - Generated Circuits + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + minLength: 1 + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/GeneratedCircuitsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /pytket-service/api/v1.0/generate-circuit: + post: + tags: + - Generate Circuit + requestBody: + description: " Generation via URL:\n \"\ + impl-url\": \"URL-OF-IMPLEMENTATION\" \n Generation via data:\n\ + \ \"impl-data\": \"BASE64-ENCODED-IMPLEMENTATION\"\n\ + \ the \"input-params\"are of the form:\n \ + \ \"input-params\": {\n \"PARAM-NAME-1\": {\n\ + \ \"rawValue\": \"YOUR-VALUE-1\",\n \ + \ \"type\": \"Integer\"\n },\n\ + \ \"PARAM-NAME-2\": {\n \ + \ \"rawValue\": \"YOUR-VALUE-2\",\n \"type\"\ + : \"String\"\n },\n ...\n\ + \ " + content: + application/json: + schema: + $ref: '#/components/schemas/GenerateCircuitRequest' + example: + impl-url: https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py + impl-language: qiskit + input-params: {} + required: true + responses: + "200": + description: Returns a content location for the generated circuit and its + properties. Access it via GET + content: + application/json: + schema: + $ref: '#/components/schemas/GenerateCircuitResponse' + "422": + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + schemas: + Error: + type: object + properties: + code: + type: integer + description: Error code + status: + type: string + description: Error name + message: + type: string + description: Error message + errors: + type: object + description: Errors + PaginationMetadata: + type: object + properties: + total: + type: integer + total_pages: + type: integer + first_page: + type: integer + last_page: + type: integer + page: + type: integer + previous_page: + type: integer + next_page: + type: integer + TranspileRequest: + type: object + properties: + impl_url: + type: string + impl_language: + type: string + qpu_name: + type: string + provider: + type: string + input_params: + type: array + items: + type: string + token: + type: string + TranspileResponse: + type: object + properties: + depth: + type: integer + multi_qubit_gate_depth: + type: integer + number_of_measurement_operations: + type: integer + number_of_multi_qubit_gates: + type: integer + number_of_single_qubit_gates: + type: integer + total_number_of_operations: + type: integer + transpiled_qasm: + type: string + width: + type: integer + ExecuteRequest: + type: object + properties: + impl_url: + type: string + impl_language: + type: string + qpu_name: + type: string + provider: + type: string + input_params: + type: array + items: + type: string + token: + type: string + noise_model: + type: string + only_measurement_errors: + type: boolean + correlation_id: + type: string + ExecuteResponse: + type: object + properties: + location: + type: string + AnalysisOriginalCircuitRequest: + type: object + properties: + impl_url: + type: string + impl_language: + type: string + input_params: + type: array + items: + type: string + AnalysisOriginalCircuitResponse: + type: object + properties: + original_depth: + type: integer + original_multi_qubit_gate_depth: + type: integer + original_number_of_measurement_operations: + type: integer + original_number_of_multi_qubit_gates: + type: integer + original_number_of_single_qubit_gates: + type: integer + original_total_number_of_operations: + type: integer + original_width: + type: integer + ResultsResponse: + type: object + properties: + result: + type: array + items: + type: string + post_processing_result: + type: array + items: + type: string + GeneratedCircuitsResponse: + type: object + properties: + original_depth: + type: integer + original_multi_qubit_gate_depth: + type: integer + original_number_of_measurement_operations: + type: integer + original_number_of_multi_qubit_gates: + type: integer + original_number_of_single_qubit_gates: + type: integer + original_total_number_of_operations: + type: integer + generated_circuit: + type: string + original_width: + type: integer + GenerateCircuitRequest: + type: object + properties: + impl_url: + type: string + impl_language: + type: string + input_params: + type: array + items: + type: string + GenerateCircuitResponse: + type: object + properties: + location: + type: string + responses: + UNPROCESSABLE_ENTITY: + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + DEFAULT_ERROR: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Error' From 5a899ae557cc1584955a1ef2094269cfd3c71bde Mon Sep 17 00:00:00 2001 From: Marie Salm Date: Thu, 20 Jun 2024 14:38:53 +0200 Subject: [PATCH 4/8] improve differentiation between different languages --- app/implementation_handler.py | 2 -- app/tasks.py | 67 ++++++++++++++++------------------- 2 files changed, 30 insertions(+), 39 deletions(-) diff --git a/app/implementation_handler.py b/app/implementation_handler.py index eff4987..2068a7c 100644 --- a/app/implementation_handler.py +++ b/app/implementation_handler.py @@ -117,8 +117,6 @@ def prepare_code_from_url(url, input_params, bearer_token: str = "", post_proces except (error.HTTPError, error.URLError): return None - print('SEE THIS THING') - print(post_processing) if not post_processing: circuit = prepare_code_from_data(impl, input_params) return circuit diff --git a/app/tasks.py b/app/tasks.py index f2ab67e..66dad01 100644 --- a/app/tasks.py +++ b/app/tasks.py @@ -16,20 +16,21 @@ # See the License for the specific language governing permissions and # limitations under the License. # ****************************************************************************** -from qiskit_ibm_runtime import Sampler +import json -from app import implementation_handler, db, app, tket_handler +from pyquil import Program as PyQuilProgram +from pytket.extensions.pyquil import pyquil_to_tk, tk_to_pyquil +from pytket.passes import DefaultMappingPass +from pytket.predicates import ConnectivityPredicate +from pytket.qasm import circuit_to_qasm_str, circuit_from_qasm_str +from qiskit_ibm_runtime import Sampler from rq import get_current_job +from app import implementation_handler, db, app, tket_handler from app.generated_circuit_model import Generated_Circuit -from app.tket_handler import tket_transpile_circuit, UnsupportedGateException, get_backend, setup_credentials from app.result_model import Result -import json -from pytket.qasm import circuit_to_qasm_str, circuit_from_qasm_str -from pyquil import Program as PyQuilProgram -from pytket.extensions.pyquil import pyquil_to_tk -from pytket.predicates import ConnectivityPredicate -from pytket.passes import DefaultMappingPass +from app.tket_handler import tket_transpile_circuit, UnsupportedGateException, get_backend, setup_credentials, \ + get_circuit_qasm def convert_counts_to_json(counts): @@ -62,12 +63,13 @@ def rename_qreg_lowercase(circuit, *regs): def generate(impl_url, impl_data, impl_language, input_params, bearer_token): app.logger.info("Starting generate task...") job = get_current_job() + short_impl_name = 'GeneratedCircuit' generated_circuit_code = None - if impl_url: - generated_circuit_code = implementation_handler.prepare_code_from_url(impl_url, input_params, bearer_token) - elif impl_data: - generated_circuit_code = implementation_handler.prepare_code_from_data(impl_data, input_params) + if impl_url or impl_data: + generated_circuit_code, short_impl_name = implementation_handler.prepare_code(impl_url, impl_data, + impl_language, input_params, + bearer_token) else: generated_circuit_object = Generated_Circuit.query.get(job.get_id()) generated_circuit_object.generated_circuit = json.dumps({'error': 'generating circuit failed'}) @@ -75,19 +77,15 @@ def generate(impl_url, impl_data, impl_language, input_params, bearer_token): db.session.commit() if generated_circuit_code: - non_transpiled_depth_old = 0 generated_circuit_object = Generated_Circuit.query.get(job.get_id()) - generated_circuit_object.generated_circuit = generated_circuit_code.qasm() - - non_transpiled_depth = generated_circuit_code.depth() - while non_transpiled_depth_old < non_transpiled_depth: - non_transpiled_depth_old = non_transpiled_depth - generated_circuit_code = generated_circuit_code.decompose() - non_transpiled_depth = generated_circuit_code.depth() - + if impl_language.lower() == 'pyquil': + generated_circuit_object.generated_circuit = str(generated_circuit_code) + elif impl_language.lower() == 'qiskit': + # convert the circuit to QASM string + generated_circuit_object.generated_circuit = generated_circuit_code.qasm() generated_circuit_code, generated_circuit_object.original_width, generated_circuit_object.original_depth, generated_circuit_object.original_multi_qubit_gate_depth, generated_circuit_object.original_total_number_of_operations, generated_circuit_object.original_number_of_multi_qubit_gates, generated_circuit_object.original_number_of_measurement_operations, generated_circuit_object.original_number_of_single_qubit_gates = tket_handler.tket_analyze_original_circuit( - generated_circuit_code, impl_language=impl_language, short_impl_name="Generated circuit", logger=app.logger.info, - precompile_circuit=False) + generated_circuit_code, impl_language=impl_language, short_impl_name=short_impl_name, + logger=app.logger.info, precompile_circuit=False) generated_circuit_object.input_params = json.dumps(input_params) app.logger.info(f"Received input params for circuit generation: {generated_circuit_object.input_params}") @@ -95,8 +93,8 @@ def generate(impl_url, impl_data, impl_language, input_params, bearer_token): db.session.commit() -def execute(correlation_id, impl_url, impl_data, transpiled_qasm, transpiled_quil, input_params, provider, qpu_name, impl_language, - shots, bearer_token: str = ""): +def execute(correlation_id, impl_url, impl_data, transpiled_qasm, transpiled_quil, input_params, provider, qpu_name, + impl_language, shots, bearer_token: str = ""): """Create database entry for result. Get implementation code, prepare it, and execute it. Save result in db""" job = get_current_job() @@ -106,20 +104,15 @@ def execute(correlation_id, impl_url, impl_data, transpiled_qasm, transpiled_qui backend = get_backend(provider, qpu_name) if (impl_url or impl_data) and not correlation_id: - circuit, short_impl_name = implementation_handler.prepare_code(impl_url, impl_data, impl_language, input_params, bearer_token) + circuit, short_impl_name = implementation_handler.prepare_code(impl_url, impl_data, impl_language, input_params, + bearer_token) # Transpile the circuit for the backend try: - circuit = tket_transpile_circuit(circuit, - impl_language=impl_language, - backend=backend, - short_impl_name=short_impl_name, - logger=None, precompile_circuit=False) + circuit = tket_transpile_circuit(circuit, impl_language=impl_language, backend=backend, + short_impl_name=short_impl_name, logger=None, precompile_circuit=False) except UnsupportedGateException: - circuit = tket_transpile_circuit(circuit, - impl_language=impl_language, - backend=backend, - short_impl_name=short_impl_name, - logger=None, precompile_circuit=True) + circuit = tket_transpile_circuit(circuit, impl_language=impl_language, backend=backend, + short_impl_name=short_impl_name, logger=None, precompile_circuit=True) finally: if not backend.valid_circuit(circuit): result = Result.query.get(job.get_id()) From efd1128e8f59c6d81758e60407104f9b7f058250 Mon Sep 17 00:00:00 2001 From: Marie Salm Date: Mon, 24 Jun 2024 11:25:26 +0200 Subject: [PATCH 5/8] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cfb7d21..2d4dd2c 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ This service takes a Qiskit, OpenQASM, or PyQuil implementation as data or via a ## Setup * Clone repository: ``` -git clone https://github.com/UST-QuAntiL/qiskit-service.git -git clone git@github.com:UST-QuAntiL/qiskit-service.git +git clone https://github.com/UST-QuAntiL/pytket-service.git +git clone git@github.com:UST-QuAntiL/pytket-service.git ``` * Start containers: @@ -20,8 +20,8 @@ Now the pytket-service is available on http://localhost:5015/. ## After implementation changes * Update container: ``` -docker build -t planqk/qiskit-service:latest . -docker push planqk/qiskit-service:latest +docker build -t planqk/pytket-service:latest . +docker push planqk/pytket-service:latest ``` * Start containers: @@ -31,7 +31,7 @@ docker-compose up ``` ## API Documentation -The qiskit-service provides a Swagger UI, specifying the request schemas and showcasing exemplary requests for all API endpoints. +The pytket-service provides a Swagger UI, specifying the request schemas and showcasing exemplary requests for all API endpoints. * http://localhost:5013/api/swagger-ui The OpenAPI specifications are also statically available: From 201dc88e8bec7ac73ebe517006d2a488e69650b2 Mon Sep 17 00:00:00 2001 From: Marie Salm Date: Mon, 24 Jun 2024 13:22:48 +0200 Subject: [PATCH 6/8] ionq support is depricated, update requirements to enable execution on IBM QPUs a PR is opened at the pytket-qiskit package, as running on real backends is not possible with the latest version, see https://github.com/CQCL/pytket-qiskit/pull/359 --- Dockerfile | 2 +- app/__init__.py | 11 +- .../analysis_original_circuit_controller.py | 2 - app/tasks.py | 14 +- app/tket_handler.py | 37 ++-- requirements-unfrozen.txt | 6 +- requirements.txt | 161 +++++++++--------- 7 files changed, 104 insertions(+), 129 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4dff586..56fd20c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9-slim +FROM python:3.11 MAINTAINER Marie Salm "marie.salm@iaas.uni-stuttgart.de" diff --git a/app/__init__.py b/app/__init__.py index 35c0fab..6b69379 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -27,16 +27,7 @@ app = Flask(__name__) app.config.from_object(Config) -from sqlalchemy import MetaData - -naming_convention = { - "ix": 'ix_%(column_0_label)s', - "uq": "uq_%(table_name)s_%(column_0_name)s", - "ck": "ck_%(table_name)s_%(column_0_name)s", - "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", - "pk": "pk_%(table_name)s" -} -db = SQLAlchemy(app, metadata=MetaData(naming_convention=naming_convention)) +db = SQLAlchemy(app) migrate = Migrate(app, db) from app import routes, result_model, errors diff --git a/app/controller/analysis_original_circuit/analysis_original_circuit_controller.py b/app/controller/analysis_original_circuit/analysis_original_circuit_controller.py index 9089325..5cddacb 100644 --- a/app/controller/analysis_original_circuit/analysis_original_circuit_controller.py +++ b/app/controller/analysis_original_circuit/analysis_original_circuit_controller.py @@ -21,8 +21,6 @@ }''', example={ "impl-url": "https://raw.githubusercontent.com/UST-QuAntiL/nisq-analyzer-content/master/example-implementations/Grover-SAT/grover-fix-sat-qiskit.py", "impl-language": "qiskit", - "qpu-name": "aer_simulator", - "provider": "ibmq", "input-params": {}}, ) diff --git a/app/tasks.py b/app/tasks.py index 66dad01..7b06e20 100644 --- a/app/tasks.py +++ b/app/tasks.py @@ -23,8 +23,8 @@ from pytket.passes import DefaultMappingPass from pytket.predicates import ConnectivityPredicate from pytket.qasm import circuit_to_qasm_str, circuit_from_qasm_str -from qiskit_ibm_runtime import Sampler from rq import get_current_job +from qiskit.qasm2 import dumps from app import implementation_handler, db, app, tket_handler from app.generated_circuit_model import Generated_Circuit @@ -82,7 +82,7 @@ def generate(impl_url, impl_data, impl_language, input_params, bearer_token): generated_circuit_object.generated_circuit = str(generated_circuit_code) elif impl_language.lower() == 'qiskit': # convert the circuit to QASM string - generated_circuit_object.generated_circuit = generated_circuit_code.qasm() + generated_circuit_object.generated_circuit = dumps(generated_circuit_code) generated_circuit_code, generated_circuit_object.original_width, generated_circuit_object.original_depth, generated_circuit_object.original_multi_qubit_gate_depth, generated_circuit_object.original_total_number_of_operations, generated_circuit_object.original_number_of_multi_qubit_gates, generated_circuit_object.original_number_of_measurement_operations, generated_circuit_object.original_number_of_single_qubit_gates = tket_handler.tket_analyze_original_circuit( generated_circuit_code, impl_language=impl_language, short_impl_name=short_impl_name, logger=app.logger.info, precompile_circuit=False) @@ -149,16 +149,6 @@ def execute(correlation_id, impl_url, impl_data, transpiled_qasm, transpiled_qui register_names = set(map(lambda q: q.reg_name, circuit.qubits)) circuit = rename_qreg_lowercase(circuit, *register_names) - # fix bug in pytket-qiskit by monkey patching - original_run = Sampler.run - - def fixed_run(self, **kwargs): - kwargs.pop("dynamic", None) # remove "dynamic" argument, as it is no longer supported - - return original_run(self, **kwargs) - - Sampler.run = fixed_run - # Execute the circuit on the backend # validity was checked before job_handle = backend.process_circuit(circuit, n_shots=shots, valid_check=False) diff --git a/app/tket_handler.py b/app/tket_handler.py index 38a047f..ff6592e 100644 --- a/app/tket_handler.py +++ b/app/tket_handler.py @@ -21,21 +21,18 @@ import os import boto3 -import pytket.extensions.qiskit from braket.aws.aws_session import AwsSession from pytket.extensions.braket import BraketBackend from pytket.extensions.qiskit import qiskit_to_tk, IBMQBackend, set_ibmq_config, AerBackend from pytket.extensions.pyquil import pyquil_to_tk, tk_to_pyquil -from pytket.extensions.ionq import IonQBackend, set_ionq_config +# from pytket.extensions.ionq import IonQBackend, set_ionq_config from pytket import Circuit as TKCircuit from pytket.circuit import OpType from pytket.qasm import circuit_to_qasm_str from flask import abort from qiskit.compiler import transpile -from qiskit import IBMQ import qiskit.circuit.library as qiskit_gates -from qiskit_aer import AerSimulator AWS_BRAKET_HOSTED_PROVIDERS = ['rigetti', 'aws'] # Get environment variables @@ -82,11 +79,12 @@ def setup_credentials(provider, **kwargs): set_ibmq_config(ibmq_api_token=kwargs['token'], instance=f"{hub}/{group}/{project}") else: abort(400) - elif provider.lower() == "ionq": - if 'token' in kwargs: - set_ionq_config(kwargs['token']) - else: - abort(400) + # Note that IonQ support is depricated, see https://pypi.org/project/pytket-ionq/ # + # elif provider.lower() == "ionq": + # if 'token' in kwargs: + # set_ionq_config(kwargs['token']) + # else: + # abort(400) elif provider.lower() in AWS_BRAKET_HOSTED_PROVIDERS: if 'aws-access-key-id' in kwargs and 'aws-secret-access-key' in kwargs: boto_session = boto3.Session( @@ -141,16 +139,17 @@ def get_backend(provider, qpu): return IBMQBackend(qpu) except ValueError: return None - if provider.lower() == "ionq": - try: - qpu = qpu.replace(" ", "-") - for backend in IonQBackend.available_devices(): - if qpu.lower() in backend.device_name.lower(): - qpu = backend.device_name.lower() - break - return IonQBackend(qpu) - except ValueError: - return None + # Note that IonQ support is depricated, see https://pypi.org/project/pytket-ionq/ # + # if provider.lower() == "ionq": + # try: + # qpu = qpu.replace(" ", "-") + # for backend in IonQBackend.available_devices(): + # if qpu.lower() in backend.device_name.lower(): + # qpu = backend.device_name.lower() + # break + # return IonQBackend(qpu) + # except ValueError: + # return None if aws_session is not None and provider.lower() == "aws": qpu_provider_for_aws = provider if "Aria" in qpu or "Harmony" in qpu: diff --git a/requirements-unfrozen.txt b/requirements-unfrozen.txt index a2a0ade..d508fc0 100644 --- a/requirements-unfrozen.txt +++ b/requirements-unfrozen.txt @@ -4,7 +4,7 @@ Flask Flask-Migrate Flask-RESTful Flask-SQLAlchemy -qiskit +flask-smorest redis requests rq @@ -17,5 +17,5 @@ pytket-qiskit pytket-pyquil pyquil pytket-braket -amazon-braket-sdk -qiskit-ibmq-provider \ No newline at end of file +qiskit +amazon-braket-sdk \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index c3a5296..b82bce6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,125 +1,122 @@ -alembic==1.12.1 -amazon-braket-default-simulator==1.20.1 -amazon-braket-schemas==1.19.1.post0 -amazon-braket-sdk==1.62.1 +alembic==1.13.1 +amazon-braket-default-simulator==1.23.4 +amazon-braket-schemas==1.22.0 +amazon-braket-sdk==1.81.1 aniso8601==9.0.1 +annotated-types==0.7.0 antlr4-python3-runtime==4.9.2 -anyio==4.1.0 +anyio==4.4.0 apispec==6.6.1 async-timeout==4.0.3 attrs==21.4.0 backoff==2.2.1 -blinker==1.7.0 -boltons==23.1.1 -boto3==1.33.1 -boto3-stubs==1.29.7 -botocore==1.33.1 -botocore-stubs==1.33.1 -certifi==2023.11.17 +backports.entry-points-selectable==1.3.0 +blinker==1.8.2 +boltons==24.0.0 +boto3==1.34.131 +boto3-stubs==1.34.131 +botocore==1.34.131 +botocore-stubs==1.34.131 +certifi==2024.6.2 cffi==1.16.0 charset-normalizer==3.3.2 click==8.1.7 cloudpickle==2.2.1 -cryptography==41.0.7 +cryptography==42.0.8 Deprecated==1.2.14 -dill==0.3.7 -exceptiongroup==1.2.0 -Flask==3.0.0 -Flask-Migrate==4.0.5 +dill==0.3.8 +Flask==3.0.3 +Flask-Migrate==4.0.7 Flask-RESTful==0.3.10 flask-smorest==0.44.0 Flask-SQLAlchemy==3.1.1 -graphviz==0.20.1 -greenlet==3.0.1 -gunicorn==21.2.0 +graphviz==0.20.3 +greenlet==3.0.3 +gunicorn==22.0.0 h11==0.14.0 httpcore==0.16.3 httpx==0.23.3 -ibm-cloud-sdk-core==3.18.0 -ibm-platform-services==0.47.1 -idna==3.6 -importlib-metadata==6.8.0 +ibm-cloud-sdk-core==3.20.1 +ibm-platform-services==0.54.1 +idna==3.7 iso8601==1.1.0 -itsdangerous==2.1.2 -Jinja2==3.1.2 +itsdangerous==2.2.0 +Jinja2==3.1.4 jmespath==1.0.1 lark==0.11.3 lark-parser==0.12.0 -Mako==1.3.0 -MarkupSafe==2.1.3 +Mako==1.3.5 +MarkupSafe==2.1.5 marshmallow==3.21.3 +matplotlib-inline==0.1.7 mpmath==1.3.0 -msgpack==1.0.7 +msgpack==1.0.8 mypy-extensions==1.0.0 -nest-asyncio==1.5.8 -networkx==3.2.1 -ntlm-auth==1.5.0 -numpy==1.23.5 -openpulse==0.4.2 -openqasm3==0.4.0 +nest-asyncio==1.6.0 +networkx==3.3 +numpy==1.26.4 +openpulse==0.5.0 +openqasm3==0.5.0 opt-einsum==3.3.0 -oqpy==0.2.1 +oqpy==0.3.6 packaging==23.2 pbr==6.0.0 -pipdeptree==2.13.1 -ply==3.11 -psutil==5.9.6 -pycparser==2.21 -pydantic==1.10.13 +psutil==6.0.0 +pycparser==2.22 +pydantic==2.7.4 +pydantic_core==2.18.4 PyJWT==2.8.0 pyquil==3.5.4 -python-dateutil==2.8.2 -python-rapidjson==1.13 -pytket==1.18.0 -pytket-braket==0.29.0 +pyspnego==0.11.0 +python-dateutil==2.9.0.post0 +python-rapidjson==1.17 +pytket==1.27.0 +pytket-braket==0.36.0 pytket-ionq==0.23.0 -pytket-pyquil==0.29.0 -pytket-qiskit==0.41.0 -pytz==2023.3.post1 -pyzmq==25.1.1 +pytket-pyquil==0.35.0 +pytket-qiskit==0.54.0 +pytz==2024.1 +pyzmq==26.0.3 qcs-api-client==0.21.6 -qiskit==0.43.1 -qiskit-aer==0.12.0 -qiskit-algorithms==0.2.1 -qiskit-ibm-provider==0.6.1 -qiskit-ibm-runtime==0.11.1 -qiskit-ibmq-provider==0.20.2 -qiskit-terra==0.24.1 +qcs-sdk-python==0.17.9 +qiskit==1.1.1 +qiskit-aer==0.14.2 +qiskit-algorithms==0.3.0 +qiskit-ibm-runtime==0.24.1 +quil==0.10.0 qwasm==1.0.1 -redis==5.0.1 -requests==2.31.0 -requests-ntlm==1.1.0 +redis==5.0.6 +requests==2.32.2 +requests_ntlm==1.3.0 retrying==1.3.4 rfc3339==6.2 rfc3986==1.5.0 rpcq==3.11.0 -rq==1.15.1 -ruamel.yaml==0.18.5 +rq==1.16.2 +ruamel.yaml==0.18.6 ruamel.yaml.clib==0.2.8 -rustworkx==0.13.2 -s3transfer==0.8.0 -scipy==1.11.4 +rustworkx==0.14.2 +s3transfer==0.10.1 +scipy==1.13.1 six==1.16.0 -sniffio==1.3.0 -SQLAlchemy==2.0.23 -stevedore==5.1.0 -symengine==0.9.2 -sympy==1.12 -tenacity==8.2.3 +sniffio==1.3.1 +SQLAlchemy==2.0.31 +stevedore==5.2.0 +symengine==0.11.0 +sympy==1.12.1 +tenacity==8.4.1 toml==0.10.2 -types-awscrt==0.19.17 -types-Deprecated==1.2.9.3 +traitlets==5.14.3 +types-awscrt==0.20.12 +types-Deprecated==1.2.9.20240311 types-pkg-resources==0.1.3 -types-python-dateutil==2.8.19.14 -types-requests==2.31.0.6 +types-python-dateutil==2.9.0.20240316 +types-requests==2.32.0.20240622 types-retry==0.9.9.4 -types-s3transfer==0.7.0 -types-urllib3==1.26.25.14 -typing_extensions==4.8.0 -urllib3==1.26.18 +types-s3transfer==0.10.1 +typing_extensions==4.12.2 +urllib3==2.2.2 webargs==8.4.0 -websocket-client==1.6.4 -websockets==12.0 -Werkzeug==3.0.1 +websocket-client==1.8.0 +Werkzeug==3.0.3 wrapt==1.16.0 -zipp==3.17.0 From 430b4dc00d24b5e32d805833893000a19befd03f Mon Sep 17 00:00:00 2001 From: Marie Salm Date: Wed, 26 Jun 2024 12:50:22 +0200 Subject: [PATCH 7/8] locally running with these requirements --- app/__init__.py | 2 +- pytket-service.py | 5 +++-- requirements-unfrozen.txt | 6 ++---- requirements.txt | 20 +++++++++----------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 6b69379..8ab3288 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -30,7 +30,7 @@ db = SQLAlchemy(app) migrate = Migrate(app, db) -from app import routes, result_model, errors +from app import routes, result_model, errors, generated_circuit_model from app.controller import register_blueprints from flask_smorest import Api diff --git a/pytket-service.py b/pytket-service.py index 7bc5f3d..dd45c9a 100644 --- a/pytket-service.py +++ b/pytket-service.py @@ -1,5 +1,5 @@ # ****************************************************************************** -# Copyright (c) 2020 University of Stuttgart +# Copyright (c) 2024 University of Stuttgart # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -18,4 +18,5 @@ # ****************************************************************************** from app import app, db -from app.result_model import Result \ No newline at end of file +from app.result_model import Result +from app.generated_circuit_model import Generated_Circuit \ No newline at end of file diff --git a/requirements-unfrozen.txt b/requirements-unfrozen.txt index d508fc0..6c942f8 100644 --- a/requirements-unfrozen.txt +++ b/requirements-unfrozen.txt @@ -11,11 +11,9 @@ rq SQLAlchemy urllib3 gunicorn -pytket -pytket-ionq -pytket-qiskit +pytket==1.29.0 +pytket-qiskit==0.54.1 pytket-pyquil -pyquil pytket-braket qiskit amazon-braket-sdk \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b82bce6..15e16a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ alembic==1.13.1 -amazon-braket-default-simulator==1.23.4 +amazon-braket-default-simulator==1.24.0 amazon-braket-schemas==1.22.0 amazon-braket-sdk==1.81.1 aniso8601==9.0.1 @@ -13,10 +13,10 @@ backoff==2.2.1 backports.entry-points-selectable==1.3.0 blinker==1.8.2 boltons==24.0.0 -boto3==1.34.131 -boto3-stubs==1.34.131 -botocore==1.34.131 -botocore-stubs==1.34.131 +boto3==1.34.133 +boto3-stubs==1.34.133 +botocore==1.34.133 +botocore-stubs==1.34.132 certifi==2024.6.2 cffi==1.16.0 charset-normalizer==3.3.2 @@ -72,9 +72,8 @@ python-dateutil==2.9.0.post0 python-rapidjson==1.17 pytket==1.27.0 pytket-braket==0.36.0 -pytket-ionq==0.23.0 pytket-pyquil==0.35.0 -pytket-qiskit==0.54.0 +pytket-qiskit==0.54.1 pytz==2024.1 pyzmq==26.0.3 qcs-api-client==0.21.6 @@ -96,22 +95,21 @@ rq==1.16.2 ruamel.yaml==0.18.6 ruamel.yaml.clib==0.2.8 rustworkx==0.14.2 -s3transfer==0.10.1 -scipy==1.13.1 +s3transfer==0.10.2 +scipy==1.14.0 six==1.16.0 sniffio==1.3.1 SQLAlchemy==2.0.31 stevedore==5.2.0 symengine==0.11.0 sympy==1.12.1 -tenacity==8.4.1 +tenacity==8.4.2 toml==0.10.2 traitlets==5.14.3 types-awscrt==0.20.12 types-Deprecated==1.2.9.20240311 types-pkg-resources==0.1.3 types-python-dateutil==2.9.0.20240316 -types-requests==2.32.0.20240622 types-retry==0.9.9.4 types-s3transfer==0.10.1 typing_extensions==4.12.2 From 4db526760970152697bbaafef87c73332ce433e6 Mon Sep 17 00:00:00 2001 From: Marie Salm Date: Tue, 6 Aug 2024 09:25:09 +0200 Subject: [PATCH 8/8] update requirements --- app/tket_handler.py | 8 ++-- requirements-unfrozen.txt | 2 +- requirements.txt | 80 +++++++++++++++------------------------ 3 files changed, 35 insertions(+), 55 deletions(-) diff --git a/app/tket_handler.py b/app/tket_handler.py index ff6592e..52d9fa9 100644 --- a/app/tket_handler.py +++ b/app/tket_handler.py @@ -158,15 +158,13 @@ def get_backend(provider, qpu): backend = BraketBackend(device=qpu_name_for_request, device_type='qpu', provider=qpu_provider_for_aws, region=aws_qpu_to_region[provider], aws_session=aws_session) return backend - """ Disabled as migration from pyquil v2 -> v3 is non-trivial if provider.lower() == "rigetti": # Create a connection to the forest SDK - connection = ForestConnection( - sync_endpoint=f"http://{qvm_hostname}:{qvm_port}", - compiler_endpoint=f"tcp://{quilc_hostname}:{quilc_port}") + connection = QCSClient(qvm_url=f"http://{qvm_hostname}:{qvm_port}", + quilc_url=f"tcp://{quilc_hostname}:{quilc_port}") return ForestBackend(qpu, simulator=True, connection=connection) - """ + # Default if no provider matched return None diff --git a/requirements-unfrozen.txt b/requirements-unfrozen.txt index 6c942f8..b3fc63b 100644 --- a/requirements-unfrozen.txt +++ b/requirements-unfrozen.txt @@ -11,7 +11,7 @@ rq SQLAlchemy urllib3 gunicorn -pytket==1.29.0 +pytket pytket-qiskit==0.54.1 pytket-pyquil pytket-braket diff --git a/requirements.txt b/requirements.txt index 15e16a5..7c6f64c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,28 +1,25 @@ -alembic==1.13.1 -amazon-braket-default-simulator==1.24.0 +alembic==1.13.2 +amazon-braket-default-simulator==1.26.0 amazon-braket-schemas==1.22.0 -amazon-braket-sdk==1.81.1 +amazon-braket-sdk==1.84.0 aniso8601==9.0.1 annotated-types==0.7.0 antlr4-python3-runtime==4.9.2 -anyio==4.4.0 apispec==6.6.1 -async-timeout==4.0.3 -attrs==21.4.0 backoff==2.2.1 backports.entry-points-selectable==1.3.0 blinker==1.8.2 boltons==24.0.0 -boto3==1.34.133 -boto3-stubs==1.34.133 -botocore==1.34.133 -botocore-stubs==1.34.132 -certifi==2024.6.2 +boto3==1.34.153 +boto3-stubs==1.34.153 +botocore==1.34.153 +botocore-stubs==1.34.153 +certifi==2024.7.4 cffi==1.16.0 charset-normalizer==3.3.2 click==8.1.7 cloudpickle==2.2.1 -cryptography==42.0.8 +cryptography==43.0.0 Deprecated==1.2.14 dill==0.3.8 Flask==3.0.3 @@ -33,18 +30,13 @@ Flask-SQLAlchemy==3.1.1 graphviz==0.20.3 greenlet==3.0.3 gunicorn==22.0.0 -h11==0.14.0 -httpcore==0.16.3 -httpx==0.23.3 -ibm-cloud-sdk-core==3.20.1 -ibm-platform-services==0.54.1 +ibm-cloud-sdk-core==3.20.4 +ibm-platform-services==0.55.2 idna==3.7 -iso8601==1.1.0 itsdangerous==2.2.0 Jinja2==3.1.4 jmespath==1.0.1 -lark==0.11.3 -lark-parser==0.12.0 +lark==1.1.9 Mako==1.3.5 MarkupSafe==2.1.5 marshmallow==3.21.3 @@ -56,61 +48,51 @@ nest-asyncio==1.6.0 networkx==3.3 numpy==1.26.4 openpulse==0.5.0 -openqasm3==0.5.0 +openqasm3==1.0.0 opt-einsum==3.3.0 oqpy==0.3.6 packaging==23.2 pbr==6.0.0 psutil==6.0.0 pycparser==2.22 -pydantic==2.7.4 -pydantic_core==2.18.4 -PyJWT==2.8.0 -pyquil==3.5.4 -pyspnego==0.11.0 +pydantic==2.8.2 +pydantic_core==2.20.1 +PyJWT==2.9.0 +pyquil==4.14.0 +pyspnego==0.11.1 python-dateutil==2.9.0.post0 -python-rapidjson==1.17 -pytket==1.27.0 -pytket-braket==0.36.0 -pytket-pyquil==0.35.0 +python-rapidjson==1.19 +pytket==1.31.0 +pytket-braket==0.37.0 +pytket-pyquil==0.36.0 pytket-qiskit==0.54.1 pytz==2024.1 -pyzmq==26.0.3 -qcs-api-client==0.21.6 -qcs-sdk-python==0.17.9 +pyzmq==26.1.0 +qcs-sdk-python==0.19.2 qiskit==1.1.1 qiskit-aer==0.14.2 qiskit-algorithms==0.3.0 -qiskit-ibm-runtime==0.24.1 -quil==0.10.0 +qiskit-ibm-runtime==0.26.0 +quil==0.11.2 qwasm==1.0.1 -redis==5.0.6 -requests==2.32.2 +redis==5.0.8 +requests==2.32.3 requests_ntlm==1.3.0 -retrying==1.3.4 -rfc3339==6.2 -rfc3986==1.5.0 rpcq==3.11.0 rq==1.16.2 ruamel.yaml==0.18.6 ruamel.yaml.clib==0.2.8 -rustworkx==0.14.2 +rustworkx==0.15.1 s3transfer==0.10.2 scipy==1.14.0 six==1.16.0 -sniffio==1.3.1 SQLAlchemy==2.0.31 stevedore==5.2.0 symengine==0.11.0 -sympy==1.12.1 -tenacity==8.4.2 -toml==0.10.2 +sympy==1.13.1 traitlets==5.14.3 -types-awscrt==0.20.12 +types-awscrt==0.21.2 types-Deprecated==1.2.9.20240311 -types-pkg-resources==0.1.3 -types-python-dateutil==2.9.0.20240316 -types-retry==0.9.9.4 types-s3transfer==0.10.1 typing_extensions==4.12.2 urllib3==2.2.2