Running on Quantinuum H-Series

In this tutorial, we demonstrate how to perform a simple quantum chemical calculation on Quantinuum H-Series.

Since this tutorial focuses on practical quantum computation, we will perform a simple calculation: the single point (i.e. not optimized/not variationally solved) total energy evaluation of the H2 molecule in the Unitary Coupled Cluster (UCC) ansatz for a set value of the ansatz variational parameter.

This tutorial will require that you have access to Quantinuum backends, which can be obtained through your InQuanto administrator or contacting Quantinuum support. You will need credentials to run on a machine for a short time. For more information on Quantinuum H-Series and instructions on granting access (for administrators), see Quantinuum H-Series page.

The steps below are such:

  • Define the system

  • Perform computation with emulated hardware noise (QuantinuumBackend emulator + machine noise profile)

  • Demonstrate error mitigation methods on emulated hardware (PMSV)

1. Define the system

[ ]:
# Preload the Hamiltonian for H2
# see the Aer tutorial for details
from inquanto.express import load_h5

h2 = load_h5("h2_sto3g.h5", as_tuple=True)
hamiltonian = h2.hamiltonian_operator

from inquanto.spaces import FermionSpace
from inquanto.states import FermionState
from inquanto.symmetry import PointGroup
from inquanto.ansatzes import FermionSpaceStateExpChemicallyAware

# Define fermion space, state, and map the fermionic operator to qubits
space = FermionSpace(
    4, point_group=PointGroup("D2h"), orb_irreps=["Ag", "Ag", "B1u", "B1u"]
)

state = FermionState([1, 1, 0, 0])
qubit_hamiltonian = hamiltonian.qubit_encode()

exponents = space.construct_single_ucc_operators(state)
## the above adds nothing due to the symmetry of the system
exponents += space.construct_double_ucc_operators(state)
# Construct an efficient ansatz
ansatz = FermionSpaceStateExpChemicallyAware(exponents, state)

p = ansatz.state_symbols.construct_from_array([0.4996755931358105])
print(p.df())

# Import an InQuanto Computable for measuring an expecation value.
# The operator is the qubit Hamiltonian, and the wavefunction is the ansatz.
from inquanto.computables import ExpectationValue

print(hamiltonian)
expectation0 = ExpectationValue(ansatz, hamiltonian.qubit_encode())

# Analyze the ansatz circuit
from pytket.circuit.display import render_circuit_jupyter
from pytket import Circuit, OpType

render_circuit_jupyter(ansatz.get_circuit(p))  # this is the uncompiled ansatz circuit

print("\nCNOT GATES:  {}".format(ansatz.state_circuit.n_gates_of_type(OpType.CX)))
print(ansatz.state_circuit)
   ordering symbol     value
0         0     d0  0.499676
<inquanto.operators._chemistry_integral_operator.ChemistryRestrictedIntegralOperator object at 0x7fde25afa010>

CNOT GATES:  4
<tket::Circuit, qubits=4, gates=31>

2. Machine emulation for quantum noise

Running emulator experiments before hardware experiments is a crucial step in the development and optimization of quantum algorithms and applications. Emulators provide a controlled environment where one can fine-tune algorithms, explore error mitigation strategies, and gain valuable insights about the behavior of quantum circuits without some constraints of physical hardware.

Below, we provide instructions for conducting experiments utilizing a Quantinuum H-series emulator. To utilize hardware instead of the emulator one only needs to change their choice of device when instantiating the QuantinuumBackend.

Note that we use the pytket-quantinuum extension to access a Quantinuum backend.

QuantinuumBackend is a pytket backend that calls an H-series device (“H1-1”, “H1-2”) or its emulator with the corresponding noise profile (“H1-1E”, “H2-1E”). The emulators are run remotely on a server. Accessing the backend retrieves information from your Quantinuum account. More information can be found on the pytket-quantinuum page.

For comparison in the figure below, we have also plotted the exact energy (-0.5876463677224993 Ha) for H\(_2\) and the user should also compare these results to result from the first part of this tutorial.

[ ]:
from pytket.extensions.quantinuum import QuantinuumBackend

# Initialize the backend, make sure to login with your credentials.
# Change the machine name and add the label and the group if necessary.
machine = "H1-1E"
backend = QuantinuumBackend(device_name=machine, group="")

# The QuantinuumBackend has additional arguements for accessing resources and labelling circuits
# label (Optional[str], optional) – Job labels used if Circuits have no name, defaults to “job”
# group (Optional[str], optional) – string identifier of a collection of jobs, can be used for usage tracking.

# Running the next line (device_state) will require logging into your
# quantinuum account. This can also be called with backend.login()
print(machine, "status:", QuantinuumBackend.device_state(device_name=machine))
H1-1E status: online
[ ]:
from inquanto.protocols import PauliAveraging
from pytket.partition import PauliPartitionStrat

# here we demonstrate building the
protocol = PauliAveraging(
    backend,
    shots_per_circuit=10,
    pauli_partition_strategy=PauliPartitionStrat.CommutingSets,
)
protocol.build(p, ansatz, hamiltonian.qubit_encode())

# you can inspect compiled measurement circuits contained in the protocol that was run on the backend
# note that the gateset of this circuit is different to the ansatz

render_circuit_jupyter(protocol.get_circuits()[1])

