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:

{
    "ground_state_problem": {
        "molecule": {
            "atoms": [["H", [0, 0, 0]], ["H", [0, 0, 0.735]]]
        },
        "basis": "sto3g",
        "mapping": "jordan_wigner"
    }
}
from classiq.interface.chemistry.molecule import Molecule
from classiq.interface.chemistry.ground_state_problem import GroundStateProblem

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

hamiltonian = gs_problem.generate_hamiltonian()

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 GroundStateProblem, 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 GroundStateProblem.

Generating the Hamiltonian using the textual interface is done by opening the Command Palette (Ctrl+Shift+P / Command+Shift+P on Windows/Mac, respectively) and choosing the "Generate Hamiltonian for a Molecule" command.

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.

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.

  {
      "ground_state_problem": {
          "molecule": {
              "atoms": [["H", [0, 0, 0]], ["H", [0, 0, 0.735]]]
          },
          "basis": "sto3g",
          "mapping": "jordan_wigner"
      },
    "model": {
      "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"
        }
      ]
    }
  }

Synthesizing the ansatz circuit using the textual interface is done by opening the Command Palette (Ctrl+Shift+P / Command+Shift+P on Windows/Mac, respectively) and choosing the "Generate Ansatz" command.

from classiq import ModelDesigner
from classiq.builtin_functions import HartreeFock, UCC
from classiq.interface.chemistry.molecule import Molecule
from classiq.interface.chemistry.ground_state_problem import GroundStateProblem


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

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()

The output Circuit is: alt text

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.

{
    "ground_state_problem": {
        "molecule": {
            "atoms": [["H", [0, 0, 0]], ["H", [0, 0, 0.735]]]
        },
        "basis": "sto3g",
        "mapping": "jordan_wigner"
    },
    "model": {
        "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"
            }
        ]
    },
    "execution_preferences": {
        "optimizer_preferences": {
            "cost_type": "AVERAGE",
            "max_iteration": 30
        },
        "backend_preferences": {
            "backend_service_provider": "IBMQ",
            "backend_name": "aer_simulator"
        },
        "num_shots": 1000
    }
}

Executing the ground state problem using the textual interface is done by opening the Command Palette (Ctrl+Shift+P / Command+Shift+P on Windows/Mac, respectively) and choosing the "Execute Ground State Problem" command.

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

from classiq.interface.chemistry.molecule import Molecule
from classiq.interface.chemistry.ground_state_problem import GroundStateProblem
from classiq.interface.chemistry.ground_state_solver import GroundStateSolver
from classiq.interface.backend.backend_preferences import IBMBackendPreferences
from classiq.interface.executor.optimizer_preferences import GroundStateOptimizer

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

hamiltonian = gs_problem.generate_hamiltonian()

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="IBMQ", 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

[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)