Skip to content

Ground-state VQE

Restricted Hamiltonian

Simple implementation of the UCC ansatz for computing the ground state of the H2 molecule. The Hamiltonian coefficients are taken from the following paper: https://arxiv.org/pdf/1512.06860.pdf.

Simple 2 qubits unitary coupled cluster ansatz for H2 molecule

import torch
from qadence import X, RX, RY, RZ, CNOT, chain, kron

def UCC_ansatz_H2():
    ansatz=chain(
        kron(chain(X(0), RX(0, -torch.pi/2)), RY(1, torch.pi/2)),
        CNOT(1,0),
        RZ(0, f"theta"),
        CNOT(1,0),
        kron(RX(0, torch.pi/2), RY(1, -torch.pi/2))
    )
    return ansatz
%3 6ab8998bf22341aca7ef1370d5c5dbba 0 6e76a9e9a3d1494fb310a0c8fec57be1 X 6ab8998bf22341aca7ef1370d5c5dbba--6e76a9e9a3d1494fb310a0c8fec57be1 4b12db8ca3f048478027da40cc6dfa14 1 73a79f3d1b3c478695f1ef4d12e63816 RX(-1.571) 6e76a9e9a3d1494fb310a0c8fec57be1--73a79f3d1b3c478695f1ef4d12e63816 2d0c82ace50b4115a0b5cb92e48a621c X 73a79f3d1b3c478695f1ef4d12e63816--2d0c82ace50b4115a0b5cb92e48a621c c1500c7436f74ce1a26d0971d630948c RZ(theta) 2d0c82ace50b4115a0b5cb92e48a621c--c1500c7436f74ce1a26d0971d630948c c1522b1f837d4d9d89f0c6a64fa30110 2d0c82ace50b4115a0b5cb92e48a621c--c1522b1f837d4d9d89f0c6a64fa30110 e18a21c7922945bc9563ee4113663096 X c1500c7436f74ce1a26d0971d630948c--e18a21c7922945bc9563ee4113663096 9a124b5d99b1416c856c15f97294a19e RX(1.571) e18a21c7922945bc9563ee4113663096--9a124b5d99b1416c856c15f97294a19e 8aaed91401c7424bbb296bd9d254f09d e18a21c7922945bc9563ee4113663096--8aaed91401c7424bbb296bd9d254f09d c56db029bed54cb8b010444c4b2d1d1c 9a124b5d99b1416c856c15f97294a19e--c56db029bed54cb8b010444c4b2d1d1c f63fcf39626e478bbb6c11defd2e6443 d99fd81b903346079bc47cf0e341cbd8 RY(1.571) 4b12db8ca3f048478027da40cc6dfa14--d99fd81b903346079bc47cf0e341cbd8 f4ae22006ef24280b0be828d7f935fbc d99fd81b903346079bc47cf0e341cbd8--f4ae22006ef24280b0be828d7f935fbc f4ae22006ef24280b0be828d7f935fbc--c1522b1f837d4d9d89f0c6a64fa30110 28b21ef1fd354b6491964f87b74b53ea c1522b1f837d4d9d89f0c6a64fa30110--28b21ef1fd354b6491964f87b74b53ea 28b21ef1fd354b6491964f87b74b53ea--8aaed91401c7424bbb296bd9d254f09d 02ef7047ca604bafba64759a7850266d RY(-1.571) 8aaed91401c7424bbb296bd9d254f09d--02ef7047ca604bafba64759a7850266d 02ef7047ca604bafba64759a7850266d--f63fcf39626e478bbb6c11defd2e6443

Let's define the Hamiltonian of the problem in the following form: hamilt = [list of coefficients, list of Pauli operators, list of qubits]. For example: hamilt=[[3,4],[[X,X],[Y]],[[0,1],[3]]].

In the following function we generate the Hamiltonian with the format above.

from typing import Iterable
from qadence import X, Y, Z, I, add
def make_hamiltonian(hamilt: Iterable, nqubits: int):

    nb_terms = len(hamilt[0])
    blocks = []

    for iter in range(nb_terms):
        block = kron(gate(qubit) for gate,qubit in zip(hamilt[1][iter], hamilt[2][iter]))
        blocks.append(hamilt[0][iter] * block)

    return add(*blocks)


nqbits = 2

# Hamiltonian definition using the convention outlined above
hamilt_R07 = [
    [0.2976, 0.3593, -0.4826,0.5818, 0.0896, 0.0896],
    [[I,I],[Z],[Z],[Z,Z],[X,X],[Y,Y]],
    [[0,1],[0],[1],[0,1],[0,1],[0,1]]
]

