<jupyter_start><jupyter_text>Molecular Encoding - SuppInfo 2Step 1 is to map our molecules onto qubits. We want to find the ground states of 3 molecules: 1. $H_2$ has 2 $1s$ orbitals, so 4 spin-orbitals. 2. $LiH$ has $1s, 2s, 2p_x$ (we assume zero-filling for $2p_y, 2p_z$) for $Li$ and $1s$ for $H$, so 8 spin-orbitals. 3. Same as above, but now we have 2 $H$ atoms, so 10 spin-orbitals. In the STO-3G (fitting 3 gaussians to the Slater atomic orbitals), we can write second quantized Hamiltonian as:$$H = H_1 + H_2 = \sumFor $LiH$ and $BeH_2$ we consider **perfect filling** in the inner $1s$ orbitals. **Okay I don't understand any of this - I'll just use the $H_2$ representation given and learn this later.**<jupyter_code>from openfermion.ops import QubitOperator
h2_hub = (0.011280 * QubitOperator('Z0 Z1') + 
          0.397936 * QubitOperator('Z0') + 
          0.397936 * QubitOperator('Z1') + 
          0.180931 * QubitOperator('X0 X1')) 
# Define objective function as Hamiltonian averaging 
from openfermioncirq import HamiltonianObjective
obj = HamiltonianObjective(h2_hub)

# Define ansatz 
from openfermioncirq import VariationalAnsatz 
from openfermioncirq.variational.letter_with_subscripts import LetterWithSubscripts
class HEA(VariationalAnsatz):
    def __init__(self, n, d=1):
        """
        n: number of qubits
        d: number of entangling + rotation layers 
        """
        self.d = d 
        self.n = n
        super().__init__(None) 
    
    def params(self): 
        """The parameters of the ansatz. N(3d + 2) parameters """
        for q in range(self.n): # Loop over qubits
            for i in range(self.d + 1): # Loop over 0,1,...,d
                for j in range(1, 4): # Loop over 1,2,3 for ZXZ 
                    # 2 starting X then Z rotations on each qubit
                    if i == 0 and j == 1: continue # No first Z rotation
                    
                    yield LetterWithSubscripts('θ', q, i, j) 
    
    def param_bounds(self): 
        bounds = []
        for param in self.params():
            bounds.append((-2*np.pi, 2*np.pi))
        return bounds
    
    def _generate_qubits(self):
        """Produce qubits that can be used by the ansatz"""
        return cirq.LineQubit.range(self.n)
    
    def operations(self, qubits): 
        """Produce the operations of the ansatz circuit"""
        param_set = set(self.params()) 
        
        for i in range(self.d + 1): 
            for q in range(self.n):
                if q != self.n - 1: 
                    yield cirq.ops.CNOT.on(self.qubits[q], self.qubits[q+1])
                    yield cirq.ops.CNOT.on(self.qubits[q+1], self.qubits[q])
                for j in range(1,4):
                    sym = LetterWithSubscripts('θ', q, i, j)
                    if sym not in param_set: 
                        print('{} was not in param_set'.format(sym))
                        continue 
                        
                    if j == 1 or j == 3: 
                        yield cirq.rz(sym).on(self.qubits[q])
                    elif j == 2: 
                        yield cirq.rx(sym).on(self.qubits[q])
                                                
                    else: print('j = ', j, '. But Should be 1,2,or 3.')
                        
# Initialize ansatz 
ansatz = HEA(2, 1)
print('The HEA ansatz: ')
print(ansatz.circuit.to_text_diagram(transpose=True))
# Create a variational study 
from openfermioncirq import VariationalStudy 
study = VariationalStudy('HEA', ansatz, obj, initial_state=np.array([0,1,0,0]))

print('Created a study with {} qubits and {} parameters'.format(
        len(study.ansatz.qubits), study.num_params))
print(obj._hamiltonian_linear_op.shape)

print('Value of objective with default initial params is {}'.format(
        study.value_of(ansatz.default_initial_params())))
from openfermioncirq.optimization import ScipyOptimizationAlgorithm, OptimizationParams

# Optimize
algorithm = ScipyOptimizationAlgorithm(
    kwargs={'method': 'COBYLA'},
#     options={'maxiter': 100},
    uses_bounds=False)
optimization_params = OptimizationParams(
    algorithm=algorithm)
result = study.optimize(optimization_params)
print(result.optimal_value)<jupyter_output>-0.8048889791046269
{'fraction_non_alphanumeric': 0.10722665415297983, 'fraction_numerical': 0.027451900516189582, 'mean_word_length': 2.2666666666666666, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '660967', 'license': 'Apache-2.0', 'n_tokens_mistral': 1425, 'n_tokens_neox': 1291, 'n_words': 391, 'path': 'HEA.ipynb', 'repo_name': 'warrenalphonso/qc-mentorship', 'size': '4262'}