# -*- coding: utf-8 -*-
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017.
#
# 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.
"""This module implements the job class used by Basic Aer Provider."""
from concurrent import futures
import sys
import functools
from qiskit.providers import BaseJob, JobStatus, JobError
from qiskit.qobj import validate_qobj_against_schema
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 BasicAerJob(BaseJob):
"""BasicAerJob class.
Attributes:
_executor (futures.Executor): executor to handle asynchronous jobs
"""
if sys.platform in ['darwin', 'win32']:
_executor = futures.ThreadPoolExecutor()
else:
_executor = futures.ProcessPoolExecutor()
def __init__(self, backend, job_id, fn, qobj):
super().__init__(backend, job_id)
self._fn = fn
self._qobj = qobj
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!")
validate_qobj_against_schema(self._qobj)
self._future = self._executor.submit(self._fn, self._job_id, self._qobj)
[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:
qiskit.providers.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