Variational Quantum State Diagonalization¶

Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.

Overview¶

  • In this tutorial, we will train a quantum neural network (QNN) through Paddle Quantum to complete the diagonalization of quantum states.

  • First, import the following packages.

In [8]:
import numpy
from numpy import diag
from numpy import pi as PI
import scipy
import scipy.stats
import paddle
from paddle import matmul, trace
import paddle_quantum
from paddle_quantum import intrinsic
from paddle_quantum.ansatz import Circuit
from paddle_quantum.linalg import dagger

Background¶

The Variational Quantum State Diagonalization [1-3] aims to output the eigen-spectrum (eigenvalues) of a quantum state. Solving the eigenvalues ​​of quantum states has many applications in quantum computation, such as calculating fidelity and Von Neumann entropy.

  • Quantum state is usually a mixed state which can be expressed as follows:
ρmixed=∑iPi|ψi⟩⟨ψi|.(1)(1)ρmixed=∑iPi|ψi⟩⟨ψi|.
  • As an example, we consider a mixed 2-qubit quantum state with eigen-spectrum [0.5,0.3,0.1,0.1][0.5,0.3,0.1,0.1].
In [2]:
# Fixed random seed
numpy.random.seed(13) 
V = scipy.stats.unitary_group.rvs(4)  # Randomly generate a unitary matrix
D = diag([0.5, 0.3, 0.1, 0.1])        # Input the spectrum of the target state rho
V_H = V.conj().T                      # Conjugate transpose operation
rho = V @ D @ V_H                     # Generate rho by inverse spectral decomposition
print(numpy.around(rho, 4))           # Print quantum state rho
[[ 0.2569-0.j     -0.012 +0.0435j -0.0492-0.0055j -0.0548+0.0682j]
 [-0.012 -0.0435j  0.2959+0.j      0.1061-0.0713j -0.0392-0.0971j]
 [-0.0492+0.0055j  0.1061+0.0713j  0.2145-0.j      0.0294-0.1132j]
 [-0.0548-0.0682j -0.0392+0.0971j  0.0294+0.1132j  0.2327+0.j    ]]

Building a quantum neural network¶

  • In this case, we will learn the eigen-spectrum of quantum state ρρ defined above by training a QNN (also known as the parameterized quantum circuit). Here, we provide a predefined 2-qubit quantum circuit.

  • One can randomly initialize the QNN parameters →θθ→ containing 15 parameters.

In [3]:
N = 2           # The width of the quantum neural network
SEED = 14       # Fixed random seed
THETA_SIZE = 15 # The number of parameters in the quantum neural network

def U_theta(N: int) -> Circuit:
    """
    Quantum Neural Network
    """
    # Initialize the quantum neural network according to the number of qubits/network width
    cir = Circuit(N)
    # Call the built-in quantum neural network template
    cir.universal_two_qubits([0, 1])
    # Return the circuit of the quantum neural network
    return cir

Training model and loss function¶

  • After setting up the quantum state and the QNN architecture, we will further define the parameters to be trained, loss function, and optimization methods.
  • The quantum state obtained by acting the quantum neural network U(θ)U(θ) on ρρ is denoted by ~ρρ~, and we set the loss function to be the inner product of the quantum state σσ and ~ρρ~ where
σ=0.1|00⟩⟨00|+0.2|01⟩⟨01|+0.3|10⟩⟨10|+0.4|11⟩⟨11|,(2)(2)σ=0.1|00⟩⟨00|+0.2|01⟩⟨01|+0.3|10⟩⟨10|+0.4|11⟩⟨11|,
  • In specific, the loss function is defined as the state overlap
L(θ)=Tr(~ρσ).(3)(3)L(θ)=Tr(ρ~σ).
In [4]:
# Enter the quantum state sigma 
sigma = diag([0.1, 0.2, 0.3, 0.4])

class Net(paddle.nn.Layer):
    """
    Construct the model net
    """

    def __init__(self, rho, sigma):
        super(Net, self).__init__()
        
        # Convert Numpy array to Tensor supported in Paddle 
        complex_dtype = paddle_quantum.get_dtype()
        self.rho = paddle.cast(paddle.to_tensor(rho), complex_dtype)
        self.sigma = paddle.cast(paddle.to_tensor(sigma), complex_dtype)

        # Apply quantum neural network
        self.cir = U_theta(N)

    # Define loss function and forward propagation mechanism
    def forward(self, N):
        
        # rho_tilde is the quantum state U*rho*U^dagger 
        U = self.cir.unitary_matrix()
        rho_tilde = matmul(matmul(U, self.rho), dagger(U))

        # Calculate the loss function
        loss = trace(matmul(self.sigma, rho_tilde))

        return paddle.real(loss), rho_tilde, self.cir

Hyper-parameters¶

Before training the quantum neural network, we also need to set up several hyper-parameters, mainly the learning rate LR and the number of iterations ITR. Here we set the learning rate to be LR = 0.1 and the number of iterations to ITR = 50. One can adjust these hyper-parameters accordingly and check how they influence the training performance.

In [5]:
ITR = 50 # Set the total number of iterations of training
LR = 0.1 # Set the learning rate

Training process¶

  • After setting all the parameters of SSVQE model, we need to convert all the data into Tensor in the PaddlePaddle, and then train the quantum neural network.
  • We used Adam Optimizer in training, and one can also call other optimizers provided in Paddle.
In [9]:
paddle.seed(SEED)

# Determine the parameter dimension of the network
net = Net(rho=rho, sigma=sigma)

# We use Adam optimizer for better performance
# One can change it to SGD or RMSprop.
opt = paddle.optimizer.Adam(learning_rate=LR, parameters=net.parameters())

# Optimization loop
for itr in range(ITR):

    # Forward propagation calculates the loss function and returns the estimated energy spectrum
    loss, rho_tilde, cir = net(N)
    rho_tilde_np = rho_tilde.numpy()

    # Back propagation minimizes the loss function
    loss.backward()
    opt.minimize(loss)
    opt.clear_grad()

    # Print training results
    if itr% 10 == 0:
        print('iter:', itr,'loss:','%.4f'% loss.numpy()[0])
iter: 0 loss: 0.2494
iter: 10 loss: 0.1959
iter: 20 loss: 0.1843
iter: 30 loss: 0.1816
iter: 40 loss: 0.1805

Benchmarking¶

After 50 iterations, we have completed the diagonalization procedure. The next step is to verify the results of spectral decomposition by printing out ~ρ=U(θ)ρU†(θ)ρ~=U(θ)ρU†(θ). One can see the results are very close to what we expect.

In [7]:
print("The estimated spectrum is:", numpy.real(numpy.diag(rho_tilde_np)))
print("The target spectrum is:", numpy.diag(D))
The estimated spectrum is: [0.49938068 0.29916352 0.10103808 0.10041767]
The target spectrum is: [0.5 0.3 0.1 0.1]

References¶

[1] Larose, R., Tikku, A., Neel-judy, É. O., Cincio, L. & Coles, P. J. Variational quantum state diagonalization. npj Quantum Inf. (2019) doi:10.1038/s41534-019-0167-6.

[2] Nakanishi, K. M., Mitarai, K. & Fujii, K. Subspace-search variational quantum eigensolver for excited states. Phys. Rev. Res. 1, 033062 (2019).

[3] Cerezo, M., Sharma, K., Arrasmith, A. & Coles, P. J. Variational Quantum State Eigensolver. arXiv:2004.01372 (2020).