From d442276579c00973ad5714fcd8bbcad7fb5a226e Mon Sep 17 00:00:00 2001 From: Juan Cruz-Benito Date: Wed, 23 Oct 2024 18:59:37 +0200 Subject: [PATCH] Implementing the method get_result() --- qiskit_ibm_transpiler/ai/synthesis.py | 6 +-- qiskit_ibm_transpiler/transpiler_service.py | 5 +- qiskit_ibm_transpiler/wrappers/ai_routing.py | 12 ++++- .../wrappers/ai_synthesis.py | 53 +++++++++++++++++-- qiskit_ibm_transpiler/wrappers/base.py | 39 +++++++++----- qiskit_ibm_transpiler/wrappers/transpile.py | 21 +++++++- 6 files changed, 110 insertions(+), 26 deletions(-) diff --git a/qiskit_ibm_transpiler/ai/synthesis.py b/qiskit_ibm_transpiler/ai/synthesis.py index dd85b74..5b138c9 100644 --- a/qiskit_ibm_transpiler/ai/synthesis.py +++ b/qiskit_ibm_transpiler/ai/synthesis.py @@ -14,7 +14,7 @@ import os from concurrent.futures import ThreadPoolExecutor, as_completed from multiprocessing import cpu_count -from typing import Dict, List, Union +from typing import List, Union from qiskit.circuit.exceptions import CircuitError from qiskit.converters import circuit_to_dag @@ -77,7 +77,7 @@ def synth_nodes(self, nodes): originals.append(orig) except CircuitError: logger.warning( - f"Error getting synth input from node. Skipping ai transpilation." + "Error getting synth input from node. Skipping ai transpilation." ) return [], [] @@ -109,7 +109,7 @@ def synth_nodes(self, nodes): return outputs, nodes def run(self, dag: DAGCircuit): - logger.info(f"Requesting synthesis to the service") + logger.info("Requesting synthesis to the service") future_list = [] diff --git a/qiskit_ibm_transpiler/transpiler_service.py b/qiskit_ibm_transpiler/transpiler_service.py index b5c2431..6fef47d 100644 --- a/qiskit_ibm_transpiler/transpiler_service.py +++ b/qiskit_ibm_transpiler/transpiler_service.py @@ -107,7 +107,7 @@ def run( The transpiled circuit(s) """ - logger.info(f"Requesting transpile to the service") + logger.info("Requesting transpile to the service") transpile_result = self.transpiler_service.transpile( circuits=circuits, backend=self.backend_name, @@ -125,3 +125,6 @@ def run( logger.info("Qiskit IBM Transpiler returned a result") return transpile_result + + def get_result(self, task_id:str, **kwargs): + return self.transpiler_service.get_result(task_id=task_id, **kwargs) diff --git a/qiskit_ibm_transpiler/wrappers/ai_routing.py b/qiskit_ibm_transpiler/wrappers/ai_routing.py index be71d2f..bd62ad8 100644 --- a/qiskit_ibm_transpiler/wrappers/ai_routing.py +++ b/qiskit_ibm_transpiler/wrappers/ai_routing.py @@ -14,8 +14,9 @@ from qiskit_ibm_transpiler.utils import get_circuit_from_qasm, input_to_qasm from .base import QiskitTranspilerService from typing import List, Union, Literal +import logging - +logger = logging.getLogger(__name__) # TODO: Reuse this code, it's repeated several times OptimizationOptions = Literal["n_cnots", "n_gates", "cnot_layers", "layers", "noise"] @@ -62,3 +63,12 @@ def routing( routing_resp["layout"]["initial"], routing_resp["layout"]["final"], ) + + def get_result( + self, task_id: str + ) -> Union[QuantumCircuit, List[QuantumCircuit], str, List[str]]: + routing_resp = self.get_task_result(endpoint="routing", task_id=task_id) + + routed_circuit = get_circuit_from_qasm(routing_resp["qasm"]) + + return routed_circuit diff --git a/qiskit_ibm_transpiler/wrappers/ai_synthesis.py b/qiskit_ibm_transpiler/wrappers/ai_synthesis.py index 15b85e5..64d6d10 100644 --- a/qiskit_ibm_transpiler/wrappers/ai_synthesis.py +++ b/qiskit_ibm_transpiler/wrappers/ai_synthesis.py @@ -19,8 +19,7 @@ from .base import QiskitTranspilerService -logging.basicConfig() -logging.getLogger(__name__).setLevel(logging.INFO) +logger = logging.getLogger(__name__) class AICliffordAPI(QiskitTranspilerService): @@ -61,7 +60,7 @@ def transpile( ) else: raise ( - f"ERROR. Either a 'coupling_map' or a 'backend_name' must be provided." + "ERROR. Either a 'coupling_map' or a 'backend_name' must be provided." ) results = [] @@ -72,6 +71,20 @@ def transpile( results.append(None) return results + def get_result( + self, task_id: str + ): + transpile_resp = self.get_task_result(endpoint="transpile", task_id=task_id) + results = [] + + for res in transpile_resp: + try: + results.append(QuantumCircuit.from_qasm_str(res["qasm"])) + except Exception as ex: + logger.error("Error transforming the result to a QuantumCircuit object") + raise + + return results class AILinearFunctionAPI(QiskitTranspilerService): """A helper class that covers some basic funcionality from the Linear Function AI Synthesis API""" @@ -111,7 +124,7 @@ def transpile( ) else: raise ( - f"ERROR. Either a 'coupling_map' or a 'backend_name' must be provided." + "ERROR. Either a 'coupling_map' or a 'backend_name' must be provided." ) results = [] @@ -122,6 +135,21 @@ def transpile( results.append(None) return results + def get_result( + self, task_id: str + ): + transpile_resp = self.get_task_result(endpoint="transpile", task_id=task_id) + results = [] + + for res in transpile_resp: + try: + results.append(QuantumCircuit.from_qasm_str(res["qasm"])) + except Exception as ex: + logger.error("Error transforming the result to a QuantumCircuit object") + raise + + return results + class AIPermutationAPI(QiskitTranspilerService): """A helper class that covers some basic funcionality from the Permutation AI Synthesis API""" @@ -158,7 +186,7 @@ def transpile( ) else: raise ( - f"ERROR. Either a 'coupling_map' or a 'backend_name' must be provided." + "ERROR. Either a 'coupling_map' or a 'backend_name' must be provided." ) results = [] @@ -168,3 +196,18 @@ def transpile( else: results.append(None) return results + + def get_result( + self, task_id: str + ): + transpile_resp = self.get_task_result(endpoint="transpile", task_id=task_id) + results = [] + + for res in transpile_resp: + try: + results.append(QuantumCircuit.from_qasm_str(res["qasm"])) + except Exception as ex: + logger.error("Error transforming the result to a QuantumCircuit object") + raise + + return results diff --git a/qiskit_ibm_transpiler/wrappers/base.py b/qiskit_ibm_transpiler/wrappers/base.py index 774277d..a291e6c 100644 --- a/qiskit_ibm_transpiler/wrappers/base.py +++ b/qiskit_ibm_transpiler/wrappers/base.py @@ -13,7 +13,6 @@ import json import logging import os -from http import HTTPStatus from pathlib import Path from typing import Dict from urllib.parse import urljoin @@ -103,6 +102,29 @@ def get_supported_backends(self): return resp + def get_task_result( + self, + endpoint: str, + task_id: str, + ): + result = BackendTaskError( + status="PENDING", + msg=( + f"The background task {task_id} timed out. Try to update the client's timeout config, " + "review your task or use the get_result() method to get the result manually" + ), + ) + + resp = self.request_status(endpoint=endpoint, task_id=task_id) + if resp.get("state") == "SUCCESS": + result = resp.get("result") + elif resp.get("state") == "FAILURE": + logger.error("The request FAILED") + result = BackendTaskError( + status="FAILURE", msg=f"The background task {task_id} FAILED" + ) + return result + def request_status(self, endpoint, task_id): def _giveup(e): # Only retry 520 errors @@ -158,20 +180,9 @@ def _request_and_wait(self, endpoint: str, body: Dict, params: Dict): resp = resp.json() task_id = resp.get("task_id") - result = BackendTaskError( - status="PENDING", - msg=f"The background task {task_id} timed out. Try to update the client's timeout config or review your task", - ) - - resp = self.request_status(endpoint, task_id) - if resp.get("state") == "SUCCESS": - result = resp.get("result") - elif resp.get("state") == "FAILURE": - logger.error("The request FAILED") - result = BackendTaskError( - status="FAILURE", msg=f"The background task {task_id} FAILED" - ) + logger.info(f"Task {task_id} submitted") + result = self.get_task_result(endpoint=endpoint, task_id=task_id) if isinstance(result, BackendTaskError): # TODO: Shall we show this "The background task 99cf52d2-3942-4ae5-b2a7-d672af7f1216 FAILED" to the user? logger.error(f"Failed to get a result for {endpoint}: {result.msg}") diff --git a/qiskit_ibm_transpiler/wrappers/transpile.py b/qiskit_ibm_transpiler/wrappers/transpile.py index 6dfbbb5..1030f8f 100644 --- a/qiskit_ibm_transpiler/wrappers/transpile.py +++ b/qiskit_ibm_transpiler/wrappers/transpile.py @@ -13,8 +13,6 @@ import logging from typing import Dict, List, Union, Literal -import numpy as np -from qiskit import QuantumCircuit, QuantumRegister from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit from qiskit.transpiler import TranspileLayout from qiskit.transpiler.layout import Layout @@ -98,6 +96,25 @@ def transpile( else transpiled_circuits[0] ) + def get_result( + self, task_id: str + ): + transpile_resp = self.get_task_result(endpoint="transpile", task_id=task_id) + transpiled_circuits = [] + + for res in transpile_resp: + try: + transpiled_circuits.append(get_circuit_from_qasm(res["qasm"])) + except Exception as ex: + logger.error("Error transforming the result to a QuantumCircuit object") + raise + + return ( + transpiled_circuits + if len(transpiled_circuits) > 1 + else transpiled_circuits[0] + ) + def _get_circuit_from_result(transpile_resp, orig_circuit): transpiled_circuit = get_circuit_from_qasm(transpile_resp["qasm"])