HHL algorithm

Harrow-Hassidim-Lloyd (HHL) quantum algorithm [1] can be used to solve linear system problems and can provide exponential speedup over the classical conjugate gradient method chosen for this purpose. HHL is the basis of many more advanced algorithms and is important in various applications such as machine learning and modeling of quantum systems.

A linear system problem (LSP) can be represented as the following: Given a matrix and a vector , find the solution that satisfies the linear system

In the quantum case, we consider a special form here for simplicity. We assume is a Hermitian positive-definite matrix in , and can be encoded as a quantum state . The solution will also be readout as a quantum state . We also assume be a power of .

In the classical case, the solution now can be represented as Similarly, in the quantum case, the solution can be represented as

Assume the spectral decomposition of is where and are the eigenvalues and the eigenvectors of , respectively. Since are orthogonal, can also be decomposed onto this basis: Therefore,

The idea of the HHL algorithm is to construct a mapping for each eigenvector . Therefore, when the input state is , the algorithm will scale each eigenvector by a multiplicative factor , thereby preparing the solution state .

The HHL algorithm consists of five major steps, as shown as follows:

hhl circuit

  • Step 1: State Preparation. We need a unitary that prepares in the input register from .

  • Step 2: Phase Estimation. Since is a Hermitian positive-definite matrix, let us define the unitary . Thus i.e., the eigenvalue corresponding to eigenvector is . Recall that the quantum phase estimation (QPE) algorithm obtains approximately for eigenvalue . Therefore, applying QPE with will encode in computational basis in the ideal case. The qubits that hold are usually named the clock register. If the clock register contains qubits, thus it can contain integers from to , where . Let , where is a constant to be chosen. Thus QPE will yield: in the ideal case, where is a integer from to . By the way, the unitary can be implemented via Hamiltonian simulation. A simple way using Trotter decomposition is introduced in [2, Section 4.7].

  • Step 3: Controlled Rotation. This step consists of the following sequence of gates which is controlled by an ancilla initialized to : where is a constant to be chosen, and is the eigenvalue corresponding to the integer . Since this yields: This state contains all ingredient of . However, the clock register and the input register is entangled, thus we cannot obtain if we measure the ancilla register now.

  • Step 4: Inverse Phase Estimation. We need to uncompute Phase Estimation to change the clock register back to . Therefore, this step is precisely the inverse of Step 2.

  • Step 5: Repeat the circuit until the measurement result of the ancilla qubit is 1. In this case, the final state is in the idea case. Thus, the algorithm achieves the goal.

Let us write an isQ program to demonstrate the HHL algorithm with the following example of the linear system:

We solve this linear system theoretically first. The spectral decomposition of is Thus:

To solve this linear system via HHL, we need to bound all the eigenvalues of in first, where is a known parameter. If we know the bound, we can scale to satisfy the above requirement, thus the resulting can be seen as an upper bound of the condition number of .

For the above example, we can define . We can choose and , thus the eigenvalues will be converted to the integers , respectively. The constant is related to the success probability of the algorithm. The probability of the measurement outcome being is where can be chosen as a positive real number without loss of generality. On the other hand, since the quantum state must be a unit vector, must satisfy . Thus we can choose .

Now we have determined all the parameters in the HHL algorithm. The complete source code is as follows.

import std;
import qft;


/* 
State Preparation for |b>. 
*/
unit state_preparation(qbit q) {
    X(q);
}


/* 
Hamiltonian Simulation.
Here A = (1/2) I - (1/6) X, 
Thus e^{iAt} = e^{i (1/2) I t} e^{i (-1/6) X t}.
Here t = 1.5 * pi.
For more complicated case, Trotter decomposion is a straightforward way.
*/
unit hamiltonian_simulation(qbit q) {
    double alpha = 1.0/2;
    double beta = -1.0/6;
    double t = 1.5 * pi;
    GPhase(alpha * t);
    H(q);
    Rz(-2 * beta * t, q);
    H(q);
} deriving gate


/* 
Phase Estimation.
Here we set 2 qubits for clock register, where q_clock[0] is LSB.
*/
unit phase_estimation(qbit q_input, qbit q_clock[2]) {
    H(q_clock);
    ctrl hamiltonian_simulation(q_clock[0], q_input);
    ctrl hamiltonian_simulation(q_clock[1], q_input);
    ctrl hamiltonian_simulation(q_clock[1], q_input);
    qft_inv(q_clock);
}


/* 
Controlled Rotation.
Here theta_tau = 2 * arcsin (1/tau) for tau = 1,2,3, respectively.
The gate sequence is sum_{tau=1,2,3} |tau><tau| otimes RY(theta_tau).
*/
unit controlled_rotation(qbit q_clock[], qbit q_ancilla) {
    double theta_1 = pi;
    double theta_2 = pi / 3;
    double theta_3 = 0.679673818908;
    nctrl ctrl Ry(theta_1, q_clock[1], q_clock[0], q_ancilla);
    ctrl nctrl Ry(theta_2, q_clock[1], q_clock[0], q_ancilla);
    ctrl ctrl Ry(theta_3, q_clock[1], q_clock[0], q_ancilla);
}


/* 
Inversed Phase Estimation.
*/
unit inv_phase_estimation(qbit q_input, qbit q_clock[2]) {
    qft(q_clock);
    ctrl inv hamiltonian_simulation(q_clock[0], q_input);
    ctrl inv hamiltonian_simulation(q_clock[1], q_input);
    ctrl inv hamiltonian_simulation(q_clock[1], q_input);
    H(q_clock);
}


/* 
The HHL process.
*/
int hhl_process() {
    while (true) {
        qbit q_input;
        qbit q_clock[2];
        qbit q_ancilla;
        state_preparation(q_input);
        phase_estimation(q_input, q_clock);
        controlled_rotation(q_clock, q_ancilla);
        inv_phase_estimation(q_input, q_clock);
        // q_input will be ~ |x> if the measurement outcome of q_ancilla is |1> 
        if (M(q_ancilla)) {
            return M(q_input);
        }
    }
}


/* 
Repeat the HHL process for statistic outcome.
*/
unit statistic(int count) {
    int outcome_array[] = [0, 0];
    for i in 0 : count {
        int outcome = hhl_process();
        outcome_array[outcome] += 1;
    }
    for outcome in outcome_array {
        print outcome;
    }
}


unit main(){
    int count = 100;
    statistic(count);
}
Since the solution is without normalization, the printed statistics should be approximately for 0s and 1s.

Reference

  1. A. W. Harrow, A. Hassidim, and S. Lloyd, "Quantum Algorithm for Linear Systems of Equations," Phys. Rev. Lett. 103, 150502, 2009.
  2. M. A. Nielsen, and I. L. Chuang. "Quantum Computation and Quantum Information: 10th Anniversary Edition." Cambridge University Press, 2010.