# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
# pylint: disable=arguments-differ
"""This module implements the job class used for AerBackend objects."""
from concurrent import futures
import logging
import functools
from qiskit.providers import BaseJob, JobStatus, JobError
logger = logging.getLogger(__name__)
def requires_submit(func):
"""
Decorator to ensure that a submit has been performed before
calling the method.
Args:
func (callable): test function to be decorated.
Returns:
callable: the decorated function.
"""
@functools.wraps(func)
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)
return _wrapper
[docs]class AerJob(BaseJob):
"""AerJob class.
Attributes:
_executor (futures.Executor): executor to handle asynchronous jobs
"""
_executor = futures.ThreadPoolExecutor(max_workers=1)
def __init__(self, backend, job_id, fn, qobj, *args):
super().__init__(backend, job_id)
self._fn = fn
self._qobj = qobj
self._args = args
self._future = None
[docs] 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!")
self._future = self._executor.submit(self._fn, self._job_id, self._qobj,
*self._args)
[docs] @requires_submit
def result(self, timeout=None):
# pylint: disable=arguments-differ
"""Get job result. The behavior is the same as the underlying
concurrent Future objects,
https://docs.python.org/3/library/concurrent.futures.html#future-objects
Args:
timeout (float): number of seconds to wait for results.
Returns:
qiskit.Result: Result object
Raises:
concurrent.futures.TimeoutError: if timeout occurred.
concurrent.futures.CancelledError: if job cancelled before completed.
"""
return self._future.result(timeout=timeout)
[docs] @requires_submit
def cancel(self):
return self._future.cancel()
[docs] @requires_submit
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:
# Note: There is an undocumented Future state: PENDING, that seems to show up when
# the job is enqueued, waiting for someone to pick it up. We need to deal with this
# state but there's no public API for it, so we are assuming that if the job is not
# in any of the previous states, is PENDING, ergo INITIALIZING for us.
_status = JobStatus.INITIALIZING
return _status
[docs] def backend(self):
"""Return the instance of the backend used for this job."""
return self._backend
[docs] def qobj(self):
"""Return the Qobj submitted for this job.
Returns:
Qobj: the Qobj submitted for this job.
"""
return self._qobj