Skip to content

H₂ Molecule Homework Assignment

View on GitHub

Quantum Software Development Journey: From Theory to Application with Classiq - Part 3

  • Similarly to what we have done in class, in this exercise we will implement the VQE on H2 molecule.

  • This time instead of using the built-in methods and functions (such as Molecule and MoleculeProblem) to difne and solve the problem, you will be provided with a two qubits Hamiltonian.

Submission

  • Submit the completed Jupyter notebook and report via GitHub. Ensure all files are correctly named and organized.

  • Use the Typeform link provided in the submission folder to confirm your submission.

Additional Resources

Important Dates

  • Assignment Release: 22.5.2024

  • Submission Deadline: 3.6.2024 (7 A.M GMT+3)


Happy coding and good luck!

Part 1

Given the following Hamiltonian:

\[\hat{H} = -1.0523 \cdot (I \otimes I) + 0.3979 \cdot (I \otimes Z) - 0.3979 \cdot (Z \otimes I) - 0.0112 \cdot (Z \otimes Z) + 0.1809 \cdot (X \otimes X)\]

Complete the following code

from typing import List

from classiq import *

HAMILTONIAN = QConstant(
    "HAMILTONIAN",
    List[PauliTerm],
    [
        PauliTerm([Pauli.I, Pauli.I], -1.0523),
        PauliTerm([Pauli.I, Pauli.Z], 0.3979),
        PauliTerm([Pauli.Z, Pauli.I], -0.3979),
        PauliTerm([Pauli.Z, Pauli.Z], -0.0112),
        PauliTerm([Pauli.X, Pauli.X], 0.1809),
    ],
)
@qfunc
def main(q: Output[QArray[QBit]], angles: CArray[CReal, 3]) -> None:
    # TODO: Create an ansatz which allows each qubit to have arbitrary rotation
    allocate(2, q)
    U(angles[0], angles[1], angles[2], 0, q[0])
    U(angles[0], angles[1], angles[2], 0, q[1])


@cfunc
def cmain() -> None:
    res = vqe(
        HAMILTONIAN,
        False,
        [],
        optimizer=Optimizer.COBYLA,
        max_iteration=1000,
        tolerance=0.001,
        step_size=0,
        skip_compute_variance=False,
        alpha_cvar=1.0,
    )
    save({"result": res})


qmod = create_model(
    main, classical_execution_function=cmain
)  # TODO: complete the line, use classical_execution_function
qprog = synthesize(qmod)
show(qprog)
Opening: https://platform.classiq.io/circuit/111f8e13-9abd-473e-9d4b-2eef47baaf6e?version=0.41.2
execution = execute(qprog)
res = execution.result()
# execution.open_in_ide()
vqe_result = res[0].value
print(f"Optimal energy: {vqe_result.energy}")
print(f"Optimal parameters: {vqe_result.optimal_parameters}")
print(f"Eigenstate: {vqe_result.eigenstate}")
Optimal energy: -1.0753044921875001
Optimal parameters: {'angles_0': 3.6207585292094877, 'angles_1': 4.926017593441268, 'angles_2': 3.315794817100976}
Eigenstate: {'01': (0.23070036031614688+0j), '00': (0.0625+0j), '10': (0.23280725665236468+0j), '11': (0.9426939455623973+0j)}

11 Optimal parameters: {'angles_0': -3.0292653800801213, 'angles_1': 0.13084292242999915, 'angles_2': -5.614248543217801} Eigenstate: {'10': (0.07328774624724109+0j), '11': (0.07328774624724109+0j), '01': (0.9946144039274718+0j)}

Does it similar to the optimal energy we calculated in class? \ Does it similar to the total energy we calculated in class?

Part 2

Now, we want to have a more interesting ansatz in our main.
Add one line of code to the main function you created in Part 1 that creates entanglement between the two qubits.
Which gate should you use?

@qfunc
def main(q: Output[QArray[QBit]], angles: CArray[CReal, 3]) -> None:
    # TODO: Create an ansatz which allows each qubit to have arbitrary rotation
    allocate(2, q)
    U(angles[0], angles[1], angles[2], 0, q[0])
    U(angles[0], angles[1], angles[2], 0, q[1])
    CX(q[0], q[1])


@cfunc
def cmain() -> None:
    res = vqe(
        HAMILTONIAN,
        False,
        [],
        optimizer=Optimizer.COBYLA,
        max_iteration=1000,
        tolerance=0.001,
        step_size=0,
        skip_compute_variance=False,
        alpha_cvar=1.0,
    )
    save({"result": res})


qmod = create_model(
    main, classical_execution_function=cmain
)  # TODO: complete the line, use classical_execution_function
qprog = synthesize(qmod)
show(qprog)
Opening: https://platform.classiq.io/circuit/8e1369c2-c531-4ecb-9311-2ca674455f30?version=0.41.2
execution = execute(qprog)
res = execution.result()
# execution.open_in_ide()
vqe_result = res[0].value  # TODO: complete the line
print(f"Optimal energy: {vqe_result.energy}")
print(f"Optimal parameters: {vqe_result.optimal_parameters}")
print(f"Eigenstate: {vqe_result.eigenstate}")
Optimal energy: -1.83920859375
Optimal parameters: {'angles_0': -2.984295902257786, 'angles_1': -6.986408481569447, 'angles_2': -1.3331417701993054}
Eigenstate: {'11': (0.06987712429686843+0j), '10': (0.07967217989988726+0j), '01': (0.9943689110435825+0j)}

Does it similar to the optimal energy we calculated in class? \ Does it similar to the total energy we calculated in class? \ What can we learn about the provided form this result Hamiltonian?