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 Software | Version |
---|---|
qiskit-terra | 0.22.0 |
qiskit-aer | 0.11.0 |
qiskit-ignis | 0.7.1 |
qiskit-ibmq-provider | 0.19.2 |
qiskit-nature | 0.4.5 |
System information | |
Python version | 3.9.7 |
Python compiler | GCC 7.5.0 |
Python build | default, Sep 16 2021 13:09:58 |
OS | Linux |
CPUs | 6 |
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.