How to use asymmetric bitstrings (with the CH₃ molecule as an example)¶
In this guide, we apply Entanglement Forging to compute the ground state energy of a \(\mathrm{CH}_3\) molecule. We use separate bitstrings lists for each subsystem, U and V.
Import the relevant modules¶
[1]:
from matplotlib import pyplot as plt
import numpy as np
from qiskit.circuit import QuantumCircuit, Parameter
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.properties.second_quantization.electronic.bases import (
ElectronicBasis,
)
from qiskit_nature.transformers.second_quantization.electronic.active_space_transformer import (
ActiveSpaceTransformer,
)
from circuit_knitting_toolbox.entanglement_forging import (
EntanglementForgingAnsatz,
EntanglementForgingGroundStateSolver,
)
from circuit_knitting_toolbox.utils import IntegralDriver
Define the \(\mathrm{CH}_3\) molecule, define the active space transform, and instantiate an ElectronicStructureProblem
¶
[2]:
# Define a molecular system of interest - Methyl radical
molecule = Molecule(
geometry=[
["C", [0.0, 0.0, 0.00]],
["H", [1.0790, 0.0, 0.00]],
["H", [-0.5395, -0.9344, 0.00]],
["H", [-0.5395, 0.9344, 0.00]],
],
charge=0,
multiplicity=2,
)
driver = PySCFDriver.from_molecule(molecule=molecule, basis="sto-3g")
converter = QubitConverter(JordanWignerMapper())
# Construct an active space composed of 6 molecular orbitals
transformer = ActiveSpaceTransformer(num_electrons=(3, 2), num_molecular_orbitals=6)
problem_reduced = ElectronicStructureProblem(driver, [transformer])
Retrieve the one and two-body integrals and the nuclear repulsion energy. These will be used to decompose the operator into a bipartite system.¶
[3]:
H_fermionic = problem_reduced.second_q_ops()
electronic_energy_object = problem_reduced.grouped_property_transformed.get_property(
"ElectronicEnergy"
)
nuclear_repulsion_energy = electronic_energy_object.nuclear_repulsion_energy
# These are the integrals in the molecular orbital basis retrieved from the 6 orbital active space.
one_body_integrals_alpha = electronic_energy_object.get_electronic_integral(
ElectronicBasis.MO, 1
)._matrices[0]
one_body_integrals_beta = electronic_energy_object.get_electronic_integral(
ElectronicBasis.MO, 1
)._matrices[1]
two_body_integrals_alpha_alpha = electronic_energy_object.get_electronic_integral(
ElectronicBasis.MO, 2
)._matrices[0]
two_body_integrals_beta_alpha = electronic_energy_object.get_electronic_integral(
ElectronicBasis.MO, 2
)._matrices[1]
two_body_integrals_beta_beta = electronic_energy_object.get_electronic_integral(
ElectronicBasis.MO, 2
)._matrices[2]
two_body_integrals_alpha_beta = electronic_energy_object.get_electronic_integral(
ElectronicBasis.MO, 2
)._matrices[3]
/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()
Use the IntegralDriver
and ElectronicStructureProblem
objects to specify the entanglement-forged operator¶
[4]:
# Create an ElectronicStructureProblem from our IntegralDriver and performing second quantization transformation
driver = IntegralDriver(
hcore=one_body_integrals_alpha,
mo_coeff=np.eye(6, 6),
eri=two_body_integrals_alpha_alpha,
num_alpha=3,
num_beta=2,
nuclear_repulsion_energy=nuclear_repulsion_energy,
)
problem = ElectronicStructureProblem(driver)
Prepare the bitstrings and the ansatz.¶
The ansatz for Entanglement Forging consists of a set of input bitstrings and a parameterized ansatz. If only one set of bitstrings is passed, it will be used for both subsystems. For this demo, we will specify different bitstrings for each subsystem.
[5]:
bitstrings_u = [
(1, 1, 1, 0, 0, 0),
(0, 1, 1, 0, 0, 1),
(1, 0, 1, 0, 1, 0),
(1, 0, 1, 1, 0, 0),
(0, 1, 1, 1, 0, 0),
]
bitstrings_v = [
(1, 1, 0, 0, 0, 0),
(0, 1, 0, 0, 0, 1),
(1, 0, 0, 0, 1, 0),
(1, 0, 0, 1, 0, 0),
(0, 1, 0, 1, 0, 0),
]
# Define ansatz parameters:
brickwall = [
(4, 5),
(3, 4),
(2, 3),
(4, 5),
(1, 2),
(3, 4),
(4, 5),
(2, 3),
(0, 1),
(1, 2),
(3, 4),
(2, 3),
(4, 5),
(3, 4),
(4, 5),
]
n_theta = len(brickwall)
nqubit = len(bitstrings_u[0])
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)
theta_vec = [Parameter("θ%d" % i) for i in range(n_theta)]
# Create the parametrized circuit (circuit_u). The same circuit will be used for both subsystems, U and V
circuit_u = QuantumCircuit(nqubit)
for m, (i, j) in enumerate(brickwall):
circuit_u.append(hop_gate.to_gate({theta: theta_vec[m]}), [i, j])
ansatz = EntanglementForgingAnsatz(
circuit_u=circuit_u, bitstrings_u=bitstrings_u, bitstrings_v=bitstrings_v
)
ansatz.circuit_u.draw("text", justify="right", fold=-1)
[5]:
┌───────────────┐ q_0: ────────────────────────────────────────────────────────────────────┤0 ├──────────────────────────────────────────────────────────────────────── ┌───────────────┐│ Hop gate(θ8) │┌───────────────┐ q_1: ───────────────────────────────────────────────────┤0 ├┤1 ├┤0 ├─────────────────────────────────────────────────────── ┌───────────────┐│ Hop gate(θ4) │├───────────────┤│ Hop gate(θ9) │ ┌────────────────┐ q_2: ──────────────────────────────────┤0 ├┤1 ├┤0 ├┤1 ├─┤0 ├──────────────────────────────────── ┌───────────────┐│ Hop gate(θ2) │├───────────────┤│ Hop gate(θ7) │├───────────────┴┐│ Hop gate(θ11) │┌────────────────┐ q_3: ─────────────────┤0 ├┤1 ├┤0 ├┤1 ├┤0 ├┤1 ├┤0 ├────────────────── ┌───────────────┐│ Hop gate(θ1) │├───────────────┤│ Hop gate(θ5) │├───────────────┤│ Hop gate(θ10) │├────────────────┤│ Hop gate(θ13) │┌────────────────┐ q_4: ┤0 ├┤1 ├┤0 ├┤1 ├┤0 ├┤1 ├┤0 ├┤1 ├┤0 ├ │ Hop gate(θ0) │└───────────────┘│ Hop gate(θ3) │└───────────────┘│ Hop gate(θ6) │└────────────────┘│ Hop gate(θ12) │└────────────────┘│ Hop gate(θ14) │ q_5: ┤1 ├─────────────────┤1 ├─────────────────┤1 ├──────────────────┤1 ├──────────────────┤1 ├ └───────────────┘ └───────────────┘ └───────────────┘ └────────────────┘ └────────────────┘
From here, the problem can be solved following the same steps as in the tutorials.
[11]:
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:39:30 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.