Skip to content

Commit

Permalink
Improve primitive intro (#2018)
Browse files Browse the repository at this point in the history
closes #1733

---------

Co-authored-by: Kaelyn Ferris <[email protected]>
Co-authored-by: abbycross <[email protected]>
Co-authored-by: Abby Mitchell <[email protected]>
  • Loading branch information
4 people authored Oct 14, 2024
1 parent df92fbc commit b7e0301
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 46 deletions.
2 changes: 1 addition & 1 deletion docs/guides/primitive-input-output.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"id": "04b9204f-f058-433b-89f9-05df14645e27",
"metadata": {},
"source": [
"<span id=\"pub-overview\"></span>\n",
"<span id=\"pubs\"></span>\n",
"## Overview of PUBs\n",
"\n",
"When invoking a primitive's [`run()`](../api/qiskit-ibm-runtime/qiskit_ibm_runtime.EstimatorV2#run) method, the main argument that is required is a `list` of one or more tuples -- one for each circuit being executed by the primitive. Each of these tuples is considered a PUB, and the required elements of each tuple in the list depends on the the primitive used. The data provided to these tuples can also be arranged in a variety of shapes to provide flexibility in a workload through broadcasting -- the rules of which are described in a [following section](#broadcasting-rules).\n",
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/primitives-rest-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ print(qasm_str)


<Admonition type="note">
The following jobs use Qiskit Runtime V2 [primitives](/guides/primitives). Both [`SamplerV2`](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.SamplerV2) and [`EstimatorV2`](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.EstimatorV2) take one or more [primitive unified blocs (PUBs)](/guides/primitive-input-output#pub-overview) as the input. Each PUB is a tuple that contains one circuit and the data broadcasted to that circuit, which can be multiple observables and parameters. Each PUB returns a result.
The following jobs use Qiskit Runtime V2 [primitives](/guides/primitives). Both [`SamplerV2`](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.SamplerV2) and [`EstimatorV2`](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.EstimatorV2) take one or more [primitive unified blocs (PUBs)](/guides/primitive-input-output#pubs) as the input. Each PUB is a tuple that contains one circuit and the data broadcasted to that circuit, which can be multiple observables and parameters. Each PUB returns a result.
</Admonition>


Expand Down
97 changes: 53 additions & 44 deletions docs/guides/primitives.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,70 @@ In this context, a *primitive* is the smallest processing instruction, the simpl
one can create something useful for a given abstraction level.

The recent progress in quantum computing has increased the need to work at higher levels of abstraction.
As we move toward larger QPUs (quantum processing units) and more complex workflows, the focus shifts from interacting with individual
qubit signals to viewing quantum devices as systems that perform tasks we need.
As the field moves toward larger quantum processing units (QPUs) and more complex workflows, the focus shifts from interacting with individual
qubit signals to viewing quantum devices as systems that perform necessary tasks.

The two most common tasks quantum computers are used for are sampling quantum states and calculating expectation values.
These tasks motivated the design of the Qiskit primitives: Sampler and Estimator.
The two most common tasks for quantum computers are sampling quantum states and calculating expectation values.
These tasks motivated the design of the Qiskit primitives: **Estimator** and **Sampler**.

- Estimator computes expectation values of observables with respect to states prepared by quantum circuits.
- Sampler samples the output register from quantum circuit execution.

In short, the computational model introduced by the Qiskit primitives moves quantum programming one step closer
to where classical programming is today, where the focus is less on the hardware details and more on the results
you are trying to achieve.

## Implementation of Qiskit primitives
## Primitive definition and implementations

There are two types of Qiskit primitives: the base classes, and their implementations. The Qiskit primitives are defined by open-source primitive base classes that live in the Qiskit SDK (in the [`qiskit.primitives`](/api/qiskit/primitives) module). Providers (such as Qiskit Runtime) can use these base classes to derive their own Sampler and Estimator implementations. Most users will interact with provider implementations, not the base primitives.

### Base classes

[`BaseEstimatorV2`](/api/qiskit/qiskit.primitives.BaseEstimatorV2) and [`BaseSamplerV2`](/api/qiskit/qiskit.primitives.BaseSamplerV2) - Abstract base classes that define a common interface for implementing primitives. All other classes in the [`qiskit.primitives`](/api/qiskit/primitives) module inherit from these base classes. Developers should use these if they are interested in creating their own primitives-based execution model for a specific provider. These classes might also be useful for those who want to do highly customized processing and find that the existing primitives implementations are too simple for their needs. General users will not directly use the base classes.

<span id="implementations"></span>
### Implementations

These are implementations of the primitives base classes:

The Qiskit primitives are defined by open-source primitive base-classes, from
which different providers can derive their own Sampler and Estimator implementations. Among the implementations
using Qiskit, you can find reference primitive implementations for local simulation in the `qiskit.primitives` module.
Providers like Qiskit Runtime enable access to appropriate QPUs through native implementations of
their own primitives.
- The Qiskit Runtime primitives ([`EstimatorV2`](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.EstimatorV2) and [`SamplerV2`](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.SamplerV2)) provide a more sophisticated implementation (for example, by including error mitigation) as a cloud-based service. This implementation of the base primitives is used to access IBM Quantum&trade; hardware. They are accessed through IBM Qiskit Runtime.

- [`StatevectorEstimator`](/api/qiskit/qiskit.primitives.StatevectorEstimator) and [`StatevectorSampler`](/api/qiskit/qiskit.primitives.StatevectorSampler#statevectorsampler) - Reference implementations of the primitives that use the simulator built into Qiskit. They are built with the Qiskit [`quantum_info`](/api/qiskit/quantum_info#quantum-information) module, producing results based on ideal statevector simulations. They are accessed through Qiskit.

- [`BackendEstimatorV2`](/api/qiskit/qiskit.primitives.BackendEstimatorV2) and [`BackendSamplerV2`](/api/qiskit/qiskit.primitives.BackendSamplerV2) - You can use these classes to “wrap” any quantum computing resource into a primitive. This lets you write primitive-style code for providers that don’t yet have a primitives-based interface. These classes can be used just like the regular Sampler and Estimator, except they should be initialized with an additional `backend` argument for selecting which quantum computer to run on. They are accessed by using Qiskit.

## Benefits of Qiskit primitives

For Qiskit users, primitives let you write quantum code for a specific QPU without having to explicitly
With primitives, Qiskit users can write quantum code for a specific QPU without having to explicitly
manage every detail. Also, because of the additional layer of abstraction, you might be able to more easily
access advanced hardware capabilities of a given provider. For example, with Qiskit Runtime primitives,
you can leverage the latest advancements in error mitigation and suppression by toggling options such as `resilience_level`, rather than building your own implementation of these techniques.
you can take advantage of the latest advancements in error mitigation and suppression by toggling options such as the primitive's [`resilience_level`,](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.options.EstimatorOptions#resilience_level) rather than building your own implementation of these techniques.

For hardware providers, implementing primitives natively means you can provide your users with a more “out-of-the-box”
way to access your hardware features. It is therefore easier for your users to benefit from your hardware's
best capabilities.
way to access your hardware features such as advanced post-processing techniques. It is therefore easier for your users to benefit from your hardware's best capabilities.

## Primitive details

As described previously, all primitives are created from the base classes; therefore, they have the same general structure and usage. For example, the format of the input for all Estimator primitives is the same. However, there are differences in implementations that make them unique.

<Admonition type="note">
Because most users access the Qiskit Runtime primitives, the examples in the rest of this section are based on Qiskit Runtime primitives.
</Admonition>

## Estimator
<span id="estimator"></span>
### Estimator

Estimator computes expectation values of observables with respect to states prepared by quantum circuits. The circuits can be parametrized, as long as the parameter values are also provided as input to the primitive.
The Estimator primitive computes the expectation values for one or more observables with respect to states prepared by quantum circuits. The circuits can be parametrized, as long as the parameter values are also provided as input to the primitive.

The input is an array of PUBs. Each PUB is in the format (`<single circuit>`, `<one or more observables>`, `<optional one or more parameter values>`, `<optional precision>`), where the optional `parameter values` can be a list or a single parameter. Different Estimator implementations support various configuration options.
The input is an array of [PUBs.](/guides/primitive-input-output#pubs) Each PUB is in the format:

The output is a `PubResult` that contains the computed expectation values per pair, and their standard errors, in `PubResult` form. Each `PubResult` contains both data and metadata.
(`<single circuit>`, `<one or more observables>`, `<optional one or more parameter values>`, `<optional precision>`),

where the optional `parameter values` can be a list or a single parameter. Different Estimator implementations support various configuration options. If the input contains measurements, they are ignored.

Estimator combines elements from observables and parameter values by following NumPy broadcasting rules as described in the [Primitive inputs and outputs](primitive-input-output#broadcasting-rules) topic.
The output is a [`PubResult`](/api/qiskit/qiskit.primitives.PubResult#pubresult) that contains the computed expectation values per pair, and their standard errors, in `PubResult` form. Each `PubResult` contains both data and metadata.

The Estimator combines elements from observables and parameter values by following NumPy broadcasting rules as described in the [Primitive inputs and outputs](primitive-input-output#broadcasting-rules) topic.

Example:

Expand All @@ -64,13 +89,17 @@ estimator.run([(circuit1, observable1, param_values1),(circuit2, observable2, pa
```

<span id="sampler"></span>
## Sampler
### Sampler

The Sampler's core task is sampling the output register from the execution of one or more quantum circuits. The input circuits can be parametrized, as long as the parameter values are also provided as input to the primitive.

The input is one or more [PUBs,](/guides/primitive-input-output#pubs) in the format:

Sampler's core task is sampling the output register from execution of quantum circuits. The input circuits can be parametrized, as long as the parameter values are also provided as input to the primitive.
(`<single circuit>`, `<one or more optional parameter value>`, `<optional shots>`),

The input is one or more PUBs, in the format (`<single circuit>`, `<optional one or more parameter values>`, `<optional shots>`), where there can be multiple `parameter values` items, and each item can be either an array or a single parameter, depending on the chosen circuit.
where there can be multiple `parameter values` items, and each item can be either an array or a single parameter, depending on the chosen circuit. Additionally, the input must contain measurements.

The output is counts or per-shot measurements, as `PubResult` objects, without weights. The result class, however, has methods to return weighted samples, such as counts. See [Primitive inputs and outputs](primitive-input-output#broadcasting-rules) for full details.
The output is counts or per-shot measurements, as [`PubResult`](/api/qiskit/qiskit.primitives.PubResult#pubresult) objects, without weights. The result class, however, has methods to return weighted samples, such as counts. See [Primitive inputs and outputs](primitive-input-output#broadcasting-rules) for full details.

Example:

Expand All @@ -81,32 +110,12 @@ sampler.run([
])
```

## How to use Qiskit primitives

The `qiskit.primitives` module enables the development of primitive-style quantum programs and was specifically
designed to simplify switching between different types of quantum compute resources. The module provides three separate classes
for each primitive type:

1. `StatevectorSampler` and `StatevectorEstimator`

These classes are reference implementations of both primitives and use the simulator built in to Qiskit. They leverage the Qiskit `quantum_info` module in the background, producing results based on ideal statevector simulations.

2. `BaseSampler` and `BaseEstimator`

These are abstract base classes that define a common interface for implementing primitives. All other classes in the `qiskit.primitives` module inherit from these base classes, and developers should use these if they are interested in developing their own primitives-based execution model for a specific provider. These classes may also be useful for those who want to do highly customized processing and find the existing primitives implementations too simple for their needs.

3. `BackendSampler` and `BackendEstimator`

If a provider does not support primitives natively, you can use these classes to “wrap” any quantum computing resource into a primitive. Users can write primitive-style code for providers that don’t yet have a primitives-based interface. These classes can be used just like the regular Sampler and Estimator, except they should be initialized with an additional `backend` argument for selecting which quantum computer to run on.

The Qiskit Runtime primitives provide a more sophisticated implementation (for example, by including error mitigation) as a cloud-based service.

## Next steps

<Admonition type="tip" title="Recommendations">
- Read [Get started with primitives](get-started-with-primitives) to implement primitives in your work.
- Review detailed [primitives examples.](primitives-examples)
- Practice with primitives by working through the [Cost function lesson](https://learning.quantum.ibm.com/course/variational-algorithm-design/cost-functions#primitives) in IBM Quantum&trade; Learning.
- Practice with primitives by working through the [Cost function lesson](https://learning.quantum.ibm.com/course/variational-algorithm-design/cost-functions#primitives) in IBM Quantum Learning.
- See the [EstimatorV2 API reference](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.EstimatorV2) and [SamplerV2 API reference](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.SamplerV2).
- Read [Migrate to V2 primitives](/migration-guides/v2-primitives).
</Admonition>
Expand Down

0 comments on commit b7e0301

Please sign in to comment.