# If Tangelo is not found in your current environment, this cell installs all dependencies required for this hands-on
try:
import tangelo
except ModuleNotFoundError:
!pip install git+https://github.com/goodchemistryco/Tangelo.git@develop --quiet
!pip install pyscf
!pip install qulacs qiskit qiskit-aer
Tangelo hands-on: VQE framework
Before you jump in
This hands-on notebook complements existing tutorials, documentation and the developer notes available in the Tangelo GitHub repositories, which present content in much more depth.
You will come across code cells that require you to change code or fill in the blanks in order to achieve a desired outcome. There may be many ways to solve these simple exercises, and you are encouraged to explore.
Getting started
Please have a look at the landing page of this repository for guidance about how to deploy these notebooks and get started easily.
In order to complete this hands-on tutorial, we recommend you use the latest version of Tangelo. If you encounter errors related to missing Python packages (classical chemistry backend, quantum circuit simulator…), you can install them on-the-fly by typing !pip install <package-name>
in a new code cell, and then restart the Jupyter notebook kernel.
For this hands-on, we recommend you consider the following resources: - the VQE tutorial notebook - Optional: Other tutorials tagged with VQA. VQE can be used to compute excited states, or take as input a user-defined ansatz or Hamtiltonian. Moreover, there are quite a few variants of VQE available in Tangelo (ADAPT, etc). - Optional: Introduction notebook on the basics of modelling a chemical system (Second Quantized molecule, Hartree-Fock, FCI, CCSD, active space selection …)
Hands-on
The use cases for this hands-on will be small molecular systems. In-depth understanding of the chemistry behind second quantization, Hartree-Fock, active space selection and classical solvers is nice to have, but not necessary to go throuygh these hands-on. Feel free to have a look at the list of resources recommended in the first section of this hands-on if you would like to know more about these topics.
We’ll start with the iconic second quantized \(H_2\) molecule in sto-3g
basis as an initial use case for this hands-on. We’ll quickly ramp up in higher basis sets and also play with \(H_2O\).
The cell below defines the molecule explicitly, specifying a geometry, charge, spin and basis set. Under the hood, a classical chemistry library (pyscf, psi4…) is used to create this object and compute its mean-field, which will enable more advanced calculations.
# Define H2 molecule in minimal basis set
from tangelo import SecondQuantizedMolecule
= [("H", (0., 0., 0.)), ("H", (0., 0., 0.7414))]
xyz_H2 = SecondQuantizedMolecule(xyz_H2, q=0, spin=0, basis="sto-3g")
mol_H2_sto3g
= mol_H2_sto3g mol
Before we delve into quantum algorithms implementing variational approaches, let’s use classical solvers such as FCI and CCSD to compute references results for the ground state energy of our molecule. These classical solvers can be used to benchmark the accuracy of our variational algorithms later in this hands-on. The results are computed in Hartrees, and in practice an accuracy of 3 digits of precision (e.g about 1 mHa
) is considered satisfying.
from tangelo.algorithms import FCISolver, CCSDSolver
# The FCI solver is the reference for chemists, but computational cost scales badly.
= FCISolver(mol)
fci_solver = fci_solver.simulate()
fci_energy print(f'FCI energy for H2 ={fci_energy}')
# The CCSD solver has better scaling, but is less accurate.
= CCSDSolver(mol)
ccsd_solver = ccsd_solver.simulate()
ccsd_energy print(f'CCSD energy for H2 ={ccsd_energy}')
FCI energy for H2 =-1.1372701746609042
CCSD energy for H2 =-1.1372701746673048
Level 1: VQE basics
The variational solvers in Tangelo currently work in 3 steps: - creation of solver object, which takes options (molecule, ansatz, backend, etc) as input - construction of the underlying objects (quantum circuits, initial parameters, …) - simulation of the algorithm
Q: Can you use the
VQESolver
class to compute the ground state energy of our \(H_2\) molecule? Use the defaultvqe_options
below. You should find a result of about-1.137270168 Ha
.
from tangelo.algorithms import VQESolver
= {"molecule": mol}
vqe_options
# INSERT CODE HERE
# Print the optimal energy found by VQE and how far it is from FCI
print(f'VQE energy = {opt_energy} (difference with FCI = {abs(opt_energy - fci_energy)})')
A lot has happened under the hood, let’s highlight a few of them:
- the molecule has been mapped into qubits using a qubit mapping
- an ansatz circuit with variational parameters has been built
- a backend (here a simulator) was to simulate quantum circuits
- a classical optimizer has been chosen to drive the optimization of variational parameters
The cell below use the vars
function in python which prints the attributes of an objects, and pprint
to print things nicely. If we use that on your vqe_solver
object, we should be able to understand what has happened in the previous cell better.
Q: Using the output of the cell below, can you find what qubit mapping, ansatz, backend and classical optimizer were used in your previous simulation ?
from pprint import pprint
vars(vqe_solver)) pprint(
{'ansatz': <tangelo.toolboxes.ansatz_generator.uccsd.UCCSD object at 0x7fe03f9bee30>,
'ansatz_options': {},
'backend': <tangelo.linq.target.target_qulacs.QulacsSimulator object at 0x7fe04d857a60>,
'backend_options': {'n_shots': None, 'noise_model': None, 'target': None},
'builtin_ansatze': {<BuiltInAnsatze.UCCSD: <class 'tangelo.toolboxes.ansatz_generator.uccsd.UCCSD'>>,
<BuiltInAnsatze.pUCCD: <class 'tangelo.toolboxes.ansatz_generator.puccd.pUCCD'>>,
<BuiltInAnsatze.ILC: <class 'tangelo.toolboxes.ansatz_generator.ilc.ILC'>>,
<BuiltInAnsatze.UCCGD: <class 'tangelo.toolboxes.ansatz_generator.uccgd.UCCGD'>>,
<BuiltInAnsatze.VSQS: <class 'tangelo.toolboxes.ansatz_generator.vsqs.VSQS'>>,
<BuiltInAnsatze.QCC: <class 'tangelo.toolboxes.ansatz_generator.qcc.QCC'>>,
<BuiltInAnsatze.QMF: <class 'tangelo.toolboxes.ansatz_generator.qmf.QMF'>>,
<BuiltInAnsatze.UpCCGSD: <class 'tangelo.toolboxes.ansatz_generator.upccgsd.UpCCGSD'>>,
<BuiltInAnsatze.HEA: <class 'tangelo.toolboxes.ansatz_generator.hea.HEA'>>,
<BuiltInAnsatze.UCC3: <tangelo.toolboxes.ansatz_generator.rucc.RUCC object at 0x7fe09086fbe0>>,
<BuiltInAnsatze.UCC1: <tangelo.toolboxes.ansatz_generator.rucc.RUCC object at 0x7fe09086e620>>},
'deflation_circuits': [],
'deflation_coeff': 1,
'initial_var_params': [2e-05, 0.03632537110234506],
'molecule': SecondQuantizedMolecule(xyz=[('H', (0.0, 0.0, 0.0)),
('H',
(0.0, 0.0, 0.7413999999999998))],
q=0,
spin=0,
solver=<tangelo.toolboxes.molecular_computation.integral_solver_pyscf.IntegralSolverPySCF object at 0x7fe09086f4f0>,
n_atoms=2,
n_electrons=2,
basis='sto-3g',
ecp={},
symmetry=False,
uhf=False,
mf_energy=-1.1166843870853411,
mo_energies=array([-0.57797481, 0.66969867]),
mo_occ=array([2., 0.]),
n_mos=2,
n_sos=4,
active_occupied=[0],
frozen_occupied=[],
active_virtual=[1],
frozen_virtual=[]),
'optimal_circuit': <tangelo.linq.circuit.Circuit object at 0x7fe03fa00a90>,
'optimal_energy': -1.1372701683419073,
'optimal_var_params': array([-5.44105775e-05, 5.65245079e-02]),
'optimizer': <bound method VQESolver._default_optimizer of <tangelo.algorithms.variational.vqe_solver.VQESolver object at 0x7fe03fa02650>>,
'penalty_terms': None,
'projective_circuit': None,
'qubit_hamiltonian': (-0.0988639693354576+0j) [] +
(-0.04532220205287404+0j) [X0 X1 Y2 Y3] +
(0.04532220205287404+0j) [X0 Y1 Y2 X3] +
(0.04532220205287404+0j) [Y0 X1 X2 Y3] +
(-0.04532220205287404+0j) [Y0 Y1 X2 X3] +
(0.1711977490343296+0j) [Z0] +
(0.16862219158920955+0j) [Z0 Z1] +
(0.12054482205301811+0j) [Z0 Z2] +
(0.16586702410589216+0j) [Z0 Z3] +
(0.17119774903432963+0j) [Z1] +
(0.16586702410589216+0j) [Z1 Z2] +
(0.12054482205301811+0j) [Z1 Z3] +
(-0.22278593040418523+0j) [Z2] +
(0.17434844185575676+0j) [Z2 Z3] +
(-0.22278593040418523+0j) [Z3],
'qubit_mapping': 'jw',
'ref_state': None,
'reference_circuit': <tangelo.linq.circuit.Circuit object at 0x7fe03fa028f0>,
'simulate_options': {},
'up_then_down': False,
'verbose': False}
Q: Now that you’ve taken a peek inside the
vqe_solver
object, can you print: - the optimal parameters values found by VQE ? - the optimal circuit ?
# INSERT CODE HERE
Q: The
simulate
method runs the entire VQE algorithm, which performed many energy estimations along the way, driven by the classical optimizer. Can you instead perform a single energy estimation, usingvqe_solver
? If you pass the optimal parameters as input, you should see that the output is the optimal energy.
# INSERT CODE HERE
Level 2: Impact of some of the options in VQE
A number of options are available in the VQE framework. In this hands-on, we only graze the surface but some of the additional resources at the beginning of this notebook are more comprehensive. Let’s see how some of choices impact the calculations. Some of these options require more information from the user, as they may be chemically-inspired. Others may work right away. Some combination of options may be incompatible. Tangelo is doing its best to return appropriate error messages when things don’t work out.
2a) Qubit mappings
Our VQE framework supports a number of qubit mappings, such as Jordan-Wigner ('jw'
), Bravyi-Kitaev ('bk'
) or Symmetry-Conserving Bravyi-Kitaev ('bk'
). But there’s more to life than these ! Tangelo also supports more uncommon ones, such as JKMN, Hardcore Boson (HCB), combinational mapping, and probably more in the future.
VQESolver
provides a rather high-level interface, but all the building blocks used underneath are available to you ! You can check out the qubit_mapping toolbox in Tangelo if you’d like to learn more. The fermion_to_qubit_mapping
function and the individual mapping functions will be helpful if you decide to explore this topic more in depth, or try to build custom Hamiltonians.
Q: Can you instantiate
VQESolver
objects using a few of these different qubit mappings, and see how they impact the complexity of the algorithm using theget_resources
method and printing thequbit_hamiltonian
?
for qb_mapping in ["JW", "SCBK", "JKMN"]:
# INSERT CODE HERE
=
vqe_options
= VQESolver(vqe_options)
vqe_solver
vqe_solver.build()= vqe_solver.simulate()
opt_energy
# INSERT PRINT INSTRUCTIONS HERE
2b) Compute backends
Algorithms in Tangelo are backend-agnostic. That means that you do not have to rewrite your code if you want to run it on a different simulator or quantum device, switching between them is rather effortless. There’s a lot of quantum simulators out there, with different tradeoffs between speed, accuracy, maximal circuit size or ability to simulate noise.
Let’s revisit \(H_2\) in a bigger basis set (this means a more accurate representation, but more intensive calculations), and see what impact the choice of backend has on our experience.
# Tangelo has some built-in molecules that are often reused in tests and tutorials.
from tangelo.molecule_library import mol_H2_321g
= mol_H2_321g mol
We’ll use FCI to get the exact value of the ground state of this molecule, for reference:
= FCISolver(mol).simulate()
fci_energy print(fci_energy)
-1.1478303189235124
# Before we attempt to simulate anything: let's get an idea of the compute requirements
# We'd rather not try to simulate something that we think is not worth it.
= {"molecule": mol}
vqe_options = VQESolver(vqe_options)
vqe_solver
vqe_solver.build()print(vqe_solver.get_resources())
{'qubit_hamiltonian_terms': 185, 'circuit_width': 8, 'circuit_depth': 586, 'circuit_2qubit_gates': 432, 'circuit_var_gates': 52, 'vqe_variational_parameters': 9}
Q: Can you fill in the cell below to run the same VQE approach on this molecule, with the different backends below ? What do you observe ?
This cell may take a few minutes to run, depending on the machine used. Feel free to continue reading, interrupt the execution of the cell, or revert the molecule to our smaller original use case mol_H2_sto3g
.
import time
for b in ["qulacs", "cirq", "qiskit"]:
# INSERT CODE HERE
=
vqe_options
# Start timer
= time.time()
t_start
# Run VQE algorithm
= VQESolver(vqe_options)
vqe_solver
vqe_solver.build()= vqe_solver.simulate()
opt_energy
# Stop timer, print elapsed time
= time.time()
t_stop print(f"{b:10} :: {t_stop - t_start:6.3f} s \t (energy = {opt_energy})")
qulacs :: 2.059 s (energy = -1.1478302233980724)
cirq :: 69.248 s (energy = -1.1478302233982864)
qiskit :: 246.155 s (energy = -1.147830223398106)
Optional boss fight: \(H_2O\) sto-3g
Boss fight, boss fight !
\(H_2O\) is a humble molecule, but already quite a challenge with VQE. Assume you want to compute the ground state energy of \(H_2O\) in the sto-3g
basis set using VQE. Use everything at your disposal here, and don’t be afraid to explore (but maybe use get_resources
before blindly jumping into simulate
).
It’s still pretty easy for FCI on a laptop though, and we can get a reference value:
from tangelo.molecule_library import mol_H2O_sto3g
= mol_H2O_sto3g
mol
= FCISolver(mol_H2O_sto3g).simulate()
fci_H2O_energy print(f"H2O FCI energy = {fci_H2O_energy}")
H2O FCI energy = -75.01277542934052
On your machine, VQE may take a while. But it is possible for a laptop to handle it in a few minutes or less. It is already valuable for you to explore ideas without performing any simulation: what “levers” do you have to shorten time-to-solution, improve accuracy and reduce resource requirements ?
Q: Can you compute the ground state energy of this molecule ? - How close can you get to FCI reference results ? - How much can you reduce computational requirements ? - How low can you bring your time-to-solution ?
# CHANGE CODE AND EXPLORE
= "qulacs"
b = {"target":'qulacs'}
backend_options = {"molecule": mol, "qubit_mapping": 'scbk', "backend_options": backend_options, "verbose": True}
vqe_options
# --------------------
# Start timer
= time.time()
t_start
# Run VQE algorithm
= VQESolver(vqe_options)
vqe_solver
vqe_solver.build()print(vqe_solver.get_resources())
= vqe_solver.simulate()
opt_energy
# Stop timer, print elapsed time
= time.time()
t_stop print(f"{b:10} :: {t_stop - t_start:3.3f} s \t (energy = {opt_energy})")
Final words
After this hands-on, you now understand how to use the VQE algorithm and its options. It is a very flexible NISQ algorithm which can yield approaches with very differing accuracies and compute requirements depending on the use cases. A lot of research is being done on the topic, in order to apply this to current quantum computers.
Do not hesitate to explore the resources mentioned at the beginning of this notebook at your own pace to learn more about the topics discussed here. There is so much more we can do with variational algortihms.