Qiskit
Published in

Qiskit

Introducing Qiskit Algorithms With Qiskit Primitives!

By Dariusz Lasecki and Stefan Wörner.

The of Qiskit introduced the Qiskit Primitives as a new way to interact with quantum computers. In addition to introducing the primitives, the new release came with a completely updated , which provides state-of-the-art quantum algorithms for the open-source community, all powered by the newly introduced primitives.

Today, we are taking a closer look at what changed in the Qiskit’s algorithms module. To see the full release notes for Qiskit v0.39 (which includes the 0.22 version of Qiskit Terra), go .

The TL;DR

We enabled all Qiskit algorithms to use suitable primitives and introduced a few new ones: ,, and . Tutorials covering updated algorithms can be found .

Below, find a summary of the top new features and improvements.

Near-term algorithms:

Near-term algorithms are how we refer to promising candidates for running on near-term devices, such as devices limited in the number of qubits and coherence times or those without error-correcting codes. They might lead to a quantum advantage over classical methods as the hardware matures and develops. If interested, refer to IBM Quantum’s recently updated for more on near-term devices. The Qiskit algorithms module provides cutting-edge near-term quantum algorithms and lays the foundation for using these methods in promising applications areas — Quantum Machine Learning, Natural Sciences, Optimization, and Finance (do not forget to check out our Qiskit Applications Modules: , , and ).

qiskit.algorithms.minimum_eigensolvers (formerly qiskit.algorithms.minimum_eigen_solvers)

The minimum_eigensolvers package is responsible for interfaces and quantum algorithms that compute the ground state of a Hamiltonian. The update fixes a small naming inconsistency of the old minimum_eigen_solvers, and contains the new primitive-based algorithms. It features popular optimization algorithms like VQE and QAOA which now support the Qiskit Primitives.

It is worth noting that from this release on, two versions of VQE are available — , which uses the sampler primitive and is optimized for diagonal Hamiltonians, and , which uses the estimator primitive. The choice of the algorithm depends on the use case — whether we are interested in accessing the probability distribution corresponding to a quantum state or an estimation of the ground state energy which might require, for example, measurements in multiple bases.

In the case of the updated VQE algorithm, we now use an estimator instead of a quantum instance, and the SparsePauliOp instead of the PauliSumOp (although the PauliSumOp will still be supported). We recommend you use the more efficient SparsePauliOp and will update also the application modules in the future accordingly. The comparison of the current and the old version of VQEon a simple example can be seen below.

New VQE:

from qiskit.algorithms.minimum_eigensolvers import VQE 
from qiskit.algorithms.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import Estimator


hamiltonian = SparsePauliOp.from_list([(“II”, -1), (“IZ”, 0.3),
(“XI”, -0.3), (“ZY”, -0.01), (“YX”, 0.1)])

estimator = Estimator()
optimizer = SLSQP()
ansatz = TwoLocal(rotation_blocks=[“ry”, “rz”], entanglement_blocks=”cz”)

vqe = VQE(estimator, ansatz, optimizer)
result = vqe.compute_minimum_eigenvalue(operator=hamiltonian)
eigenvalue = result.eigenvalue

Old VQE (now deprecated):

from qiskit import BasicAer 
from qiskit.algorithms.minimum_eigen_solvers import VQE
from qiskit.algorithms.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal
from qiskit.opflow import PauliSumOp
from qiskit.quantum_info import SparsePauliOp


hamiltonian = PauliSumOp(SparsePauliOp.from_list([(“II”, -1), (“IZ”, 0.3),
(“XI”, -0.3), (“ZY”, -0.01), (“YX”, 0.1)]))

quantum_instance = BasicAer.get_backend(“statevector_simulator”)
optimizer = SLSQP()
ansatz = TwoLocal(rotation_blocks=[“ry”, “rz”], entanglement_blocks=”cz”)

vqe = VQE(ansatz, optimizer, quantum_instance=quantum_instance)
result = vqe.compute_minimum_eigenvalue(operator=hamiltonian)
eigenvalue = result.eigenvalue

When it comes to using VQE in the sampling context, the updated code can look like the one below. Note that SamplingVQE is only applicable to diagonal Hamiltonians, as only then can the energy be evaluated directly from the sampled bitstrings, and only then do the bitstrings directly correspond to eigenstates, i.e., correspond to potential solutions of the considered problem.

