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_operator(Inputstr): ops = qforte.QuantumOperator() sepstr = Inputstr.split(';') for i in range(len(sepstr)): inputterm = sepstr[i].split(',') ops.add_term(complex(inputterm[0]), qforte.build_circuit(inputterm[1])) return ops
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 do_qite_step(self): btot = self.build_b() A = qf.QuantumOperator() if (self._sparseSb): sp_idxs, S, btot = self.build_sparse_S_b(btot) else: S = self.build_S() x = lstsq(S, btot)[0] x = np.real(x) if (self._sparseSb): for I, spI in enumerate(sp_idxs): if np.abs(x[I]) > self._x_thresh: A.add_term(-1.0j * self._db * x[I], self._sig.terms()[spI][1].terms()[0][1]) self._n_classical_params += 1 else: for I, SigI in enumerate(self._sig.terms()): if np.abs(x[I]) > self._x_thresh: A.add_term(-1.0j * self._db * x[I], SigI[1].terms()[0][1]) self._n_classical_params += 1 if (self._verbose): print('\nbtot:\n ', btot) print('\n S: \n') matprint(S) print('\n x: \n') print(x) eiA_kb, phase1 = trotterize(A, trotter_number=self._trotter_number) self._total_phase *= phase1 self._Uqite.add_circuit(eiA_kb) self._qc.apply_circuit(eiA_kb) self._Ekb.append(np.real(self._qc.direct_op_exp_val(self._qb_ham))) self._n_cnot += eiA_kb.get_num_cnots() if (self._verbose): qf.smart_print(self._qc)
def 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 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 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 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