Skip to content

Commit

Permalink
Add circuit-runner, sampler and custom VQE programs from Qiskit-Runti…
Browse files Browse the repository at this point in the history
…me (#157)

* Small change.

* First commit

* Working circuit runner.

* Sampler

* Sampler

* VQE is working.

* Headers.

* Update for batches.

* Update docstrigs.

* CodeFactor.

* Add tests for runtime.

* Tests.

* Test.

* Test circuit runner.

* Test Sampler.

* Correct generate_samples

* Add entry points.

* More shots.

* Add token arg.

* Change import

* Change Sampler sample.

* Circuit runner kwargs test.

* Shots kwargs

* Change import.

* Add kwargs Sampler test.

* Import.

* Impport again.

* Import change

* Add test tracker.

* Update track tests.

* Update tracker runtime.

* Update tracker.

* Update.

* Codfactor changes.

* Update.

* Update codefactor

* More codefactor.

* Add callback

* Update

* Move vqe

* Update.

* Add test VQE.

* Update.

* delete

* Update VQE test.

* Add shots.

* Update test

* Update delete

* Unused import.

* More iteration.

* m

* Change tol.

* More VQE tests.

* Black

* Update.

* Update.

* Update

* Update pennylane_qiskit/vqe/__init__.py

Co-authored-by: antalszava <[email protected]>

* Update from review.

* Update tests.

* Typo.

* Update from tests.

* Runtime updated.

* Runtime updated.

* Update vqe

* Update

* Update from Antal review.

* Update

* Black.

* More test.

* Update parameters

* Codefactor update.

* Update.

* Update.

* Change order jac.

* Typo.

* Params.

* Update tests.

* ValueError.

* print

* Update.

* Tol.

* Update review.

* Add doc.

* Unused variable.

* Update tests.

* Update params.

* Update review.

* Readd mthree

* Typo.

* Path.

* Black

* Change path.

* Update runtime_programs/vqe_runtime_program.py

* Update doc/devices/runtime.rst

Co-authored-by: antalszava <[email protected]>

* Update review.

* coverarc

* coverarc omit + skip IBMQ and Runtime tests

* Changelog

Co-authored-by: antalszava <[email protected]>
  • Loading branch information
rmoyard and antalszava committed Jan 31, 2022
1 parent 214313c commit ef7dfa5
Show file tree
Hide file tree
Showing 16 changed files with 2,057 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# .coveragerc to control coverage.py
[run]
source = pennylane_qiskit
omit =
# omit IBMQ files
pennylane_qiskit/ibmq.py
# omit Runtime files
pennylane_qiskit/*runtime*

[report]
# Regexes for lines to exclude from consideration
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ jobs:
pip install dist/PennyLane*.whl
- name: Run tests
run: python -m pytest tests --cov=pennylane_qiskit --cov-report=term-missing --cov-report=xml -p no:warnings --tb=native
# Skip IBMQ and Runtime tests as they depend on IBMQ's availability and
# easily result in timeouts
run: python -m pytest tests -k 'not test_ibmq.py and not test_runtime.py' --cov=pennylane_qiskit --cov-report=term-missing --cov-report=xml -p no:warnings --tb=native
env:
IBMQX_TOKEN_TEST: ${{ secrets.IBMQX_TOKEN_TEST }}

Expand Down
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

### New features since last release

* Add two devices for runtime programs and one VQE runtime program solver.
[(#157)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/157)

### Breaking changes

### Improvements
Expand All @@ -18,7 +21,7 @@

This release contains contributions from (in alphabetical order):

Tanner Rogalsky
Guillermo Alonso-Linaje, Romain Moyard, Tanner Rogalsky, Antal Száva

---

Expand Down
57 changes: 57 additions & 0 deletions doc/devices/runtime.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
Qiskit Runtime Programs
=======================

PennyLane-Qiskit supports running PennyLane on IBM Q hardware via the Qiskit runtime programs ``circuit-runner``
and ``sampler``. You can choose between those two runtime programs and also have the possibility to choose the
backend on which the circuits will be run. Those two devices inherit directly from the ``IBMQ`` device and work the
the same way, you can refer to the corresponding documentation for details about token and providers
`IBMQ documentation for PennyLane <https://pennylaneqiskit.readthedocs.io/en/latest/devices/ibmq.html>`_.

You can use the ``circuit_runner`` and ``sampler`` devices by using their short names, for example:

.. code-block:: python
dev = qml.device('qiskit.ibmq.circuit_runner', wires=2, backend='ibmq_qasm_simulator', shots=8000, **kwargs)
.. code-block:: python
dev = qml.device('qiskit.ibmq.sampler', wires=2, backend='ibmq_qasm_simulator', shots=8000, **kwargs)
Custom Runtime Programs
~~~~~~~~~~~~~~~~~~~~~~~

Not all Qiskit runtime programs correspond to complete devices, some solve specific problems (VQE, QAOA, etc...).
We created a custom Qiskit runtime program for solving VQE problems in PennyLane ``runtime_programs\vqe_runtime_program.py``.
In order to use this program you need to upload on IBMQ (only once), get the program ID and use the VQE runner.

.. code-block:: python
from pennylane_qiskit import upload_vqe_runner, vqe_runner
IBMQ.enable_account(token)
program_id = upload_vqe_runner(hub="ibm-q", group="open", project="main")
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)
job = vqe_runner(
program_id=program_id,
backend="ibmq_qasm_simulator",
hamiltonian=hamiltonian,
ansatz=vqe_circuit,
x0=[3.97507603, 3.00854038],
shots=shots,
optimizer="SPSA",
optimizer_config={"maxiter": 40},
kwargs={"hub": "ibm-q", "group": "open", "project": "main"},
)
More details on Qiskit runtime programs in the `IBMQ runtime documentation <https://qiskit.org/documentation/apidoc/ibmq_runtime.html>`_.
11 changes: 11 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ Currently, there are three different devices available:
:description: Allows integration with qiskit's hardware backends, and hardware-specific simulators.
:link: devices/ibmq.html

.. devicegalleryitem::
:name: 'qiskit.ibmq.circuit_runner'
:description: Allows integration with qiskit's circuit runner runtime program.
:link: devices/runtime.html

.. devicegalleryitem::
:name: 'qiskit.ibmq.sampler'
:description: Allows integration with qiskit's sampler runtime program.
:link: devices/runtime.html

.. raw:: html

<div style='clear:both'></div>
Expand Down Expand Up @@ -112,6 +122,7 @@ hardware access.
devices/aer
devices/basicaer
devices/ibmq
devices/runtime

.. toctree::
:maxdepth: 1
Expand Down
3 changes: 3 additions & 0 deletions pennylane_qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@
from .basic_aer import BasicAerDevice
from .converter import load, load_qasm, load_qasm_from_file
from .ibmq import IBMQDevice
from .runtime_devices import IBMQCircuitRunnerDevice
from .runtime_devices import IBMQSamplerDevice
from .vqe_runtime_runner import vqe_runner, upload_vqe_runner, delete_vqe_runner
90 changes: 49 additions & 41 deletions pennylane_qiskit/ibmq.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,52 +58,14 @@ class IBMQDevice(QiskitDevice):
short_name = "qiskit.ibmq"

def __init__(self, wires, provider=None, backend="ibmq_qasm_simulator", shots=1024, **kwargs):
token = kwargs.get("ibmqx_token", None) or os.getenv("IBMQX_TOKEN")
url = kwargs.get("ibmqx_url", None) or os.getenv("IBMQX_URL")

# Specify a single hub, group and project
# Connection to IBMQ
connect(kwargs)

hub = kwargs.get("hub", "ibm-q")
group = kwargs.get("group", "open")
project = kwargs.get("project", "main")

# TODO: remove "no cover" when #173 is resolved
if token is not None: # 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()
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

# IBM Q account is now enabled

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

Expand All @@ -129,3 +91,49 @@ def _track_run(self): # pragma: no cover
}
self.tracker.update(job_time=job_time)
self.tracker.record()


def connect(kwargs):
"""Function that allows connection to IBMQ.
Args:
kwargs(dict): dictionary that contains the token and the url"""

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 is not None: # 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()
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
20 changes: 16 additions & 4 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ def create_circuit_object(self, operations, **kwargs):
self._circuit.save_state()

def apply(self, operations, **kwargs):

self.create_circuit_object(operations, **kwargs)

# These operations need to run for all devices
Expand Down Expand Up @@ -377,13 +376,19 @@ def analytic_probability(self, wires=None):
prob = self.marginal_prob(np.abs(self._state) ** 2, wires)
return prob

def batch_execute(self, circuits):
def compile_circuits(self, circuits):
r"""Compiles multiple circuits one after the other.
compiled_circuits = []
Args:
circuits (list[.tapes.QuantumTape]): the circuits to be compiled
Returns:
list[QuantumCircuit]: the list of compiled circuits
"""
# Compile each circuit object
for circuit in circuits:
compiled_circuits = []

for circuit in circuits:
# We need to reset the device here, else it will
# not start the next computation in the zero state
self.reset()
Expand All @@ -393,6 +398,13 @@ def batch_execute(self, circuits):
compiled_circ.name = f"circ{len(compiled_circuits)}"
compiled_circuits.append(compiled_circ)

return compiled_circuits

def batch_execute(self, circuits):
# 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()
Expand Down
Loading

0 comments on commit ef7dfa5

Please sign in to comment.