How to build Qiskit circuits for the LUCJ ansatz¶
This guide provides some examples of building and transpiling Qiskit circuits to implement the LUCJ ansatz.
[1]:
import pyscf
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
import ffsim
LUCJ circuit for a closed-shell molecule¶
For a closed-shell system, use the spin-balanced LUCJ ansatz. This example creates an LUCJ circuit for a nitrogen molecule in the 6-31g basis.
[2]:
# Build N2 molecule
mol = pyscf.gto.Mole()
mol.build(
atom=[["N", (0, 0, 0)], ["N", (1.0, 0, 0)]],
basis="6-31g",
symmetry="Dooh",
)
# Define active space
n_frozen = 2
active_space = range(n_frozen, mol.nao_nr())
# Get molecular data and Hamiltonian
scf = pyscf.scf.RHF(mol).run()
mol_data = ffsim.MolecularData.from_scf(scf, active_space=active_space)
norb, nelec = mol_data.norb, mol_data.nelec
mol_hamiltonian = mol_data.hamiltonian
print(f"norb = {norb}")
print(f"nelec = {nelec}")
# Get CCSD t2 amplitudes for initializing the ansatz
ccsd = pyscf.cc.CCSD(
scf, frozen=[i for i in range(mol.nao_nr()) if i not in active_space]
).run()
# Use 2 ansatz layers
n_reps = 2
# Use interactions implementable on a square lattice
pairs_aa = [(p, p + 1) for p in range(norb - 1)]
pairs_ab = [(p, p) for p in range(norb)]
ucj_op = ffsim.UCJOpSpinBalanced.from_t_amplitudes(
ccsd.t2, t1=ccsd.t1, n_reps=n_reps, interaction_pairs=(pairs_aa, pairs_ab)
)
# Construct circuit
qubits = QuantumRegister(2 * norb)
circuit = QuantumCircuit(qubits)
circuit.append(ffsim.qiskit.PrepareHartreeFockJW(norb, nelec), qubits)
circuit.append(ffsim.qiskit.UCJOpSpinBalancedJW(ucj_op), qubits)
circuit.measure_all()
# Create a generic backend and transpile the circuit to it
backend = GenericBackendV2(2 * norb, basis_gates=["cp", "xx_plus_yy", "p", "x"])
pass_manager = generate_preset_pass_manager(optimization_level=3, backend=backend)
# Set the pre-initialization stage of the pass manager with passes suggested by ffsim
pass_manager.pre_init = ffsim.qiskit.PRE_INIT
transpiled = pass_manager.run(circuit)
transpiled.count_ops()
converged SCF energy = -108.835236570775
norb = 16
nelec = (5, 5)
E(CCSD) = -109.0398256929734 E_corr = -0.2045891221988307
[2]:
OrderedDict([('xx_plus_yy', 582),
('p', 64),
('cp', 52),
('measure', 32),
('x', 10),
('barrier', 1)])
LUCJ circuit for an open-shell molecule¶
For an open-shell system, use the spin-unbalanced LUCJ ansatz. This example creates an LUCJ circuit for a hydroxyl radical in the 6-31g basis.
[3]:
# Build HO molecule
mol = pyscf.gto.Mole()
mol.build(
atom=[["H", (0, 0, 0)], ["O", (0, 0, 1.1)]],
basis="6-31g",
spin=1,
symmetry="Coov",
)
# Get molecular data and Hamiltonian
scf = pyscf.scf.ROHF(mol).run()
mol_data = ffsim.MolecularData.from_scf(scf)
norb, nelec = mol_data.norb, mol_data.nelec
mol_hamiltonian = mol_data.hamiltonian
print(f"norb = {norb}")
print(f"nelec = {nelec}")
# Get CCSD t2 amplitudes for initializing the ansatz
ccsd = pyscf.cc.CCSD(scf).run()
# Use 4 layers from opposite-spin amplitudes and 2 layers from same-spin amplitudes
n_reps = (4, 2)
# Use interactions implementable on a square lattice
pairs_aa = [(p, p + 1) for p in range(norb - 1)]
pairs_ab = [(p, p) for p in range(norb)]
pairs_bb = [(p, p + 1) for p in range(norb - 1)]
ucj_op = ffsim.UCJOpSpinUnbalanced.from_t_amplitudes(
ccsd.t2, t1=ccsd.t1, n_reps=n_reps, interaction_pairs=(pairs_aa, pairs_ab, pairs_bb)
)
# Construct circuit
qubits = QuantumRegister(2 * norb)
circuit = QuantumCircuit(qubits)
circuit.append(ffsim.qiskit.PrepareHartreeFockJW(norb, nelec), qubits)
circuit.append(ffsim.qiskit.UCJOpSpinUnbalancedJW(ucj_op), qubits)
circuit.measure_all()
# Create a generic backend and transpile the circuit to it
backend = GenericBackendV2(2 * norb, basis_gates=["cp", "xx_plus_yy", "p", "x"])
pass_manager = generate_preset_pass_manager(optimization_level=3, backend=backend)
# Set the pre-initialization stage of the pass manager with passes suggested by ffsim
pass_manager.pre_init = ffsim.qiskit.PRE_INIT
transpiled = pass_manager.run(circuit)
transpiled.count_ops()
SCF not converged.
SCF energy = -75.3484557079827
norb = 11
nelec = (5, 4)
WARN: RCCSD method does not support ROHF method. ROHF object is converted to UHF object and UCCSD method is called.
E(UCCSD) = -75.45619739109992 E_corr = -0.1077416831172149
[3]:
OrderedDict([('xx_plus_yy', 696),
('p', 112),
('cp', 108),
('measure', 22),
('x', 9),
('barrier', 1)])