goodchemistryco / Tangelo

A python package for exploring end-to-end chemistry workflows on quantum computers and simulators.
https://goodchemistryco.github.io/Tangelo/
Other
99 stars 27 forks source link

VQE get_rdm performance improvements #357

Closed ValentinS4t1qbit closed 7 months ago

ValentinS4t1qbit commented 7 months ago

The get_rdm code in VQE spent a lot of time concatenating a state preparation circuit with measurement gates and simulating everything, but we could have simply simulated the measurement gates circuits reusing the intiial statevector. This greatly accelerates calculations and now the fragment solvers take most of the time.

The example below ran originally in 278s on my laptop (2017), and now runs in 50s, which means a speedup of over x5 in this scale, scaling to even bigger speedups for more complex use cases. Here we have 2 fragments requiring 8 qubits, 1K gates and 185 terms in the Hamiltonian. Most of the time is now spent in the qubit mapping used to update parameters in the UCCSD ansatz.

edit: I implemented changes that yield a speedup for noisy simulation as well, which add and remove only the gates corresponding to the measurement bases instead of reconstructing the entire circuit.

from time import time
from tangelo import SecondQuantizedMolecule
from tangelo.problem_decomposition.dmet.dmet_problem_decomposition import Localization, DMETProblemDecomposition

xyz_H4 = [("H", [0.0, 0.0, i*2.5]) for i in range(4)]
mol_H4_sto3g = SecondQuantizedMolecule(xyz_H4, q=0, spin=0, basis="sto-3g")

my_backend_options = {"target": "qulacs", "n_shots": None, "noise_model": None}
solvers_options = {"backend_options": my_backend_options} 

opt_dmet = {"molecule": mol_H4_sto3g,
            "fragment_atoms": [2]*2,
            "fragment_solvers": ["vqe"]*2,
            "solvers_options": [solvers_options]*2,
            "electron_localization": Localization.meta_lowdin,
            "verbose": True
            }

# Run DMET
dmet = DMETProblemDecomposition(opt_dmet)
dmet.build()
print(dmet.get_resources())

t1 = time()
energy = dmet.simulate()
print(energy)
print(f'Elapsed : {time()-t1} s')
ValentinS4t1qbit commented 7 months ago

This is good for a statevector simulation, but in the case one wants to use a shot-based/noisy simulator, it would apply the same shot-based/noisy statevector to all RDMs element calculation (thus the statistical/error noises would be the same, creating a bias that shouldn't happen when working with real hardware). Is this a behaviour we are OK with?

Ack, you're right. In the noiseless case I think it doesnt matter (we simulate once, get samples from final state). For noisy, thats another story. I'll look into it again, and see if I can implement a "switch" of some sort.