hamiltonian = make_hamiltonian(hamilt_R07, nqbits)
%3 cluster_8a2069be0f634837909d31ae7102eaba 0cd3ec89dcdc49abaa4901b2440146cd 0 f3d2f41c031c4493af97844d8456e8fb AddBlock 0cd3ec89dcdc49abaa4901b2440146cd--f3d2f41c031c4493af97844d8456e8fb f9ab8aae13ce4bbca9b8ca77577f2e52 1 7de6ada6f2734da7829c254a83524770 f3d2f41c031c4493af97844d8456e8fb--7de6ada6f2734da7829c254a83524770 521aa40ff85142e8b17fcbf67a3551a3 588c25e43cd94439bbf4922adf730522 f9ab8aae13ce4bbca9b8ca77577f2e52--588c25e43cd94439bbf4922adf730522 588c25e43cd94439bbf4922adf730522--521aa40ff85142e8b17fcbf67a3551a3

Let's now create a QuantumCircuit representing the variational ansatz and plug it into a QuantumModel instance. From there, it is very easy to compute the energy by simply evaluating the expectation value of the Hamiltonian operator.

from qadence import QuantumCircuit, QuantumModel

ansatz = QuantumCircuit(nqbits, UCC_ansatz_H2())
model = QuantumModel(ansatz, observable=hamiltonian, backend="pyqtorch", diff_mode="ad")

values={}
out = model.expectation(values)
print(out)
tensor([[-1.1424]], grad_fn=<CatBackward0>)
Let's now resent the parameters and set them randomly before starting the optimization loop.

init_params = torch.rand(model.num_vparams)
model.reset_vparams(init_params)

n_epochs = 100
lr = 0.05
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
for i in range(n_epochs):
    optimizer.zero_grad()
    out=model.expectation({})
    out.backward()
    optimizer.step()

print("Ground state energy =", out.item(), "Hatree")
Ground state energy = -1.1449597119944634 Hatree

Unrestricted Hamiltonian

This result is in line with what obtained in the reference paper. Let's now perform the same calculations but with a standard hardware efficient ansatz (i.e. not specifically tailored for the H2 molecule) and with an unrestricted Hamiltonian on 4 qubits. The values of the coefficients are taken from BK Hamiltonian, page 28[^2].

from qadence import hea

nqbits = 4

gates = [[I,I,I,I],[Z],[Z],[Z],[Z,Z],[Z,Z],[Z,Z],[X,Z,X],[Y,Z,Y],[Z,Z,Z],[Z,Z,Z],[Z,Z,Z],[Z,X,Z,X],[Z,Y,Z,Y],[Z,Z,Z,Z]]
qubits = [[0,1,2,3],[0],[1],[2],[0,1],[0,2],[1,3],[2,1,0],[2,1,0],[2,1,0],[3,2,0],[3,2,1],[3,2,1,0],[3,2,1,0],[3,2,1,0]]
coeffs = [
    -0.81261,0.171201,0.16862325,- 0.2227965,0.171201,0.12054625,0.17434925  ,0.04532175,0.04532175,0.165868 ,
    0.12054625,-0.2227965 ,0.04532175 ,0.04532175,0.165868
]

