def status(self): """Query the API to update the status. Returns: qiskit.providers.JobStatus: The status of the job, once updated. Raises: JobError: if there was an exception in the future being executed or the server sent an unknown answer. """ # Implies self._job_id is None if self._future_captured_exception is not None: raise JobError(str(self._future_captured_exception)) if self._job_id is None or self._status in JOB_FINAL_STATES: return self._status try: # TODO: See result values api_response = self._api.get_status_job(self._job_id) self._update_status(api_response) # pylint: disable=broad-except except Exception as err: raise JobError(str(err)) return self._status
def status(self): """Query the API to update the status. Returns: qiskit.providers.JobStatus: The status of the job, once updated. Raises: JobError: if there was an exception in the future being executed or the server sent an unknown answer. """ # Implies self._job_id is None if self._future_captured_exception is not None: raise JobError(str(self._future_captured_exception)) if self._job_id is None or self._status in JOB_FINAL_STATES: return self._status try: # TODO: See result values api_job = self._api.get_status_job(self._job_id) if 'status' not in api_job: raise JobError('get_status_job didn\'t return status: %s' % pprint.pformat(api_job)) # pylint: disable=broad-except except Exception as err: raise JobError(str(err)) if api_job['status'] == 'VALIDATING': self._status = JobStatus.VALIDATING elif api_job['status'] == 'RUNNING': self._status = JobStatus.RUNNING queued, self._queue_position = _is_job_queued(api_job) if queued: self._status = JobStatus.QUEUED elif api_job['status'] == 'COMPLETED': self._status = JobStatus.DONE elif api_job['status'] == 'CANCELLED': self._status = JobStatus.CANCELLED self._cancelled = True elif 'ERROR' in api_job['status']: # Error status are of the form "ERROR_*_JOB" self._status = JobStatus.ERROR # TODO: This seems to be an inconsistency in the API package. self._api_error_msg = api_job.get('error') or api_job.get('Error') else: raise JobError('Unrecognized answer from server: \n{}'.format( pprint.pformat(api_job))) return self._status
def split_qobj(qobj, max_size=None, max_shot_size=None, qobj_id=None): """Split a qobj and return a list of qobjs each with a single experiment. Args: qobj (Qobj): The input qobj object to split max_size (int or None): the maximum number of circuits per job. If None don't split (Default: None). max_shot_size (int or None): the maximum number of shots per job. If None don't split (Default: None). qobj_id (str): Optional, set a fixed qobj ID for all subjob qobjs. Raises: JobError : If max_job_size > 1 and seed is set. JobError : If custom instructions exist. Returns: List: A list of qobjs. """ optypes = getattr(qobj.config, 'optypes', None) split_qobj_list = [] if (max_shot_size is not None and max_shot_size > 0): if _check_custom_instruction(qobj.experiments, optypes): raise JobError( "`max_shot_size` option cannot be used with circuits" " containing save or snapshot instructions.") _seed = getattr(qobj.config, "seed_simulator", 0) if hasattr(qobj.config, "noise_model"): if _seed and max_size is not None and max_size > 1: raise JobError( "cannot support max_job_size > 1 for noise simulation, " "when seed_simulator is set.") if max_shot_size is not None and max_shot_size > 0: _qobj = _copy_qobj_for_noise(qobj, max_shot_size, qobj_id) if isinstance(_qobj, list): for each_qobj in _qobj: _split = _split_qobj(each_qobj, max_size, qobj_id, _seed) if isinstance(_split, QasmQobj): split_qobj_list.append([_split]) else: split_qobj_list.append(_split) _set_seed(split_qobj_list, _seed) return split_qobj_list _qobj = _split_qobj(qobj, max_size, qobj_id, _seed) if isinstance(_qobj, (PulseQobj, QasmQobj)): return _qobj else: split_qobj_list.append(_qobj) _set_seed(split_qobj_list, _seed) return split_qobj_list
def worker(self, experiment: Union[str, QuantumCircuit, Schedule] ) -> Union[int, List[int]]: """Retrieve the index of job. Args: experiment: Retrieve the job used to submit this experiment. Several types are accepted for convenience: * str: The name of the experiment. * QuantumCircuit: The name of the circuit instance will be used. * Schedule: The name of the schedule instance will be used. Returns: list or integer value of the job id Raises: JobError: If the job for the experiment could not be found. """ if isinstance(experiment, (QuantumCircuit, Schedule)): experiment = experiment.name job_list = [] for job in self._futures: for i, exp in enumerate(job.qobj().experiments): if hasattr(exp.header, 'name') and exp.header.name == experiment: job_list.append(i) if len(job_list) == 1: return job_list[0] elif len(job_list) > 1: return job_list raise JobError( 'Unable to find the job for experiment {}.'.format(experiment))
def _wait_for_job(self, timeout=None, wait=5): """Blocks until the job is complete and returns the job content from the API, consuming it. Args: timeout (float): number of seconds to wait for job. wait (int): time between queries to IBM Q server. Return: dict: a dictionary with the contents of the job. Raises: JobError: if there is an error while requesting the results. """ self._wait_for_completion(timeout, wait) try: job_response = self._get_job() if not self._qobj_payload: if self._use_object_storage: # Attempt to use object storage. self._qobj_payload = self._api.job_download_qobj_object_storage( self._job_id) else: self._qobj_payload = job_response.get('qObject', {}) except ApiError as api_err: raise JobError(str(api_err)) return job_response
def _wait_for_job(self, timeout=60, wait=5): """Wait until all online ran circuits of a qobj are 'COMPLETED'. Args: timeout (float or None): seconds to wait for job. If None, wait indefinitely. wait (float): seconds between queries Returns: dict: A dict with the contents of the API request. Raises: JobTimeoutError: if the job does not return results before a specified timeout. JobError: if something wrong happened in some of the server API calls """ start_time = time.time() while self.status() not in JOB_FINAL_STATES: elapsed_time = time.time() - start_time if timeout is not None and elapsed_time >= timeout: raise JobTimeoutError( 'Timeout while waiting for the job: {}'.format( self._job_id)) logger.info('status = %s (%d seconds)', self._status, elapsed_time) time.sleep(wait) if self._cancelled: raise JobError( 'Job result impossible to retrieve. The job was cancelled.') return self._api.get_job(self._job_id)
async def status(self, job_id, timeout=300): """Query the API to update the status. Returns: qiskit.providers.JobStatus: The status of the job, once updated. Raises: JobError: if there was an exception in the future being executed or the server sent an unknown answer. """ if job_id is None or self._status in JOB_FINAL_STATES: return self._status try: api_response = self._api.job_status(job_id) if 'websocket' in api_response: task_token = api_response['websocket']['task_token'] execution_arn = api_response['websocket']['executionArn'] websocket_uri = "wss://ws.qapi.honeywell.com/v1" async with websockets.connect(websocket_uri, extra_headers={'x-api-key': self._api.client_api.session.access_token}) as websocket: body = { "action": "OpenConnection", "task_token": task_token, "executionArn": execution_arn } await websocket.send(json.dumps(body)) api_response = await asyncio.wait_for(websocket.recv(), timeout=timeout) api_response = json.loads(api_response) # self._update_status_and_result(api_response) except Exception as err: raise JobError(str(err)) return api_response
def submit(self): if len(self._futures) > 0: raise JobError("We have already submitted the job!") self._t_submit = time.time() logger.debug("submitting...") all_exps = self._qobj_dict exp_index = 0 optimization_level = None backend_options = self._backend_options if backend_options: optimization_level = backend_options.get("toaster_optimization", None) toaster_path = None if int(self._use_cli) != 0: toaster_path = "qubit-toaster" for exp in all_exps["experiments"]: exp_index += 1 exp_job_id = "Exp_%d_%s" % (exp_index, self._job_id) single_exp = copy.deepcopy(all_exps) single_exp["experiments"] = [exp] self._futures.append( self._executor.submit( _run_with_qtoaster_static, single_exp, self._getstates, exp_job_id, optimization_level=optimization_level, toaster_url=self._toaster_url, toaster_path=toaster_path, ))
async def _get_status(self, job_id, timeout=300): """Query the API to update the status. Returns: qiskit.providers.JobStatus: The api response including the job status Raises: JobError: if there was an exception in the future being executed or the server sent an unknown answer. """ if job_id is None or self._status in JOB_FINAL_STATES: return self._status try: api_response = self._api.job_status(job_id) if 'websocket' in api_response: task_token = api_response['websocket']['task_token'] execution_arn = api_response['websocket']['executionArn'] credentials = self.backend().provider().credentials websocket_uri = credentials.url.replace( 'https://', 'wss://ws.') async with websockets.connect(websocket_uri, extra_headers={ 'Authorization': credentials.access_token }) as websocket: body = { "action": "OpenConnection", "task_token": task_token, "executionArn": execution_arn } await websocket.send(json.dumps(body)) api_response = await asyncio.wait_for(websocket.recv(), timeout=timeout) api_response = json.loads(api_response) else: logger.warning( 'Websockets via proxy not supported. Falling-back to polling.' ) residual_delay = timeout / 1000 # convert us -> s request_delay = min(1.0, residual_delay) while api_response['status'] not in [ 'failed', 'completed', 'canceled' ]: sleep(request_delay) api_response = self._api.job_status(job_id) residual_delay = residual_delay - request_delay if residual_delay <= 0: # break if we have exceeded timeout break # Max-out at 10 second delay request_delay = min(min(request_delay * 1.5, 10), residual_delay) except Exception as err: raise JobError(str(err)) return api_response
def _wait_for_result(self, timeout=None, wait=5): start_time = time.time() result = None header = { "Ocp-Apim-Subscription-Key": self._backend._provider.access_token, "SDK": "qiskit" } while True: elapsed = time.time() - start_time if timeout and elapsed >= timeout: raise JobTimeoutError('Timed out waiting for result') result = requests.put(self._backend.url, data={ 'id': self._job_id, 'access_token': self._backend._provider.access_token }, headers=header).json() if result['status'] == 'finished': break if result['status'] == 'error': raise JobError('API returned error:\n' + str(result)) time.sleep(wait) return result
def _wait_for_result(self, timeout=None, wait=5): self._wait_for_submission(timeout) try: job_response = self._wait_for_job(timeout=timeout, wait=wait) if not self._qobj_payload: self._qobj_payload = job_response.get('qObject', {}) except ApiError as api_err: raise JobError(str(api_err)) status = self.status() if status is not JobStatus.DONE: raise JobError('Invalid job state. The job should be DONE but ' 'it is {}'.format(str(status))) return job_response
def status(self): # Implies self._job_id is None if self._future_captured_exception is not None: raise JobError(str(self._future_captured_exception)) if self._job_id is None or self._status in JOB_FINAL_STATES: return self._status try: # TODO: See result values api_response = self._api.circuit_job_status(self._job_id) self._update_status(api_response) # pylint: disable=broad-except except Exception as err: raise JobError(str(err)) return self._status
def _wait_for_submission(self, timeout=60): """Waits for the request to return a job ID""" if self._job_id is None: if self._future is None: raise JobError("You have to submit before asking for status or results!") try: submit_info = self._future.result(timeout=timeout) if self._future_captured_exception is not None: raise self._future_captured_exception except TimeoutError as ex: raise JobTimeoutError( "Timeout waiting for the job being submitted: {}".format(ex) ) if 'error' in submit_info: self._status = JobStatus.ERROR self._api_error_msg = str(submit_info['error']) raise JobError(str(submit_info['error']))
def submit(self): if self._future is not None: raise JobError("Primitive job has already been submitted.") with ThreadPoolExecutor(max_workers=1) as executor: future = executor.submit(self._function, *self._args, **self._kwargs) self._future = future
def _combine_results(self, results: List[Union[Result, None]] = None ) -> Result: """Combine results from all jobs into a single `Result`. Note: Since the order of the results must match the order of the initial experiments, job results can only be combined if all jobs succeeded. Args: results: Result will be combined. Returns: A :class:`~qiskit.result.Result` object that contains results from all jobs. Raises: JobError: If results cannot be combined because some jobs failed. """ if not results: raise JobError( "Results cannot be combined - no results.") # find first non-null result and copy it's config _result = next((r for r in results if r is not None), None) if _result: combined_result = copy.copy(_result) combined_result.results = [] else: raise JobError( "Results cannot be combined - no results.") for each_result in results: if each_result is not None: combined_result.results.extend(each_result.results) if self._end_time is None: self._end_time = datetime.datetime.now() if self._start_time: _time_taken = self._end_time - self._start_time combined_result.time_taken = _time_taken.total_seconds() else: combined_result.time_taken = 0 combined_result.date = datetime.datetime.isoformat(self._end_time) return combined_result
def submit(self) -> None: """ Submit a job to the Quantum Inspire platform. :raises JobError: An error if the job has already been submitted. """ if self._job_id: raise JobError('Job has already been submitted!') self._job_id = self._backend.run(self._qobj)
def submit(self): """Submit the job to the backend for execution. Raises: JobError: if trying to re-submit the job. """ if self._future is not None: raise JobError("We have already submitted the job!") self._future = self._executor.submit(self._fn, self._job_id, self._qobj)
def _get_worker_result(self, worker: int, timeout: Optional[float] = None): """Return the result of the jobs specified with worker_id. this call return all worker's result specified worker and block until job result become available or the timeout is reached. Analogous to dask.client.gather() Args: worker: Worker id to wait for job result. timeout: Number of seconds to wait for job results. Returns: qiskit.Result: Result object instance that can be used to retrieve a result. Raises: JobError: if unable to retrieve all job results before the specified timeout. """ start_time = time.time() original_timeout = timeout aer_job = self._futures[worker] try: result = aer_job.result(timeout=timeout) if result is None or not result.success: if result: logger.warning('AerJobSet %s Error: %s', aer_job.name(), result.header) else: logger.warning('AerJobSet %s did not return a result', aer_job.name()) except JobError: raise JobError( 'Timeout while waiting for the results of experiment {}'.format( aer_job.name())) if timeout: timeout = original_timeout - (time.time() - start_time) if timeout <= 0: raise JobError( "Timeout while waiting for JobSet results") return result
def submit(self): """Submit the job to the backend for execution. Raises: QobjValidationError: if the JSON serialization of the Qobj passed during construction does not validate against the Qobj schema. JobError: if trying to re-submit the job. """ if self._future is not None: raise JobError("Aer job has already been submitted.") self._future = self._executor.submit(self._fn, self._qobj, self._job_id)
def _wait_for_job(self, timeout=None, wait=5): self._wait_for_completion(timeout, wait) try: job_response = self._get_job() if not self._qobj_payload: self._qobj_payload = self._api.job_download_qobj_object_storage( self._job_id) except ApiError as api_err: raise JobError(str(api_err)) return job_response
def submit(self): """Submit job to IBM-Q. Raises: JobError: If we have already submitted the job. """ # TODO: Validation against the schema should be done here and not # during initialization. Once done, we should document that the method # can raise QobjValidationError. if self._future is not None or self._job_id is not None: raise JobError("We have already submitted the job!") self._future = self._executor.submit(self._submit_callback)
def result(self, timeout=300): """Return the result of the job. Args: timeout (float): number of seconds to wait for job Returns: qiskit.Result: Result object Raises: JobError: if attempted to recover a result on a failed job. Notes: Currently when calling get_counts() on a result returned by a Honeywell backend, since Honeywell backends currently support only running one experiment per job, do not supply an argument to the get_counts() function. Doing so may raise an exception. """ if self._result: return self._result # Wait for results sequentially for job_id in self._job_ids: self._experiment_results.append( asyncio.get_event_loop().run_until_complete( self._get_status(job_id, timeout))) # Process results self._result = self._process_results() if not (self._status is JobStatus.DONE or self._status is JobStatus.CANCELLED): raise JobError( 'Invalid job state. The job should be DONE or CANCELLED but ' 'it is {}'.format(str(self._status))) if not self._result: raise JobError('Server did not return result') return self._result
def _update_status(self, api_response): """Update the job status from an API status. Args: api_response (dict): API response for a status query. Raises: JobError: if the API response could not be parsed. """ if 'status' not in api_response: raise JobError('Unrecognized answer from server: \n{}'.format( pprint.pformat(api_response))) try: api_status = ApiJobStatus(api_response['status']) except ValueError: raise JobError('Unrecognized status from server: {}'.format( api_response['status'])) if api_status is ApiJobStatus.VALIDATING: self._status = JobStatus.VALIDATING elif api_status is ApiJobStatus.RUNNING: self._status = JobStatus.RUNNING queued, self._queue_position = _is_job_queued(api_response) if queued: self._status = JobStatus.QUEUED elif api_status is ApiJobStatus.COMPLETED: self._status = JobStatus.DONE elif api_status is ApiJobStatus.CANCELLED: self._status = JobStatus.CANCELLED self._cancelled = True elif api_status in (ApiJobStatus.ERROR_CREATING_JOB, ApiJobStatus.ERROR_VALIDATING_JOB, ApiJobStatus.ERROR_RUNNING_JOB): self._status = JobStatus.ERROR
def result(self, timeout=None, wait=5): self._wait_for_completion(timeout=timeout, wait=wait) status = self.status() if status is not JobStatus.DONE: raise JobError('Invalid job state. The job should be DONE but ' 'it is {}'.format(str(status))) if not self._result: # Retrieve the results via object storage. result_response = self._api.job_result_object_storage(self._job_id) self._result = Result.from_dict(result_response) return self._result
def status(self): # The order is important here if self._future.running(): _status = JobStatus.RUNNING elif self._future.cancelled(): _status = JobStatus.CANCELLED elif self._future.done(): _status = JobStatus.DONE if self._future.exception( ) is None else JobStatus.ERROR elif self._future._state == 'PENDING': _status = JobStatus.QUEUED else: raise JobError('Unexpected behavior of {0}'.format( self.__class__.__name__)) return _status
def submit(self): """Submit the job to the backend for execution. Raises: QobjValidationError: if the JSON serialization of the Qobj passed during construction does not validate against the Qobj schema. JobError: if trying to re-submit the job. """ if self._future is not None: raise JobError("We have already submitted the job!") validate_qobj_against_schema(self._qobj) self._future = self._executor.submit(self._fn, self._job_id, self._qobj, *self._args)
def _get_job(self): """Query the API for retrieving the job complete state, consuming it. Returns: dict: a dictionary with the contents of the result. Raises: JobTimeoutError: if the job does not return results before a specified timeout. JobError: if something wrong happened in some of the server API calls. """ if self._cancelled: raise JobError( 'Job result impossible to retrieve. The job was cancelled.') return self._api.get_job(self._job_id)
def cancel(self): """Attempt to cancel a job. Returns: bool: True if job can be cancelled, else False. Note this operation might not be possible depending on the environment. Raises: JobError: if there was some unexpected failure in the server. """ try: response = self._api.cancel_job(self._job_id) self._cancelled = 'error' not in response return self._cancelled except ApiError as error: self._cancelled = False raise JobError('Error cancelling job: %s' % error.usr_msg)
def result(self, timeout: Optional[float] = None, wait: float = 5) -> Result: """Return the result of the job. Note: Some IBMQ job results can be read only once. A second attempt to query the API for the job will fail, as the job is "consumed". The first call to this method in an ``IBMQJob`` instance will query the API and consume the job if it finished successfully (otherwise it will raise a ``JobError`` exception without consuming the job). Subsequent calls to that instance's method will also return the results, since they are cached. However, attempting to retrieve the results again in another instance or session might fail due to the job having been consumed. Args: timeout: number of seconds to wait for job wait: time between queries to IBM Q server Returns: Result object Raises: JobError: if the job has failed or cancelled, or if there was some unexpected failure in the server. """ # pylint: disable=arguments-differ # pylint: disable=access-member-before-definition,attribute-defined-outside-init if not self._wait_for_completion(timeout=timeout, wait=wait, required_status=(JobStatus.DONE, )): message = 'Job was cancelled.' if self._status is JobStatus.CANCELLED \ else 'Job has failed. Use job.error_message() to get more details.' raise JobError('Unable to retrieve job result. ' + message) if not self._result: # type: ignore[has-type] with api_to_job_error(): result_response = self._api.job_result( self.job_id(), self._use_object_storage) self._result = Result.from_dict(result_response) return self._result
def result(self, timeout=None, wait=5): """Return the result of the job. Note: Some IBMQ job results can be read only once. A second attempt to query the API for the job will fail, as the job is "consumed". The first call to this method in an ``IBMQJob`` instance will query the API and consume the job if it finished successfully (otherwise it will raise a ``JobError`` exception without consuming the job). Subsequent calls to that instance's method will also return the results, since they are cached. However, attempting to retrieve the results again in another instance or session might fail due to the job having been consumed. Args: timeout (float): number of seconds to wait for job wait (int): time between queries to IBM Q server Returns: qiskit.Result: Result object Raises: JobError: if attempted to recover a result on a failed job. """ self._wait_for_completion(timeout=timeout, wait=wait) status = self.status() if status is not JobStatus.DONE: raise JobError('Invalid job state. The job should be DONE but ' 'it is {}'.format(str(status))) if not self._result: if self._use_object_storage: # Retrieve the results via object storage. result_response = self._api.job_result_object_storage( self._job_id) self._result = Result.from_dict(result_response) else: job_response = self._get_job() self._result = Result.from_dict(job_response['qObjectResult']) return self._result