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 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: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.