Path: blob/main/notebooks/ch-applications/hhl_tutorial.ipynb
3328 views
Solving linear systems of equations using HHL and its Qiskit implementation
In this tutorial, we introduce the HHL algorithm, derive the circuit, and implement it using Qiskit. We show how to run the HHL on a simulator and on a five qubit device.
1. Introduction
We see systems of linear equations in many real-life applications across a wide range of areas. Examples include the solution of Partial Differential Equations, the calibration of financial models, fluid simulation or numerical field calculation. The problem can be defined as, given a matrix and a vector , find satisfying
For example, take ,
Then the problem can also be written as find {latex} x_{1}, x_{2}\in\mathbb{C}
such that
A system of linear equations is called -sparse if has at most non-zero entries per row or column. Solving an -sparse system of size with a classical computer requires running time using the conjugate gradient method 1. Here denotes the condition number of the system and the accuracy of the approximation.
The HHL algorithm estimates a function of the solution with running time complexity of 2. The matrix must be Hermitian, and we assume we have efficient oracles for loading the data, Hamiltonian simulation, and computing a function of the solution. This is an exponential speed up in the size of the system, with the catch that HHL can only approximate functions of the solution vector, while the classical algorithm returns the full solution.
2. The HHL algorithm
A. Some mathematical background
The first step towards solving a system of linear equations with a quantum computer is to encode the problem in the quantum language. By rescaling the system, we can assume and to be normalised and map them to the respective quantum states and . Usually the mapping used is such that component of (resp. ) corresponds to the amplitude of the basis state of the quantum state (resp. ). From now on, we will focus on the rescaled problem
Since is Hermitian, it has a spectral decomposition where is the eigenvector of with respective eigenvalue . Then, and the right hand side of the system can be written in the eigenbasis of as It is useful to keep in mind that the goal of the HHL is to exit the algorithm with the readout register in the state Note that here we already have an implicit normalisation constant since we are talking about a quantum state.
B. Description of the HHL algorithm
The algorithm uses three quantum registers, all set to at the beginning of the algorithm. One register, which we will denote with the subindex , is used to store a binary representation of the eigenvalues of . A second register, denoted by , contains the vector solution, and from now on . There is an extra register for auxiliary qubits, used for intermediate steps in the computation. We can ignore any auxiliary in the following description as they are at the beginning of each computation, and are restored back to at the end of each individual operation.
The following is an outline of the HHL algorithm with a high-level drawing of the corresponding circuit. For simplicity all computations are assumed to be exact in the ensuing description, and a more detailed explanation of the non-exact case is given in Section 2.D..
Load the data . That is, perform the transformation
Apply Quantum Phase Estimation (QPE) with The quantum state of the register expressed in the eigenbasis of is now where
{latex} |\lambda _ {j }\rangle_{n_{l}}
is the -bit binary representation of .Add an auxiliary qubit and apply a rotation conditioned on , where is a normalisation constant, and, as expressed in the current form above, should be less than the smallest eigenvalue in magnitude, i.e., .
Apply QPE. Ignoring possible errors from QPE, this results in
Measure the auxiliary qubit in the computational basis. If the outcome is , the register is in the post-measurement state which up to a normalisation factor corresponds to the solution.
Apply an observable to calculate .
C. Quantum Phase Estimation (QPE) within HHL
Quantum Phase Estimation is described in more detail in Chapter 3. However, since this quantum procedure is at the core of the HHL algorithm, we recall here the definition. Roughly speaking, it is a quantum algorithm which, given a unitary with eigenvector and eigenvalue , finds . We can formally define this as follows.
Definition: Let be unitary and let be one of its eigenvectors with respective eigenvalue . The Quantum Phase Estimation algorithm, abbreviated QPE, takes as inputs the unitary gate for and the state {latex} |0\rangle_{n}|\psi\rangle_{m}
and returns the state {latex} |\tilde{\theta}\rangle_{n}|\psi\rangle_{m}
. Here denotes a binary approximation to and the subscript denotes it has been truncated to digits.
For the HHL we will use QPE with , where is the matrix associated to the system we want to solve. In this case, Then, for the eigenvector {latex} |u_{j}\rangle_{n_{b}}
, which has eigenvalue , QPE will output {latex} |\tilde{\lambda }_ { j }\rangle_{n_{l}}|u_{j}\rangle_{n_{b}}
. Where represents an -bit binary approximation to . Therefore, if each can be exactly represented with bits,
D. Non-exact QPE
In reality, the quantum state of the register after applying QPE to the initial state is
where
Denote by the best -bit approximation to , . Then we can relabel the -register so that denotes the amplitude of {latex} |l + \tilde { \lambda } _ { j } \rangle_{n_{l}}
. So now,
If each can be represented exactly with binary bits, then {latex} \frac { \lambda _ { j } t } { 2 \pi }=\frac { \tilde { \lambda } _ { j } } { 2 ^ { n_{l} } }
. Therefore in this case , , it holds that and . Only in this case we can write that the state of the register after QPE is
Otherwise, is large if and only if {latex} \frac { \lambda _ { j } t } { 2 \pi } \approx \frac { l + \tilde { \lambda } _ { j } } { 2 ^ { n_{l} } }
and the state of the register is
3. Example: 4-qubit HHL
Let's take the small example from the introduction to illustrate the algorithm. That is,
We will use qubit to represent , and later the solution , qubits to store the binary representation of the eigenvalues and auxiliary qubit to store whether the conditioned rotation, hence the algorithm, was successful.
For the purpose of illustrating the algorithm, we will cheat a bit and calculate the eigenvalues of to be able to choose to obtain an exact binary representation of the rescaled eigenvalues in the -register. However, keep in mind that for the HHL algorithm implementation one does not need previous knowledge of the eigenvalues. Having said that, a short calculation will give
Recall from the previous section that the QPE will output an -bit (-bit in this case) binary approximation to . Therefore, if we set the QPE will give a -bit binary approximation to which is, respectively,
The eigenvectors are, respectively,
Again, keep in mind that one does not need to compute the eigenvectors for the HHL implementation. In fact, a general Hermitian matrix of dimension can have up to different eigenvalues, therefore calculating them would take time and the quantum advantage would be lost.
We can then write in the eigenbasis of as
Now we are ready to go through the different steps of the HHL algorithm.
State preparation in this example is trivial since .
Applying QPE will yield
Conditioned rotation with that is less than the smallest (rescaled) eigenvalue of . Note, the constant here needs to be chosen such that it is less than the smallest (rescaled) eigenvalue of but as large as possible so that when the auxiliary qubit is measured, the probability of it being in the state is large.
After applying QPE the quantum computer is in the state
On outcome when measuring the auxiliary qubit, the state is A quick calculation shows that
Without using extra gates, we can compute the norm of : it is the probability of measuring in the auxiliary qubit from the previous step.
4. Qiskit Implementation
Now that we have analytically solved the problem from the example we are going to use it to illustrate how to run the HHL on a quantum simulator and on the real hardware. The following uses quantum_linear_solvers
, a Qiskit-based package which can be found in this repository and installed as described in the corresponding Readme
file. For the quantum simulator, quantum_linear_solvers
already provides an implementation of the HHL algorithm requiring only the matrix and as inputs in the simplest example. Although we can give the algorithm a general Hermitian matrix and an arbitrary initial state as NumPy arrays, in these cases the quantum algorithm will not achieve an exponential speedup. This is because the default implementation is exact and therefore exponential in the number of qubits. There is no algorithm polynomial resources in the number of qubits that can prepare an exact arbitrary quantum state, or that can perform the exact operation for some general Hermitian matrix . If we know an efficient implementation for a particular problem, the matrix and/or the vector can be given as QuantumCircuit
objects. Alternatively, there's already an efficient implementation for tridiagonal Toeplitz matrices and in the future there might be more.
However,at the time of writing the existing quantum computers are noisy and can only run small circuits. Therefore, in Section 4.B. we will see an optimised circuit that can be used for a class of problems to which our example belongs and mention the existing procedures to deal with noise in quantum computers.
A. Running HHL on a simulator: general method
The interface for all algorithms to solve the linear system problem is LinearSolver
. The problem to be solved is only specified when the solve()
method is called:
The simplest implementation takes the matrix and the vector as NumPy arrays. Below we also create a NumPyLinearSolver
(the classical algorithm) to validate our solutions.
For the classical solver we need to rescale the right hand side (i.e. vector / np.linalg.norm(vector)
) to take into account the renormalisation that occurs once vector
is encoded in a quantum state within HHL.
The linear_solvers
package contains a folder called matrices
intended to be a placeholder for efficient implementations of particular types of matrices. At the time of writing the only truly efficient implementation it contains (i.e. complexity scaling polynomially in the number of qubits) is the TridiagonalToeplitz
class. Tridiagonal Toeplitz symmetric real matrices are of the following form (note that in this setting we do not consider non symmetric matrices since the HHL algorithm assumes that the input matrix is Hermitian).
Since the matrix from our example is of this form we can create an instance of TridiagonalToeplitz(num_qubits, a, b)
and compare the results to solving the system with an array as input.
Recall that the HHL algorithm can find a solution exponentially faster in the size of the system than their classical counterparts (i.e. logarithmic complexity instead of polynomial). However the cost for this exponential speedup is that we do not obtain the full solution vector. Instead, we obtain a quantum state representing the vector and learning all the components of this vector would take a linear time in its dimension, diminishing any speedup obtained by the quantum algorithm.
Therefore, we can only compute functions from (the so called observables) to learn information about the solution. This is reflected in the LinearSolverResult
object returned by solve()
, which contains the following properties
state
: either the circuit that prepares the solution or the solution as a vectoreuclidean_norm
: the euclidean norm if the algorithm knows how to calculate itobservable
: the (list of) calculated observable(s)circuit_results
: the observable results from the (list of) circuit(s)
Let's ignore observable
and circuit_results
for the time being and check the solutions we obtained before.
First, classical_solution
was the result from a classical algorithm, so if we call .state
it will return an array:
Our other two examples were quantum algorithms, hence we can only access to the quantum state. This is achieved by returning the quantum circuit that prepares the solution state:
Recall that the Euclidean norm for a vector {latex} \mathbf{x}=(x_1,\dots,x_N)
is defined as . Therefore, the probability of measuring in the auxiliary qubit from Step in Section B is the squared norm of . This means that the HHL algorithm can always calculate the euclidean norm of the solution and we can compare the accuracy of the results:
Comparing the solution vectors component-wise is more tricky, reflecting again the idea that we cannot obtain the full solution vector from the quantum algorithm. However, for educational purposes we can check that indeed the different solution vectors obtained are a good approximation at the vector component level as well.
To do so first we need to use Statevector
from the quantum_info
package and extract the right vector components, i.e. those corresponding to the ancillary qubit (bottom in the circuits) being and the work qubits (the two middle in the circuits) being . Thus, we are interested in the states 10000
and 10001
, corresponding to the first and second components of the solution vector respectively.
At a first glance it might seem that this is wrong because the components are complex numbers instead of reals. However note that the imaginary part is very small, most likely due to computer accuracy, and can be disregarded in this case (we'll use the array's .real
attribute to get the real part).
Next, we will divide the vectors by their respective norms to suppress any constants coming from the different parts of the circuits. The full solution vector can then be recovered by multiplying these normalised vectors by the respective Euclidean norms calculated above:
It should not come as a surprise that naive_hhl_solution
is exact because all the default methods used are exact. However, tridi_solution
is exact only in the system size case. For larger matrices it will be an approximation, as shown in the slightly larger example below.
We can also compare the difference in resources from the exact method and the efficient implementation. The system size is again special in that the exact algorithm requires less resources, but as we increase the system size, we can see that indeed the exact method scales exponentially in the number of qubits while TridiagonalToeplitz
is polynomial.
The reason the implementation still seems to need exponential resources is because the current conditioned rotation implementation (step 3 from Section 2.B) is exact (i.e. needs exponential resources in ). Instead we can calculate how many more resources the default implementation needs compared to Tridiagonal - since they only differ in how they implement :
In the near future the plan is to integrate qiskit.circuit.library.arithmetics.PiecewiseChebyshev
to obtain a polynomial implementation of the conditioned rotation as well.
Now we can return to the topic of observables and find out what the observable
and circuit_results
properties contain.
The way to compute functions of the solution vector is through giving the .solve()
method a LinearSystemObservable
as input. There are two types of available LinearSystemObservable
which can be given as input:
For a vector {latex} \mathbf{x}=(x_1,...,x_N)
, the AbsoluteAverage
observable computes .
The MatrixFunctional
observable computes for a vector and a tridiagonal symmetric Toeplitz matrix . The class takes the main and off diagonal values of the matrix for its constructor method.
Therefore, observable
contains the final value of the function on , while circuit_results
contains the raw values obtained from the circuit and used to process the result of observable
.
This 'how to process the result' is better explained by looking at what arguments .solve()
takes. The solve()
method accepts up to five arguments:
The first two are the matrix defining the linear system and the vector right hand side of the equation, which we have already covered. The remaining parameters concern the (list of) observable(s) to be computed out of the solution vector , and can be specified in two different ways. One option is to give as the third and last parameter a (list of) LinearSystemObservable
(s). Alternatively, we can give our own implementations of the observable
, post_rotation
and post_processing
, where
observable
is the operator to compute the expected value of the observable and can be e.g. aPauliSumOp
post_rotation
is the circuit to be applied to the solution to extract information if additional gates are needed.post_processing
is the function to compute the value of the observable from the calculated probabilities.
In other words, there will be as many circuit_results
as post_rotation
circuits, and post_processing
is telling the algorithm how to use the values we see when we print circuit_results
to obtain the value we see when we print observable
.
Finally, the HHL
class accepts the following parameters in its constructor method:
error tolerance : the accuracy of the approximation of the solution, the default is
1e-2
expectation : how the expectation values are evaluated, the default is
PauliExpectation
quantum instance: the
QuantumInstance
or backend, the default is aStatevector
simulation
B. Running HHL on a real quantum device: optimised example
In the previous section we ran the standard algorithm provided in Qiskit and saw that it uses qubits, has a depth of ~ gates and requires a total of CNOT gates. These numbers are not feasible for the current available hardware, therefore we need to decrease these quantities. In particular, the goal will be to reduce the number of CNOTs by a factor of since they have worse fidelity than single-qubit gates. Furthermore, we can reduce the number of qubits to as was the original statement of the problem: the Qiskit method was written for a general problem and that is why it requires additional auxiliary qubits.
However, solely decreasing the number of gates and qubits will not give a good approximation to the solution on real hardware. This is because there are two sources of errors: those that occur during the run of the circuit and readout errors.
Qiskit provides a module to mitigate the readout errors by individually preparing and measuring all basis states, a detailed treatment on the topic can be found in the paper by Dewes et al.3 To mitigate errors, we can use Richardson extrapolation to calculate the error to the zero limit by running the circuit three times, each replacing each CNOT gate by , and CNOTs respectively4. The idea is that theoretically the three circuits should produce the same result, but in real hardware adding CNOTs means amplifying the error. Since we know we've obtained results with an amplified error, and we can estimate how much the error was amplified by in each case, we can recombine the quantities to get a new result closer to the analytic solution.
Below we give the optimised circuit that can be used for any problem of the form
The following optimisation was extracted from a work on the HHL for tridiagonal symmetric matrices[5], this particular circuit was derived with the aid of the UniversalQCompiler software[6].
The code below takes as inputs our circuit, the real hardware backend and the set of qubits we want to use, and returns and instance that can be run on the specified device. Creating the circuits with and CNOTs is the same but calling the transpile method with the right quantum circuit.
Real hardware devices need to be recalibrated regularly, and the fidelity of a specific qubit or gate can change over time. Furthermore, different chips have different qubit connectivity. If we try to run a circuit that performs a two-qubit gate between two qubits that are not connected on the specified device, the transpiler will add SWAP gates. Therefore it is good practice to check with the IBM Quantum Experience webpage[7] before running the following code and choose a set of qubits with the right connectivity and lowest error rates at the given time.
The next step is to create the extra circuits used to mitigate the readout errors[3].
The following plot[5], shows the results from running the circuit above on real hardware for different initial states. The -axis represents the angle defining the initial state in each case. The results where obtained after mitigating the readout error and then extrapolating the errors arising during the run of the circuit from the results with the circuits with , and CNOTs.
Compare to the results without error mitigation nor extrapolation from the CNOTs5.
8. Problems
Real hardware:
Set the time parameter for the optimised example.
The best result is to set it so that the smallest eigenvalue can be represented exactly, since it's inverse will have the largest contribution in the solution
Create transpiled circuits for and CNOTs from a given circuit '
qc
'. When creating the circuits you will have to add barriers so that these consecutive CNOT gates do not get cancelled when using thetranspile()
function.Run your circuits on the real hardware and apply a quadratic fit to the results to obtain the extrapolated value.
9. References
J. R. Shewchuk. An Introduction to the Conjugate Gradient Method Without the Agonizing Pain. Technical Report CMU-CS-94-125, School of Computer Science, Carnegie Mellon University, Pittsburgh, Pennsylvania, March 1994.
A. W. Harrow, A. Hassidim, and S. Lloyd, “Quantum algorithm for linear systems of equations,” Phys. Rev. Lett. 103.15 (2009), p. 150502.
A. Dewes, F. R. Ong, V. Schmitt, R. Lauro, N. Boulant, P. Bertet, D. Vion, and D. Esteve, “Characterization of a two-transmon processor with individual single-shot qubit readout,” Phys. Rev. Lett. 108, 057002 (2012).
N. Stamatopoulos, D. J. Egger, Y. Sun, C. Zoufal, R. Iten, N. Shen, and S. Woerner, “Option Pricing using Quantum Computers,” arXiv:1905.02666 .
A. Carrera Vazquez, A. Frisch, D. Steenken, H. S. Barowski, R. Hiptmair, and S. Woerner, “Enhancing Quantum Linear System Algorithm by Richardson Extrapolation,” ACM Trans. Quantum Comput. 3 (2022).
R. Iten, O. Reardon-Smith, L. Mondada, E. Redmond, R. Singh Kohli, R. Colbeck, “Introduction to UniversalQCompiler,” arXiv:1904.01072 .
D. Bucher, J. Mueggenburg, G. Kus, I. Haide, S. Deutschle, H. Barowski, D. Steenken, A. Frisch, "Qiskit Aqua: Solving linear systems of equations with the HHL algorithm" https://github.com/Qiskit/qiskit-tutorials/blob/master/legacy_tutorials/aqua/linear_systems_of_equations.ipynb