Skip to content

Commit

Permalink
docs: add guide and API reference on primitives (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
airwoodix authored Oct 4, 2023
1 parent 76a1a32 commit 005b164
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 23 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Unreleased

* Check that the circuits submitted to the offline simulators can be converted to the AQT API (#68)
* Update the user guide and improve the API reference consistency (#72)
* Update the user guide and improve the API reference consistency (#72, #75)
* Add quickstart examples for the Qiskit.org homepage (#73)

## qiskit-aqt-provider v0.17.0
Expand Down
13 changes: 13 additions & 0 deletions docs/apidoc/primitives.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. _qiskit-aqt-primitives:

=================
Qiskit primitives
=================

.. autoclass:: qiskit_aqt_provider.primitives.sampler.AQTSampler
:show-inheritance:
:exclude-members: __new__

.. autoclass:: qiskit_aqt_provider.primitives.estimator.AQTEstimator
:show-inheritance:
:exclude-members: __new__
55 changes: 53 additions & 2 deletions docs/guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ If the filtering criteria correspond to multiple or no backends, a :class:`Qiski
:hide-code:

backend.options.with_progress_bar = False
backend.simulator.options.seed_simulator = 1000

Quantum circuit evaluation
==========================
Expand All @@ -89,6 +90,8 @@ Single circuit evaluation

Basic quantum circuit execution follows the regular Qiskit workflow. A quantum circuit is defined by a :class:`QuantumCircuit <qiskit.circuit.QuantumCircuit>` instance:

.. _bell-state-circuit:

.. jupyter-execute::

circuit = qiskit.QuantumCircuit(2)
Expand Down Expand Up @@ -140,6 +143,45 @@ The result of a batch job is also a standard Qiskit :class:`Result <qiskit.resul

.. warning:: In a batch job, the execution order of circuits is not guaranteed. In the :class:`Result <qiskit.result.Result>` instance, however, results are listed in submission order.

Using Qiskit primitives
-----------------------

Circuit evaluation can also be performed using :mod:`Qiskit primitives <qiskit.primitives>` through their specialized implementations for AQT backends :class:`AQTSampler <qiskit_aqt_provider.primitives.sampler.AQTSampler>` and :class:`AQTEstimator <qiskit_aqt_provider.primitives.estimator.AQTEstimator>`.

.. warning:: The generic implementations :class:`BackendSampler <qiskit.primitives.BackendSampler>` and :class:`BackendEstimator <qiskit.primitives.BackendEstimator>` are **not** compatible with backends retrieved from the :class:`AQTProvider <qiskit_aqt_provider.aqt_provider.AQTProvider>`. Please use the specialized implementations :class:`AQTSampler <qiskit_aqt_provider.primitives.sampler.AQTSampler>` and :class:`AQTEstimator <qiskit_aqt_provider.primitives.estimator.AQTEstimator>` instead.

For example, the :class:`AQTSampler <qiskit_aqt_provider.primitives.sampler.AQTSampler>` can evaluate bitstring quasi-probabilities for a given circuit. Using the :ref:`Bell state circuit <bell-state-circuit>` defined above, we see that the states :math:`|00\rangle` and :math:`|11\rangle` roughly have the same quasi-probability:

.. jupyter-execute::

from qiskit.visualization import plot_distribution
from qiskit_aqt_provider.primitives import AQTSampler

sampler = AQTSampler(backend)
result = sampler.run(circuit, shots=200).result()
data = {f"{b:02b}": p for b, p in result.quasi_dists[0].items()}
plot_distribution(data, figsize=(5, 4), color="#d1e0e0")


In this Bell state, the expectation value of the the :math:`\sigma_z\otimes\sigma_z` operator is :math:`1`. This expectation value can be evaluated by applying the :class:`AQTEstimator <qiskit_aqt_provider.primitives.estimator.AQTEstimator>`:

.. jupyter-execute::

from qiskit.quantum_info import SparsePauliOp
from qiskit_aqt_provider.primitives import AQTEstimator

estimator = AQTEstimator(backend)

bell_circuit = qiskit.QuantumCircuit(2)
bell_circuit.h(0)
bell_circuit.cnot(0, 1)

observable = SparsePauliOp.from_list([("ZZ", 1)])
result = estimator.run(bell_circuit, observable).result()
print(result.values[0])

.. tip:: The circuit passed to estimator's :meth:`run <qiskit.primitives.BaseEstimator.run>` method is used to prepare the state the observable is evaluated in. Therefore, it must not contain unconditional measurement operations.

Quantum circuit transpilation
=============================

Expand All @@ -160,14 +202,14 @@ Transpilation can also be triggered separately from job submission using the :fu
.. jupyter-execute::
:hide-code:

circuit.draw("mpl")
circuit.draw("mpl", style="bw")

to the transpiled one:

.. jupyter-execute::

transpiled_circuit = qiskit.transpile(circuit, backend, optimization_level=2)
transpiled_circuit.draw("mpl")
transpiled_circuit.draw("mpl", style="bw")


Transpiler bypass
Expand Down Expand Up @@ -195,6 +237,8 @@ If a circuit is already defined in terms of the :ref:`native gates set <basis-ga

Circuits that do not satisfy the AQT API restrictions are rejected by raising a :class:`ValueError` exception.

.. _transpiler-plugin:

Transpiler plugin
-----------------

Expand All @@ -208,3 +252,10 @@ The built-in transpiler largely leverages the :mod:`qiskit.transpiler`. Custom p
:math:`R(\theta,\,\phi)\ \to\ R(\pi, \pi)\cdot R(\theta+\pi,\,\phi)`.

The threshold for triggering this transformation is an implementation detail, typically around :math:`\theta=\pi/5`. Please contact AQT for details.

Transpilation in Qiskit primitives
----------------------------------

The generic implementations of the Qiskit primitives :class:`Sampler <qiskit.primitives.BaseSampler>` and :class:`Estimator <qiskit.primitives.BaseEstimator>` cache transpilation results to improve their runtime performance. This is particularly effective when evaluating batches of circuits that differ only in their parametrization.

However, some passes registered by the AQT :ref:`transpiler plugin <transpiler-plugin>` require knowledge of the bound parameter values. The specialized implementations :class:`AQTSampler <qiskit_aqt_provider.primitives.sampler.AQTSampler>` and :class:`AQTEstimator <qiskit_aqt_provider.primitives.estimator.AQTEstimator>` use a hybrid approach, where the transpilation results of passes that do not require bound parameters are cached, while the small subset of passes that require fixed parameter values is executed before each circuit submission to the execution backend.
29 changes: 20 additions & 9 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,34 @@ hardware.
Quick start
-----------

Define a circuit that generates 2-qubit Bell state and execute it on a simulator backend:
Define a circuit that generates 2-qubit Bell state and sample it on a simulator backend running on the local machine:

.. jupyter-execute::

import qiskit
from qiskit import QuantumCircuit

from qiskit_aqt_provider import AQTProvider
from qiskit_aqt_provider.primitives import AQTSampler

# Define a circuit.
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cnot(0, 1)
circuit.measure_all()

backend = AQTProvider("ACCESS_TOKEN").get_backend("offline_simulator_no_noise")
# Select an execution backend.
# Any token (even invalid ones) give access to the offline simulation backends.
provider = AQTProvider("ACCESS_TOKEN")
backend = provider.get_backend("offline_simulator_no_noise")

qc = QuantumCircuit(2)
qc.h(0)
qc.cnot(0, 1)
qc.measure_all()
# Instantiate a sampler on the execution backend.
sampler = AQTSampler(backend)

result = qiskit.execute(qc, backend, with_progress_bar=False).result()
print(result.get_counts())
# Sample the circuit on the execution backend.
result = sampler.run(circuit).result()

quasi_dist = result.quasi_dists[0]
print(quasi_dist)

For more details see the :ref:`user guide <user-guide>`, a selection of `examples <https://github.com/qiskit-community/qiskit-aqt-provider/tree/master/examples>`_, or the API reference.

Expand All @@ -48,6 +58,7 @@ For more details see the :ref:`user guide <user-guide>`, a selection of `example
AQTResource <apidoc/resource>
AQTJob <apidoc/job>
AQTOptions <apidoc/options>
Qiskit primitives <apidoc/primitives>
Transpiler plugin <apidoc/transpiler_plugin>

.. toctree::
Expand Down
14 changes: 8 additions & 6 deletions qiskit_aqt_provider/primitives/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@


class AQTEstimator(BackendEstimator):
"""Estimator primitive for AQT backends."""
""":class:`Estimator <qiskit.primitives.Estimator>` primitive for AQT backends."""

_backend: AQTResource

Expand All @@ -31,13 +31,15 @@ def __init__(
abelian_grouping: bool = True,
skip_transpilation: bool = False,
):
"""Initialize an Estimator primitive for AQT resources.
"""Initialize an ``Estimator`` primitive using an AQT backend.
Args:
backend: AQT resource to evaluate circuits on
options: options passed to the base sampler
abelian_grouping: whether the observable should be grouped into commuting parts
skip_transpilation: if true, pass circuits unchanged to the backend.
backend: AQT resource to evaluate circuits on.
options: options passed to through to the underlying
:class:`BackendEstimator <qiskit.primitives.BackendEstimator>`.
abelian_grouping: whether the observable should be grouped into commuting parts.
skip_transpilation: if :data:`True`, do not transpile circuits
before passing them to the execution backend.
"""
# Signal the transpiler to disable passes that require bound
# parameters.
Expand Down
12 changes: 7 additions & 5 deletions qiskit_aqt_provider/primitives/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@


class AQTSampler(BackendSampler):
"""Sampler primitive for AQT backends."""
""":class:`Sampler <qiskit.primitives.Sampler>` primitive for AQT backends."""

_backend: AQTResource

Expand All @@ -30,12 +30,14 @@ def __init__(
options: Optional[Dict[str, Any]] = None,
skip_transpilation: bool = False,
):
"""Initialize a Sampler primitive for AQT resources.
"""Initialize a ``Sampler`` primitive using an AQT backend.
Args:
backend: AQT resource to evaluate circuits on
options: options passed to the base sampler
skip_transpilation: if true, pass circuits unchanged to the backend.
backend: AQT resource to evaluate circuits on.
options: options passed through to the underlying
:class:`BackendSampler <qiskit.primitives.BackendSampler>`.
skip_transpilation: if :data:`True`, do not transpile circuits
before passing them to the execution backend.
"""
# Signal the transpiler to disable passes that require bound
# parameters.
Expand Down

0 comments on commit 005b164

Please sign in to comment.