Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update code that is pending deprecation #301

Merged
merged 11 commits into from
May 4, 2023
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
now been removed. The `upload_vqe_runner` and `delete_vqe_runner` functions have also been removed.
[(#298)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/298)

### Improvements

* Updated many small things across the plugin to match re-works and deprecations in `qiskit`. The plugin
can still be used in the same way as before. However, we suggest you authenticate with
`qiskit_ibm_provider.IBMProvider` instead of `qiskit.IBMQ` from now on, as the latter is deprecated.
[(#301)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/301)

### Contributors

This release contains contributions from (in alphabetical order):
Expand Down
5 changes: 4 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ in the source folder. Tests restricted to a specific provider can be run by exec
only be run if a ``ibmqx_token`` for the
`IBM Q experience <https://quantum-computing.ibm.com/>`_ is
configured in the `PennyLane configuration file
<https://pennylane.readthedocs.io/en/latest/introduction/configuration.html>`_.
<https://pennylane.readthedocs.io/en/latest/introduction/configuration.html>`_, if the token is
exported in your environment under the name ``IBMQX_TOKEN``, or if you have previously saved your
account credentials using the
`new IBMProvider <https://qiskit.org/ecosystem/ibm-provider/stubs/qiskit_ibm_provider.IBMProvider.html>`_

If this is the case, running ``make test`` also executes tests on the ``ibmq`` device.
By default tests on the ``ibmq`` device run with ``ibmq_qasm_simulator`` backend
Expand Down
6 changes: 3 additions & 3 deletions doc/devices/aer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ or, alternatively,

.. code-block:: python

from qiskit import Aer
from qiskit_aer import Aer
Aer.backends()

.. note::
Expand Down Expand Up @@ -84,7 +84,7 @@ The options are set via additional keyword arguments:
)

For more information on available methods and their options, please visit the `AerSimulator
documentation <https://qiskit.org/documentation/stubs/qiskit.providers.aer.AerSimulator.html>`_.
documentation <https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.AerSimulator.html>`_.

.. warning::

Expand All @@ -103,7 +103,7 @@ which you can instantiate and apply to the device as follows
import pennylane as qml

import qiskit
import qiskit.providers.aer.noise as noise
from qiskit_aer import noise

# Error probabilities
prob_1 = 0.001 # 1-qubit gate
Expand Down
15 changes: 10 additions & 5 deletions doc/devices/ibmq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ IBM Q account. If the device finds no account it will raise an error:

'No active IBM Q account, and no IBM Q token provided.

You can use the ``qiskit.IBMQ.save_account("<my_token>")`` function to permanently store an account,
and the ``qiskit.IBMQ.load_account()`` function to load the stored account in a given session.
You can use the ``qiskit_ibm_provider.IBMProvider.save_account("<my_token>")`` function to permanently
store an account, and the account will be automatically used from then onward.
Alternatively, you can specify the token with PennyLane via the
`PennyLane configuration file <https://pennylane.readthedocs.io/en/latest/introduction/configuration.html>`__ by
adding the section
Expand All @@ -34,6 +34,11 @@ You may also directly pass your IBM Q API token to the device:

dev = qml.device('qiskit.ibmq', wires=2, backend='ibmq_qasm_simulator', ibmqx_token="XXX")

You may also save your token as an environment variable by running the following in a terminal:

.. code::

export IBMQX_TOKEN=<my_token>
timmysilv marked this conversation as resolved.
Show resolved Hide resolved

.. warning:: Never publish code containing your token online.

Expand All @@ -58,8 +63,8 @@ Custom providers can be passed as arguments when a ``qiskit.ibmq`` device is cre

.. code-block:: python

from qiskit import IBMQ
provider = IBMQ.enable_account('XYZ')
from qiskit_ibm_provider import IBMProvider
provider = IBMProvider("XYZ")

import pennylane as qml
dev = qml.device('qiskit.ibmq', wires=2, backend='ibmq_qasm_simulator', provider=provider)
Expand All @@ -76,4 +81,4 @@ Custom provider options can also be passed as keyword arguments when creating a
ibmqx_token='XXX', hub='MYHUB', group='MYGROUP', project='MYPROJECT')

More details on Qiskit providers can be found
in the `IBMQ provider documentation <https://qiskit.org/documentation/apidoc/ibmq-provider.html>`_.
in the `IBMQ provider documentation <https://qiskit.org/ecosystem/ibm-provider/stubs/qiskit_ibm_provider.IBMProvider.html>`_.
3 changes: 1 addition & 2 deletions doc/devices/runtime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ We created a wrapper to use PennyLane objects while solving VQE problems on IBM

from pennylane_qiskit import vqe_runner

IBMQ.enable_account(token)

def vqe_circuit(params):
qml.RX(params[0], wires=0)
qml.RY(params[1], wires=0)

coeffs = [1, 1]
obs = [qml.PauliX(0), qml.PauliZ(0)]
hamiltonian = qml.Hamiltonian(coeffs, obs)
shots = 8000

job = vqe_runner(
backend="ibmq_qasm_simulator",
Expand Down
2 changes: 1 addition & 1 deletion doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ follows:

PennyLane chooses the ``qasm_simulator`` as the default backend if no backend is specified.
For more details on the ``qasm_simulator``, including available backend options, see
`Qiskit Qasm Simulator documentation <https://qiskit.org/documentation/stubs/qiskit.providers.aer.QasmSimulator.html>`_.
`Qiskit Qasm Simulator documentation <https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.QasmSimulator.html>`_.

Tutorials
~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions doc/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pygments==2.7.4
pygments-github-lexers==0.0.5
qiskit==0.42.1
qiskit-ibm-runtime==0.9.3
timmysilv marked this conversation as resolved.
Show resolved Hide resolved
qiskit-ibm-provider==0.5.2
sphinxcontrib-bibtex==2.5.0
sphinx-automodapi==0.14.1
pennylane-sphinx-theme
12 changes: 6 additions & 6 deletions pennylane_qiskit/aer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
evaluation and differentiation of Qiskit Aer's C++ simulator
using PennyLane.
"""
import qiskit
import qiskit_aer

from .qiskit_device import QiskitDevice

Expand All @@ -31,16 +31,16 @@ class AerDevice(QiskitDevice):
a range of transpile options can be given as kwargs.

For more information on backends, please visit the
`Aer provider documentation <https://qiskit.org/documentation/apidoc/aer_provider.html>`_.
`qiskit_aer documentation <https://qiskit.org/ecosystem/aer/index.html>`_.

Args:
wires (int or Iterable[Number, str]]): Number of subsystems represented by the device,
or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``)
or strings (``['ancilla', 'q1', 'q2']``).
backend (str): the desired backend
method (str): The desired simulation method. A list of supported simulation
methods can be returned using ``qiskit.Aer.available_methods()``, or by referring
to the ``AerSimulator`` `documentation <https://qiskit.org/documentation/stubs/qiskit.providers.aer.AerSimulator.html>`__.
methods can be returned using ``qiskit_aer.AerSimulator().available_methods()``, or by referring
timmysilv marked this conversation as resolved.
Show resolved Hide resolved
to the ``AerSimulator`` `documentation <https://qiskit.org/ecosystem/aer/stubs/qiskit_aer.AerSimulator.html>`__.
shots (int or None): number of circuit evaluations/random samples used
to estimate expectation values and variances of observables. For statevector backends,
setting to ``None`` results in computing statistics like expectation values and variances analytically.
Expand All @@ -49,7 +49,7 @@ class AerDevice(QiskitDevice):
name (str): The name of the circuit. Default ``'circuit'``.
compile_backend (BaseBackend): The backend used for compilation. If you wish
to simulate a device compliant circuit, you can specify a backend here.
noise_model (NoiseModel): NoiseModel Object from ``qiskit.providers.aer.noise``
noise_model (NoiseModel): NoiseModel Object from ``qiskit_aer.noise``
"""

# pylint: disable=too-many-arguments
Expand All @@ -60,4 +60,4 @@ def __init__(self, wires, shots=1024, backend="aer_simulator", method="automatic
if method != "automatic":
backend += "_" + method

super().__init__(wires, provider=qiskit.Aer, backend=backend, shots=shots, **kwargs)
super().__init__(wires, provider=qiskit_aer.Aer, backend=backend, shots=shots, **kwargs)
106 changes: 57 additions & 49 deletions pennylane_qiskit/ibmq.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
"""
import os

from qiskit import IBMQ
from qiskit.providers.ibmq.exceptions import IBMQAccountError
from qiskit_ibm_provider import IBMProvider
from qiskit_ibm_provider.exceptions import IBMAccountError
from qiskit_ibm_provider.accounts.exceptions import AccountsError
from qiskit_ibm_provider.job import IBMJobError

from .qiskit_device import QiskitDevice

Expand All @@ -41,17 +43,19 @@ class IBMQDevice(QiskitDevice):
or strings (``['ancilla', 'q1', 'q2']``). Note that for some backends, the number
of wires has to match the number of qubits accessible.
provider (Provider): The IBM Q provider you wish to use. If not provided,
then the default provider returned by ``IBMQ.get_provider()`` is used.
then the default provider returned by ``IBMProvider()`` is used.
backend (str): the desired provider backend
shots (int): number of circuit evaluations/random samples used
to estimate expectation values and variances of observables
timeout_secs (int): A timeout value in seconds to wait for job results from an IBMQ backend.
The default value of ``None`` means no timeout

Keyword Args:
ibmqx_token (str): The IBM Q API token. If not provided, the environment
variable ``IBMQX_TOKEN`` is used.
ibmqx_url (str): The IBM Q URL. If not provided, the environment
variable ``IBMQX_URL`` is used, followed by the default URL.
noise_model (NoiseModel): NoiseModel Object from ``qiskit.providers.aer.noise``.
noise_model (NoiseModel): NoiseModel Object from ``qiskit_aer.noise``.
Only applicable for simulator backends.
hub (str): Name of the provider hub.
group (str): Name of the provider group.
Expand All @@ -60,36 +64,55 @@ class IBMQDevice(QiskitDevice):

short_name = "qiskit.ibmq"

def __init__(self, wires, provider=None, backend="ibmq_qasm_simulator", shots=1024, **kwargs):
def __init__(
self,
wires,
provider=None,
backend="ibmq_qasm_simulator",
shots=1024,
timeout_secs=None,
**kwargs,
): # pylint:disable=too-many-arguments
# Connection to IBMQ
connect(kwargs)

hub = kwargs.get("hub", "ibm-q")
group = kwargs.get("group", "open")
project = kwargs.get("project", "main")
instance = "/".join([hub, group, project])

# get a provider
p = provider or IBMQ.get_provider(hub=hub, group=group, project=project)
p = provider or IBMProvider(instance=instance)

super().__init__(wires=wires, provider=p, backend=backend, shots=shots, **kwargs)
self.timeout_secs = timeout_secs

def batch_execute(self, circuits): # pragma: no cover
res = super().batch_execute(circuits)
def batch_execute(self, circuits): # pragma: no cover, pylint:disable=arguments-differ
res = super().batch_execute(circuits, timeout=self.timeout_secs)
if self.tracker.active:
self._track_run()
return res

def _track_run(self): # pragma: no cover
"""Provide runtime information."""

expected_keys = {"created", "running", "finished"}
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
time_per_step = self._current_job.time_per_step()
if not set(time_per_step).issuperset(expected_keys):
# self._current_job.result() should have already run by now
# tests see a race condition, so this is ample time for that case
timeout_secs = self.timeout_secs or 60
self._current_job.wait_for_final_state(timeout=timeout_secs)
self._current_job.refresh()
time_per_step = self._current_job.time_per_step()
if not set(time_per_step).issuperset(expected_keys):
raise IBMJobError(
f"time_per_step had keys {set(time_per_step)}, needs {expected_keys}. If your program takes a long time, you may want to configure the device with a higher `timeout_secs`"
)

job_time = {
"creating": (time_per_step["CREATED"] - time_per_step["CREATING"]).total_seconds(),
"validating": (
time_per_step["VALIDATED"] - time_per_step["VALIDATING"]
).total_seconds(),
"queued": (time_per_step["RUNNING"] - time_per_step["QUEUED"]).total_seconds(),
"running": (time_per_step["COMPLETED"] - time_per_step["RUNNING"]).total_seconds(),
"queued": (time_per_step["running"] - time_per_step["created"]).total_seconds(),
"running": (time_per_step["finished"] - time_per_step["running"]).total_seconds(),
}
self.tracker.update(job_time=job_time)
self.tracker.record()
Expand All @@ -101,41 +124,26 @@ def connect(kwargs):
Args:
kwargs(dict): dictionary that contains the token and the url"""

hub = kwargs.get("hub", "ibm-q")
group = kwargs.get("group", "open")
project = kwargs.get("project", "main")
instance = "/".join([hub, group, project])

token = kwargs.get("ibmqx_token", None) or os.getenv("IBMQX_TOKEN")
url = kwargs.get("ibmqx_url", None) or os.getenv("IBMQX_URL")

# TODO: remove "no cover" when #173 is resolved
if token: # pragma: no cover
# token was provided by the user, so attempt to enable an
# IBM Q account manually
def login():
ibmq_kwargs = {"url": url} if url is not None else {}
IBMQ.enable_account(token, **ibmq_kwargs)

active_account = IBMQ.active_account()
if active_account is None:
login()
else:
# There is already an active account:
# If the token is the same, do nothing.
# If the token is different, authenticate with the new account.
if active_account["token"] != token:
IBMQ.disable_account()
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
login()
else:
# check if an IBM Q account is already active.
#
# * IBMQ v2 credentials stored in active_account().
# If no accounts are active, it returns None.

if IBMQ.active_account() is None:
# no active account
try:
# attempt to load a v2 account stored on disk
IBMQ.load_account()
except IBMQAccountError:
# attempt to enable an account manually using
# a provided token
raise IBMQAccountError(
"No active IBM Q account, and no IBM Q token provided."
) from None
saved_accounts = IBMProvider.saved_accounts()
if not token:
if not saved_accounts:
raise IBMAccountError("No active IBM Q account, and no IBM Q token provided.")
try:
IBMProvider(url=url, instance=instance)
except AccountsError as e:
raise AccountsError(
f"Accounts were found ({set(saved_accounts)}), but all failed to load."
) from e
return
for account in saved_accounts.values():
if account["token"] == token:
return
IBMProvider.save_account(token=token, url=url, instance=instance)
12 changes: 9 additions & 3 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,13 @@ def __init__(self, wires, provider, backend, shots=1024, **kwargs):

self.provider = provider
self.backend_name = backend
self._capabilities["backend"] = [b.name() for b in self.provider.backends()]

def _get_backend_name(name):
return name if isinstance(name, str) else name()
rmoyard marked this conversation as resolved.
Show resolved Hide resolved

self._capabilities["backend"] = [
_get_backend_name(b.name) for b in self.provider.backends()
]
self._capabilities["returns_state"] = backend in self._state_backends

# Check that the backend exists
Expand Down Expand Up @@ -453,14 +459,14 @@ def compile_circuits(self, circuits):

return compiled_circuits

def batch_execute(self, circuits):
def batch_execute(self, circuits, timeout: int = None):
# pylint: disable=missing-function-docstring

compiled_circuits = self.compile_circuits(circuits)

# Send the batch of circuit objects using backend.run
self._current_job = self.backend.run(compiled_circuits, shots=self.shots, **self.run_args)
result = self._current_job.result()
result = self._current_job.result(timeout=timeout)

# increment counter for number of executions of qubit device
self._num_executions += 1
Expand Down
Loading