How to freeze orbitals (with the H₂O molecule as an example)

In this guide, we apply Entanglement Forging to compute the energy of a \(\mathrm{H}_2\mathrm{O}\) molecule. We reduce the number of orbitals in the problem, in turn reducing the number of qubits needed in each circuit, following the logic given in the explanatory material.

Import the relevant modules

First, we import the relevant modules. The imports are similar to the introductory tutorial, but this time, we also import the reduce_bitstrings function from circuit_knitting_toolbox.utils.

[1]:
from matplotlib import pyplot as plt
import numpy as np

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.circuit.library import TwoLocal
from qiskit.algorithms.optimizers import COBYLA
from qiskit_nature.drivers import Molecule
from qiskit_nature.drivers.second_quantization import PySCFDriver
from qiskit_nature.problems.second_quantization import ElectronicStructureProblem
from qiskit_nature.mappers.second_quantization import JordanWignerMapper
from qiskit_nature.converters.second_quantization import QubitConverter
from qiskit_nature.algorithms.ground_state_solvers import (
    GroundStateEigensolver,
    NumPyMinimumEigensolverFactory,
)

from circuit_knitting_toolbox.entanglement_forging import (
    EntanglementForgingAnsatz,
    EntanglementForgingGroundStateSolver,
)
from circuit_knitting_toolbox.utils import reduce_bitstrings

Instantiate the ElectronicStructureProblem

Next, we set up the \(\mathrm{H}_2\mathrm{O}\) molecule, specify the driver and converter, and instantiate an ElectronicStructureProblem.

[2]:
radius_1 = 0.958  # position for the first H atom
radius_2 = 0.958  # position for the second H atom
thetas_in_deg = 104.478  # bond angles.

H1_x = radius_1
H2_x = radius_2 * np.cos(np.pi / 180 * thetas_in_deg)
H2_y = radius_2 * np.sin(np.pi / 180 * thetas_in_deg)

molecule = Molecule(
    geometry=[
        ["O", [0.0, 0.0, 0.0]],
        ["H", [H1_x, 0.0, 0.0]],
        ["H", [H2_x, H2_y, 0.0]],
    ],
    charge=0,
    multiplicity=1,
)
driver = PySCFDriver.from_molecule(molecule=molecule, basis="sto6g")
problem = ElectronicStructureProblem(driver)
converter = QubitConverter(JordanWignerMapper())

Compute the classical result

For comparison, we also use numpy to compute the classical result.

[3]:
solver = GroundStateEigensolver(
    converter, NumPyMinimumEigensolverFactory(use_default_filter_criterion=False)
)

result = solver.solve(problem)
classical_energy = result.total_energies[0]

print("Classical energy = ", classical_energy)
/Users/caleb/opt/anaconda3/envs/ckt/lib/python3.7/site-packages/qiskit_nature/problems/second_quantization/electronic/electronic_structure_problem.py:93: ListAuxOpsDeprecationWarning: List-based `aux_operators` are deprecated as of version 0.3.0 and support for them will be removed no sooner than 3 months after the release. Instead, use dict-based `aux_operators`. You can switch to the dict-based interface immediately, by setting `qiskit_nature.settings.dict_aux_operators` to `True`.
  second_quantized_ops = self._grouped_property_transformed.second_q_ops()
Classical energy =  -75.72890671869246

Prepare the bitstrings and the ansatz

The ansatz for Entanglement Forging consists of a set of input bitstrings and a parameterized circuit. (See the “explanatory material” section of the documentation for additional background on the method.) For this demo, we will use the same bitstrings and ansatz for both the U and V subsystems.

[4]:
theta = Parameter("θ")

hop_gate = QuantumCircuit(2, name="Hop gate")
hop_gate.h(0)
hop_gate.cx(1, 0)
hop_gate.cx(0, 1)
hop_gate.ry(-theta, 0)
hop_gate.ry(-theta, 1)
hop_gate.cx(0, 1)
hop_gate.h(0)

hop_gate.draw()
[4]:
     ┌───┐┌───┐     ┌────────────┐     ┌───┐
q_0: ┤ H ├┤ X ├──■──┤ Ry(-1.0*θ) ├──■──┤ H ├
     └───┘└─┬─┘┌─┴─┐├────────────┤┌─┴─┐└───┘
q_1: ───────■──┤ X ├┤ Ry(-1.0*θ) ├┤ X ├─────
               └───┘└────────────┘└───┘     
[5]:
theta_1, theta_2, theta_3, theta_4 = (
    Parameter("θ1"),
    Parameter("θ2"),
    Parameter("θ3"),
    Parameter("θ4"),
)

circuit_u = QuantumCircuit(5)
circuit_u.append(hop_gate.to_gate({theta: theta_1}), [0, 1])
circuit_u.append(hop_gate.to_gate({theta: theta_2}), [3, 4])
circuit_u.append(hop_gate.to_gate({theta: 0}), [1, 4])
circuit_u.append(hop_gate.to_gate({theta: theta_3}), [0, 2])
circuit_u.append(hop_gate.to_gate({theta: theta_4}), [3, 4])

# Set our bitstrings, and then reduce the chosen orbitals
orbitals_to_reduce = [0, 3]
bitstrings_u = [(1, 1, 1, 1, 1, 0, 0), (1, 0, 1, 1, 1, 0, 1), (1, 0, 1, 1, 1, 1, 0)]
reduced_bitstrings = reduce_bitstrings(bitstrings_u, orbitals_to_reduce)

ansatz = EntanglementForgingAnsatz(circuit_u=circuit_u, bitstrings_u=reduced_bitstrings)

ansatz.circuit_u.draw()
[5]:
     ┌───────────────┐                ┌───────────────┐
q_0: ┤0              ├────────────────┤0              ├
     │  Hop gate(θ1) │┌──────────────┐│               │
q_1: ┤1              ├┤0             ├┤  Hop gate(θ3) ├
     └───────────────┘│              ││               │
q_2: ─────────────────┤              ├┤1              ├
     ┌───────────────┐│  Hop gate(0) │├───────────────┤
q_3: ┤0              ├┤              ├┤0              ├
     │  Hop gate(θ2) ││              ││  Hop gate(θ4) │
q_4: ┤1              ├┤1             ├┤1              ├
     └───────────────┘└──────────────┘└───────────────┘

From here, the problem can be solved following the same steps as in the tutorials.

[10]:
import qiskit.tools.jupyter

%qiskit_version_table

Version Information

Qiskit SoftwareVersion
qiskit-terra0.22.0
qiskit-aer0.11.0
qiskit-ignis0.7.1
qiskit-ibmq-provider0.19.2
qiskit-nature0.4.5
System information
Python version3.9.7
Python compilerGCC 7.5.0
Python builddefault, Sep 16 2021 13:09:58
OSLinux
CPUs6
Memory (Gb)30.943462371826172
Wed Oct 26 13:40:28 2022 EDT

This code is a Qiskit project.

© Copyright IBM 2022.

This code is licensed under the Apache License, Version 2.0. You may obtain a copy of this license in the LICENSE.txt file in the root directory of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this copyright notice, and modified files need to carry a notice indicating that they have been altered from the originals.