def do_qite_step(self): btot = self.build_b() A = qf.QubitOperator() if(self._sparseSb): sp_idxs, S, btot = self.build_sparse_S_b(btot) else: S = self.build_S() x = lstsq(S, btot)[0] x = np.real(x) # sing_vals = lstsq(S, btot)[3] # print(np.abs(sing_vals[-1] / sing_vals[0])) if(self._sparseSb): for I, spI in enumerate(sp_idxs): if np.abs(x[I]) > self._x_thresh: A.add(-1.0j * self._db * x[I], self._sig.terms()[spI][1].terms()[0][1]) self._n_classical_params += 1 else: for I, SigI in enumerate(self._sig.terms()): if np.abs(x[I]) > self._x_thresh: A.add(-1.0j * self._db * x[I], SigI[1].terms()[0][1]) self._n_classical_params += 1 if(self._verbose): print('\nbtot:\n ', btot) print('\n S: \n') matprint(S) print('\n x: \n') print(x) eiA_kb, phase1 = trotterize(A, trotter_number=self._trotter_number) self._total_phase *= phase1 self._Uqite.add(eiA_kb) self._qc.apply_circuit(eiA_kb) self._Ekb.append(np.real(self._qc.direct_op_exp_val(self._qb_ham))) self._n_cnot += eiA_kb.get_num_cnots() if(self._verbose): qf.smart_print(self._qc)
def ansatz_circuit(self, amplitudes=None): """ This function returns the Circuit object built from the appropriate amplitudes. Parameters ---------- amplitudes : list A list of parameters that define the variational degrees of freedom in the state preparation circuit Uvqc. This is needed for the scipy minimizer. """ temp_pool = qf.SQOpPool() tamps = self._tamps if amplitudes is None else amplitudes for tamp, top in zip(tamps, self._tops): temp_pool.add(tamp, self._pool_obj[top][1]) A = temp_pool.get_qubit_operator('commuting_grp_lex') U, phase1 = trotterize(A, trotter_number=self._trotter_number) if phase1 != 1.0 + 0.0j: raise ValueError("Encountered phase change, phase not equal to (1.0 + 0.0i)") return U
def build_sa_qk_mats(self): """Returns the QK effective hamiltonain and overlap matrices in a basis of spin adapted references. """ num_tot_basis = len(self._sa_ref_lst) * self._nstates_per_ref h_mat = np.zeros((num_tot_basis,num_tot_basis), dtype=complex) s_mat = np.zeros((num_tot_basis,num_tot_basis), dtype=complex) omega_lst = [] Homega_lst = [] for i, ref in enumerate(self._sa_ref_lst): for m in range(self._nstates_per_ref): Um = qforte.Circuit() phase1 = 1.0 if(m>0): fact = (0.0-1.0j) * m * self._mr_dt expn_op1, phase1 = trotterize(self._qb_ham, factor=fact, trotter_number=self._trotter_number) Um.add(expn_op1) QC = qforte.Computer(self._nqb) state_prep_lst = [] for term in ref: coeff = term[0] det = term[1] idx = ref_to_basis_idx(det) state = qforte.QubitBasis(idx) state_prep_lst.append( (state, coeff) ) QC.set_state(state_prep_lst) QC.apply_circuit(Um) QC.apply_constant(phase1) omega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) QC.apply_operator(self._qb_ham) Homega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) if(self._diagonalize_each_step): print('\n\n') print(f"{'k(S)':>7}{'E(Npar)':>19}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}") print('-------------------------------------------------------------------------------') # TODO (opt): add this to previous loop for p in range(num_tot_basis): for q in range(p, num_tot_basis): h_mat[p][q] = np.vdot(omega_lst[p], Homega_lst[q]) h_mat[q][p] = np.conj(h_mat[p][q]) s_mat[p][q] = np.vdot(omega_lst[p], omega_lst[q]) s_mat[q][p] = np.conj(s_mat[p][q]) if (self._diagonalize_each_step): # TODO (cleanup): have this print to a separate file evals, evecs = canonical_geig_solve(s_mat[0:p+1, 0:p+1], h_mat[0:p+1, 0:p+1], print_mats=False, sort_ret_vals=True) scond = np.linalg.cond(s_mat[0:p+1, 0:p+1]) cs_str = '{:.2e}'.format(scond) k = p+1 self._n_classical_params = k if(k==1): self._n_cnot = self._srqk._n_cnot else: self._n_cnot = 2 * Um.get_num_cnots() self._n_pauli_trm_measures = k * self._Nl + self._srqk._n_pauli_trm_measures self._n_pauli_trm_measures += k * (k-1) * self._Nl self._n_pauli_trm_measures += k * (k-1) print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}') return s_mat, h_mat
def build_qk_mats(self): num_tot_basis = len(self._single_det_refs) * self._nstates_per_ref h_mat = np.zeros((num_tot_basis,num_tot_basis), dtype=complex) s_mat = np.zeros((num_tot_basis,num_tot_basis), dtype=complex) # TODO (opt): make numpy arrays. omega_lst = [] Homega_lst = [] for i, ref in enumerate(self._single_det_refs): for m in range(self._nstates_per_ref): # NOTE: do NOT use Uprep here (is determinant specific). Um = qforte.Circuit() for j in range(self._nqb): if ref[j] == 1: Um.add(qforte.gate('X', j, j)) phase1 = 1.0 if(m>0): fact = (0.0-1.0j) * m * self._mr_dt expn_op1, phase1 = trotterize(self._qb_ham, factor=fact, trotter_number=self._trotter_number) Um.add(expn_op1) QC = qforte.Computer(self._nqb) QC.apply_circuit(Um) QC.apply_constant(phase1) omega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) QC.apply_operator(self._qb_ham) Homega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) if(self._diagonalize_each_step): print('\n\n') print(f"{'k(S)':>7}{'E(Npar)':>19}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}") print('-------------------------------------------------------------------------------') for p in range(num_tot_basis): for q in range(p, num_tot_basis): h_mat[p][q] = np.vdot(omega_lst[p], Homega_lst[q]) h_mat[q][p] = np.conj(h_mat[p][q]) s_mat[p][q] = np.vdot(omega_lst[p], omega_lst[q]) s_mat[q][p] = np.conj(s_mat[p][q]) if (self._diagonalize_each_step): # TODO (cleanup): have this print to a separate file evals, evecs = canonical_geig_solve(s_mat[0:p+1, 0:p+1], h_mat[0:p+1, 0:p+1], print_mats=False, sort_ret_vals=True) scond = np.linalg.cond(s_mat[0:p+1, 0:p+1]) cs_str = '{:.2e}'.format(scond) k = p+1 self._n_classical_params = k if(k==1): self._n_cnot = self._srqk._n_cnot else: self._n_cnot = 2 * Um.get_num_cnots() self._n_pauli_trm_measures = k * self._Nl + self._srqk._n_pauli_trm_measures self._n_pauli_trm_measures += k * (k-1) * self._Nl self._n_pauli_trm_measures += k * (k-1) print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}') return s_mat, h_mat
def build_sa_qk_mats(self): # TODO (cleanup): imporve/update docs """Returns matrices P and Q with dimension (nstates_per_ref * len(ref_lst) X nstates_per_ref * len(ref_lst)) based on the evolution of two unitary operators Um = exp(-i * m * dt * H) and Un = exp(-i * n * dt *H). This is done for all spin adapted refrerences |Phi_K> in ref_lst, with (Q) and without (P) measuring with respect to the operator H. Elements P_mn are given by <Phi_I| Um^dag Un | Phi_J>. Elements Q_mn are given by <Phi_I| Um^dag H Un | Phi_J>. This function builds P and Q in an efficient manor and gives the same result as M built from 'matrix_element', but is unphysical for a quantum computer. Arguments --------- ref_lst : list of lists A list containing all of the spin adapted references |Phi_K> to perfrom evolutions on. Is specifically a list of lists of pairs containing coefficient vales and a lists pertaning to single determinants. As an example, ref_lst = [ [ (1.0, [1,1,0,0]) ], [ (0.7071, [0,1,1,0]), (0.7071, [1,0,0,1]) ] ]. nstates_per_ref : int The number of Krylov basis states to generate for each reference. dt_lst : list List of time steps to use for each reference (ususally the same for all references). H : QuantumOperator The operator to time evolove and measure with respect to (usually the Hamiltonain). nqubits : int The number of qubits trot_number : int The number of trotter steps (m) to perform when approximating the matrix exponentials (Um or Un). For the exponential of two non commuting terms e^(A + B), the approximate operator C(m) = (e^(A/m) * e^(B/m))^m is exact in the infinite m limit. Returns ------- s_mat : ndarray A numpy array containing the elements P_mn h_mat : ndarray A numpy array containing the elements Q_mn """ num_tot_basis = len(self._sa_ref_lst) * self._nstates_per_ref h_mat = np.zeros((num_tot_basis,num_tot_basis), dtype=complex) s_mat = np.zeros((num_tot_basis,num_tot_basis), dtype=complex) omega_lst = [] Homega_lst = [] for i, ref in enumerate(self._sa_ref_lst): for m in range(self._nstates_per_ref): # TODO (cleanup): will need to consider gate count for this part. Um = qforte.QuantumCircuit() phase1 = 1.0 if(m>0): fact = (0.0-1.0j) * m * self._mr_dt expn_op1, phase1 = trotterize(self._qb_ham, factor=fact, trotter_number=self._trotter_number) Um.add_circuit(expn_op1) QC = qforte.QuantumComputer(self._nqb) state_prep_lst = [] for term in ref: coeff = term[0] det = term[1] idx = ref_to_basis_idx(det) state = qforte.QuantumBasis(idx) state_prep_lst.append( (state, coeff) ) QC.set_state(state_prep_lst) QC.apply_circuit(Um) QC.apply_constant(phase1) omega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) QC.apply_operator(self._qb_ham) Homega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) if(self._diagonalize_each_step): print('\n\n') print(f"{'k(S)':>7}{'E(Npar)':>19}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}") print('-------------------------------------------------------------------------------') # TODO (opt): add this to previous loop for p in range(num_tot_basis): for q in range(p, num_tot_basis): h_mat[p][q] = np.vdot(omega_lst[p], Homega_lst[q]) h_mat[q][p] = np.conj(h_mat[p][q]) s_mat[p][q] = np.vdot(omega_lst[p], omega_lst[q]) s_mat[q][p] = np.conj(s_mat[p][q]) if (self._diagonalize_each_step): # TODO (cleanup): have this print to a separate file evals, evecs = canonical_geig_solve(s_mat[0:p+1, 0:p+1], h_mat[0:p+1, 0:p+1], print_mats=False, sort_ret_vals=True) scond = np.linalg.cond(s_mat[0:p+1, 0:p+1]) cs_str = '{:.2e}'.format(scond) k = p+1 self._n_classical_params = k if(k==1): self._n_cnot = self._srqk._n_cnot else: self._n_cnot = 2 * Um.get_num_cnots() self._n_pauli_trm_measures = k * self._Nl + self._srqk._n_pauli_trm_measures self._n_pauli_trm_measures += k * (k-1) * self._Nl self._n_pauli_trm_measures += k * (k-1) print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}') return s_mat, h_mat
def measure_gradient(self, params=None, use_entire_pool=False): """ Parameters ---------- HAm : QuantumOpPool The commutator to measure. Ucirc : QuantumCircuit The state preparation circuit. """ if not self._fast: raise ValueError( "self._fast must be True for gradient measurement.") grads = np.zeros(len(self._tamps)) if use_entire_pool: M = len(self._pool) pool_amps = np.zeros(M) for tamp, top in zip(self._tamps, self._tops): pool_amps[top] = tamp else: M = len(self._tamps) grads = np.zeros(M) if params is None: Utot = self.build_Uvqc() else: Utot = self.build_Uvqc(params) qc_psi = qforte.QuantumComputer( self._nqb ) # build | sig_N > according ADAPT-VQE analytical grad section qc_psi.apply_circuit(Utot) qc_sig = qforte.QuantumComputer( self._nqb ) # build | psi_N > according ADAPT-VQE analytical grad section psi_i = copy.deepcopy(qc_psi.get_coeff_vec()) qc_sig.set_coeff_vec(copy.deepcopy( psi_i)) # not sure if copy is faster or reapplication of state qc_sig.apply_operator(self._qb_ham) mu = M - 1 # find <sing_N | K_N | psi_N> if use_entire_pool: Kmu_prev = self._pool[mu][1].jw_transform() Kmu_prev.mult_coeffs(self._pool[mu][0]) else: Kmu_prev = self._pool[self._tops[mu]][1].jw_transform() Kmu_prev.mult_coeffs(self._pool[self._tops[mu]][0]) qc_psi.apply_operator(Kmu_prev) grads[mu] = 2.0 * np.real( np.vdot(qc_sig.get_coeff_vec(), qc_psi.get_coeff_vec())) #reset Kmu_prev |psi_i> -> |psi_i> qc_psi.set_coeff_vec(copy.deepcopy(psi_i)) for mu in reversed(range(M - 1)): # mu => N-1 => M-2 # mu+1 => N => M-1 # Kmu => KN-1 # Kmu_prev => KN if use_entire_pool: tamp = pool_amps[mu + 1] elif params is None: tamp = self._tamps[mu + 1] else: tamp = params[mu + 1] if use_entire_pool: Kmu = self._pool[mu][1].jw_transform() Kmu.mult_coeffs(self._pool[mu][0]) else: Kmu = self._pool[self._tops[mu]][1].jw_transform() Kmu.mult_coeffs(self._pool[self._tops[mu]][0]) Umu, pmu = trotterize(Kmu_prev, factor=-tamp, trotter_number=self._trotter_number) if (pmu != 1.0 + 0.0j): raise ValueError( "Encountered phase change, phase not equal to (1.0 + 0.0i)" ) qc_sig.apply_circuit(Umu) qc_psi.apply_circuit(Umu) psi_i = copy.deepcopy(qc_psi.get_coeff_vec()) qc_psi.apply_operator(Kmu) grads[mu] = 2.0 * np.real( np.vdot(qc_sig.get_coeff_vec(), qc_psi.get_coeff_vec())) #reset Kmu |psi_i> -> |psi_i> qc_psi.set_coeff_vec(copy.deepcopy(psi_i)) Kmu_prev = Kmu np.testing.assert_allclose(np.imag(grads), np.zeros_like(grads), atol=1e-7) return grads
def run(self, spqe_thresh=1.0e-2, spqe_maxiter=20, dt=0.001, M_omega='inf', opt_thresh=1.0e-5, opt_maxiter=30, use_cumulative_thresh=True): if (self._state_prep_type != 'occupation_list'): raise ValueError( "SPQE implementation can only handle occupation_list Hartree-Fock reference." ) self._spqe_thresh = spqe_thresh self._spqe_maxiter = spqe_maxiter self._dt = dt if (M_omega != 'inf'): self._M_omega = int(M_omega) else: self._M_omega = M_omega self._use_cumulative_thresh = use_cumulative_thresh self._opt_thresh = opt_thresh self._opt_maxiter = opt_maxiter self._nbody_counts = [] self._n_classical_params_lst = [] self._results = [] self._energies = [] self._grad_norms = [] self._tops = [] self._tamps = [] self._converged = False self._res_vec_evals = 0 self._res_m_evals = 0 self._curr_energy = 0.0 self._n_classical_params = 0 self._n_cnot = 0 self._n_cnot_lst = [] self._n_pauli_trm_measures = 0 self._n_pauli_trm_measures_lst = [] self.print_options_banner() self._Nm = [] self._pool_type = 'full' self._eiH, self._eiH_phase = trotterize( self._qb_ham, factor=self._dt * (0.0 + 1.0j), trotter_number=self._trotter_number) for occupation in self._ref: if occupation: self._nbody_counts.append(0) self._pool_obj = qf.SQOpPool() for I in range(2**self._nqb): self._pool_obj.add_term(0.0, self.get_op_from_basis_idx(I)) self.build_orb_energies() spqe_iter = 0 hit_maxiter = 0 if (self._print_summary_file): f = open("summary.dat", "w+", buffering=1) f.write( f"#{'Iter(k)':>8}{'E(k)':>14}{'N(params)':>17}{'N(CNOT)':>18}{'N(measure)':>20}\n" ) f.write( '#-------------------------------------------------------------------------------\n' ) while not self._converged: print('\n\n -----> SPQE iteration ', spqe_iter, ' <-----\n') self.update_ansatz() if self._converged: break if (self._verbose): print('\ntoperators included from pool: \n', self._tops) print('\ntamplitudes for tops: \n', self._tamps) self.solve() if (self._verbose): print('\ntamplitudes for tops post solve: \n', np.real(self._tamps)) if (self._print_summary_file): f.write( f' {spqe_iter:7} {self._energies[-1]:+15.9f} {len(self._tamps):8} {self._n_cnot_lst[-1]:10} {sum(self._n_pauli_trm_measures_lst):12}\n' ) spqe_iter += 1 if spqe_iter > self._spqe_maxiter - 1: hit_maxiter = 1 break if (self._print_summary_file): f.close() if hit_maxiter: self._Egs = self.get_final_energy(hit_max_spqe_iter=1) self._Egs = self.get_final_energy() print("\n\n") print("---> Final n-body excitation counts in SPQE ansatz <---") print("\n") print(f"{'Excitaion order':>20}{'Number of operators':>30}") print('---------------------------------------------------------') for l, nl in enumerate(self._nbody_counts): print(f"{l+1:12} {nl:14}") print('\n\n') print( f"{'Iter(k)':>8}{'E(k)':>14}{'N(params)':>17}{'N(CNOT)':>18}{'N(measure)':>20}" ) print( '-------------------------------------------------------------------------------' ) for k, Ek in enumerate(self._energies): print( f' {k:7} {Ek:+15.9f} {self._n_classical_params_lst[k]:8} {self._n_cnot_lst[k]:10} {sum(self._n_pauli_trm_measures_lst[:k+1]):12}' ) self._n_classical_params = len(self._tamps) self._n_cnot = self._n_cnot_lst[-1] self._n_pauli_trm_measures = sum(self._n_pauli_trm_measures_lst) self.print_summary_banner() self.verify_run()
def get_residual_vector(self, trial_amps): if (self._pool_type == 'sa_SD'): raise ValueError( 'Must use single term particle-hole nbody operators for residual calculation' ) temp_pool = qforte.SQOpPool() for param, top in zip(trial_amps, self._tops): temp_pool.add_term(param, self._pool[top][1]) A = temp_pool.get_quantum_operator('commuting_grp_lex') U, U_phase = trotterize(A, trotter_number=self._trotter_number) if U_phase != 1.0 + 0.0j: raise ValueError( "Encountered phase change, phase not equal to (1.0 + 0.0i)") qc_res = qforte.QuantumComputer(self._nqb) qc_res.apply_circuit(self._Uprep) qc_res.apply_circuit(U) qc_res.apply_operator(self._qb_ham) qc_res.apply_circuit(U.adjoint()) coeffs = qc_res.get_coeff_vec() residuals = [] for m in self._tops: # 1. Identify the excitation operator sq_op = self._pool[m][1] # occ => i,j,k,... # vir => a,b,c,... # sq_op is 1.0(a^ b^ i j) - 1.0(j^ i^ b a) temp_idx = sq_op.terms()[0][1][-1] # TODO: This code assumes that the first N orbitals are occupied, and the others are virtual. # Use some other mechanism to identify the occupied orbitals, so we can use use PQE on excited # determinants. if temp_idx < int( sum(self._ref) / 2): # if temp_idx is an occupied idx sq_ex_op = sq_op.terms()[0][1] else: sq_ex_op = sq_op.terms()[1][1] # 2. Get the bit representation of the sq_ex_op acting on the reference. # We determine the projective condition for this amplitude by zero'ing this residual. nbody = int(len(sq_ex_op) / 2) # `destroyed` exists solely for error catching. destroyed = False excited_det = qforte.QuantumBasis(self._nqb) for k, occ in enumerate(self._ref): excited_det.set_bit(k, occ) # loop over annihilators for p in reversed(range(nbody, 2 * nbody)): if (excited_det.get_bit(sq_ex_op[p]) == 0): destroyed = True break excited_det.set_bit(sq_ex_op[p], 0) # then over creators for p in reversed(range(0, nbody)): if (excited_det.get_bit(sq_ex_op[p]) == 1): destroyed = True break excited_det.set_bit(sq_ex_op[p], 1) if destroyed: raise ValueError( "no ops should destroy reference, something went wrong!!") I = excited_det.add() # 3. Compute the phase of the operator, relative to its determinant. qc_temp = qforte.QuantumComputer(self._nqb) qc_temp.apply_circuit(self._Uprep) qc_temp.apply_operator(sq_op.jw_transform()) phase_factor = qc_temp.get_coeff_vec()[I] # 4. Get the residual element, after accounting for numerical noise. res_m = coeffs[I] * phase_factor if (np.imag(res_m) != 0.0): raise ValueError( "residual has imaginary component, something went wrong!!") if (self._noise_factor > 1e-12): res_m = np.random.normal(np.real(res_m), self._noise_factor) residuals.append(res_m) return residuals
def update_ansatz(self): self._n_pauli_measures_k = 0 x0 = copy.deepcopy(self._tamps) init_gues_energy = self.energy_feval(x0) # do U^dag e^iH U |Phi_o> = |Phi_res> temp_pool = qf.SQOpPool() for param, top in zip(self._tamps, self._tops): temp_pool.add_term(param, self._pool[top][1]) A = temp_pool.get_quantum_operator('commuting_grp_lex') U, U_phase = trotterize(A, trotter_number=self._trotter_number) if U_phase != 1.0 + 0.0j: raise ValueError( "Encountered phase change, phase not equal to (1.0 + 0.0i)") qc_res = qf.QuantumComputer(self._nqb) qc_res.apply_circuit(self._Uprep) qc_res.apply_circuit(U) qc_res.apply_circuit(self._eiH) qc_res.apply_circuit(U.adjoint()) res_coeffs = qc_res.get_coeff_vec() lgrst_op_factor = 0.0 # ned to sort the coeffs to psi_tilde temp_order_resids = [] # build different res_sq list using M_omega if (self._M_omega != 'inf'): res_sq_tmp = [ np.real(np.conj(res_coeffs[I]) * res_coeffs[I]) for I in range(len(res_coeffs)) ] # Nmu_lst => [ det1, det2, det3, ... det_M_omega] det_lst = np.random.choice(len(res_coeffs), self._M_omega, p=res_sq_tmp) print(f'|Co|dt^2 : {np.amax(res_sq_tmp):12.14f}') print( f'mu_o : {np.where(res_sq_tmp == np.amax(res_sq_tmp))[0][0]}' ) No_idx = np.where(res_sq_tmp == np.amax(res_sq_tmp))[0][0] print(f'\nNo_idx {No_idx:4}') No = np.count_nonzero(det_lst == No_idx) print(f'\nNo {No:10}') res_sq = [] Nmu_lst = [] for mu in range(len(res_coeffs)): Nmu = np.count_nonzero(det_lst == mu) if (Nmu > 0): print( f'mu: {mu:8} Nmu {Nmu:10} r_mu: { Nmu / (self._M_omega):12.14f} ' ) Nmu_lst.append((Nmu, mu)) res_sq.append((Nmu / (self._M_omega), mu)) ## 1. sort Nmu_lst.sort() res_sq.sort() ## 2. set norm self._curr_res_sq_norm = 0.0 for rmu_sq in res_sq[:-1]: self._curr_res_sq_norm += rmu_sq[0] self._curr_res_sq_norm /= (self._dt * self._dt) ## 3. print stuff print(' \n--> Begin selection opt with residual magnitudes:') print(' Initial guess energy: ', round(init_gues_energy, 10)) print( f' Norm of approximate res vec: {np.sqrt(self._curr_res_sq_norm):14.12f}' ) ## 4. check conv status (need up update function with if(M_omega != 'inf')) if (len(Nmu_lst) == 1): print(' SPQE converged with M_omega thresh!') self._converged = 1 self._final_energy = self._energies[-1] self._final_result = self._results[-1] else: self._converged = 0 ## 5. add new toperator if not self._converged: if self._verbose: print('\n') print(' op index (Imu) Number of times measured') print(' -----------------------------------------------') res_sq_sum = 0.0 n_ops_added = 0 for Nmu_tup in Nmu_lst[:-1]: if (self._verbose): print( f" {Nmu_tup[1]:10} {np.real(Nmu_tup[0]):14}" ) n_ops_added += 1 if (Nmu_tup[1] not in self._tops): self._tops.insert(0, Nmu_tup[1]) self._tamps.insert(0, 0.0) self.add_op_from_basis_idx(Nmu_tup[1]) self._n_classical_params_lst.append(len(self._tops)) else: # when M_omega == 'inf', proceed with standard SPQE res_sq = [(np.real(np.conj(res_coeffs[I]) * res_coeffs[I]), I) for I in range(len(res_coeffs))] res_sq.sort() self._curr_res_sq_norm = 0.0 for rmu_sq in res_sq[:-1]: self._curr_res_sq_norm += rmu_sq[0] self._curr_res_sq_norm /= (self._dt * self._dt) print( ' \n--> Begin selection opt with residual magnitudes |r_mu|:') print(' Initial guess energy: ', round(init_gues_energy, 10)) print( f' Norm of res vec: {np.sqrt(self._curr_res_sq_norm):14.12f}' ) self.conv_status() if not self._converged: if self._verbose: print('\n') print(' op index (Imu) Residual Facotr') print(' -----------------------------------------------') res_sq_sum = 0.0 n_ops_added = 0 # for the canonical SPQE batch addition from |r^2| as a importance criterion if (self._use_cumulative_thresh): temp_ops = [] for rmu_sq in res_sq[:-1]: res_sq_sum += (rmu_sq[0] / (self._dt * self._dt)) if res_sq_sum > (self._spqe_thresh * self._spqe_thresh): if (self._verbose): Ktemp = self.get_op_from_basis_idx(rmu_sq[1]) print( f" {rmu_sq[1]:10} {np.real(rmu_sq[0])/(self._dt * self._dt):14.12f} {Ktemp.str()}" ) n_ops_added += 1 if (rmu_sq[1] not in self._tops): temp_ops.append(rmu_sq[1]) self.add_op_from_basis_idx(rmu_sq[1]) ### consistant with op ordering inspired by traditional renormalization approaches ### for temp_op in temp_ops[::-1]: self._tops.insert(0, temp_op) self._tamps.insert(0, 0.0) else: # add individual ops res_sq.reverse() op_added = False for rmu_sq in res_sq[1:]: if (op_added): break print( f" {rmu_sq[1]:10} {np.real(rmu_sq[0])/(self._dt * self._dt):14.12f}" ) if (rmu_sq[1] not in self._tops): print('op added!') self._tops.insert(0, rmu_sq[1]) self._tamps.insert(0, 0.0) self.add_op_from_basis_idx(rmu_sq[1]) op_added = True self._n_classical_params_lst.append(len(self._tops))
def get_residual_vector(self, trial_amps): temp_pool = qforte.SQOpPool() for param, top in zip(trial_amps, self._tops): temp_pool.add_term(param, self._pool[top][1]) A = temp_pool.get_quantum_operator('commuting_grp_lex') U, U_phase = trotterize(A, trotter_number=self._trotter_number) if U_phase != 1.0 + 0.0j: raise ValueError( "Encountered phase change, phase not equal to (1.0 + 0.0i)") qc_res = qforte.QuantumComputer(self._nqb) qc_res.apply_circuit(self._Uprep) qc_res.apply_circuit(U) qc_res.apply_operator(self._qb_ham) qc_res.apply_circuit(U.adjoint()) coeffs = qc_res.get_coeff_vec() residuals = [] # each operator needs a score, so loop over toperators for m in self._tops: sq_op = self._pool[m][1] # occ => i,j,k,... # vir => a,b,c,... # sq_op is 1.0(a^ b^ i j) - 1.0(j^ i^ b a) temp_idx = sq_op.terms()[0][1][-1] if temp_idx < int( sum(self._ref) / 2): # if temp_idx is an occupid idx sq_sub_tamp, sq_sub_top = sq_op.terms()[0] else: sq_sub_tamp, sq_sub_top = sq_op.terms()[1] nbody = int(len(sq_sub_top) / 2) destroyed = False denom = 1.0 basis_I = qforte.QuantumBasis(self._nqb) for k, occ in enumerate(self._ref): basis_I.set_bit(k, occ) # loop over anihilators for p in reversed(range(nbody, 2 * nbody)): if (basis_I.get_bit(sq_sub_top[p]) == 0): destroyed = True break basis_I.set_bit(sq_sub_top[p], 0) # then over creators for p in reversed(range(0, nbody)): if (basis_I.get_bit(sq_sub_top[p]) == 1): destroyed = True break basis_I.set_bit(sq_sub_top[p], 1) if not destroyed: I = basis_I.add() ## check for correct dets det_I = integer_to_ref(I, self._nqb) nel_I = sum(det_I) cor_spin_I = correct_spin(det_I, 0) qc_temp = qforte.QuantumComputer(self._nqb) qc_temp.apply_circuit(self._Uprep) qc_temp.apply_operator(sq_op.jw_transform()) sign_adjust = qc_temp.get_coeff_vec()[I] res_m = coeffs[I] * sign_adjust # * sq_sub_tamp if (np.imag(res_m) > 0.0): raise ValueError( "residual has imaginary component, someting went wrong!!" ) residuals.append(res_m) else: raise ValueError( "no ops should destroy reference, something went wrong!!") return residuals
def build_qk_mats(self): """Returns matrices S and H needed for the MRSQK algorithm using the Trotterized form of the unitary operators U_n = exp(-i n dt H) The mathematical operations of this function are unphysical for a quantum computer, but efficient for a simulator. Returns ------- s_mat : ndarray A numpy array containing the elements S_mn = <Phi | Um^dag Un | Phi>. _nstates by _nstates h_mat : ndarray A numpy array containing the elements H_mn = <Phi | Um^dag H Un | Phi> _nstates by _nstates """ h_mat = np.zeros((self._nstates, self._nstates), dtype=complex) s_mat = np.zeros((self._nstates, self._nstates), dtype=complex) # Store these vectors for the aid of MRSQK self._omega_lst = [] Homega_lst = [] if (self._diagonalize_each_step): print('\n\n') print( f"{'k(S)':>7}{'E(Npar)':>19}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}" ) print( '-------------------------------------------------------------------------------' ) if (self._print_summary_file): f = open("summary.dat", "w+", buffering=1) f.write( f"#{'k(S)':>7}{'E(Npar)':>19}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}\n" ) f.write( '#-------------------------------------------------------------------------------\n' ) for m in range(self._nstates): # Compute U_m = exp(-i m dt H) Um = qforte.QuantumCircuit() Um.add_circuit(self._Uprep) phase1 = 1.0 if (m > 0): fact = (0.0 - 1.0j) * m * self._dt expn_op1, phase1 = trotterize( self._qb_ham, factor=fact, trotter_number=self._trotter_number) Um.add_circuit(expn_op1) # Compute U_m |φ> QC = qforte.QuantumComputer(self._nqb) QC.apply_circuit(Um) QC.apply_constant(phase1) self._omega_lst.append( np.asarray(QC.get_coeff_vec(), dtype=complex)) # Compute H U_m |φ> QC.apply_operator(self._qb_ham) Homega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) # Compute S_mn = <φ| U_m^\dagger U_n |φ> and H_mn = <φ| U_m^\dagger H U_n |φ> for n in range(len(self._omega_lst)): h_mat[m][n] = np.vdot(self._omega_lst[m], Homega_lst[n]) h_mat[n][m] = np.conj(h_mat[m][n]) s_mat[m][n] = np.vdot(self._omega_lst[m], self._omega_lst[n]) s_mat[n][m] = np.conj(s_mat[m][n]) if (self._diagonalize_each_step): # TODO (cleanup): have this print to a separate file k = m + 1 evals, evecs = canonical_geig_solve(s_mat[0:k, 0:k], h_mat[0:k, 0:k], print_mats=False, sort_ret_vals=True) scond = np.linalg.cond(s_mat[0:k, 0:k]) self._n_classical_params = k self._n_cnot = 2 * Um.get_num_cnots() self._n_pauli_trm_measures = k * self._Nl self._n_pauli_trm_measures += k * (k - 1) * self._Nl self._n_pauli_trm_measures += k * (k - 1) print( f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}' ) if (self._print_summary_file): f.write( f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}\n' ) if (self._diagonalize_each_step and self._print_summary_file): f.close() self._n_classical_params = self._nstates self._n_cnot = 2 * Um.get_num_cnots() # diagonal terms of Hbar self._n_pauli_trm_measures = self._nstates * self._Nl # off-diagonal of Hbar (<X> and <Y> of Hadamard test) self._n_pauli_trm_measures += self._nstates * (self._nstates - 1) * self._Nl # off-diagonal of S (<X> and <Y> of Hadamard test) self._n_pauli_trm_measures += self._nstates * (self._nstates - 1) return s_mat, h_mat
def measure_gradient(self, params=None): """ Returns the disentangled (factorized) UCC gradient, using a recursive approach. Parameters ---------- params : list of floats The variational parameters which characterize _Uvqc. """ if not self._fast: raise ValueError("self._fast must be True for gradient measurement.") M = len(self._tamps) grads = np.zeros(M) if params is None: Utot = self.build_Uvqc() else: Utot = self.build_Uvqc(params) qc_psi = qforte.Computer(self._nqb) # build | sig_N > according ADAPT-VQE analytical grad section qc_psi.apply_circuit(Utot) qc_sig = qforte.Computer(self._nqb) # build | psi_N > according ADAPT-VQE analytical grad section psi_i = copy.deepcopy(qc_psi.get_coeff_vec()) qc_sig.set_coeff_vec(copy.deepcopy(psi_i)) # not sure if copy is faster or reapplication of state qc_sig.apply_operator(self._qb_ham) mu = M-1 # find <sing_N | K_N | psi_N> Kmu_prev = self._pool_obj[self._tops[mu]][1].jw_transform() Kmu_prev.mult_coeffs(self._pool_obj[self._tops[mu]][0]) qc_psi.apply_operator(Kmu_prev) grads[mu] = 2.0 * np.real(np.vdot(qc_sig.get_coeff_vec(), qc_psi.get_coeff_vec())) #reset Kmu_prev |psi_i> -> |psi_i> qc_psi.set_coeff_vec(copy.deepcopy(psi_i)) for mu in reversed(range(M-1)): # mu => N-1 => M-2 # mu+1 => N => M-1 # Kmu => KN-1 # Kmu_prev => KN if params is None: tamp = self._tamps[mu+1] else: tamp = params[mu+1] Kmu = self._pool_obj[self._tops[mu]][1].jw_transform() Kmu.mult_coeffs(self._pool_obj[self._tops[mu]][0]) Umu, pmu = trotterize(Kmu_prev, factor=-tamp, trotter_number=self._trotter_number) if (pmu != 1.0 + 0.0j): raise ValueError("Encountered phase change, phase not equal to (1.0 + 0.0i)") qc_sig.apply_circuit(Umu) qc_psi.apply_circuit(Umu) psi_i = copy.deepcopy(qc_psi.get_coeff_vec()) qc_psi.apply_operator(Kmu) grads[mu] = 2.0 * np.real(np.vdot(qc_sig.get_coeff_vec(), qc_psi.get_coeff_vec())) #reset Kmu |psi_i> -> |psi_i> qc_psi.set_coeff_vec(copy.deepcopy(psi_i)) Kmu_prev = Kmu np.testing.assert_allclose(np.imag(grads), np.zeros_like(grads), atol=1e-7) return grads