[ ]:
# now we loop over different numbers of shots to examine convergence
# the protocols are built, run, and the expectation values collected
set_shots = [10, 50, 100, 500, 1000, 5000, 10000]
noisy_H1_1E_energies = []

for i in set_shots:
    protocol = PauliAveraging(
        backend,
        shots_per_circuit=i,
        pauli_partition_strategy=PauliPartitionStrat.CommutingSets,
    )
    protocol.build(p, ansatz, hamiltonian.qubit_encode())
    protocol.run()  # no seeding H series, returns results to the protocol
    noisy_H1_1E_expectation = protocol.evaluate_expectation_value(
        ansatz, hamiltonian.qubit_encode()
    )
    noisy_H1_1E_energies.append(noisy_H1_1E_expectation)

The Quantinuum Portal can be used to inspect your submitted job progress and other details as illustrated below.

Status

JobID

Name

MACHINE

Group

Submit Date

Start Date

Result Date

Shots

Cost

queued

3049eb4cf05746ffb271c8bf11cd6e91

GQ4WEZBUGM3DI,1

H1-1E

UserGroup

29/11/2023 11:42

10000

251

queued

474e5da741a34c5b9ad0b577db0141a0

GQ4WEZBUGM3DI,0

H1-1E

UserGroup

29/11/2023 11:42

10000

185

completed

7dd6b36ea21a43c8bb8dd5067244a11f

GQ4WEZBUGM3DI,1

H1-1E

UserGroup

29/11/2023 11:40

29/11/2023 11:42

29/11/2023 11:42

5000

5000

128

completed

df15c1a8e2f442a694463c59538d466f

GQ4WEZBUGM3DI,0

H1-1E

UserGroup

29/11/2023 11:40

29/11/2023 11:42

29/11/2023 11:42

5000

5000

95

[ ]:
import matplotlib.pyplot as plt
# we plot the results of our sampling
plt.rcParams["figure.figsize"] = (12, 4)

plt.hlines(
    y=-0.5876463677224993,
    xmin=10,
    xmax=50000,
    ls="--",
    colors="black",
    label="Exact Energy",
)
plt.plot(set_shots, noisy_H1_1E_energies, label="H1-1E")
plt.xscale("log")
plt.ylim([-0.8, 0])
plt.xlabel("Number of shots")
plt.ylabel("Energy (Ha)")
plt.title(
    "Convergence behavior of the expectation value for "
    + r"$\theta=$"
    + str(p.df().iloc[0, 2])[0:7]
)
plt.legend()
<matplotlib.legend.Legend at 0x7f605017bc70>
../_images/tutorials_InQ_htut_Hser_H2_9_1.png

3. Noise mitigation methods in Quantinuum emulation

We can use noise mitigation techniques to reduce the impact of noise in our expectation value. In this case we will ‘purify’ results by discarding a shot if it has a certain error. There are many other mitigation methods.

Specifically, we will define the symmetries of the Qubit Operators in the system to use PMSV (Partition Measurement Symmetry Verification). As a result, noise mitigation improves the accuracy of the energy obtained from the quantum hardware compared to the unmitigated results and the exact energy of H2.

State Preparation and Measurement (SPAM) correction, which calibrates the calculation for system noise, can also be used.

[ ]:
from inquanto.protocols.averaging._mitigation import PMSV
from inquanto.mappings import QubitMappingJordanWigner

backend = QuantinuumBackend(device_name="", label="", group="")

stabilizers = QubitMappingJordanWigner().operator_map(
    space.symmetry_operators_z2_in_sector(state)
)

mitms_pmsv = PMSV(stabilizers)

miti_H1_1E_energies = []
for i in set_shots:
    protocol = PauliAveraging(
        backend,
        shots_per_circuit=i,
        pauli_partition_strategy=PauliPartitionStrat.CommutingSets,
    )
    protocol.build(
        p, ansatz, hamiltonian.qubit_encode(), noise_mitigation=mitms_pmsv
    ).run()
    miti_H1_1E_expectation = protocol.evaluate_expectation_value(
        ansatz, hamiltonian.qubit_encode()
    )
    miti_H1_1E_energies.append(miti_H1_1E_expectation)
[ ]:
#statevector
plt.hlines(
    y=-0.5876463677224993,
    xmin=10,
    xmax=50000,
    ls="--",
    colors="black",
    label="Exact Energy",
)
plt.plot(set_shots, noisy_H1_1E_energies, label="H1-1E")
plt.plot(set_shots, miti_H1_1E_energies, label="H1-1E mitigated")
plt.xscale("log")
plt.ylim([-0.8, 0])
plt.xlabel("Number of shots")
plt.ylabel("Energy (Ha)")
plt.title(
    "Convergence behavior of the expectation value for "
    + r"$\theta=$"
    + str(p.df().iloc[0, 2])[0:7]
)
plt.legend()
<matplotlib.legend.Legend at 0x7f6049d55ae0>
../_images/tutorials_InQ_htut_Hser_H2_12_1.png

One may also explore the impact of SPAM on enhancing the convergence behavior of the expectation value. In the case of this simple system with a shallow circuit, the energy converges with slightly improved efficiency and/or heightened precision upon employing noise mitigation techniques.

Transitioning to hardware experiments is straightforward. This is facilitated by the pytket.extensions.quantinuum module. The user simply changes the device name (e.g., from “H1-1E” to “H1-1”), and the circuit will be run on the physical quantum device provided sufficient credits and circuit syntax.