def integer_to_ref(n, nqubits): """Takes an integer pertaining to a biary number and returns the corresponding determinant occupation list (reference). Arguments --------- n : int The index. nqubits : int The number of qubits (will be length of list). Returns ------- ref : list A list of 1's and 0's representing spin orbital occupations for a single Slater determinant. """ qb = qforte.QubitBasis(n) ref = [] for i in range(nqubits): if (qb.get_bit(i)): ref.append(1) else: ref.append(0) return ref
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 get_op_from_basis_idx(ref, I): max_nbody = 100 nqb = len(ref) nel = int(sum(ref)) # TODO(Nick): incorparate more flexability into this na_el = int(nel / 2) nb_el = int(nel / 2) basis_I = qf.QubitBasis(I) nbody = 0 pn = 0 na_I = 0 nb_I = 0 holes = [] # i, j, k, ... particles = [] # a, b, c, ... parity = [] # for ( p=0; p<nel; p++) { for p in range(nel): bit_val = int(basis_I.get_bit(p)) nbody += (1 - bit_val) pn += bit_val if (p % 2 == 0): na_I += bit_val else: nb_I += bit_val if (bit_val - 1): holes.append(p) if (p % 2 == 0): parity.append(1) else: parity.append(-1) # for ( q=nel; q<nqb; q++) for q in range(nel, nqb): bit_val = int(basis_I.get_bit(q)) pn += bit_val if (q % 2 == 0): na_I += bit_val else: nb_I += bit_val if (bit_val): particles.append(q) if (q % 2 == 0): parity.append(1) else: parity.append(-1) if (pn == nel and na_I == na_el and nb_I == nb_el): if (nbody != 0 and nbody <= max_nbody): total_parity = 1 # for (const auto& z: parity) for z in parity: total_parity *= z if (total_parity == 1): # particles.insert(particles.end(), holes.begin(), holes.end()); excitation = particles + holes dexcitation = list(reversed(excitation)) # std::vector<> particles_adj (particles.rbegin(), particles.rend()); sigma_I = [1.0, tuple(excitation)]
def build_sa_refs(self): """Builds a list of spin adapted references to be used in the MRSQK procedure. """ if(self._fast==False): raise NotImplementedError('Only fast algorithm avalible to build spin adapted refs.') target_root = self._target_root self._pre_sa_ref_lst = [] num_refs_per_config = [] for ref in self._single_det_refs: if(ref not in self._pre_sa_ref_lst): if(open_shell(ref)): temp = build_eq_dets(ref) self._pre_sa_ref_lst = self._pre_sa_ref_lst + temp num_refs_per_config.append(len(temp)) else: self._pre_sa_ref_lst.append(ref) num_refs_per_config.append(1) h_mat = self.build_classical_CI_mats() evals, evecs = eig(h_mat) sorted_evals_idxs = sorted_largest_idxs(evals, use_real=True, rev=False) sorted_evals = np.zeros((len(evals)), dtype=float) sorted_evecs = np.zeros(np.shape(evecs), dtype=float) for n in range(len(evals)): old_idx = sorted_evals_idxs[n][1] sorted_evals[n] = np.real(evals[old_idx]) sorted_evecs[:,n] = np.real(evecs[:,old_idx]) if(np.abs(sorted_evecs[:,0][0]) < 1.0e-6): print('Classical CI ground state likely of wrong symmetry, trying other roots!') max = len(sorted_evals) adjusted_root = 0 Co_val = 0.0 while (Co_val < 1.0e-6): adjusted_root += 1 Co_val = np.abs(sorted_evecs[:,adjusted_root][0]) target_root = adjusted_root print('Now using classical CI root: ', target_root) target_state = sorted_evecs[:,target_root] basis_coeff_lst = [] norm_basis_coeff_lst = [] det_lst = [] coeff_idx = 0 for num_refs in num_refs_per_config: start = coeff_idx end = coeff_idx + num_refs summ = 0.0 for val in target_state[start:end]: summ += val * val temp = [x / np.sqrt(summ) for x in target_state[start:end]] norm_basis_coeff_lst.append(temp) basis_coeff_lst.append(target_state[start:end]) det_lst.append(self._pre_sa_ref_lst[start:end]) coeff_idx += num_refs print('\n\n ==> Classical CI with spin adapted dets summary <==') print('-----------------------------------------------------------') print('\nList augmented to included all spin \nconfigurations for open shells.') print('\n Coeff determinant ') print('----------------------------------------') for i, det in enumerate(self._pre_sa_ref_lst): qf_det_idx = ref_to_basis_idx(det) basis = qforte.QubitBasis(qf_det_idx) if(target_state[i] > 0.0): print(' ', round(target_state[i], 4), ' ', basis.str(self._nqb)) else: print(' ', round(target_state[i], 4), ' ', basis.str(self._nqb)) basis_importnace_lst = [] for basis_coeff in basis_coeff_lst: for coeff in basis_coeff: val = 0.0 val += coeff*coeff basis_importnace_lst.append(val) sorted_basis_importnace_lst = sorted_largest_idxs(basis_importnace_lst, use_real=True, rev=True) print('\n\n ==> Final MRSQK reference space summary <==') print('-----------------------------------------------------------') self._sa_ref_lst = [] for i in range(self._d): print('\nRef ', i+1) print('---------------------------') old_idx = sorted_basis_importnace_lst[i][1] basis_vec = [] for k in range( len(basis_coeff_lst[old_idx]) ): temp = ( norm_basis_coeff_lst[old_idx][k], det_lst[old_idx][k] ) basis_vec.append( temp ) qf_det_idx = ref_to_basis_idx(temp[1]) basis = qforte.QubitBasis(qf_det_idx) if(temp[0] > 0.0): print(' ', round(temp[0], 4), ' ', basis.str(self._nqb)) else: print(' ', round(temp[0], 4), ' ', basis.str(self._nqb)) self._sa_ref_lst.append(basis_vec)
def build_refs(self): """Builds a list of single determinant references (non spin-adapted) to be used in the MRSQK procedure. """ initial_ref_lst = [] true_initial_ref_lst = [] Nis_untruncated = self._ninitial_states # Adjust dimension of system in case matrix was ill conditioned. if(self._ninitial_states > len(self._srqk._eigenvalues)): print('\n', self._ninitial_states, ' initial states requested, but QK produced ', len(self._srqk._eigenvalues), ' stable roots.\n Using ', len(self._srqk._eigenvalues), 'intial states instead.') self._ninitial_states = len(self._srqk._eigenvalues) sorted_evals_idxs = sorted_largest_idxs(self._srqk._eigenvalues, use_real=True, rev=False) sorted_evals = np.zeros((self._ninitial_states), dtype=complex) sorted_evecs = np.zeros((Nis_untruncated, self._ninitial_states), dtype=complex) for n in range(self._ninitial_states): old_idx = sorted_evals_idxs[n][1] sorted_evals[n] = self._srqk._eigenvalues[old_idx] sorted_evecs[:,n] = self._srqk._eigenvectors[:,old_idx] sorted_sq_mod_evecs = sorted_evecs * np.conjugate(sorted_evecs) basis_coeff_mat = np.array(self._srqk._omega_lst) Cprime = (np.conj(sorted_evecs.transpose())).dot(basis_coeff_mat) for n in range(self._ninitial_states): for i, val in enumerate(Cprime[n]): Cprime[n][i] *= np.conj(val) for n in range(self._ninitial_states): for i, val in enumerate(basis_coeff_mat[n]): basis_coeff_mat[n][i] *= np.conj(val) Cprime_sq_mod = (sorted_sq_mod_evecs.transpose()).dot(basis_coeff_mat) true_idx_lst = [] idx_lst = [] if(self._use_spin_adapted_refs): num_single_det_refs = 2*self._d else: num_single_det_refs = self._d if(self._target_root is not None): true_sorted_idxs = sorted_largest_idxs(Cprime[self._target_root,:]) sorted_idxs = sorted_largest_idxs(Cprime_sq_mod[self._target_root,:]) for n in range(num_single_det_refs): idx_lst.append( sorted_idxs[n][1] ) true_idx_lst.append( true_sorted_idxs[n][1] ) else: raise NotImplementedError("psudo state-avaraged selection approach not yet functional") print('\n\n ==> Initial QK Determinat selection summary <==') print('-----------------------------------------------------------') if(self._use_phase_based_selection): print('\nMost important determinats:\n') print('index determinant ') print('----------------------------------------') for i, idx in enumerate(true_idx_lst): basis = qforte.QubitBasis(idx) print(' ', i+1, ' ', basis.str(self._nqb)) else: print('\nMost important determinats:\n') print('index determinant ') print('----------------------------------------') for i, idx in enumerate(idx_lst): basis = qforte.QubitBasis(idx) print(' ', i+1, ' ', basis.str(self._nqb)) for idx in true_idx_lst: true_initial_ref_lst.append(integer_to_ref(idx, self._nqb)) if(self._ref not in true_initial_ref_lst): print('\n***Adding initial referance determinant!***\n') for i in range(len(true_initial_ref_lst) - 1): true_initial_ref_lst[i+1] = true_initial_ref_lst[i] true_initial_ref_lst[0] = initial_ref for idx in idx_lst: initial_ref_lst.append(integer_to_ref(idx, self._nqb)) if(self._ref not in initial_ref_lst): print('\n***Adding initial referance determinant!***\n') staggard_initial_ref_lst = [initial_ref] for i in range(len(initial_ref_lst) - 1): staggard_initial_ref_lst.append(initial_ref_lst[i].copy()) initial_ref_lst[0] = initial_ref initial_ref_lst = staggard_initial_ref_lst.copy() if(self._use_phase_based_selection): self._single_det_refs = true_initial_ref_lst else: self._single_det_refs = initial_ref_lst
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 ref_string(ref, nqb): temp = ref.copy() temp.reverse() ref_basis_idx = int("".join(str(x) for x in temp), 2) ref_basis = qforte.QubitBasis(ref_basis_idx) return ref_basis.str(nqb)
def get_op_from_basis_idx(self, I): max_nbody = len(self._nbody_counts) nqb = len(self._ref) nel = int(sum(self._ref)) # TODO(Nick): incorparate more flexability into this na_el = int(nel / 2) nb_el = int(nel / 2) basis_I = qf.QubitBasis(I) nbody = 0 pn = 0 na_I = 0 nb_I = 0 holes = [] # i, j, k, ... particles = [] # a, b, c, ... parity = [] for p in range(nel): bit_val = int(basis_I.get_bit(p)) nbody += (1 - bit_val) pn += bit_val if (p % 2 == 0): na_I += bit_val else: nb_I += bit_val if (bit_val - 1): holes.append(p) if (p % 2 == 0): parity.append(1) else: parity.append(-1) for q in range(nel, nqb): bit_val = int(basis_I.get_bit(q)) pn += bit_val if (q % 2 == 0): na_I += bit_val else: nb_I += bit_val if (bit_val): particles.append(q) if (q % 2 == 0): parity.append(1) else: parity.append(-1) if (pn == nel and na_I == na_el and nb_I == nb_el): if (nbody == 0): return qf.SQOperator() if (nbody != 0 and nbody <= max_nbody): total_parity = 1 for z in parity: total_parity *= z if (total_parity == 1): K_temp = qf.SQOperator() K_temp.add(+1.0, particles, holes) K_temp.add(-1.0, holes[::-1], particles[::-1]) K_temp.simplify() return K_temp return qf.SQOperator()
def add_from_basis_idx(self, I): max_nbody = len(self._nbody_counts) nqb = len(self._ref) nel = int(sum(self._ref)) # TODO(Nick): incorparate more flexability into this na_el = int(nel / 2) nb_el = int(nel / 2) basis_I = qf.QubitBasis(I) nbody = 0 pn = 0 na_I = 0 nb_I = 0 holes = [] # i, j, k, ... particles = [] # a, b, c, ... parity = [] # for ( p=0; p<nel; p++) { for p in range(nel): bit_val = int(basis_I.get_bit(p)) nbody += (1 - bit_val) pn += bit_val if (p % 2 == 0): na_I += bit_val else: nb_I += bit_val if (bit_val - 1): holes.append(p) if (p % 2 == 0): parity.append(1) else: parity.append(-1) for q in range(nel, nqb): bit_val = int(basis_I.get_bit(q)) pn += bit_val if (q % 2 == 0): na_I += bit_val else: nb_I += bit_val if (bit_val): particles.append(q) if (q % 2 == 0): parity.append(1) else: parity.append(-1) if (pn == nel and na_I == na_el and nb_I == nb_el): if (nbody != 0 and nbody <= max_nbody): total_parity = 1 for z in parity: total_parity *= z if (total_parity == 1): K_temp = qf.SQOperator() K_temp.add(+1.0, particles, holes) K_temp.add(-1.0, holes[::-1], particles[::-1]) K_temp.simplify() # this is potentially slow self._Nm.insert(0, len(K_temp.jw_transform().terms())) self._nbody_counts[nbody - 1] += 1