def status(self): """Query the API to update the status. Returns: 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 _wait_for_result(self, timeout=None, wait=5): self._wait_for_submission() try: job_response = self._wait_for_job(timeout=timeout, wait=wait) 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 _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)
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 initiliazation. Once done, we should document that the method # can raise QobjValidationError. if self._future is not None or self._id is not None: raise JobError("We have already submitted the job!") api_jobs = [] circuits = self._job_data['circuits'] for circuit in circuits: job = _create_api_job_from_circuit(circuit) api_jobs.append(job) hpc = self._job_data['hpc'] seed = self._job_data['seed'] shots = self._job_data['shots'] max_credits = self._job_data['max_credits'] hpc_camel_cased = _format_hpc_parameters(hpc) self._future = self._executor.submit(self._submit_callback, api_jobs, self._backend_name, hpc_camel_cased, seed, shots, max_credits)
def submit(self): """Submit job to IBM-Q. Raises: JobError: If we have already submitted the job. """ if self._future is not None or self._id is not None: raise JobError("We have already submitted the job!") api_jobs = [] circuits = self._job_data['circuits'] for circuit in circuits: job = _create_api_job_from_circuit(circuit) api_jobs.append(job) hpc = self._job_data['hpc'] seed = self._job_data['seed'] shots = self._job_data['shots'] max_credits = self._job_data['max_credits'] hpc_camel_cased = _format_hpc_parameters(hpc) self._future = self._executor.submit(self._submit_callback, api_jobs, self._backend_name, hpc_camel_cased, seed, shots, max_credits)
def _wait_for_submission(self, timeout=60): """Waits for the request to return a job ID""" if self._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) 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 _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: # pylint can't see if catch of None type # pylint: disable=raising-bad-type 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): """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 initiliazation. 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 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._qobj)
def result(self, timeout=None, wait=5): """Return the result from the job. Args: timeout (int): number of seconds to wait for job wait (int): time between queries to IBM Q server Returns: Result: Result object Raises: JobError: exception raised during job initialization """ self._wait_for_submission() try: job_data = self._wait_for_job(timeout=timeout, wait=wait) except ApiError as api_err: raise JobError(str(api_err)) if self._is_device and self.status() == JobStatus.DONE: _reorder_bits(job_data) # Build the Result. job_result_list = [] for circuit_result in job_data['qasms']: this_result = { 'data': circuit_result['data'], 'name': circuit_result.get('name'), 'compiled_circuit_qasm': circuit_result.get('qasm'), 'status': circuit_result['status'], 'success': circuit_result['status'] == 'DONE', 'shots': job_data['shots'] } if 'metadata' in circuit_result: this_result['metadata'] = circuit_result['metadata'] job_result_list.append(this_result) return result_from_old_style_dict( { 'id': self._id, 'status': job_data['status'], 'used_credits': job_data.get('usedCredits'), 'result': job_result_list, 'backend_name': self.backend_name(), 'success': job_data['status'] == 'DONE' }, [circuit_result['name'] for circuit_result in job_data['qasms']])
def status(self): """Gets the status of the job by querying the Python's future Returns: JobStatus: The current JobStatus Raises: JobError: If the future is in unexpected state concurrent.futures.TimeoutError: if timeout occurred. """ # 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 else: raise JobError('Unexpected behavior of {0}'.format( self.__class__.__name__)) return _status
def cancel(self): """Attempt to cancel a job. Returns: bool: True if job can be cancelled, else False. Currently this is only possible on commercial systems. Raises: JobError: if there was some unexpected failure in the server """ hub = self._api.config.get('hub', None) group = self._api.config.get('group', None) project = self._api.config.get('project', None) try: response = self._api.cancel_job(self._job_id, hub, group, project) 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=None, wait=5): """Return the result from the job. Args: timeout (int): number of seconds to wait for job wait (int): time between queries to IBM Q server Returns: Result: Result object Raises: JobError: exception raised during job initialization """ self._wait_for_submission() try: this_result = self._wait_for_job(timeout=timeout, wait=wait) except ApiError as api_err: raise JobError(str(api_err)) if self._is_device and self.status() == JobStatus.DONE: _reorder_bits(this_result) return this_result
def submit(self): """Submit the job to the backend for running """ if self._future is not None: raise JobError("We have already submitted the job!") self._future = self._executor.submit(self._fn, self._qobj)
def _wrapper(self, *args, **kwargs): if self._future is None: raise JobError("Job not submitted yet!. You have to .submit() first!") return func(self, *args, **kwargs)
def _reorder_bits(job_data): """Temporary fix for ibmq backends. For every ran circuit, get reordering information from qobj and apply reordering on result. Args: job_data (dict): dict with the bare contents of the API.get_job request. Raises: JobError: raised if the creg sizes don't add up in result header. """ for circuit_result in job_data['qasms']: if 'metadata' in circuit_result: circ = circuit_result['metadata'].get('compiled_circuit') else: logger.warning('result object missing metadata for reordering' ' bits: bits may be out of order') return # device_qubit -> device_clbit (how it should have been) measure_dict = {op['qubits'][0]: op['clbits'][0] for op in circ['operations'] if op['name'] == 'measure'} counts_dict_new = {} for item in circuit_result['data']['counts'].items(): # fix clbit ordering to what it should have been bits = list(item[0]) bits.reverse() # lsb in 0th position count = item[1] reordered_bits = list('x' * len(bits)) for device_clbit, bit in enumerate(bits): if device_clbit in measure_dict: correct_device_clbit = measure_dict[device_clbit] reordered_bits[correct_device_clbit] = bit reordered_bits.reverse() # only keep the clbits specified by circuit, not everything on device num_clbits = circ['header']['number_of_clbits'] compact_key = reordered_bits[-num_clbits:] compact_key = "".join([b if b != 'x' else '0' for b in compact_key]) # insert spaces to signify different classical registers cregs = circ['header']['clbit_labels'] if sum([creg[1] for creg in cregs]) != num_clbits: raise JobError("creg sizes don't add up in result header.") creg_begin_pos = [] creg_end_pos = [] acc = 0 for creg in reversed(cregs): creg_size = creg[1] creg_begin_pos.append(acc) creg_end_pos.append(acc + creg_size) acc += creg_size compact_key = " ".join([compact_key[creg_begin_pos[i]:creg_end_pos[i]] for i in range(len(cregs))]) # marginalize over unwanted measured qubits if compact_key not in counts_dict_new: counts_dict_new[compact_key] = count else: counts_dict_new[compact_key] += count circuit_result['data']['counts'] = counts_dict_new