Use this file to discover all available pages before exploring further.
View on GitHub
Open this notebook in GitHub to run it yourself
The Poisson equation is a partial differential equation that appears in various research fields, such as physics and engineering. It describes the distribution of a potential field, u, under some source term b:∇2u=b,where the ∇2 is the Laplacian (second derivatives) operator.One approach for numerically solving the Poisson equation is to move from the continuous description to a discrete one, using the finite difference method that casts the problem into a set of linear equations. Then, the solution can be obtained by a linear solver.In this notebook we treat the Poisson equation on a rectangular geometry, Lx×Ly, with a Dirichlet boundary condition on the x axis and a Neumann boundary condition on the y axis:u(0)=u(Lx)=f0,∂yu∣y=0=∂yu∣y=Ly=g0.Furthermore, we assume that f0=g0=0.The discretization of space, including the treatment of the above boundary conditions, is given in Figure
The resulting linear equation reads:
L⋅u=b,L=Lxx⊗Iy+Ix⊗Lyy,where Lxx and Lyy are the Laplacian operators [1]:Lxx=Δx213−10⋮0−12−1⋮00−12⋮⋯⋯⋯⋯⋱−1000−13,Lyy=Δy211−10⋮0−12−1⋮00−12⋮⋯⋯⋯⋯⋱−1000−11and Δx and Δy are the discretization of the x and y axes, respectively.The square matrices above, which are of dimensions Nx and Ny, respectively, represent the solution at the inner part of our geometry.
In this notebook we solve the Poisson problem with the HHL quantum linear solver. We utilize similar ideas appearing in Ref.[2], where a quantum cosine and a quantum sine transforms [3] are performed towards achieving scalable implementation.
The HHL algorithm essentially applies a matrix inversion.Here we treat the Laplacian matrix, which can be diagonalized by quantum sine and cosine transforms. Thus, the matrix to invert is diagonal.The four main quantum blocks of the algorithm are thus (see Figure 2):
Prepare the amplitudes of the source term on a quantum variable.
Perform QST and QCT at the beginning of the computation.
This is done by applying the QST to the x qubits and the QCT to the y qubits.
Perform matrix inversion for a diagonal matrix.
Uncompute the QST and QCT at the end of the computation.
Below we define several classical and quantum functions for constructing our quantum linear solver.
import matplotlib.pyplot as pltimport numpy as npimport sympyfrom classiq import *
The eigenvalues of the Poisson equation with Dirichlet boundary conditions in the x direction and Neumann boundary conditions in the y direction are given byλk,j≡λx,k+λy,jwhereλx,k=Δx24sin2(2Nxπ(k+1)),λy,j=Δy24sin2(2Nyπj),and k=0,1,…,Ny−1 and j=0,1,…,Nx−1.The HHL algorithm requires the application of an Hamiltonian simulation eiH. In this notebook this quantum block is implemented using the Suzuki-Trotter built-in function. We start with defining a function that gets Nx and Ny and returns the corresponding Hamiltonian.The decomposition of the diagonal matrix to the Pauli basis is done using the Walsh Hadamard transform.
def get_poisson_dirichletx_neumanny_ham(nx, ny): dx2 = ny / nx dy2 = nx / ny eigenvalues_x = dx2 * 4 * np.sin(np.pi / 2 * np.arange(1, nx + 1) / nx) ** 2 eigenvalues_y = dy2 * 4 * np.sin(np.pi / 2 * np.arange(0, ny) / ny) ** 2 num_qubits_x = int(np.log2(nx)) num_qubits_y = int(np.log2(ny)) # Decompose the eigenvalues of the Poisson equation into Pauli terms and construct the corresponding Hamiltonian pauli_coefficients_x = sympy.fwht(eigenvalues_x / nx) pauli_coefficients_y = sympy.fwht(eigenvalues_y / ny) def convert_bitstring_to_pauli_representation( bitstring: int, num_qubits: int ) -> list[Pauli]: return [ Pauli.Z if (bitstring >> s) & 1 else Pauli.I for s in range(num_qubits) ][::-1] hamiltonian_y = [ PauliTerm( pauli=convert_bitstring_to_pauli_representation(i, num_qubits_y) + [Pauli.I] * num_qubits_x, coefficient=pauli_coefficients_y[i], ) for i in range(ny) ] hamiltonian_x = [ PauliTerm( pauli=[Pauli.I] * num_qubits_y + convert_bitstring_to_pauli_representation(i, num_qubits_x), coefficient=pauli_coefficients_x[i], ) for i in range(nx) ] hamiltonian = hamiltonian_x + hamiltonian_y return hamiltonian
The HHL is based on a QPE applied on eiHt.For this, we need to define a function implementing (eiHt)p for an integer power p.Since in our case the Hamiltonian is diagonal, an exact implementation is given by the first order Suzuki-Trotter formula, where in addition (eiHt)p=eipHt.
@qfuncdef powered_hamiltonian_evolution( hamiltonian: CArray[PauliTerm], # the hamiltonian H scaling: CReal, # the scaling factor t p: CInt, # the power qba: QArray,): suzuki_trotter( pauli_operator=hamiltonian, evolution_coefficient=p * (-2 * np.pi * scaling), order=1, repetitions=1, qbv=qba, )
For the system treated in this notebook, diagonalization of the 2D Laplacian is given by appying a quantum sine (cosine) transform on the x (y) dimension. We define a quantum function for implementing this, using the open library functions:
We solve an example with a square grid of Nx,Ny=23.For the source term we take a non-separable 2Nx+Ny vector that represents the functionb=F[xy(x−Lx)(y−Ly)].We choose F(z)=tanh(z)2, which satisfies the boundary conditions.
# Set discretization of X and Y axes.NUM_QUBITS_X = 3NUM_QUBITS_Y = 3nx = 2**NUM_QUBITS_Xny = 2**NUM_QUBITS_Y# Set the source term as function of x and y such that b(x,y) = exp(-1/xy(x-Lx)(y-Ly))xgrid = (np.arange(nx) + 0.5) / nxygrid = (np.arange(ny) + 0.5) / nyzgrid = np.kron(ygrid, xgrid) * np.kron( (ygrid - 1), (xgrid - 1)) # in classiq the variable order is [y,x]b_vector = np.tanh(zgrid) ** 2# Normalize the source termb_vector = b_vector / np.linalg.norm(b_vector)
Now, we need to define the resolution (QPE size) for the quantum solver.The required number of QPE phase qubits depends on the condition number κ of the inverted matrix. In the case of the Laplacian matrix, this parameter is of the order of the matrix dimension O(2Nx+Ny), which eliminates the exponential advantage. However, in our example we have a smooth source term, and high modes are expected to have minor effects on the solution.The highest mode of L is λmax=8, whereas the smallest one is λx,0∼4π2/(2Nx)2.Let us assume that the highest mode participating in the solution is ∼26λx,0, and take a QPE size of six qubits.
# number of qubits for the QPEQPE_SIZE = 6MAX_EIG = 4 * (np.pi / (2 * nx)) ** 2 * 2**6# parameters for the amplitude preparationPREFACTOR = 2**-QPE_SIZE
We build the model and synthesize it:Comment: the model is designed for the case of symmetric grid Nx=Ny, working with QArray[QNum,2]. In the more general case one can define a QStruct with two QNum variable of different sizes