def get_ansatz_circuit(self, param_list, provide_op_list=None): """ Return a qforte.QuantumCircuit object parametrized by input param_list Parameters ---------- param_list: list list of parameters [param_1,..., param_n] to prepare the circuit 'exp(param_n*A_n)...exp(param_1*A_1)' Returns ------- param_circuit: instance of qforte.QuantumCircuit class the circuit to be applied on the reference state to get wavefunction ansatz. """ if provide_op_list == None: op_list = self.ansatz_ops.copy() else: op_list = provide_op_list param_circuit = qforte.QuantumCircuit() for i in range(len(param_list)): param_i = param_list[i] op = op_list[i] # exp_op is a circuit object exp_op = qforte.QuantumCircuit() for coeff, term in op.terms(): factor = coeff * param_i #'exponentiate_single_term' function returns a tuple (exponential(Cir), 1.0) exp_term = exponentiate_single_term(factor, term)[0] exp_op.add_circuit(exp_term) param_circuit.add_circuit(exp_op) return param_circuit
def test_build_from_openfermion(self): print('\n') trial_state = qforte.QuantumComputer(4) trial_prep = [None] * 5 trial_prep[0] = qforte.gate('H', 0, 0) trial_prep[1] = qforte.gate('H', 1, 1) trial_prep[2] = qforte.gate('H', 2, 2) trial_prep[3] = qforte.gate('H', 3, 3) trial_prep[4] = qforte.gate('cX', 0, 1) trial_circ = qforte.QuantumCircuit() #prepare the circuit for gate in trial_prep: trial_circ.add_gate(gate) # use circuit to prepare trial state trial_state.apply_circuit(trial_circ) test_operator = QubitOperator('X2 Y1', 0.0 - 0.25j) test_operator += QubitOperator('Y2 Y1', 0.25) test_operator += QubitOperator('X2 X1', 0.25) test_operator += QubitOperator('Y2 X1', 0.0 + 0.25j) print(test_operator) qforte_operator = qforte.build_from_openfermion(test_operator) qforte.smart_print(qforte_operator) exp = trial_state.direct_op_exp_val(qforte_operator) print(exp) self.assertAlmostEqual(exp, 0.2499999999999999 + 0.0j)
def build_from_openfermion(OF_qubitops, time_evo_factor = 1.0): """ builds a QuantumOperator instance in qforte from a openfermion QubitOperator instance :param OF_qubitops: (QubitOperator) the QubitOperator instance from openfermion. """ qforte_ops = qforte.QuantumOperator() #for term, coeff in sorted(OF_qubitops.terms.items()): for term, coeff in OF_qubitops.terms.items(): circ_term = qforte.QuantumCircuit() #Exclude zero terms if np.isclose(coeff, 0.0): continue for factor in term: index, action = factor # Read the string name for actions(gates) action_string = OF_qubitops.action_strings[OF_qubitops.actions.index(action)] #Make qforte gates and add to circuit gate_this = qforte.gate(action_string, index, index) circ_term.add_gate(gate_this) #Add this term to operator qforte_ops.add_term(coeff*time_evo_factor, circ_term) return qforte_ops
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.QuantumCircuit() for j in range(self._nqb): if ref[j] == 1: Un.add_gate(qforte.gate('X', j, j)) QC = qforte.QuantumComputer(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 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._sq_ham = self._sys.get_sq_hamiltonian() self._expansion_type = expansion_type self._sparseSb = sparseSb self._total_phase = 1.0 + 0.0j self._Uqite = qf.QuantumCircuit() self._b_thresh = b_thresh self._x_thresh = x_thresh self._n_classical_params = 0 self._n_cnot = 0 self._n_pauli_trm_measures = 0 self._do_lanczos = do_lanczos self._lanczos_gap = lanczos_gap qc_ref = qf.QuantumComputer(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_circuit(Inputstr): circ = qforte.QuantumCircuit() 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_gate(qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[1]))) else: if 'R' in inputgate[0]: circ.add_gate(qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[1]), float(inputgate[2]))) else: circ.add_gate(qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[2]))) return circ
def ref_state(self): """ Return a qforte.QuantumComputer instance which represents the Hartree-Fock state of the input molecule """ hf_qc = qforte.QuantumComputer(self.molecule.n_qubits) hf_cir = qforte.QuantumCircuit() for i_a in self.occ_idx_alpha: X_ia = qforte.make_gate('X', i_a, i_a) hf_cir.add_gate(X_ia) for i_b in self.occ_idx_beta: X_ib = qforte.make_gate('X', i_b, i_b) hf_cir.add_gate(X_ib) hf_qc.apply_circuit(hf_cir) return hf_qc
def build_expansion_pool(self): print('\n==> Building expansion pool <==') self._sig = qf.QuantumOpPool() 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_quantum_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 alph, rho in sig_temp.terms(): nygates = 0 temp_rho = qf.QuantumCircuit() for gate in rho.gates(): temp_rho.add_gate( qf.gate(gate.gate_id(), gate.target(), gate.control())) if (gate.gate_id() == "Y"): nygates += 1 if (nygates % 2 == 1): rho_op = qf.QuantumOperator() rho_op.add_term(1.0, temp_rho) self._sig.add_term(1.0, rho_op) elif (self._expansion_type == 'test'): self._sig.fill_pool("test", self._ref) else: raise ValueError('Invalid expansion type specified.') self._NI = len(self._sig.terms())
def trotterize(operator, factor=1.0, trotter_number=1, trotter_order=1): """ returns a circuit equivilant to an exponentiated QuantumOperator :param operator: (QuantumOperator) 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 troterization approximation, can be 1 or 2 """ total_phase = 1.0 troterized_operator = qforte.QuantumCircuit() if (trotter_number == 1) and (trotter_order == 1): #loop over terms in operator for term in operator.terms(): term_generator, phase = qforte.exponentiate_single_term(factor*term[0],term[1]) for gate in term_generator.gates(): troterized_operator.add_gate(gate) total_phase *= phase else: if(trotter_order > 1): raise NotImplementedError("Higher order trotterization is not yet implemented.") ho_op = qforte.QuantumOperator() for k in range(1, trotter_number+1): for term in operator.terms(): ho_op.add_term( factor * term[0] / float(trotter_number) , term[1]) for trot_term in ho_op.terms(): term_generator, phase = qforte.exponentiate_single_term(trot_term[0],trot_term[1]) for gate in term_generator.gates(): troterized_operator.add_gate(gate) total_phase *= phase return (troterized_operator, total_phase)
def get_qft_circuit(self, direct): """Generates a circuit for Quantum Fourier Transformation with no swaping 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 : QuantumCircuit A circuit representing the Quantum Fourier Transform. """ qft_circ = qforte.QuantumCircuit() lens = self._aend - self._abegin + 1 for j in range(lens): qft_circ.add_gate(qforte.gate('H', j+self._abegin, j+self._abegin)) for k in range(2, lens+1-j): phase = 2.0*np.pi/(2**k) qft_circ.add_gate(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"') return qft_circ
def organizer_to_circuit(op_organizer): """Builds a QuantumCircuit from a operator orgainizer. Parameters ---------- op_organizer : list An object to organize what the coefficient and Pauli operators in terms of the QuantumOperator 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 QuantumOperator. """ operator = qforte.QuantumOperator() for coeff, word in op_organizer: circ = qforte.QuantumCircuit() for letter in word: circ.add_gate(qforte.gate(letter[0], letter[1], letter[1])) operator.add_term(coeff, circ) return operator
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 : QuantumCircuit A circuit of consecutive Hadamard gates. """ Uhad = qforte.QuantumCircuit() for j in range(self._abegin, self._aend + 1): Uhad.add_gate(qforte.gate('H', j, j)) return Uhad
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.QuantumCircuit() lens = nb - na + 1 for j in range(lens): qft_circ.add_gate(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_gate(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_gate(qforte.gate('SWAP', i + na, lens - 1 - i + na)) else: for i in range(int((lens - 1) / 2)): qft_circ.add_gate(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 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 : QuantumCircuit A circuit representing the the Z gates to be measured. """ Z_circ = qforte.QuantumCircuit() for j in range(self._abegin, self._aend + 1): Z_circ.add_gate(qforte.gate('Z', j, j)) return Z_circ
def trotterize_w_cRz(operator, ancilla_qubit_idx, factor=1.0, Use_open_cRz=False, trotter_number=1, trotter_order=1): """ returns a circuit equivilant to an exponentiated QuantumOperator 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: (QuantumOperator) 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 troterization approximation, can be 1 or 2 """ total_phase = 1.0 troterized_operator = qforte.QuantumCircuit() if (trotter_number == 1) and (trotter_order == 1): #loop over terms in operator if(Use_open_cRz): for term in operator.terms(): term_generator, phase = qforte.exponentiate_single_term(factor*term[0],term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx, Use_open_cRz=True) for gate in term_generator.gates(): troterized_operator.add_gate(gate) total_phase *= phase else: for term in operator.terms(): term_generator, phase = qforte.exponentiate_single_term(factor*term[0],term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx) for gate in term_generator.gates(): troterized_operator.add_gate(gate) total_phase *= phase else: if(trotter_order > 1): raise NotImplementedError("Higher order trotterization is not yet implemented.") ho_op = qforte.QuantumOperator() for k in range(1, trotter_number+1): k = float(k) for term in operator.terms(): ho_op.add_term( factor*term[0] / float(trotter_number) , term[1]) if(Use_open_cRz): for trot_term in ho_op.terms(): term_generator, phase = qforte.exponentiate_single_term(trot_term[0],trot_term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx, Use_open_cRz=True) for gate in term_generator.gates(): troterized_operator.add_gate(gate) total_phase *= phase else: for trot_term in ho_op.terms(): term_generator, phase = qforte.exponentiate_single_term(trot_term[0],trot_term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx) for gate in term_generator.gates(): troterized_operator.add_gate(gate) total_phase *= phase return (troterized_operator, total_phase)
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 --------- ref : list The the reference state |Phi_o>. dt : float The real time step value (delta t). m : int The number of time steps for the Um evolution. n : int The number of time steps for the Un evolution. H : QuantumOperator The operator to time evolove with respect to (usually the Hamiltonain). nqubits : int The number of qubits A : QuantumOperator The overal operator to measure with respect to (optional). 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 ------- 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.QuantumCircuit() temp_op1 = qforte.QuantumOperator() # 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_term(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(gate) Ub = qforte.QuantumCircuit() temp_op2 = qforte.QuantumOperator() for t in self._qb_ham.terms(): c, op = t phase = -1.0j * m * self._dt * c temp_op2.add_term(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(gate) if not use_op: # TODO (opt): use Uprep cir = qforte.QuantumCircuit() for j in range(self._nqb): if self._ref[j] == 1: cir.add_gate(qforte.gate('X', j, j)) cir.add_gate(qforte.gate('H', ancilla_idx, ancilla_idx)) cir.add_circuit(Uk) cir.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx)) cir.add_circuit(Ub) cir.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx)) X_op = qforte.QuantumOperator() x_circ = qforte.QuantumCircuit() Y_op = qforte.QuantumOperator() y_circ = qforte.QuantumCircuit() x_circ.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx)) y_circ.add_gate(qforte.gate('Y', ancilla_idx, ancilla_idx)) X_op.add_term(1.0, x_circ) Y_op.add_term(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.QuantumCircuit() for gate in V_l.gates(): gate_str = gate.gate_id() target = gate.target() control_gate_str = 'c' + gate_str cV_l.add_gate( qforte.gate(control_gate_str, target, ancilla_idx)) cir = qforte.QuantumCircuit() # TODO (opt): use Uprep for j in range(self._nqb): if self._ref[j] == 1: cir.add_gate(qforte.gate('X', j, j)) cir.add_gate(qforte.gate('H', ancilla_idx, ancilla_idx)) cir.add_circuit(Uk) cir.add_circuit(cV_l) cir.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx)) cir.add_circuit(Ub) cir.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx)) X_op = qforte.QuantumOperator() x_circ = qforte.QuantumCircuit() Y_op = qforte.QuantumOperator() y_circ = qforte.QuantumCircuit() x_circ.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx)) y_circ.add_gate(qforte.gate('Y', ancilla_idx, ancilla_idx)) X_op.add_term(1.0, x_circ) Y_op.add_term(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 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 run(self, t = 1.0, nruns = 20, success_prob = 0.5, num_precise_bits = 4, return_phases=False): self._t = t self._nruns = nruns self._success_prob = success_prob self._num_precise_bits = num_precise_bits self._return_phases = return_phases self._Uqpe = qforte.QuantumCircuit() self._n_state_qubits = self._nqb eps = 1 - success_prob self._n_ancilla = num_precise_bits + int(np.log2(2 + (1.0/eps))) 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 # Print options banner (should done for all algorithms). self.print_options_banner() ######### QPE ######## # add hadamard circ to split ancilla register self._Uqpe.add_circuit(self.get_Uhad()) # add initial state preparation circuit self._Uqpe.add_circuit(self._Uprep) # add controll e^-iHt circuit self._Uqpe.add_circuit(self.get_dynamics_circ()) # add reverse QFT self._Uqpe.add_circuit(self.get_qft_circuit('reverse')) computer = qforte.QuantumComputer(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 = 0.0 i = 1 for z in readout: val += z / (2**i) i += 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 = 0.0 for readout in z_readouts: iave += readout[i] iave /= nruns final_readout_aves.append(iave) if (iave > (1.0/2)): final_readout.append(1) else: final_readout.append(0) self._final_phase = 0.0 counter = 0 for i, z in enumerate(final_readout): self._final_phase += z / (2**(i+1)) Eqpe = -2 * np.pi * self._final_phase / t res = stats.mode(np.asarray(self._phases)) self._mode_phase = res.mode[0] self._mode_energy = -2 * np.pi * self._mode_phase / t 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 = Eqpe # 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 get_dynamics_circ(self): """Generates a circuit for controlled dynamics operations used in phase estimation. It approximates the exponentiated hermetina operator H as e^-iHt. Arguments --------- H : QuantumOperator The hermetian operaotr whos dynamics and eigenstates are of interest, ususally the Hamiltonian. trotter_num : int The trotter number (m) to use for the decompostion. Exponentiation is exact in the m --> infinity limit. self._abegin : int The index of the begin qubit. n_ancilla : int The number of anciall qubit used for the phase estimation. Determintes the total number of time steps. t : float The total evolution time. Returns ------- Udyn : QuantumCircuit A circuit approximating controlled application of e^-iHt. """ Udyn = qforte.QuantumCircuit() ancilla_idx = self._abegin total_phase = 1.0 for n in range(self._n_ancilla): tn = 2 ** n temp_op = qforte.QuantumOperator() scaler_terms = [] for h in self._qb_ham.terms(): c, op = h phase = -1.0j * self._t * c #* tn temp_op.add_term(phase, op) gates = op.gates() if op.size() == 0: scaler_terms.append(c * self._t) expn_op, phase1 = trotterize_w_cRz(temp_op, ancilla_idx, trotter_number=self._trotter_number) # Rotation for the scaler Hamiltonian term Udyn.add_gate(qforte.gate('R', ancilla_idx, ancilla_idx, -1.0 * np.sum(scaler_terms) * float(tn))) # NOTE: Approach uses 2^ancilla_idx blocks of the time evolution circuit for i in range(tn): for gate in expn_op.gates(): Udyn.add_gate(gate) ancilla_idx += 1 return Udyn
def exponentiate_single_term(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: float an imaginary coefficient that multiplies the Pauli string :param term: QuantumCircuit 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-16: print("exp factor: ", coefficient) raise ValueError( 'exponentiate_single_term() called with a real coefficient') # If the Pauli string has no terms this is just a phase factor if term.size() == 0: return (qforte.QuantumCircuit(), np.exp(coefficient)) exponential = qforte.QuantumCircuit() to_z = qforte.QuantumCircuit() to_original = qforte.QuantumCircuit() cX_circ = qforte.QuantumCircuit() 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_gate(qforte.gate('H', target, control)) to_original.add_gate(qforte.gate('H', target, control)) elif (id == 'Y'): to_z.add_gate(qforte.gate('Rzy', target, control)) to_original.add_gate(qforte.gate('Rzy', target, control)) elif (id == 'I'): continue if (prev_target is not None): cX_circ.add_gate(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_circuit(to_z) exponential.add_circuit(cX_circ) if (Use_open_cRz): exponential.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx)) exponential.add_gate(z_rot) if (Use_open_cRz): exponential.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx)) adj_cX_circ = cX_circ.adjoint() exponential.add_circuit(adj_cX_circ) adj_to_z = to_z.adjoint() exponential.add_circuit(adj_to_z) return (exponential, 1.0)
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