def Fredkin(i, j, k): """ builds a circuit to simulate a three-qubit Fredkin gate (Controled-SWAP, CSWAP gate). :param i: control qubit 1 :param j: swap qubit 1 :param k: swap qubit 2 """ C12 = qforte.gate('cX', j, i) C32 = qforte.gate('cX', j, k) CV23 = qforte.gate('cV', k, j) CV13 = qforte.gate('cV', k, i) F_circ = qforte.Circuit() F_circ.add(C32) F_circ.add(CV23) F_circ.add(CV13) F_circ.add(C12) F_circ.add(CV23.adjoint()) F_circ.add(C32) F_circ.add(C12) return F_circ
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_Uprep(ref, state_prep_type): Uprep = qforte.Circuit() if state_prep_type == 'occupation_list': for j in range(len(ref)): if ref[j] == 1: Uprep.add(qforte.gate('X', j, j)) else: raise ValueError( "Only 'occupation_list' supported as state preparation type") return Uprep
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 trotterize(operator, factor=1.0, trotter_number=1, trotter_order=1): """ returns a circuit equivalent to an exponentiated QubitOperator :param operator: (QubitOperator) the operator or state preparation ansatz (represented as a sum of pauli terms) to be exponentiated :param trotter_number: (int) for an operator A with terms A_i, the trotter_number is the exponent (N) for to product of single term exponentals e^A ~ ( Product_i(e^(A_i/N)) )^N :param trotter_number: (int) the order of the trotterization approximation, can be 1 or 2 """ total_phase = 1.0 trotterized_operator = qforte.Circuit() if (trotter_number == 1) and (trotter_order == 1): #loop over terms in operator for term in operator.terms(): term_generator, phase = qforte.exponentiate_pauli_string( factor * term[0], term[1]) for gate in term_generator.gates(): trotterized_operator.add(gate) total_phase *= phase else: if (trotter_order > 1): raise NotImplementedError( "Higher order trotterization is not yet implemented.") ho_op = qforte.QubitOperator() for k in range(1, trotter_number + 1): for term in operator.terms(): ho_op.add(factor * term[0] / float(trotter_number), term[1]) for trot_term in ho_op.terms(): term_generator, phase = qforte.exponentiate_pauli_string( trot_term[0], trot_term[1]) for gate in term_generator.gates(): troterized_operator.add(gate) total_phase *= phase return (trotterized_operator, total_phase)
def build_circuit(Inputstr): circ = qforte.Circuit() sepstr = Inputstr.split() #Separate string to a list by space for i in range(len(sepstr)): inputgate = sepstr[i].split('_') if len(inputgate) == 2: circ.add( qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[1]))) else: if 'R' in inputgate[0]: circ.add( qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[1]), float(inputgate[2]))) else: circ.add( qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[2]))) return circ
def build_expansion_pool(self): print('\n==> Building expansion pool <==') self._sig = qf.QubitOpPool() if(self._expansion_type == 'complete_qubit'): if (self._nqb > 6): raise ValueError('Using complete qubits expansion will result in a very large number of terms!') self._sig.fill_pool("complete_qubit", self._ref) elif(self._expansion_type == 'cqoy'): self._sig.fill_pool("cqoy", self._ref) elif(self._expansion_type in {'SD', 'GSD', 'SDT', 'SDTQ', 'SDTQP', 'SDTQPH'}): P = qf.SQOpPool() P.set_orb_spaces(self._ref) P.fill_pool(self._expansion_type) sig_temp = P.get_qubit_operator("commuting_grp_lex", False) # Filter the generated operators, so that only those with an odd number of Y gates are allowed. # See section "Real Hamiltonians and states" in the SI of Motta for theoretical justification. # Briefly, this method solves Ax=b, but all b elements with an odd number of Y gates are imaginary and # thus vanish. This method will not be correct for non-real Hamiltonians or states. for _, rho in sig_temp.terms(): nygates = 0 temp_rho = qf.Circuit() for gate in rho.gates(): temp_rho.add(qf.gate(gate.gate_id(), gate.target(), gate.control())) if (gate.gate_id() == "Y"): nygates += 1 if (nygates % 2 == 1): rho_op = qf.QubitOperator() rho_op.add(1.0, temp_rho) self._sig.add(1.0, rho_op) else: raise ValueError('Invalid expansion type specified.') self._NI = len(self._sig.terms())
def get_dynamics_circ(self): """Generates controlled unitaries. Ancilla qubit n controls a Trotter approximation to exp(-iHt*2^n). Returns ------- U : Circuit A circuit approximating controlled application of e^-iHt. """ U = qforte.Circuit() ancilla_idx = self._abegin temp_op = qforte.QubitOperator() scalar_terms = [] for scalar, operator in self._qb_ham.terms(): phase = -1.0j * scalar * self._t if operator.size() == 0: scalar_terms.append(scalar * self._t) else: # Strangely, the code seems to work even if this line is outside the else clause. # TODO: Figure out how. temp_op.add(phase, operator) for n in range(self._n_ancilla): tn = 2 ** n expn_op, _ = trotterize_w_cRz(temp_op, ancilla_idx, trotter_number=self._trotter_number) # Rotation for the scalar Hamiltonian term U.add(qforte.gate('R', ancilla_idx, ancilla_idx, -1.0 * np.sum(scalar_terms) * float(tn))) for i in range(tn): U.add_circuit(expn_op) ancilla_idx += 1 return U
def get_qft_circuit(self, direct): """Generates a circuit for Quantum Fourier Transformation with no swapping of bit positions. Arguments --------- self._abegin : int The index of the begin qubit. self._aend : int The index of the end qubit. direct : string The direction of the Fourier Transform can be 'forward' or 'reverse.' Returns ------- qft_circ : Circuit A circuit representing the Quantum Fourier Transform. """ qft_circ = qforte.Circuit() lens = self._aend - self._abegin + 1 for j in range(lens): qft_circ.add(qforte.gate('H', j+self._abegin)) for k in range(2, lens+1-j): phase = 2.0*np.pi/(2**k) qft_circ.add(qforte.gate('cR', j+self._abegin, j+k-1+self._abegin, phase)) if direct == 'forward': return qft_circ elif direct == 'reverse': return qft_circ.adjoint() else: raise ValueError('QFT directions can only be "forward" or "reverse"')
def qft_circuit(na, nb, direct): """ generates a circuit for Quantum Fourier Transformation :param na: (int) the begin qubit :param nb: (int) the end qubit :param direct: (string) the direction of the Fourier Transform can be 'forward' or 'reverse' """ # Build qft circuit qft_circ = qforte.Circuit() lens = nb - na + 1 for j in range(lens): qft_circ.add(qforte.gate('H', j + na, j + na)) for k in range(2, lens + 1 - j): phase = 2.0 * numpy.pi / (2**k) qft_circ.add(qforte.gate('cR', j + na, j + k - 1 + na, phase)) # Build reversing circuit if lens % 2 == 0: for i in range(int(lens / 2)): qft_circ.add(qforte.gate('SWAP', i + na, lens - 1 - i + na)) else: for i in range(int((lens - 1) / 2)): qft_circ.add(qforte.gate('SWAP', i + na, lens - 1 - i + na)) if direct == 'forward': return qft_circ elif direct == 'reverse': return qft_circ.adjoint() else: raise ValueError('QFT directions can only be "forward" or "reverse"') return qft_circ
def Toffoli(i, j, k): """ builds a circuit to simulate a three-qubit Toffoli gate (Control-Control-NOT, CCNOT gate). :param i: control qubit 1 :param j: control qubit 2 :param k: target qubit """ T1 = qforte.gate('T', i, i) T2 = qforte.gate('T', j, j) T3 = qforte.gate('T', k, k) C12 = qforte.gate('cX', j, i) C13 = qforte.gate('cX', k, i) C23 = qforte.gate('cX', k, j) H3 = qforte.gate('H', k, k) T_circ = qforte.Circuit() T_circ.add(H3) T_circ.add(C23) T_circ.add(T3.adjoint()) T_circ.add(C13) T_circ.add(T3) T_circ.add(C23) T_circ.add(T3.adjoint()) T_circ.add(C13) T_circ.add(T2) T_circ.add(T3) T_circ.add(C12) T_circ.add(H3) T_circ.add(T1) T_circ.add(T2.adjoint()) T_circ.add(C12) return T_circ
def get_z_circuit(self): """Generates a circuit of Z gates for each quibit in the ancilla register. Arguments --------- self._abegin : int The index of the begin qubit. self._aend : int The index of the end qubit. Returns ------- z_circ : Circuit A circuit representing the the Z gates to be measured. """ Z_circ = qforte.Circuit() for j in range(self._abegin, self._aend + 1): Z_circ.add(qforte.gate('Z', j)) return Z_circ
def get_Uhad(self): """Generates a circuit which to puts all of the ancilla regester in superpostion. Arguments --------- self._abegin : int The index of the begin qubit. self._aend : int The index of the end qubit. Returns ------- qft_circ : Circuit A circuit of consecutive Hadamard gates. """ Uhad = qforte.Circuit() for j in range(self._abegin, self._aend + 1): Uhad.add(qforte.gate('H', j)) return Uhad
def organizer_to_circuit(op_organizer): """Builds a Circuit from a operator orgainizer. Parameters ---------- op_organizer : list An object to organize what the coefficient and Pauli operators in terms of the QubitOperator will be. The orginzer is of the form [[coeff_a, [ ("X", i), ("Z", j), ("Y", k), ... ] ], [...] ...] where X, Y, Z are strings that indicate Pauli opterators; i, j, k index qubits and coeff_a indicates the coefficient for the ath term in the QubitOperator. """ operator = qforte.QubitOperator() for coeff, word in op_organizer: circ = qforte.Circuit() for letter in word: circ.add(qforte.gate(letter[0], letter[1], letter[1])) operator.add(coeff, circ) return operator
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): """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' ) for m in range(self._nstates): # Compute U_m = exp(-i m dt H) Um = qforte.Circuit() Um.add(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(expn_op1) # Compute U_m |φ> QC = qforte.Computer(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 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 matrix_element(self, m, n, use_op=False): """Returns a single matrix element M_mn based on the evolution of two unitary operators Um = exp(-i * m * dt * H) and Un = exp(-i * n * dt * H) on a reference state |Phi_o>, (optionally) with respect to an operator A. Specifically, M_mn is given by <Phi_o| Um^dag Un | Phi_o> or (optionally if A is specified) <Phi_o| Um^dag A Un | Phi_o>. Arguments --------- m : int The number of time steps for the Um evolution. n : int The number of time steps for the Un evolution. Returns ------- value : complex The outcome of measuring <X> and <Y> to determine <2*sigma_+>, ultimately the value of the matrix elemet. """ value = 0.0 ancilla_idx = self._nqb Uk = qforte.Circuit() temp_op1 = qforte.QubitOperator() # TODO (opt): move to C side. for t in self._qb_ham.terms(): c, op = t phase = -1.0j * n * self._dt * c temp_op1.add(phase, op) expn_op1, phase1 = trotterize_w_cRz( temp_op1, ancilla_idx, trotter_number=self._trotter_number) for gate in expn_op1.gates(): Uk.add(gate) Ub = qforte.Circuit() temp_op2 = qforte.QubitOperator() for t in self._qb_ham.terms(): c, op = t phase = -1.0j * m * self._dt * c temp_op2.add(phase, op) expn_op2, phase2 = trotterize_w_cRz( temp_op2, ancilla_idx, trotter_number=self._trotter_number, Use_open_cRz=False) for gate in expn_op2.gates(): Ub.add(gate) if not use_op: # TODO (opt): use Uprep cir = qforte.Circuit() for j in range(self._nqb): if self._ref[j] == 1: cir.add(qforte.gate('X', j, j)) cir.add(qforte.gate('H', ancilla_idx, ancilla_idx)) cir.add(Uk) cir.add(qforte.gate('X', ancilla_idx, ancilla_idx)) cir.add(Ub) cir.add(qforte.gate('X', ancilla_idx, ancilla_idx)) X_op = qforte.QubitOperator() x_circ = qforte.Circuit() Y_op = qforte.QubitOperator() y_circ = qforte.Circuit() x_circ.add(qforte.gate('X', ancilla_idx, ancilla_idx)) y_circ.add(qforte.gate('Y', ancilla_idx, ancilla_idx)) X_op.add(1.0, x_circ) Y_op.add(1.0, y_circ) X_exp = qforte.Experiment(self._nqb + 1, cir, X_op, 100) Y_exp = qforte.Experiment(self._nqb + 1, cir, Y_op, 100) params = [1.0] x_value = X_exp.perfect_experimental_avg(params) y_value = Y_exp.perfect_experimental_avg(params) value = (x_value + 1.0j * y_value) * phase1 * np.conj(phase2) else: value = 0.0 for t in self._qb_ham.terms(): c, V_l = t # TODO (opt): cV_l = qforte.Circuit() for gate in V_l.gates(): gate_str = gate.gate_id() target = gate.target() control_gate_str = 'c' + gate_str cV_l.add(qforte.gate(control_gate_str, target, ancilla_idx)) cir = qforte.Circuit() # TODO (opt): use Uprep for j in range(self._nqb): if self._ref[j] == 1: cir.add(qforte.gate('X', j, j)) cir.add(qforte.gate('H', ancilla_idx, ancilla_idx)) cir.add(Uk) cir.add(cV_l) cir.add(qforte.gate('X', ancilla_idx, ancilla_idx)) cir.add(Ub) cir.add(qforte.gate('X', ancilla_idx, ancilla_idx)) X_op = qforte.QubitOperator() x_circ = qforte.Circuit() Y_op = qforte.QubitOperator() y_circ = qforte.Circuit() x_circ.add(qforte.gate('X', ancilla_idx, ancilla_idx)) y_circ.add(qforte.gate('Y', ancilla_idx, ancilla_idx)) X_op.add(1.0, x_circ) Y_op.add(1.0, y_circ) X_exp = qforte.Experiment(self._nqb + 1, cir, X_op, 100) Y_exp = qforte.Experiment(self._nqb + 1, cir, Y_op, 100) # TODO (cleanup): Remove params as required arg (Nick) params = [1.0] x_value = X_exp.perfect_experimental_avg(params) y_value = Y_exp.perfect_experimental_avg(params) element = (x_value + 1.0j * y_value) * phase1 * np.conj(phase2) value += c * element return value
def trotterize_w_cRz(operator, ancilla_qubit_idx, factor=1.0, Use_open_cRz=False, trotter_number=1, trotter_order=1): """ Returns a circuit equivalent to an exponentiated QubitOperator in which each term in the trotterization exp(-i * theta_k ) only acts on the register if the ancilla qubit is in the |1> state. :param operator: (QubitOperator) the operator or state preparation ansatz (represented as a sum of pauli terms) to be exponentiated :param ancilla_qubit_idx: (int) the index of the ancilla qubit :param Use_open_cRz: (bool) uses an open controlled Rz gate in exponentiation (see Fig. 11 on page 185 of Nielson and Chung's "Quantum Computation and Quantum Informatoin 10th Aniversary Ed.") :param trotter_number: (int) for an operator A with terms A_i, the trotter_number is the exponent (N) for to product of single term exponentals e^A ~ ( Product_i(e^(A_i/N)) )^N :param trotter_order: (int) the order of the trotterization approximation, can be 1 or 2 """ total_phase = 1.0 trotterized_operator = qforte.Circuit() if (trotter_number == 1) and (trotter_order == 1): for term in operator.terms(): term_generator, phase = qforte.exponentiate_pauli_string( factor * term[0], term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx, Use_open_cRz=Use_open_cRz) for gate in term_generator.gates(): trotterized_operator.add(gate) total_phase *= phase else: if (trotter_order > 1): raise NotImplementedError( "Higher order trotterization is not yet implemented.") ho_op = qforte.QubitOperator() for k in range(1, trotter_number + 1): k = float(k) for term in operator.terms(): ho_op.add(factor * term[0] / float(trotter_number), term[1]) for trot_term in ho_op.terms(): term_generator, phase = qforte.exponentiate_pauli_string( trot_term[0], trot_term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx, Use_open_cRz=Use_open_cRz) for gate in term_generator.gates(): trotterized_operator.add(gate) total_phase *= phase return (trotterized_operator, total_phase)
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 exponentiate_pauli_string(coefficient, term, Use_cRz=False, ancilla_idx=None, Use_open_cRz=False): """ returns the exponential of an string of Pauli operators multiplied by an imaginary coefficient exp(coefficient * term) Parameters ---------- :param coefficient: complex an imaginary coefficient that multiplies the Pauli string :param term: Circuit a Pauli string to be exponentiated """ # This function assumes that the factor is imaginary. The following tests for it. if np.abs(np.real(coefficient)) > 1.0e-14: raise ValueError( f'exponentiate_pauli_string() called with a real coefficient {coefficient}' ) # If the Pauli string has no terms this is just a phase factor times the identity circuit if term.size() == 0: return (qforte.Circuit(), np.exp(coefficient)) exponential = qforte.Circuit() to_z = qforte.Circuit() to_original = qforte.Circuit() cX_circ = qforte.Circuit() prev_target = None max_target = None for gate in term.gates(): id = gate.gate_id() target = gate.target() control = gate.control() if (id == 'X'): to_z.add(qforte.gate('H', target, control)) to_original.add(qforte.gate('H', target, control)) elif (id == 'Y'): to_z.add(qforte.gate('Rzy', target, control)) to_original.add(qforte.gate('Rzy', target, control)) elif (id == 'I'): continue if (prev_target is not None): cX_circ.add(qforte.gate('cX', target, prev_target)) prev_target = target max_target = target # Gate that actually contains the parameterization for the term # TODO(Nick): investigate real/imaginary usage of 'factor' in below expression if (Use_cRz): z_rot = qforte.gate('cRz', max_target, ancilla_idx, -2.0 * np.imag(coefficient)) else: z_rot = qforte.gate('Rz', max_target, max_target, -2.0 * np.imag(coefficient)) # Assemble the actual exponential exponential.add(to_z) exponential.add(cX_circ) if (Use_open_cRz): exponential.add(qforte.gate('X', ancilla_idx, ancilla_idx)) exponential.add(z_rot) if (Use_open_cRz): exponential.add(qforte.gate('X', ancilla_idx, ancilla_idx)) adj_cX_circ = cX_circ.adjoint() exponential.add(adj_cX_circ) adj_to_z = to_z.adjoint() exponential.add(adj_to_z) return (exponential, 1.0)