コード例 #1
0
    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
コード例 #2
0
    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
コード例 #3
0
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
コード例 #4
0
ファイル: aerjobset.py プロジェクト: hhorii/qiskit-aer
    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))
コード例 #5
0
    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
コード例 #6
0
    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)
コード例 #7
0
    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
コード例 #8
0
ファイル: ToasterJob.py プロジェクト: 00mjk/qiskit-toaster
    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,
                ))
コード例 #9
0
    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
コード例 #10
0
ファイル: aqt_job.py プロジェクト: ma5x/qiskit-aqt-provider
 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
コード例 #11
0
    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
コード例 #12
0
    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
コード例 #13
0
 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']))
コード例 #14
0
    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
コード例 #15
0
ファイル: aerjobset.py プロジェクト: hhorii/qiskit-aer
    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
コード例 #16
0
    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)
コード例 #17
0
    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)
コード例 #18
0
ファイル: aerjobset.py プロジェクト: hhorii/qiskit-aer
    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
コード例 #19
0
ファイル: aerjob.py プロジェクト: jwoehr/qiskit-aer
    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)
コード例 #20
0
    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
コード例 #21
0
    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)
コード例 #22
0
    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
コード例 #23
0
    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
コード例 #24
0
    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
コード例 #25
0
 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
コード例 #26
0
ファイル: aerjob.py プロジェクト: danmills0/qiskit
    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)
コード例 #27
0
    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)
コード例 #28
0
    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)
コード例 #29
0
    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
コード例 #30
0
    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