<jupyter_start><jupyter_text>Unitary ansatz entering the VQEThe final energy output of a VQE calculation will crucially depend on the ansatz/form of the parameterized unitary $\hat U(\boldsymbol{\theta})$ employed in state preparation. Here we review two popular approaches, the unitary coupled cluster and qubit coupled cluster methodologies, and benchmark them for energy calculations of small molecules.<jupyter_code>import numpy as np
import tequila as tq
from utility import *
threshold = 1e-6 #Cutoff for UCC MP2 amplitudes and QCC ranking gradients
basis='sto-3g'<jupyter_output><empty_output><jupyter_text>Unitary Coupled Cluster (UCC)The UCC ansatz is obtained by 'unitarizing' the traditional coupled cluster ansatz,$$ e^{\hat T} \rightarrow e^{\hat T - \hat T^\dagger} \equiv \hat U_{\text{UCC}}$$Due to non-commutativity of terms in $\hat T - \hat T^\dagger$, the UCC ansatz does not have a straightforward decomposition in terms of circuit primitives implementable on the quantum computer. Therefore, to obtain a form which can be compiled, we employ the Trotter approximation. The accuracy of the circuit ansatz relative to the exact UCC operator will be dependent on how many Trotter steps are employed. The number of Trotter steps is commonly set to its minimal value of one to avoid excessive circuit depth.<jupyter_code>trotter_steps = 1<jupyter_output><empty_output><jupyter_text>Below is a sample VQE simulation using the UCCSD ansatz compiled using a single trotter step for H$_2$ in minimal basis at $R=2$ (Angstrom). The VQE optimization is of the form$$E = \min_{\boldsymbol{\theta}} \langle \text{HF} | \hat U_{\text{UCC}}^\dagger(\boldsymbol{\theta}) \hat H  \hat U_{\text{UCC}} (\boldsymbol{\theta}) | \text{HF} \rangle $$<jupyter_code>xyz_data = get_molecular_data('h2', geometry=2, xyz_format=True)

molecule = tq.quantumchemistry.Molecule(geometry=xyz_data, basis_set=basis)

H = molecule.make_hamiltonian()

U_UCCSD = molecule.make_uccsd_ansatz(initial_amplitudes='mp2', threshold=threshold, trotter_steps=trotter_steps)

E = tq.ExpectationValue(H=H, U=U_UCCSD)

result = tq.minimize(objective=E, method="BFGS", initial_values={k:0.0 for k in E.extract_variables()}, tol=1e-6)<jupyter_output>Optimizer: <class 'tequila.optimizers.optimizer_scipy.OptimizerSciPy'> 
backend         : qulacs
samples         : None
save_history    : True
noise           : None

Method          : BFGS
Objective       : 1 expectationvalues
gradient        : 32 expectationvalues

active variables : 1

E=-0.78379265  angles= {(1, 0, 1, 0): 0.0}  samples= None
E=-0.94730762  angles= {(1, 0, 1, 0): -0.5182770490646362}  samples= None
E=-0.94853739  angles= {(1, 0, 1, 0): -0.5800341642936221}  samples= None
E=-0.94864111  angles= {(1, 0, 1, 0): -0.5665552235469631}  samples= None
E=-0.94864111  angles= {(1, 0, 1, 0): -0.5665703294450197}  samples= None
Optimization terminated successfully.
         Current function value: -0.948641
         Iterations: 4
         Function evaluations: 5
         Gradient evaluations: 5<jupyter_text>Qubit Coupled Cluster (QCC)In contrast to UCC, the QCC methodology makes no direct reference to fermionic algebra and seeks to construct an efficient ansatz directly in qubit-space by finding multi-qubit Pauli strings (entanglers) which lower energy. This is done through an energy-lowering heuristic employing the energy gradient with respect to a Pauli strings variational amplitude.<jupyter_code>xyz_data = get_molecular_data('h2', geometry=2, xyz_format=True)

molecule = tq.quantumchemistry.Molecule(geometry=xyz_data, basis_set=basis)