hamilt_R074_bis = [coeffs,gates,qubits]
Hamiltonian_bis = make_hamiltonian(hamilt_R074_bis, nqbits)
ansatz_bis = QuantumCircuit(4, hea(nqbits))
%3 9e2c7fffed7c4f4b82a8ff6bd1c5cb0c 0 155dfc1efd8640a5a90ce98b21dc3a20 RX(theta₀) 9e2c7fffed7c4f4b82a8ff6bd1c5cb0c--155dfc1efd8640a5a90ce98b21dc3a20 5ed5b5ffd4e9450b859ee98ca62ec92d 1 750bf1243b9f4533bd08f2eb92115feb RY(theta₄) 155dfc1efd8640a5a90ce98b21dc3a20--750bf1243b9f4533bd08f2eb92115feb 2c09731d063245669bcd8782f711696d RX(theta₈) 750bf1243b9f4533bd08f2eb92115feb--2c09731d063245669bcd8782f711696d dbbd54205f674f928bd6736f8d62ef3f 2c09731d063245669bcd8782f711696d--dbbd54205f674f928bd6736f8d62ef3f dde36d08aaa84c68905cef18d20b26c6 dbbd54205f674f928bd6736f8d62ef3f--dde36d08aaa84c68905cef18d20b26c6 d7fa7dcbfc5e44dc971ac2824be54a30 dde36d08aaa84c68905cef18d20b26c6--d7fa7dcbfc5e44dc971ac2824be54a30 be690caa97354689afe5b9dd05987ee6 1e68b377fc0c41bea10b2fc870c12018 RX(theta₁) 5ed5b5ffd4e9450b859ee98ca62ec92d--1e68b377fc0c41bea10b2fc870c12018 db8be7ebde1f458987ad75570b1e009f 2 e5949fb757b84bbcb724b15d409012ac RY(theta₅) 1e68b377fc0c41bea10b2fc870c12018--e5949fb757b84bbcb724b15d409012ac 66728acff123451aa645def337fc04df RX(theta₉) e5949fb757b84bbcb724b15d409012ac--66728acff123451aa645def337fc04df e4c954f02fda46b78c53ce3d4eb85661 X 66728acff123451aa645def337fc04df--e4c954f02fda46b78c53ce3d4eb85661 e4c954f02fda46b78c53ce3d4eb85661--dbbd54205f674f928bd6736f8d62ef3f 5c0b8c910e0c4e57add7ab90523dcd25 e4c954f02fda46b78c53ce3d4eb85661--5c0b8c910e0c4e57add7ab90523dcd25 5c0b8c910e0c4e57add7ab90523dcd25--be690caa97354689afe5b9dd05987ee6 dc334bfc00d140b0a506547d3d13716c 45c773c45a1a4c06ae953c95d8f292b8 RX(theta₂) db8be7ebde1f458987ad75570b1e009f--45c773c45a1a4c06ae953c95d8f292b8 0f21b0afc7f34b2e97194df681fab66d 3 e0b3db40288f48988b76ddf0f220baab RY(theta₆) 45c773c45a1a4c06ae953c95d8f292b8--e0b3db40288f48988b76ddf0f220baab 3b3a1a873ce94aa39a92c1351d816e64 RX(theta₁₀) e0b3db40288f48988b76ddf0f220baab--3b3a1a873ce94aa39a92c1351d816e64 da2b0b5ede344fdf8e18bcc83c89afbe 3b3a1a873ce94aa39a92c1351d816e64--da2b0b5ede344fdf8e18bcc83c89afbe 8a0d36dd9a604fffa4a5ad23f7b027dd X da2b0b5ede344fdf8e18bcc83c89afbe--8a0d36dd9a604fffa4a5ad23f7b027dd 8a0d36dd9a604fffa4a5ad23f7b027dd--5c0b8c910e0c4e57add7ab90523dcd25 8a0d36dd9a604fffa4a5ad23f7b027dd--dc334bfc00d140b0a506547d3d13716c 99896b37906c488db829f47cda696571 064aac6a10664503bf6a4e8ca4132cb0 RX(theta₃) 0f21b0afc7f34b2e97194df681fab66d--064aac6a10664503bf6a4e8ca4132cb0 750a9f4e8d8a4114b8bc7e08664055a5 RY(theta₇) 064aac6a10664503bf6a4e8ca4132cb0--750a9f4e8d8a4114b8bc7e08664055a5 8aafadb8e3ae443bb865deec21fd0ef1 RX(theta₁₁) 750a9f4e8d8a4114b8bc7e08664055a5--8aafadb8e3ae443bb865deec21fd0ef1 b9c127e3892847db84d780696a7f402b X 8aafadb8e3ae443bb865deec21fd0ef1--b9c127e3892847db84d780696a7f402b b9c127e3892847db84d780696a7f402b--da2b0b5ede344fdf8e18bcc83c89afbe 201602e830d74d7b998a34f69b1ab012 b9c127e3892847db84d780696a7f402b--201602e830d74d7b998a34f69b1ab012 201602e830d74d7b998a34f69b1ab012--99896b37906c488db829f47cda696571
model = QuantumModel(ansatz_bis, observable=Hamiltonian_bis, backend="pyqtorch", diff_mode="ad")

values={}
out=model.expectation(values)

# initialize some random initial parameters
init_params = torch.rand(model.num_vparams)
model.reset_vparams(init_params)

n_epochs = 100
lr = 0.05
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
for i in range(n_epochs):

    optimizer.zero_grad()
    out=model.expectation(values)
    out.backward()
    optimizer.step()
    if (i+1) % 10 == 0:
        print(f"Epoch {i+1} - Loss: {out.item()}")

print("Ground state energy =", out.item(),"a.u")
Epoch 10 - Loss: -1.1329324443124502
Epoch 20 - Loss: -1.405209968460553
Epoch 30 - Loss: -1.765939961559965
Epoch 40 - Loss: -1.8019699954319166
Epoch 50 - Loss: -1.8181175217139227
Epoch 60 - Loss: -1.8264773402663725
Epoch 70 - Loss: -1.8294418234023306
Epoch 80 - Loss: -1.8296881789023665
Epoch 90 - Loss: -1.8303925469664442
Epoch 100 - Loss: -1.8303628675056682
Ground state energy = -1.8303628675056682 a.u

In a.u, the final ground state energy is a bit higher the expected -1.851 a.u (see page 33 of the reference paper mentioned above). Increasing the ansatz depth is enough to reach the desired accuracy.

References


  1. Seeley et al. - The Bravyi-Kitaev transformation for quantum computation of electronic structure