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

Handle Qiskit 1.0 removals #804

Merged
merged 8 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions qiskit_ibm_provider/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import numpy as np

from qiskit import circuit as circuit_mod
from qiskit import extensions
from qiskit.circuit import library, controlflow, CircuitInstruction, ControlFlowOp
from qiskit.circuit.classical import expr
from qiskit.circuit.classicalregister import ClassicalRegister, Clbit
Expand All @@ -34,7 +33,6 @@
from qiskit.circuit.instruction import Instruction
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit.quantumregister import QuantumRegister, Qubit
from qiskit.extensions import quantum_initializer
from qiskit.quantum_info.operators import SparsePauliOp
from qiskit.synthesis import evolution as evo_synth
from qiskit.transpiler.layout import Layout, TranspileLayout
Expand Down Expand Up @@ -307,10 +305,6 @@ def _read_instruction( # type: ignore[no-untyped-def]
gate_class = getattr(library, gate_name)
elif hasattr(circuit_mod, gate_name):
gate_class = getattr(circuit_mod, gate_name)
elif hasattr(extensions, gate_name):
gate_class = getattr(extensions, gate_name)
elif hasattr(quantum_initializer, gate_name):
gate_class = getattr(quantum_initializer, gate_name)
elif hasattr(controlflow, gate_name):
gate_class = getattr(controlflow, gate_name)
else:
Expand Down Expand Up @@ -637,8 +631,6 @@ def _write_instruction( # type: ignore[no-untyped-def]
(
not hasattr(library, gate_class_name)
and not hasattr(circuit_mod, gate_class_name)
and not hasattr(extensions, gate_class_name)
and not hasattr(quantum_initializer, gate_class_name)
and not hasattr(controlflow, gate_class_name)
)
or gate_class_name == "Gate"
Expand Down
16 changes: 11 additions & 5 deletions qiskit_ibm_provider/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,20 @@ class Session:
and submit one or more jobs.

For example::

from qiskit.test.reference_circuits import ReferenceCircuits
from qiskit_ibm_provider import IBMProvider

circ = ReferenceCircuits.bell()
# Bell Circuit
qr = QuantumRegister(2, name="qr")
cr = ClassicalRegister(2, name="cr")
qc = QuantumCircuit(qr, cr, name="bell")
qc.h(qr[0])
qc.cx(qr[0], qr[1])
qc.measure(qr, cr)

backend = IBMProvider().get_backend("ibmq_qasm_simulator")
backend.open_session()
job = backend.run(circ)

job = backend.run(qc)
print(f"Job ID: {job.job_id()}")
print(f"Result: {job.result()}")
# Close the session only if all jobs are finished and
Expand All @@ -49,7 +55,7 @@ class Session:
Session can also be used as a context manager::

with backend.open_session() as session:
job = backend.run(ReferenceCircuits.bell())
job = backend.run(qc)
assert job.job_id() == session.session_id

"""
Expand Down
2 changes: 0 additions & 2 deletions qiskit_ibm_provider/test/ibm_provider_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,12 @@ def mock_get_backend(backend):
Raises:
NameError: If the specified value of backend
"""
print(backend_mocks)
mock_ibm_provider = MagicMock()
if not hasattr(backend_mocks, backend):
raise NameError(
"The specified backend name is not a valid backend from "
"qiskit.providers.fake_provider"
)
print(backend_mocks, backend, getattr(backend_mocks, backend))
fake_backend = getattr(backend_mocks, backend)()
mock_ibm_provider.get_backend.return_value = fake_backend
mock_ibm_provider.return_value = mock_ibm_provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@
from qiskit.circuit.reset import Reset
from qiskit.dagcircuit import DAGCircuit, DAGNode, DAGInNode, DAGOpNode
from qiskit.quantum_info.operators.predicates import matrix_equal
from qiskit.quantum_info.synthesis import OneQubitEulerDecomposer
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.instruction_durations import InstructionDurations
from qiskit.transpiler.passes.optimization import Optimize1qGates
from qiskit.transpiler import CouplingMap

from .block_base_padder import BlockBasePadder

try:
from qiskit.quantum_info.synthesis import OneQubitEulerDecomposer
except ImportError:
from qiskit.synthesis import OneQubitEulerDecomposer


class PadDynamicalDecoupling(BlockBasePadder):
"""Dynamical decoupling insertion pass for IBM dynamic circuit backends.
Expand Down
16 changes: 6 additions & 10 deletions test/e2e/test_real_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,13 @@
QuantumCircuit,
QuantumRegister,
)
from qiskit.test.reference_circuits import ReferenceCircuits
from qiskit.providers.jobstatus import JobStatus
from qiskit_ibm_provider.job.exceptions import IBMJobFailureError

from qiskit_ibm_provider.ibm_backend import IBMBackend, QOBJRUNNERPROGRAMID

from ..ibm_test_case import IBMTestCase
from ..utils import (
cancel_job,
submit_job_one_bad_instr,
)
from ..utils import cancel_job, submit_job_one_bad_instr, bell
from ..decorators import (
IntegrationTestDependencies,
integration_test_setup_with_backend,
Expand Down Expand Up @@ -63,7 +59,7 @@ def test_job_submission(self):
filters=lambda b: b.configuration().n_qubits >= 5,
)[0]
with self.subTest(provider=provider, backend=backend):
job = self._submit_job_with_retry(ReferenceCircuits.bell(), backend)
job = self._submit_job_with_retry(bell(), backend)

# Fetch the results.
result = job.result()
Expand All @@ -88,7 +84,7 @@ def test_job_backend_properties_and_status(self):
filters=lambda b: b.configuration().n_qubits >= 5,
)[0]
with self.subTest(backend=backend):
job = self._submit_job_with_retry(ReferenceCircuits.bell(), backend)
job = self._submit_job_with_retry(bell(), backend)
self.assertIsNotNone(job.properties())
self.assertTrue(job.status())
# Cancel job so it doesn't consume more resources.
Expand All @@ -98,7 +94,7 @@ def test_run_device(self):
"""Test running in a real device."""
shots = 8192
job = self.real_device_backend.run(
transpile(ReferenceCircuits.bell(), backend=self.real_device_backend),
transpile(bell(), backend=self.real_device_backend),
shots=shots,
program_id=QOBJRUNNERPROGRAMID,
)
Expand All @@ -107,7 +103,7 @@ def test_run_device(self):
result = job.result()
counts_qx = result.get_counts(0)
counts_ex = {"00": shots / 2, "11": shots / 2}
self.assertDictAlmostEqual(counts_qx, counts_ex, shots * 0.2)
self.assert_dict_almost_equal(counts_qx, counts_ex, shots * 0.2)

def test_run_multiple_device(self):
"""Test running multiple jobs in a real device."""
Expand Down Expand Up @@ -197,7 +193,7 @@ def test_headers_in_result_devices(self):
def test_websockets_device(self):
"""Test checking status of a job via websockets for a device."""
job = self.real_device_backend.run(
transpile(ReferenceCircuits.bell(), self.real_device_backend), shots=1
transpile(bell(), self.real_device_backend), shots=1
)

# Manually disable the non-websocket polling.
Expand Down
117 changes: 111 additions & 6 deletions test/ibm_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,27 @@
import os
import time
from typing import List, Any
from qiskit.test.base import BaseQiskitTestCase

from qiskit_ibm_provider import QISKIT_IBM_PROVIDER_LOGGER_NAME
from unittest import TestCase
from unittest.util import safe_repr
from qiskit import QuantumCircuit
from qiskit_ibm_provider import QISKIT_IBM_PROVIDER_LOGGER_NAME, IBMBackend, IBMJob
from qiskit_ibm_provider.apiconstants import ApiJobStatus, API_JOB_FINAL_STATES
from qiskit_ibm_provider.job.exceptions import IBMJobNotFoundError
from .utils import setup_test_logging
from .decorators import IntegrationTestDependencies


class IBMTestCase(BaseQiskitTestCase):
class IBMTestCase(TestCase):
"""Custom TestCase for use with qiskit-ibm-provider."""

log: logging.Logger
dependencies: IntegrationTestDependencies
sim_backend: IBMBackend
backend: IBMBackend
bell: QuantumCircuit
real_device_backend: IBMBackend
sim_job: IBMJob

@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down Expand Up @@ -68,6 +78,101 @@ def _set_logging_level(cls, logger: logging.Logger) -> None:
logger.addHandler(logging.StreamHandler())
logger.propagate = False

def assert_dict_almost_equal(
self, dict1, dict2, delta=None, msg=None, places=None, default_value=0
):
"""Assert two dictionaries with numeric values are almost equal.

Fail if the two dictionaries are unequal as determined by
comparing that the difference between values with the same key are
not greater than delta (default 1e-8), or that difference rounded
to the given number of decimal places is not zero. If a key in one
dictionary is not in the other the default_value keyword argument
will be used for the missing value (default 0). If the two objects
compare equal then they will automatically compare almost equal.

Args:
dict1 (dict): a dictionary.
dict2 (dict): a dictionary.
delta (number): threshold for comparison (defaults to 1e-8).
msg (str): return a custom message on failure.
places (int): number of decimal places for comparison.
default_value (number): default value for missing keys.

Raises:
TypeError: if the arguments are not valid (both `delta` and
`places` are specified).
AssertionError: if the dictionaries are not almost equal.
"""

error_msg = self.dicts_almost_equal(dict1, dict2, delta, places, default_value)

if error_msg:
msg = self._formatMessage(msg, error_msg)
raise self.failureException(msg)

def dicts_almost_equal(
self, dict1, dict2, delta=None, places=None, default_value=0
):
"""Test if two dictionaries with numeric values are almost equal.

Fail if the two dictionaries are unequal as determined by
comparing that the difference between values with the same key are
not greater than delta (default 1e-8), or that difference rounded
to the given number of decimal places is not zero. If a key in one
dictionary is not in the other the default_value keyword argument
will be used for the missing value (default 0). If the two objects
compare equal then they will automatically compare almost equal.

Args:
dict1 (dict): a dictionary.
dict2 (dict): a dictionary.
delta (number): threshold for comparison (defaults to 1e-8).
places (int): number of decimal places for comparison.
default_value (number): default value for missing keys.

Raises:
TypeError: if the arguments are not valid (both `delta` and
`places` are specified).

Returns:
String: Empty string if dictionaries are almost equal. A description
of their difference if they are deemed not almost equal.
"""

def valid_comparison(value):
"""compare value to delta, within places accuracy"""
if places is not None:
return round(value, places) == 0
else:
return value < delta

# Check arguments.
if dict1 == dict2:
return ""
if places is not None:
if delta is not None:
raise TypeError("specify delta or places not both")
msg_suffix = " within %s places" % places
else:
delta = delta or 1e-8
msg_suffix = " within %s delta" % delta

# Compare all keys in both dicts, populating error_msg.
error_msg = ""
for key in set(dict1.keys()) | set(dict2.keys()):
val1 = dict1.get(key, default_value)
val2 = dict2.get(key, default_value)
if not valid_comparison(abs(val1 - val2)):
error_msg += (
f"({safe_repr(key)}: {safe_repr(val1)} != {safe_repr(val2)}), "
)

if error_msg:
return error_msg[:-2] + msg_suffix
else:
return ""

def setUp(self) -> None:
"""Test level setup."""
super().setUp()
Expand All @@ -80,8 +185,8 @@ def tearDown(self) -> None:
failed = False
# It's surprisingly difficult to find out whether the test failed.
# Using a private attribute is not ideal but it'll have to do.
if self._outcome and hasattr(self._outcome, "errors"):
for _, exc_info in self._outcome.errors:
if self._outcome and hasattr(self._outcome, "errors"): # type: ignore[attr-defined]
for _, exc_info in self._outcome.errors: # type: ignore[attr-defined]
if exc_info is not None:
failed = True

Expand Down
8 changes: 4 additions & 4 deletions test/integration/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from qiskit import QuantumCircuit, transpile
from qiskit.providers.models import QasmBackendConfiguration
from qiskit.providers.exceptions import QiskitBackendNotFoundError
from qiskit.test.reference_circuits import ReferenceCircuits

from qiskit_ibm_provider import IBMBackend, IBMProvider
from qiskit_ibm_provider.ibm_qubit_properties import IBMQubitProperties
Expand All @@ -30,6 +29,7 @@
production_only,
)
from ..ibm_test_case import IBMTestCase
from ..utils import bell


class TestIBMBackend(IBMTestCase):
Expand Down Expand Up @@ -91,7 +91,7 @@ def test_sim_backend_options(self):
backend = provider.get_backend("ibmq_qasm_simulator")
backend.options.shots = 2048
backend.set_options(memory=True)
job = backend.run(ReferenceCircuits.bell(), shots=1024, foo="foo")
job = backend.run(bell(), shots=1024, foo="foo")
backend_options = provider.backend.retrieve_job(job.job_id()).backend_options()
self.assertEqual(backend_options["shots"], 1024)
self.assertTrue(backend_options["memory"])
Expand All @@ -105,7 +105,7 @@ def test_paused_backend_warning(self):
paused_status.status_msg = "internal"
backend.status = mock.MagicMock(return_value=paused_status)
with self.assertWarns(Warning):
backend.run(ReferenceCircuits.bell())
backend.run(bell())

def test_deprecate_id_instruction(self):
"""Test replacement of 'id' Instructions with 'Delay' instructions."""
Expand Down Expand Up @@ -186,7 +186,7 @@ def test_too_many_qubits_in_circuit(self):

def test_job_backend_properties(self):
"""Test job backend properties."""
job = self.backend.run(ReferenceCircuits.bell())
job = self.backend.run(bell())
backend_version = self.backend.properties().backend_version
job_version = job.properties().backend_version
self.assertEqual(job_version, backend_version)
6 changes: 4 additions & 2 deletions test/integration/test_basic_server_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@
from datetime import datetime, timedelta

from qiskit import transpile
from qiskit.test.reference_circuits import ReferenceCircuits

from qiskit_ibm_provider.exceptions import IBMBackendJobLimitError
from ..decorators import (
IntegrationTestDependencies,
integration_test_setup,
)
from ..ibm_test_case import IBMTestCase
from ..utils import bell


class TestBasicServerPaths(IBMTestCase):
"""Test the basic server endpoints using both a public and premium provider."""

last_week: datetime

@classmethod
@integration_test_setup()
def setUpClass(cls, dependencies: IntegrationTestDependencies) -> None:
Expand All @@ -45,7 +47,7 @@ def test_retrieve_jobs(self):
raise self.skipTest("Skip test because there is no private provider")
backend = self.dependencies[provider].get_backend(backend_name)
with self.subTest(provider=provider, backend=backend):
job = self._submit_job_with_retry(ReferenceCircuits.bell(), backend)
job = self._submit_job_with_retry(bell(), backend)
job_id = job.job_id()

retrieved_jobs = self.dependencies[provider].backend.jobs(
Expand Down
Loading
Loading