from qiskit.algorithms.minimum_eigensolvers import SamplingVQE 
from qiskit.algorithms.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal
from qiskit.primitives import Sampler
from qiskit.quantum_info import SparsePauliOp


operator = SparsePauliOp.from_list([(“ZZ”, 1), (“IZ”, -0.5), (“II”, 0.12)])

sampler = Sampler()
ansatz = TwoLocal(rotation_blocks=[“ry”, “rz”], entanglement_blocks=”cz”)
optimizer = SLSQP()

sampling_vqe = SamplingVQE(sampler, ansatz, optimizer)
result = sampling_vqe.compute_minimum_eigenvalue(operator)
eigenvalue = result.eigenvalue

As a new algorithm introduced in this release, we moved from Qiskit Nature to Qiskit (Terra) and generalized it beyond the chemistry applications. It computes the ground state energy of a system by creating a compact ansatz from a set of evolution operators. To learn the theory behind it, please refer to this .

Below you can find the example of how to use it.

from qiskit.algorithms.minimum_eigensolvers import AdaptVQE, VQE 
from qiskit.algorithms.optimizers import SLSQP
from qiskit.primitives import Estimator
from qiskit.circuit.library import EvolvedOperatorAnsatz

# get your Hamiltonian
hamiltonian = …
# construct your ansatz
ansatz = EvolvedOperatorAnsatz(…)

vqe = VQE(Estimator(), ansatz, SLSQP())

adapt_vqe = AdaptVQE(vqe)

result = adapt_vqe.compute_minimum_eigenvalue(hamiltonian)

The qiskit.algorithms.minimum_eigen_solverspackage, which includes algorithms without the Qiskit Primitives, is now deprecated.

qiskit.algorithms.eigensolvers (formerly qiskit.algorithms.eigen_solvers)

The eigensolvers package provides interfaces and algorithms for calculating eigenvalues of operators. Again, this update fixes a small naming inconsistency in the old eigen_solvers module and contains the new primitive-based algorithms. They are useful for finding energy spectra of quantum systems, for example. Currently, Qiskit offers a reference classical implementation based on NumPy and a quantum algorithm called Variational Quantum Deflation (VQD). VQD computes excited state energies of Hamiltonians and is a new addition to the module in this version — see the . To learn more about the algorithm, refer to the relevant .

The VQD algorithm can be used as follows:

from qiskit.algorithms.eigensolvers import VQD 
from qiskit.algorithms.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal
from qiskit.primitives import Sampler, Estimator
from qiskit.algorithms.state_fidelities import ComputeUncompute
from qiskit.opflow import PauliSumOp
from qiskit.quantum_info import SparsePauliOp


h2_op = PauliSumOp(SparsePauliOp(
[“II”, “IZ”, “ZI”, “ZZ”, “XX”],
coeffs=[
-1.052373245772859,
0.39793742484318045,
-0.39793742484318045,
-0.01128010425623538,
0.18093119978423156,
],
))

estimator = Estimator()
ansatz = TwoLocal(rotation_blocks=[“ry”, “rz”], entanglement_blocks=”cz”)
optimizer = SLSQP()
fidelity = ComputeUncompute(Sampler())

vqd = VQD(estimator, fidelity, ansatz, optimizer, k=2)
result = vqd.compute_eigenvalues(h2_op)
eigenvalues = result.eigenvalues

All the algorithms in the qiskit.algorithms.eigensolvers package use the Qiskit Primitives. The old qiskit.algorithms.eigen_solvers package is now deprecated.

qiskit.algorithms.time_evolvers (formerly qiskit.algorithms.evolvers)

Primitive-enabled time evolution algorithms and interfaces now reside in the time_evolvers package. This includes the Trotterization-based Quantum Real Time Evolution algorithm and a new addition in this release — the Projected Variational Quantum Dynamics () algorithm. To learn more about the latter one, see the relevant .

You can use pVQD as follows:

import numpy as np 

from qiskit.algorithms.state_fidelities import ComputeUncompute
from qiskit.algorithms.evolvers import EvolutionProblem
from qiskit.algorithms.time_evolvers.pvqd import PVQD
from qiskit.primitives import Estimator, Sampler
from qiskit import BasicAer
from qiskit.circuit.library import EfficientSU2
from qiskit.quantum_info import Pauli, SparsePauliOp
from qiskit.algorithms.optimizers import L_BFGS_B

