Skip to content

Chemistry

Classiq chemistry package allows the user to study quantum chemistry problems by defining a desired molecule and using quantum algorithms to solve corresponding problems.

Introduction and Background

Calculating the energies of a molecular Hamiltonian, especially the ground state, allows us to understand the chemical properties of the molecule. Since the molecular Hamiltonian is inherently quantum, quantum computing can be used to calculate such properties efficiently by leveraging the same quantum effects in appropriate quantum algorithms. This is done in four steps:

  1. Defining the molecule
  2. Calculating the molecular Hamiltonian for the desired molecule.
  3. Generating an appropriate variational quantum circuit (ansatz) to calculate the Hamiltonian's ground state.
  4. Execute the desired quantum circuit as part of a hybrid quantum-classical algorithm.

The process is described visually in the following diagram.

flowchart LR
  classDef default fill:#FFFFFF,stroke:#000000,stroke-width:1px;

  A(Molecule Definition) --> B(Generate Hamiltonian);
  B --> C(Ansatz Definition);
  C --> D(Optimization);

Generating a Molecular Hamiltonian

Given a molecule, we would like to know the full Hamiltonian describing the interactions between all electrons and nuclei. The following is an example of calculating the Hamiltonian of the Hydrogen molecule:

ensure you are in the chemistry application suite in the synthesis page chemistry Suite

enter the following Model in the ground state problem Editor:

{
  "molecule": {
      "atoms": [["H", [0, 0, 0]], ["H", [0, 0, 0.735]]]
  },
  "basis": "sto3g",
  "mapping": "jordan_wigner"
}
click the Generate Hamiltonian Button, you should now see the Hamiltonian in a compact format such as shown below:

Generated_Hamiltonian

{
  "molecule": {
      "atoms": [["H", [0, 0, 0]], ["H", [0, 0, 0.735]]]
  },
  "basis": "sto3g",
  "mapping": "jordan_wigner"
  "num_qubits": 4
}
from classiq.applications.chemistry import Molecule, MoleculeProblem

molecule = Molecule(
    atoms=[
        ("H", (0.0, 0.0, 0.0)),
        ("H", (0.0, 0.0, 0.735)),
    ],
)
gs_problem = MoleculeProblem(
    molecule=molecule,
    basis="sto3g",
    mapping="jordan_wigner",
)

hamiltonian = gs_problem.generate_hamiltonian()
gs_problem = gs_problem.update_problem(hamiltonian.num_qubits)

In the example above, a Hydrogen molecule is defined by specifying its two Hydrogen atoms and their position in 3D space. The molecule is then used to create a MoleculeProblem, together with the following optional parameters:

  • basis (str): the basis set to be used. For supported types see PySCF
  • mapping (str): the mapping between the fermionic Hamiltonian and an qubits Hamiltonian. Supported types:
    • "jordan_wigner"
    • "parity"
    • "bravyi_kitaev"
    • "fast_bravyi_kitaev"
  • freeze_core (bool): remove the "core" orbitals of the atoms defining the molecule.
  • two_qubit_reduction (bool): perform two qubit reduction if possible.
  • z2_symmetries (bool): preform Z2 symmetries reduction [1]

Finally, the Hamiltonian is generated from the MoleculeProblem.

Generating a Variational Quantum Circuit

The ground state of a Hamiltonian is found by using an appropriate variational quantum circuit (usually called an Ansatz), which is combined with a classical optimizer to find the minimal expectation value for the Hamiltonian. For a list of build-in ansatze see the Ansatze section.

Notice that the chemical ansatze functions (ucc, hva) need to know in advance the num_qubits attribute of the chemical problem. Therefore, the user can provide directly the num_qubits to the MoleculeProblem or HamiltonianProblem class, use the generate_hamiltonian method or the update_problem method.

Example - Unitary Coupled Cluster Ansatz

In this example we show the generation of the commonly used chemistry-inspired UCC ansatz, which is a unitary version of the classical coupled cluster (CC) method [2] . First the circuit is initialized to the Hartree-Fock state, then the UCC function is applied. More details on the UCC ansatz can be found in UCC.

after generating a Hamiltonian like described above, you should add the circuit in the Model Editor

{
  "logic_flow": [
      {
        "function": "HartreeFock",
        "function_params": {
          "gs_problem": "ground_state_problem"
        },
        "outputs": "hf_out"
      },
      {
        "function": "UCC",
        "function_params": {
          "gs_problem": "ground_state_problem",
          "max_depth": 100
        },
        "inputs": "hf_out"
      }
    ]
}

Now you can click on either the Synthesis or Solve button: Clicking the Synthesis Button will lead you to the Visualization Tab, and Clicking on Solve will lead you straight to the Execution Tab to solve the Ground State Problem.

