Source code for openqemist.quantum_solvers.microsoft_qsharp.generate_uccsd_operators

#   Copyright 2019 1QBit
#   
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

import itertools

[docs]def complex_as_dict(re, im): import cmath x = complex(re, im) return {'Real': re, 'Imaginary': im, 'Magnitude': abs(x), 'Phase': cmath.phase(x)}
[docs]def alpha_spinorbital(MO): ''' Return the corresponding alpha spinorbital index given a molecular orbital (MO) index Args: MO(int): molecular orbital index Returns: (2*MO)(int): alpha spin-orbital index ''' return 2*MO
[docs]def beta_spinorbital(MO): ''' Return the corresponding beta spinorbital index given a molecular orbital (MO) index Args: MO(int): molecular orbital index Returns: (2*MO + 1)(int): beta spin-orbital index ''' return 2 * MO + 1
[docs]def count_amplitudes(n_spinorbitals,n_electrons): """Count the number of singles and doubles amplitudes for a given UCCSD-VQE run Args: n_spinorbitals(int): integer representing the number of spinorbitals (qubits) for a given molecule and basis set n_electrons(int): integer representing the number of electrons for a given molecule Returns: n_amplitudes(int): integer representing the total number of amplitudes (MO basis) """ # Compute the number of MOs and the number of occupied and virtual MOs n_MO = n_spinorbitals // 2 n_occ = n_electrons // 2 n_virt = n_MO - n_occ # Compute the number of singles and doubles amplitudes n_singles = n_occ*n_virt n_doubles = n_singles*(n_singles + 1) // 2 return n_singles + n_doubles
[docs]def compute_cluster_operator(n_spinorbitals, n_electrons, amplitudes, multiply=False, operator = []): """Compute or update the cluster operator for a given UCCSD-VQE run n_spinorbitals(int): integer representing the number of spinorbitals (qubits) for a given molecule and basis set n_electrons(int): integer representing the number of electrons for a given molecule amplitudes(list): list of the amplitudes, with the singles appearing first, followed by the diagonal (i,i,a,a) doubles and then the off-diagonal (i,j,a,b) doubles multiply(bool): optional boolean to indicate whether we are performing an amplitude update (i.e. multiplying a new set of amplitudes by the corresponding operators) or not operator(list): optional list of the contributions to the cluster operator Returns: ref(tuple): tuple of tuples representing the reference configuration t(list): list of tuples representing the cluster operator """ # Compute the number of MOs and the number of occupied and virtual MOs n_MO = n_spinorbitals // 2 n_occ = n_electrons // 2 n_virt = n_MO - n_occ # Extract the singles amplitudes and diagonal doubles amplitudes singles = amplitudes[:(n_occ*n_virt)] doubles_diag = amplitudes[(n_occ*n_virt):(2*n_occ*n_virt)] doubles_offdiag = amplitudes[(2*n_occ*n_virt):] # Spin indexing spin_index = [alpha_spinorbital, beta_spinorbital] # ------------------------------------------------------- # Reference configuration # ------------------------------------------------------- #Loop over occupied orbitals li = [] j = 0 for i in range(n_occ): # Define alpha and beta spinorbitals i_a = alpha_spinorbital(i) i_b = beta_spinorbital(i) if(multiply): li += [i_a,i_b] else: li += [('u',i_a), ('u', i_b)] # Define the reference state if(multiply): ref = ((1.0,0.0),li) #ref = (li,1) else: #ref = ((1.0, 0.0),(li,1)) ref = (li,1) t = [] # ------------------------------------------------------- # Single excitations and diagonal double excitations # ------------------------------------------------------- # Loop over occupied and virtual orbitals for i, (m,n) in enumerate(itertools.product(range(n_virt),range(n_occ))): # n labels virtual orbitals (offset of n_occ) m += n_occ # Loop over spin for spin in range(2): # Mapping of spatial orbitals to spin-orbitals ind_1 = spin_index[spin] ind_2 = spin_index[1-spin] # Define spin-orbital labels m_1 = ind_1(n) m_2 = ind_2(n) n_1 = ind_1(m) n_2 = ind_2(m) if(multiply): # Multiply the singles operators by the correct amplitudes t += [((singles[i], 0.0), operator[j][1])] #t += [(operator[j][1], complex_as_dict(singles[i],0.0))] j += 1 # Multiply the diagonal doubles operators by the correct amplitudes if(m_1 != m_2 and n_1 != n_2): t += [((doubles_diag[i], 0.0), operator[j][1])] j += 1 else: # Generate the singles excitations in the proper format t += [(([('u',n_1),('d',m_1)],1), complex_as_dict(singles[i],0.0))] # Generate the diagonal doubles excitations in the proper format if(m_1 != m_2 and n_1 != n_2): t += [(([('u',n_1),('u',n_2),('d',m_1),('d',m_2)],1), complex_as_dict(doubles_diag[i],0.0))] # Loop over unique off-diagonal doubles for i, ((m,u),(n,v)) in enumerate(itertools.combinations( itertools.product(range(n_virt),range(n_occ)),2) ): # m and n label virtual orbitals (offset of n_occ) m += n_occ n += n_occ # Loop over spin for (spin_1, spin_2) in itertools.product(range(2), repeat=2): # Mapping of spatial orbitals to spin-orbitals ind_1 = spin_index[spin_1] ind_2 = spin_index[spin_2] # Define spin-orbital labels m_1 = ind_1(u) m_2 = ind_2(v) n_1 = ind_1(m) n_2 = ind_2(n) if(multiply): # Multiply the off-diagonal doubles excitation operators by the correct amplitudes if(m_1 != m_2 and n_1 != n_2): t += [((1.0*doubles_offdiag[i],0.0), operator[j][1])] j += 1 else: # Generate the off-diagonal doubles excitations in the proper format if(m_1 != m_2 and n_1 != n_2): t += [(([('u',n_1),('u',n_2),('d',m_1),('d',m_2)],1), complex_as_dict(doubles_offdiag[i],0.0))] # If multiply is true, then append the reference configuration to t if (multiply): t += [ref] return ref, t