def perfect_experimental_avg(self, params): """ calculates the exact experimental result of the operator the Experiment object was initialized with :param params: (list) the list of parameters for the state preparation ansatz. """ if(self.prepare_each_time_==False): #1 initialize a quantum computer qc = qforte.Computer(self.n_qubits_) #2 build/update generator with params # self.generator_.set_parameters(params) #3 apply generator (once if prepare_each_time = False, N_sample times if) qc.apply_circuit(self.generator_) #4 measure operator n_terms = len(self.operator_.terms()) term_sum = 0.0 for k in range(n_terms): term_sum += self.operator_.terms()[k][0] * qc.perfect_measure_circuit(self.operator_.terms()[k][1]); return numpy.real(term_sum) elif(self.prepare_each_time_==True): raise Exception('No support yet for measurement with multiple state preparations')
def build_classical_CI_mats(self): """Builds a classical configuration interaction out of single determinants. """ num_tot_basis = len(self._pre_sa_ref_lst) h_CI = np.zeros((num_tot_basis,num_tot_basis), dtype=complex) omega_lst = [] Homega_lst = [] for i, ref in enumerate(self._pre_sa_ref_lst): # NOTE: do NOT use Uprep here (is determinant specific). Un = qforte.Circuit() for j in range(self._nqb): if ref[j] == 1: Un.add(qforte.gate('X', j, j)) QC = qforte.Computer(self._nqb) QC.apply_circuit(Un) omega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) Homega = np.zeros((2**self._nqb), dtype=complex) QC.apply_operator(self._qb_ham) Homega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex)) for j in range(len(omega_lst)): h_CI[i][j] = np.vdot(omega_lst[i], Homega_lst[j]) h_CI[j][i] = np.conj(h_CI[i][j]) return h_CI
def build_sparse_S_b(self, b): b_sparse = [] idx_sparse = [] for I, bI in enumerate(b): if(np.abs(bI) > self._b_thresh): idx_sparse.append(I) b_sparse.append(bI) Idim = len(idx_sparse) self._n_pauli_trm_measures += int(Idim*(Idim+1)*0.5) S = np.zeros((len(b_sparse),len(b_sparse)), dtype=complex) Ipsi_qc = qf.Computer(self._nqb) Ipsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec())) CI = np.zeros(shape=(Idim, int(2**self._nqb)), dtype=complex) for i in range(Idim): S[i][i] = 1.0 # With Pauli strings, this is always the inner product Ii = idx_sparse[i] Ipsi_qc.apply_operator(self._sig.terms()[Ii][1]) CI[i,:] = copy.deepcopy(Ipsi_qc.get_coeff_vec()) for j in range(i): S[i][j] = S[j][i] = np.vdot(CI[i,:], CI[j,:]) Ipsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec())) return idx_sparse, np.real(S), np.real(b_sparse)
def evolve(self): """Perform QITE for a time step :math:`\\Delta \\beta`. """ self._Uqite.add(self._Uprep) self._qc = qf.Computer(self._nqb) self._qc.apply_circuit(self._Uqite) if(self._do_lanczos): self._lanczos_vecs = [] self._Hlanczos_vecs = [] self._lanczos_vecs.append(copy.deepcopy(self._qc.get_coeff_vec())) qcSig_temp = qf.Computer(self._nqb) qcSig_temp.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec())) qcSig_temp.apply_operator(self._qb_ham) self._Hlanczos_vecs.append(copy.deepcopy(qcSig_temp.get_coeff_vec())) print(f"{'beta':>7}{'E(beta)':>18}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}") print('-------------------------------------------------------------------------------') print(f' {0.0:7.3f} {self._Ekb[0]:+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}') if (self._print_summary_file): f = open("summary.dat", "w+", buffering=1) f.write(f"#{'beta':>7}{'E(beta)':>18}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}\n") f.write('#-------------------------------------------------------------------------------\n') f.write(f' {0.0:7.3f} {self._Ekb[0]:+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}\n') for kb in range(1, self._nbeta): self.do_qite_step() if(self._do_lanczos): if(kb % self._lanczos_gap == 0): self._lanczos_vecs.append(copy.deepcopy(self._qc.get_coeff_vec())) qcSig_temp = qf.Computer(self._nqb) qcSig_temp.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec())) qcSig_temp.apply_operator(self._qb_ham) self._Hlanczos_vecs.append(copy.deepcopy(qcSig_temp.get_coeff_vec())) print(f' {kb*self._db:7.3f} {self._Ekb[kb]:+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' {kb*self._db:7.3f} {self._Ekb[kb]:+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}\n') self._Egs = self._Ekb[-1] if (self._print_summary_file): f.close()
def fill_excited_dets(self): for _, sq_op in self._pool_obj: # 1. Identify the excitation operator # 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][2][-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_creators = sq_op.terms()[0][1] sq_annihilators = sq_op.terms()[0][2] else: sq_creators = sq_op.terms()[0][2] sq_annihilators = sq_op.terms()[0][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. # `destroyed` exists solely for error catching. destroyed = False excited_det = qforte.QubitBasis(self._nqb) for k, occ in enumerate(self._ref): excited_det.set_bit(k, occ) # loop over annihilators for p in reversed(sq_annihilators): if (excited_det.get_bit(p) == 0): destroyed = True break excited_det.set_bit(p, 0) # then over creators for p in reversed(sq_creators): if (excited_det.get_bit(p) == 1): destroyed = True break excited_det.set_bit(p, 1) if destroyed: raise ValueError( "no ops should destroy reference, something went wrong!!") I = excited_det.add() qc_temp = qforte.Computer(self._nqb) qc_temp.apply_circuit(self._Uprep) qc_temp.apply_operator(sq_op.jw_transform()) phase_factor = qc_temp.get_coeff_vec()[I] self._excited_dets.append((I, phase_factor))
def run(self, beta=1.0, db=0.2, expansion_type='SD', sparseSb=True, b_thresh=1.0e-6, x_thresh=1.0e-10, do_lanczos=False, lanczos_gap=2): self._beta = beta self._db = db self._nbeta = int(beta/db)+1 self._expansion_type = expansion_type self._sparseSb = sparseSb self._total_phase = 1.0 + 0.0j self._Uqite = qf.Circuit() self._b_thresh = b_thresh self._x_thresh = x_thresh self._n_classical_params = 0 self._n_cnot = self._Uprep.get_num_cnots() self._n_pauli_trm_measures = 0 self._do_lanczos = do_lanczos self._lanczos_gap = lanczos_gap qc_ref = qf.Computer(self._nqb) qc_ref.apply_circuit(self._Uprep) self._Ekb = [np.real(qc_ref.direct_op_exp_val(self._qb_ham))] # Print options banner (should done for all algorithms). self.print_options_banner() # Build expansion pool. self.build_expansion_pool() # Do the imaginary time evolution. self.evolve() if (self._do_lanczos): self.do_qlanczos() # Print summary banner (should done for all algorithms). self.print_summary_banner() # verify that required attributes were defined # (should done for all algorithms). self.verify_run()
def build_orb_energies(self): """Calculates single qubit energies. Used in quasi-Newton updates. """ self._orb_e = [] print('\nBuilding single particle energies list:') print('---------------------------------------', flush=True) qc = qf.Computer(self._nqb) qc.apply_circuit(build_Uprep(self._ref, 'occupation_list')) E0 = qc.direct_op_exp_val(self._qb_ham) for i in range(self._nqb): qc = qf.Computer(self._nqb) qc.apply_circuit(build_Uprep(self._ref, 'occupation_list')) qc.apply_gate(qf.gate('X', i, i)) Ei = qc.direct_op_exp_val(self._qb_ham) if(i<sum(self._ref)): ei = E0 - Ei else: ei = Ei - E0 print(f' {i:3} {ei:+16.12f}', flush=True) self._orb_e.append(ei)
def get_residual_vector(self, trial_amps): """Returns the residual vector with elements pertaining to all operators in the ansatz circuit. Parameters ---------- trial_amps : list of floats The list of (real) floating point numbers which will characterize the state preparation circuit used in calculation of the residuals. """ if (self._pool_type == 'sa_SD'): raise ValueError( 'Must use single term particle-hole nbody operators for residual calculation' ) U = self.ansatz_circuit(trial_amps) qc_res = qforte.Computer(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 I, phase_factor in self._excited_dets: # 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) self._res_vec_evals += 1 self._res_m_evals += len(self._tamps) return residuals
def build_S(self): """Construct the matrix S (eq. 5a) of Motta. """ Idim = self._NI S = np.zeros((Idim, Idim), dtype=complex) Ipsi_qc = qf.Computer(self._nqb) Ipsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec())) # CI[I][J] = (σ_I Ψ)_J self._n_pauli_trm_measures += int(self._NI*(self._NI+1)*0.5) CI = np.zeros(shape=(Idim, int(2**self._nqb)), dtype=complex) for i in range(Idim): S[i][i] = 1.0 # With Pauli strings, this is always the inner product Ipsi_qc.apply_operator(self._sig.terms()[i][1]) CI[i,:] = copy.deepcopy(Ipsi_qc.get_coeff_vec()) for j in range(i): S[i][j] = S[j][i] = np.vdot(CI[i,:], CI[j,:]) Ipsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec())) return np.real(S)
def build_b(self): """Construct the vector b (eq. 5b) of Motta, with h[m] the full Hamiltonian. """ b = np.zeros(self._NI, dtype=complex) denom = np.sqrt(1 - 2*self._db*self._Ekb[-1]) prefactor = -1.0j / denom self._n_pauli_trm_measures += self._Nl * self._NI Hpsi_qc = qf.Computer(self._nqb) Hpsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec())) Hpsi_qc.apply_operator(self._qb_ham) C_Hpsi_qc = copy.deepcopy(Hpsi_qc.get_coeff_vec()) for I, (op_coefficient, operator) in enumerate(self._sig.terms()): Hpsi_qc.apply_operator(operator) exp_val = np.vdot(self._qc.get_coeff_vec(), Hpsi_qc.get_coeff_vec()) b[I] = prefactor * op_coefficient * exp_val Hpsi_qc.set_coeff_vec(copy.deepcopy(C_Hpsi_qc)) return np.real(b)
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 run(self, guess_energy: float, t = 1.0, nruns = 20, success_prob = 0.5, num_precise_bits = 4): """ guess_energy : A guess for the eigenvalue of the eigenspace with which |0>^(n) has greatest overlap. You should be confident the ground state is within t : A scaling parameter that controls the precision of the computation. You should confident that the eigenvalue of interest is within +/- t of the guess energy. """ # float: evolution times self._t = t # int: number of times to sample the eigenvalue distribution self._nruns = nruns self._success_prob = success_prob self._num_precise_bits = num_precise_bits self._Uqpe = qforte.Circuit() # int: The number of qubits needed to represent the state. self._n_state_qubits = self._nqb eps = 1 - success_prob # int: The number of ancilla qubits used to hold eigenvalue information self._n_ancilla = num_precise_bits + int(np.log2(2 + (1.0/eps))) # int: The total number of qubits needed in the circuit self._n_tot_qubits = self._n_state_qubits + self._n_ancilla self._abegin = self._n_state_qubits self._aend = self._n_tot_qubits - 1 self._n_classical_params = 0 self._n_pauli_trm_measures = nruns self._guess_energy = guess_energy self._guess_periods = round(self._t * guess_energy / (-2 * np.pi)) # Print options banner (should done for all algorithms). self.print_options_banner() ######### QPE ######## # Apply Hadamard gates to all ancilla qubits self._Uqpe.add(self.get_Uhad()) # Prepare the trial state on the non-ancilla qubits self._Uqpe.add(self._Uprep) # add controll e^-iHt circuit self._Uqpe.add(self.get_dynamics_circ()) # add reverse QFT self._Uqpe.add(self.get_qft_circuit('reverse')) computer = qforte.Computer(self._n_tot_qubits) computer.apply_circuit(self._Uqpe) self._n_cnot = self._Uqpe.get_num_cnots() if(self._fast): z_readouts = computer.measure_z_readouts_fast(self._abegin, self._aend, self._nruns) else: Zcirc = self.get_z_circuit() z_readouts = computer.measure_readouts(Zcirc, self._nruns) self._phases = [] for readout in z_readouts: val = sum(z / (2**i) for i, z in enumerate(readout, start=1)) self._phases.append(val) # find final binary string of phase readouts: final_readout = [] final_readout_aves = [] for i in range(self._n_ancilla): iave = sum(readout[i] for readout in z_readouts) / nruns final_readout_aves.append(iave) final_readout.append(1 if iave > 0.5 else 0) self._final_phase = sum(z / (2**i) for i, z in enumerate(final_readout, start=1)) E_u = -2 * np.pi * (self._final_phase + self._guess_periods - 1) / t E_l = -2 * np.pi * (self._final_phase + self._guess_periods - 0) / t E_qpe = E_l if abs(E_l - guess_energy) < abs(E_u - guess_energy) else E_u res = stats.mode(np.asarray(self._phases)) self._mode_phase = res.mode[0] E_u = -2 * np.pi * (self._mode_phase + self._guess_periods - 1) / t E_l = -2 * np.pi * (self._mode_phase + self._guess_periods - 0) / t self._mode_energy = E_l if abs(E_l - guess_energy) < abs(E_u - guess_energy) else E_u print('\n ==> QPE readout averages <==') print('------------------------------------------------') for i, ave in enumerate(final_readout_aves): print(' bit ', i, ': ', ave) print('\n Final bit readout: ', final_readout) ######### QPE ######## # set Egs self._Egs = E_qpe # set Umaxdepth self._Umaxdepth = self._Uqpe # Print summary banner (should done for all algorithms). self.print_summary_banner() # verify that required attributes were defined # (should done for all algorithms). self.verify_run()
def update_ansatz(self): self._n_pauli_measures_k = 0 # TODO: Check if this deepcopy is needed. The one argument of energy_feval should be const. x0 = copy.deepcopy(self._tamps) init_gues_energy = self.energy_feval(x0) # do U^dag e^iH U |Phi_o> = |Phi_res> U = self.ansatz_circuit() qc_res = qf.Computer(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() # 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 = sum( rmu_sq[0] for rmu_sq in res_sq[:-1]) / (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 = True self._final_energy = self._energies[-1] self._final_result = self._results[-1] else: self._converged = False ## 5. add new toperator if not self._converged: if self._verbose: print('\n') print(' op index (Imu) Number of times measured') print(' -----------------------------------------------') for Nmu_tup in Nmu_lst[:-1]: if (self._verbose): print( f" {Nmu_tup[1]:10} {np.real(Nmu_tup[0]):14}" ) if (Nmu_tup[1] not in self._tops): self._tops.insert(0, Nmu_tup[1]) self._tamps.insert(0, 0.0) self.add_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 = sum( rmu_sq[0] for rmu_sq in res_sq[:-1]) / (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 Factor') print(' -----------------------------------------------') res_sq_sum = 0.0 if (self._use_cumulative_thresh): # Make a running list of operators. When the sum of res_sq exceeds the target, every operator # from here out is getting added to the ansatz.. temp_ops = [] for rmu_sq, op_idx in res_sq[:-1]: res_sq_sum += rmu_sq / (self._dt * self._dt) if res_sq_sum > (self._spqe_thresh * self._spqe_thresh): if (self._verbose): Ktemp = self.get_op_from_basis_idx(op_idx) print( f" {op_idx:10} {np.real(rmu_sq)/(self._dt * self._dt):14.12f} {Ktemp.str()}" ) if op_idx not in self._tops: temp_ops.append(op_idx) self.add_from_basis_idx(op_idx) for temp_op in temp_ops[::-1]: self._tops.insert(0, temp_op) self._tamps.insert(0, 0.0) else: # Add the single operator with greatest rmu_sq not yet in the ansatz res_sq.reverse() for rmu_sq, op_idx in res_sq[1:]: print( f" {op_idx:10} {np.real(rmu_sq)/(self._dt * self._dt):14.12f}" ) if op_idx not in self._tops: print('Adding this operator to ansatz') self._tops.insert(0, op_idx) self._tamps.insert(0, 0.0) self.add_from_basis_idx(op_idx) break self._n_classical_params_lst.append(len(self._tops))
def build_qk_mats(self): """Returns matrices S and H needed for the QK 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') Hsp = get_scipy_csc_from_op(self._qb_ham, -1.0j) QC = qforte.Computer(self._nqb) QC.apply_circuit(self._Uprep) # get the time evolution vectors psi_t_vecs = apply_time_evolution_op(QC, Hsp, self._dt, self._nstates) for m in range(self._nstates): # # Compute U_m |φ> QC = qforte.Computer(self._nqb) QC.set_coeff_vec(psi_t_vecs[m]) 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 = 0 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} {0: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} {0: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 = 0 # 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