Skip to content
This repository was archived by the owner on Jul 28, 2023. It is now read-only.

Add Backend Jobs Limit #513

Merged
merged 21 commits into from
Jan 14, 2020
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ The format is based on [Keep a Changelog].

- `IBMQJob` now has three new methods: `done()`, `running()`, and
`cancelled()`. The methods are used to indicate the job status. (\#494)
- `IBMQBackend` now has a new `jobs_limit()` method that returns the
jobs limit for a backend, such as the current number of running jobs
on the backend and also the maximum number of jobs that could be
submitted to the backend. (\#513)
- `IBMQBackend` now has a new `job_limit()` method that returns the
job limit for a backend, such as the current number of jobs running
on the backend and also the maximum number of concurrent jobs that
could be submitted to the backend at a time. Note the job limit for a
backend is given for a specific provider (i.e. a specific backend
with a specific provider). (\#513)

### Changed

Expand Down
10 changes: 5 additions & 5 deletions qiskit/providers/ibmq/api/clients/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019.
# (C) Copyright IBM 2018, 2020.
#
# 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
Expand Down Expand Up @@ -116,16 +116,16 @@ def backend_pulse_defaults(self, backend_name: str) -> Dict:
"""
return self.client_api.backend(backend_name).pulse_defaults()

def backend_jobs_limit(self, backend_name: str) -> Dict[str, Any]:
"""Return the jobs limit for the backend.
def backend_job_limit(self, backend_name: str) -> Dict[str, Any]:
"""Return the job limit for the backend.

Args:
backend_name: the name of the backend.

Returns:
backend jobs limit.
backend job limit.
"""
return self.client_api.backend(backend_name).jobs_limit()
return self.client_api.backend(backend_name).job_limit()

# Jobs-related public functions.

Expand Down
6 changes: 3 additions & 3 deletions qiskit/providers/ibmq/api/rest/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019.
# (C) Copyright IBM 2018, 2020.
#
# 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
Expand Down Expand Up @@ -101,7 +101,7 @@ def status(self) -> Dict[str, Any]:

return ret

def jobs_limit(self) -> Dict[str, Any]:
"""Return backend jobs limit."""
def job_limit(self) -> Dict[str, Any]:
"""Return backend job limit."""
url = self.get_url('jobs_limit')
return self.session.get(url).json()
12 changes: 6 additions & 6 deletions qiskit/providers/ibmq/api/rest/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This code is part of Qiskit.
#
# (C) Copyright IBM 2019.
# (C) Copyright IBM 2019, 2020.
#
# 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
Expand Down Expand Up @@ -65,12 +65,12 @@ class StatusResponseSchema(BaseSchema):
status = String(required=True, validate=OneOf([status.value for status in ApiJobStatus]))


class BackendJobsLimitResponseSchema(BaseSchema):
"""Schema for BackendJobsLimit"""
class BackendJobLimitResponseSchema(BaseSchema):
"""Schema for BackendJobLimit"""

# required properties
maximum_jobs = Integer(required=True)
running_jobs = Integer(required=True)
# Optional properties
maximum_jobs = Integer(required=False, missing=None)
running_jobs = Integer(required=False, missing=None)

@pre_load
def preprocess_field_names(self, data, **_): # type: ignore
Expand Down
17 changes: 17 additions & 0 deletions qiskit/providers/ibmq/backend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2020.
#
# 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.

"""Utilities related to IBMQBackend."""

from .backendjoblimit import BackendJobLimit
38 changes: 38 additions & 0 deletions qiskit/providers/ibmq/backend/backendjoblimit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2020.
#
# 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.

"""Job limit information related to a backend."""

from typing import Any

from qiskit.validation import BaseModel, bind_schema
from ..api.rest.validation import BackendJobLimitResponseSchema


@bind_schema(BackendJobLimitResponseSchema)
class BackendJobLimit(BaseModel):
"""Jobs limit for a backend."""
def __init__(self, maximum_jobs: int, running_jobs: int, **kwargs: Any) -> None:
"""Creates a new BackendJobLimit instance.

Args:
maximum_jobs: maximum number of concurrent jobs this account is
allowed to submit to this backend with this provider at a time.
running_jobs: current number of jobs running on this backend with
this provider.
kwargs: additional attributes that will be added as instance members.
"""
self.maximum_jobs = maximum_jobs
self.running_jobs = running_jobs
super().__init__(**kwargs)
84 changes: 55 additions & 29 deletions qiskit/providers/ibmq/ibmqbackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
# (C) Copyright IBM 2017, 2020.
#
# 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
Expand All @@ -25,15 +25,14 @@
from qiskit.providers import BaseBackend, JobStatus # type: ignore[attr-defined]
from qiskit.providers.models import (BackendStatus, BackendProperties,
PulseDefaults, BackendConfiguration, GateConfig)
from qiskit.validation import BaseModel, bind_schema
from qiskit.validation.exceptions import ModelValidationError
from qiskit.tools.events.pubsub import Publisher
from qiskit.providers.ibmq import accountprovider # pylint: disable=unused-import
from qiskit.providers.ibmq.apiconstants import ApiJobShareLevel
from qiskit.providers.ibmq.api.rest.validation import BackendJobsLimitResponseSchema

from .api.clients import AccountClient
from .api.exceptions import ApiError
from .backend import BackendJobLimit
from .credentials import Credentials
from .exceptions import (IBMQBackendError, IBMQBackendValueError,
IBMQBackendApiError, IBMQBackendApiProtocolError)
Expand All @@ -43,22 +42,6 @@
logger = logging.getLogger(__name__)


@bind_schema(BackendJobsLimitResponseSchema)
class BackendJobsLimit(BaseModel):
"""Jobs limit for a backend."""
def __init__(self, maximum_jobs: int, running_jobs: int, **kwargs: Any) -> None:
"""Creates a new BackendJobsLimit instance.

Args:
maximum_jobs: maximum number of jobs for the backend.
running_jobs: current number of jobs running on the backend.
kwargs: additional attributes that will be added as instance members.
"""
self.maximum_jobs = maximum_jobs
self.running_jobs = running_jobs
super().__init__(**kwargs)


class IBMQBackend(BaseBackend):
"""Backend class interfacing with an IBMQ backend."""

Expand Down Expand Up @@ -272,29 +255,72 @@ def defaults(self, refresh: bool = False) -> Optional[PulseDefaults]:

return self._defaults

def jobs_limit(self) -> BackendJobsLimit:
"""Return the jobs limit for the backend.
def job_limit(self) -> BackendJobLimit:
"""Return job limit for the backend.

The job limit information may include the current number of
jobs running on the backend at this time and the maximum number
of concurrent jobs a user could submit to the backend at a time.

Note:
If `BackendJobsLimit.maximum_jobs = -1`, then there
are no limits to the maximum number of jobs that could
be submitted on the backend.
The job limit information for the backend is given for
this account (i.e. a specific backend with a specific
provider).

If the method call was successful, you can inspect the job
limit for the backend by accessing the ``maximum_jobs``
and ``running_jobs`` attributes of the ``BackendJobLimit``
class returned.

For example:
backend_job_limit = IBMQBackend.job_limit()
maximum_jobs = backend_job_limit.maximum_jobs
running_jobs = backend_job_limit.running_jobs

* If ``maximum_jobs`` or ``running_jobs`` are ``None``, the
job limit information is currently not available.
* If ``maximum_jobs`` is equal to ``-1``, then there are
no limits to the maximum number of concurrent jobs a user
could submit to the backend at a time.

Returns:
the current number of running jobs and the maximum number
of jobs for the backend.
the current number of jobs running on the backend and the
maximum number of concurrent jobs a user could submit to the
backend at a time.

Raises:
LookupError: If jobs limit for the backend can't be found.
"""
api_jobs_limit = self._api.backend_jobs_limit(self.name())
api_job_limit = self._api.backend_job_limit(self.name())

try:
return BackendJobsLimit.from_dict(api_jobs_limit)
return BackendJobLimit.from_dict(api_job_limit)
except ValidationError as ex:
raise LookupError(
"Couldn't get backend jobs limit: {0}".format(ex))

def remaining_jobs(self) -> int:
"""Return remaining jobs that could be submitted to the backend.

Return the remaining number of jobs that could be submitted to the
backend, for this account, before the job limit is reached. See
``IBMQBackend.job_limit()`` for the job limit of a given backend.

Note:
The number of remaining jobs for the backend is given for
this account (i.e. a specific backend with a specific
provider).

Returns:
Remaining number of jobs a user could submit to the backend
for this account.

Raises:
LookupError: If jobs limit for the backend can't be found.
"""
backend_job_limit = self.job_limit()
return backend_job_limit.maximum_jobs - backend_job_limit.running_jobs

def jobs(
self,
limit: int = 10,
Expand Down Expand Up @@ -473,7 +499,7 @@ def status(self) -> BackendStatus:
"""Return the online backend status."""
return self._status

def jobs_limit(self) -> None:
def job_limit(self) -> None:
"""Return the job limits for the backend."""
return None

Expand Down
10 changes: 6 additions & 4 deletions test/ibmq/test_account_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019.
# (C) Copyright IBM 2018, 2020.
#
# 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
Expand Down Expand Up @@ -164,12 +164,14 @@ def test_backend_properties(self, backend):
self.assertIsNotNone(properties)

@requires_device
def test_backend_jobs_limit(self, backend):
def test_backend_job_limit(self, backend):
"""Check the backend job limits of a real backend."""
api = self._get_client()

jobs_limit = api.backend_jobs_limit(backend.name())
self.assertIsNotNone(jobs_limit)
job_limit = api.backend_job_limit(backend.name())
self.assertIsNotNone(job_limit)
self.assertIsNotNone(job_limit['maximumJobs'])
self.assertIsNotNone(job_limit['runningJobs'])

def test_backend_pulse_defaults(self):
"""Check the backend pulse defaults of each backend."""
Expand Down