diff --git a/quafu/algorithms/ansatz.py b/quafu/algorithms/ansatz.py index fffca15a..add12394 100644 --- a/quafu/algorithms/ansatz.py +++ b/quafu/algorithms/ansatz.py @@ -145,7 +145,7 @@ class QuantumNeuralNetwork(Ansatz): """A Wrapper of quantum circuit as QNN""" # TODO(zhaoyilun): docs - def __init__(self, num_qubits: int, layers: List[Any], interface="torch", backend="sim"): + def __init__(self, num_qubits: int, layers: List[Any], interface="torch", backend="sim", sync: bool = False): """""" # Get transformer according to specified interface self._transformer = InterfaceProvider.get(interface) @@ -154,6 +154,7 @@ def __init__(self, num_qubits: int, layers: List[Any], interface="torch", backen self._weights = None self._backend = backend + self._sync = sync super().__init__(num_qubits) def __call__(self, inputs): @@ -161,7 +162,7 @@ def __call__(self, inputs): # pylint: disable=import-outside-toplevel from quafu.algorithms.estimator import Estimator - estimator = Estimator(self, backend=self._backend) + estimator = Estimator(self, backend=self._backend, sync=self._sync) return self._transformer.execute(self, inputs, estimator=estimator) def _build(self): diff --git a/quafu/algorithms/estimator.py b/quafu/algorithms/estimator.py index 6033bb2c..642e1ce0 100644 --- a/quafu/algorithms/estimator.py +++ b/quafu/algorithms/estimator.py @@ -37,17 +37,26 @@ def execute_circuit(circ: QuantumCircuit, observables: Hamiltonian): class Estimator: """Estimate expectation for quantum circuits and observables""" - def __init__(self, circ: QuantumCircuit, backend: str = "sim", task: Optional[Task] = None, **task_options) -> None: + def __init__( + self, + circ: QuantumCircuit, + backend: str = "sim", + sync: bool = False, + task: Optional[Task] = None, + **task_options, + ) -> None: """ Args: circ: quantum circuit. backend: run on simulator (sim) or real machines (ScQ-PXX) + sync: specify whether to send tasks to real machines synchronously task: task instance for real machine execution (should be none if backend is "sim") task_options: options to config a task instance """ self._circ = circ self._circ.get_parameter_grads() # parameter shift currently requires calling this for initialization self._backend = backend + self._sync = sync self._task = None if backend != "sim": if task is not None: @@ -113,16 +122,22 @@ def _run_real_machine(self, observables: Hamiltonian, cache_key: Optional[str] = for measure_base in measure_basis: res = self._measure_obs(self._circ, measure_base=measure_base) self._circ.gates = copy.deepcopy(inputs) - lst_task_id.append(res.taskid) - - for tid in lst_task_id: - # retrieve task results - while True: - res = self._task.retrieve(tid) - if res.task_status == "Completed": - exec_res.append(res) - break - time.sleep(0.2) + if self._sync: + # directly add res to exec_res + exec_res.append(res) + else: + # record task id and retrieve later + lst_task_id.append(res.taskid) + + if not self._sync: + for tid in lst_task_id: + # retrieve task results + while True: + res = self._task.retrieve(tid) + if res.task_status == "Completed": + exec_res.append(res) + break + time.sleep(0.2) if cache_key is not None: # put into cache @@ -156,7 +171,7 @@ def _measure_obs(self, qc: QuantumCircuit, measure_base: Optional[List] = None) elif base == "Y": qc.rx(pos, np.pi / 2) - res = self._task.send(qc) + res = self._task.send(qc, wait=self._sync) res.measure_base = measure_base return res diff --git a/quafu/algorithms/gradients/vjp.py b/quafu/algorithms/gradients/vjp.py index a089ac29..c714a87b 100644 --- a/quafu/algorithms/gradients/vjp.py +++ b/quafu/algorithms/gradients/vjp.py @@ -36,6 +36,7 @@ def run_circ( circ: QuantumCircuit, params: Optional[List[float]] = None, backend: str = "sim", + sync: bool = False, estimator: Optional[Estimator] = None, ): """Execute a circuit @@ -44,11 +45,12 @@ def run_circ( circ (QuantumCircuit): circ params (Optional[List[float]]): params backend (str): backend + sync (bool): specify whether to submit tasks synchronously when using real machines estimator (Optional[Estimator]): estimator """ obs_list = _generate_expval_z(circ.num) if estimator is None: - estimator = Estimator(circ, backend=backend) + estimator = Estimator(circ, backend=backend, sync=sync) if params is None: params = [g.paras for g in circ.parameterized_gates] output = [estimator.run(obs, params, cache_key="00") for obs in obs_list]