sampler = Sampler()
fidelity = ComputeUncompute(sampler)
estimator = Estimator()
hamiltonian = 0.1 * SparsePauliOp([Pauli(“ZZ”), Pauli(“IX”), Pauli(“XI”)])
observable = Pauli(“ZZ”)
ansatz = EfficientSU2(2, reps=1)
initial_parameters = np.zeros(ansatz.num_parameters)

time = 1
optimizer = L_BFGS_B()

pvqd = PVQD(
fidelity,
ansatz,
initial_parameters,
estimator,
num_timesteps=100,
optimizer=optimizer,
)

problem = EvolutionProblem(
hamiltonian, time, aux_operators=[hamiltonian, observable]
)

result = pvqd.evolve(problem)

The qiskit.algorithms.evolvers package, with algorithms not supporting Qiskit Primitives, is now deprecated.

qiskit.algorithms.phase_estimators, amplitude_estimators, and amplitude_amplifiers

Phase estimation and amplitude amplification/estimation algorithms now accept Qiskit primitive programs in their signatures. Otherwise, their logic and structure remain mostly unchanged. Throughout the deprecation period, they still accept the old QuantumInstance argument.

qiskit.algorithms.optimizers

In the optimizers package, the QN-SPSA optimizer now supports Qiskit Primitives by accepting a sampler in its get_fidelity method. Throughout the deprecation period, it still accepts the old QuantumInstance argument. Other optimizers in the package did not require changes.

New subroutines

With the introduction of Qiskit primitive programs, our developers used them to port features from the gradient framework, already available in Qiskit (Terra)’s opflow, to a new module with enhanced capabilities. They also implemented another subroutine useful in quantum algorithms development: calculating quantum state fidelities. They allow every algorithm developer to harvest the benefits of Qiskit Primitives in their work.

qiskit.algorithms.gradients

Many quantum algorithms require the computation of gradients of, e.g., sampling probabilities or expectation values. To make it easier to do that with Qiskit Primitives, we introduced a new module in algorithms called. Estimator gradients allow computing gradients of observables and sampler gradients allow computing gradients of sampled probabilities.

The supported gradient methods are: finite difference, linear combination of unitaries, parameters shift, and SPSA. A newly introduced class also enables calculating Quantum Fisher Information (QFI).

For example, to calculate the QFI of a quantum circuit with parametrized Z-rotation and X-rotation gates, one would do the following:

from qiskit.circuit import QuantumCircuit, Parameter 
from qiskit.algorithms.gradients import LinCombQFI

estimator = Estimator()
a, b = Parameter(“a”), Parameter(“b”)
qc = QuantumCircuit(1)
qc.h(0)
qc.rz(a, 0)
qc.rx(b, 0)
qfi = LinCombQFI(estimator)
parameter_value = [[np.pi / 4, 0]]
qfi_result = qfi.run([qc], parameter_value).result()

qiskit.algorithms.state_fidelities

Determining the overlap (or fidelity) between quantum states is a fundamental routine used in multiple settings — to assess the quality of time evolution algorithms of quantum states, to evaluate the cost functions or kernels in Quantum Machine Learning, or to characterize the noise of QPUs. In this release, we introduced a routine to do that using Qiskit Primitives. Currently, the compute-uncompute fidelity calculation method is provided in the package and you can read more about it .

For example, to calculate the fidelity between two quantum states given as quantum circuits, one would do it in the following way:

import numpy as np 
from qiskit.primitives import Sampler
from qiskit.algorithms.state_fidelities import ComputeUncompute
from qiskit.circuit.library import RealAmplitudes

sampler = Sampler(…)
fidelity = ComputeUncompute(sampler)
circuit = RealAmplitudes(2)
values = np.random.random(circuit.num_parameters)
shift = np.ones_like(values) * 0.01

job = fidelity.run([circuit], [circuit], [values], [values+shift])
fidelities = job.result().fidelities

Deprecations

The use of QuantumInstance

The use of the quantum_instance argument throughout the module was deprecated and primitives-based arguments were introduced. Algorithms will still support the use of a QuantumInstance throughout a deprecation period.

qiskit.algorithms.factorizers and linear_solvers

These packages that include Shor’s and HHL algorithms become deprecated. For working Qiskit implementations as well as the corresponding theory we refer to the Qiskit Textbook and .

Many people , special thanks to (in alphabetical order): , , , , , , , , .

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Qiskit

An open source quantum computing framework for writing quantum experiments and applications