Skip to content

Commit

Permalink
Merge branch 'main' into dr-fix-dd-disjoint-maps
Browse files Browse the repository at this point in the history
  • Loading branch information
kt474 authored Aug 22, 2023
2 parents bd072f3 + d724b5d commit 91ebacc
Show file tree
Hide file tree
Showing 13 changed files with 85 additions and 152 deletions.
2 changes: 1 addition & 1 deletion docs/tutorials/1_the_ibm_quantum_account.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@
}
],
"source": [
"provider.backend.ibmq_qasm_simulator"
"provider.get_backend(\"ibmq_qasm_simulator\")"
]
},
{
Expand Down
5 changes: 5 additions & 0 deletions qiskit_ibm_provider/ibm_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ class IBMProvider(Provider):
simulator_backend = provider.get_backend('ibmq_qasm_simulator')
IBMBackend's are uniquely identified by their name. If you invoke :meth:`get_backend()` twice,
you will get the same IBMBackend instance, and any previously updated options will be reset
to the default values.
It is also possible to use the ``backend`` attribute to reference a backend.
As an example, to retrieve the same backend from the example above::
Expand Down Expand Up @@ -660,6 +664,7 @@ def get_backend(
)
if not backends:
raise QiskitBackendNotFoundError("No backend matches the criteria")
backends[0]._options = IBMBackend._default_options()
return backends[0]

def __repr__(self) -> str:
Expand Down
19 changes: 5 additions & 14 deletions qiskit_ibm_provider/jupyter/dashboard/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,11 @@ def _get_backends(self) -> None:
"""Get all the backends accessible with this account."""

ibm_backends = {}
for hgp in self.provider._get_hgps():
hgp_name = "{hub}/{group}/{project}".format(
hub=hgp._hub,
group=hgp._group,
project=hgp._project,
)
for backend in hgp.backends.values():
if not backend.configuration().simulator:
if backend.name not in ibm_backends:
ibm_backends[backend.name] = BackendWithProviders(
backend=backend, providers=[hgp_name]
)
else:
ibm_backends[backend.name].providers.append(hgp_name)
for backend in self.provider.backends():
if not backend.configuration().simulator:
ibm_backends[backend.name] = BackendWithProviders(
backend=backend, providers=[backend._instance]
)

self.backend_dict = ibm_backends

Expand Down
12 changes: 12 additions & 0 deletions releasenotes/notes/reset_backend_options-f7e02c9bfff58213.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
fixes:
- |
``IBMProvider.get_backend()`` returns the backend with its default options. At the end of this
.. code-block::
backend1 = provider.get_backend("xxx")
backend1.options.shots = 100
backend2 = provider.get_backend("xxx")
``backend2.options.shots`` has the default value (4000). ``backend1.options.shots``
also has the default value, because backend1 and backend2 are the same instance.
18 changes: 18 additions & 0 deletions test/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,26 @@
from dataclasses import dataclass
from functools import wraps
from typing import Callable, Optional
from unittest import SkipTest

from qiskit_ibm_provider import IBMProvider, least_busy
from .unit.mock.fake_provider import FakeProvider


def production_only(func):
"""Decorator that runs a test only on production services."""

@wraps(func)
def _wrapper(self, *args, **kwargs):
if "dev" in self.dependencies.url:
raise SkipTest(
f"Skipping integration test. {self} is not supported on staging."
)
func(self, *args, **kwargs)

return _wrapper


def run_fake_provider(func):
"""Decorator that runs a test using a fake provider."""

Expand Down Expand Up @@ -49,6 +64,7 @@ def integration_test_setup_with_backend(
backend_name: Optional[str] = None,
simulator: Optional[bool] = True,
min_num_qubits: Optional[int] = None,
staging: Optional[bool] = True,
) -> Callable:
"""Returns a decorator that retrieves the appropriate backend to use for testing.
Expand All @@ -70,6 +86,8 @@ def _decorator(func):
def _wrapper(self, *args, **kwargs):
dependencies: IntegrationTestDependencies = kwargs["dependencies"]
provider: IBMProvider = dependencies.provider
if not staging:
raise SkipTest("Tests not supported on staging.")
if backend_name:
_backend = provider.get_backend(
name=backend_name, instance=dependencies.instance
Expand Down
28 changes: 15 additions & 13 deletions test/integration/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ..decorators import (
IntegrationTestDependencies,
integration_test_setup_with_backend,
production_only,
)
from ..ibm_test_case import IBMTestCase
from ..utils import get_pulse_schedule, cancel_job
Expand Down Expand Up @@ -57,6 +58,7 @@ def test_backend_status(self):
self.dependencies.provider.backends()
self.assertTrue(self.backend.status().operational)

@production_only
def test_backend_properties(self):
"""Check the properties of calibration of a real chip."""
self.assertIsNotNone(self.backend.properties())
Expand Down Expand Up @@ -124,10 +126,9 @@ def test_sim_backend_options(self):
self.assertTrue(backend_options["memory"])
self.assertEqual(backend_options["foo"], "foo")

@production_only
def test_paused_backend_warning(self):
"""Test that a warning is given when running jobs on a paused backend."""
if "dev" in self.dependencies.url:
raise SkipTest("Not supported in staging.")
backend = self.dependencies.provider.get_backend("ibmq_qasm_simulator")
paused_status = backend.status()
paused_status.status_msg = "internal"
Expand Down Expand Up @@ -218,14 +219,15 @@ def test_too_many_qubits_in_circuit(self):
instance=self.dependencies.instance, simulator=False
)
least_busy_backend = least_busy(backends)
self.assertTrue(least_busy_backend)

num = len(least_busy_backend.properties().qubits)
num_qubits = num + 1
circuit = QuantumCircuit(num_qubits, num_qubits)
with self.assertRaises(IBMBackendValueError) as err:
_ = least_busy_backend.run(circuit)
self.assertIn(
f"Circuit contains {num_qubits} qubits, but backend has only {num}.",
str(err.exception),
)
if least_busy_backend.properties():
self.assertTrue(least_busy_backend)

num = len(least_busy_backend.properties().qubits)
num_qubits = num + 1
circuit = QuantumCircuit(num_qubits, num_qubits)
with self.assertRaises(IBMBackendValueError) as err:
_ = least_busy_backend.run(circuit)
self.assertIn(
f"Circuit contains {num_qubits} qubits, but backend has only {num}.",
str(err.exception),
)
2 changes: 1 addition & 1 deletion test/integration/test_filter_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class TestBackendFilters(IBMTestCase):
"""Qiskit Backend Filtering Tests."""

@classmethod
@integration_test_setup_with_backend()
@integration_test_setup_with_backend(staging=False)
def setUpClass(
cls, backend: IBMBackend, dependencies: IntegrationTestDependencies
) -> None:
Expand Down
3 changes: 3 additions & 0 deletions test/integration/test_ibm_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from ..decorators import (
IntegrationTestDependencies,
integration_test_setup_with_backend,
production_only,
)
from ..fake_account_client import BaseFakeAccountClient, CancelableFakeJob
from ..ibm_test_case import IBMTestCase
Expand Down Expand Up @@ -169,8 +170,10 @@ def test_retrieve_pending_jobs(self):
for job in pending_job_list:
self.assertTrue(job.status() in [JobStatus.QUEUED, JobStatus.RUNNING])

@production_only
def test_retrieve_running_error_jobs(self):
"""Test client side filtering with running and error jobs."""
self.sim_job.wait_for_final_state()
statuses = ["RUNNING", JobStatus.ERROR]
job_list = self.provider.backend.jobs(
backend_name=self.sim_backend.name, limit=3, status=statuses
Expand Down
36 changes: 1 addition & 35 deletions test/integration/test_ibm_job_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@
from qiskit_ibm_provider.api.clients.runtime import RuntimeClient
from qiskit_ibm_provider.exceptions import (
IBMBackendValueError,
IBMBackendApiProtocolError,
)
from qiskit_ibm_provider.job.exceptions import IBMJobFailureError
from ..decorators import (
IntegrationTestDependencies,
integration_test_setup,
)
from ..fake_account_client import BaseFakeAccountClient, MissingFieldFakeJob
from ..fake_account_client import BaseFakeAccountClient
from ..ibm_test_case import IBMTestCase
from ..utils import (
most_busy_backend,
Expand Down Expand Up @@ -275,19 +274,6 @@ def test_esp_readout_default_value(self):
delattr(self.sim_backend._configuration, "measure_esp_enabled")
self.sim_backend._api_client = saved_api

@skip("not supported by api")
def test_esp_readout_enabled_not_used(self):
"""Test that ESP readout is not used if user sets to ``False``, even if backend supports it."""
saved_api = self.sim_backend._api_client
try:
self.sim_backend._api_client = BaseFakeAccountClient()
setattr(self.sim_backend._configuration, "measure_esp_enabled", True)
job = self.sim_backend.run(self.bell, use_measure_esp=False)
self.assertEqual(job.backend_options()["use_measure_esp"], False)
finally:
delattr(self.sim_backend._configuration, "measure_esp_enabled")
self.sim_backend._api_client = saved_api

def test_job_tags(self):
"""Test using job tags."""
# Use a unique tag.
Expand Down Expand Up @@ -359,23 +345,3 @@ def test_cost_estimation(self):
"""Test cost estimation is returned correctly."""
self.assertTrue(self.sim_job.usage_estimation)
self.assertIn("quantum_seconds", self.sim_job.usage_estimation)

@skip("TODO refactor fake client")
def test_missing_required_fields(self):
"""Test response data is missing required fields."""
saved_api = self.sim_backend._api_client
try:
self.sim_backend._api_client = BaseFakeAccountClient(
job_class=MissingFieldFakeJob
)
self.assertRaises(
IBMBackendApiProtocolError, self.sim_backend.run, self.bell
)
finally:
self.sim_backend._api_client = saved_api

@skip("not supported by api")
def test_client_version(self):
"""Test job client version information."""
self.assertIsNotNone(self.sim_job.result().client_version)
self.assertIsNotNone(self.sim_job.client_version)
15 changes: 15 additions & 0 deletions test/integration/test_ibm_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
IntegrationTestDependencies,
integration_test_setup,
integration_test_setup_with_backend,
production_only,
)
from ..ibm_test_case import IBMTestCase

Expand Down Expand Up @@ -140,6 +141,7 @@ def test_active_account_instance(self):
)
self.assertEqual(hgp.name, provider.active_account()["instance"])

@production_only
def test_active_account_with_saved_instance(self):
"""Test active_account with a saved instance."""
hgp = self.provider._get_hgp()
Expand Down Expand Up @@ -200,6 +202,19 @@ def test_get_backend(self):
backend = self.dependencies.provider.get_backend(name=self.backend_name)
self.assertEqual(backend.name, self.backend_name)

def test_get_backend_options(self):
"""Test resetting backend options when calling get_backend."""
default_shots = 4000
backend1 = self.dependencies.provider.get_backend(name=self.backend_name)
self.assertEqual(backend1.options.shots, default_shots)
backend1.options.shots = 100
self.assertEqual(backend1.options.shots, 100)
backend2 = self.dependencies.provider.get_backend(name=self.backend_name)
# When getting a backend, it has the default options.
# backend1 and backend2 are the same instance.
self.assertEqual(backend2.options.shots, default_shots)
self.assertEqual(backend1.options.shots, default_shots)

def test_jobs_filter(self):
"""Test limit filters when accessing jobs from the provider."""
num_jobs = PAGE_SIZE + 1
Expand Down
12 changes: 7 additions & 5 deletions test/integration/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@
"""Test serializing and deserializing data sent to the server."""

from typing import Set, Any, Dict, Optional
from unittest import SkipTest, skipIf, skip
from unittest import SkipTest, skip

import dateutil.parser
from qiskit import transpile, schedule, QuantumCircuit
from qiskit.circuit import Parameter
from qiskit.test.reference_circuits import ReferenceCircuits
from qiskit.version import VERSION as terra_version

from qiskit_ibm_provider import least_busy
from qiskit_ibm_provider.utils.json_encoder import IBMJsonEncoder
from ..decorators import IntegrationTestDependencies, integration_test_setup
from ..decorators import (
IntegrationTestDependencies,
integration_test_setup,
production_only,
)
from ..ibm_test_case import IBMTestCase
from ..utils import cancel_job

Expand Down Expand Up @@ -112,6 +114,7 @@ def test_pulse_defaults(self):
with self.subTest(backend=backend):
self._verify_data(backend.defaults().to_dict(), good_keys)

@production_only
def test_backend_properties(self):
"""Test deserializing backend properties."""
backends = self.dependencies.provider.backends(
Expand Down Expand Up @@ -185,7 +188,6 @@ def _verify_data(
}
self.assertFalse(suspect_keys)

@skipIf(terra_version < "0.17", "Need Terra >= 0.17")
def test_convert_complex(self):
"""Verify that real and complex ParameterExpressions are supported."""
param = Parameter("test")
Expand Down
Loading

0 comments on commit 91ebacc

Please sign in to comment.