{
  "logic_flow": [
      {
        "function": "HartreeFock",
        "function_params": {
          "gs_problem": "ground_state_problem"
        },
        "outputs": "hf_out"
      },
      {
        "function": "UCC",
        "function_params": {
          "gs_problem": "ground_state_problem",
          "max_depth": 100
        },
        "inputs": "hf_out"
      }
    ]
}
from classiq import ModelDesigner
from classiq.builtin_functions import HartreeFock, UCC
from classiq.applications.chemistry import Molecule, MoleculeProblem

molecule = Molecule(
    atoms=[("H", (0.0, 0.0, 0.0)), ("H", (0.0, 0.0, 0.735))],
)
gs_problem = MoleculeProblem(
    molecule=molecule,
    mapping="jordan_wigner",
)
gs_problem = gs_problem.update_problem()

model_designer = ModelDesigner()

hf_params = HartreeFock(gs_problem=gs_problem)
output_dict = model_designer.HartreeFock(hf_params)
hf_output = output_dict["OUT"]

ucc_params = UCC(gs_problem=gs_problem, max_depth=100)

model_designer.UCC(ucc_params, in_wires={"IN": hf_output})
generation_result = model_designer.synthesize()

generation_result.show_interactive()

The output Circuit is: Ucc Circuit

Executing a Ground State Problem

Once we've specified a molecular Hamiltonian, and a desired Ansatz to be used in our algorithm as well, we can combine everything and execute the VQE algorithm to find the Hamiltonian's ground state.

First, ensure you are in the Circuit's Execution tab. If you have clicked Solve after Modeling the Circuit, you should already be in the Execution tab. Otherwise, go to the execution tab in the IDE.

Now, you should check the box Execute for Hamiltonian. (If the checkbox is missing, please refer to the troubleshooting section below)

Checkbox

Then you should specify the Optimizer Prefences,

Prefences

these are the prefences for the Classical Optimizer in VQE.

Finally, you can click on the Run button, like you execute every other circuit throughout the platform.

from classiq import ModelDesigner
from classiq.builtin_functions import UCC, HartreeFock

from classiq.applications.chemistry import (
    Molecule,
    MoleculeProblem,
    GroundStateSolver,
    GroundStateOptimizer,
)
from classiq.execution import IBMBackendPreferences

molecule = Molecule(
    atoms=[
        ("H", (0.0, 0.0, 0.0)),
        ("H", (0.0, 0.0, 0.735)),
    ],
)
gs_problem = MoleculeProblem(
    molecule=molecule,
    mapping="jordan_wigner",
)

hamiltonian = gs_problem.generate_hamiltonian()
gs_problem = gs_problem.update_problem(hamiltonian.num_qubits)

model_designer = ModelDesigner()

hf_params = HartreeFock(gs_problem=gs_problem)
output_dict = model_designer.HartreeFock(hf_params)
hf_output = output_dict["OUT"]

ucc_params = UCC(gs_problem=gs_problem, max_depth=100)

model_designer.UCC(ucc_params, in_wires={"IN": hf_output})
circuit = model_designer.synthesize()

optimizer_preferences = GroundStateOptimizer(
    max_iteration=30,
    num_shots=1000,
)
backend_preferences = IBMBackendPreferences(
    backend_service_provider="IBM Quantum", backend_name="aer_simulator"
)

gs_solver = GroundStateSolver(
    ground_state_problem=gs_problem,
    ansatz=circuit,
    optimizer_preferences=optimizer_preferences,
    backend_preferences=backend_preferences,
)

result = gs_solver.solve()
result.show_convergence_graph()
exact_result = gs_solver.solve_exact()

The resulting convergence graph is alt text

The VQE result can be compared to the exact result (for small molecules) obtained by the solve_exact method.

The solve method of the GroundStateSolver class returns a GroundStateResult object. For more details on the result object see GroundStateResult

IDE Troubleshooting

  • Missing "Execute for Hamiltonian" checkbox:
    • The checkbox only appears when there is a Hamiltonian available for the circuit; it might be missing if:
      • You clicked on the Synthesize button before the Generate Hamiltonian button.
      • You used the SDK to generate the circuit, but the IDE to execute it, we currently do not support Hamiltonians generated in the SDK

References

[1] Bravyi, Sergey, Jay M. Gambetta, Antonio Mezzacapo, and Kristan Temme. Tapering off qubits to simulate fermionic Hamiltonians. arXiv preprint arXiv:1701.08213 (2017).

[2] Panagiotis Kl. Barkoutsos, Jerome F. Gonthier, Igor Sokolov, Nikolaj Moll, Gian Salis, Andreas Fuhrer, Marc Ganzhorn, Daniel J. Egger, Matthias Troyer, Antonio Mezzacapo, Stefan Filipp, and Ivano Tavernelli Quantum algorithms for electronic structure calculations: Particle-hole Hamiltonian and optimized wave-function expansions Phys. Rev. A 98, 022322 (2018)