hf_reference = hf_occ(2*molecule.n_orbitals, molecule.n_electrons)

H = molecule.make_hamiltonian()

#Define number of entanglers to enter ansatz
n_ents = 1

#Rank entanglers using energy gradient criterion
ranked_entangler_groupings = generate_QCC_gradient_groupings(H.to_openfermion(), 
                                                             2*molecule.n_orbitals, 
                                                             hf_reference, 
                                                             cutoff=threshold)

print('Grouping gradient magnitudes:')
for grouping in ranked_entangler_groupings:
    print(grouping[1])

entanglers = get_QCC_entanglers(ranked_entangler_groupings, n_ents, 2*molecule.n_orbitals)

print('\nGenerated entanglers:')
for ent in entanglers:
    print(ent)<jupyter_output>Grouping gradient magnitudes:
0.2591

Generated entanglers:
1.0 [X0 Y1 X2 X3]<jupyter_text>Once the QCC ranking procedure has been ran, we can simulate the QCC VQE optimization with the generated entanglers. The VQE optimization for the QCC ansatz is of the form$$E = \min_{\boldsymbol{\Omega}, \boldsymbol{\tau}} \langle \boldsymbol{\Omega} | U_{\text{ENT}}^\dagger (\boldsymbol{\tau}) \hat H  U_{\text{ENT}} (\boldsymbol{\tau}) | \boldsymbol{\Omega} \rangle $$where $\boldsymbol{\Omega}$ denote collective Euler angles parameterizing single-qubit rotations, and $\boldsymbol{\tau}$ are entangler amplitudes.<jupyter_code>#Mean-field part of U (Omega):    
U_MF = construct_QMF_ansatz(n_qubits = 2*molecule.n_orbitals)
#Entangling part of U:
U_ENT = construct_QCC_ansatz(entanglers)

U_QCC = U_MF + U_ENT

E = tq.ExpectationValue(H=H, U=U_QCC)

initial_vals = init_qcc_params(hf_reference, E.extract_variables())

#Minimize wrt the entangler amplitude and MF angles:
result = tq.minimize(objective=E, method="BFGS", initial_values=initial_vals, tol=1.e-6)<jupyter_output>Optimizer: <class 'tequila.optimizers.optimizer_scipy.OptimizerSciPy'> 
backend         : qulacs
samples         : None
save_history    : True
noise           : None

Method          : BFGS
Objective       : 1 expectationvalues
gradient        : 18 expectationvalues

active variables : 9

E=-0.78379265  angles= {beta_1: 3.141592653589793, beta_0: 3.141592653589793, tau_0: 0.0, gamma_1: 0.0, beta_3: 0.0, gamma_3: 0.0, gamma_2: 0.0, gamma_0: 0.0, beta_2: 0.0}  samples= None
E=-0.84614773  angles= {beta_1: 3.141592653589793, beta_0: 3.141592653589793, tau_0: 0.25913846492767334, gamma_1: 0.0, beta_3: 0.0, gamma_3: 0.0, gamma_2: 0.0, gamma_0: 0.0, beta_2: 0.0}  samples= None
E=-0.93586763  angles= {beta_1: 3.141592653589793, beta_0: 3.141592653589793, tau_0: 0.8331991688325238, gamma_1: 0.0, beta_3: 0.0, gamma_3: 0.0, gamma_2: 0.0, gamma_0: 0.0, beta_2: 0.0}  samples= None
E=-0.94812904  angles= {beta_1: 3.141592653589793, beta_0: 3.141592653589793, tau_0: 1.192979270155198, gamma_1: 0.0, [...]
{'fraction_non_alphanumeric': 0.10163479333744602, 'fraction_numerical': 0.0731030228254164, 'mean_word_length': 4.259529602595296, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '48576', 'license': 'MIT', 'n_tokens_mistral': 2496, 'n_tokens_neox': 2125, 'n_words': 631, 'path': 'Project_2_VQE_Molecules/S3_Unitary_Ansatz.ipynb', 'repo_name': 'DuckTigger/CohortProject_2020', 'size': '6484'}