Computing systems are built upon multiple layers of abstraction. Abstractions allow to focus on a particular level of detail relevant to the task at hand. The closer you get to the hardware, the lower the level of abstraction you'll need (for example, you could want to manipulate electrical signals), and vice versa, the more complex the task you want to perform, the higher-level the abstractions will be (for example, you could be using a programming library to perform algebraic calculations).
In this context, a primitive is the smallest processing instruction, the simplest building block from which 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 towards larger systems and more complex workflows, the focus shifts from interacting with individual qubit signals to viewing quantum devices as systems that perform tasks we need.
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 first two Qiskit primitives: Sampler and Estimator.
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.
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
Providers like IBM’s Qiskit Runtime enable access to appropriate backends through native implementations of
their own primitives.
For Qiskit users, primitives allow you to write quantum code for a specific backend without having to explicitly
manage every detail. In addition, because of the additional layer of abstraction, you may 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.
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.
The Estimator primitive computes expectation values of observables with respect to states prepared by quantum circuits. The Estimator receives circuit-observable pairs (with the observable expressed as a weighted sum of Pauli operators) as inputs, and returns the computed expectation values per pair, as well as their variances. Different Estimator implementations support various configuration options. The circuits can be parametrized, as long as the parameter values are also provided as input to the primitive.
The Sampler primitive samples from the classical output registers resulting from execution of quantum circuits. For this reason, the inputs to the Sampler are (parametrized) quantum circuits, for which it returns the corresponding quasi-probability distributions of sampled bitstrings. Quasi-probability distributions are similar to regular probabilities, except they may include negative values, which can occur when using certain error mitigation techniques.
qiskit.primitives module enables the development of primitive-style quantum programs and was specifically
designed to simplify switching between different types of backends. The module provides three separate classes
for each primitive type:
These classes are reference implementations of both primitives and use Qiskit’s built-in simulator. They leverage Qiskit’s
quantum_info module in the background, producing results based on ideal statevector simulations.
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 backend 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.
If a provider does not support primitives natively, you can use these classes to “wrap” any backend 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 backend to run on.
The Qiskit Runtime primitives provide a more sophisticated implementation (such as with error mitigation) as a cloud-based service.